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.Toolkit;
014import java.awt.event.ActionEvent;
015import java.awt.event.ActionListener;
016import java.awt.event.KeyEvent;
017import java.awt.event.WindowAdapter;
018import java.awt.event.WindowEvent;
019
020import javax.swing.AbstractAction;
021import javax.swing.BorderFactory;
022import javax.swing.JCheckBox;
023import javax.swing.JComponent;
024import javax.swing.JDialog;
025import javax.swing.JOptionPane;
026import javax.swing.JPanel;
027import javax.swing.KeyStroke;
028
029import org.openstreetmap.josm.actions.ExpertToggleAction;
030import org.openstreetmap.josm.gui.SideButton;
031import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
032import org.openstreetmap.josm.gui.help.HelpUtil;
033import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.ValidationListener;
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 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( Toolkit.getDefaultToolkit().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        public 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        public 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}