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

Last change on this file since 2710 was 2710, checked in by stoecker, 14 years ago

close #4222 - unify design of right menus again

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