From 8871adc4a00e4c019caa435f4d086e15f9626e45 Mon Sep 17 00:00:00 2001 From: Akumas_Coffin_ <68540569+AkumasCoffin@users.noreply.github.com> Date: Thu, 14 May 2026 20:25:07 +1000 Subject: [PATCH] Fix JIDE/Windows-LookAndFeel crash on non-Windows JDKs JIDE 3.6.18's LookAndFeelFactory contains compile-time \`instanceof com.sun.java.swing.plaf.windows.WindowsLookAndFeel\` checks in multiple methods, including a JIDE PropertyChangeListener that fires on every UIManager.setLookAndFeel call. On Linux and macOS JDKs the class is genuinely absent from java.desktop, so the JVM throws NoClassDefFoundError the first time JIDE's dispatch code is executed (during JFrame init and again from JideSplitPane.updateUI on each LAF change). Two complementary fixes: 1. Pass an explicit MetalLookAndFeel instance to the 3-arg installJideExtension(UIDefaults, LookAndFeel, int) overload so JIDE's Metal branch matches first and never falls through to the Windows branch in the normal install path. 2. Add a no-op stub class at com.sun.java.swing.plaf.windows.WindowsLookAndFeel so that the compile-time class reference resolves on non-Windows JDKs. The stub reports isSupportedLookAndFeel = false and is never instantiated - JIDE only uses it for instanceof comparisons against the active LAF, which always return false in our setup. On Windows JDKs the real class in java.desktop is loaded by the platform classloader and shadows this stub. --- .../plaf/windows/WindowsLookAndFeel.java | 81 +++++++++++++++++++ .../dsheirer/gui/theme/ThemeManager.java | 14 ++-- 2 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java diff --git a/src/main/java/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/src/main/java/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java new file mode 100644 index 00000000..8603623a --- /dev/null +++ b/src/main/java/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -0,0 +1,81 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2026 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ + +package com.sun.java.swing.plaf.windows; + +import javax.swing.LookAndFeel; +import javax.swing.UIDefaults; + +/** + * No-op stub for the JDK's Windows look-and-feel. + * + *

JIDE 3.6.18's {@code LookAndFeelFactory} contains compile-time + * {@code instanceof com.sun.java.swing.plaf.windows.WindowsLookAndFeel} checks throughout its + * dispatch logic. On Linux and macOS JDK builds that class is genuinely absent from + * {@code java.desktop}, so the JVM throws {@link NoClassDefFoundError} the first time JIDE's + * dispatch code executes the {@code instanceof} bytecode. That happens during + * {@link com.jidesoft.swing.JideSplitPane#updateUI()} on every LAF change. + * + *

This stub lives in the classpath so the class reference resolves on non-Windows JDKs. + * On Windows JDK builds the real {@code WindowsLookAndFeel} is loaded from the + * {@code java.desktop} module via the boot class loader and shadows this class. + * + *

The stub reports itself as not native and not supported - JIDE never instantiates this + * class, it only uses it for {@code instanceof} comparisons against the active LAF. Since + * sdrtrunk never installs this LAF, the {@code instanceof} check always returns false and + * JIDE proceeds to its next branch. + */ +public class WindowsLookAndFeel extends LookAndFeel +{ + @Override + public String getName() + { + return "Windows (stub)"; + } + + @Override + public String getID() + { + return "WindowsStub"; + } + + @Override + public String getDescription() + { + return "Compatibility stub for JIDE's instanceof checks on non-Windows JDKs."; + } + + @Override + public boolean isNativeLookAndFeel() + { + return false; + } + + @Override + public boolean isSupportedLookAndFeel() + { + return false; + } + + @Override + public UIDefaults getDefaults() + { + return new UIDefaults(); + } +} diff --git a/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java b/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java index 1384749d..f6f3cd9d 100644 --- a/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java +++ b/src/main/java/io/github/dsheirer/gui/theme/ThemeManager.java @@ -36,6 +36,7 @@ import javafx.application.Platform; import javafx.scene.Scene; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.plaf.metal.MetalLookAndFeel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -259,11 +260,14 @@ public class ThemeManager try { - //JIDE components (split panes, docking) need their UI delegates re-registered - //after every LAF change. Pass an explicit style: the no-arg version probes - //com.sun.java.swing.plaf.windows.WindowsLookAndFeel which is not present on - //non-Windows JDKs, throwing ClassNotFoundException at startup. - LookAndFeelFactory.installJideExtension(LookAndFeelFactory.VSNET_STYLE); + //JIDE 3.6.18 has compile-time `instanceof WindowsLookAndFeel` checks throughout + //LookAndFeelFactory. On Linux/macOS JDKs that class is not present at all, so any + //call path that reaches those checks fails with NoClassDefFoundError. The 3-arg + //overload lets us hand JIDE an explicit Metal LAF instance for *type detection* + //while leaving the real (FlatLaf) defaults table in place; JIDE's Metal branch + //matches first and the Windows branch is never reached. + LookAndFeelFactory.installJideExtension(UIManager.getLookAndFeelDefaults(), + new MetalLookAndFeel(), LookAndFeelFactory.VSNET_STYLE); } catch(Throwable t) {