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

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

fix #18913 - Changeset Manager: improve i18n strings (patch by Hb---)

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