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.JPanel;
025import javax.swing.KeyStroke;
026
027import org.openstreetmap.josm.actions.ExpertToggleAction;
028import org.openstreetmap.josm.gui.SideButton;
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 SideButton(new OKAction()));
070        btns.add(new SideButton(new CancelAction()));
071        btns.add(new SideButton(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            putValue(SMALL_ICON, ImageProvider.get("cancel"));
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            putValue(SMALL_ICON, ImageProvider.get("ok"));
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}