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.gui.HelpAwareOptionPane;
023import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
024import org.openstreetmap.josm.gui.help.HelpUtil;
025import org.openstreetmap.josm.gui.util.WindowGeometry;
026import org.openstreetmap.josm.io.ChangesetQuery;
027import org.openstreetmap.josm.tools.ImageProvider;
028import org.openstreetmap.josm.tools.InputMapUtils;
029import org.openstreetmap.josm.tools.Logging;
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    /**
099     * Determines if the dialog has been canceled.
100     * @return {@code true} if the dialog has been canceled
101     */
102    public boolean isCanceled() {
103        return canceled;
104    }
105
106    /**
107     * Initializes HMI for user input.
108     */
109    public void initForUserInput() {
110        pnlBasicChangesetQueries.init();
111    }
112
113    protected void setCanceled(boolean canceled) {
114        this.canceled = canceled;
115    }
116
117    /**
118     * Returns the changeset query.
119     * @return the changeset query
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    /**
138     * Initializes HMI for user input.
139     */
140    public void startUserInput() {
141        pnlUrlBasedQueries.startUserInput();
142        pnlAdvancedQueries.startUserInput();
143    }
144
145    @Override
146    public void setVisible(boolean visible) {
147        if (visible) {
148            new WindowGeometry(
149                    getClass().getName() + ".geometry",
150                    WindowGeometry.centerInWindow(
151                            getParent(),
152                            new Dimension(400, 400)
153                    )
154            ).applySafe(this);
155            setCanceled(false);
156            startUserInput();
157        } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
158            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
159            pnlAdvancedQueries.rememberSettings();
160        }
161        super.setVisible(visible);
162    }
163
164    class QueryAction extends AbstractAction {
165        QueryAction() {
166            putValue(NAME, tr("Query"));
167            new ImageProvider("dialogs", "search").getResource().attachImageIcon(this);
168            putValue(SHORT_DESCRIPTION, tr("Query and download changesets"));
169        }
170
171        protected void alertInvalidChangesetQuery() {
172            HelpAwareOptionPane.showOptionDialog(
173                    ChangesetQueryDialog.this,
174                    tr("Please enter a valid changeset query URL first."),
175                    tr("Illegal changeset query URL"),
176                    JOptionPane.WARNING_MESSAGE,
177                    HelpUtil.ht("/Dialog/ChangesetQueryDialog#EnterAValidChangesetQueryUrlFirst")
178            );
179        }
180
181        @Override
182        public void actionPerformed(ActionEvent arg0) {
183            try {
184                switch(tpQueryPanels.getSelectedIndex()) {
185                case 0:
186                    // currently, query specifications can't be invalid in the basic query panel.
187                    // We select from a couple of predefined queries and there is always a query
188                    // selected
189                    break;
190                case 1:
191                    if (getChangesetQuery() == null) {
192                        alertInvalidChangesetQuery();
193                        pnlUrlBasedQueries.startUserInput();
194                        return;
195                    }
196                    break;
197                case 2:
198                    if (getChangesetQuery() == null) {
199                        pnlAdvancedQueries.displayMessageIfInvalid();
200                        return;
201                    }
202                }
203                setCanceled(false);
204                setVisible(false);
205            } catch (IllegalStateException e) {
206                Logging.error(e);
207                JOptionPane.showMessageDialog(ChangesetQueryDialog.this, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
208            }
209        }
210    }
211
212    class CancelAction extends AbstractAction {
213
214        CancelAction() {
215            putValue(NAME, tr("Cancel"));
216            new ImageProvider("cancel").getResource().attachImageIcon(this);
217            putValue(SHORT_DESCRIPTION, tr("Close the dialog and abort querying of changesets"));
218        }
219
220        public void cancel() {
221            setCanceled(true);
222            setVisible(false);
223        }
224
225        @Override
226        public void actionPerformed(ActionEvent arg0) {
227            cancel();
228        }
229    }
230
231    class WindowEventHandler extends WindowAdapter {
232        @Override
233        public void windowClosing(WindowEvent arg0) {
234            new CancelAction().cancel();
235        }
236    }
237}