source: josm/trunk/src/org/openstreetmap/josm/gui/io/UploadDialog.java @ 5241

Revision 5200, 18.8 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. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.io;
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.Dimension;
9import java.awt.FlowLayout;
10import java.awt.Image;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.WindowAdapter;
14import java.awt.event.WindowEvent;
15import java.beans.PropertyChangeEvent;
16import java.beans.PropertyChangeListener;
17import java.util.Collections;
18import java.util.List;
19import java.util.Map;
20
21import javax.swing.AbstractAction;
22import javax.swing.BorderFactory;
23import javax.swing.Icon;
24import javax.swing.ImageIcon;
25import javax.swing.InputMap;
26import javax.swing.JButton;
27import javax.swing.JComponent;
28import javax.swing.JDialog;
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.data.APIDataSet;
36import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
37import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
38import org.openstreetmap.josm.data.osm.Changeset;
39import org.openstreetmap.josm.data.osm.OsmPrimitive;
40import org.openstreetmap.josm.gui.ExtendedDialog;
41import org.openstreetmap.josm.gui.HelpAwareOptionPane;
42import org.openstreetmap.josm.gui.SideButton;
43import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
44import org.openstreetmap.josm.gui.help.HelpUtil;
45import org.openstreetmap.josm.io.OsmApi;
46import org.openstreetmap.josm.tools.ImageProvider;
47import org.openstreetmap.josm.tools.InputMapUtils;
48import org.openstreetmap.josm.tools.WindowGeometry;
49
50/**
51 * This is a dialog for entering upload options like the parameters for
52 * the upload changeset and the strategy for opening/closing a changeset.
53 *
54 */
55public class UploadDialog extends JDialog implements PropertyChangeListener, PreferenceChangedListener{
56    /**  the unique instance of the upload dialog */
57    static private UploadDialog uploadDialog;
58
59    /**
60     * Replies the unique instance of the upload dialog
61     *
62     * @return the unique instance of the upload dialog
63     */
64    static public UploadDialog getUploadDialog() {
65        if (uploadDialog == null) {
66            uploadDialog = new UploadDialog();
67        }
68        return uploadDialog;
69    }
70
71    /** the panel with the objects to upload */
72    private UploadedObjectsSummaryPanel pnlUploadedObjects;
73    /** the panel to select the changeset used */
74    private ChangesetManagementPanel pnlChangesetManagement;
75
76    private BasicUploadSettingsPanel pnlBasicUploadSettings;
77
78    private UploadStrategySelectionPanel pnlUploadStrategySelectionPanel;
79
80    /** checkbox for selecting whether an atomic upload is to be used  */
81    private TagSettingsPanel pnlTagSettings;
82    /** the tabbed pane used below of the list of primitives  */
83    private JTabbedPane tpConfigPanels;
84    /** the upload button */
85    private JButton btnUpload;
86    private boolean canceled = false;
87
88    /** the changeset comment model keeping the state of the changeset comment */
89    private ChangesetCommentModel changesetCommentModel;
90
91    /**
92     * builds the content panel for the upload dialog
93     *
94     * @return the content panel
95     */
96    protected JPanel buildContentPanel() {
97        JPanel pnl = new JPanel();
98        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
99        pnl.setLayout(new BorderLayout());
100
101        // the panel with the list of uploaded objects
102        //
103        pnl.add(pnlUploadedObjects = new UploadedObjectsSummaryPanel(), BorderLayout.CENTER);
104
105        // a tabbed pane with two configuration panels in the
106        // lower half
107        //
108        tpConfigPanels = new JTabbedPane() {
109            @Override
110            public Dimension getPreferredSize() {
111                // make sure the tabbed pane never grabs more space than necessary
112                //
113                return super.getMinimumSize();
114            }
115        };
116        tpConfigPanels.add(new JPanel());
117        tpConfigPanels.add(new JPanel());
118        tpConfigPanels.add(new JPanel());
119        tpConfigPanels.add(new JPanel());
120
121        changesetCommentModel = new ChangesetCommentModel();
122
123        tpConfigPanels.setComponentAt(0, pnlBasicUploadSettings = new BasicUploadSettingsPanel(changesetCommentModel));
124        tpConfigPanels.setTitleAt(0, tr("Settings"));
125        tpConfigPanels.setToolTipTextAt(0, tr("Decide how to upload the data and which changeset to use"));
126
127        tpConfigPanels.setComponentAt(1,pnlTagSettings = new TagSettingsPanel(changesetCommentModel));
128        tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
129        tpConfigPanels.setToolTipTextAt(1, tr("Apply tags to the changeset data is uploaded to"));
130
131        tpConfigPanels.setComponentAt(2,pnlChangesetManagement = new ChangesetManagementPanel(changesetCommentModel));
132        tpConfigPanels.setTitleAt(2, tr("Changesets"));
133        tpConfigPanels.setToolTipTextAt(2, tr("Manage open changesets and select a changeset to upload to"));
134
135        tpConfigPanels.setComponentAt(3, pnlUploadStrategySelectionPanel = new UploadStrategySelectionPanel());
136        tpConfigPanels.setTitleAt(3, tr("Advanced"));
137        tpConfigPanels.setToolTipTextAt(3, tr("Configure advanced settings"));
138
139        pnl.add(tpConfigPanels, BorderLayout.SOUTH);
140        return pnl;
141    }
142
143    /**
144     * builds the panel with the OK and CANCEL buttons
145     *
146     * @return
147     */
148    protected JPanel buildActionPanel() {
149        JPanel pnl = new JPanel();
150        pnl.setLayout(new FlowLayout(FlowLayout.CENTER));
151        pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
152
153        // -- upload button
154        UploadAction uploadAction = new UploadAction();
155        pnl.add(btnUpload = new SideButton(uploadAction));
156        btnUpload.setFocusable(true);
157        InputMapUtils.enableEnter(btnUpload);
158
159        // -- cancel button
160        CancelAction cancelAction = new CancelAction();
161        pnl.add(new SideButton(cancelAction));
162        getRootPane().registerKeyboardAction(
163                cancelAction,
164                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0),
165                JComponent.WHEN_IN_FOCUSED_WINDOW
166        );
167        pnl.add(new SideButton(new ContextSensitiveHelpAction(ht("/Dialog/Upload"))));
168        HelpUtil.setHelpContext(getRootPane(),ht("/Dialog/Upload"));
169        return pnl;
170    }
171
172    /**
173     * builds the gui
174     */
175    protected void build() {
176        setTitle(tr("Upload to ''{0}''", OsmApi.getOsmApi().getBaseUrl()));
177        getContentPane().setLayout(new BorderLayout());
178        getContentPane().add(buildContentPanel(), BorderLayout.CENTER);
179        getContentPane().add(buildActionPanel(), BorderLayout.SOUTH);
180
181        addWindowListener(new WindowEventHandler());
182
183
184        // make sure the configuration panels listen to each other
185        // changes
186        //
187        pnlChangesetManagement.addPropertyChangeListener(
188                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
189        );
190        pnlChangesetManagement.addPropertyChangeListener(this);
191        pnlUploadedObjects.addPropertyChangeListener(
192                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
193        );
194        pnlUploadedObjects.addPropertyChangeListener(pnlUploadStrategySelectionPanel);
195        pnlUploadStrategySelectionPanel.addPropertyChangeListener(
196                pnlBasicUploadSettings.getUploadParameterSummaryPanel()
197        );
198
199
200        // users can click on either of two links in the upload parameter
201        // summary handler. This installs the handler for these two events.
202        // We simply select the appropriate tab in the tabbed pane with the
203        // configuration dialogs.
204        //
205        pnlBasicUploadSettings.getUploadParameterSummaryPanel().setConfigurationParameterRequestListener(
206                new ConfigurationParameterRequestHandler() {
207                    public void handleUploadStrategyConfigurationRequest() {
208                        tpConfigPanels.setSelectedIndex(3);
209                    }
210                    public void handleChangesetConfigurationRequest() {
211                        tpConfigPanels.setSelectedIndex(2);
212                    }
213                }
214        );
215
216        pnlBasicUploadSettings.setUploadCommentDownFocusTraversalHandler(
217                new AbstractAction() {
218                    public void actionPerformed(ActionEvent e) {
219                        btnUpload.requestFocusInWindow();
220                    }
221                }
222        );
223
224        Main.pref.addPreferenceChangeListener(this);
225    }
226
227    /**
228     * constructor
229     */
230    public UploadDialog() {
231        super(JOptionPane.getFrameForComponent(Main.parent), ModalityType.DOCUMENT_MODAL);
232        build();
233    }
234
235    /**
236     * Sets the collection of primitives to upload
237     *
238     * @param toUpload the dataset with the objects to upload. If null, assumes the empty
239     * set of objects to upload
240     *
241     */
242    public void setUploadedPrimitives(APIDataSet toUpload) {
243        if (toUpload == null) {
244            List<OsmPrimitive> emptyList = Collections.emptyList();
245            pnlUploadedObjects.setUploadedPrimitives(emptyList, emptyList, emptyList);
246            return;
247        }
248        pnlUploadedObjects.setUploadedPrimitives(
249                toUpload.getPrimitivesToAdd(),
250                toUpload.getPrimitivesToUpdate(),
251                toUpload.getPrimitivesToDelete()
252        );
253    }
254
255    /**
256     * Remembers the user input in the preference settings
257     */
258    public void rememberUserInput() {
259        pnlBasicUploadSettings.rememberUserInput();
260        pnlUploadStrategySelectionPanel.rememberUserInput();
261    }
262
263    /**
264     * Initializes the panel for user input
265     */
266    public void startUserInput() {
267        tpConfigPanels.setSelectedIndex(0);
268        pnlBasicUploadSettings.startUserInput();
269        pnlTagSettings.startUserInput();
270        pnlTagSettings.initFromChangeset(pnlChangesetManagement.getSelectedChangeset());
271        pnlUploadStrategySelectionPanel.initFromPreferences();
272        UploadParameterSummaryPanel pnl = pnlBasicUploadSettings.getUploadParameterSummaryPanel();
273        pnl.setUploadStrategySpecification(pnlUploadStrategySelectionPanel.getUploadStrategySpecification());
274        pnl.setCloseChangesetAfterNextUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
275        pnl.setNumObjects(pnlUploadedObjects.getNumObjectsToUpload());
276    }
277
278    /**
279     * Replies the current changeset
280     *
281     * @return the current changeset
282     */
283    public Changeset getChangeset() {
284        Changeset cs = pnlChangesetManagement.getSelectedChangeset();
285        if (cs == null) {
286            cs = new Changeset();
287        }
288        cs.setKeys(pnlTagSettings.getTags());
289        return cs;
290    }
291
292    public void setSelectedChangesetForNextUpload(Changeset cs) {
293        pnlChangesetManagement.setSelectedChangesetForNextUpload(cs);
294    }
295
296    public Map<String, String> getDefaultChangesetTags() {
297        return pnlTagSettings.getDefaultTags();
298    }
299
300    public void setDefaultChangesetTags(Map<String, String> tags) {
301        pnlTagSettings.setDefaultTags(tags);
302         for (String key: tags.keySet()) { 
303            if (key.equals("comment")) { 
304                changesetCommentModel.setComment(tags.get(key));
305            } 
306        } 
307    }
308
309    /**
310     * Replies the {@see UploadStrategySpecification} the user entered in the dialog.
311     *
312     * @return the {@see UploadStrategySpecification} the user entered in the dialog.
313     */
314    public UploadStrategySpecification getUploadStrategySpecification() {
315        UploadStrategySpecification spec = pnlUploadStrategySelectionPanel.getUploadStrategySpecification();
316        spec.setCloseChangesetAfterUpload(pnlChangesetManagement.isCloseChangesetAfterUpload());
317        return spec;
318    }
319
320    /**
321     * Returns the current value for the upload comment
322     *
323     * @return the current value for the upload comment
324     */
325    protected String getUploadComment() {
326        return changesetCommentModel.getComment();
327    }
328
329    /**
330     * Returns true if the dialog was canceled
331     *
332     * @return true if the dialog was canceled
333     */
334    public boolean isCanceled() {
335        return canceled;
336    }
337
338    /**
339     * Sets whether the dialog was canceled
340     *
341     * @param canceled true if the dialog is canceled
342     */
343    protected void setCanceled(boolean canceled) {
344        this.canceled = canceled;
345    }
346
347    @Override
348    public void setVisible(boolean visible) {
349        if (visible) {
350            new WindowGeometry(
351                    getClass().getName() + ".geometry",
352                    WindowGeometry.centerInWindow(
353                            Main.parent,
354                            new Dimension(400,600)
355                    )
356            ).applySafe(this);
357            startUserInput();
358        } else if (!visible && isShowing()){
359            new WindowGeometry(this).remember(getClass().getName() + ".geometry");
360        }
361        super.setVisible(visible);
362    }
363
364    /**
365     * Handles an upload
366     *
367     */
368    class UploadAction extends AbstractAction {
369        public UploadAction() {
370            putValue(NAME, tr("Upload Changes"));
371            putValue(SMALL_ICON, ImageProvider.get("upload"));
372            putValue(SHORT_DESCRIPTION, tr("Upload the changed primitives"));
373        }
374
375        /**
376         * returns true if the user wants to revisit, false if they
377         * want to continue
378         */
379        protected boolean warnUploadComment() {
380            ExtendedDialog dlg = new ExtendedDialog(UploadDialog.this,
381                    tr("Please revise upload comment"),
382                    new String[] {tr("Revise"), tr("Cancel"), tr("Continue as is")});
383            dlg.setContent("<html>" +
384                    tr("Your upload comment is <i>empty</i>, or <i>very short</i>.<br /><br />" +
385                            "This is technically allowed, but please consider that many users who are<br />" +
386                            "watching changes in their area depend on meaningful changeset comments<br />" +
387                            "to understand what is going on!<br /><br />" +
388                            "If you spend a minute now to explain your change, you will make life<br />" +
389                    "easier for many other mappers.") +
390            "</html>");
391            dlg.setButtonIcons(new Icon[] {
392                    ImageProvider.get("ok"),
393                    ImageProvider.get("cancel"),
394                    ImageProvider.overlay(
395                            ImageProvider.get("upload"),
396                            new ImageIcon(ImageProvider.get("warning-small").getImage().getScaledInstance(10 , 10, Image.SCALE_SMOOTH)),
397                            ImageProvider.OverlayPosition.SOUTHEAST)});
398            dlg.setToolTipTexts(new String[] {
399                    tr("Return to the previous dialog to enter a more descriptive comment"),
400                    tr("Cancel and return to the previous dialog"),
401                    tr("Ignore this hint and upload anyway")});
402            dlg.setIcon(JOptionPane.WARNING_MESSAGE);
403            dlg.toggleEnable("upload_comment_is_empty_or_very_short");
404            dlg.setToggleCheckboxText(tr("Do not show this message again"));
405            dlg.setCancelButton(1, 2);
406            return dlg.showDialog().getValue() != 3;
407        }
408
409        protected void warnIllegalChunkSize() {
410            HelpAwareOptionPane.showOptionDialog(
411                    UploadDialog.this,
412                    tr("Please enter a valid chunk size first"),
413                    tr("Illegal chunk size"),
414                    JOptionPane.ERROR_MESSAGE,
415                    ht("/Dialog/Upload#IllegalChunkSize")
416            );
417        }
418
419        public void actionPerformed(ActionEvent e) {
420            if (getUploadComment().trim().length() < 10) {
421                if (warnUploadComment())
422                {
423                    tpConfigPanels.setSelectedIndex(0);
424                    pnlBasicUploadSettings.initEditingOfUploadComment();
425                    return;
426                }
427            }
428            UploadStrategySpecification strategy = getUploadStrategySpecification();
429            if (strategy.getStrategy().equals(UploadStrategy.CHUNKED_DATASET_STRATEGY)) {
430                if (strategy.getChunkSize() == UploadStrategySpecification.UNSPECIFIED_CHUNK_SIZE) {
431                    warnIllegalChunkSize();
432                    tpConfigPanels.setSelectedIndex(0);
433                    return;
434                }
435            }
436            setCanceled(false);
437            setVisible(false);
438        }
439    }
440
441    /**
442     * Action for canceling the dialog
443     *
444     */
445    class CancelAction extends AbstractAction {
446        public CancelAction() {
447            putValue(NAME, tr("Cancel"));
448            putValue(SMALL_ICON, ImageProvider.get("cancel"));
449            putValue(SHORT_DESCRIPTION, tr("Cancel the upload and resume editing"));
450        }
451
452        public void actionPerformed(ActionEvent e) {
453            setCanceled(true);
454            setVisible(false);
455        }
456    }
457
458    /**
459     * Listens to window closing events and processes them as cancel events.
460     * Listens to window open events and initializes user input
461     *
462     */
463    class WindowEventHandler extends WindowAdapter {
464        @Override
465        public void windowClosing(WindowEvent e) {
466            setCanceled(true);
467        }
468
469        @Override
470        public void windowOpened(WindowEvent e) {
471            //startUserInput();
472        }
473
474        @Override
475        public void windowActivated(WindowEvent arg0) {
476            if (tpConfigPanels.getSelectedIndex() == 0) {
477                pnlBasicUploadSettings.initEditingOfUploadComment();
478            }
479        }
480    }
481
482    /* -------------------------------------------------------------------------- */
483    /* Interface PropertyChangeListener                                           */
484    /* -------------------------------------------------------------------------- */
485    public void propertyChange(PropertyChangeEvent evt) {
486        if (evt.getPropertyName().equals(ChangesetManagementPanel.SELECTED_CHANGESET_PROP)) {
487            Changeset cs = (Changeset)evt.getNewValue();
488            if (cs == null) {
489                tpConfigPanels.setTitleAt(1, tr("Tags of new changeset"));
490            } else {
491                tpConfigPanels.setTitleAt(1, tr("Tags of changeset {0}", cs.getId()));
492            }
493        }
494    }
495
496    /* -------------------------------------------------------------------------- */
497    /* Interface PreferenceChangedListener                                        */
498    /* -------------------------------------------------------------------------- */
499    public void preferenceChanged(PreferenceChangeEvent e) {
500        if (e.getKey() == null || ! e.getKey().equals("osm-server.url"))
501            return;
502        if (e.getNewValue() == null) {
503            setTitle(tr("Upload"));
504        } else {
505            setTitle(tr("Upload to ''{0}''", e.getNewValue()));
506        }
507    }
508}
Note: See TracBrowser for help on using the repository browser.