001package apps; 002 003import static jmri.util.gui.GuiLafPreferencesManager.MIN_FONT_SIZE; 004 005import java.awt.FlowLayout; 006import java.awt.event.ActionEvent; 007import java.awt.event.ItemEvent; 008import java.text.MessageFormat; 009import java.util.Arrays; 010import java.util.HashMap; 011import java.util.Locale; 012import javax.swing.BoxLayout; 013import javax.swing.ButtonGroup; 014import javax.swing.DefaultComboBoxModel; 015import javax.swing.JButton; 016import javax.swing.JCheckBox; 017import javax.swing.JComboBox; 018import javax.swing.JComponent; 019import javax.swing.JLabel; 020import javax.swing.JPanel; 021import javax.swing.JRadioButton; 022import javax.swing.JSpinner; 023import javax.swing.SpinnerNumberModel; 024import javax.swing.SwingUtilities; 025import javax.swing.UIManager; 026import javax.swing.event.ChangeEvent; 027import jmri.InstanceManager; 028import jmri.profile.Profile; 029import jmri.profile.ProfileManager; 030import jmri.swing.PreferencesPanel; 031import jmri.util.gui.GuiLafPreferencesManager; 032import jmri.util.swing.JComboBoxUtil; 033import org.openide.util.lookup.ServiceProvider; 034 035/** 036 * Provide GUI to configure Swing GUI LAF defaults 037 * <p> 038 * Provides GUI configuration for SWING LAF by displaying radio buttons for each 039 * LAF implementation available. This information is then persisted separately 040 * by the {@link jmri.util.gui.GuiLafPreferencesManager}. 041 * <p> 042 * Locale default language and country is also considered a GUI (and perhaps 043 * LAF) configuration item. 044 * 045 * @author Bob Jacobsen Copyright (C) 2001, 2003, 2010 046 * @since 2.9.5 (Previously in jmri package) 047 */ 048@ServiceProvider(service = PreferencesPanel.class) 049public final class GuiLafConfigPane extends JPanel implements PreferencesPanel { 050 051 public static final int MAX_TOOLTIP_TIME = 3600; 052 public static final int MIN_TOOLTIP_TIME = 1; 053 054 /** 055 * Smallest font size shown to a user ({@value}). 056 * 057 * @see GuiLafPreferencesManager#MIN_FONT_SIZE 058 */ 059 public static final int MIN_DISPLAYED_FONT_SIZE = MIN_FONT_SIZE; 060 /** 061 * Largest font size shown to a user ({@value}). 062 * 063 * @see GuiLafPreferencesManager#MAX_FONT_SIZE 064 */ 065 public static final int MAX_DISPLAYED_FONT_SIZE = 20; 066 067 private final JComboBox<String> localeBox = new JComboBox<>(new String[]{ 068 Locale.getDefault().getDisplayName(), 069 "(Please Wait)"}); 070 private final HashMap<String, Locale> locale = new HashMap<>(); 071 private final ButtonGroup LAFGroup = new ButtonGroup(); 072 public JCheckBox mouseEvent; 073 private JComboBox<Integer> fontSizeComboBox; 074 public JCheckBox graphicStateDisplay; 075 public JCheckBox tabbedOblockEditor; 076 public JCheckBox editorUseOldLocSizeDisplay; 077 public ButtonGroup fileChooserGroup= new ButtonGroup(); 078 public JCheckBox force100percentScaling; 079 080 public GuiLafConfigPane() { 081 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 082 JPanel p; 083 doLAF(p = new JPanel()); 084 add(p); 085 doFontSize(p = new JPanel()); 086 add(p); 087 doFileChooserLayoutType(p = new JPanel()); 088 add(p); 089 doClickSelection(p = new JPanel()); 090 add(p); 091 doGraphicState(p = new JPanel()); 092 add(p); 093 doTabbedOblockEditor(p = new JPanel()); 094 add(p); 095 doEditorUseOldLocSize(p = new JPanel()); 096 add(p); 097 doForce100percentScaling(p = new JPanel()); 098 add(p); 099 doMaxComboRows(p = new JPanel()); 100 add(p); 101 doToolTipDismissDelay(p = new JPanel()); 102 add(p); 103 } 104 105 void doClickSelection(JPanel panel) { 106 panel.setLayout(new FlowLayout()); 107 mouseEvent = new JCheckBox(ConfigBundle.getMessage("GUIButtonNonStandardRelease")); 108 mouseEvent.addItemListener((ItemEvent e) -> { 109 InstanceManager.getDefault(GuiLafPreferencesManager.class).setNonStandardMouseEvent(mouseEvent.isSelected()); 110 }); 111 panel.add(mouseEvent); 112 } 113 114 void doGraphicState(JPanel panel) { 115 panel.setLayout(new FlowLayout()); 116 graphicStateDisplay = new JCheckBox(ConfigBundle.getMessage("GUIGraphicTableState")); 117 graphicStateDisplay.setSelected(InstanceManager.getDefault(GuiLafPreferencesManager.class).isGraphicTableState()); 118 graphicStateDisplay.addItemListener((ItemEvent e) -> { 119 InstanceManager.getDefault(GuiLafPreferencesManager.class).setGraphicTableState(graphicStateDisplay.isSelected()); 120 }); 121 panel.add(graphicStateDisplay); 122 } 123 124 void doTabbedOblockEditor(JPanel panel) { 125 panel.setLayout(new FlowLayout()); 126 tabbedOblockEditor = new JCheckBox(ConfigBundle.getMessage("GUITabbedOblockEditor")); 127 tabbedOblockEditor.setSelected(InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed()); 128 tabbedOblockEditor.setToolTipText(ConfigBundle.getMessage("GUIToolTipTabbedEdit")); 129 tabbedOblockEditor.addItemListener((ItemEvent e) -> { 130 InstanceManager.getDefault(GuiLafPreferencesManager.class).setOblockEditTabbed(tabbedOblockEditor.isSelected()); 131 }); 132 panel.add(tabbedOblockEditor); 133 } 134 135 void doEditorUseOldLocSize(JPanel panel) { 136 panel.setLayout(new FlowLayout()); 137 editorUseOldLocSizeDisplay = new JCheckBox(ConfigBundle.getMessage("GUIUseOldLocSize")); 138 editorUseOldLocSizeDisplay.setSelected(InstanceManager.getDefault(GuiLafPreferencesManager.class).isEditorUseOldLocSize()); 139 editorUseOldLocSizeDisplay.addItemListener((ItemEvent e) -> { 140 InstanceManager.getDefault(GuiLafPreferencesManager.class).setEditorUseOldLocSize(editorUseOldLocSizeDisplay.isSelected()); 141 }); 142 panel.add(editorUseOldLocSizeDisplay); 143 } 144 145 void doFileChooserLayoutType(JPanel panel) { 146 panel.setLayout(new FlowLayout()); 147 JLabel fileChooserLabel = new JLabel(ConfigBundle.getMessage("GUIJChooserUseOption")); 148 panel.add(fileChooserLabel); 149 // make the radio buttons 150 JRadioButton jButDefault = new JRadioButton(ConfigBundle.getMessage("GUIJChooserUseDefault")); 151 panel.add(jButDefault); 152 fileChooserGroup.add(jButDefault); 153 jButDefault.addActionListener((ActionEvent e) -> { 154 if (((JRadioButton)e.getSource()).isSelected() ) { 155 InstanceManager.getDefault(GuiLafPreferencesManager.class).setJFileChooserFormat(0); 156 } 157 }); 158 JRadioButton jButList = new JRadioButton(ConfigBundle.getMessage("GUIJChooserUseList")); 159 panel.add(jButList); 160 fileChooserGroup.add(jButList); 161 jButList.addActionListener((ActionEvent e) -> { 162 if (((JRadioButton)e.getSource()).isSelected() ) { 163 InstanceManager.getDefault(GuiLafPreferencesManager.class).setJFileChooserFormat(1); 164 } 165 }); 166 JRadioButton jButDetail = new JRadioButton(ConfigBundle.getMessage("GUIJChooserUseDetail")); 167 panel.add(jButDetail); 168 fileChooserGroup.add(jButDetail); 169 jButDetail.addActionListener((ActionEvent e) -> { 170 if (((JRadioButton)e.getSource()).isSelected() ) { 171 InstanceManager.getDefault(GuiLafPreferencesManager.class).setJFileChooserFormat(2); 172 } 173 }); 174 switch (InstanceManager.getDefault(GuiLafPreferencesManager.class).getJFileChooserFormat()) { 175 case 0: 176 jButDefault.setSelected(true); 177 break; 178 case 1: 179 jButList.setSelected(true); 180 break; 181 case 2: 182 jButDetail.setSelected(true); 183 break; 184 default: 185 jButDefault.setSelected(true); 186 } 187 } 188 189 void doForce100percentScaling(JPanel panel) { 190 jmri.util.EarlyInitializationPreferences eip = 191 jmri.util.EarlyInitializationPreferences.getInstance(); 192 193 panel.setLayout(new FlowLayout()); 194 force100percentScaling = new JCheckBox(ConfigBundle.getMessage("GUIForce100percentScaling")); 195 force100percentScaling.setSelected(eip.getGUIForce100percentScaling()); 196 force100percentScaling.addItemListener((ItemEvent e) -> { 197 eip.setGUIForce100percentScaling(force100percentScaling.isSelected()); 198 }); 199 panel.add(force100percentScaling); 200 } 201 202 void doLAF(JPanel panel) { 203 // find L&F definitions from Swing 204 panel.setLayout(new FlowLayout()); 205 UIManager.LookAndFeelInfo[] plafs = UIManager.getInstalledLookAndFeels(); 206 HashMap<String, String> installedLAFs = new HashMap<>(plafs.length); 207 for (UIManager.LookAndFeelInfo plaf : plafs) { 208 installedLAFs.put(plaf.getName(), plaf.getClassName()); 209 } 210 211 // explicitly add the desired DarkLaf LaFs 212 installedLAFs.put("DarkLAF HC", "com.github.weisj.darklaf.theme.HighContrastDarkTheme"); 213 // The LaFs available are 214 // com/github/weisj/darklaf/theme/DarculaTheme 215 // com/github/weisj/darklaf/theme/HighContrastDarkTheme 216 // com/github/weisj/darklaf/theme/HighContrastLightTheme 217 // com/github/weisj/darklaf/theme/IntelliJTheme 218 // com/github/weisj/darklaf/theme/OneDarkTheme 219 // com/github/weisj/darklaf/theme/SolarizedDarkTheme 220 // com/github/weisj/darklaf/theme/SolarizedLightTheme 221 // But we're only listing a subset to avoid overwhelming the user 222 // See GuiLafPreferencesManager.applyLookAndFeel(..) for matching code 223 224 // make the radio buttons 225 for (java.util.Map.Entry<String, String> entry : installedLAFs.entrySet()) { 226 String name = entry.getKey(); 227 JRadioButton jmi = new JRadioButton(name); 228 panel.add(jmi); 229 LAFGroup.add(jmi); 230 jmi.setActionCommand(name); 231 jmi.addActionListener((ActionEvent e) -> { 232 InstanceManager.getDefault(GuiLafPreferencesManager.class).setLookAndFeel(installedLAFs.get(name)); 233 }); 234 if ( entry.getValue().equals(UIManager.getLookAndFeel().getClass().getName())) { 235 jmi.setSelected(true); 236 } 237 } 238 } 239 240 /** 241 * Create and return a JPanel for configuring default local. 242 * <p> 243 * Most of the action is handled in a separate thread, which replaces the 244 * contents of a JComboBox when the list of Locales is available. 245 * 246 * @return the panel 247 */ 248 public JPanel doLocale() { 249 JPanel panel = new JPanel(); 250 // add JComboBoxen for language and country 251 panel.setLayout(new FlowLayout()); 252 panel.add(localeBox); 253 JComboBoxUtil.setupComboBoxMaxRows(localeBox); 254 255 // create object to find locales in new Thread 256 Runnable r = () -> { 257 Locale[] locales = Locale.getAvailableLocales(); 258 String[] localeNames = new String[locales.length]; 259 for (int i = 0; i < locales.length; i++) { 260 locale.put(locales[i].getDisplayName(), locales[i]); 261 localeNames[i] = locales[i].getDisplayName(); 262 } 263 Arrays.sort(localeNames); 264 Runnable update = () -> { 265 localeBox.setModel(new DefaultComboBoxModel<>(localeNames)); 266 //localeBox.setModel(new javax.swing.DefaultComboBoxModel(locale.keySet().toArray())); 267 localeBox.setSelectedItem(Locale.getDefault().getDisplayName()); 268 localeBox.addActionListener((ActionEvent e) -> { 269 InstanceManager.getDefault(GuiLafPreferencesManager.class).setLocale(locale.getOrDefault(localeBox.getSelectedItem(), Locale.getDefault())); 270 }); 271 }; 272 SwingUtilities.invokeLater(update); 273 }; 274 new Thread(r).start(); 275 return panel; 276 } 277 278 public void setLocale(String loc) { 279 localeBox.setSelectedItem(loc); 280 } 281 282 /** 283 * Get the currently configured Locale or Locale.getDefault if no 284 * configuration has been done. 285 * 286 * @return the in-use Locale 287 */ 288 @Override 289 public Locale getLocale() { 290 Locale desired = locale.get(localeBox.getSelectedItem().toString()); 291 return (desired != null) ? desired : Locale.getDefault(); 292 } 293 294 public void doFontSize(JPanel panel) { 295 GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class); 296 Integer[] sizes = new Integer[MAX_DISPLAYED_FONT_SIZE - MIN_DISPLAYED_FONT_SIZE + 1]; 297 for (int i = 0; i < sizes.length; i++) { 298 sizes[i] = i + MIN_DISPLAYED_FONT_SIZE; 299 } 300 fontSizeComboBox = new JComboBox<>(sizes); 301 fontSizeComboBox.setEditable(true); // allow users to set font sizes not listed 302 JLabel fontSizeLabel = new JLabel(ConfigBundle.getMessage("ConsoleFontSize")); 303 fontSizeComboBox.setSelectedItem(manager.getFontSize()); 304 JLabel fontSizeUoM = new JLabel(ConfigBundle.getMessage("ConsoleFontSizeUoM")); 305 JButton resetButton = new JButton(ConfigBundle.getMessage("ResetDefault")); 306 resetButton.setToolTipText(ConfigBundle.getMessage("GUIFontSizeReset")); 307 308 panel.add(fontSizeLabel); 309 panel.add(fontSizeComboBox); 310 panel.add(fontSizeUoM); 311 panel.add(resetButton); 312 313 JComboBoxUtil.setupComboBoxMaxRows(fontSizeComboBox); 314 315 fontSizeComboBox.addActionListener((ActionEvent e) -> { 316 manager.setFontSize((int) fontSizeComboBox.getSelectedItem()); 317 }); 318 resetButton.addActionListener((ActionEvent e) -> { 319 if ((int) fontSizeComboBox.getSelectedItem() != manager.getDefaultFontSize()) { 320 fontSizeComboBox.setSelectedItem(manager.getDefaultFontSize()); 321 } 322 }); 323 } 324 325 private JSpinner maxComboRowsSpinner; 326 327 public void doMaxComboRows(JPanel panel) { 328 GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class); 329 JLabel maxComboRowsLabel = new JLabel(ConfigBundle.getMessage("GUIMaxComboRows")); 330 maxComboRowsSpinner = new JSpinner(new SpinnerNumberModel(manager.getMaxComboRows(), 0, 999, 1)); 331 this.maxComboRowsSpinner.addChangeListener((ChangeEvent e) -> { 332 manager.setMaxComboRows((int) maxComboRowsSpinner.getValue()); 333 }); 334 this.maxComboRowsSpinner.setToolTipText(ConfigBundle.getMessage("GUIMaxComboRowsToolTip")); 335 maxComboRowsLabel.setToolTipText(this.maxComboRowsSpinner.getToolTipText()); 336 panel.add(maxComboRowsLabel); 337 panel.add(maxComboRowsSpinner); 338 } 339 340 private JSpinner toolTipDismissDelaySpinner; 341 342 public void doToolTipDismissDelay(JPanel panel) { 343 GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class); 344 JLabel toolTipDismissDelayLabel = new JLabel(ConfigBundle.getMessage("GUIToolTipDismissDelay")); 345 toolTipDismissDelaySpinner = new JSpinner(new SpinnerNumberModel(manager.getToolTipDismissDelay() / 1000, MIN_TOOLTIP_TIME, MAX_TOOLTIP_TIME, 1)); 346 this.toolTipDismissDelaySpinner.addChangeListener((ChangeEvent e) -> { 347 manager.setToolTipDismissDelay((int) toolTipDismissDelaySpinner.getValue() * 1000); // convert to milliseconds from seconds 348 }); 349 this.toolTipDismissDelaySpinner.setToolTipText(MessageFormat.format(ConfigBundle.getMessage("GUIToolTipDismissDelayToolTip"), MIN_TOOLTIP_TIME, MAX_TOOLTIP_TIME)); 350 toolTipDismissDelayLabel.setToolTipText(this.toolTipDismissDelaySpinner.getToolTipText()); 351 JLabel toolTipDismissDelayUoM = new JLabel(ConfigBundle.getMessage("GUIToolTipDismissDelayUoM")); 352 toolTipDismissDelayUoM.setToolTipText(this.toolTipDismissDelaySpinner.getToolTipText()); 353 panel.add(toolTipDismissDelayLabel); 354 panel.add(toolTipDismissDelaySpinner); 355 panel.add(toolTipDismissDelayUoM); 356 } 357 358 public String getClassName() { 359 return LAFGroup.getSelection().getActionCommand(); 360 361 } 362 363 @Override 364 public String getPreferencesItem() { 365 return "DISPLAY"; // NOI18N 366 } 367 368 @Override 369 public String getPreferencesItemText() { 370 return ConfigBundle.getMessage("MenuDisplay"); // NOI18N 371 } 372 373 @Override 374 public String getTabbedPreferencesTitle() { 375 return ConfigBundle.getMessage("TabbedLayoutGUI"); // NOI18N 376 } 377 378 @Override 379 public String getLabelKey() { 380 return ConfigBundle.getMessage("LabelTabbedLayoutGUI"); // NOI18N 381 } 382 383 @Override 384 public JComponent getPreferencesComponent() { 385 return this; 386 } 387 388 @Override 389 public boolean isPersistant() { 390 return true; 391 } 392 393 @Override 394 public String getPreferencesTooltip() { 395 return null; 396 } 397 398 @Override 399 public void savePreferences() { 400 Profile profile = ProfileManager.getDefault().getActiveProfile(); 401 if (profile != null) { 402 InstanceManager.getDefault(GuiLafPreferencesManager.class).savePreferences(profile); 403 } 404 } 405 406 @Override 407 public boolean isDirty() { 408 return InstanceManager.getDefault(GuiLafPreferencesManager.class).isDirty(); 409 } 410 411 @Override 412 public boolean isRestartRequired() { 413 return InstanceManager.getDefault(GuiLafPreferencesManager.class).isRestartRequired(); 414 } 415 416 @Override 417 public boolean isPreferencesValid() { 418 return true; // no validity checking performed 419 } 420}