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