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

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

see #15182 - move WindowGeometry from tools to gui.util

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