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