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