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

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

fixed line endings of recent checkins

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