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

Last change on this file since 2689 was 2689, checked in by Gubaer, 17 years ago

new: Changeset Cache Manager for querying, downloading, browsing, and managing changesets within JOSM. See also Changeset Manager and Changeset Query Dialog

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