001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.display; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.GridBagLayout; 008import java.text.DateFormat; 009import java.util.Date; 010 011import javax.swing.BorderFactory; 012import javax.swing.Box; 013import javax.swing.DefaultListCellRenderer; 014import javax.swing.JCheckBox; 015import javax.swing.JFormattedTextField; 016import javax.swing.JLabel; 017import javax.swing.JList; 018import javax.swing.JScrollPane; 019import javax.swing.JSpinner; 020import javax.swing.ListCellRenderer; 021import javax.swing.LookAndFeel; 022import javax.swing.SpinnerNumberModel; 023import javax.swing.UIManager; 024import javax.swing.UIManager.LookAndFeelInfo; 025 026import org.openstreetmap.josm.Main; 027import org.openstreetmap.josm.actions.ExpertToggleAction; 028import org.openstreetmap.josm.gui.MapMover; 029import org.openstreetmap.josm.gui.NavigatableComponent; 030import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 031import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 032import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 033import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 034import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting; 035import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting; 036import org.openstreetmap.josm.gui.widgets.FileChooserManager; 037import org.openstreetmap.josm.gui.widgets.JosmComboBox; 038import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 039import org.openstreetmap.josm.tools.GBC; 040import org.openstreetmap.josm.tools.date.DateUtils; 041 042/** 043 * Look-and-feel preferences. 044 */ 045public class LafPreference implements SubPreferenceSetting { 046 047 /** 048 * Factory used to create a new {@code LafPreference}. 049 */ 050 public static class Factory implements PreferenceSettingFactory { 051 @Override 052 public PreferenceSetting createPreferenceSetting() { 053 return new LafPreference(); 054 } 055 } 056 057 /** 058 * ComboBox with all look and feels. 059 */ 060 private JosmComboBox<LookAndFeelInfo> lafCombo; 061 VerticallyScrollablePanel panel; 062 private final JCheckBox showSplashScreen = new JCheckBox(tr("Show splash screen at startup")); 063 private final JCheckBox showID = new JCheckBox(tr("Show object ID in selection lists")); 064 private final JCheckBox showLocalizedName = new JCheckBox(tr("Show localized name in selection lists")); 065 private final JCheckBox modeless = new JCheckBox(tr("Modeless working (Potlatch style)")); 066 private final JCheckBox dynamicButtons = new JCheckBox(tr("Dynamic buttons in side menus")); 067 private final JCheckBox isoDates = new JCheckBox(tr("Display ISO dates")); 068 private final JCheckBox nativeFileChoosers = new JCheckBox(tr("Use native file choosers (nicer, but do not support file filters)")); 069 private final JCheckBox zoomReverseWheel = new JCheckBox(tr("Reverse zoom with mouse wheel")); 070 private final JCheckBox zoomIntermediateSteps = new JCheckBox(tr("Intermediate steps between native resolutions")); 071 private JSpinner spinZoomRatio; 072 073 @Override 074 public void addGui(PreferenceTabbedPane gui) { 075 lafCombo = new JosmComboBox<>(UIManager.getInstalledLookAndFeels()); 076 077 // let's try to load additional LookAndFeels and put them into the list 078 if (Main.isPlatformOsx()) { 079 try { 080 Class<?> cquaqua = Class.forName("ch.randelshofer.quaqua.QuaquaLookAndFeel"); 081 Object oquaqua = cquaqua.getConstructor((Class[]) null).newInstance((Object[]) null); 082 // no exception? Then Go! 083 lafCombo.addItem( 084 new UIManager.LookAndFeelInfo(((LookAndFeel) oquaqua).getName(), "ch.randelshofer.quaqua.QuaquaLookAndFeel") 085 ); 086 } catch (ReflectiveOperationException ex) { 087 // just debug, Quaqua may not even be installed... 088 Main.debug(ex.getMessage()); 089 } 090 } 091 092 String laf = Main.pref.get("laf", Main.platform.getDefaultStyle()); 093 for (int i = 0; i < lafCombo.getItemCount(); ++i) { 094 if (lafCombo.getItemAt(i).getClassName().equals(laf)) { 095 lafCombo.setSelectedIndex(i); 096 break; 097 } 098 } 099 100 lafCombo.setRenderer(new ListCellRenderer<LookAndFeelInfo>() { 101 private final DefaultListCellRenderer def = new DefaultListCellRenderer(); 102 @Override 103 public Component getListCellRendererComponent(JList<? extends LookAndFeelInfo> list, LookAndFeelInfo value, 104 int index, boolean isSelected, boolean cellHasFocus) { 105 return def.getListCellRendererComponent(list, value.getName(), index, isSelected, cellHasFocus); 106 } 107 }); 108 109 panel = new VerticallyScrollablePanel(new GridBagLayout()); 110 panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 111 112 // Show splash screen on startup 113 showSplashScreen.setToolTipText(tr("Show splash screen at startup")); 114 showSplashScreen.setSelected(Main.pref.getBoolean("draw.splashscreen", true)); 115 panel.add(showSplashScreen, GBC.eop().insets(20, 0, 0, 0)); 116 117 // Show ID in selection 118 showID.setToolTipText(tr("Show object ID in selection lists")); 119 showID.setSelected(Main.pref.getBoolean("osm-primitives.showid", false)); 120 121 // Show localized names 122 showLocalizedName.setToolTipText(tr("Show localized name in selection lists, if available")); 123 showLocalizedName.setSelected(Main.pref.getBoolean("osm-primitives.localize-name", true)); 124 ExpertToggleAction.addVisibilitySwitcher(showLocalizedName); 125 126 modeless.setToolTipText(tr("Do not require to switch modes (potlatch style workflow)")); 127 modeless.setSelected(Main.pref.getBoolean("modeless", false)); 128 ExpertToggleAction.addVisibilitySwitcher(modeless); 129 130 panel.add(showID, GBC.eop().insets(20, 0, 0, 0)); 131 panel.add(showLocalizedName, GBC.eop().insets(20, 0, 0, 0)); 132 panel.add(modeless, GBC.eop().insets(20, 0, 0, 0)); 133 134 dynamicButtons.setToolTipText(tr("Display buttons in right side menus only when mouse is inside the element")); 135 dynamicButtons.setSelected(ToggleDialog.PROP_DYNAMIC_BUTTONS.get()); 136 panel.add(dynamicButtons, GBC.eop().insets(20, 0, 0, 0)); 137 138 Date today = new Date(); 139 isoDates.setToolTipText(tr("Format dates according to {0}. Today''s date will be displayed as {1} instead of {2}", 140 tr("ISO 8601"), 141 DateUtils.newIsoDateFormat().format(today), 142 DateFormat.getDateInstance(DateFormat.SHORT).format(today))); 143 isoDates.setSelected(DateUtils.PROP_ISO_DATES.get()); 144 panel.add(isoDates, GBC.eop().insets(20, 0, 0, 0)); 145 146 nativeFileChoosers.setToolTipText( 147 tr("Use file choosers that behave more like native ones. They look nicer but do not support some features like file filters")); 148 nativeFileChoosers.setSelected(FileChooserManager.PROP_USE_NATIVE_FILE_DIALOG.get()); 149 panel.add(nativeFileChoosers, GBC.eop().insets(20, 0, 0, 0)); 150 151 zoomReverseWheel.setToolTipText( 152 tr("Check if you feel opposite direction more convenient")); 153 zoomReverseWheel.setSelected(MapMover.PROP_ZOOM_REVERSE_WHEEL.get()); 154 panel.add(zoomReverseWheel, GBC.eop().insets(20, 0, 0, 0)); 155 156 zoomIntermediateSteps.setToolTipText( 157 tr("Divide intervals between native resolution levels to smaller steps if they are much larger than zoom ratio")); 158 zoomIntermediateSteps.setSelected(NavigatableComponent.PROP_ZOOM_INTERMEDIATE_STEPS.get()); 159 ExpertToggleAction.addVisibilitySwitcher(zoomIntermediateSteps); 160 panel.add(zoomIntermediateSteps, GBC.eop().insets(20, 0, 0, 0)); 161 162 panel.add(Box.createVerticalGlue(), GBC.eol().insets(0, 10, 0, 0)); 163 164 double logZoomLevel = Math.log(2) / Math.log(NavigatableComponent.PROP_ZOOM_RATIO.get()); 165 logZoomLevel = Math.max(1, logZoomLevel); 166 logZoomLevel = Math.min(5, logZoomLevel); 167 JLabel labelZoomRatio = new JLabel(tr("Zoom steps to get double scale")); 168 spinZoomRatio = new JSpinner(new SpinnerNumberModel(logZoomLevel, 1, 5, 1)); 169 Component spinZoomRatioEditor = spinZoomRatio.getEditor(); 170 JFormattedTextField jftf = ((JSpinner.DefaultEditor) spinZoomRatioEditor).getTextField(); 171 jftf.setColumns(2); 172 String zoomRatioToolTipText = tr("Higher value means more steps needed, therefore zoom steps will be smaller"); 173 spinZoomRatio.setToolTipText(zoomRatioToolTipText); 174 labelZoomRatio.setToolTipText(zoomRatioToolTipText); 175 labelZoomRatio.setLabelFor(spinZoomRatio); 176 panel.add(labelZoomRatio, GBC.std().insets(20, 0, 0, 0)); 177 panel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL)); 178 panel.add(spinZoomRatio, GBC.eol()); 179 180 panel.add(new JLabel(tr("Look and Feel")), GBC.std().insets(20, 0, 0, 0)); 181 panel.add(GBC.glue(5, 0), GBC.std().fill(GBC.HORIZONTAL)); 182 panel.add(lafCombo, GBC.eol().fill(GBC.HORIZONTAL)); 183 184 JScrollPane scrollpane = panel.getVerticalScrollPane(); 185 scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 186 gui.getDisplayPreference().addSubTab(this, tr("Look and Feel"), scrollpane); 187 } 188 189 @Override 190 public boolean ok() { 191 boolean mod = false; 192 Main.pref.put("draw.splashscreen", showSplashScreen.isSelected()); 193 Main.pref.put("osm-primitives.showid", showID.isSelected()); 194 Main.pref.put("osm-primitives.localize-name", showLocalizedName.isSelected()); 195 Main.pref.put("modeless", modeless.isSelected()); 196 Main.pref.put(ToggleDialog.PROP_DYNAMIC_BUTTONS.getKey(), dynamicButtons.isSelected()); 197 Main.pref.put(DateUtils.PROP_ISO_DATES.getKey(), isoDates.isSelected()); 198 Main.pref.put(FileChooserManager.PROP_USE_NATIVE_FILE_DIALOG.getKey(), nativeFileChoosers.isSelected()); 199 MapMover.PROP_ZOOM_REVERSE_WHEEL.put(zoomReverseWheel.isSelected()); 200 NavigatableComponent.PROP_ZOOM_INTERMEDIATE_STEPS.put(zoomIntermediateSteps.isSelected()); 201 NavigatableComponent.PROP_ZOOM_RATIO.put(Math.pow(2, 1/(double) spinZoomRatio.getModel().getValue())); 202 mod |= Main.pref.put("laf", ((LookAndFeelInfo) lafCombo.getSelectedItem()).getClassName()); 203 return mod; 204 } 205 206 @Override 207 public boolean isExpert() { 208 return false; 209 } 210 211 @Override 212 public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) { 213 return gui.getDisplayPreference(); 214 } 215}