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

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

code cleanup / robustness in edit layer handling

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