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

Last change on this file since 5903 was 5903, checked in by stoecker, 11 years ago

fix javadoc

  • Property svn:eol-style set to native
File size: 23.1 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.Container;
8import java.awt.Dimension;
9import java.awt.FlowLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.KeyEvent;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.awt.event.WindowAdapter;
15import java.awt.event.WindowEvent;
16import java.util.Collection;
17import java.util.HashSet;
18import java.util.List;
19import java.util.Set;
20
21import javax.swing.AbstractAction;
22import javax.swing.DefaultListSelectionModel;
23import javax.swing.JComponent;
24import javax.swing.JFrame;
25import javax.swing.JOptionPane;
26import javax.swing.JPanel;
27import javax.swing.JPopupMenu;
28import javax.swing.JScrollPane;
29import javax.swing.JSplitPane;
30import javax.swing.JTabbedPane;
31import javax.swing.JTable;
32import javax.swing.JToolBar;
33import javax.swing.KeyStroke;
34import javax.swing.ListSelectionModel;
35import javax.swing.SwingUtilities;
36import javax.swing.event.ListSelectionEvent;
37import javax.swing.event.ListSelectionListener;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.data.osm.Changeset;
41import org.openstreetmap.josm.data.osm.ChangesetCache;
42import org.openstreetmap.josm.gui.HelpAwareOptionPane;
43import org.openstreetmap.josm.gui.JosmUserIdentityManager;
44import org.openstreetmap.josm.gui.SideButton;
45import org.openstreetmap.josm.gui.dialogs.changeset.query.ChangesetQueryDialog;
46import org.openstreetmap.josm.gui.dialogs.changeset.query.ChangesetQueryTask;
47import org.openstreetmap.josm.gui.help.ContextSensitiveHelpAction;
48import org.openstreetmap.josm.gui.help.HelpUtil;
49import org.openstreetmap.josm.gui.io.CloseChangesetTask;
50import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
51import org.openstreetmap.josm.io.ChangesetQuery;
52import org.openstreetmap.josm.tools.ImageProvider;
53import org.openstreetmap.josm.tools.WindowGeometry;
54
55/**
56 * ChangesetCacheManager manages the local cache of changesets
57 * retrieved from the OSM API. It displays both a table of the locally cached changesets
58 * and detail information about an individual changeset. It also provides actions for
59 * downloading, querying, closing changesets, in addition to removing changesets from
60 * the local cache.
61 *
62 */
63public class ChangesetCacheManager extends JFrame {
64
65 /** the unique instance of the cache manager */
66 private static ChangesetCacheManager instance;
67
68 /**
69 * Replies the unique instance of the changeset cache manager
70 *
71 * @return the unique instance of the changeset cache manager
72 */
73 public static ChangesetCacheManager getInstance() {
74 if (instance == null) {
75 instance = new ChangesetCacheManager();
76 }
77 return instance;
78 }
79
80 /**
81 * Hides and destroys the unique instance of the changeset cache
82 * manager.
83 *
84 */
85 public static void destroyInstance() {
86 if (instance != null) {
87 instance.setVisible(true);
88 instance.dispose();
89 instance = null;
90 }
91 }
92
93 private ChangesetCacheManagerModel model;
94 private JSplitPane spContent;
95 private boolean needsSplitPaneAdjustment;
96
97 private RemoveFromCacheAction actRemoveFromCacheAction;
98 private CloseSelectedChangesetsAction actCloseSelectedChangesetsAction;
99 private DownloadSelectedChangesetsAction actDownloadSelectedChangesets;
100 private DownloadSelectedChangesetContentAction actDownloadSelectedContent;
101 private JTable tblChangesets;
102
103 /**
104 * Creates the various models required
105 */
106 protected void buildModel() {
107 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
108 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
109 model = new ChangesetCacheManagerModel(selectionModel);
110
111 actRemoveFromCacheAction = new RemoveFromCacheAction();
112 actCloseSelectedChangesetsAction = new CloseSelectedChangesetsAction();
113 actDownloadSelectedChangesets = new DownloadSelectedChangesetsAction();
114 actDownloadSelectedContent = new DownloadSelectedChangesetContentAction();
115 }
116
117 /**
118 * builds the toolbar panel in the heading of the dialog
119 *
120 * @return the toolbar panel
121 */
122 protected JPanel buildToolbarPanel() {
123 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
124
125 SideButton btn = new SideButton(new QueryAction());
126 pnl.add(btn);
127 pnl.add(new SingleChangesetDownloadPanel());
128 pnl.add(new SideButton(new DownloadMyChangesets()));
129
130 return pnl;
131 }
132
133 /**
134 * builds the button panel in the footer of the dialog
135 *
136 * @return the button row pane
137 */
138 protected JPanel buildButtonPanel() {
139 JPanel pnl = new JPanel(new FlowLayout(FlowLayout.CENTER));
140
141 //-- cancel and close action
142 pnl.add(new SideButton(new CancelAction()));
143
144 //-- help action
145 pnl.add(new SideButton(
146 new ContextSensitiveHelpAction(
147 HelpUtil.ht("/Dialog/ChangesetCacheManager"))
148 )
149 );
150
151 return pnl;
152 }
153
154 /**
155 * Builds the panel with the changeset details
156 *
157 * @return the panel with the changeset details
158 */
159 protected JPanel buildChangesetDetailPanel() {
160 JPanel pnl = new JPanel(new BorderLayout());
161 JTabbedPane tp = new JTabbedPane();
162
163 // -- add the details panel
164 ChangesetDetailPanel pnlChangesetDetail;
165 tp.add(pnlChangesetDetail = new ChangesetDetailPanel());
166 model.addPropertyChangeListener(pnlChangesetDetail);
167
168 // -- add the tags panel
169 ChangesetTagsPanel pnlChangesetTags = new ChangesetTagsPanel();
170 tp.add(pnlChangesetTags);
171 model.addPropertyChangeListener(pnlChangesetTags);
172
173 // -- add the panel for the changeset content
174 ChangesetContentPanel pnlChangesetContent = new ChangesetContentPanel();
175 tp.add(pnlChangesetContent);
176 model.addPropertyChangeListener(pnlChangesetContent);
177
178 tp.setTitleAt(0, tr("Properties"));
179 tp.setToolTipTextAt(0, tr("Display the basic properties of the changeset"));
180 tp.setTitleAt(1, tr("Tags"));
181 tp.setToolTipTextAt(1, tr("Display the tags of the changeset"));
182 tp.setTitleAt(2, tr("Content"));
183 tp.setToolTipTextAt(2, tr("Display the objects created, updated, and deleted by the changeset"));
184
185 pnl.add(tp, BorderLayout.CENTER);
186 return pnl;
187 }
188
189 /**
190 * builds the content panel of the dialog
191 *
192 * @return the content panel
193 */
194 protected JPanel buildContentPanel() {
195 JPanel pnl = new JPanel(new BorderLayout());
196
197 spContent = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
198 spContent.setLeftComponent(buildChangesetTablePanel());
199 spContent.setRightComponent(buildChangesetDetailPanel());
200 spContent.setOneTouchExpandable(true);
201 spContent.setDividerLocation(0.5);
202
203 pnl.add(spContent, BorderLayout.CENTER);
204 return pnl;
205 }
206
207 /**
208 * Builds the table with actions which can be applied to the currently visible changesets
209 * in the changeset table.
210 *
211 * @return changset actions panel
212 */
213 protected JPanel buildChangesetTableActionPanel() {
214 JPanel pnl = new JPanel(new BorderLayout());
215
216 JToolBar tb = new JToolBar(JToolBar.VERTICAL);
217 tb.setFloatable(false);
218
219 // -- remove from cache action
220 model.getSelectionModel().addListSelectionListener(actRemoveFromCacheAction);
221 tb.add(actRemoveFromCacheAction);
222
223 // -- close selected changesets action
224 model.getSelectionModel().addListSelectionListener(actCloseSelectedChangesetsAction);
225 tb.add(actCloseSelectedChangesetsAction);
226
227 // -- download selected changesets
228 model.getSelectionModel().addListSelectionListener(actDownloadSelectedChangesets);
229 tb.add(actDownloadSelectedChangesets);
230
231 // -- download the content of the selected changesets
232 model.getSelectionModel().addListSelectionListener(actDownloadSelectedContent);
233 tb.add(actDownloadSelectedContent);
234
235 pnl.add(tb, BorderLayout.CENTER);
236 return pnl;
237 }
238
239 /**
240 * Builds the panel with the table of changesets
241 *
242 * @return the panel with the table of changesets
243 */
244 protected JPanel buildChangesetTablePanel() {
245 JPanel pnl = new JPanel(new BorderLayout());
246 tblChangesets = new JTable(
247 model,
248 new ChangesetCacheTableColumnModel(),
249 model.getSelectionModel()
250 );
251 tblChangesets.addMouseListener(new ChangesetTablePopupMenuLauncher());
252 tblChangesets.addMouseListener(new DblClickHandler());
253 tblChangesets.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "showDetails");
254 tblChangesets.getActionMap().put("showDetails", new ShowDetailAction());
255 model.getSelectionModel().addListSelectionListener(new ChangesetDetailViewSynchronizer());
256
257 // activate DEL on the table
258 tblChangesets.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "removeFromCache");
259 tblChangesets.getActionMap().put("removeFromCache", actRemoveFromCacheAction);
260
261 pnl.add(new JScrollPane(tblChangesets), BorderLayout.CENTER);
262 pnl.add(buildChangesetTableActionPanel(), BorderLayout.WEST);
263 return pnl;
264 }
265
266 protected void build() {
267 setTitle(tr("Changeset Management Dialog"));
268 setIconImage(ImageProvider.get("dialogs/changeset", "changesetmanager").getImage());
269 Container cp = getContentPane();
270
271 cp.setLayout(new BorderLayout());
272
273 buildModel();
274 cp.add(buildToolbarPanel(), BorderLayout.NORTH);
275 cp.add(buildContentPanel(), BorderLayout.CENTER);
276 cp.add(buildButtonPanel(), BorderLayout.SOUTH);
277
278 // the help context
279 HelpUtil.setHelpContext(getRootPane(), HelpUtil.ht("/Dialog/ChangesetCacheManager"));
280
281 // make the dialog respond to ESC
282 getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "cancelAndClose");
283 getRootPane().getActionMap().put("cancelAndClose", new CancelAction());
284
285 // install a window event handler
286 addWindowListener(new WindowEventHandler());
287 }
288
289 public ChangesetCacheManager() {
290 build();
291 }
292
293 @Override
294 public void setVisible(boolean visible) {
295 if (visible) {
296 new WindowGeometry(
297 getClass().getName() + ".geometry",
298 WindowGeometry.centerInWindow(
299 getParent(),
300 new Dimension(1000,600)
301 )
302 ).applySafe(this);
303 needsSplitPaneAdjustment = true;
304 model.init();
305
306 } else if (!visible && isShowing()){
307 model.tearDown();
308 new WindowGeometry(this).remember(getClass().getName() + ".geometry");
309 }
310 super.setVisible(visible);
311 }
312
313 /**
314 * Handler for window events
315 *
316 */
317 class WindowEventHandler extends WindowAdapter {
318 @Override
319 public void windowClosing(WindowEvent e) {
320 new CancelAction().cancelAndClose();
321 }
322
323 @Override
324 public void windowActivated(WindowEvent arg0) {
325 if (needsSplitPaneAdjustment) {
326 spContent.setDividerLocation(0.5);
327 needsSplitPaneAdjustment = false;
328 }
329 }
330 }
331
332 /**
333 * the cancel / close action
334 */
335 static class CancelAction extends AbstractAction {
336 public CancelAction() {
337 putValue(NAME, tr("Close"));
338 putValue(SMALL_ICON, ImageProvider.get("cancel"));
339 putValue(SHORT_DESCRIPTION, tr("Close the dialog"));
340 }
341
342 public void cancelAndClose() {
343 destroyInstance();
344 }
345
346 @Override
347 public void actionPerformed(ActionEvent arg0) {
348 cancelAndClose();
349 }
350 }
351
352 /**
353 * The action to query and download changesets
354 */
355 class QueryAction extends AbstractAction {
356 public QueryAction() {
357 putValue(NAME, tr("Query"));
358 putValue(SMALL_ICON, ImageProvider.get("dialogs","search"));
359 putValue(SHORT_DESCRIPTION, tr("Launch the dialog for querying changesets"));
360 }
361
362 @Override
363 public void actionPerformed(ActionEvent evt) {
364 ChangesetQueryDialog dialog = new ChangesetQueryDialog(ChangesetCacheManager.this);
365 dialog.initForUserInput();
366 dialog.setVisible(true);
367 if (dialog.isCanceled())
368 return;
369
370 try {
371 ChangesetQuery query = dialog.getChangesetQuery();
372 if (query == null) return;
373 ChangesetQueryTask task = new ChangesetQueryTask(ChangesetCacheManager.this, query);
374 ChangesetCacheManager.getInstance().runDownloadTask(task);
375 } catch (IllegalStateException e) {
376 JOptionPane.showMessageDialog(ChangesetCacheManager.this, e.getMessage(), tr("Error"), JOptionPane.ERROR_MESSAGE);
377 }
378 }
379 }
380
381 /**
382 * Removes the selected changesets from the local changeset cache
383 *
384 */
385 class RemoveFromCacheAction extends AbstractAction implements ListSelectionListener{
386 public RemoveFromCacheAction() {
387 putValue(NAME, tr("Remove from cache"));
388 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
389 putValue(SHORT_DESCRIPTION, tr("Remove the selected changesets from the local cache"));
390 updateEnabledState();
391 }
392
393 @Override
394 public void actionPerformed(ActionEvent arg0) {
395 List<Changeset> selected = model.getSelectedChangesets();
396 ChangesetCache.getInstance().remove(selected);
397 }
398
399 protected void updateEnabledState() {
400 setEnabled(model.hasSelectedChangesets());
401 }
402
403 @Override
404 public void valueChanged(ListSelectionEvent e) {
405 updateEnabledState();
406
407 }
408 }
409
410 /**
411 * Closes the selected changesets
412 *
413 */
414 class CloseSelectedChangesetsAction extends AbstractAction implements ListSelectionListener{
415 public CloseSelectedChangesetsAction() {
416 putValue(NAME, tr("Close"));
417 putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
418 putValue(SHORT_DESCRIPTION, tr("Close the selected changesets"));
419 updateEnabledState();
420 }
421
422 @Override
423 public void actionPerformed(ActionEvent arg0) {
424 List<Changeset> selected = model.getSelectedChangesets();
425 Main.worker.submit(new CloseChangesetTask(selected));
426 }
427
428 protected void updateEnabledState() {
429 List<Changeset> selected = model.getSelectedChangesets();
430 JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
431 for (Changeset cs: selected) {
432 if (cs.isOpen()) {
433 if (im.isPartiallyIdentified() && cs.getUser() != null && cs.getUser().getName().equals(im.getUserName())) {
434 setEnabled(true);
435 return;
436 }
437 if (im.isFullyIdentified() && cs.getUser() != null && cs.getUser().getId() == im.getUserId()) {
438 setEnabled(true);
439 return;
440 }
441 }
442 }
443 setEnabled(false);
444 }
445
446 @Override
447 public void valueChanged(ListSelectionEvent e) {
448 updateEnabledState();
449 }
450 }
451
452 /**
453 * Downloads the selected changesets
454 *
455 */
456 class DownloadSelectedChangesetsAction extends AbstractAction implements ListSelectionListener{
457 public DownloadSelectedChangesetsAction() {
458 putValue(NAME, tr("Update changeset"));
459 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "updatechangeset"));
460 putValue(SHORT_DESCRIPTION, tr("Updates the selected changesets with current data from the OSM server"));
461 updateEnabledState();
462 }
463
464 @Override
465 public void actionPerformed(ActionEvent arg0) {
466 List<Changeset> selected = model.getSelectedChangesets();
467 ChangesetHeaderDownloadTask task =ChangesetHeaderDownloadTask.buildTaskForChangesets(ChangesetCacheManager.this,selected);
468 ChangesetCacheManager.getInstance().runDownloadTask(task);
469 }
470
471 protected void updateEnabledState() {
472 setEnabled(model.hasSelectedChangesets());
473 }
474
475 @Override
476 public void valueChanged(ListSelectionEvent e) {
477 updateEnabledState();
478 }
479 }
480
481 /**
482 * Downloads the content of selected changesets from the OSM server
483 *
484 */
485 class DownloadSelectedChangesetContentAction extends AbstractAction implements ListSelectionListener{
486 public DownloadSelectedChangesetContentAction() {
487 putValue(NAME, tr("Download changeset content"));
488 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "downloadchangesetcontent"));
489 putValue(SHORT_DESCRIPTION, tr("Download the content of the selected changesets from the server"));
490 updateEnabledState();
491 }
492
493 @Override
494 public void actionPerformed(ActionEvent arg0) {
495 ChangesetContentDownloadTask task = new ChangesetContentDownloadTask(ChangesetCacheManager.this,model.getSelectedChangesetIds());
496 ChangesetCacheManager.getInstance().runDownloadTask(task);
497 }
498
499 protected void updateEnabledState() {
500 setEnabled(model.hasSelectedChangesets());
501 }
502
503 @Override
504 public void valueChanged(ListSelectionEvent e) {
505 updateEnabledState();
506 }
507 }
508
509 class ShowDetailAction extends AbstractAction {
510
511 public void showDetails() {
512 List<Changeset> selected = model.getSelectedChangesets();
513 if (selected.size() != 1) return;
514 model.setChangesetInDetailView(selected.get(0));
515 }
516
517 @Override
518 public void actionPerformed(ActionEvent arg0) {
519 showDetails();
520 }
521 }
522
523 class DownloadMyChangesets extends AbstractAction {
524 public DownloadMyChangesets() {
525 putValue(NAME, tr("My changesets"));
526 putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "downloadchangeset"));
527 putValue(SHORT_DESCRIPTION, tr("Download my changesets from the OSM server (max. 100 changesets)"));
528 }
529
530 protected void alertAnonymousUser() {
531 HelpAwareOptionPane.showOptionDialog(
532 ChangesetCacheManager.this,
533 tr("<html>JOSM is currently running with an anonymous user. It cannot download<br>"
534 + "your changesets from the OSM server unless you enter your OSM user name<br>"
535 + "in the JOSM preferences.</html>"
536 ),
537 tr("Warning"),
538 JOptionPane.WARNING_MESSAGE,
539 HelpUtil.ht("/Dialog/ChangesetCacheManager#CanDownloadMyChangesets")
540 );
541 }
542
543 @Override
544 public void actionPerformed(ActionEvent arg0) {
545 JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
546 if (im.isAnonymous()) {
547 alertAnonymousUser();
548 return;
549 }
550 ChangesetQuery query = new ChangesetQuery();
551 if (im.isFullyIdentified()) {
552 query = query.forUser(im.getUserId());
553 } else {
554 query = query.forUser(im.getUserName());
555 }
556 ChangesetQueryTask task = new ChangesetQueryTask(ChangesetCacheManager.this, query);
557 ChangesetCacheManager.getInstance().runDownloadTask(task);
558 }
559 }
560
561 class DblClickHandler extends MouseAdapter {
562 @Override
563 public void mouseClicked(MouseEvent evt) {
564 if (! SwingUtilities.isLeftMouseButton(evt) || evt.getClickCount()<2)
565 return;
566 new ShowDetailAction().showDetails();
567 }
568 }
569
570 class ChangesetTablePopupMenuLauncher extends PopupMenuLauncher {
571 ChangesetTablePopupMenu menu = new ChangesetTablePopupMenu();
572 @Override
573 public void launch(MouseEvent evt) {
574 if (! model.hasSelectedChangesets()) {
575 int row = tblChangesets.rowAtPoint(evt.getPoint());
576 if (row >= 0) {
577 model.setSelectedByIdx(row);
578 }
579 }
580 menu.show(tblChangesets, evt.getPoint().x, evt.getPoint().y);
581 }
582 }
583
584 class ChangesetTablePopupMenu extends JPopupMenu {
585 public ChangesetTablePopupMenu() {
586 add(actRemoveFromCacheAction);
587 add(actCloseSelectedChangesetsAction);
588 add(actDownloadSelectedChangesets);
589 add(actDownloadSelectedContent);
590 }
591 }
592
593 class ChangesetDetailViewSynchronizer implements ListSelectionListener {
594 @Override
595 public void valueChanged(ListSelectionEvent e) {
596 List<Changeset> selected = model.getSelectedChangesets();
597 if (selected.size() == 1) {
598 model.setChangesetInDetailView(selected.get(0));
599 } else {
600 model.setChangesetInDetailView(null);
601 }
602 }
603 }
604
605 /**
606 * Selects the changesets in <code>changests</code>, provided the
607 * respective changesets are already present in the local changeset cache.
608 *
609 * @param changesets the collection of changesets. If {@code null}, the
610 * selection is cleared.
611 */
612 public void setSelectedChangesets(Collection<Changeset> changesets) {
613 model.setSelectedChangesets(changesets);
614 int idx = model.getSelectionModel().getMinSelectionIndex();
615 if (idx < 0) return;
616 tblChangesets.scrollRectToVisible(tblChangesets.getCellRect(idx, 0, true));
617 repaint();
618 }
619
620 /**
621 * Selects the changesets with the ids in <code>ids</code>, provided the
622 * respective changesets are already present in the local changeset cache.
623 *
624 * @param ids the collection of ids. If null, the selection is cleared.
625 */
626 public void setSelectedChangesetsById(Collection<Integer> ids) {
627 if (ids == null) {
628 setSelectedChangesets(null);
629 return;
630 }
631 Set<Changeset> toSelect = new HashSet<Changeset>();
632 ChangesetCache cc = ChangesetCache.getInstance();
633 for (int id: ids) {
634 if (cc.contains(id)) {
635 toSelect.add(cc.get(id));
636 }
637 }
638 setSelectedChangesets(toSelect);
639 }
640
641 public void runDownloadTask(final ChangesetDownloadTask task) {
642 Main.worker.submit(task);
643 Runnable r = new Runnable() {
644 @Override public void run() {
645 if (task.isCanceled() || task.isFailed()) return;
646 setSelectedChangesets(task.getDownloadedChangesets());
647 }
648 };
649 Main.worker.submit(r);
650 }
651}
Note: See TracBrowser for help on using the repository browser.