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