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

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

see #8465 - switch core to Java 7

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