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

Last change on this file since 6296 was 6296, checked in by Don-vip, 11 years ago

Sonar/Findbugs - Avoid commented-out lines of code, javadoc

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