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
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        // make the radio buttons
211        for (java.util.Map.Entry<String, String> entry : installedLAFs.entrySet()) {
212            String name = entry.getKey();
213            JRadioButton jmi = new JRadioButton(name);
214            panel.add(jmi);
215            LAFGroup.add(jmi);
216            jmi.setActionCommand(name);
217            jmi.addActionListener((ActionEvent e) -> {
218                InstanceManager.getDefault(GuiLafPreferencesManager.class).setLookAndFeel(installedLAFs.get(name));
219            });
220            if ( entry.getValue().equals(UIManager.getLookAndFeel().getClass().getName())) {
221                jmi.setSelected(true);
222            }
223        }
224    }
225
226    /**
227     * Create and return a JPanel for configuring default local.
228     * <p>
229     * Most of the action is handled in a separate thread, which replaces the
230     * contents of a JComboBox when the list of Locales is available.
231     *
232     * @return the panel
233     */
234    public JPanel doLocale() {
235        JPanel panel = new JPanel();
236        // add JComboBoxen for language and country
237        panel.setLayout(new FlowLayout());
238        panel.add(localeBox);
239        JComboBoxUtil.setupComboBoxMaxRows(localeBox);
240
241        // create object to find locales in new Thread
242        Runnable r = () -> {
243            Locale[] locales = Locale.getAvailableLocales();
244            String[] localeNames = new String[locales.length];
245            for (int i = 0; i < locales.length; i++) {
246                locale.put(locales[i].getDisplayName(), locales[i]);
247                localeNames[i] = locales[i].getDisplayName();
248            }
249            Arrays.sort(localeNames);
250            Runnable update = () -> {
251                localeBox.setModel(new DefaultComboBoxModel<>(localeNames));
252                //localeBox.setModel(new javax.swing.DefaultComboBoxModel(locale.keySet().toArray()));
253                localeBox.setSelectedItem(Locale.getDefault().getDisplayName());
254                localeBox.addActionListener((ActionEvent e) -> {
255                    InstanceManager.getDefault(GuiLafPreferencesManager.class).setLocale(locale.getOrDefault(localeBox.getSelectedItem(), Locale.getDefault()));
256                });
257            };
258            SwingUtilities.invokeLater(update);
259        };
260        new Thread(r).start();
261        return panel;
262    }
263
264    public void setLocale(String loc) {
265        localeBox.setSelectedItem(loc);
266    }
267
268    /**
269     * Get the currently configured Locale or Locale.getDefault if no
270     * configuration has been done.
271     *
272     * @return the in-use Locale
273     */
274    @Override
275    public Locale getLocale() {
276        Locale desired = locale.get(localeBox.getSelectedItem().toString());
277        return (desired != null) ? desired : Locale.getDefault();
278    }
279
280    public void doFontSize(JPanel panel) {
281        GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class);
282        Integer[] sizes = new Integer[MAX_DISPLAYED_FONT_SIZE - MIN_DISPLAYED_FONT_SIZE + 1];
283        for (int i = 0; i < sizes.length; i++) {
284            sizes[i] = i + MIN_DISPLAYED_FONT_SIZE;
285        }
286        fontSizeComboBox = new JComboBox<>(sizes);
287        fontSizeComboBox.setEditable(true); // allow users to set font sizes not listed
288        JLabel fontSizeLabel = new JLabel(ConfigBundle.getMessage("ConsoleFontSize"));
289        fontSizeComboBox.setSelectedItem(manager.getFontSize());
290        JLabel fontSizeUoM = new JLabel(ConfigBundle.getMessage("ConsoleFontSizeUoM"));
291        JButton resetButton = new JButton(ConfigBundle.getMessage("ResetDefault"));
292        resetButton.setToolTipText(ConfigBundle.getMessage("GUIFontSizeReset"));
293
294        panel.add(fontSizeLabel);
295        panel.add(fontSizeComboBox);
296        panel.add(fontSizeUoM);
297        panel.add(resetButton);
298
299        JComboBoxUtil.setupComboBoxMaxRows(fontSizeComboBox);
300
301        fontSizeComboBox.addActionListener((ActionEvent e) -> {
302            manager.setFontSize((int) fontSizeComboBox.getSelectedItem());
303        });
304        resetButton.addActionListener((ActionEvent e) -> {
305            if ((int) fontSizeComboBox.getSelectedItem() != manager.getDefaultFontSize()) {
306                fontSizeComboBox.setSelectedItem(manager.getDefaultFontSize());
307            }
308        });
309    }
310
311    private JSpinner maxComboRowsSpinner;
312
313    public void doMaxComboRows(JPanel panel) {
314        GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class);
315        JLabel maxComboRowsLabel = new JLabel(ConfigBundle.getMessage("GUIMaxComboRows"));
316        maxComboRowsSpinner = new JSpinner(new SpinnerNumberModel(manager.getMaxComboRows(), 0, 999, 1));
317        this.maxComboRowsSpinner.addChangeListener((ChangeEvent e) -> {
318            manager.setMaxComboRows((int) maxComboRowsSpinner.getValue());
319        });
320        this.maxComboRowsSpinner.setToolTipText(ConfigBundle.getMessage("GUIMaxComboRowsToolTip"));
321        maxComboRowsLabel.setToolTipText(this.maxComboRowsSpinner.getToolTipText());
322        panel.add(maxComboRowsLabel);
323        panel.add(maxComboRowsSpinner);
324    }
325
326    private JSpinner toolTipDismissDelaySpinner;
327
328    public void doToolTipDismissDelay(JPanel panel) {
329        GuiLafPreferencesManager manager = InstanceManager.getDefault(GuiLafPreferencesManager.class);
330        JLabel toolTipDismissDelayLabel = new JLabel(ConfigBundle.getMessage("GUIToolTipDismissDelay"));
331        toolTipDismissDelaySpinner = new JSpinner(new SpinnerNumberModel(manager.getToolTipDismissDelay() / 1000, MIN_TOOLTIP_TIME, MAX_TOOLTIP_TIME, 1));
332        this.toolTipDismissDelaySpinner.addChangeListener((ChangeEvent e) -> {
333            manager.setToolTipDismissDelay((int) toolTipDismissDelaySpinner.getValue() * 1000); // convert to milliseconds from seconds
334        });
335        this.toolTipDismissDelaySpinner.setToolTipText(MessageFormat.format(ConfigBundle.getMessage("GUIToolTipDismissDelayToolTip"), MIN_TOOLTIP_TIME, MAX_TOOLTIP_TIME));
336        toolTipDismissDelayLabel.setToolTipText(this.toolTipDismissDelaySpinner.getToolTipText());
337        JLabel toolTipDismissDelayUoM = new JLabel(ConfigBundle.getMessage("GUIToolTipDismissDelayUoM"));
338        toolTipDismissDelayUoM.setToolTipText(this.toolTipDismissDelaySpinner.getToolTipText());
339        panel.add(toolTipDismissDelayLabel);
340        panel.add(toolTipDismissDelaySpinner);
341        panel.add(toolTipDismissDelayUoM);
342    }
343
344    public String getClassName() {
345        return LAFGroup.getSelection().getActionCommand();
346
347    }
348
349    @Override
350    public String getPreferencesItem() {
351        return "DISPLAY"; // NOI18N
352    }
353
354    @Override
355    public String getPreferencesItemText() {
356        return ConfigBundle.getMessage("MenuDisplay"); // NOI18N
357    }
358
359    @Override
360    public String getTabbedPreferencesTitle() {
361        return ConfigBundle.getMessage("TabbedLayoutGUI"); // NOI18N
362    }
363
364    @Override
365    public String getLabelKey() {
366        return ConfigBundle.getMessage("LabelTabbedLayoutGUI"); // NOI18N
367    }
368
369    @Override
370    public JComponent getPreferencesComponent() {
371        return this;
372    }
373
374    @Override
375    public boolean isPersistant() {
376        return true;
377    }
378
379    @Override
380    public String getPreferencesTooltip() {
381        return null;
382    }
383
384    @Override
385    public void savePreferences() {
386        Profile profile = ProfileManager.getDefault().getActiveProfile();
387        if (profile != null) {
388            InstanceManager.getDefault(GuiLafPreferencesManager.class).savePreferences(profile);
389        }
390    }
391
392    @Override
393    public boolean isDirty() {
394        return InstanceManager.getDefault(GuiLafPreferencesManager.class).isDirty();
395    }
396
397    @Override
398    public boolean isRestartRequired() {
399        return InstanceManager.getDefault(GuiLafPreferencesManager.class).isRestartRequired();
400    }
401
402    @Override
403    public boolean isPreferencesValid() {
404        return true; // no validity checking performed
405    }
406}