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

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

see #15229 - deprecate all Main methods related to network features. New NetworkManager class

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