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

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

fix #7776 - Selection of deleted objects

  • Property svn:eol-style set to native
File size: 21.1 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 ChangesetPopupMenuLauncher popupMenuLauncher = new ChangesetPopupMenuLauncher();
111 lstInSelection.addMouseListener(popupMenuLauncher);
112 lstInActiveDataLayer.addMouseListener(popupMenuLauncher);
113 }
114
115 protected void registerAsListener() {
116 // let the model for changesets in the current selection listen to various
117 // events
118 ChangesetCache.getInstance().addChangesetCacheListener(inSelectionModel);
119 MapView.addEditLayerChangeListener(inSelectionModel);
120 DataSet.addSelectionListener(inSelectionModel);
121
122 // let the model for changesets in the current layer listen to various
123 // events and bootstrap it's content
124 ChangesetCache.getInstance().addChangesetCacheListener(inActiveDataLayerModel);
125 MapView.addEditLayerChangeListener(inActiveDataLayerModel);
126 if (Main.main.getEditLayer() != null) {
127 Main.main.getEditLayer().data.addDataSetListener(inActiveDataLayerModel);
128 inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
129 inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getAllSelected());
130 }
131 }
132
133 protected void unregisterAsListener() {
134 // remove the list model for the current edit layer as listener
135 //
136 ChangesetCache.getInstance().removeChangesetCacheListener(inActiveDataLayerModel);
137 MapView.removeEditLayerChangeListener(inActiveDataLayerModel);
138 if (Main.main.getEditLayer() != null) {
139 Main.main.getEditLayer().data.removeDataSetListener(inActiveDataLayerModel);
140 }
141
142 // remove the list model for the changesets in the current selection as
143 // listener
144 //
145 MapView.removeEditLayerChangeListener(inSelectionModel);
146 DataSet.removeSelectionListener(inSelectionModel);
147 }
148
149 @Override
150 public void showNotify() {
151 registerAsListener();
152 DatasetEventManager.getInstance().addDatasetListener(inActiveDataLayerModel, FireMode.IN_EDT);
153 }
154
155 @Override
156 public void hideNotify() {
157 unregisterAsListener();
158 DatasetEventManager.getInstance().removeDatasetListener(inActiveDataLayerModel);
159 }
160
161 protected JPanel buildFilterPanel() {
162 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
163 pnl.setBorder(null);
164 pnl.add(cbInSelectionOnly = new JCheckBox(tr("For selected objects only")));
165 cbInSelectionOnly.setToolTipText(tr("<html>Select to show changesets for the currently selected objects only.<br>"
166 + "Unselect to show all changesets for objects in the current data layer.</html>"));
167 cbInSelectionOnly.setSelected(Main.pref.getBoolean("changeset-dialog.for-selected-objects-only", false));
168 return pnl;
169 }
170
171 protected JPanel buildListPanel() {
172 buildChangesetsLists();
173 JPanel pnl = new JPanel(new BorderLayout());
174 if (cbInSelectionOnly.isSelected()) {
175 pnl.add(new JScrollPane(lstInSelection));
176 } else {
177 pnl.add(new JScrollPane(lstInActiveDataLayer));
178 }
179 return pnl;
180 }
181
182 protected void build() {
183 JPanel pnl = new JPanel(new BorderLayout());
184 pnl.add(buildFilterPanel(), BorderLayout.NORTH);
185 pnl.add(pnlList = buildListPanel(), BorderLayout.CENTER);
186
187 cbInSelectionOnly.addItemListener(new FilterChangeHandler());
188
189 HelpUtil.setHelpContext(pnl, HelpUtil.ht("/Dialog/ChangesetListDialog"));
190
191 // -- select objects action
192 selectObjectsAction = new SelectObjectsAction();
193 cbInSelectionOnly.addItemListener(selectObjectsAction);
194
195 // -- read changesets action
196 readChangesetAction = new ReadChangesetsAction();
197 cbInSelectionOnly.addItemListener(readChangesetAction);
198
199 // -- close changesets action
200 closeChangesetAction = new CloseOpenChangesetsAction();
201 cbInSelectionOnly.addItemListener(closeChangesetAction);
202
203 // -- show info action
204 showChangesetInfoAction = new ShowChangesetInfoAction();
205 cbInSelectionOnly.addItemListener(showChangesetInfoAction);
206
207 // -- launch changeset manager action
208 launchChangesetManagerAction = new LaunchChangesetManagerAction();
209 cbInSelectionOnly.addItemListener(launchChangesetManagerAction);
210
211 popupMenu = new ChangesetDialogPopup(lstInActiveDataLayer, lstInSelection);
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 public void itemStateChanged(ItemEvent e) {
271 Main.pref.put("changeset-dialog.for-selected-objects-only", cbInSelectionOnly.isSelected());
272 pnlList.removeAll();
273 if (cbInSelectionOnly.isSelected()) {
274 pnlList.add(new JScrollPane(lstInSelection), BorderLayout.CENTER);
275 } else {
276 pnlList.add(new JScrollPane(lstInActiveDataLayer), BorderLayout.CENTER);
277 }
278 validate();
279 repaint();
280 }
281 }
282
283 /**
284 * Selects objects for the currently selected changesets.
285 */
286 class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener{
287
288 public SelectObjectsAction() {
289 putValue(NAME, tr("Select"));
290 putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
291 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
292 updateEnabledState();
293 }
294
295 public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
296 if (ds == null || ids == null)
297 return;
298 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
299 for (OsmPrimitive p: ds.allPrimitives()) {
300 if (ids.contains(p.getChangesetId())) {
301 sel.add(p);
302 }
303 }
304 ds.setSelected(sel);
305 }
306
307 public void actionPerformed(ActionEvent e) {
308 if (Main.main.getEditLayer() == null)
309 return;
310 ChangesetListModel model = getCurrentChangesetListModel();
311 Set<Integer> sel = model.getSelectedChangesetIds();
312 if (sel.isEmpty())
313 return;
314
315 DataSet ds = Main.main.getEditLayer().data;
316 selectObjectsByChangesetIds(ds,sel);
317 }
318
319 protected void updateEnabledState() {
320 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
321 }
322
323 public void itemStateChanged(ItemEvent arg0) {
324 updateEnabledState();
325
326 }
327
328 public void valueChanged(ListSelectionEvent e) {
329 updateEnabledState();
330 }
331 }
332
333 /**
334 * Downloads selected changesets
335 *
336 */
337 class ReadChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener{
338 public ReadChangesetsAction() {
339 putValue(NAME, tr("Download"));
340 putValue(SHORT_DESCRIPTION, tr("Download information about the selected changesets from the OSM server"));
341 putValue(SMALL_ICON, ImageProvider.get("download"));
342 updateEnabledState();
343 }
344
345 public void actionPerformed(ActionEvent arg0) {
346 ChangesetListModel model = getCurrentChangesetListModel();
347 Set<Integer> sel = model.getSelectedChangesetIds();
348 if (sel.isEmpty())
349 return;
350 ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
351 Main.worker.submit(task);
352 }
353
354 protected void updateEnabledState() {
355 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
356 }
357
358 public void itemStateChanged(ItemEvent arg0) {
359 updateEnabledState();
360
361 }
362
363 public void valueChanged(ListSelectionEvent e) {
364 updateEnabledState();
365 }
366 }
367
368 /**
369 * Closes the currently selected changesets
370 *
371 */
372 class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
373 public CloseOpenChangesetsAction() {
374 putValue(NAME, tr("Close open changesets"));
375 putValue(SHORT_DESCRIPTION, tr("Closes the selected open changesets"));
376 putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
377 updateEnabledState();
378 }
379
380 public void actionPerformed(ActionEvent arg0) {
381 List<Changeset> sel = getCurrentChangesetListModel().getSelectedOpenChangesets();
382 if (sel.isEmpty())
383 return;
384 Main.worker.submit(new CloseChangesetTask(sel));
385 }
386
387 protected void updateEnabledState() {
388 setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
389 }
390
391 public void itemStateChanged(ItemEvent arg0) {
392 updateEnabledState();
393 }
394
395 public void valueChanged(ListSelectionEvent e) {
396 updateEnabledState();
397 }
398 }
399
400 /**
401 * Show information about the currently selected changesets
402 *
403 */
404 class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
405 public ShowChangesetInfoAction() {
406 putValue(NAME, tr("Show info"));
407 putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
408 putValue(SMALL_ICON, ImageProvider.get("about"));
409 updateEnabledState();
410 }
411
412 public void actionPerformed(ActionEvent arg0) {
413 Set<Changeset> sel = getCurrentChangesetListModel().getSelectedChangesets();
414 if (sel.isEmpty())
415 return;
416 if (sel.size() > 10 && ! AbstractInfoAction.confirmLaunchMultiple(sel.size()))
417 return;
418 String baseUrl = AbstractInfoAction.getBaseBrowseUrl();
419 for (Changeset cs: sel) {
420 String url = baseUrl + "/changeset/" + cs.getId();
421 OpenBrowser.displayUrl(
422 url
423 );
424 }
425 }
426
427 protected void updateEnabledState() {
428 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
429 }
430
431 public void itemStateChanged(ItemEvent arg0) {
432 updateEnabledState();
433 }
434
435 public void valueChanged(ListSelectionEvent e) {
436 updateEnabledState();
437 }
438 }
439
440 /**
441 * Show information about the currently selected changesets
442 *
443 */
444 class LaunchChangesetManagerAction extends AbstractAction implements ListSelectionListener, ItemListener {
445 public LaunchChangesetManagerAction() {
446 putValue(NAME, tr("Details"));
447 putValue(SHORT_DESCRIPTION, tr("Opens the Changeset Manager window for the selected changesets"));
448 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "changesetmanager"));
449 }
450
451 protected void launchChangesetManager(Collection<Integer> toSelect) {
452 ChangesetCacheManager cm = ChangesetCacheManager.getInstance();
453 if (cm.isVisible()) {
454 cm.setExtendedState(Frame.NORMAL);
455 cm.toFront();
456 cm.requestFocus();
457 } else {
458 cm.setVisible(true);
459 cm.toFront();
460 cm.requestFocus();
461 }
462 cm.setSelectedChangesetsById(toSelect);
463 }
464
465 public void actionPerformed(ActionEvent arg0) {
466 ChangesetListModel model = getCurrentChangesetListModel();
467 Set<Integer> sel = model.getSelectedChangesetIds();
468 final Set<Integer> toDownload = new HashSet<Integer>();
469 ChangesetCache cc = ChangesetCache.getInstance();
470 for (int id: sel) {
471 if (!cc.contains(id)) {
472 toDownload.add(id);
473 }
474 }
475
476 final ChangesetHeaderDownloadTask task;
477 final Future<?> future;
478 if (toDownload.isEmpty()) {
479 task = null;
480 future = null;
481 } else {
482 task = new ChangesetHeaderDownloadTask(toDownload);
483 future = Main.worker.submit(task);
484 }
485
486 Runnable r = new Runnable() {
487 public void run() {
488 // first, wait for the download task to finish, if a download
489 // task was launched
490 if (future != null) {
491 try {
492 future.get();
493 } catch(InterruptedException e) {
494 e.printStackTrace();
495 } catch(ExecutionException e){
496 e.printStackTrace();
497 BugReportExceptionHandler.handleException(e.getCause());
498 return;
499 }
500 }
501 if (task != null) {
502 if (task.isCanceled())
503 // don't launch the changeset manager if the download task
504 // was canceled
505 return;
506 if (task.isFailed()) {
507 toDownload.clear();
508 }
509 }
510 // launch the task
511 launchChangesetManager(toDownload);
512 }
513 };
514 Main.worker.submit(r);
515 }
516
517 public void itemStateChanged(ItemEvent arg0) {
518 }
519
520 public void valueChanged(ListSelectionEvent e) {
521 }
522 }
523
524 class ChangesetPopupMenuLauncher extends PopupMenuLauncher {
525 @Override
526 public void launch(MouseEvent evt) {
527 JList lst = getCurrentChangesetList();
528 if (lst.getSelectedIndices().length == 0) {
529 int idx = lst.locationToIndex(evt.getPoint());
530 if (idx >=0) {
531 lst.getSelectionModel().addSelectionInterval(idx, idx);
532 }
533 }
534 popupMenu.show(lst, evt.getX(), evt.getY());
535 }
536 }
537
538 class ChangesetDialogPopup extends ListPopupMenu {
539 public ChangesetDialogPopup(JList ... lists) {
540 super(lists);
541 add(selectObjectsAction);
542 addSeparator();
543 add(readChangesetAction);
544 add(closeChangesetAction);
545 addSeparator();
546 add(showChangesetInfoAction);
547 }
548 }
549
550 public void addPopupMenuSeparator() {
551 popupMenu.addSeparator();
552 }
553
554 public JMenuItem addPopupMenuAction(Action a) {
555 return popupMenu.add(a);
556 }
557}
Note: See TracBrowser for help on using the repository browser.