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

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

see #11390 - sonar - squid:S1604 - Java 8: Anonymous inner classes containing only one method should become lambdas

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