001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs;
003
004import org.openstreetmap.josm.Main;
005import org.openstreetmap.josm.data.osm.PrimitiveId;
006import org.openstreetmap.josm.gui.ExtendedDialog;
007import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
008import org.openstreetmap.josm.gui.widgets.HtmlPanel;
009import org.openstreetmap.josm.gui.widgets.JosmTextField;
010import org.openstreetmap.josm.gui.widgets.OsmIdTextField;
011import org.openstreetmap.josm.gui.widgets.OsmPrimitiveTypesComboBox;
012import org.openstreetmap.josm.tools.Utils;
013
014import javax.swing.BorderFactory;
015import javax.swing.GroupLayout;
016import javax.swing.JLabel;
017import javax.swing.JOptionPane;
018import javax.swing.JPanel;
019import javax.swing.KeyStroke;
020import javax.swing.border.EtchedBorder;
021import javax.swing.plaf.basic.BasicComboBoxEditor;
022import java.awt.Component;
023import java.awt.Dimension;
024import java.awt.event.ItemEvent;
025import java.awt.event.ItemListener;
026import java.awt.event.KeyEvent;
027import java.awt.event.WindowEvent;
028import java.awt.event.WindowListener;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.LinkedList;
032import java.util.List;
033
034import static org.openstreetmap.josm.tools.I18n.tr;
035import static org.openstreetmap.josm.tools.I18n.trc;
036
037/**
038 * Dialog prompt to user to let him choose OSM primitives by specifying their type and IDs.
039 * @since 6448, split from DownloadObjectDialog
040 */
041public class OsmIdSelectionDialog extends ExtendedDialog implements WindowListener {
042
043    protected final JPanel panel = new JPanel();
044    protected final OsmPrimitiveTypesComboBox cbType = new OsmPrimitiveTypesComboBox();
045    protected final OsmIdTextField tfId = new OsmIdTextField();
046    protected final HistoryComboBox cbId = new HistoryComboBox();
047    protected final GroupLayout layout = new GroupLayout(panel);
048
049    public OsmIdSelectionDialog(Component parent, String title, String[] buttonTexts) {
050        super(parent, title, buttonTexts);
051    }
052
053    public OsmIdSelectionDialog(Component parent, String title, String[] buttonTexts, boolean modal) {
054        super(parent, title, buttonTexts, modal);
055    }
056
057    public OsmIdSelectionDialog(Component parent, String title, String[] buttonTexts, boolean modal, boolean disposeOnClose) {
058        super(parent, title, buttonTexts, modal, disposeOnClose);
059    }
060
061    protected void init() {
062        panel.setLayout(layout);
063        layout.setAutoCreateGaps(true);
064        layout.setAutoCreateContainerGaps(true);
065
066        JLabel lbl1 = new JLabel(tr("Object type:"));
067
068        cbType.addItem(trc("osm object types", "mixed"));
069        cbType.setToolTipText(tr("Choose the OSM object type"));
070        JLabel lbl2 = new JLabel(tr("Object ID:"));
071
072        cbId.setEditor(new BasicComboBoxEditor() {
073            @Override
074            protected JosmTextField createEditorComponent() {
075                return tfId;
076            }
077        });
078        cbId.setToolTipText(tr("Enter the ID of the object that should be downloaded"));
079        restorePrimitivesHistory(cbId);
080
081        // forward the enter key stroke to the download button
082        tfId.getKeymap().removeKeyStrokeBinding(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false));
083        tfId.setPreferredSize(new Dimension(400, tfId.getPreferredSize().height));
084
085        HtmlPanel help = new HtmlPanel(tr("Object IDs can be separated by comma or space.<br/>"
086                + " Examples: <b><ul><li>1 2 5</li><li>1,2,5</li></ul><br/></b>"
087                + " In mixed mode, specify objects like this: <b>w123, n110, w12, r15</b><br/>"));
088        help.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
089
090        cbType.addItemListener(new ItemListener() {
091            @Override
092            public void itemStateChanged(ItemEvent e) {
093                tfId.setType(cbType.getType());
094                tfId.performValidation();
095            }
096        });
097
098        final GroupLayout.SequentialGroup sequentialGroup = layout.createSequentialGroup()
099                .addGroup(layout.createParallelGroup()
100                        .addComponent(lbl1)
101                        .addComponent(cbType, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE))
102                .addGroup(layout.createParallelGroup()
103                        .addComponent(lbl2)
104                        .addComponent(cbId, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE, GroupLayout.PREFERRED_SIZE));
105
106        final GroupLayout.ParallelGroup parallelGroup = layout.createParallelGroup()
107                .addGroup(layout.createSequentialGroup()
108                        .addGroup(layout.createParallelGroup()
109                                .addComponent(lbl1)
110                                .addComponent(lbl2)
111                        )
112                        .addGroup(layout.createParallelGroup()
113                                .addComponent(cbType)
114                                .addComponent(cbId))
115                );
116
117        for (Component i : getComponentsBeforeHelp()) {
118            sequentialGroup.addComponent(i);
119            parallelGroup.addComponent(i);
120        }
121
122        layout.setVerticalGroup(sequentialGroup.addComponent(help));
123        layout.setHorizontalGroup(parallelGroup.addComponent(help));
124    }
125
126    /**
127     * Let subclasses add custom components between the id input field and the help text
128     * @return the collections to add
129     */
130    protected Collection<Component> getComponentsBeforeHelp() {
131        return Collections.emptySet();
132    }
133
134    /**
135     * Allows subclasses to specify a different continue button index. If this button is pressed, the history is updated.
136     * @return the button index
137     */
138    public int getContinueButtonIndex() {
139        return 1;
140    }
141
142    /**
143     * Restore the current history from the preferences
144     *
145     * @param cbHistory the {@link HistoryComboBox} to which the history is restored to
146     */
147    protected void restorePrimitivesHistory(HistoryComboBox cbHistory) {
148        java.util.List<String> cmtHistory = new LinkedList<String>(Main.pref.getCollection(getClass().getName() + ".primitivesHistory", new LinkedList<String>()));
149        // we have to reverse the history, because ComboBoxHistory will reverse it again in addElement()
150        Collections.reverse(cmtHistory);
151        cbHistory.setPossibleItems(cmtHistory);
152    }
153
154    /**
155     * Remind the current history in the preferences
156     *
157     * @param cbHistory the {@link HistoryComboBox} of which to restore the history
158     */
159    protected void remindPrimitivesHistory(HistoryComboBox cbHistory) {
160        cbHistory.addCurrentItemToHistory();
161        Main.pref.putCollection(getClass().getName() + ".primitivesHistory", cbHistory.getHistory());
162    }
163
164    /**
165     * Gets the requested OSM object IDs.
166     *
167     * @return The list of requested OSM object IDs
168     */
169    public final List<PrimitiveId> getOsmIds() {
170        return tfId.getIds();
171    }
172
173    @Override
174    public void setupDialog() {
175        setContent(panel, false);
176        cbType.setSelectedIndex(Main.pref.getInteger("downloadprimitive.lasttype", 0));
177        tfId.setType(cbType.getType());
178        if (Main.pref.getBoolean("downloadprimitive.autopaste", true)) {
179            tryToPasteFromClipboard(tfId, cbType);
180        }
181        setDefaultButton(getContinueButtonIndex());
182        addWindowListener(this);
183        super.setupDialog();
184    }
185
186    protected void tryToPasteFromClipboard(OsmIdTextField tfId, OsmPrimitiveTypesComboBox cbType) {
187        String buf = Utils.getClipboardContent();
188        if (buf != null) {
189            if (buf.contains("node")) cbType.setSelectedIndex(0);
190            if (buf.contains("way")) cbType.setSelectedIndex(1);
191            if (buf.contains("relation")) cbType.setSelectedIndex(2);
192            String[] res = buf.split("/");
193            String txt;
194            if (res.length > 0) {
195                txt = res[res.length - 1];
196                if (txt.isEmpty() && txt.length() > 1) txt = res[res.length - 2];
197            } else {
198                txt = buf;
199            }
200            if (buf.length() <= Main.pref.getInteger("downloadprimitive.max-autopaste-length", 2000)) {
201                tfId.tryToPasteFrom(txt);
202            }
203        }
204    }
205
206    @Override public void windowClosed(WindowEvent e) {
207        if (e != null && e.getComponent() == this && getValue() == getContinueButtonIndex()) {
208            Main.pref.putInteger("downloadprimitive.lasttype", cbType.getSelectedIndex());
209
210            if (!tfId.readIds()) {
211                JOptionPane.showMessageDialog(getParent(),
212                        tr("Invalid ID list specified\n"
213                                + "Cannot continue."),
214                        tr("Information"),
215                        JOptionPane.INFORMATION_MESSAGE
216                );
217                return;
218            }
219
220            remindPrimitivesHistory(cbId);
221        }
222    }
223
224    @Override public void windowOpened(WindowEvent e) {}
225    @Override public void windowClosing(WindowEvent e) {}
226    @Override public void windowIconified(WindowEvent e) {}
227    @Override public void windowDeiconified(WindowEvent e) {}
228    @Override public void windowActivated(WindowEvent e) {}
229    @Override public void windowDeactivated(WindowEvent e) {}
230}