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

Last change on this file since 4363 was 4363, checked in by stoecker, 13 years ago

use createLayout() in all internal ToggleDialog's

  • Property svn:eol-style set to native
File size: 22.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.DefaultListSelectionModel;
24import javax.swing.JCheckBox;
25import javax.swing.JList;
26import javax.swing.JPanel;
27import javax.swing.JPopupMenu;
28import javax.swing.JScrollPane;
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.data.osm.event.DatasetEventManager.FireMode;
42import org.openstreetmap.josm.gui.MapFrame;
43import org.openstreetmap.josm.gui.MapView;
44import org.openstreetmap.josm.gui.SideButton;
45import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetCacheManager;
46import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetHeaderDownloadTask;
47import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetInSelectionListModel;
48import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListCellRenderer;
49import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListModel;
50import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetsInActiveDataLayerListModel;
51import org.openstreetmap.josm.gui.help.HelpUtil;
52import org.openstreetmap.josm.gui.io.CloseChangesetTask;
53import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
54import org.openstreetmap.josm.tools.BugReportExceptionHandler;
55import org.openstreetmap.josm.tools.ImageProvider;
56import org.openstreetmap.josm.tools.OpenBrowser;
57
58/**
59 * ChangesetDialog is a toggle dialog which displays the current list of changesets.
60 * It either displays
61 * <ul>
62 * <li>the list of changesets the currently selected objects are assigned to</li>
63 * <li>the list of changesets objects in the current data layer are assigend to</li>
64 * </ul>
65 *
66 * The dialog offers actions to download and to close changesets. It can also launch an external
67 * browser with information about a changeset. Furthermore, it can select all objects in
68 * the current data layer being assigned to a specific changeset.
69 *
70 */
71public class ChangesetDialog extends ToggleDialog{
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 showChangesetInfoAction;
83 private CloseOpenChangesetsAction closeChangesetAction;
84 private LaunchChangesetManagerAction launchChangesetManagerAction;
85
86 protected void buildChangesetsLists() {
87 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
88 inSelectionModel = new ChangesetInSelectionListModel(selectionModel);
89
90 lstInSelection = new JList(inSelectionModel);
91 lstInSelection.setSelectionModel(selectionModel);
92 lstInSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
93 lstInSelection.setCellRenderer(new ChangesetListCellRenderer());
94
95 selectionModel = new DefaultListSelectionModel();
96 inActiveDataLayerModel = new ChangesetsInActiveDataLayerListModel(selectionModel);
97 lstInActiveDataLayer = new JList(inActiveDataLayerModel);
98 lstInActiveDataLayer.setSelectionModel(selectionModel);
99 lstInActiveDataLayer.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
100 lstInActiveDataLayer.setCellRenderer(new ChangesetListCellRenderer());
101
102 DblClickHandler dblClickHandler = new DblClickHandler();
103 lstInSelection.addMouseListener(dblClickHandler);
104 lstInActiveDataLayer.addMouseListener(dblClickHandler);
105
106 ChangesetPopupMenuLauncher popupMenuLauncher = new ChangesetPopupMenuLauncher();
107 lstInSelection.addMouseListener(popupMenuLauncher);
108 lstInActiveDataLayer.addMouseListener(popupMenuLauncher);
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.getSelected());
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 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(selectObjectsAction);
191 lstInSelection.getSelectionModel().addListSelectionListener(selectObjectsAction);
192
193 // -- read changesets action
194 readChangesetAction = new ReadChangesetsAction();
195 cbInSelectionOnly.addItemListener(readChangesetAction);
196 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(readChangesetAction);
197 lstInSelection.getSelectionModel().addListSelectionListener(readChangesetAction);
198
199 // -- close changesets action
200 closeChangesetAction = new CloseOpenChangesetsAction();
201 cbInSelectionOnly.addItemListener(closeChangesetAction);
202 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(closeChangesetAction);
203 lstInSelection.getSelectionModel().addListSelectionListener(closeChangesetAction);
204
205 // -- show info action
206 showChangesetInfoAction = new ShowChangesetInfoAction();
207 cbInSelectionOnly.addItemListener(showChangesetInfoAction);
208 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(showChangesetInfoAction);
209 lstInSelection.getSelectionModel().addListSelectionListener(showChangesetInfoAction);
210
211 // -- launch changeset manager action
212 launchChangesetManagerAction = new LaunchChangesetManagerAction();
213 cbInSelectionOnly.addItemListener(launchChangesetManagerAction);
214 lstInActiveDataLayer.getSelectionModel().addListSelectionListener(launchChangesetManagerAction);
215 lstInSelection.getSelectionModel().addListSelectionListener(launchChangesetManagerAction);
216
217 createLayout(pnl, false, Arrays.asList(new SideButton[] {
218 new SideButton(selectObjectsAction, false),
219 new SideButton(readChangesetAction, false),
220 new SideButton(closeChangesetAction, false),
221 new SideButton(showChangesetInfoAction, false),
222 new SideButton(launchChangesetManagerAction, false)
223 }));
224 }
225
226 protected JList getCurrentChangesetList() {
227 if (cbInSelectionOnly.isSelected())
228 return lstInSelection;
229 return lstInActiveDataLayer;
230 }
231
232 protected ChangesetListModel getCurrentChangesetListModel() {
233 if (cbInSelectionOnly.isSelected())
234 return inSelectionModel;
235 return inActiveDataLayerModel;
236 }
237
238 protected void initWithCurrentData() {
239 if (Main.main.getEditLayer() != null) {
240 inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getSelected());
241 inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().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 public void itemStateChanged(ItemEvent e) {
275 Main.pref.put("changeset-dialog.for-selected-objects-only", cbInSelectionOnly.isSelected());
276 pnlList.removeAll();
277 if (cbInSelectionOnly.isSelected()) {
278 pnlList.add(new JScrollPane(lstInSelection), BorderLayout.CENTER);
279 } else {
280 pnlList.add(new JScrollPane(lstInActiveDataLayer), BorderLayout.CENTER);
281 }
282 validate();
283 repaint();
284 }
285 }
286
287 /**
288 * Selects objects for the currently selected changesets.
289 */
290 class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener{
291
292 public SelectObjectsAction() {
293 putValue(NAME, tr("Select"));
294 putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
295 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
296 updateEnabledState();
297 }
298
299 public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
300 if (ds == null || ids == null)
301 return;
302 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
303 for (OsmPrimitive p: ds.allPrimitives()) {
304 if (ids.contains(p.getChangesetId())) {
305 sel.add(p);
306 }
307 }
308 ds.setSelected(sel);
309 }
310
311 public void actionPerformed(ActionEvent e) {
312 if (Main.main.getEditLayer() == null)
313 return;
314 ChangesetListModel model = getCurrentChangesetListModel();
315 Set<Integer> sel = model.getSelectedChangesetIds();
316 if (sel.isEmpty())
317 return;
318
319 DataSet ds = Main.main.getEditLayer().data;
320 selectObjectsByChangesetIds(ds,sel);
321 }
322
323 protected void updateEnabledState() {
324 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
325 }
326
327 public void itemStateChanged(ItemEvent arg0) {
328 updateEnabledState();
329
330 }
331
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 public void actionPerformed(ActionEvent arg0) {
350 ChangesetListModel model = getCurrentChangesetListModel();
351 Set<Integer> sel = model.getSelectedChangesetIds();
352 if (sel.isEmpty())
353 return;
354 ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
355 Main.worker.submit(task);
356 }
357
358 protected void updateEnabledState() {
359 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
360 }
361
362 public void itemStateChanged(ItemEvent arg0) {
363 updateEnabledState();
364
365 }
366
367 public void valueChanged(ListSelectionEvent e) {
368 updateEnabledState();
369 }
370 }
371
372 /**
373 * Closes the currently selected changesets
374 *
375 */
376 class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
377 public CloseOpenChangesetsAction() {
378 putValue(NAME, tr("Close open changesets"));
379 putValue(SHORT_DESCRIPTION, tr("Closes the selected open changesets"));
380 putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
381 updateEnabledState();
382 }
383
384 public void actionPerformed(ActionEvent arg0) {
385 List<Changeset> sel = getCurrentChangesetListModel().getSelectedOpenChangesets();
386 if (sel.isEmpty())
387 return;
388 Main.worker.submit(new CloseChangesetTask(sel));
389 }
390
391 protected void updateEnabledState() {
392 setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
393 }
394
395 public void itemStateChanged(ItemEvent arg0) {
396 updateEnabledState();
397 }
398
399 public void valueChanged(ListSelectionEvent e) {
400 updateEnabledState();
401 }
402 }
403
404 /**
405 * Show information about the currently selected changesets
406 *
407 */
408 class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
409 public ShowChangesetInfoAction() {
410 putValue(NAME, tr("Show info"));
411 putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
412 putValue(SMALL_ICON, ImageProvider.get("about"));
413 updateEnabledState();
414 }
415
416 public void actionPerformed(ActionEvent arg0) {
417 Set<Changeset> sel = getCurrentChangesetListModel().getSelectedChangesets();
418 if (sel.isEmpty())
419 return;
420 if (sel.size() > 10 && ! AbstractInfoAction.confirmLaunchMultiple(sel.size()))
421 return;
422 String baseUrl = AbstractInfoAction.getBaseBrowseUrl();
423 for (Changeset cs: sel) {
424 String url = baseUrl + "/changeset/" + cs.getId();
425 OpenBrowser.displayUrl(
426 url
427 );
428 }
429 }
430
431 protected void updateEnabledState() {
432 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
433 }
434
435 public void itemStateChanged(ItemEvent arg0) {
436 updateEnabledState();
437 }
438
439 public void valueChanged(ListSelectionEvent e) {
440 updateEnabledState();
441 }
442 }
443
444 /**
445 * Show information about the currently selected changesets
446 *
447 */
448 class LaunchChangesetManagerAction extends AbstractAction implements ListSelectionListener, ItemListener {
449 public LaunchChangesetManagerAction() {
450 putValue(NAME, tr("Details"));
451 putValue(SHORT_DESCRIPTION, tr("Opens the Changeset Manager window for the selected changesets"));
452 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "changesetmanager"));
453 updateEnabledState();
454 }
455
456 protected void launchChangesetManager(Collection<Integer> toSelect) {
457 ChangesetCacheManager cm = ChangesetCacheManager.getInstance();
458 if (cm.isVisible()) {
459 cm.setExtendedState(Frame.NORMAL);
460 cm.toFront();
461 cm.requestFocus();
462 } else {
463 cm.setVisible(true);
464 cm.toFront();
465 cm.requestFocus();
466 }
467 cm.setSelectedChangesetsById(toSelect);
468 }
469
470 public void actionPerformed(ActionEvent arg0) {
471 ChangesetListModel model = getCurrentChangesetListModel();
472 Set<Integer> sel = model.getSelectedChangesetIds();
473 if (sel.isEmpty())
474 return;
475 final Set<Integer> toDownload = new HashSet<Integer>();
476 ChangesetCache cc = ChangesetCache.getInstance();
477 for (int id: sel) {
478 if (!cc.contains(id)) {
479 toDownload.add(id);
480 }
481 }
482
483 final ChangesetHeaderDownloadTask task;
484 final Future<?> future;
485 if (toDownload.isEmpty()) {
486 task = null;
487 future = null;
488 } else {
489 task = new ChangesetHeaderDownloadTask(toDownload);
490 future = Main.worker.submit(task);
491 }
492
493 Runnable r = new Runnable() {
494 public void run() {
495 // first, wait for the download task to finish, if a download
496 // task was launched
497 if (future != null) {
498 try {
499 future.get();
500 } catch(InterruptedException e) {
501 e.printStackTrace();
502 } catch(ExecutionException e){
503 e.printStackTrace();
504 BugReportExceptionHandler.handleException(e.getCause());
505 return;
506 }
507 }
508 if (task != null) {
509 if (task.isCanceled())
510 // don't launch the changeset manager if the download task
511 // was canceled
512 return;
513 if (task.isFailed()) {
514 toDownload.clear();
515 }
516 }
517 // launch the task
518 launchChangesetManager(toDownload);
519 }
520 };
521 Main.worker.submit(r);
522 }
523
524 protected void updateEnabledState() {
525 setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
526 }
527
528 public void itemStateChanged(ItemEvent arg0) {
529 updateEnabledState();
530 }
531
532 public void valueChanged(ListSelectionEvent e) {
533 updateEnabledState();
534 }
535 }
536
537 class ChangesetPopupMenuLauncher extends PopupMenuLauncher {
538 @Override
539 public void launch(MouseEvent evt) {
540 JList lst = getCurrentChangesetList();
541 if (lst.getSelectedIndices().length == 0) {
542 int idx = lst.locationToIndex(evt.getPoint());
543 if (idx >=0) {
544 lst.getSelectionModel().addSelectionInterval(idx, idx);
545 }
546 }
547 ChangesetDialogPopup popup = new ChangesetDialogPopup();
548 popup.show(lst, evt.getX(), evt.getY());
549
550 }
551 }
552
553 class ChangesetDialogPopup extends JPopupMenu {
554 public ChangesetDialogPopup() {
555 add(selectObjectsAction);
556 addSeparator();
557 add(readChangesetAction);
558 add(closeChangesetAction);
559 addSeparator();
560 add(showChangesetInfoAction);
561 }
562 }
563}
Note: See TracBrowser for help on using the repository browser.