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