001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs.changeset.query;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.BorderLayout;
007import java.awt.Container;
008import java.awt.Dimension;
009import java.awt.FlowLayout;
010import java.awt.Window;
011import java.awt.event.ActionEvent;
012import java.awt.event.KeyEvent;
013import java.awt.event.WindowAdapter;
014import java.awt.event.WindowEvent;
015
016import javax.swing.AbstractAction;
017import javax.swing.JComponent;
018import javax.swing.JDialog;
019import javax.swing.JOptionPane;
020import javax.swing.JPanel;
021import javax.swing.JTabbedPane;
022import javax.swing.KeyStroke;
023
024import org.openstreetmap.josm.gui.HelpAwareOptionPane;
025import org.openstreetmap.josm.gui.SideButton;
026import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
027import org.openstreetmap.josm.gui.help.HelpUtil;
028import org.openstreetmap.josm.io.ChangesetQuery;
029import org.openstreetmap.josm.tools.ImageProvider;
030import org.openstreetmap.josm.tools.WindowGeometry;
031
032/**
033 * This is a modal dialog for entering query criteria to search for changesets.
034 *
035 */
036public class ChangesetQueryDialog extends JDialog {
037
038    private JTabbedPane tpQueryPanels;
039    private BasicChangesetQueryPanel pnlBasicChangesetQueries;
040    private UrlBasedQueryPanel pnlUrlBasedQueries;
041    private AdvancedChangesetQueryPanel pnlAdvancedQueries;
042    private boolean canceled;
043
044    protected JPanel buildContentPanel() {
045        tpQueryPanels = new JTabbedPane();
046        tpQueryPanels.add(pnlBasicChangesetQueries = new BasicChangesetQueryPanel());
047        tpQueryPanels.add(pnlUrlBasedQueries = new UrlBasedQueryPanel());
048        tpQueryPanels.add(pnlAdvancedQueries = new AdvancedChangesetQueryPanel());
049
050        tpQueryPanels.setTitleAt(0, tr("Basic"));
051        tpQueryPanels.setToolTipTextAt(0, tr("Download changesets using predefined queries"));
052
053        tpQueryPanels.setTitleAt(1, tr("From URL"));
054        tpQueryPanels.setToolTipTextAt(1, tr("Query changesets from a server URL"));
055
056        tpQueryPanels.setTitleAt(2, tr("Advanced"));
057        tpQueryPanels.setToolTipTextAt(2, tr("Use a custom changeset query"));
058
059        JPanel pnl = new JPanel(new BorderLayout());
060        pnl.add(tpQueryPanels, BorderLayout.CENTER);
061        return pnl;
062    }
063
064    protected JPanel buildButtonPanel() {
065        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
066
067        // -- query action
068        pnl.add(new SideButton(new QueryAction()));
069
070        // -- cancel action
071        pnl.add(new SideButton(new CancelAction()));
072
073        // -- help action
074        pnl.add(new SideButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/ChangesetQuery"))));
075
076        return pnl;
077    }
078
079    protected final void build() {
080        setTitle(tr("Query changesets"));
081        Container cp = getContentPane();
082        cp.setLayout(new BorderLayout());
083        cp.add(buildContentPanel(), BorderLayout.CENTER);
084        cp.add(buildButtonPanel(), BorderLayout.SOUTH);
085
086        // cancel on ESC
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
090        // context sensitive help
091        HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/ChangesetQueryDialog"));
092
093        addWindowListener(new WindowEventHandler());
094    }
095
096    public ChangesetQueryDialog(Window parent) {
097        super(parent, ModalityType.DOCUMENT_MODAL);
098        build();
099    }
100
101    public boolean isCanceled() {
102        return canceled;
103    }
104
105    public void initForUserInput() {
106        pnlBasicChangesetQueries.init();
107    }
108
109    protected void setCanceled(boolean canceled) {
110        this.canceled = canceled;
111    }
112
113    public ChangesetQuery getChangesetQuery() {
114        if (isCanceled())
115            return null;
116        switch(tpQueryPanels.getSelectedIndex()) {
117        case 0:
118            return pnlBasicChangesetQueries.buildChangesetQuery();
119        case 1:
120            return pnlUrlBasedQueries.buildChangesetQuery();
121        case 2:
122            return pnlAdvancedQueries.buildChangesetQuery();
123        default:
124            // FIXME: extend with advanced queries
125            return null;
126        }
127    }
128
129    public void startUserInput() {
130        pnlUrlBasedQueries.startUserInput();
131        pnlAdvancedQueries.startUserInput();
132    }
133
134    @Override
135    public void setVisible(boolean visible) {
136        if (visible) {
137            new WindowGeometry(
138                    getClass().getName() + ".geometry",
139                    WindowGeometry.centerInWindow(
140                            getParent(),
141                            new Dimension(400, 400)
142                    )
143            ).applySafe(this);
144            setCanceled(false);
145            startUserInput();
146        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
147            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
148            pnlAdvancedQueries.rememberSettings();
149        }
150        super.setVisible(visible);
151    }
152
153    class QueryAction extends AbstractAction {
154        QueryAction() {
155            putValue(NAME, tr("Query"));
156            putValue(SMALL_ICON, ImageProvider.get("dialogs", "search"));
157            putValue(SHORT_DESCRIPTION, tr("Query and download changesets"));
158        }
159
160        protected void alertInvalidChangesetQuery() {
161            HelpAwareOptionPane.showOptionDialog(
162                    ChangesetQueryDialog.this,
163                    tr("Please enter a valid changeset query URL first."),
164                    tr("Illegal changeset query URL"),
165                    JOptionPane.WARNING_MESSAGE,
166                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#EnterAValidChangesetQueryUrlFirst")
167            );
168        }
169
170        @Override
171        public void actionPerformed(ActionEvent arg0) {
172            try {
173                switch(tpQueryPanels.getSelectedIndex()) {
174                case 0:
175                    // currently, query specifications can't be invalid in the basic query panel.
176                    // We select from a couple of predefined queries and there is always a query
177                    // selected
178                    break;
179                case 1:
180                    if (getChangesetQuery() == null) {
181                        alertInvalidChangesetQuery();
182                        pnlUrlBasedQueries.startUserInput();
183                        return;
184                    }
185                    break;
186
187                case 2:
188                    if (getChangesetQuery() == null) {
189                        pnlAdvancedQueries.displayMessageIfInvalid();
190                        return;
191                    }
192                }
193                setCanceled(false);
194                setVisible(false);
195            } catch (IllegalStateException e) {
196                JOptionPane.showMessageDialog(ChangesetQueryDialog.this, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
197            }
198        }
199    }
200
201    class CancelAction extends AbstractAction {
202
203        CancelAction() {
204            putValue(NAME, tr("Cancel"));
205            putValue(SMALL_ICON, ImageProvider.get("cancel"));
206            putValue(SHORT_DESCRIPTION, tr("Close the dialog and abort querying of changesets"));
207        }
208
209        public void cancel() {
210            setCanceled(true);
211            setVisible(false);
212        }
213
214        @Override
215        public void actionPerformed(ActionEvent arg0) {
216            cancel();
217        }
218    }
219
220    class WindowEventHandler extends WindowAdapter {
221        @Override
222        public void windowClosing(WindowEvent arg0) {
223            new CancelAction().cancel();
224        }
225    }
226}