source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java@ 6313

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

cosmetics in error reporting

  • Property svn:eol-style set to native
File size: 21.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.FlowLayout;
8import java.awt.Frame;
9import java.awt.event.ActionEvent;
10import java.awt.event.ItemEvent;
11import java.awt.event.ItemListener;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Set;
19import java.util.concurrent.ExecutionException;
20import java.util.concurrent.Future;
21
22import javax.swing.AbstractAction;
23import javax.swing.Action;
24import javax.swing.DefaultListSelectionModel;
25import javax.swing.JCheckBox;
26import javax.swing.JList;
27import javax.swing.JMenuItem;
28import javax.swing.JPanel;
29import javax.swing.JScrollPane;
30import javax.swing.ListSelectionModel;
31import javax.swing.SwingUtilities;
32import javax.swing.event.ListSelectionEvent;
33import javax.swing.event.ListSelectionListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.actions.AbstractInfoAction;
37import org.openstreetmap.josm.data.osm.Changeset;
38import org.openstreetmap.josm.data.osm.ChangesetCache;
39import org.openstreetmap.josm.data.osm.DataSet;
40import org.openstreetmap.josm.data.osm.OsmPrimitive;
41import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
42import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
43import org.openstreetmap.josm.gui.MapFrame;
44import org.openstreetmap.josm.gui.MapView;
45import org.openstreetmap.josm.gui.SideButton;
46import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetCacheManager;
47import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetHeaderDownloadTask;
48import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetInSelectionListModel;
49import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListCellRenderer;
50import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListModel;
51import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetsInActiveDataLayerListModel;
52import org.openstreetmap.josm.gui.help.HelpUtil;
53import org.openstreetmap.josm.gui.io.CloseChangesetTask;
54import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
55import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
56import org.openstreetmap.josm.tools.BugReportExceptionHandler;
57import org.openstreetmap.josm.tools.ImageProvider;
58import org.openstreetmap.josm.tools.OpenBrowser;
59
60/**
61 * ChangesetDialog is a toggle dialog which displays the current list of changesets.
62 * It either displays
63 * <ul>
64 * <li>the list of changesets the currently selected objects are assigned to</li>
65 * <li>the list of changesets objects in the current data layer are assigend to</li>
66 * </ul>
67 *
68 * The dialog offers actions to download and to close changesets. It can also launch an external
69 * browser with information about a changeset. Furthermore, it can select all objects in
70 * the current data layer being assigned to a specific changeset.
71 *
72 */
73public class ChangesetDialog extends ToggleDialog{
74 private ChangesetInSelectionListModel inSelectionModel;
75 private ChangesetsInActiveDataLayerListModel inActiveDataLayerModel;
76 private JList lstInSelection;
77 private JList lstInActiveDataLayer;
78 private JCheckBox cbInSelectionOnly;
79 private JPanel pnlList;
80
81 // the actions
82 private SelectObjectsAction selectObjectsAction;
83 private ReadChangesetsAction readChangesetAction;
84 private ShowChangesetInfoAction showChangesetInfoAction;
85 private CloseOpenChangesetsAction closeChangesetAction;
86 private LaunchChangesetManagerAction launchChangesetManagerAction;
87
88 private ChangesetDialogPopup popupMenu;
89
90 protected void buildChangesetsLists() {
91 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
92 inSelectionModel = new ChangesetInSelectionListModel(selectionModel);
93
94 lstInSelection = new JList(inSelectionModel);
95 lstInSelection.setSelectionModel(selectionModel);
96 lstInSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
97 lstInSelection.setCellRenderer(new ChangesetListCellRenderer());
98
99 selectionModel = new DefaultListSelectionModel();
100 inActiveDataLayerModel = new ChangesetsInActiveDataLayerListModel(selectionModel);
101 lstInActiveDataLayer = new JList(inActiveDataLayerModel);
102 lstInActiveDataLayer.setSelectionModel(selectionModel);
103 lstInActiveDataLayer.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
104 lstInActiveDataLayer.setCellRenderer(new ChangesetListCellRenderer());
105
106 DblClickHandler dblClickHandler = new DblClickHandler();
107 lstInSelection.addMouseListener(dblClickHandler);
108 lstInActiveDataLayer.addMouseListener(dblClickHandler);
109 }
110
111 protected void registerAsListener() {
112 // let the model for changesets in the current selection listen to various
113 // events
114 ChangesetCache.getInstance().addChangesetCacheListener(inSelectionModel);
115 MapView.addEditLayerChangeListener(inSelectionModel);
116 DataSet.addSelectionListener(inSelectionModel);
117
118 // let the model for changesets in the current layer listen to various
119 // events and bootstrap it's content
120 ChangesetCache.getInstance().addChangesetCacheListener(inActiveDataLayerModel);
121 MapView.addEditLayerChangeListener(inActiveDataLayerModel);
122 if (Main.main.getEditLayer() != null) {
123 Main.main.getEditLayer().data.addDataSetListener(inActiveDataLayerModel);
124 inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
125 inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getAllSelected());
126 }
127 }
128
129 protected void unregisterAsListener() {
130 // remove the list model for the current edit layer as listener
131 //
132 ChangesetCache.getInstance().removeChangesetCacheListener(inActiveDataLayerModel);
133 MapView.removeEditLayerChangeListener(inActiveDataLayerModel);
134 if (Main.main.getEditLayer() != null) {
135 Main.main.getEditLayer().data.removeDataSetListener(inActiveDataLayerModel);
136 }
137
138 // remove the list model for the changesets in the current selection as
139 // listener
140 //
141 MapView.removeEditLayerChangeListener(inSelectionModel);
142 DataSet.removeSelectionListener(inSelectionModel);
143 }
144
145 @Override
146 public void showNotify() {
147 registerAsListener();
148 DatasetEventManager.getInstance().addDatasetListener(inActiveDataLayerModel, FireMode.IN_EDT);
149 }
150
151 @Override
152 public void hideNotify() {
153 unregisterAsListener();
154 DatasetEventManager.getInstance().removeDatasetListener(inActiveDataLayerModel);
155 }
156
157 protected JPanel buildFilterPanel() {
158 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
159 pnl.setBorder(null);
160 pnl.add(cbInSelectionOnly = new JCheckBox(tr("For selected objects only")));
161 cbInSelectionOnly.setToolTipText(tr("<html>Select to show changesets for the currently selected objects only.<br>"
162 + "Unselect to show all changesets for objects in the current data layer.</html>"));
163 cbInSelectionOnly.setSelected(Main.pref.getBoolean("changeset-dialog.for-selected-objects-only", false));
164 return pnl;
165 }
166
167 protected JPanel buildListPanel() {
168 buildChangesetsLists();
169 JPanel pnl = new JPanel(new BorderLayout());
170 if (cbInSelectionOnly.isSelected()) {
171 pnl.add(new JScrollPane(lstInSelection));
172 } else {
173 pnl.add(new JScrollPane(lstInActiveDataLayer));
174 }
175 return pnl;
176 }
177
178 protected void build() {
179 JPanel pnl = new JPanel(new BorderLayout());
180 pnl.add(buildFilterPanel(), BorderLayout.NORTH);
181 pnl.add(pnlList = buildListPanel(), BorderLayout.CENTER);
182
183 cbInSelectionOnly.addItemListener(new FilterChangeHandler());
184
185 HelpUtil.setHelpContext(pnl, HelpUtil.ht("/Dialog/ChangesetListDialog"));
186
187 // -- select objects action
188 selectObjectsAction = new SelectObjectsAction();
189 cbInSelectionOnly.addItemListener(selectObjectsAction);
190
191 // -- read changesets action
192 readChangesetAction = new ReadChangesetsAction();
193 cbInSelectionOnly.addItemListener(readChangesetAction);
194
195 // -- close changesets action
196 closeChangesetAction = new CloseOpenChangesetsAction();
197 cbInSelectionOnly.addItemListener(closeChangesetAction);
198
199 // -- show info action
200 showChangesetInfoAction = new ShowChangesetInfoAction();
201 cbInSelectionOnly.addItemListener(showChangesetInfoAction);
202
203 // -- launch changeset manager action
204 launchChangesetManagerAction = new LaunchChangesetManagerAction();
205 cbInSelectionOnly.addItemListener(launchChangesetManagerAction);
206
207 popupMenu = new ChangesetDialogPopup(lstInActiveDataLayer, lstInSelection);
208
209 PopupMenuLauncher popupMenuLauncher = new PopupMenuLauncher(popupMenu);
210 lstInSelection.addMouseListener(popupMenuLauncher);
211 lstInActiveDataLayer.addMouseListener(popupMenuLauncher);
212
213 createLayout(pnl, false, Arrays.asList(new SideButton[] {
214 new SideButton(selectObjectsAction, false),
215 new SideButton(readChangesetAction, false),
216 new SideButton(closeChangesetAction, false),
217 new SideButton(showChangesetInfoAction, false),
218 new SideButton(launchChangesetManagerAction, false)
219 }));
220 }
221
222 protected JList getCurrentChangesetList() {
223 if (cbInSelectionOnly.isSelected())
224 return lstInSelection;
225 return lstInActiveDataLayer;
226 }
227
228 protected ChangesetListModel getCurrentChangesetListModel() {
229 if (cbInSelectionOnly.isSelected())
230 return inSelectionModel;
231 return inActiveDataLayerModel;
232 }
233
234 protected void initWithCurrentData() {
235 if (Main.main.getEditLayer() != null) {
236 inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getAllSelected());
237 inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
238 }
239 }
240
241 public ChangesetDialog(MapFrame mapFrame) {
242 super(
243 tr("Changesets"),
244 "changesetdialog",
245 tr("Open the list of changesets in the current layer."),
246 null, /* no keyboard shortcut */
247 200, /* the preferred height */
248 false /* don't show if there is no preference */
249 );
250 build();
251 initWithCurrentData();
252 }
253
254 class DblClickHandler extends MouseAdapter {
255 @Override
256 public void mouseClicked(MouseEvent e) {
257 if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() < 2)
258 return;
259 Set<Integer> sel = getCurrentChangesetListModel().getSelectedChangesetIds();
260 if (sel.isEmpty())
261 return;
262 if (Main.main.getCurrentDataSet() == null)
263 return;
264 new SelectObjectsAction().selectObjectsByChangesetIds(Main.main.getCurrentDataSet(), sel);
265 }
266
267 }
268
269 class FilterChangeHandler implements ItemListener {
270 @Override
271 public void itemStateChanged(ItemEvent e) {
272 Main.pref.put("changeset-dialog.for-selected-objects-only", cbInSelectionOnly.isSelected());
273 pnlList.removeAll();
274 if (cbInSelectionOnly.isSelected()) {
275 pnlList.add(new JScrollPane(lstInSelection), BorderLayout.CENTER);
276 } else {
277 pnlList.add(new JScrollPane(lstInActiveDataLayer), BorderLayout.CENTER);
278 }
279 validate();
280 repaint();
281 }
282 }
283
284 /**
285 * Selects objects for the currently selected changesets.
286 */
287 class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener{
288
289 public SelectObjectsAction() {
290 putValue(NAME, tr("Select"));
291 putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
292 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
293 updateEnabledState();
294 }
295
296 public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
297 if (ds == null || ids == null)
298 return;
299 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
300 for (OsmPrimitive p: ds.allPrimitives()) {
301 if (ids.contains(p.getChangesetId())) {
302 sel.add(p);
303 }
304 }
305 ds.setSelected(sel);
306 }
307
308 @Override
309 public void actionPerformed(ActionEvent e) {
310 if (Main.main.getEditLayer() == null)
311 return;
312 ChangesetListModel model = getCurrentChangesetListModel();
313 Set<Integer> sel = model.getSelectedChangesetIds();
314 if (sel.isEmpty())
315 return;
316
317 DataSet ds = Main.main.getEditLayer().data;
318 selectObjectsByChangesetIds(ds,sel);
319 }
320
321 protected void updateEnabledState() {
322 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
323 }
324
325 @Override
326 public void itemStateChanged(ItemEvent arg0) {
327 updateEnabledState();
328
329 }
330
331 @Override
332 public void valueChanged(ListSelectionEvent e) {
333 updateEnabledState();
334 }
335 }
336
337 /**
338 * Downloads selected changesets
339 *
340 */
341 class ReadChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener{
342 public ReadChangesetsAction() {
343 putValue(NAME, tr("Download"));
344 putValue(SHORT_DESCRIPTION, tr("Download information about the selected changesets from the OSM server"));
345 putValue(SMALL_ICON, ImageProvider.get("download"));
346 updateEnabledState();
347 }
348
349 @Override
350 public void actionPerformed(ActionEvent arg0) {
351 ChangesetListModel model = getCurrentChangesetListModel();
352 Set<Integer> sel = model.getSelectedChangesetIds();
353 if (sel.isEmpty())
354 return;
355 ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
356 Main.worker.submit(task);
357 }
358
359 protected void updateEnabledState() {
360 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
361 }
362
363 @Override
364 public void itemStateChanged(ItemEvent arg0) {
365 updateEnabledState();
366
367 }
368
369 @Override
370 public void valueChanged(ListSelectionEvent e) {
371 updateEnabledState();
372 }
373 }
374
375 /**
376 * Closes the currently selected changesets
377 *
378 */
379 class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
380 public CloseOpenChangesetsAction() {
381 putValue(NAME, tr("Close open changesets"));
382 putValue(SHORT_DESCRIPTION, tr("Closes the selected open changesets"));
383 putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
384 updateEnabledState();
385 }
386
387 @Override
388 public void actionPerformed(ActionEvent arg0) {
389 List<Changeset> sel = getCurrentChangesetListModel().getSelectedOpenChangesets();
390 if (sel.isEmpty())
391 return;
392 Main.worker.submit(new CloseChangesetTask(sel));
393 }
394
395 protected void updateEnabledState() {
396 setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
397 }
398
399 @Override
400 public void itemStateChanged(ItemEvent arg0) {
401 updateEnabledState();
402 }
403
404 @Override
405 public void valueChanged(ListSelectionEvent e) {
406 updateEnabledState();
407 }
408 }
409
410 /**
411 * Show information about the currently selected changesets
412 *
413 */
414 class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
415 public ShowChangesetInfoAction() {
416 putValue(NAME, tr("Show info"));
417 putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
418 putValue(SMALL_ICON, ImageProvider.get("about"));
419 updateEnabledState();
420 }
421
422 @Override
423 public void actionPerformed(ActionEvent arg0) {
424 Set<Changeset> sel = getCurrentChangesetListModel().getSelectedChangesets();
425 if (sel.isEmpty())
426 return;
427 if (sel.size() > 10 && ! AbstractInfoAction.confirmLaunchMultiple(sel.size()))
428 return;
429 String baseUrl = AbstractInfoAction.getBaseBrowseUrl();
430 for (Changeset cs: sel) {
431 String url = baseUrl + "/changeset/" + cs.getId();
432 OpenBrowser.displayUrl(
433 url
434 );
435 }
436 }
437
438 protected void updateEnabledState() {
439 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
440 }
441
442 @Override
443 public void itemStateChanged(ItemEvent arg0) {
444 updateEnabledState();
445 }
446
447 @Override
448 public void valueChanged(ListSelectionEvent e) {
449 updateEnabledState();
450 }
451 }
452
453 /**
454 * Show information about the currently selected changesets
455 *
456 */
457 class LaunchChangesetManagerAction extends AbstractAction implements ListSelectionListener, ItemListener {
458 public LaunchChangesetManagerAction() {
459 putValue(NAME, tr("Details"));
460 putValue(SHORT_DESCRIPTION, tr("Opens the Changeset Manager window for the selected changesets"));
461 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "changesetmanager"));
462 }
463
464 protected void launchChangesetManager(Collection<Integer> toSelect) {
465 ChangesetCacheManager cm = ChangesetCacheManager.getInstance();
466 if (cm.isVisible()) {
467 cm.setExtendedState(Frame.NORMAL);
468 cm.toFront();
469 cm.requestFocus();
470 } else {
471 cm.setVisible(true);
472 cm.toFront();
473 cm.requestFocus();
474 }
475 cm.setSelectedChangesetsById(toSelect);
476 }
477
478 @Override
479 public void actionPerformed(ActionEvent arg0) {
480 ChangesetListModel model = getCurrentChangesetListModel();
481 Set<Integer> sel = model.getSelectedChangesetIds();
482 final Set<Integer> toDownload = new HashSet<Integer>();
483 ChangesetCache cc = ChangesetCache.getInstance();
484 for (int id: sel) {
485 if (!cc.contains(id)) {
486 toDownload.add(id);
487 }
488 }
489
490 final ChangesetHeaderDownloadTask task;
491 final Future<?> future;
492 if (toDownload.isEmpty()) {
493 task = null;
494 future = null;
495 } else {
496 task = new ChangesetHeaderDownloadTask(toDownload);
497 future = Main.worker.submit(task);
498 }
499
500 Runnable r = new Runnable() {
501 @Override
502 public void run() {
503 // first, wait for the download task to finish, if a download
504 // task was launched
505 if (future != null) {
506 try {
507 future.get();
508 } catch(InterruptedException e) {
509 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while downloading changeset header");
510 } catch(ExecutionException e){
511 e.printStackTrace();
512 BugReportExceptionHandler.handleException(e.getCause());
513 return;
514 }
515 }
516 if (task != null) {
517 if (task.isCanceled())
518 // don't launch the changeset manager if the download task
519 // was canceled
520 return;
521 if (task.isFailed()) {
522 toDownload.clear();
523 }
524 }
525 // launch the task
526 launchChangesetManager(toDownload);
527 }
528 };
529 Main.worker.submit(r);
530 }
531
532 @Override
533 public void itemStateChanged(ItemEvent arg0) {
534 }
535
536 @Override
537 public void valueChanged(ListSelectionEvent e) {
538 }
539 }
540
541 class ChangesetDialogPopup extends ListPopupMenu {
542 public ChangesetDialogPopup(JList ... lists) {
543 super(lists);
544 add(selectObjectsAction);
545 addSeparator();
546 add(readChangesetAction);
547 add(closeChangesetAction);
548 addSeparator();
549 add(showChangesetInfoAction);
550 }
551 }
552
553 public void addPopupMenuSeparator() {
554 popupMenu.addSeparator();
555 }
556
557 public JMenuItem addPopupMenuAction(Action a) {
558 return popupMenu.add(a);
559 }
560}
Note: See TracBrowser for help on using the repository browser.