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.JButton;
018import javax.swing.JComponent;
019import javax.swing.JDialog;
020import javax.swing.JOptionPane;
021import javax.swing.JPanel;
022import javax.swing.JTabbedPane;
023import javax.swing.KeyStroke;
024
025import org.openstreetmap.josm.gui.HelpAwareOptionPane;
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        pnl.add(new JButton(new QueryAction()));
077        pnl.add(new JButton(new CancelAction()));
078        pnl.add(new JButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/ChangesetQuery"))));
079
080        return pnl;
081    }
082
083    protected final void build() {
084        setTitle(tr("Query changesets"));
085        Container cp = getContentPane();
086        cp.setLayout(new BorderLayout());
087        cp.add(buildContentPanel(), BorderLayout.CENTER);
088        cp.add(buildButtonPanel(), BorderLayout.SOUTH);
089
090        // cancel on ESC
091        getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
092        getRootPane().getActionMap().put("cancel", new CancelAction());
093
094        // context sensitive help
095        HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/ChangesetQueryDialog"));
096
097        addWindowListener(new WindowEventHandler());
098    }
099
100    public boolean isCanceled() {
101        return canceled;
102    }
103
104    public void initForUserInput() {
105        pnlBasicChangesetQueries.init();
106    }
107
108    protected void setCanceled(boolean canceled) {
109        this.canceled = canceled;
110    }
111
112    public ChangesetQuery getChangesetQuery() {
113        if (isCanceled())
114            return null;
115        switch(tpQueryPanels.getSelectedIndex()) {
116        case 0:
117            return pnlBasicChangesetQueries.buildChangesetQuery();
118        case 1:
119            return pnlUrlBasedQueries.buildChangesetQuery();
120        case 2:
121            return pnlAdvancedQueries.buildChangesetQuery();
122        default:
123            // FIXME: extend with advanced queries
124            return null;
125        }
126    }
127
128    public void startUserInput() {
129        pnlUrlBasedQueries.startUserInput();
130        pnlAdvancedQueries.startUserInput();
131    }
132
133    @Override
134    public void setVisible(boolean visible) {
135        if (visible) {
136            new WindowGeometry(
137                    getClass().getName() + ".geometry",
138                    WindowGeometry.centerInWindow(
139                            getParent(),
140                            new Dimension(400, 400)
141                    )
142            ).applySafe(this);
143            setCanceled(false);
144            startUserInput();
145        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
146            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
147            pnlAdvancedQueries.rememberSettings();
148        }
149        super.setVisible(visible);
150    }
151
152    class QueryAction extends AbstractAction {
153        QueryAction() {
154            putValue(NAME, tr("Query"));
155            new ImageProvider("dialogs", "search").getResource().attachImageIcon(this);
156            putValue(SHORT_DESCRIPTION, tr("Query and download changesets"));
157        }
158
159        protected void alertInvalidChangesetQuery() {
160            HelpAwareOptionPane.showOptionDialog(
161                    ChangesetQueryDialog.this,
162                    tr("Please enter a valid changeset query URL first."),
163                    tr("Illegal changeset query URL"),
164                    JOptionPane.WARNING_MESSAGE,
165                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#EnterAValidChangesetQueryUrlFirst")
166            );
167        }
168
169        @Override
170        public void actionPerformed(ActionEvent arg0) {
171            try {
172                switch(tpQueryPanels.getSelectedIndex()) {
173                case 0:
174                    // currently, query specifications can't be invalid in the basic query panel.
175                    // We select from a couple of predefined queries and there is always a query
176                    // selected
177                    break;
178                case 1:
179                    if (getChangesetQuery() == null) {
180                        alertInvalidChangesetQuery();
181                        pnlUrlBasedQueries.startUserInput();
182                        return;
183                    }
184                    break;
185                case 2:
186                    if (getChangesetQuery() == null) {
187                        pnlAdvancedQueries.displayMessageIfInvalid();
188                        return;
189                    }
190                }
191                setCanceled(false);
192                setVisible(false);
193            } catch (IllegalStateException e) {
194                JOptionPane.showMessageDialog(ChangesetQueryDialog.this, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
195            }
196        }
197    }
198
199    class CancelAction extends AbstractAction {
200
201        CancelAction() {
202            putValue(NAME, tr("Cancel"));
203            new ImageProvider("cancel").getResource().attachImageIcon(this);
204            putValue(SHORT_DESCRIPTION, tr("Close the dialog and abort querying of changesets"));
205        }
206
207        public void cancel() {
208            setCanceled(true);
209            setVisible(false);
210        }
211
212        @Override
213        public void actionPerformed(ActionEvent arg0) {
214            cancel();
215        }
216    }
217
218    class WindowEventHandler extends WindowAdapter {
219        @Override
220        public void windowClosing(WindowEvent arg0) {
221            new CancelAction().cancel();
222        }
223    }
224}