001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.Component; 008import java.awt.Container; 009import java.awt.Dimension; 010import java.awt.FlowLayout; 011import java.awt.GridBagLayout; 012import java.awt.Insets; 013import java.awt.event.ActionEvent; 014import java.awt.event.WindowAdapter; 015import java.awt.event.WindowEvent; 016 017import javax.swing.AbstractAction; 018import javax.swing.BorderFactory; 019import javax.swing.JButton; 020import javax.swing.JCheckBox; 021import javax.swing.JDialog; 022import javax.swing.JPanel; 023 024import org.openstreetmap.josm.actions.ExpertToggleAction; 025import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; 026import org.openstreetmap.josm.gui.help.HelpUtil; 027import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener; 028import org.openstreetmap.josm.gui.util.GuiHelper; 029import org.openstreetmap.josm.gui.util.WindowGeometry; 030import org.openstreetmap.josm.tools.GBC; 031import org.openstreetmap.josm.tools.ImageProvider; 032import org.openstreetmap.josm.tools.InputMapUtils; 033 034/** 035 * The main preferences dialog. 036 * 037 * Dialog window where the user can change various settings. Organized in main 038 * tabs to the left ({@link TabPreferenceSetting}) and (optional) sub-pages 039 * ({@link SubPreferenceSetting}). 040 */ 041public class PreferenceDialog extends JDialog { 042 043 private final PreferenceTabbedPane tpPreferences = new PreferenceTabbedPane(); 044 private final ContextSensitiveHelpAction helpAction = new ContextSensitiveHelpAction(); 045 private final WindowEventHandler windowEventHandler = new WindowEventHandler(); 046 private boolean canceled; 047 048 /** 049 * Constructs a new {@code PreferenceDialog}. 050 * @param parent parent component 051 */ 052 public PreferenceDialog(Component parent) { 053 super(GuiHelper.getFrameForComponent(parent), tr("Preferences"), ModalityType.DOCUMENT_MODAL); 054 build(); 055 this.setMinimumSize(new Dimension(600, 350)); 056 // set the maximum width to the current screen. If the dialog is opened on a 057 // smaller screen than before, this will reset the stored preference. 058 this.setMaximumSize(GuiHelper.getScreenSize()); 059 } 060 061 protected JPanel buildActionPanel() { 062 JPanel pnl = new JPanel(new GridBagLayout()); 063 064 JCheckBox expert = new JCheckBox(tr("Expert Mode")); 065 expert.setSelected(ExpertToggleAction.isExpert()); 066 expert.addActionListener(e -> ExpertToggleAction.getInstance().actionPerformed(null)); 067 068 JPanel btns = new JPanel(new FlowLayout(FlowLayout.CENTER)); 069 btns.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 070 OKAction okAction = new OKAction(); 071 btns.add(new JButton(okAction)); 072 btns.add(new JButton(new CancelAction())); 073 btns.add(new JButton(helpAction)); 074 pnl.add(expert, GBC.std().insets(5, 0, 0, 0)); 075 pnl.add(btns, GBC.std().fill(GBC.HORIZONTAL)); 076 InputMapUtils.addCtrlEnterAction(pnl, okAction); 077 return pnl; 078 } 079 080 protected final void build() { 081 Container c = getContentPane(); 082 c.setLayout(new BorderLayout()); 083 c.add(tpPreferences, BorderLayout.CENTER); 084 tpPreferences.buildGui(); 085 tpPreferences.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 086 c.add(buildActionPanel(), BorderLayout.SOUTH); 087 addWindowListener(windowEventHandler); 088 089 InputMapUtils.addEscapeAction(getRootPane(), new CancelAction()); 090 setHelpContext(HelpUtil.ht("/Action/Preferences")); 091 } 092 093 /** 094 * Sets the help context of the preferences dialog. 095 * @param helpContext new help context 096 * @since 13431 097 */ 098 public final void setHelpContext(String helpContext) { 099 helpAction.setHelpTopic(helpContext); 100 HelpUtil.setHelpContext(getRootPane(), helpContext); 101 } 102 103 /** 104 * Replies the preferences tabbed pane. 105 * @return The preferences tabbed pane, or null if the dialog is not yet initialized. 106 * @since 5604 107 */ 108 public PreferenceTabbedPane getTabbedPane() { 109 return tpPreferences; 110 } 111 112 /** 113 * Determines if preferences changes have been canceled. 114 * @return {@code true} if preferences changes have been canceled 115 */ 116 public boolean isCanceled() { 117 return canceled; 118 } 119 120 protected void setCanceled(boolean canceled) { 121 this.canceled = canceled; 122 } 123 124 @Override 125 public void setVisible(boolean visible) { 126 if (visible) { 127 // Make the pref window at most as large as the parent JOSM window 128 // Have to take window decorations into account or the windows will be too large 129 Insets i = this.getParent().getInsets(); 130 Dimension p = this.getParent().getSize(); 131 p = new Dimension(Math.min(p.width-i.left-i.right, 700), 132 Math.min(p.height-i.top-i.bottom, 800)); 133 new WindowGeometry( 134 getClass().getName() + ".geometry", 135 WindowGeometry.centerInWindow( 136 getParent(), 137 p 138 ) 139 ).applySafe(this); 140 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775 141 new WindowGeometry(this).remember(getClass().getName() + ".geometry"); 142 } 143 super.setVisible(visible); 144 } 145 146 /** 147 * Select preferences tab by name. 148 * @param name preferences tab name (icon) 149 */ 150 public void selectPreferencesTabByName(String name) { 151 tpPreferences.selectTabByName(name); 152 } 153 154 /** 155 * Select preferences tab by class. 156 * @param clazz preferences tab class 157 */ 158 public void selectPreferencesTabByClass(Class<? extends TabPreferenceSetting> clazz) { 159 tpPreferences.selectTabByPref(clazz); 160 } 161 162 /** 163 * Select preferences sub-tab by class. 164 * @param clazz preferences sub-tab class 165 */ 166 public void selectSubPreferencesTabByClass(Class<? extends SubPreferenceSetting> clazz) { 167 tpPreferences.selectSubTabByPref(clazz); 168 } 169 170 class CancelAction extends AbstractAction { 171 CancelAction() { 172 putValue(NAME, tr("Cancel")); 173 new ImageProvider("cancel").getResource().attachImageIcon(this); 174 putValue(SHORT_DESCRIPTION, tr("Close the preferences dialog and discard preference updates")); 175 } 176 177 public void cancel() { 178 setCanceled(true); 179 dispose(); 180 } 181 182 @Override 183 public void actionPerformed(ActionEvent evt) { 184 cancel(); 185 } 186 } 187 188 class OKAction extends AbstractAction { 189 OKAction() { 190 putValue(NAME, tr("OK")); 191 new ImageProvider("ok").getResource().attachImageIcon(this); 192 putValue(SHORT_DESCRIPTION, tr("Save the preferences and close the dialog")); 193 } 194 195 @Override 196 public void actionPerformed(ActionEvent evt) { 197 for (ValidationListener listener: tpPreferences.validationListeners) { 198 if (!listener.validatePreferences()) 199 return; 200 } 201 202 tpPreferences.savePreferences(); 203 setCanceled(false); 204 dispose(); 205 } 206 } 207 208 class WindowEventHandler extends WindowAdapter { 209 @Override 210 public void windowClosing(WindowEvent arg0) { 211 new CancelAction().cancel(); 212 } 213 } 214 215 @Override 216 public void dispose() { 217 removeWindowListener(windowEventHandler); 218 super.dispose(); 219 } 220}