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}