source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/changeset/ChangesetCacheManager.java@ 16436

Last change on this file since 16436 was 16436, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 28.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.changeset;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Container;
9import java.awt.Dimension;
10import java.awt.FlowLayout;
11import java.awt.GraphicsEnvironment;
12import java.awt.Window;
13import java.awt.datatransfer.Clipboard;
14import java.awt.event.ActionEvent;
15import java.awt.event.KeyEvent;
16import java.awt.event.MouseEvent;
17import java.awt.event.WindowAdapter;
18import java.awt.event.WindowEvent;
19import java.util.Collection;
20import java.util.List;
21import java.util.Objects;
22import java.util.Set;
23import java.util.stream.Collectors;
24
25import javax.swing.AbstractAction;
26import javax.swing.DefaultListSelectionModel;
27import javax.swing.JButton;
28import javax.swing.JComponent;
29import javax.swing.JFrame;
30import javax.swing.JOptionPane;
31import javax.swing.JPanel;
32import javax.swing.JPopupMenu;
33import javax.swing.JScrollPane;
34import javax.swing.JSplitPane;
35import javax.swing.JTabbedPane;
36import javax.swing.JTable;
37import javax.swing.JToolBar;
38import javax.swing.KeyStroke;
39import javax.swing.ListSelectionModel;
40import javax.swing.TransferHandler;
41import javax.swing.event.ListSelectionEvent;
42import javax.swing.event.ListSelectionListener;
43
44import org.openstreetmap.josm.actions.downloadtasks.AbstractChangesetDownloadTask;
45import org.openstreetmap.josm.actions.downloadtasks.ChangesetContentDownloadTask;
46import org.openstreetmap.josm.actions.downloadtasks.ChangesetHeaderDownloadTask;
47import org.openstreetmap.josm.actions.downloadtasks.ChangesetQueryTask;
48import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
49import org.openstreetmap.josm.data.UserIdentityManager;
50import org.openstreetmap.josm.data.osm.Changeset;
51import org.openstreetmap.josm.data.osm.ChangesetCache;
52import org.openstreetmap.josm.data.osm.ChangesetDataSet;
53import org.openstreetmap.josm.data.osm.PrimitiveId;
54import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
55import org.openstreetmap.josm.gui.HelpAwareOptionPane;
56import org.openstreetmap.josm.gui.MainApplication;
57import org.openstreetmap.josm.gui.datatransfer.ChangesetTransferable;
58import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
59import org.openstreetmap.josm.gui.dialogs.changeset.query.ChangesetQueryDialog;
60import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
61import org.openstreetmap.josm.gui.help.HelpUtil;
62import org.openstreetmap.josm.gui.io.CloseChangesetTask;
63import org.openstreetmap.josm.gui.io.DownloadPrimitivesWithReferrersTask;
64import org.openstreetmap.josm.gui.util.GuiHelper;
65import org.openstreetmap.josm.gui.util.WindowGeometry;
66import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
67import org.openstreetmap.josm.io.ChangesetQuery;
68import org.openstreetmap.josm.io.NetworkManager;
69import org.openstreetmap.josm.io.OnlineResource;
70import org.openstreetmap.josm.tools.ImageProvider;
71import org.openstreetmap.josm.tools.InputMapUtils;
72import org.openstreetmap.josm.tools.Logging;
73import org.openstreetmap.josm.tools.StreamUtils;
74
75/**
76 * ChangesetCacheManager manages the local cache of changesets
77 * retrieved from the OSM API. It displays both a table of the locally cached changesets
78 * and detail information about an individual changeset. It also provides actions for
79 * downloading, querying, closing changesets, in addition to removing changesets from
80 * the local cache.
81 * @since 2689
82 */
83public class ChangesetCacheManager extends JFrame {
84
85 /** the unique instance of the cache manager */
86 private static volatile ChangesetCacheManager instance;
87 private JTabbedPane pnlChangesetDetailTabs;
88
89 /**
90 * Replies the unique instance of the changeset cache manager
91 *
92 * @return the unique instance of the changeset cache manager
93 */
94 public static ChangesetCacheManager getInstance() {
95 if (instance == null) {
96 instance = new ChangesetCacheManager();
97 }
98 return instance;
99 }
100
101 /**
102 * Hides and destroys the unique instance of the changeset cache manager.
103 *
104 */
105 public static void destroyInstance() {
106 if (instance != null) {
107 instance.setVisible(false);
108 GuiHelper.destroyComponents(instance, false);
109 instance.dispose();
110 instance = null;
111 }
112 }
113
114 private ChangesetCacheManagerModel model;
115 private JSplitPane spContent;
116 private boolean needsSplitPaneAdjustment;
117
118 private RemoveFromCacheAction actRemoveFromCacheAction;
119 private CloseSelectedChangesetsAction actCloseSelectedChangesetsAction;
120 private DownloadSelectedChangesetsAction actDownloadSelectedChangesets;
121 private DownloadSelectedChangesetContentAction actDownloadSelectedContent;
122 private DownloadSelectedChangesetObjectsAction actDownloadSelectedChangesetObjects;
123 private JTable tblChangesets;
124
125 /**
126 * Creates the various models required.
127 * @return the changeset cache model
128 */
129 static ChangesetCacheManagerModel buildModel() {
130 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
131 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
132 return new ChangesetCacheManagerModel(selectionModel);
133 }
134
135 /**
136 * builds the toolbar panel in the heading of the dialog
137 *
138 * @return the toolbar panel
139 */
140 static JPanel buildToolbarPanel() {
141 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
142
143 JButton btn = new JButton(new QueryAction());
144 pnl.add(btn);
145 pnl.add(new SingleChangesetDownloadPanel());
146 pnl.add(new JButton(new DownloadMyChangesets()));
147
148 return pnl;
149 }
150
151 /**
152 * builds the button panel in the footer of the dialog
153 *
154 * @return the button row pane
155 */
156 static JPanel buildButtonPanel() {
157 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
158
159 //-- cancel and close action
160 pnl.add(new JButton(new CancelAction()));
161
162 //-- help action
163 pnl.add(new JButton(new ContextSensitiveHelpAction(HelpUtil.ht("/Dialog/ChangesetManager"))));
164
165 return pnl;
166 }
167
168 /**
169 * Builds the panel with the changeset details
170 *
171 * @return the panel with the changeset details
172 */
173 protected JPanel buildChangesetDetailPanel() {
174 JPanel pnl = new JPanel(new BorderLayout());
175 JTabbedPane tp = new JTabbedPane();
176 pnlChangesetDetailTabs = tp;
177
178 // -- add the details panel
179 ChangesetDetailPanel pnlChangesetDetail = new ChangesetDetailPanel();
180 tp.add(pnlChangesetDetail);
181 model.addPropertyChangeListener(pnlChangesetDetail);
182
183 // -- add the tags panel
184 ChangesetTagsPanel pnlChangesetTags = new ChangesetTagsPanel();
185 tp.add(pnlChangesetTags);
186 model.addPropertyChangeListener(pnlChangesetTags);
187
188 // -- add the panel for the changeset content
189 ChangesetContentPanel pnlChangesetContent = new ChangesetContentPanel();
190 tp.add(pnlChangesetContent);
191 model.addPropertyChangeListener(pnlChangesetContent);
192
193 // -- add the panel for the changeset discussion
194 ChangesetDiscussionPanel pnlChangesetDiscussion = new ChangesetDiscussionPanel();
195 tp.add(pnlChangesetDiscussion);
196 model.addPropertyChangeListener(pnlChangesetDiscussion);
197
198 tp.setTitleAt(0, tr("Properties"));
199 tp.setToolTipTextAt(0, tr("Display the basic properties of the changeset"));
200 tp.setTitleAt(1, tr("Tags"));
201 tp.setToolTipTextAt(1, tr("Display the tags of the changeset"));
202 tp.setTitleAt(2, tr("Content"));
203 tp.setToolTipTextAt(2, tr("Display the objects created, updated, and deleted by the changeset"));
204 tp.setTitleAt(3, tr("Discussion"));
205 tp.setToolTipTextAt(3, tr("Display the public discussion around this changeset"));
206
207 pnl.add(tp, BorderLayout.CENTER);
208 return pnl;
209 }
210
211 /**
212 * builds the content panel of the dialog
213 *
214 * @return the content panel
215 */
216 protected JPanel buildContentPanel() {
217 JPanel pnl = new JPanel(new BorderLayout());
218
219 spContent = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
220 spContent.setLeftComponent(buildChangesetTablePanel());
221 spContent.setRightComponent(buildChangesetDetailPanel());
222 spContent.setOneTouchExpandable(true);
223 spContent.setDividerLocation(0.5);
224
225 pnl.add(spContent, BorderLayout.CENTER);
226 return pnl;
227 }
228
229 /**
230 * Builds the table with actions which can be applied to the currently visible changesets
231 * in the changeset table.
232 *
233 * @return changset actions panel
234 */
235 protected JPanel buildChangesetTableActionPanel() {
236 JPanel pnl = new JPanel(new BorderLayout());
237
238 JToolBar tb = new JToolBar(JToolBar.VERTICAL);
239 tb.setFloatable(false);
240
241 // -- remove from cache action
242 model.getSelectionModel().addListSelectionListener(actRemoveFromCacheAction);
243 tb.add(actRemoveFromCacheAction);
244
245 // -- close selected changesets action
246 model.getSelectionModel().addListSelectionListener(actCloseSelectedChangesetsAction);
247 tb.add(actCloseSelectedChangesetsAction);
248
249 // -- download selected changesets
250 model.getSelectionModel().addListSelectionListener(actDownloadSelectedChangesets);
251 tb.add(actDownloadSelectedChangesets);
252
253 // -- download the content of the selected changesets
254 model.getSelectionModel().addListSelectionListener(actDownloadSelectedContent);
255 tb.add(actDownloadSelectedContent);
256
257 // -- download the objects contained in the selected changesets from the OSM server
258 model.getSelectionModel().addListSelectionListener(actDownloadSelectedChangesetObjects);
259 tb.add(actDownloadSelectedChangesetObjects);
260
261 pnl.add(tb, BorderLayout.CENTER);
262 return pnl;
263 }
264
265 /**
266 * Builds the panel with the table of changesets
267 *
268 * @return the panel with the table of changesets
269 */
270 protected JPanel buildChangesetTablePanel() {
271 JPanel pnl = new JPanel(new BorderLayout());
272 tblChangesets = new JTable(
273 model,
274 new ChangesetCacheTableColumnModel(),
275 model.getSelectionModel()
276 );
277 tblChangesets.addMouseListener(new MouseEventHandler());
278 InputMapUtils.addEnterAction(tblChangesets, new ShowDetailAction(model));
279 model.getSelectionModel().addListSelectionListener(new ChangesetDetailViewSynchronizer(model));
280
281 // activate DEL on the table
282 tblChangesets.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "removeFromCache");
283 tblChangesets.getActionMap().put("removeFromCache", actRemoveFromCacheAction);
284
285 tblChangesets.setTransferHandler(new TransferHandler() {
286 @Override
287 public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
288 List<Changeset> changesets = model.getSelectedChangesets();
289 ChangesetTransferable transferable = new ChangesetTransferable(changesets);
290 ClipboardUtils.copy(transferable);
291 }
292 });
293
294 pnl.add(new JScrollPane(tblChangesets), BorderLayout.CENTER);
295 pnl.add(buildChangesetTableActionPanel(), BorderLayout.WEST);
296 return pnl;
297 }
298
299 protected void build() {
300 setTitle(tr("Changeset Manager"));
301 setIconImage(ImageProvider.get("dialogs/changeset", "changesetmanager").getImage());
302 Container cp = getContentPane();
303
304 cp.setLayout(new BorderLayout());
305
306 model = buildModel();
307 actRemoveFromCacheAction = new RemoveFromCacheAction(model);
308 actCloseSelectedChangesetsAction = new CloseSelectedChangesetsAction(model);
309 actDownloadSelectedChangesets = new DownloadSelectedChangesetsAction(model);
310 actDownloadSelectedContent = new DownloadSelectedChangesetContentAction(model);
311 actDownloadSelectedChangesetObjects = new DownloadSelectedChangesetObjectsAction();
312
313 cp.add(buildToolbarPanel(), BorderLayout.NORTH);
314 cp.add(buildContentPanel(), BorderLayout.CENTER);
315 cp.add(buildButtonPanel(), BorderLayout.SOUTH);
316
317 // the help context
318 HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/ChangesetManager"));
319
320 // make the dialog respond to ESC
321 InputMapUtils.addEscapeAction(getRootPane(), new CancelAction());
322
323 // install a window event handler
324 addWindowListener(new WindowEventHandler());
325 }
326
327 /**
328 * Constructs a new {@code ChangesetCacheManager}.
329 */
330 public ChangesetCacheManager() {
331 build();
332 }
333
334 @Override
335 public void setVisible(boolean visible) {
336 if (visible) {
337 new WindowGeometry(
338 getClass().getName() + ".geometry",
339 WindowGeometry.centerInWindow(
340 getParent(),
341 new Dimension(1000, 600)
342 )
343 ).applySafe(this);
344 needsSplitPaneAdjustment = true;
345 model.init();
346
347 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
348 model.tearDown();
349 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
350 }
351 super.setVisible(visible);
352 }
353
354 /**
355 * Handler for window events
356 *
357 */
358 class WindowEventHandler extends WindowAdapter {
359 @Override
360 public void windowClosing(WindowEvent e) {
361 destroyInstance();
362 }
363
364 @Override
365 public void windowActivated(WindowEvent e) {
366 if (needsSplitPaneAdjustment) {
367 spContent.setDividerLocation(0.5);
368 needsSplitPaneAdjustment = false;
369 }
370 }
371 }
372
373 /**
374 * the cancel / close action
375 */
376 static class CancelAction extends AbstractAction {
377 CancelAction() {
378 putValue(NAME, tr("Close"));
379 new ImageProvider("cancel").getResource().attachImageIcon(this);
380 putValue(SHORT_DESCRIPTION, tr("Close the dialog"));
381 }
382
383 public void cancelAndClose() {
384 destroyInstance();
385 }
386
387 @Override
388 public void actionPerformed(ActionEvent e) {
389 cancelAndClose();
390 }
391 }
392
393 /**
394 * The action to query and download changesets
395 */
396 static class QueryAction extends AbstractAction {
397
398 QueryAction() {
399 putValue(NAME, tr("Query"));
400 new ImageProvider("dialogs", "search").getResource().attachImageIcon(this);
401 putValue(SHORT_DESCRIPTION, tr("Launch the dialog for querying changesets"));
402 setEnabled(!NetworkManager.isOffline(OnlineResource.OSM_API));
403 }
404
405 @Override
406 public void actionPerformed(ActionEvent evt) {
407 Window parent = GuiHelper.getWindowAncestorFor(evt);
408 ChangesetQueryDialog dialog = new ChangesetQueryDialog(parent);
409 dialog.initForUserInput();
410 dialog.setVisible(true);
411 if (dialog.isCanceled())
412 return;
413
414 try {
415 ChangesetQuery query = dialog.getChangesetQuery();
416 if (query != null) {
417 ChangesetCacheManager.getInstance().runDownloadTask(new ChangesetQueryTask(parent, query));
418 }
419 } catch (IllegalStateException e) {
420 Logging.error(e);
421 JOptionPane.showMessageDialog(parent, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
422 }
423 }
424 }
425
426 /**
427 * Removes the selected changesets from the local changeset cache
428 *
429 */
430 static class RemoveFromCacheAction extends AbstractAction implements ListSelectionListener {
431 private final ChangesetCacheManagerModel model;
432
433 RemoveFromCacheAction(ChangesetCacheManagerModel model) {
434 putValue(NAME, tr("Remove from cache"));
435 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this);
436 putValue(SHORT_DESCRIPTION, tr("Remove the selected changesets from the local cache"));
437 this.model = model;
438 updateEnabledState();
439 }
440
441 @Override
442 public void actionPerformed(ActionEvent e) {
443 ChangesetCache.getInstance().remove(model.getSelectedChangesets());
444 }
445
446 protected void updateEnabledState() {
447 setEnabled(model.hasSelectedChangesets());
448 }
449
450 @Override
451 public void valueChanged(ListSelectionEvent e) {
452 updateEnabledState();
453 }
454 }
455
456 /**
457 * Closes the selected changesets
458 *
459 */
460 static class CloseSelectedChangesetsAction extends AbstractAction implements ListSelectionListener {
461 private final ChangesetCacheManagerModel model;
462
463 CloseSelectedChangesetsAction(ChangesetCacheManagerModel model) {
464 putValue(NAME, tr("Close"));
465 new ImageProvider("closechangeset").getResource().attachImageIcon(this);
466 putValue(SHORT_DESCRIPTION, tr("Close the selected changesets"));
467 this.model = model;
468 updateEnabledState();
469 }
470
471 @Override
472 public void actionPerformed(ActionEvent e) {
473 MainApplication.worker.submit(new CloseChangesetTask(model.getSelectedChangesets()));
474 }
475
476 protected void updateEnabledState() {
477 List<Changeset> selected = model.getSelectedChangesets();
478 UserIdentityManager im = UserIdentityManager.getInstance();
479 for (Changeset cs: selected) {
480 if (cs.isOpen()) {
481 if (im.isPartiallyIdentified() && cs.getUser() != null && cs.getUser().getName().equals(im.getUserName())) {
482 setEnabled(true);
483 return;
484 }
485 if (im.isFullyIdentified() && cs.getUser() != null && cs.getUser().getId() == im.getUserId()) {
486 setEnabled(true);
487 return;
488 }
489 }
490 }
491 setEnabled(false);
492 }
493
494 @Override
495 public void valueChanged(ListSelectionEvent e) {
496 updateEnabledState();
497 }
498 }
499
500 /**
501 * Downloads the selected changesets
502 *
503 */
504 static class DownloadSelectedChangesetsAction extends AbstractAction implements ListSelectionListener {
505 private final ChangesetCacheManagerModel model;
506
507 DownloadSelectedChangesetsAction(ChangesetCacheManagerModel model) {
508 putValue(NAME, tr("Update changeset"));
509 new ImageProvider("dialogs/changeset", "updatechangeset").getResource().attachImageIcon(this);
510 putValue(SHORT_DESCRIPTION, tr("Updates the selected changesets with current data from the OSM server"));
511 this.model = model;
512 updateEnabledState();
513 }
514
515 @Override
516 public void actionPerformed(ActionEvent e) {
517 if (!GraphicsEnvironment.isHeadless()) {
518 ChangesetCacheManager.getInstance().runDownloadTask(
519 ChangesetHeaderDownloadTask.buildTaskForChangesets(GuiHelper.getWindowAncestorFor(e), model.getSelectedChangesets()));
520 }
521 }
522
523 protected void updateEnabledState() {
524 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
525 }
526
527 @Override
528 public void valueChanged(ListSelectionEvent e) {
529 updateEnabledState();
530 }
531 }
532
533 /**
534 * Downloads the content of selected changesets from the OSM server
535 *
536 */
537 static class DownloadSelectedChangesetContentAction extends AbstractAction implements ListSelectionListener {
538 private final ChangesetCacheManagerModel model;
539
540 DownloadSelectedChangesetContentAction(ChangesetCacheManagerModel model) {
541 putValue(NAME, tr("Download changeset content"));
542 new ImageProvider("dialogs/changeset", "downloadchangesetcontent").getResource().attachImageIcon(this);
543 putValue(SHORT_DESCRIPTION, tr("Download the content of the selected changesets from the server"));
544 this.model = model;
545 updateEnabledState();
546 }
547
548 @Override
549 public void actionPerformed(ActionEvent e) {
550 if (!GraphicsEnvironment.isHeadless()) {
551 ChangesetCacheManager.getInstance().runDownloadTask(
552 new ChangesetContentDownloadTask(GuiHelper.getWindowAncestorFor(e), model.getSelectedChangesetIds()));
553 }
554 }
555
556 protected void updateEnabledState() {
557 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
558 }
559
560 @Override
561 public void valueChanged(ListSelectionEvent e) {
562 updateEnabledState();
563 }
564 }
565
566 /**
567 * Downloads the objects contained in the selected changesets from the OSM server
568 */
569 private class DownloadSelectedChangesetObjectsAction extends AbstractAction implements ListSelectionListener {
570
571 DownloadSelectedChangesetObjectsAction() {
572 putValue(NAME, tr("Download changed objects"));
573 new ImageProvider("downloadprimitive").getResource().attachImageIcon(this);
574 putValue(SHORT_DESCRIPTION, tr("Download the current version of the changed objects in the selected changesets"));
575 updateEnabledState();
576 }
577
578 @Override
579 public void actionPerformed(ActionEvent e) {
580 if (!GraphicsEnvironment.isHeadless()) {
581 actDownloadSelectedContent.actionPerformed(e);
582 MainApplication.worker.submit(() -> {
583 final List<PrimitiveId> primitiveIds = model.getSelectedChangesets().stream()
584 .map(Changeset::getContent)
585 .filter(Objects::nonNull)
586 .flatMap(content -> StreamUtils.toStream(content::iterator))
587 .map(ChangesetDataSet.ChangesetDataSetEntry::getPrimitive)
588 .map(HistoryOsmPrimitive::getPrimitiveId)
589 .distinct()
590 .collect(Collectors.toList());
591 new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null).run();
592 });
593 }
594 }
595
596 protected void updateEnabledState() {
597 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
598 }
599
600 @Override
601 public void valueChanged(ListSelectionEvent e) {
602 updateEnabledState();
603 }
604 }
605
606 static class ShowDetailAction extends AbstractAction {
607 private final ChangesetCacheManagerModel model;
608
609 ShowDetailAction(ChangesetCacheManagerModel model) {
610 this.model = model;
611 }
612
613 protected void showDetails() {
614 List<Changeset> selected = model.getSelectedChangesets();
615 if (selected.size() == 1) {
616 model.setChangesetInDetailView(selected.get(0));
617 }
618 }
619
620 @Override
621 public void actionPerformed(ActionEvent e) {
622 showDetails();
623 }
624 }
625
626 static class DownloadMyChangesets extends AbstractAction {
627 DownloadMyChangesets() {
628 putValue(NAME, tr("My changesets"));
629 new ImageProvider("dialogs/changeset", "downloadchangeset").getResource().attachImageIcon(this);
630 putValue(SHORT_DESCRIPTION, tr("Download my changesets from the OSM server (max. 100 changesets)"));
631 setEnabled(!NetworkManager.isOffline(OnlineResource.OSM_API));
632 }
633
634 protected void alertAnonymousUser(Component parent) {
635 HelpAwareOptionPane.showOptionDialog(
636 parent,
637 tr("<html>JOSM is currently running with an anonymous user. It cannot download<br>"
638 + "your changesets from the OSM server unless you enter your OSM user name<br>"
639 + "in the JOSM preferences.</html>"
640 ),
641 tr("Warning"),
642 JOptionPane.WARNING_MESSAGE,
643 HelpUtil.ht("/Dialog/ChangesetManager#CanDownloadMyChangesets")
644 );
645 }
646
647 @Override
648 public void actionPerformed(ActionEvent e) {
649 Window parent = GuiHelper.getWindowAncestorFor(e);
650 try {
651 ChangesetQuery query = ChangesetQuery.forCurrentUser();
652 if (!GraphicsEnvironment.isHeadless()) {
653 ChangesetCacheManager.getInstance().runDownloadTask(new ChangesetQueryTask(parent, query));
654 }
655 } catch (IllegalStateException ex) {
656 alertAnonymousUser(parent);
657 Logging.trace(ex);
658 }
659 }
660 }
661
662 class MouseEventHandler extends PopupMenuLauncher {
663
664 MouseEventHandler() {
665 super(new ChangesetTablePopupMenu());
666 }
667
668 @Override
669 public void mouseClicked(MouseEvent evt) {
670 if (isDoubleClick(evt)) {
671 new ShowDetailAction(model).showDetails();
672 }
673 }
674 }
675
676 class ChangesetTablePopupMenu extends JPopupMenu {
677 ChangesetTablePopupMenu() {
678 add(actRemoveFromCacheAction);
679 add(actCloseSelectedChangesetsAction);
680 add(actDownloadSelectedChangesets);
681 add(actDownloadSelectedContent);
682 add(actDownloadSelectedChangesetObjects);
683 }
684 }
685
686 static class ChangesetDetailViewSynchronizer implements ListSelectionListener {
687 private final ChangesetCacheManagerModel model;
688
689 ChangesetDetailViewSynchronizer(ChangesetCacheManagerModel model) {
690 this.model = model;
691 }
692
693 @Override
694 public void valueChanged(ListSelectionEvent e) {
695 List<Changeset> selected = model.getSelectedChangesets();
696 if (selected.size() == 1) {
697 model.setChangesetInDetailView(selected.get(0));
698 } else {
699 model.setChangesetInDetailView(null);
700 }
701 }
702 }
703
704 /**
705 * Returns the changeset cache model.
706 * @return the changeset cache model
707 * @since 12495
708 */
709 public ChangesetCacheManagerModel getModel() {
710 return model;
711 }
712
713 /**
714 * Selects the changesets in <code>changests</code>, provided the
715 * respective changesets are already present in the local changeset cache.
716 *
717 * @param changesets the collection of changesets. If {@code null}, the
718 * selection is cleared.
719 */
720 public void setSelectedChangesets(Collection<Changeset> changesets) {
721 model.setSelectedChangesets(changesets);
722 final int idx = model.getSelectionModel().getMinSelectionIndex();
723 if (idx < 0)
724 return;
725 GuiHelper.runInEDTAndWait(() -> tblChangesets.scrollRectToVisible(tblChangesets.getCellRect(idx, 0, true)));
726 repaint();
727 }
728
729 /**
730 * Selects the changesets with the ids in <code>ids</code>, provided the
731 * respective changesets are already present in the local changeset cache.
732 *
733 * @param ids the collection of ids. If null, the selection is cleared.
734 */
735 public void setSelectedChangesetsById(Collection<Integer> ids) {
736 if (ids == null) {
737 setSelectedChangesets(null);
738 return;
739 }
740 ChangesetCache cc = ChangesetCache.getInstance();
741 Set<Changeset> toSelect = ids.stream()
742 .filter(cc::contains)
743 .map(cc::get)
744 .collect(Collectors.toSet());
745 setSelectedChangesets(toSelect);
746 }
747
748 /**
749 * Selects the given component in the detail tabbed panel
750 * @param clazz the class of the component to select
751 */
752 public void setSelectedComponentInDetailPanel(Class<? extends JComponent> clazz) {
753 for (Component component : pnlChangesetDetailTabs.getComponents()) {
754 if (component.getClass().equals(clazz)) {
755 pnlChangesetDetailTabs.setSelectedComponent(component);
756 break;
757 }
758 }
759 }
760
761 /**
762 * Runs the given changeset download task.
763 * @param task The changeset download task to run
764 */
765 public void runDownloadTask(final AbstractChangesetDownloadTask task) {
766 MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
767 MainApplication.worker.submit(() -> {
768 if (task.isCanceled() || task.isFailed())
769 return;
770 GuiHelper.runInEDT(() -> setSelectedChangesets(task.getDownloadedData()));
771 });
772 }
773}
Note: See TracBrowser for help on using the repository browser.