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