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.ActionListener; 015import java.awt.event.KeyEvent; 016import java.awt.event.WindowAdapter; 017import java.awt.event.WindowEvent; 018 019import javax.swing.AbstractAction; 020import javax.swing.BorderFactory; 021import javax.swing.JCheckBox; 022import javax.swing.JComponent; 023import javax.swing.JButton; 024import javax.swing.JDialog; 025import javax.swing.JPanel; 026import javax.swing.KeyStroke; 027 028import org.openstreetmap.josm.actions.ExpertToggleAction; 029import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction; 030import org.openstreetmap.josm.gui.help.HelpUtil; 031import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener; 032import org.openstreetmap.josm.gui.util.GuiHelper; 033import org.openstreetmap.josm.tools.GBC; 034import org.openstreetmap.josm.tools.ImageProvider; 035import org.openstreetmap.josm.tools.WindowGeometry; 036 037public class PreferenceDialog extends JDialog { 038 039 private final PreferenceTabbedPane tpPreferences = new PreferenceTabbedPane(); 040 private boolean canceled; 041 042 /** 043 * Constructs a new {@code PreferenceDialog}. 044 * @param parent parent component 045 */ 046 public PreferenceDialog(Component parent) { 047 super(GuiHelper.getFrameForComponent(parent), tr("Preferences"), ModalityType.DOCUMENT_MODAL); 048 build(); 049 this.setMinimumSize(new Dimension(600, 350)); 050 // set the maximum width to the current screen. If the dialog is opened on a 051 // smaller screen than before, this will reset the stored preference. 052 this.setMaximumSize(GuiHelper.getScreenSize()); 053 } 054 055 protected JPanel buildActionPanel() { 056 JPanel pnl = new JPanel(new GridBagLayout()); 057 058 JCheckBox expert = new JCheckBox(tr("Expert mode")); 059 expert.setSelected(ExpertToggleAction.isExpert()); 060 expert.addActionListener(new ActionListener() { 061 @Override 062 public void actionPerformed(ActionEvent e) { 063 ExpertToggleAction.getInstance().actionPerformed(null); 064 } 065 }); 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(new ContextSensitiveHelpAction(HelpUtil.ht("/Action/Preferences")))); 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 getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel"); 088 getRootPane().getActionMap().put("cancel", new CancelAction()); 089 HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Action/Preferences")); 090 } 091 092 /** 093 * Replies the preferences tabbed pane. 094 * @return The preferences tabbed pane, or null if the dialog is not yet initialized. 095 * @since 5604 096 */ 097 public PreferenceTabbedPane getTabbedPane() { 098 return tpPreferences; 099 } 100 101 public boolean isCanceled() { 102 return canceled; 103 } 104 105 protected void setCanceled(boolean canceled) { 106 this.canceled = canceled; 107 } 108 109 @Override 110 public void setVisible(boolean visible) { 111 if (visible) { 112 // Make the pref window at most as large as the parent JOSM window 113 // Have to take window decorations into account or the windows will 114 // be too large 115 Insets i = this.getParent().getInsets(); 116 Dimension p = this.getParent().getSize(); 117 p = new Dimension(Math.min(p.width-i.left-i.right, 700), 118 Math.min(p.height-i.top-i.bottom, 800)); 119 new WindowGeometry( 120 getClass().getName() + ".geometry", 121 WindowGeometry.centerInWindow( 122 getParent(), 123 p 124 ) 125 ).applySafe(this); 126 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775 127 new WindowGeometry(this).remember(getClass().getName() + ".geometry"); 128 } 129 super.setVisible(visible); 130 } 131 132 /** 133 * Select preferences tab by name. 134 * @param name preferences tab name (icon) 135 */ 136 public void selectPreferencesTabByName(String name) { 137 tpPreferences.selectTabByName(name); 138 } 139 140 /** 141 * Select preferences tab by class. 142 * @param clazz preferences tab class 143 */ 144 public void selectPreferencesTabByClass(Class<? extends TabPreferenceSetting> clazz) { 145 tpPreferences.selectTabByPref(clazz); 146 } 147 148 /** 149 * Select preferences sub-tab by class. 150 * @param clazz preferences sub-tab class 151 */ 152 public void selectSubPreferencesTabByClass(Class<? extends SubPreferenceSetting> clazz) { 153 tpPreferences.selectSubTabByPref(clazz); 154 } 155 156 class CancelAction extends AbstractAction { 157 CancelAction() { 158 putValue(NAME, tr("Cancel")); 159 new ImageProvider("cancel").getResource().attachImageIcon(this); 160 putValue(SHORT_DESCRIPTION, tr("Close the preferences dialog and discard preference updates")); 161 } 162 163 public void cancel() { 164 setCanceled(true); 165 setVisible(false); 166 tpPreferences.validationListeners.clear(); 167 } 168 169 @Override 170 public void actionPerformed(ActionEvent evt) { 171 cancel(); 172 } 173 } 174 175 class OKAction extends AbstractAction { 176 OKAction() { 177 putValue(NAME, tr("OK")); 178 new ImageProvider("ok").getResource().attachImageIcon(this); 179 putValue(SHORT_DESCRIPTION, tr("Save the preferences and close the dialog")); 180 } 181 182 @Override 183 public void actionPerformed(ActionEvent evt) { 184 for (ValidationListener listener: tpPreferences.validationListeners) { 185 if (!listener.validatePreferences()) 186 return; 187 } 188 189 tpPreferences.savePreferences(); 190 tpPreferences.validationListeners.clear(); 191 setCanceled(false); 192 setVisible(false); 193 } 194 } 195 196 class WindowEventHandler extends WindowAdapter { 197 @Override 198 public void windowClosing(WindowEvent arg0) { 199 new CancelAction().cancel(); 200 } 201 } 202}