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