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