mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
* Add dark mode toggle under User Preferences > Application
- Add darkMode boolean preference to ApplicationPreference (persisted via java.util.prefs)
- Add ToggleSwitch labeled "Dark Mode" to ApplicationPreferenceEditor
- Add ThemeManager singleton that listens for preference updates and applies/removes
the dark stylesheet to all registered JavaFX Scenes and swaps the Swing LAF
- Add sdrtrunk_dark.css with dark styling for controls, tables, menus, dialogs, charts
- Add FlatLaf dependency for full Swing dark theming (replaces previous OS-specific
MetalLookAndFeel setup; FlatLaf works cross-platform)
- Register all Stages created in JavaFxWindowManager with the ThemeManager and walk
Window.getWindows() at apply-time so unregistered dialogs/alerts also pick up the theme
- Add Foojay toolchain resolver to settings.gradle so Gradle can auto-provision JDK 25
* Fix dark-mode startup failures on Linux
- Apply Swing LAF synchronously on the calling thread so the main JFrame's RootPane
has a populated UIDefaults set when constructed. Previously the LAF install was
scheduled via EventQueue.invokeLater, so JFrame construction raced ahead and threw
"no ComponentUI class for: javax.swing.JPanel".
- Call LookAndFeelFactory.installJideExtension(VSNET_STYLE) with an explicit style
instead of the no-arg auto-detection variant. The auto-detection path probes
com.sun.java.swing.plaf.windows.WindowsLookAndFeel via Class.forName, which is
absent on non-Windows JDKs and threw ClassNotFoundException at startup.
- Bounce only the SwingUtilities.updateComponentTreeUI walk through the EDT so it
redraws already-realized windows on theme toggle.
* 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.
* Re-apply FlatLaf defaults after JIDE install + strengthen FX borders
Symptom: With dark mode enabled, the Swing main window's spectrum chrome and
menu bar were dark (FlatLaf) but tables, tabbed panes and the inner content
area were light/Metal coloured.
Root cause: LookAndFeelFactory.installJideExtension installs JIDE's
Metal-flavored UI defaults (VsnetMetalUtils etc.) into the live UIDefaults
table, overwriting FlatLaf's standard component colors and fonts for keys
like Table.background, TabbedPane.foreground, Panel.background.
Fix: After the JIDE install, walk a fresh FlatLaf UIDefaults table and put
every key back into the active defaults, skipping anything Jide-prefixed.
JIDE's UI delegate registrations live under Jide* keys that FlatLaf does
not define, so they survive untouched while the standard Swing color and
font keys are restored to their dark equivalents.
Also strengthen the JavaFX dark stylesheet: explicit borders on text fields,
tables, titled panes, split-pane dividers and separators so components have
more defining outlines in dark mode.
* Force-override Swing foreground colors via UIManager user-defaults layer
Symptom: With dark mode enabled the inner Swing content area now had dark
panels (good) but tab labels, table cells, status-bar labels and button
text were rendering nearly black-on-dark (bad).
Root cause: UIManager.getLookAndFeelDefaults() lookups for FlatLaf's
component foreground keys still resolve to Metal-era dark values after the
JIDE install + FlatLaf re-apply, because some FlatLaf keys are derived /
LazyValue and recompute against state that JIDE seeded.
Fix: After the bulk FlatLaf-defaults copy, explicitly UIManager.put the
foreground / background / selection / disabled / border colors for every
standard Swing component family. UIManager.put writes to the user-defaults
layer that takes precedence over the LAF defaults, so these overrides win
regardless of what JIDE or derived-value resolution produces.
Also bump the JavaFX dark stylesheet text colour from #d6d6d6 to #ececec so
labels read closer to white on the dark panels.
* Theme JIDE-specific UIManager keys so JideTabbedPane reads dark colors
The Now Playing / Map / Tuners / Playlist Editor / Details / Events /
Messages / Channel tabs are JIDE JideTabbedPane components and their UI
delegate reads JideTabbedPane.* UIManager keys, not the standard
TabbedPane.* keys. The previous overrides only set the standard keys so
the tabs were still rendering with JIDE's Metal-era dark foreground on
the now-dark background.
Override the JIDE-specific text foreground keys
(JideTabbedPane.foreground, .selectedTabTextForeground,
.unselectedTabTextForeground, .activeTabTextForeground) along with the
matching background and shadow keys. Also theme JideSplitPane,
JideButton and JideLabel keys for consistency.
* Remove hardcoded Color.BLACK foreground on JideTabbedPanes
ControllerPanel.getTabbedPane() and NowPlayingPanel.getTabbedPane() each
called setForeground(Color.BLACK) on their JideTabbedPane instances.
Color.BLACK is a plain java.awt.Color, not a ColorUIResource, so it is
never replaced by LookAndFeel.installColorsAndFont() on subsequent UI
installs - it permanently pins the tab labels to black regardless of any
JIDE or FlatLaf defaults the theme manager sets. In dark mode this leaves
the tab labels black-on-dark and unreadable.
Drop the explicit foreground calls so the LAF (JideTabbedPaneUI reading
JideTabbedPane.foreground / .unselectedTabTextForeground /
.selectedTabTextForeground / .activeTabTextForeground) drives the colour
in both light and dark modes.
* Register Channel tab's JFXPanel scenes with the ThemeManager
The Channel tab embeds two JavaFX scenes inside Swing via JFXPanel: the
noise squelch chart (NoiseSquelchView) and the symbol view. Neither was
registered with the ThemeManager, so the dark stylesheet was never applied
to them. In dark mode the chart legend, axis area and surrounding panel
rendered with JavaFX's default light Modena styling against the otherwise
dark UI.
JFXPanel-hosted scenes are invisible to javafx.stage.Window.getWindows(),
so the theme manager cannot find them automatically on toggle - they have
to be explicitly registered at creation time, the same as the JavaFX
Stages JavaFxWindowManager already registers.
* Use ColorUIResource for theme overrides + brighten chart series colors
Toggle-off problem: applyExplicitOverrides was storing plain java.awt.Color
values via UIManager.put. LookAndFeel.installColorsAndFont only replaces a
component's existing colour when it is null or an instance of UIResource -
plain Colors are not, so once a component picked up its dark colour it kept
that colour for the rest of the JVM lifetime even after toggling back to
light mode. Switching every override to ColorUIResource lets subsequent
updateComponentTreeUI calls replace the cached colour cleanly.
Chart visibility: the JavaFX charts on the Channel tab (NoiseSquelchView)
were inheriting Modena's default 8-colour series palette, which has several
hues that read poorly against a dark plot background. The existing
sdrtrunk_style.css also hardcodes .chart-symbol to solid black. Override
.default-color0..7.chart-series-line and matching .chart-line-symbol selectors
with a brighter high-contrast palette, darken the plot background to #1c1c1c
for stronger contrast against the grid lines, and add explicit colours for
axis tick marks, alternate row fills and legend border.
* Use primary foreground for selected tab text instead of selection foreground
The previous overrides set JideTabbedPane.selectedTabTextForeground and
TabbedPane.selectedForeground to fgSelection (always white). Selected tabs
render on the raised surface (bgRaised) which is white in light mode, so
white-on-white made selected tab labels invisible.
Selected tabs are not list/table row selections that use the blue
bgSelection background - they sit on the same raised tone as other surfaces.
Use fgPrimary so the text is dark on the light raised surface in light mode
and light on the dark raised surface in dark mode.
* Brighten chart data points to solid white in dark mode
* Restore default chart symbol size, only override colour
* Refresh detached Channel-tab swing components on theme toggle
The Channel tab swaps one of three right-pane components into its
JSplitPane depending on the active decoder type: SignalPowerView for AM,
the NoiseSquelchPanel JFXPanel for NBFM, the SymbolPanel JFXPanel for
feedback decoders. Only one is attached at any moment - the other two
are detached references on the ChannelSpectrumPanel.
SwingUtilities.updateComponentTreeUI(Window) during a theme toggle walks
only the currently-realized component trees, so the detached swappables
miss the refresh and carry stale colours from the LAF that was active
when they were last attached. Swapping back in then shows e.g. an AM
SignalPowerView in dark colours after a light-mode toggle.
Add ThemeManager.registerSwing(Component) tracking, walk those refs on
every toggle, and register the three swappables when constructed.
* Add preset themes selector under User Preferences > Application
Replace the single Dark Mode toggle with a curated selection of 9 preset
themes, exposed as a vertical radio-button group:
Light IntelliJ Nord
Dark Darcula Dracula
Solarized Light Solarized Dark Monokai Pro
Each Theme enum value pairs a human-readable display name with a FlatLaf
class name and a dark flag. The first four come from the core flatlaf
dependency; the others come from flatlaf-intellij-themes which is added
as a new implementation dependency (~1MB).
ApplicationPreference now stores the selected theme by enum name under
the new ui.theme key. isDarkMode() is retained as a compatibility shim
that delegates to the current theme. Existing users with the legacy
dark.mode boolean are auto-migrated on first read.
ThemeManager is refactored to drive the LAF from the Theme:
- Reflectively instantiates the FlatLaf class.
- Reads Panel.background, Button.background, TextField.background,
List.selectionBackground/Foreground, Label.foreground/disabledForeground,
Component.borderColor and Table.alternateRowColor from UIManager *after*
FlatLaf installs, then seeds those values into the JIDE-specific keys.
Each theme automatically contributes its own palette to JIDE widgets
without per-theme hardcoded colours.
ApplicationPreferenceEditor swaps the ToggleSwitch for a ToggleGroup of
RadioButtons that persist the selection through ApplicationPreference.setTheme.
* Fix theme switching past first toggle + move Themes to Appearance sub-page
Critical regression in 56b0f6c9: applyExplicitOverrides reads colours via
UIManager.getColor(...) which consults the merged defaults (user defaults
overlay LAF defaults). After the first apply our own UIManager.put calls
populate the user-defaults layer, so subsequent reads return our previous
theme's colours instead of the newly-installed LAF's colours. The result:
the first theme switch works, every switch after that silently re-applies
the previous palette and the UI appears frozen on the first picked theme.
Switch every palette read to UIManager.getLookAndFeelDefaults().getColor(...)
which is the LAF-only layer and ignores the user-defaults overlay we wrote.
Also:
- Move the Themes selector to its own sub-node:
User Preferences > Application > Appearance
PreferenceEditorType gets a new APPEARANCE entry, the existing APPLICATION
leaf is relabelled "General" so the tree no longer reads "Application >
Application > Application". New AppearancePreferenceEditor hosts the theme
radio group; ApplicationPreferenceEditor loses it.
- Replace a few hardcoded label foregrounds that read as
invisible-on-dark-bg with LAF-driven equivalents:
TunerViewPanel TunerStatus.DISABLED: DARK_GRAY -> Label.disabledForeground
BroadcastStatusPanel DISABLED state: LIGHT_GRAY -> Label.disabledForeground
JFrequencyControl Digit mouseExited: Color.WHITE -> TextField.background
Semantic colour usage (RED/YELLOW for error and warning states, the
audio-panel hardware-style black backgrounds) is intentionally kept.
* Auto-theme every JavaFX window so transient dialogs follow the theme
The Playlist Editor's main Scene is already registered with the ThemeManager,
but the many Alert / TextInputDialog / RadioReferenceUnavailableAlert popups
it spawns each create a fresh Stage with a fresh Scene that nothing registers.
Once one is shown after a theme switch it renders with default Modena styling
on top of an otherwise themed app.
Install a ListChangeListener on javafx.stage.Window.getWindows() at startup.
Every newly-added Window is themed immediately if its Scene is set, otherwise
a one-shot sceneProperty listener applies the theme the moment the Scene is
attached. Already-open windows at install time are also walked once.
This removes the need to manually register every transient dialog and covers
the playlist editor, alias editor confirmations, broadcastify error alerts,
channel-map editor warnings, and any future ad-hoc Alert / Dialog the
codebase grows.
* Curate themes to 8 modern variants + per-theme JavaFX accent colours
Theme set is trimmed and refocused:
Drop: IntelliJ (too similar to Light), FlatDarculaLaf (duplicates Dracula),
Monokai Pro (low identity in this app).
Add: Material Deep Ocean (vibrant teal dark), Arc Dark (flat modern dark).
Keep: Light, Dark, Nord, Dracula (FlatDraculaIJTheme - the one user liked),
Solarized Light, Solarized Dark.
Each theme now contributes its own accent palette to the JavaFX panels too,
not just the Swing chrome. ThemeManager builds a small per-theme stylesheet
at apply time by reading the FlatLaf-installed colours (Panel.background,
TextField.background, Component.accentColor, List.selectionBackground/Foreground,
Label.foreground, Component.borderColor, Table.alternateRowColor) and
substituting them into Modena's core variables (-fx-base, -fx-accent,
-fx-selection-bar, -fx-control-inner-background, etc.) plus a handful of
explicit rules for table selection rows, default button, scroll bar thumb.
The generated CSS is attached to every registered Scene as a data: URL, so
Nord reads blue-tinted, Dracula purple-tinted, Solarized cream/teal,
Material Deep Ocean dark teal, Arc Dark cool gray. The static
sdrtrunk_dark.css remains as the structural baseline for all dark themes.
* Add GUI scale slider under Appearance preferences
New slider in User Preferences > Application > Appearance lets users zoom
the whole UI between 50% and 200% in 10% increments. The value is
persisted as a new ApplicationPreference (ui.gui.scale, default 1.0,
bounded 0.5-2.0) and applied immediately on slider release - intermediate
drag positions do not trigger a re-apply so the LAF reinstall cost is
paid once at the end of the drag.
Swing side: ThemeManager sets the flatlaf.uiScale system property before
each setLookAndFeel call, so the new FlatLaf instance picks up the user's
zoom on initialise. All component sizes (fonts, padding, icons) scale
together because FlatLaf does the math internally.
JavaFX side: the per-theme accent stylesheet emits an
.root { -fx-font-size: <12 * scale>px; }
rule. Modena's component sizes are em-relative so cascading the font
size scales the JavaFX panels in sync with the Swing chrome.
The onPreferenceUpdated guard now compares both theme and scale, so
moving only the slider still triggers a re-apply. A Reset button next
to the slider returns to 100%.
544 lines
18 KiB
Java
544 lines
18 KiB
Java
/*
|
|
* *****************************************************************************
|
|
* Copyright (C) 2014-2024 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 <http://www.gnu.org/licenses/>
|
|
* ****************************************************************************
|
|
*/
|
|
|
|
package io.github.dsheirer.gui;
|
|
|
|
import com.google.common.eventbus.Subscribe;
|
|
import io.github.dsheirer.alias.AliasModel;
|
|
import io.github.dsheirer.controller.channel.map.ChannelMap;
|
|
import io.github.dsheirer.controller.channel.map.ChannelRange;
|
|
import io.github.dsheirer.eventbus.MyEventBus;
|
|
import io.github.dsheirer.gui.icon.IconManager;
|
|
import io.github.dsheirer.gui.icon.ViewIconManagerRequest;
|
|
import io.github.dsheirer.gui.playlist.PlaylistEditor;
|
|
import io.github.dsheirer.gui.playlist.PlaylistEditorRequest;
|
|
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
|
|
import io.github.dsheirer.gui.playlist.channelMap.ChannelMapEditor;
|
|
import io.github.dsheirer.gui.playlist.channelMap.ViewChannelMapEditorRequest;
|
|
import io.github.dsheirer.gui.preference.PreferenceEditorType;
|
|
import io.github.dsheirer.gui.preference.UserPreferencesEditor;
|
|
import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest;
|
|
import io.github.dsheirer.gui.preference.calibration.CalibrationDialog;
|
|
import io.github.dsheirer.gui.theme.ThemeManager;
|
|
import io.github.dsheirer.gui.viewer.MessageRecordingViewer;
|
|
import io.github.dsheirer.gui.viewer.ViewRecordingViewerRequest;
|
|
import io.github.dsheirer.icon.IconModel;
|
|
import io.github.dsheirer.jmbe.JmbeEditor;
|
|
import io.github.dsheirer.jmbe.JmbeEditorRequest;
|
|
import io.github.dsheirer.module.log.EventLogManager;
|
|
import io.github.dsheirer.monitor.ResourceMonitor;
|
|
import io.github.dsheirer.monitor.StatusBox;
|
|
import io.github.dsheirer.playlist.PlaylistManager;
|
|
import io.github.dsheirer.preference.UserPreferences;
|
|
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
|
import javafx.application.Application;
|
|
import javafx.application.Platform;
|
|
import javafx.embed.swing.JFXPanel;
|
|
import javafx.scene.Scene;
|
|
import javafx.stage.Stage;
|
|
import jiconfont.javafx.IconFontFX;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* Java FX window manager. Handles all secondary Java FX windows that are used within this primarily
|
|
* Swing application.
|
|
*/
|
|
public class JavaFxWindowManager extends Application
|
|
{
|
|
private final static Logger mLog = LoggerFactory.getLogger(JavaFxWindowManager.class);
|
|
|
|
public static final String CHANNEL_MAP_EDITOR = "channelmap";
|
|
public static final String ICON_MANAGER = "iconmanager";
|
|
public static final String PLAYLIST_EDITOR = "playlist";
|
|
public static final String USER_PREFERENCES_EDITOR = "preferences";
|
|
public static final String STAGE_MONITOR_KEY_CALIBRATION_DIALOG = "calibration.dialog";
|
|
public static final String STAGE_MONITOR_KEY_CHANNEL_MAP_EDITOR = "channel.map";
|
|
public static final String STAGE_MONITOR_KEY_RECORDING_VIEWER = "recording.viewer";
|
|
public static final String STAGE_MONITOR_KEY_ICON_MANAGER_EDITOR = "icon.manager";
|
|
public static final String STAGE_MONITOR_KEY_JMBE_EDITOR = "jmbe.editor";
|
|
public static final String STAGE_MONITOR_KEY_PLAYLIST_EDITOR = "playlist";
|
|
public static final String STAGE_MONITOR_KEY_USER_PREFERENCES_EDITOR = "user.preferences";
|
|
|
|
private JFXPanel mJFXPanel;
|
|
private ChannelMapEditor mChannelMapEditor;
|
|
private IconManager mIconManager;
|
|
private JmbeEditor mJmbeEditor;
|
|
private PlaylistEditor mPlaylistEditor;
|
|
private PlaylistManager mPlaylistManager;
|
|
private TunerManager mTunerManager;
|
|
private UserPreferences mUserPreferences;
|
|
private UserPreferencesEditor mUserPreferencesEditor;
|
|
private MessageRecordingViewer mMessageRecordingViewer;
|
|
|
|
private Stage mChannelMapStage;
|
|
private Stage mIconManagerStage;
|
|
private Stage mJmbeEditorStage;
|
|
private Stage mPlaylistStage;
|
|
private Stage mUserPreferencesStage;
|
|
private Stage mRecordingViewerStage;
|
|
private JFXPanel mStatusPanel;
|
|
|
|
/**
|
|
* Constructs an instance. Note: this constructor is used for Swing applications.
|
|
*/
|
|
public JavaFxWindowManager(UserPreferences userPreferences, TunerManager tunerManager, PlaylistManager playlistManager)
|
|
{
|
|
mUserPreferences = userPreferences;
|
|
mTunerManager = tunerManager;
|
|
mPlaylistManager = playlistManager;
|
|
|
|
setup();
|
|
}
|
|
|
|
/**
|
|
* Constructs an instance. Note: this constructor is used for standalone JavaFX application testing
|
|
*/
|
|
public JavaFxWindowManager()
|
|
{
|
|
mUserPreferences = new UserPreferences();
|
|
AliasModel aliasModel = new AliasModel();
|
|
EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences);
|
|
mTunerManager = new TunerManager(mUserPreferences);
|
|
mTunerManager.start();
|
|
mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, new IconModel());
|
|
mPlaylistManager.init();
|
|
setup();
|
|
}
|
|
|
|
/**
|
|
* Creates or accesses the JavaFX status panel, used by the main application GUI.
|
|
* @param resourceMonitor for statistics
|
|
* @return JFXPanel accessible on Swing thread that delegates JavaFX scene creation to the FX event thread.
|
|
*/
|
|
public JFXPanel getStatusPanel(ResourceMonitor resourceMonitor)
|
|
{
|
|
if(mStatusPanel == null)
|
|
{
|
|
mStatusPanel = new JFXPanel();
|
|
|
|
//JFXPanel has to be populated on the FX event thread
|
|
Platform.runLater(() -> {
|
|
Scene scene = new Scene(new StatusBox(resourceMonitor));
|
|
ThemeManager.getInstance().register(scene);
|
|
mStatusPanel.setScene(scene);
|
|
});
|
|
}
|
|
|
|
return mStatusPanel;
|
|
}
|
|
|
|
private void setup()
|
|
{
|
|
//Register this class to receive events via each method annotated with @Subscribe
|
|
MyEventBus.getGlobalEventBus().register(this);
|
|
|
|
//Register JavaFX icon fonts
|
|
IconFontFX.register(jiconfont.icons.font_awesome.FontAwesome.getIconFont());
|
|
|
|
createJFXPanel();
|
|
}
|
|
|
|
/**
|
|
* Executes the runnable on the JavaFX application thread
|
|
*/
|
|
private void execute(Runnable runnable)
|
|
{
|
|
createJFXPanel();
|
|
|
|
if(Platform.isFxApplicationThread())
|
|
{
|
|
runnable.run();
|
|
}
|
|
else
|
|
{
|
|
Platform.runLater(runnable);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a JavaFX panel for Swing application compatibility
|
|
*/
|
|
private void createJFXPanel()
|
|
{
|
|
if(mJFXPanel == null)
|
|
{
|
|
mJFXPanel = new JFXPanel();
|
|
Platform.setImplicitExit(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes monitoring for all JavaFX stages and shuts down the FX thread, killing all FX windows.
|
|
*/
|
|
public void shutdown()
|
|
{
|
|
MyEventBus.getGlobalEventBus().unregister(this);
|
|
mUserPreferences.getJavaFxPreferences().clearStageMonitors();
|
|
Platform.exit();
|
|
}
|
|
|
|
public CalibrationDialog getCalibrationDialog(UserPreferences userPreferences)
|
|
{
|
|
createJFXPanel();
|
|
return new CalibrationDialog(userPreferences);
|
|
}
|
|
|
|
/**
|
|
* Stage for the recording viewer
|
|
*/
|
|
public Stage getRecordingViewerStage()
|
|
{
|
|
if(mRecordingViewerStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getRecordingViewer(), 1100, 800);
|
|
ThemeManager.getInstance().register(scene);
|
|
mRecordingViewerStage = new Stage();
|
|
mRecordingViewerStage.setTitle("sdrtrunk - Message Recording Viewer (.bits)");
|
|
mRecordingViewerStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mRecordingViewerStage, STAGE_MONITOR_KEY_RECORDING_VIEWER);
|
|
}
|
|
|
|
return mRecordingViewerStage;
|
|
}
|
|
|
|
public MessageRecordingViewer getRecordingViewer()
|
|
{
|
|
if(mMessageRecordingViewer == null)
|
|
{
|
|
mMessageRecordingViewer = new MessageRecordingViewer();
|
|
}
|
|
|
|
return mMessageRecordingViewer;
|
|
}
|
|
|
|
public Stage getIconManagerStage()
|
|
{
|
|
if(mIconManagerStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getIconManager(), 500, 500);
|
|
ThemeManager.getInstance().register(scene);
|
|
mIconManagerStage = new Stage();
|
|
mIconManagerStage.setTitle("sdrtrunk - Icon Manager");
|
|
mIconManagerStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mIconManagerStage, STAGE_MONITOR_KEY_ICON_MANAGER_EDITOR);
|
|
}
|
|
|
|
return mIconManagerStage;
|
|
}
|
|
|
|
public IconManager getIconManager()
|
|
{
|
|
if(mIconManager == null)
|
|
{
|
|
mIconManager = new IconManager(mPlaylistManager.getIconModel());
|
|
}
|
|
|
|
return mIconManager;
|
|
}
|
|
|
|
/**
|
|
* Processes a JMBE s editor request
|
|
*/
|
|
@Subscribe
|
|
public void process(final JmbeEditorRequest request)
|
|
{
|
|
if(request.isCloseEditorRequest())
|
|
{
|
|
execute(() -> {
|
|
getJmbeEditorStage().hide();
|
|
mJmbeEditorStage = null;
|
|
mJmbeEditor = null;
|
|
});
|
|
}
|
|
else
|
|
{
|
|
execute(() -> {
|
|
restoreStage(getJmbeEditorStage());
|
|
getJmbeEditor().process(request);
|
|
});
|
|
}
|
|
}
|
|
|
|
public Stage getJmbeEditorStage()
|
|
{
|
|
if(mJmbeEditorStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getJmbeEditor(), 650, 650);
|
|
ThemeManager.getInstance().register(scene);
|
|
mJmbeEditorStage = new Stage();
|
|
mJmbeEditorStage.setTitle("sdrtrunk - JMBE Library Updater");
|
|
mJmbeEditorStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mJmbeEditorStage, STAGE_MONITOR_KEY_JMBE_EDITOR);
|
|
}
|
|
|
|
return mJmbeEditorStage;
|
|
}
|
|
|
|
public JmbeEditor getJmbeEditor()
|
|
{
|
|
if(mJmbeEditor == null)
|
|
{
|
|
mJmbeEditor = new JmbeEditor(mUserPreferences);
|
|
}
|
|
|
|
return mJmbeEditor;
|
|
}
|
|
|
|
/**
|
|
* Lazy construct and access the playlist editor
|
|
*/
|
|
public PlaylistEditor getPlaylistEditor()
|
|
{
|
|
if(mPlaylistEditor == null)
|
|
{
|
|
mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mTunerManager, mUserPreferences);
|
|
}
|
|
|
|
return mPlaylistEditor;
|
|
}
|
|
|
|
/**
|
|
* Access the playlist stage.
|
|
*/
|
|
private Stage getPlaylistStage()
|
|
{
|
|
if(mPlaylistStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getPlaylistEditor(), 1000, 750);
|
|
ThemeManager.getInstance().register(scene);
|
|
mPlaylistStage = new Stage();
|
|
mPlaylistStage.setTitle("sdrtrunk - Playlist Editor");
|
|
mPlaylistStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mPlaylistStage, STAGE_MONITOR_KEY_PLAYLIST_EDITOR);
|
|
}
|
|
|
|
return mPlaylistStage;
|
|
}
|
|
|
|
/**
|
|
* Processes a playlist editor request and brings the playlist editor into focus
|
|
*/
|
|
@Subscribe
|
|
public void process(PlaylistEditorRequest request)
|
|
{
|
|
execute(() -> {
|
|
try
|
|
{
|
|
restoreStage(getPlaylistStage());
|
|
getPlaylistEditor().process(request);
|
|
}
|
|
catch(Throwable t)
|
|
{
|
|
mLog.error("Error processing show playlist editor request", t);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* User Preferences Editor
|
|
*/
|
|
private UserPreferencesEditor getUserPreferencesEditor()
|
|
{
|
|
if(mUserPreferencesEditor == null)
|
|
{
|
|
mUserPreferencesEditor = new UserPreferencesEditor(mUserPreferences);
|
|
}
|
|
|
|
return mUserPreferencesEditor;
|
|
}
|
|
|
|
/**
|
|
* User Preferences Stage
|
|
*/
|
|
private Stage getUserPreferencesStage()
|
|
{
|
|
if(mUserPreferencesStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getUserPreferencesEditor(), 900, 500);
|
|
ThemeManager.getInstance().register(scene);
|
|
mUserPreferencesStage = new Stage();
|
|
mUserPreferencesStage.setTitle("sdrtrunk - User Preferences");
|
|
mUserPreferencesStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mUserPreferencesStage, STAGE_MONITOR_KEY_USER_PREFERENCES_EDITOR);
|
|
}
|
|
|
|
return mUserPreferencesStage;
|
|
}
|
|
|
|
/**
|
|
* Processes a user preferences editor request
|
|
*/
|
|
@Subscribe
|
|
public void process(final ViewUserPreferenceEditorRequest request)
|
|
{
|
|
execute(() -> {
|
|
restoreStage(getUserPreferencesStage());
|
|
getUserPreferencesEditor().process(request);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Channel Map Editor
|
|
*/
|
|
private ChannelMapEditor getChannelMapEditor()
|
|
{
|
|
if(mChannelMapEditor == null)
|
|
{
|
|
mChannelMapEditor = new ChannelMapEditor(mPlaylistManager.getChannelMapModel());
|
|
}
|
|
|
|
return mChannelMapEditor;
|
|
}
|
|
|
|
/**
|
|
* Channel Map Stage
|
|
*/
|
|
private Stage getChannelMapStage()
|
|
{
|
|
if(mChannelMapStage == null)
|
|
{
|
|
createJFXPanel();
|
|
Scene scene = new Scene(getChannelMapEditor(), 500, 500);
|
|
ThemeManager.getInstance().register(scene);
|
|
mChannelMapStage = new Stage();
|
|
mChannelMapStage.setTitle("sdrtrunk - Channel Map Editor");
|
|
mChannelMapStage.setScene(scene);
|
|
mUserPreferences.getJavaFxPreferences().monitor(mChannelMapStage, STAGE_MONITOR_KEY_CHANNEL_MAP_EDITOR);
|
|
}
|
|
|
|
return mChannelMapStage;
|
|
}
|
|
|
|
@Subscribe
|
|
public void process(final ViewIconManagerRequest request)
|
|
{
|
|
execute(() -> restoreStage(getIconManagerStage()));
|
|
}
|
|
|
|
/**
|
|
* Process a channel map editor request
|
|
*/
|
|
@Subscribe
|
|
public void process(final ViewChannelMapEditorRequest request)
|
|
{
|
|
execute(() -> {
|
|
restoreStage(getChannelMapStage());
|
|
getChannelMapEditor().process(request);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Process a channel map editor request
|
|
*/
|
|
@Subscribe
|
|
public void process(final ViewRecordingViewerRequest request)
|
|
{
|
|
execute(() -> restoreStage(getRecordingViewerStage()));
|
|
}
|
|
|
|
/**
|
|
* Restores the stage to previous size and location.
|
|
* @param stage to restore.
|
|
*/
|
|
private void restoreStage(Stage stage)
|
|
{
|
|
stage.setIconified(false);
|
|
stage.show();
|
|
stage.requestFocus();
|
|
stage.toFront();
|
|
}
|
|
|
|
@Override
|
|
public void start(Stage primaryStage) throws Exception
|
|
{
|
|
mLog.debug("Starting ...");
|
|
Parameters parameters = getParameters();
|
|
mLog.debug("Parameters: " + (parameters != null));
|
|
|
|
boolean valid = false;
|
|
|
|
if(parameters != null && parameters.getRaw().size() == 1)
|
|
{
|
|
String window = parameters.getRaw().get(0);
|
|
|
|
if(window != null)
|
|
{
|
|
switch(window)
|
|
{
|
|
case CHANNEL_MAP_EDITOR:
|
|
//Generate some test data for the editor
|
|
ChannelMap channelMap1 = new ChannelMap("Test Map 1");
|
|
channelMap1.addRange(new ChannelRange(1,199,150000000, 12500));
|
|
channelMap1.addRange(new ChannelRange(200,299,160000000, 25000));
|
|
channelMap1.addRange(new ChannelRange(300,399,170000000, 12500));
|
|
channelMap1.addRange(new ChannelRange(400,499,180000000, 25000));
|
|
mPlaylistManager.getChannelMapModel().addChannelMap(channelMap1);
|
|
|
|
ChannelMap channelMap2 = new ChannelMap("Test Map 2");
|
|
channelMap2.addRange(new ChannelRange(1,199,450000000, 12500));
|
|
channelMap2.addRange(new ChannelRange(200,299,460000000, 25000));
|
|
channelMap2.addRange(new ChannelRange(300,399,470000000, 12500));
|
|
channelMap2.addRange(new ChannelRange(400,499,480000000, 25000));
|
|
mPlaylistManager.getChannelMapModel().addChannelMap(channelMap2);
|
|
valid = true;
|
|
process(new ViewChannelMapEditorRequest());
|
|
break;
|
|
case ICON_MANAGER:
|
|
valid = true;
|
|
process(new ViewIconManagerRequest());
|
|
break;
|
|
case PLAYLIST_EDITOR:
|
|
valid = true;
|
|
process(new ViewPlaylistRequest());
|
|
break;
|
|
case USER_PREFERENCES_EDITOR:
|
|
valid = true;
|
|
process(new ViewUserPreferenceEditorRequest(PreferenceEditorType.DEFAULT));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!valid)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("An argument is required to launch JavaFX windows from this window manager. " +
|
|
"Valid options are:\n\tchannelmap\tChannel Map Editor\n\ticonmanager\tIcon Manager\n\tplaylist\tPlaylist Editor\n" +
|
|
"\tpreferences\tUser Preferences Editor\n");
|
|
sb.append("Supplied Argument(s): ").append(parameters.getRaw());
|
|
|
|
mLog.error(sb.toString());
|
|
}
|
|
}
|
|
|
|
public static void main(String[] args)
|
|
{
|
|
mLog.info("Application Start - Parameters: " + args);
|
|
launch(args);
|
|
}
|
|
}
|