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

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

fix #18919 - Changeset Manager: sort tables

  • Property svn:eol-style set to native
File size: 28.5 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.setRowSorter(new ChangesetCacheTableRowSorter(model));
278 tblChangesets.addMouseListener(new MouseEventHandler());
279 InputMapUtils.addEnterAction(tblChangesets, new ShowDetailAction(model));
280 model.getSelectionModel().addListSelectionListener(new ChangesetDetailViewSynchronizer(model));
281
282 // activate DEL on the table
283 tblChangesets.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "removeFromCache");
284 tblChangesets.getActionMap().put("removeFromCache", actRemoveFromCacheAction);
285
286 tblChangesets.setTransferHandler(new TransferHandler() {
287 @Override
288 public void exportToClipboard(JComponent comp, Clipboard clip, int action) {
289 List<Changeset> changesets = model.getSelectedChangesets();
290 ChangesetTransferable transferable = new ChangesetTransferable(changesets);
291 ClipboardUtils.copy(transferable);
292 }
293 });
294
295 pnl.add(new JScrollPane(tblChangesets), BorderLayout.CENTER);
296 pnl.add(buildChangesetTableActionPanel(), BorderLayout.WEST);
297 return pnl;
298 }
299
300 protected void build() {
301 setTitle(tr("Changeset Manager"));
302 setIconImage(ImageProvider.get("dialogs/changeset", "changesetmanager").getImage());
303 Container cp = getContentPane();
304
305 cp.setLayout(new BorderLayout());
306
307 model = buildModel();
308 actRemoveFromCacheAction = new RemoveFromCacheAction(model);
309 actCloseSelectedChangesetsAction = new CloseSelectedChangesetsAction(model);
310 actDownloadSelectedChangesets = new DownloadSelectedChangesetsAction(model);
311 actDownloadSelectedContent = new DownloadSelectedChangesetContentAction(model);
312 actDownloadSelectedChangesetObjects = new DownloadSelectedChangesetObjectsAction();
313
314 cp.add(buildToolbarPanel(), BorderLayout.NORTH);
315 cp.add(buildContentPanel(), BorderLayout.CENTER);
316 cp.add(buildButtonPanel(), BorderLayout.SOUTH);
317
318 // the help context
319 HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/ChangesetManager"));
320
321 // make the dialog respond to ESC
322 InputMapUtils.addEscapeAction(getRootPane(), new CancelAction());
323
324 // install a window event handler
325 addWindowListener(new WindowEventHandler());
326 }
327
328 /**
329 * Constructs a new {@code ChangesetCacheManager}.
330 */
331 public ChangesetCacheManager() {
332 build();
333 }
334
335 @Override
336 public void setVisible(boolean visible) {
337 if (visible) {
338 new WindowGeometry(
339 getClass().getName() + ".geometry",
340 WindowGeometry.centerInWindow(
341 getParent(),
342 new Dimension(1000, 600)
343 )
344 ).applySafe(this);
345 needsSplitPaneAdjustment = true;
346 model.init();
347
348 } else if (isShowing()) { // Avoid IllegalComponentStateException like in #8775
349 model.tearDown();
350 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
351 }
352 super.setVisible(visible);
353 }
354
355 /**
356 * Handler for window events
357 *
358 */
359 class WindowEventHandler extends WindowAdapter {
360 @Override
361 public void windowClosing(WindowEvent e) {
362 destroyInstance();
363 }
364
365 @Override
366 public void windowActivated(WindowEvent e) {
367 if (needsSplitPaneAdjustment) {
368 spContent.setDividerLocation(0.5);
369 needsSplitPaneAdjustment = false;
370 }
371 }
372 }
373
374 /**
375 * the cancel / close action
376 */
377 static class CancelAction extends AbstractAction {
378 CancelAction() {
379 putValue(NAME, tr("Close"));
380 new ImageProvider("cancel").getResource().attachImageIcon(this);
381 putValue(SHORT_DESCRIPTION, tr("Close the dialog"));
382 }
383
384 public void cancelAndClose() {
385 destroyInstance();
386 }
387
388 @Override
389 public void actionPerformed(ActionEvent e) {
390 cancelAndClose();
391 }
392 }
393
394 /**
395 * The action to query and download changesets
396 */
397 static class QueryAction extends AbstractAction {
398
399 QueryAction() {
400 putValue(NAME, tr("Query"));
401 new ImageProvider("dialogs", "search").getResource().attachImageIcon(this);
402 putValue(SHORT_DESCRIPTION, tr("Launch the dialog for querying changesets"));
403 setEnabled(!NetworkManager.isOffline(OnlineResource.OSM_API));
404 }
405
406 @Override
407 public void actionPerformed(ActionEvent evt) {
408 Window parent = GuiHelper.getWindowAncestorFor(evt);
409 ChangesetQueryDialog dialog = new ChangesetQueryDialog(parent);
410 dialog.initForUserInput();
411 dialog.setVisible(true);
412 if (dialog.isCanceled())
413 return;
414
415 try {
416 ChangesetQuery query = dialog.getChangesetQuery();
417 if (query != null) {
418 ChangesetCacheManager.getInstance().runDownloadTask(new ChangesetQueryTask(parent, query));
419 }
420 } catch (IllegalStateException e) {
421 Logging.error(e);
422 JOptionPane.showMessageDialog(parent, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
423 }
424 }
425 }
426
427 /**
428 * Removes the selected changesets from the local changeset cache
429 *
430 */
431 static class RemoveFromCacheAction extends AbstractAction implements ListSelectionListener {
432 private final ChangesetCacheManagerModel model;
433
434 RemoveFromCacheAction(ChangesetCacheManagerModel model) {
435 putValue(NAME, tr("Remove from cache"));
436 new ImageProvider("dialogs", "delete").getResource().attachImageIcon(this);
437 putValue(SHORT_DESCRIPTION, tr("Remove the selected changesets from the local cache"));
438 this.model = model;
439 updateEnabledState();
440 }
441
442 @Override
443 public void actionPerformed(ActionEvent e) {
444 ChangesetCache.getInstance().remove(model.getSelectedChangesets());
445 }
446
447 protected void updateEnabledState() {
448 setEnabled(model.hasSelectedChangesets());
449 }
450
451 @Override
452 public void valueChanged(ListSelectionEvent e) {
453 updateEnabledState();
454 }
455 }
456
457 /**
458 * Closes the selected changesets
459 *
460 */
461 static class CloseSelectedChangesetsAction extends AbstractAction implements ListSelectionListener {
462 private final ChangesetCacheManagerModel model;
463
464 CloseSelectedChangesetsAction(ChangesetCacheManagerModel model) {
465 putValue(NAME, tr("Close"));
466 new ImageProvider("closechangeset").getResource().attachImageIcon(this);
467 putValue(SHORT_DESCRIPTION, tr("Close the selected changesets"));
468 this.model = model;
469 updateEnabledState();
470 }
471
472 @Override
473 public void actionPerformed(ActionEvent e) {
474 MainApplication.worker.submit(new CloseChangesetTask(model.getSelectedChangesets()));
475 }
476
477 protected void updateEnabledState() {
478 List<Changeset> selected = model.getSelectedChangesets();
479 UserIdentityManager im = UserIdentityManager.getInstance();
480 for (Changeset cs: selected) {
481 if (cs.isOpen()) {
482 if (im.isPartiallyIdentified() && cs.getUser() != null && cs.getUser().getName().equals(im.getUserName())) {
483 setEnabled(true);
484 return;
485 }
486 if (im.isFullyIdentified() && cs.getUser() != null && cs.getUser().getId() == im.getUserId()) {
487 setEnabled(true);
488 return;
489 }
490 }
491 }
492 setEnabled(false);
493 }
494
495 @Override
496 public void valueChanged(ListSelectionEvent e) {
497 updateEnabledState();
498 }
499 }
500
501 /**
502 * Downloads the selected changesets
503 *
504 */
505 static class DownloadSelectedChangesetsAction extends AbstractAction implements ListSelectionListener {
506 private final ChangesetCacheManagerModel model;
507
508 DownloadSelectedChangesetsAction(ChangesetCacheManagerModel model) {
509 putValue(NAME, tr("Update changeset"));
510 new ImageProvider("dialogs/changeset", "updatechangeset").getResource().attachImageIcon(this);
511 putValue(SHORT_DESCRIPTION, tr("Updates the selected changesets with current data from the OSM server"));
512 this.model = model;
513 updateEnabledState();
514 }
515
516 @Override
517 public void actionPerformed(ActionEvent e) {
518 if (!GraphicsEnvironment.isHeadless()) {
519 ChangesetCacheManager.getInstance().runDownloadTask(
520 ChangesetHeaderDownloadTask.buildTaskForChangesets(GuiHelper.getWindowAncestorFor(e), model.getSelectedChangesets()));
521 }
522 }
523
524 protected void updateEnabledState() {
525 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
526 }
527
528 @Override
529 public void valueChanged(ListSelectionEvent e) {
530 updateEnabledState();
531 }
532 }
533
534 /**
535 * Downloads the content of selected changesets from the OSM server
536 *
537 */
538 static class DownloadSelectedChangesetContentAction extends AbstractAction implements ListSelectionListener {
539 private final ChangesetCacheManagerModel model;
540
541 DownloadSelectedChangesetContentAction(ChangesetCacheManagerModel model) {
542 putValue(NAME, tr("Download changeset content"));
543 new ImageProvider("dialogs/changeset", "downloadchangesetcontent").getResource().attachImageIcon(this);
544 putValue(SHORT_DESCRIPTION, tr("Download the content of the selected changesets from the server"));
545 this.model = model;
546 updateEnabledState();
547 }
548
549 @Override
550 public void actionPerformed(ActionEvent e) {
551 if (!GraphicsEnvironment.isHeadless()) {
552 ChangesetCacheManager.getInstance().runDownloadTask(
553 new ChangesetContentDownloadTask(GuiHelper.getWindowAncestorFor(e), model.getSelectedChangesetIds()));
554 }
555 }
556
557 protected void updateEnabledState() {
558 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
559 }
560
561 @Override
562 public void valueChanged(ListSelectionEvent e) {
563 updateEnabledState();
564 }
565 }
566
567 /**
568 * Downloads the objects contained in the selected changesets from the OSM server
569 */
570 private class DownloadSelectedChangesetObjectsAction extends AbstractAction implements ListSelectionListener {
571
572 DownloadSelectedChangesetObjectsAction() {
573 putValue(NAME, tr("Download changed objects"));
574 new ImageProvider("downloadprimitive").getResource().attachImageIcon(this);
575 putValue(SHORT_DESCRIPTION, tr("Download the current version of the changed objects in the selected changesets"));
576 updateEnabledState();
577 }
578
579 @Override
580 public void actionPerformed(ActionEvent e) {
581 if (!GraphicsEnvironment.isHeadless()) {
582 actDownloadSelectedContent.actionPerformed(e);
583 MainApplication.worker.submit(() -> {
584 final List<PrimitiveId> primitiveIds = model.getSelectedChangesets().stream()
585 .map(Changeset::getContent)
586 .filter(Objects::nonNull)
587 .flatMap(content -> StreamUtils.toStream(content::iterator))
588 .map(ChangesetDataSet.ChangesetDataSetEntry::getPrimitive)
589 .map(HistoryOsmPrimitive::getPrimitiveId)
590 .distinct()
591 .collect(Collectors.toList());
592 new DownloadPrimitivesWithReferrersTask(false, primitiveIds, true, true, null, null).run();
593 });
594 }
595 }
596
597 protected void updateEnabledState() {
598 setEnabled(model.hasSelectedChangesets() && !NetworkManager.isOffline(OnlineResource.OSM_API));
599 }
600
601 @Override
602 public void valueChanged(ListSelectionEvent e) {
603 updateEnabledState();
604 }
605 }
606
607 static class ShowDetailAction extends AbstractAction {
608 private final ChangesetCacheManagerModel model;
609
610 ShowDetailAction(ChangesetCacheManagerModel model) {
611 this.model = model;
612 }
613
614 protected void showDetails() {
615 List<Changeset> selected = model.getSelectedChangesets();
616 if (selected.size() == 1) {
617 model.setChangesetInDetailView(selected.get(0));
618 }
619 }
620
621 @Override
622 public void actionPerformed(ActionEvent e) {
623 showDetails();
624 }
625 }
626
627 static class DownloadMyChangesets extends AbstractAction {
628 DownloadMyChangesets() {
629 putValue(NAME, tr("My changesets"));
630 new ImageProvider("dialogs/changeset", "downloadchangeset").getResource().attachImageIcon(this);
631 putValue(SHORT_DESCRIPTION, tr("Download my changesets from the OSM server (max. 100 changesets)"));
632 setEnabled(!NetworkManager.isOffline(OnlineResource.OSM_API));
633 }
634
635 protected void alertAnonymousUser(Component parent) {
636 HelpAwareOptionPane.showOptionDialog(
637 parent,
638 tr("<html>JOSM is currently running with an anonymous user. It cannot download<br>"
639 + "your changesets from the OSM server unless you enter your OSM user name<br>"
640 + "in the JOSM preferences.</html>"
641 ),
642 tr("Warning"),
643 JOptionPane.WARNING_MESSAGE,
644 HelpUtil.ht("/Dialog/ChangesetManager#CanDownloadMyChangesets")
645 );
646 }
647
648 @Override
649 public void actionPerformed(ActionEvent e) {
650 Window parent = GuiHelper.getWindowAncestorFor(e);
651 try {
652 ChangesetQuery query = ChangesetQuery.forCurrentUser();
653 if (!GraphicsEnvironment.isHeadless()) {
654 ChangesetCacheManager.getInstance().runDownloadTask(new ChangesetQueryTask(parent, query));
655 }
656 } catch (IllegalStateException ex) {
657 alertAnonymousUser(parent);
658 Logging.trace(ex);
659 }
660 }
661 }
662
663 class MouseEventHandler extends PopupMenuLauncher {
664
665 MouseEventHandler() {
666 super(new ChangesetTablePopupMenu());
667 }
668
669 @Override
670 public void mouseClicked(MouseEvent evt) {
671 if (isDoubleClick(evt)) {
672 new ShowDetailAction(model).showDetails();
673 }
674 }
675 }
676
677 class ChangesetTablePopupMenu extends JPopupMenu {
678 ChangesetTablePopupMenu() {
679 add(actRemoveFromCacheAction);
680 add(actCloseSelectedChangesetsAction);
681 add(actDownloadSelectedChangesets);
682 add(actDownloadSelectedContent);
683 add(actDownloadSelectedChangesetObjects);
684 }
685 }
686
687 static class ChangesetDetailViewSynchronizer implements ListSelectionListener {
688 private final ChangesetCacheManagerModel model;
689
690 ChangesetDetailViewSynchronizer(ChangesetCacheManagerModel model) {
691 this.model = model;
692 }
693
694 @Override
695 public void valueChanged(ListSelectionEvent e) {
696 List<Changeset> selected = model.getSelectedChangesets();
697 if (selected.size() == 1) {
698 model.setChangesetInDetailView(selected.get(0));
699 } else {
700 model.setChangesetInDetailView(null);
701 }
702 }
703 }
704
705 /**
706 * Returns the changeset cache model.
707 * @return the changeset cache model
708 * @since 12495
709 */
710 public ChangesetCacheManagerModel getModel() {
711 return model;
712 }
713
714 /**
715 * Selects the changesets in <code>changests</code>, provided the
716 * respective changesets are already present in the local changeset cache.
717 *
718 * @param changesets the collection of changesets. If {@code null}, the
719 * selection is cleared.
720 */
721 public void setSelectedChangesets(Collection<Changeset> changesets) {
722 model.setSelectedChangesets(changesets);
723 final int idx = model.getSelectionModel().getMinSelectionIndex();
724 if (idx < 0)
725 return;
726 GuiHelper.runInEDTAndWait(() -> tblChangesets.scrollRectToVisible(tblChangesets.getCellRect(idx, 0, true)));
727 repaint();
728 }
729
730 /**
731 * Selects the changesets with the ids in <code>ids</code>, provided the
732 * respective changesets are already present in the local changeset cache.
733 *
734 * @param ids the collection of ids. If null, the selection is cleared.
735 */
736 public void setSelectedChangesetsById(Collection<Integer> ids) {
737 if (ids == null) {
738 setSelectedChangesets(null);
739 return;
740 }
741 ChangesetCache cc = ChangesetCache.getInstance();
742 Set<Changeset> toSelect = ids.stream()
743 .filter(cc::contains)
744 .map(cc::get)
745 .collect(Collectors.toSet());
746 setSelectedChangesets(toSelect);
747 }
748
749 /**
750 * Selects the given component in the detail tabbed panel
751 * @param clazz the class of the component to select
752 */
753 public void setSelectedComponentInDetailPanel(Class<? extends JComponent> clazz) {
754 for (Component component : pnlChangesetDetailTabs.getComponents()) {
755 if (component.getClass().equals(clazz)) {
756 pnlChangesetDetailTabs.setSelectedComponent(component);
757 break;
758 }
759 }
760 }
761
762 /**
763 * Runs the given changeset download task.
764 * @param task The changeset download task to run
765 */
766 public void runDownloadTask(final AbstractChangesetDownloadTask task) {
767 MainApplication.worker.submit(new PostDownloadHandler(task, task.download()));
768 MainApplication.worker.submit(() -> {
769 if (task.isCanceled() || task.isFailed())
770 return;
771 GuiHelper.runInEDT(() -> setSelectedChangesets(task.getDownloadedData()));
772 });
773 }
774}
Note: See TracBrowser for help on using the repository browser.