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

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

checkstyle - NoWhiteSpaceBefore ...

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