source: josm/trunk/src/org/openstreetmap/josm/gui/download/DownloadDialog.java @ 5241

Revision 5200, 16.3 KB checked in by akks, 5 weeks ago (diff)

see #7626, fix #7463: keys Ctrl-Shift-Up/Down, Enter, Spacebar work better in toggle dialogs
Enter and Spacebar = useful actions for list items (select, toggle, etc.)

  • Property svn:eol-style set to native
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.download;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.BorderLayout;
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Dimension;
11import java.awt.FlowLayout;
12import java.awt.Font;
13import java.awt.Graphics;
14import java.awt.GridBagLayout;
15import java.awt.event.ActionEvent;
16import java.awt.event.ActionListener;
17import java.awt.event.InputEvent;
18import java.awt.event.KeyEvent;
19import java.awt.event.WindowAdapter;
20import java.awt.event.WindowEvent;
21import java.util.ArrayList;
22import java.util.List;
23
24import javax.swing.AbstractAction;
25import javax.swing.JCheckBox;
26import javax.swing.JComponent;
27import javax.swing.JDialog;
28import javax.swing.JLabel;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31import javax.swing.JTabbedPane;
32import javax.swing.KeyStroke;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.ExpertToggleAction;
36import org.openstreetmap.josm.data.Bounds;
37import org.openstreetmap.josm.gui.MapView;
38import org.openstreetmap.josm.gui.SideButton;
39import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
40import org.openstreetmap.josm.gui.help.HelpUtil;
41import org.openstreetmap.josm.plugins.PluginHandler;
42import org.openstreetmap.josm.tools.GBC;
43import org.openstreetmap.josm.tools.ImageProvider;
44import org.openstreetmap.josm.tools.InputMapUtils;
45import org.openstreetmap.josm.tools.OsmUrlToBounds;
46import org.openstreetmap.josm.tools.Utils;
47import org.openstreetmap.josm.tools.WindowGeometry;
48
49/**
50 *
51 */
52public class DownloadDialog extends JDialog  {
53    /** the unique instance of the download dialog */
54    static private DownloadDialog instance;
55
56    /**
57     * Replies the unique instance of the download dialog
58     *
59     * @return the unique instance of the download dialog
60     */
61    static public DownloadDialog getInstance() {
62        if (instance == null) {
63            instance = new DownloadDialog(Main.parent);
64        }
65        return instance;
66    }
67
68    protected final List<DownloadSelection> downloadSelections = new ArrayList<DownloadSelection>();
69    protected final JTabbedPane tpDownloadAreaSelectors = new JTabbedPane();
70    protected JCheckBox cbNewLayer;
71    protected JCheckBox cbStartup;
72    protected final JLabel sizeCheck = new JLabel();
73    protected Bounds currentBounds = null;
74    protected boolean canceled;
75
76    protected JCheckBox cbDownloadOsmData;
77    protected JCheckBox cbDownloadGpxData;
78    /** the download action and button */
79    private DownloadAction actDownload;
80    protected SideButton btnDownload;
81
82    private void makeCheckBoxRespondToEnter(JCheckBox cb) {
83        cb.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "doDownload");
84        cb.getActionMap().put("doDownload", actDownload);
85    }
86
87    protected JPanel buildMainPanel() {
88        JPanel pnl = new JPanel();
89        pnl.setLayout(new GridBagLayout());
90
91        // adding the download tasks
92        pnl.add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5,5,1,5));
93        cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
94        cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
95        pnl.add(cbDownloadOsmData,  GBC.std().insets(1,5,1,5));
96        cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
97        cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
98        pnl.add(cbDownloadGpxData,  GBC.eol().insets(5,5,1,5));
99
100        // hook for subclasses
101        buildMainPanelAboveDownloadSelections(pnl);
102
103        // predefined download selections
104        downloadSelections.add(new SlippyMapChooser());
105        downloadSelections.add(new BookmarkSelection());
106        downloadSelections.add(new BoundingBoxSelection());
107        downloadSelections.add(new PlaceSelection());
108        downloadSelections.add(new TileSelection());
109
110        // add selections from plugins
111        PluginHandler.addDownloadSelection(downloadSelections);
112
113        // now everybody may add their tab to the tabbed pane
114        // (not done right away to allow plugins to remove one of
115        // the default selectors!)
116        for (DownloadSelection s : downloadSelections) {
117            s.addGui(this);
118        }
119
120        pnl.add(tpDownloadAreaSelectors, GBC.eol().fill());
121
122        try {
123            tpDownloadAreaSelectors.setSelectedIndex(Main.pref.getInteger("download.tab", 0));
124        } catch (Exception ex) {
125            Main.pref.putInteger("download.tab", 0);
126        }
127
128        Font labelFont = sizeCheck.getFont();
129        sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
130
131        cbNewLayer = new JCheckBox(tr("Download as new layer"));
132        cbNewLayer.setToolTipText(tr("<html>Select to download data into a new data layer.<br>"
133                +"Unselect to download into the currently active data layer.</html>"));
134
135        cbStartup = new JCheckBox(tr("Open this dialog on startup"));
136        cbStartup.setToolTipText(tr("<html>Autostart ''Download from OSM'' dialog every time JOSM is started.<br>You can open it manually from File menu or toolbar.</html>"));
137        cbStartup.addActionListener(new ActionListener() {
138            public void actionPerformed(ActionEvent e) {
139                 Main.pref.put("download.autorun", cbStartup.isSelected());
140            }});
141
142        pnl.add(cbNewLayer, GBC.std().anchor(GBC.WEST).insets(5,5,5,5));
143        pnl.add(cbStartup, GBC.std().anchor(GBC.WEST).insets(15,5,5,5));
144
145        pnl.add(sizeCheck,  GBC.eol().anchor(GBC.EAST).insets(5,5,5,2));
146       
147        if (!ExpertToggleAction.isExpert()) {
148            JLabel infoLabel  = new JLabel(tr("Use left click&drag to select area, arrows or right mouse button to scroll map, wheel or +/- to zoom."));
149            pnl.add(infoLabel,GBC.eol().anchor(GBC.SOUTH).insets(0,0,0,0));
150        }
151        return pnl;
152    }
153
154    /* This should not be necessary, but if not here, repaint is not always correct in SlippyMap! */
155    public void paint(Graphics g) {
156        tpDownloadAreaSelectors.getSelectedComponent().paint(g);
157        super.paint(g);
158    }
159
160    protected JPanel buildButtonPanel() {
161        JPanel pnl = new JPanel();
162        pnl.setLayout(new FlowLayout());
163
164        // -- download button
165        pnl.add(btnDownload = new SideButton(actDownload = new DownloadAction()));
166        InputMapUtils.enableEnter(btnDownload);
167       
168        makeCheckBoxRespondToEnter(cbDownloadGpxData);
169        makeCheckBoxRespondToEnter(cbDownloadOsmData);
170        makeCheckBoxRespondToEnter(cbNewLayer);
171
172        // -- cancel button
173        SideButton btnCancel;
174        CancelAction actCancel = new CancelAction();
175        pnl.add(btnCancel = new SideButton(actCancel));
176        InputMapUtils.enableEnter(btnCancel);
177
178        // -- cancel on ESC
179        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "cancel");
180        getRootPane().getActionMap().put("cancel", actCancel);
181
182        // -- help button
183        SideButton btnHelp;
184        pnl.add(btnHelp = new SideButton(new ContextSensitiveHelpAction(ht("/Action/Download"))));
185        InputMapUtils.enableEnter(btnHelp);
186
187        return pnl;
188    }
189
190    public DownloadDialog(Component parent) {
191        super(JOptionPane.getFrameForComponent(parent),tr("Download"), ModalityType.DOCUMENT_MODAL);
192        getContentPane().setLayout(new BorderLayout());
193        getContentPane().add(buildMainPanel(), BorderLayout.CENTER);
194        getContentPane().add(buildButtonPanel(), BorderLayout.SOUTH);
195
196        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
197                KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK), "checkClipboardContents");
198
199        getRootPane().getActionMap().put("checkClipboardContents", new AbstractAction() {
200            public void actionPerformed(ActionEvent e) {
201                String clip = Utils.getClipboardContent();
202                if (clip == null) {
203                    return;
204                }
205                Bounds b = OsmUrlToBounds.parse(clip);
206                if (b != null) {
207                    boundingBoxChanged(new Bounds(b), null);
208                }
209            }
210        });
211        HelpUtil.setHelpContext(getRootPane(), ht("/Action/Download"));
212        addWindowListener(new WindowEventHandler());
213        restoreSettings();
214    }
215
216    private void updateSizeCheck() {
217        if (currentBounds == null) {
218            sizeCheck.setText(tr("No area selected yet"));
219            sizeCheck.setForeground(Color.darkGray);
220        } else if (currentBounds.getArea() > Main.pref.getDouble("osm-server.max-request-area", 0.25)) {
221            sizeCheck.setText(tr("Download area too large; will probably be rejected by server"));
222            sizeCheck.setForeground(Color.red);
223        } else {
224            sizeCheck.setText(tr("Download area ok, size probably acceptable to server"));
225            sizeCheck.setForeground(Color.darkGray);
226        }
227    }
228
229    /**
230     * Distributes a "bounding box changed" from one DownloadSelection
231     * object to the others, so they may update or clear their input
232     * fields.
233     *
234     * @param eventSource - the DownloadSelection object that fired this notification.
235     */
236    public void boundingBoxChanged(Bounds b, DownloadSelection eventSource) {
237        this.currentBounds = b;
238        for (DownloadSelection s : downloadSelections) {
239            if (s != eventSource) {
240                s.setDownloadArea(currentBounds);
241            }
242        }
243        updateSizeCheck();
244    }
245
246    /**
247     * Invoked by
248     * @param b
249     */
250    public void startDownload(Bounds b) {
251        this.currentBounds = b;
252        actDownload.run();
253    }
254
255    /**
256     * Replies true if the user selected to download OSM data
257     *
258     * @return true if the user selected to download OSM data
259     */
260    public boolean isDownloadOsmData() {
261        return cbDownloadOsmData.isSelected();
262    }
263
264    /**
265     * Replies true if the user selected to download GPX data
266     *
267     * @return true if the user selected to download GPX data
268     */
269    public boolean isDownloadGpxData() {
270        return cbDownloadGpxData.isSelected();
271    }
272
273    /**
274     * Replies true if the user requires to download into a new layer
275     *
276     * @return true if the user requires to download into a new layer
277     */
278    public boolean isNewLayerRequired() {
279        return cbNewLayer.isSelected();
280    }
281
282    /**
283     * Adds a new download area selector to the download dialog
284     *
285     * @param selector the download are selector
286     * @param displayName the display name of the selector
287     */
288    public void addDownloadAreaSelector(JPanel selector, String displayName) {
289        tpDownloadAreaSelectors.add(displayName, selector);
290    }
291
292    /**
293     * Remembers the current settings in the download dialog
294     *
295     */
296    public void rememberSettings() {
297        Main.pref.put("download.tab", Integer.toString(tpDownloadAreaSelectors.getSelectedIndex()));
298        Main.pref.put("download.osm", cbDownloadOsmData.isSelected());
299        Main.pref.put("download.gps", cbDownloadGpxData.isSelected());
300        Main.pref.put("download.newlayer", cbNewLayer.isSelected());
301        if (currentBounds != null) {
302            Main.pref.put("osm-download.bounds", currentBounds.encodeAsString(";"));
303        }
304    }
305
306    public void restoreSettings() {
307        cbDownloadOsmData.setSelected(Main.pref.getBoolean("download.osm", true));
308        cbDownloadGpxData.setSelected(Main.pref.getBoolean("download.gps", false));
309        cbNewLayer.setSelected(Main.pref.getBoolean("download.newlayer", false));
310        cbStartup.setSelected( isAutorunEnabled() ); 
311        int idx = Main.pref.getInteger("download.tab", 0);
312        if (idx < 0 || idx > tpDownloadAreaSelectors.getTabCount()) {
313            idx = 0;
314        }
315        tpDownloadAreaSelectors.setSelectedIndex(idx);
316
317        if (Main.map != null) {
318            MapView mv = Main.map.mapView;
319            currentBounds = new Bounds(
320                    mv.getLatLon(0, mv.getHeight()),
321                    mv.getLatLon(mv.getWidth(), 0)
322            );
323            boundingBoxChanged(currentBounds,null);
324        }
325        else if (!Main.pref.get("osm-download.bounds").isEmpty()) {
326            // read the bounding box from the preferences
327            try {
328                currentBounds = new Bounds(Main.pref.get("osm-download.bounds"), ";");
329                boundingBoxChanged(currentBounds,null);
330            }
331            catch (Exception e) {
332                e.printStackTrace();
333            }
334        }
335    }
336
337    public static boolean isAutorunEnabled() {
338        //String autorun=Main.pref.get("download.autorun",null);
339        //boolean expert=ExpertToggleAction.isExpert();
340        //return (autorun==null && !expert) || "true".equals(autorun) ;
341        return Main.pref.getBoolean("download.autorun",false);
342    }
343
344    public static void autostartIfNeeded() {
345        if (isAutorunEnabled()) {
346            Main.main.menu.download.actionPerformed(null);
347        }
348    }
349
350    /**
351     * Replies the currently selected download area. May be null, if no download area is selected
352     * yet.
353     */
354    public Bounds getSelectedDownloadArea() {
355        return currentBounds;
356    }
357
358    @Override
359    public void setVisible(boolean visible) {
360        if (visible) {
361            new WindowGeometry(
362                    getClass().getName() + ".geometry",
363                    WindowGeometry.centerInWindow(
364                            getParent(),
365                            new Dimension(1000,600)
366                    )
367            ).applySafe(this);
368        } else if (!visible && isShowing()){
369            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
370        }
371        super.setVisible(visible);
372    }
373
374    /**
375     * Replies true if the dialog was canceled
376     *
377     * @return true if the dialog was canceled
378     */
379    public boolean isCanceled() {
380        return canceled;
381    }
382
383    protected void setCanceled(boolean canceled) {
384        this.canceled = canceled;
385    }
386
387    protected void buildMainPanelAboveDownloadSelections(JPanel pnl) {
388    }
389
390    class CancelAction extends AbstractAction {
391        public CancelAction() {
392            putValue(NAME, tr("Cancel"));
393            putValue(SMALL_ICON, ImageProvider.get("cancel"));
394            putValue(SHORT_DESCRIPTION, tr("Click to close the dialog and to abort downloading"));
395        }
396
397        public void run() {
398            setCanceled(true);
399            setVisible(false);
400        }
401
402        public void actionPerformed(ActionEvent e) {
403            run();
404        }
405    }
406
407    class DownloadAction extends AbstractAction {
408        public DownloadAction() {
409            putValue(NAME, tr("Download"));
410            putValue(SMALL_ICON, ImageProvider.get("download"));
411            putValue(SHORT_DESCRIPTION, tr("Click to download the currently selected area"));
412        }
413
414        public void run() {
415            if (currentBounds == null) {
416                JOptionPane.showMessageDialog(
417                        DownloadDialog.this,
418                        tr("Please select a download area first."),
419                        tr("Error"),
420                        JOptionPane.ERROR_MESSAGE
421                );
422                return;
423            }
424            if (!isDownloadOsmData() && !isDownloadGpxData()) {
425                JOptionPane.showMessageDialog(
426                        DownloadDialog.this,
427                        tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> is enabled.<br>"
428                                + "Please choose to either download OSM data, or GPX data, or both.</html>",
429                                cbDownloadOsmData.getText(),
430                                cbDownloadGpxData.getText()
431                        ),
432                        tr("Error"),
433                        JOptionPane.ERROR_MESSAGE
434                );
435                return;
436            }
437            setCanceled(false);
438            setVisible(false);
439        }
440
441        public void actionPerformed(ActionEvent e) {
442            run();
443        }
444    }
445
446    class WindowEventHandler extends WindowAdapter {
447        @Override
448        public void windowClosing(WindowEvent e) {
449            new CancelAction().run();
450        }
451
452        @Override
453        public void windowActivated(WindowEvent e) {
454            btnDownload.requestFocusInWindow();
455        }
456    }
457}
Note: See TracBrowser for help on using the repository browser.