source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ChangesetDialog.java @ 5241

Revision 4459, 21.1 KB checked in by bastiK, 8 months ago (diff)

applied #6856 - Enable plugins to add actions in popup menus (patch by Don-vip)

  • Property svn:eol-style set to native
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.FlowLayout;
8import java.awt.Frame;
9import java.awt.event.ActionEvent;
10import java.awt.event.ItemEvent;
11import java.awt.event.ItemListener;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Set;
19import java.util.concurrent.ExecutionException;
20import java.util.concurrent.Future;
21
22import javax.swing.AbstractAction;
23import javax.swing.Action;
24import javax.swing.DefaultListSelectionModel;
25import javax.swing.JCheckBox;
26import javax.swing.JList;
27import javax.swing.JMenuItem;
28import javax.swing.JPanel;
29import javax.swing.JScrollPane;
30import javax.swing.ListSelectionModel;
31import javax.swing.SwingUtilities;
32import javax.swing.event.ListSelectionEvent;
33import javax.swing.event.ListSelectionListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.actions.AbstractInfoAction;
37import org.openstreetmap.josm.data.osm.Changeset;
38import org.openstreetmap.josm.data.osm.ChangesetCache;
39import org.openstreetmap.josm.data.osm.DataSet;
40import org.openstreetmap.josm.data.osm.OsmPrimitive;
41import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
42import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
43import org.openstreetmap.josm.gui.MapFrame;
44import org.openstreetmap.josm.gui.MapView;
45import org.openstreetmap.josm.gui.SideButton;
46import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetCacheManager;
47import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetHeaderDownloadTask;
48import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetInSelectionListModel;
49import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListCellRenderer;
50import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetListModel;
51import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetsInActiveDataLayerListModel;
52import org.openstreetmap.josm.gui.help.HelpUtil;
53import org.openstreetmap.josm.gui.io.CloseChangesetTask;
54import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
55import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
56import org.openstreetmap.josm.tools.BugReportExceptionHandler;
57import org.openstreetmap.josm.tools.ImageProvider;
58import org.openstreetmap.josm.tools.OpenBrowser;
59
60/**
61 * ChangesetDialog is a toggle dialog which displays the current list of changesets.
62 * It either displays
63 * <ul>
64 *   <li>the list of changesets the currently selected objects are assigned to</li>
65 *   <li>the list of changesets objects in the current data layer are assigend to</li>
66 * </ul>
67 *
68 * The dialog offers actions to download and to close changesets. It can also launch an external
69 * browser with information about a changeset. Furthermore, it can select all objects in
70 * the current data layer being assigned to a specific changeset.
71 *
72 */
73public class ChangesetDialog extends ToggleDialog{
74    private ChangesetInSelectionListModel inSelectionModel;
75    private ChangesetsInActiveDataLayerListModel inActiveDataLayerModel;
76    private JList lstInSelection;
77    private JList lstInActiveDataLayer;
78    private JCheckBox cbInSelectionOnly;
79    private JPanel pnlList;
80
81    // the actions
82    private SelectObjectsAction selectObjectsAction;
83    private  ReadChangesetsAction readChangesetAction;
84    private ShowChangesetInfoAction showChangesetInfoAction;
85    private CloseOpenChangesetsAction closeChangesetAction;
86    private LaunchChangesetManagerAction launchChangesetManagerAction;
87   
88    private ChangesetDialogPopup popupMenu;
89
90    protected void buildChangesetsLists() {
91        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
92        inSelectionModel = new ChangesetInSelectionListModel(selectionModel);
93
94        lstInSelection = new JList(inSelectionModel);
95        lstInSelection.setSelectionModel(selectionModel);
96        lstInSelection.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
97        lstInSelection.setCellRenderer(new ChangesetListCellRenderer());
98
99        selectionModel = new DefaultListSelectionModel();
100        inActiveDataLayerModel = new ChangesetsInActiveDataLayerListModel(selectionModel);
101        lstInActiveDataLayer = new JList(inActiveDataLayerModel);
102        lstInActiveDataLayer.setSelectionModel(selectionModel);
103        lstInActiveDataLayer.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
104        lstInActiveDataLayer.setCellRenderer(new ChangesetListCellRenderer());
105
106        DblClickHandler dblClickHandler = new DblClickHandler();
107        lstInSelection.addMouseListener(dblClickHandler);
108        lstInActiveDataLayer.addMouseListener(dblClickHandler);
109
110        ChangesetPopupMenuLauncher popupMenuLauncher = new ChangesetPopupMenuLauncher();
111        lstInSelection.addMouseListener(popupMenuLauncher);
112        lstInActiveDataLayer.addMouseListener(popupMenuLauncher);
113    }
114
115    protected void registerAsListener() {
116        // let the model for changesets in the current selection listen to various
117        // events
118        ChangesetCache.getInstance().addChangesetCacheListener(inSelectionModel);
119        MapView.addEditLayerChangeListener(inSelectionModel);
120        DataSet.addSelectionListener(inSelectionModel);
121
122        // let the model for changesets in the current layer listen to various
123        // events and bootstrap it's content
124        ChangesetCache.getInstance().addChangesetCacheListener(inActiveDataLayerModel);
125        MapView.addEditLayerChangeListener(inActiveDataLayerModel);
126        if (Main.main.getEditLayer() != null) {
127            Main.main.getEditLayer().data.addDataSetListener(inActiveDataLayerModel);
128            inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
129            inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getSelected());
130        }
131    }
132
133    protected void unregisterAsListener() {
134        // remove the list model for the current edit layer as listener
135        //
136        ChangesetCache.getInstance().removeChangesetCacheListener(inActiveDataLayerModel);
137        MapView.removeEditLayerChangeListener(inActiveDataLayerModel);
138        if (Main.main.getEditLayer() != null) {
139            Main.main.getEditLayer().data.removeDataSetListener(inActiveDataLayerModel);
140        }
141
142        // remove the list model for the changesets in the current selection as
143        // listener
144        //
145        MapView.removeEditLayerChangeListener(inSelectionModel);
146        DataSet.removeSelectionListener(inSelectionModel);
147    }
148
149    @Override
150    public void showNotify() {
151        registerAsListener();
152        DatasetEventManager.getInstance().addDatasetListener(inActiveDataLayerModel, FireMode.IN_EDT);
153    }
154
155    @Override
156    public void hideNotify() {
157        unregisterAsListener();
158        DatasetEventManager.getInstance().removeDatasetListener(inActiveDataLayerModel);
159    }
160
161    protected JPanel buildFilterPanel() {
162        JPanel pnl = new JPanel(new FlowLayout(FlowLayout.LEFT));
163        pnl.setBorder(null);
164        pnl.add(cbInSelectionOnly = new JCheckBox(tr("For selected objects only")));
165        cbInSelectionOnly.setToolTipText(tr("<html>Select to show changesets for the currently selected objects only.<br>"
166                + "Unselect to show all changesets for objects in the current data layer.</html>"));
167        cbInSelectionOnly.setSelected(Main.pref.getBoolean("changeset-dialog.for-selected-objects-only", false));
168        return pnl;
169    }
170
171    protected JPanel buildListPanel() {
172        buildChangesetsLists();
173        JPanel pnl = new JPanel(new BorderLayout());
174        if (cbInSelectionOnly.isSelected()) {
175            pnl.add(new JScrollPane(lstInSelection));
176        } else {
177            pnl.add(new JScrollPane(lstInActiveDataLayer));
178        }
179        return pnl;
180    }
181
182    protected void build() {
183        JPanel pnl = new JPanel(new BorderLayout());
184        pnl.add(buildFilterPanel(), BorderLayout.NORTH);
185        pnl.add(pnlList = buildListPanel(), BorderLayout.CENTER);
186
187        cbInSelectionOnly.addItemListener(new FilterChangeHandler());
188
189        HelpUtil.setHelpContext(pnl, HelpUtil.ht("/Dialog/ChangesetListDialog"));
190
191        // -- select objects action
192        selectObjectsAction = new SelectObjectsAction();
193        cbInSelectionOnly.addItemListener(selectObjectsAction);
194
195        // -- read changesets action
196        readChangesetAction = new ReadChangesetsAction();
197        cbInSelectionOnly.addItemListener(readChangesetAction);
198
199        // -- close changesets action
200        closeChangesetAction = new CloseOpenChangesetsAction();
201        cbInSelectionOnly.addItemListener(closeChangesetAction);
202
203        // -- show info action
204        showChangesetInfoAction = new ShowChangesetInfoAction();
205        cbInSelectionOnly.addItemListener(showChangesetInfoAction);
206
207        // -- launch changeset manager action
208        launchChangesetManagerAction = new LaunchChangesetManagerAction();
209        cbInSelectionOnly.addItemListener(launchChangesetManagerAction);
210       
211        popupMenu = new ChangesetDialogPopup(lstInActiveDataLayer, lstInSelection);
212
213        createLayout(pnl, false, Arrays.asList(new SideButton[] {
214            new SideButton(selectObjectsAction, false),
215            new SideButton(readChangesetAction, false),
216            new SideButton(closeChangesetAction, false),
217            new SideButton(showChangesetInfoAction, false),
218            new SideButton(launchChangesetManagerAction, false)
219        }));
220    }
221
222    protected JList getCurrentChangesetList() {
223        if (cbInSelectionOnly.isSelected())
224            return lstInSelection;
225        return lstInActiveDataLayer;
226    }
227
228    protected ChangesetListModel getCurrentChangesetListModel() {
229        if (cbInSelectionOnly.isSelected())
230            return inSelectionModel;
231        return inActiveDataLayerModel;
232    }
233
234    protected void initWithCurrentData() {
235        if (Main.main.getEditLayer() != null) {
236            inSelectionModel.initFromPrimitives(Main.main.getEditLayer().data.getSelected());
237            inActiveDataLayerModel.initFromDataSet(Main.main.getEditLayer().data);
238        }
239    }
240
241    public ChangesetDialog(MapFrame mapFrame) {
242        super(
243                tr("Changesets"),
244                "changesetdialog",
245                tr("Open the list of changesets in the current layer."),
246                null, /* no keyboard shortcut */
247                200, /* the preferred height */
248                false /* don't show if there is no preference */
249        );
250        build();
251        initWithCurrentData();
252    }
253
254    class DblClickHandler extends MouseAdapter {
255        @Override
256        public void mouseClicked(MouseEvent e) {
257            if (!SwingUtilities.isLeftMouseButton(e) || e.getClickCount() < 2)
258                return;
259            Set<Integer> sel = getCurrentChangesetListModel().getSelectedChangesetIds();
260            if (sel.isEmpty())
261                return;
262            if (Main.main.getCurrentDataSet() == null)
263                return;
264            new SelectObjectsAction().selectObjectsByChangesetIds(Main.main.getCurrentDataSet(), sel);
265        }
266
267    }
268
269    class FilterChangeHandler implements ItemListener {
270        public void itemStateChanged(ItemEvent e) {
271            Main.pref.put("changeset-dialog.for-selected-objects-only", cbInSelectionOnly.isSelected());
272            pnlList.removeAll();
273            if (cbInSelectionOnly.isSelected()) {
274                pnlList.add(new JScrollPane(lstInSelection), BorderLayout.CENTER);
275            } else {
276                pnlList.add(new JScrollPane(lstInActiveDataLayer), BorderLayout.CENTER);
277            }
278            validate();
279            repaint();
280        }
281    }
282
283    /**
284     * Selects objects for the currently selected changesets.
285     */
286    class SelectObjectsAction extends AbstractAction implements ListSelectionListener, ItemListener{
287
288        public SelectObjectsAction() {
289            putValue(NAME, tr("Select"));
290            putValue(SHORT_DESCRIPTION, tr("Select all objects assigned to the currently selected changesets"));
291            putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
292            updateEnabledState();
293        }
294
295        public void selectObjectsByChangesetIds(DataSet ds, Set<Integer> ids) {
296            if (ds == null || ids == null)
297                return;
298            Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
299            for (OsmPrimitive p: ds.allPrimitives()) {
300                if (ids.contains(p.getChangesetId())) {
301                    sel.add(p);
302                }
303            }
304            ds.setSelected(sel);
305        }
306
307        public void actionPerformed(ActionEvent e) {
308            if (Main.main.getEditLayer() == null)
309                return;
310            ChangesetListModel model = getCurrentChangesetListModel();
311            Set<Integer> sel = model.getSelectedChangesetIds();
312            if (sel.isEmpty())
313                return;
314
315            DataSet ds = Main.main.getEditLayer().data;
316            selectObjectsByChangesetIds(ds,sel);
317        }
318
319        protected void updateEnabledState() {
320            setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
321        }
322
323        public void itemStateChanged(ItemEvent arg0) {
324            updateEnabledState();
325
326        }
327
328        public void valueChanged(ListSelectionEvent e) {
329            updateEnabledState();
330        }
331    }
332
333    /**
334     * Downloads selected changesets
335     *
336     */
337    class ReadChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener{
338        public ReadChangesetsAction() {
339            putValue(NAME, tr("Download"));
340            putValue(SHORT_DESCRIPTION, tr("Download information about the selected changesets from the OSM server"));
341            putValue(SMALL_ICON, ImageProvider.get("download"));
342            updateEnabledState();
343        }
344
345        public void actionPerformed(ActionEvent arg0) {
346            ChangesetListModel model = getCurrentChangesetListModel();
347            Set<Integer> sel = model.getSelectedChangesetIds();
348            if (sel.isEmpty())
349                return;
350            ChangesetHeaderDownloadTask task = new ChangesetHeaderDownloadTask(sel);
351            Main.worker.submit(task);
352        }
353
354        protected void updateEnabledState() {
355            setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
356        }
357
358        public void itemStateChanged(ItemEvent arg0) {
359            updateEnabledState();
360
361        }
362
363        public void valueChanged(ListSelectionEvent e) {
364            updateEnabledState();
365        }
366    }
367
368    /**
369     * Closes the currently selected changesets
370     *
371     */
372    class CloseOpenChangesetsAction extends AbstractAction implements ListSelectionListener, ItemListener {
373        public CloseOpenChangesetsAction() {
374            putValue(NAME, tr("Close open changesets"));
375            putValue(SHORT_DESCRIPTION, tr("Closes the selected open changesets"));
376            putValue(SMALL_ICON, ImageProvider.get("closechangeset"));
377            updateEnabledState();
378        }
379
380        public void actionPerformed(ActionEvent arg0) {
381            List<Changeset> sel = getCurrentChangesetListModel().getSelectedOpenChangesets();
382            if (sel.isEmpty())
383                return;
384            Main.worker.submit(new CloseChangesetTask(sel));
385        }
386
387        protected void updateEnabledState() {
388            setEnabled(getCurrentChangesetListModel().hasSelectedOpenChangesets());
389        }
390
391        public void itemStateChanged(ItemEvent arg0) {
392            updateEnabledState();
393        }
394
395        public void valueChanged(ListSelectionEvent e) {
396            updateEnabledState();
397        }
398    }
399
400    /**
401     * Show information about the currently selected changesets
402     *
403     */
404    class ShowChangesetInfoAction extends AbstractAction implements ListSelectionListener, ItemListener {
405        public ShowChangesetInfoAction() {
406            putValue(NAME, tr("Show info"));
407            putValue(SHORT_DESCRIPTION, tr("Open a web page for each selected changeset"));
408            putValue(SMALL_ICON, ImageProvider.get("about"));
409            updateEnabledState();
410        }
411
412        public void actionPerformed(ActionEvent arg0) {
413            Set<Changeset> sel = getCurrentChangesetListModel().getSelectedChangesets();
414            if (sel.isEmpty())
415                return;
416            if (sel.size() > 10 && ! AbstractInfoAction.confirmLaunchMultiple(sel.size()))
417                return;
418            String baseUrl = AbstractInfoAction.getBaseBrowseUrl();
419            for (Changeset cs: sel) {
420                String url = baseUrl + "/changeset/" + cs.getId();
421                OpenBrowser.displayUrl(
422                        url
423                );
424            }
425        }
426
427        protected void updateEnabledState() {
428            setEnabled(getCurrentChangesetList().getSelectedIndices().length > 0);
429        }
430
431        public void itemStateChanged(ItemEvent arg0) {
432            updateEnabledState();
433        }
434
435        public void valueChanged(ListSelectionEvent e) {
436            updateEnabledState();
437        }
438    }
439
440    /**
441     * Show information about the currently selected changesets
442     *
443     */
444    class LaunchChangesetManagerAction extends AbstractAction implements ListSelectionListener, ItemListener {
445        public LaunchChangesetManagerAction() {
446            putValue(NAME, tr("Details"));
447            putValue(SHORT_DESCRIPTION, tr("Opens the Changeset Manager window for the selected changesets"));
448            putValue(SMALL_ICON, ImageProvider.get("dialogs/changeset", "changesetmanager"));
449        }
450
451        protected void launchChangesetManager(Collection<Integer> toSelect) {
452            ChangesetCacheManager cm = ChangesetCacheManager.getInstance();
453            if (cm.isVisible()) {
454                cm.setExtendedState(Frame.NORMAL);
455                cm.toFront();
456                cm.requestFocus();
457            } else {
458                cm.setVisible(true);
459                cm.toFront();
460                cm.requestFocus();
461            }
462            cm.setSelectedChangesetsById(toSelect);
463        }
464
465        public void actionPerformed(ActionEvent arg0) {
466            ChangesetListModel model = getCurrentChangesetListModel();
467            Set<Integer> sel = model.getSelectedChangesetIds();
468            final Set<Integer> toDownload = new HashSet<Integer>();
469            ChangesetCache cc = ChangesetCache.getInstance();
470            for (int id: sel) {
471                if (!cc.contains(id)) {
472                    toDownload.add(id);
473                }
474            }
475
476            final ChangesetHeaderDownloadTask task;
477            final Future<?> future;
478            if (toDownload.isEmpty()) {
479                task = null;
480                future = null;
481            } else {
482                task = new ChangesetHeaderDownloadTask(toDownload);
483                future = Main.worker.submit(task);
484            }
485
486            Runnable r = new Runnable() {
487                public void run() {
488                    // first, wait for the download task to finish, if a download
489                    // task was launched
490                    if (future != null) {
491                        try {
492                            future.get();
493                        } catch(InterruptedException e) {
494                            e.printStackTrace();
495                        } catch(ExecutionException e){
496                            e.printStackTrace();
497                            BugReportExceptionHandler.handleException(e.getCause());
498                            return;
499                        }
500                    }
501                    if (task != null) {
502                        if (task.isCanceled())
503                            // don't launch the changeset manager if the download task
504                            // was canceled
505                            return;
506                        if (task.isFailed()) {
507                            toDownload.clear();
508                        }
509                    }
510                    // launch the task
511                    launchChangesetManager(toDownload);
512                }
513            };
514            Main.worker.submit(r);
515        }
516
517        public void itemStateChanged(ItemEvent arg0) {
518        }
519
520        public void valueChanged(ListSelectionEvent e) {
521        }
522    }
523
524    class ChangesetPopupMenuLauncher extends PopupMenuLauncher {
525        @Override
526        public void launch(MouseEvent evt) {
527            JList lst = getCurrentChangesetList();
528            if (lst.getSelectedIndices().length == 0) {
529                int idx = lst.locationToIndex(evt.getPoint());
530                if (idx >=0) {
531                    lst.getSelectionModel().addSelectionInterval(idx, idx);
532                }
533            }
534            popupMenu.show(lst, evt.getX(), evt.getY());
535        }
536    }
537
538    class ChangesetDialogPopup extends ListPopupMenu {
539        public ChangesetDialogPopup(JList ... lists) {
540            super(lists);
541            add(selectObjectsAction);
542            addSeparator();
543            add(readChangesetAction);
544            add(closeChangesetAction);
545            addSeparator();
546            add(showChangesetInfoAction);
547        }
548    }
549
550    public void addPopupMenuSeparator() {
551        popupMenu.addSeparator();
552    }
553
554    public JMenuItem addPopupMenuAction(Action a) {
555        return popupMenu.add(a);
556    }
557}
Note: See TracBrowser for help on using the repository browser.