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