source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/RelationListDialog.java @ 13434

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

see #8039, see #10456 - support read-only data layers

  • Property svn:eol-style set to native
File size: 28.9 KB
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.Component;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.awt.event.MouseEvent;
11import java.util.ArrayList;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.EnumSet;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Set;
19
20import javax.swing.AbstractAction;
21import javax.swing.AbstractListModel;
22import javax.swing.DefaultListSelectionModel;
23import javax.swing.FocusManager;
24import javax.swing.JComponent;
25import javax.swing.JList;
26import javax.swing.JMenuItem;
27import javax.swing.JPanel;
28import javax.swing.JPopupMenu;
29import javax.swing.JScrollPane;
30import javax.swing.KeyStroke;
31import javax.swing.ListSelectionModel;
32import javax.swing.event.PopupMenuEvent;
33import javax.swing.event.PopupMenuListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.actions.ExpertToggleAction;
37import org.openstreetmap.josm.actions.OsmPrimitiveAction;
38import org.openstreetmap.josm.actions.relation.AddSelectionToRelations;
39import org.openstreetmap.josm.actions.relation.DeleteRelationsAction;
40import org.openstreetmap.josm.actions.relation.DownloadMembersAction;
41import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
42import org.openstreetmap.josm.actions.relation.DuplicateRelationAction;
43import org.openstreetmap.josm.actions.relation.EditRelationAction;
44import org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction;
45import org.openstreetmap.josm.actions.relation.ExportRelationToGpxAction.Mode;
46import org.openstreetmap.josm.actions.relation.RecentRelationsAction;
47import org.openstreetmap.josm.actions.relation.SelectMembersAction;
48import org.openstreetmap.josm.actions.relation.SelectRelationAction;
49import org.openstreetmap.josm.data.osm.DataSet;
50import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
51import org.openstreetmap.josm.data.osm.OsmPrimitive;
52import org.openstreetmap.josm.data.osm.Relation;
53import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
54import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
55import org.openstreetmap.josm.data.osm.event.DataSetListener;
56import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
57import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
58import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
59import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
60import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
61import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
62import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
63import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
64import org.openstreetmap.josm.data.osm.search.SearchCompiler;
65import org.openstreetmap.josm.gui.MainApplication;
66import org.openstreetmap.josm.gui.MapView;
67import org.openstreetmap.josm.gui.NavigatableComponent;
68import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
69import org.openstreetmap.josm.gui.PopupMenuHandler;
70import org.openstreetmap.josm.gui.SideButton;
71import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
72import org.openstreetmap.josm.gui.layer.Layer;
73import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
74import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
75import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
76import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
77import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
78import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
79import org.openstreetmap.josm.gui.layer.OsmDataLayer;
80import org.openstreetmap.josm.gui.util.HighlightHelper;
81import org.openstreetmap.josm.gui.widgets.CompileSearchTextDecorator;
82import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
83import org.openstreetmap.josm.gui.widgets.JosmTextField;
84import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
85import org.openstreetmap.josm.spi.preferences.Config;
86import org.openstreetmap.josm.tools.ImageProvider;
87import org.openstreetmap.josm.tools.InputMapUtils;
88import org.openstreetmap.josm.tools.Shortcut;
89import org.openstreetmap.josm.tools.SubclassFilteredCollection;
90
91/**
92 * A dialog showing all known relations, with buttons to add, edit, and delete them.
93 *
94 * We don't have such dialogs for nodes, segments, and ways, because those
95 * objects are visible on the map and can be selected there. Relations are not.
96 */
97public class RelationListDialog extends ToggleDialog
98        implements DataSetListener, NavigatableComponent.ZoomChangeListener, ExpertToggleAction.ExpertModeChangeListener {
99    /** The display list. */
100    private final JList<Relation> displaylist;
101    /** the list model used */
102    private final RelationListModel model;
103
104    private final NewAction newAction;
105
106    /** the popup menu and its handler */
107    private final JPopupMenu popupMenu = new JPopupMenu();
108    private final transient PopupMenuHandler popupMenuHandler = new PopupMenuHandler(popupMenu);
109
110    private final JosmTextField filter;
111
112    // Actions
113    /** the edit action */
114    private final EditRelationAction editAction = new EditRelationAction();
115    /** the delete action */
116    private final DeleteRelationsAction deleteRelationsAction = new DeleteRelationsAction();
117    /** the duplicate action */
118    private final DuplicateRelationAction duplicateAction = new DuplicateRelationAction();
119    private final DownloadMembersAction downloadMembersAction = new DownloadMembersAction();
120    private final DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction =
121            new DownloadSelectedIncompleteMembersAction();
122    private final SelectMembersAction selectMembersAction = new SelectMembersAction(false);
123    private final SelectMembersAction addMembersToSelectionAction = new SelectMembersAction(true);
124    private final SelectRelationAction selectRelationAction = new SelectRelationAction(false);
125    private final SelectRelationAction addRelationToSelectionAction = new SelectRelationAction(true);
126    /** add all selected primitives to the given relations */
127    private final AddSelectionToRelations addSelectionToRelations = new AddSelectionToRelations();
128    private transient JMenuItem addSelectionToRelationMenuItem;
129
130    /** export relation to GPX track action */
131    private final ExportRelationToGpxAction exportRelationFromFirstAction =
132            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_FIRST_MEMBER, Mode.TO_FILE));
133    private final ExportRelationToGpxAction exportRelationFromLastAction =
134            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_LAST_MEMBER, Mode.TO_FILE));
135    private final ExportRelationToGpxAction exportRelationFromFirstToLayerAction =
136            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_FIRST_MEMBER, Mode.TO_LAYER));
137    private final ExportRelationToGpxAction exportRelationFromLastToLayerAction =
138            new ExportRelationToGpxAction(EnumSet.of(Mode.FROM_LAST_MEMBER, Mode.TO_LAYER));
139
140    private final transient HighlightHelper highlightHelper = new HighlightHelper();
141    private final boolean highlightEnabled = Config.getPref().getBoolean("draw.target-highlight", true);
142    private final transient RecentRelationsAction recentRelationsAction;
143
144    /**
145     * Constructs <code>RelationListDialog</code>
146     */
147    public RelationListDialog() {
148        super(tr("Relations"), "relationlist", tr("Open a list of all relations."),
149                Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")),
150                KeyEvent.VK_R, Shortcut.ALT_SHIFT), 150, true);
151
152        // create the list of relations
153        //
154        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
155        model = new RelationListModel(selectionModel);
156        displaylist = new JList<>(model);
157        displaylist.setSelectionModel(selectionModel);
158        displaylist.setCellRenderer(new NoTooltipOsmRenderer());
159        displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
160        displaylist.addMouseListener(new MouseEventHandler());
161
162        // the new action
163        //
164        newAction = new NewAction();
165
166        filter = setupFilter();
167
168        displaylist.addListSelectionListener(e -> {
169            if (!e.getValueIsAdjusting()) updateActionsRelationLists();
170        });
171
172        // Setup popup menu handler
173        setupPopupMenuHandler();
174
175        JPanel pane = new JPanel(new BorderLayout());
176        pane.add(filter, BorderLayout.NORTH);
177        pane.add(new JScrollPane(displaylist), BorderLayout.CENTER);
178
179        SideButton editButton = new SideButton(editAction, false);
180        recentRelationsAction = new RecentRelationsAction(editButton);
181
182        createLayout(pane, false, Arrays.asList(
183                new SideButton(newAction, false),
184                editButton,
185                new SideButton(duplicateAction, false),
186                new SideButton(deleteRelationsAction, false),
187                new SideButton(selectRelationAction, false)
188        ));
189
190        InputMapUtils.unassignCtrlShiftUpDown(displaylist, JComponent.WHEN_FOCUSED);
191
192        // Select relation on Enter
193        InputMapUtils.addEnterAction(displaylist, selectRelationAction);
194
195        // Edit relation on Ctrl-Enter
196        displaylist.getActionMap().put("edit", editAction);
197        displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK), "edit");
198
199        // Do not hide copy action because of default JList override (fix #9815)
200        displaylist.getActionMap().put("copy", MainApplication.getMenu().copy);
201        displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, Main.platform.getMenuShortcutKeyMaskEx()), "copy");
202
203        updateActionsRelationLists();
204    }
205
206    @Override
207    public void destroy() {
208        recentRelationsAction.destroy();
209        model.clear();
210        super.destroy();
211    }
212
213    /**
214     * Enable the "recent relations" dropdown menu next to edit button.
215     */
216    public void enableRecentRelations() {
217        recentRelationsAction.enableArrow();
218    }
219
220    // inform all actions about list of relations they need
221    private void updateActionsRelationLists() {
222        List<Relation> sel = model.getSelectedRelations();
223        popupMenuHandler.setPrimitives(sel);
224
225        Component focused = FocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
226
227        //update highlights
228        if (highlightEnabled && focused == displaylist && MainApplication.isDisplayingMapView() && highlightHelper.highlightOnly(sel)) {
229            MainApplication.getMap().mapView.repaint();
230        }
231    }
232
233    @Override
234    public void showNotify() {
235        MainApplication.getLayerManager().addLayerChangeListener(newAction);
236        MainApplication.getLayerManager().addActiveLayerChangeListener(newAction);
237        MapView.addZoomChangeListener(this);
238        newAction.updateEnabledState();
239        DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT);
240        DataSet.addSelectionListener(addSelectionToRelations);
241        dataChanged(null);
242        ExpertToggleAction.addExpertModeChangeListener(this);
243        expertChanged(ExpertToggleAction.isExpert());
244    }
245
246    @Override
247    public void hideNotify() {
248        MainApplication.getLayerManager().removeActiveLayerChangeListener(newAction);
249        MainApplication.getLayerManager().removeLayerChangeListener(newAction);
250        MapView.removeZoomChangeListener(this);
251        DatasetEventManager.getInstance().removeDatasetListener(this);
252        DataSet.removeSelectionListener(addSelectionToRelations);
253        ExpertToggleAction.removeExpertModeChangeListener(this);
254    }
255
256    private void resetFilter() {
257        filter.setText(null);
258    }
259
260    /**
261     * Initializes the relation list dialog from a layer. If <code>layer</code> is null
262     * or if it isn't an {@link OsmDataLayer} the dialog is reset to an empty dialog.
263     * Otherwise it is initialized with the list of non-deleted and visible relations
264     * in the layer's dataset.
265     *
266     * @param layer the layer. May be null.
267     */
268    protected void initFromLayer(Layer layer) {
269        if (!(layer instanceof OsmDataLayer)) {
270            model.setRelations(null);
271            return;
272        }
273        OsmDataLayer l = (OsmDataLayer) layer;
274        model.setRelations(l.data.getRelations());
275        model.updateTitle();
276        updateActionsRelationLists();
277    }
278
279    /**
280     * @return The selected relation in the list
281     */
282    private Relation getSelected() {
283        if (model.getSize() == 1) {
284            displaylist.setSelectedIndex(0);
285        }
286        return displaylist.getSelectedValue();
287    }
288
289    /**
290     * Selects the relation <code>relation</code> in the list of relations.
291     *
292     * @param relation  the relation
293     */
294    public void selectRelation(Relation relation) {
295        selectRelations(Collections.singleton(relation));
296    }
297
298    /**
299     * Selects the relations in the list of relations.
300     * @param relations  the relations to be selected
301     */
302    public void selectRelations(Collection<Relation> relations) {
303        if (relations == null || relations.isEmpty()) {
304            model.setSelectedRelations(null);
305        } else {
306            model.setSelectedRelations(relations);
307            Integer i = model.getVisibleRelationIndex(relations.iterator().next());
308            if (i != null) {
309                // Not all relations have to be in the list
310                // (for example when the relation list is hidden, it's not updated with new relations)
311                displaylist.scrollRectToVisible(displaylist.getCellBounds(i, i));
312            }
313        }
314    }
315
316    private JosmTextField setupFilter() {
317        final JosmTextField f = new DisableShortcutsOnFocusGainedTextField();
318        f.setToolTipText(tr("Relation list filter"));
319        final CompileSearchTextDecorator decorator = CompileSearchTextDecorator.decorate(f);
320        f.addPropertyChangeListener("filter", evt -> model.setFilter(decorator.getMatch()));
321        return f;
322    }
323
324    static final class NoTooltipOsmRenderer extends OsmPrimitivRenderer {
325        @Override
326        protected String getComponentToolTipText(OsmPrimitive value) {
327            // Don't show the default tooltip in the relation list
328            return null;
329        }
330    }
331
332    class MouseEventHandler extends PopupMenuLauncher {
333
334        MouseEventHandler() {
335            super(popupMenu);
336        }
337
338        @Override
339        public void mouseExited(MouseEvent me) {
340            if (highlightEnabled) highlightHelper.clear();
341        }
342
343        protected void setCurrentRelationAsSelection() {
344            MainApplication.getLayerManager().getActiveDataSet().setSelected(displaylist.getSelectedValue());
345        }
346
347        protected void editCurrentRelation() {
348            EditRelationAction.launchEditor(getSelected());
349        }
350
351        @Override
352        public void mouseClicked(MouseEvent e) {
353            DataSet ds = MainApplication.getLayerManager().getActiveDataSet();
354            if (ds != null && isDoubleClick(e)) {
355                if (e.isControlDown() && !ds.isReadOnly()) {
356                    editCurrentRelation();
357                } else {
358                    setCurrentRelationAsSelection();
359                }
360            }
361        }
362    }
363
364    /**
365     * The action for creating a new relation.
366     */
367    static class NewAction extends AbstractAction implements LayerChangeListener, ActiveLayerChangeListener {
368        NewAction() {
369            putValue(SHORT_DESCRIPTION, tr("Create a new relation"));
370            putValue(NAME, tr("New"));
371            new ImageProvider("dialogs", "addrelation").getResource().attachImageIcon(this, true);
372            updateEnabledState();
373        }
374
375        public void run() {
376            RelationEditor.getEditor(MainApplication.getLayerManager().getEditLayer(), null, null).setVisible(true);
377        }
378
379        @Override
380        public void actionPerformed(ActionEvent e) {
381            run();
382        }
383
384        protected void updateEnabledState() {
385            setEnabled(MainApplication.getLayerManager().getEditLayer() != null);
386        }
387
388        @Override
389        public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
390            updateEnabledState();
391        }
392
393        @Override
394        public void layerAdded(LayerAddEvent e) {
395            updateEnabledState();
396        }
397
398        @Override
399        public void layerRemoving(LayerRemoveEvent e) {
400            updateEnabledState();
401        }
402
403        @Override
404        public void layerOrderChanged(LayerOrderChangeEvent e) {
405            // Do nothing
406        }
407    }
408
409    /**
410     * The list model for the list of relations displayed in the relation list dialog.
411     */
412    private class RelationListModel extends AbstractListModel<Relation> {
413        private final transient List<Relation> relations = new ArrayList<>();
414        private transient List<Relation> filteredRelations;
415        private final DefaultListSelectionModel selectionModel;
416        private transient SearchCompiler.Match filter;
417
418        RelationListModel(DefaultListSelectionModel selectionModel) {
419            this.selectionModel = selectionModel;
420        }
421
422        /**
423         * Clears the model.
424         */
425        public void clear() {
426            relations.clear();
427            if (filteredRelations != null)
428                filteredRelations.clear();
429            filter = null;
430        }
431
432        /**
433         * Sorts the model using {@link DefaultNameFormatter} relation comparator.
434         */
435        public void sort() {
436            relations.sort(DefaultNameFormatter.getInstance().getRelationComparator());
437        }
438
439        private boolean isValid(Relation r) {
440            return !r.isDeleted() && !r.isIncomplete();
441        }
442
443        public void setRelations(Collection<Relation> relations) {
444            List<Relation> sel = getSelectedRelations();
445            this.relations.clear();
446            this.filteredRelations = null;
447            if (relations == null) {
448                selectionModel.clearSelection();
449                fireContentsChanged(this, 0, getSize());
450                return;
451            }
452            for (Relation r: relations) {
453                if (isValid(r)) {
454                    this.relations.add(r);
455                }
456            }
457            sort();
458            updateFilteredRelations();
459            fireIntervalAdded(this, 0, getSize());
460            setSelectedRelations(sel);
461        }
462
463        /**
464         * Add all relations in <code>addedPrimitives</code> to the model for the
465         * relation list dialog
466         *
467         * @param addedPrimitives the collection of added primitives. May include nodes,
468         * ways, and relations.
469         */
470        public void addRelations(Collection<? extends OsmPrimitive> addedPrimitives) {
471            boolean added = false;
472            for (OsmPrimitive p: addedPrimitives) {
473                if (!(p instanceof Relation)) {
474                    continue;
475                }
476
477                Relation r = (Relation) p;
478                if (relations.contains(r)) {
479                    continue;
480                }
481                if (isValid(r)) {
482                    relations.add(r);
483                    added = true;
484                }
485            }
486            if (added) {
487                List<Relation> sel = getSelectedRelations();
488                sort();
489                updateFilteredRelations();
490                fireIntervalAdded(this, 0, getSize());
491                setSelectedRelations(sel);
492            }
493        }
494
495        /**
496         * Removes all relations in <code>removedPrimitives</code> from the model
497         *
498         * @param removedPrimitives the removed primitives. May include nodes, ways,
499         *   and relations
500         */
501        public void removeRelations(Collection<? extends OsmPrimitive> removedPrimitives) {
502            if (removedPrimitives == null) return;
503            // extract the removed relations
504            //
505            Set<Relation> removedRelations = new HashSet<>();
506            for (OsmPrimitive p: removedPrimitives) {
507                if (!(p instanceof Relation)) {
508                    continue;
509                }
510                removedRelations.add((Relation) p);
511            }
512            if (removedRelations.isEmpty())
513                return;
514            int size = relations.size();
515            relations.removeAll(removedRelations);
516            if (filteredRelations != null) {
517                filteredRelations.removeAll(removedRelations);
518            }
519            if (size != relations.size()) {
520                List<Relation> sel = getSelectedRelations();
521                sort();
522                fireContentsChanged(this, 0, getSize());
523                setSelectedRelations(sel);
524            }
525        }
526
527        private void updateFilteredRelations() {
528            if (filter != null) {
529                filteredRelations = new ArrayList<>(SubclassFilteredCollection.filter(relations, filter::match));
530            } else if (filteredRelations != null) {
531                filteredRelations = null;
532            }
533        }
534
535        public void setFilter(final SearchCompiler.Match filter) {
536            this.filter = filter;
537            updateFilteredRelations();
538            List<Relation> sel = getSelectedRelations();
539            fireContentsChanged(this, 0, getSize());
540            setSelectedRelations(sel);
541            updateTitle();
542        }
543
544        private List<Relation> getVisibleRelations() {
545            return filteredRelations == null ? relations : filteredRelations;
546        }
547
548        private Relation getVisibleRelation(int index) {
549            if (index < 0 || index >= getVisibleRelations().size()) return null;
550            return getVisibleRelations().get(index);
551        }
552
553        @Override
554        public Relation getElementAt(int index) {
555            return getVisibleRelation(index);
556        }
557
558        @Override
559        public int getSize() {
560            return getVisibleRelations().size();
561        }
562
563        /**
564         * Replies the list of selected relations. Empty list,
565         * if there are no selected relations.
566         *
567         * @return the list of selected, non-new relations.
568         */
569        public List<Relation> getSelectedRelations() {
570            List<Relation> ret = new ArrayList<>();
571            for (int i = 0; i < getSize(); i++) {
572                if (!selectionModel.isSelectedIndex(i)) {
573                    continue;
574                }
575                ret.add(getVisibleRelation(i));
576            }
577            return ret;
578        }
579
580        /**
581         * Sets the selected relations.
582         *
583         * @param sel the list of selected relations
584         */
585        public void setSelectedRelations(Collection<Relation> sel) {
586            selectionModel.setValueIsAdjusting(true);
587            selectionModel.clearSelection();
588            if (sel != null && !sel.isEmpty()) {
589                if (!getVisibleRelations().containsAll(sel)) {
590                    resetFilter();
591                }
592                for (Relation r: sel) {
593                    Integer i = getVisibleRelationIndex(r);
594                    if (i != null) {
595                        selectionModel.addSelectionInterval(i, i);
596                    }
597                }
598            }
599            selectionModel.setValueIsAdjusting(false);
600        }
601
602        private Integer getVisibleRelationIndex(Relation rel) {
603            int i = getVisibleRelations().indexOf(rel);
604            if (i < 0)
605                return null;
606            return i;
607        }
608
609        public void updateTitle() {
610            if (!relations.isEmpty() && relations.size() != getSize()) {
611                RelationListDialog.this.setTitle(tr("Relations: {0}/{1}", getSize(), relations.size()));
612            } else if (getSize() > 0) {
613                RelationListDialog.this.setTitle(tr("Relations: {0}", getSize()));
614            } else {
615                RelationListDialog.this.setTitle(tr("Relations"));
616            }
617        }
618    }
619
620    private void setupPopupMenuHandler() {
621        List<JMenuItem> checkDisabled = new ArrayList<>();
622
623        // -- select action
624        popupMenuHandler.addAction(selectRelationAction);
625        popupMenuHandler.addAction(addRelationToSelectionAction);
626
627        // -- select members action
628        popupMenuHandler.addAction(selectMembersAction);
629        popupMenuHandler.addAction(addMembersToSelectionAction);
630
631        // -- download members action
632        popupMenuHandler.addSeparator();
633        popupMenuHandler.addAction(downloadMembersAction);
634        popupMenuHandler.addAction(downloadSelectedIncompleteMembersAction);
635
636        // -- export relation to gpx action
637        popupMenuHandler.addSeparator();
638        checkDisabled.add(popupMenuHandler.addAction(exportRelationFromFirstAction));
639        checkDisabled.add(popupMenuHandler.addAction(exportRelationFromLastAction));
640        popupMenuHandler.addSeparator();
641        checkDisabled.add(popupMenuHandler.addAction(exportRelationFromFirstToLayerAction));
642        checkDisabled.add(popupMenuHandler.addAction(exportRelationFromLastToLayerAction));
643
644        popupMenuHandler.addSeparator();
645        popupMenuHandler.addAction(editAction).setVisible(false);
646        popupMenuHandler.addAction(duplicateAction).setVisible(false);
647        popupMenuHandler.addAction(deleteRelationsAction).setVisible(false);
648
649        addSelectionToRelationMenuItem = popupMenuHandler.addAction(addSelectionToRelations);
650
651        popupMenuHandler.addListener(new PopupMenuListener() {
652            @Override
653            public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
654                for (JMenuItem mi: checkDisabled) {
655                    mi.setVisible(((OsmPrimitiveAction) mi.getAction()).isEnabled());
656
657                    Component sep = popupMenu.getComponent(
658                            Math.max(0, popupMenu.getComponentIndex(mi)-1));
659                    if (!(sep instanceof JMenuItem)) {
660                        sep.setVisible(mi.isVisible());
661                    }
662                }
663            }
664
665            @Override
666            public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
667            }
668
669            @Override
670            public void popupMenuCanceled(PopupMenuEvent e) {
671            }
672        });
673    }
674
675    /* ---------------------------------------------------------------------------------- */
676    /* Methods that can be called from plugins                                            */
677    /* ---------------------------------------------------------------------------------- */
678
679    /**
680     * Replies the popup menu handler.
681     * @return The popup menu handler
682     */
683    public PopupMenuHandler getPopupMenuHandler() {
684        return popupMenuHandler;
685    }
686
687    /**
688     * Replies the list of selected relations. Empty list, if there are no selected relations.
689     * @return the list of selected, non-new relations.
690     */
691    public Collection<Relation> getSelectedRelations() {
692        return model.getSelectedRelations();
693    }
694
695    /* ---------------------------------------------------------------------------------- */
696    /* DataSetListener                                                                    */
697    /* ---------------------------------------------------------------------------------- */
698
699    @Override
700    public void nodeMoved(NodeMovedEvent event) {
701        /* irrelevant in this context */
702    }
703
704    @Override
705    public void wayNodesChanged(WayNodesChangedEvent event) {
706        /* irrelevant in this context */
707    }
708
709    @Override
710    public void primitivesAdded(final PrimitivesAddedEvent event) {
711        model.addRelations(event.getPrimitives());
712        model.updateTitle();
713    }
714
715    @Override
716    public void primitivesRemoved(final PrimitivesRemovedEvent event) {
717        model.removeRelations(event.getPrimitives());
718        model.updateTitle();
719    }
720
721    @Override
722    public void relationMembersChanged(final RelationMembersChangedEvent event) {
723        List<Relation> sel = model.getSelectedRelations();
724        model.sort();
725        model.setSelectedRelations(sel);
726        displaylist.repaint();
727    }
728
729    @Override
730    public void tagsChanged(TagsChangedEvent event) {
731        OsmPrimitive prim = event.getPrimitive();
732        if (!(prim instanceof Relation))
733            return;
734        // trigger a sort of the relation list because the display name may have changed
735        List<Relation> sel = model.getSelectedRelations();
736        model.sort();
737        model.setSelectedRelations(sel);
738        displaylist.repaint();
739    }
740
741    @Override
742    public void dataChanged(DataChangedEvent event) {
743        initFromLayer(MainApplication.getLayerManager().getActiveDataLayer());
744    }
745
746    @Override
747    public void otherDatasetChange(AbstractDatasetChangedEvent event) {
748        /* ignore */
749    }
750
751    @Override
752    public void zoomChanged() {
753        // re-filter relations
754        if (model.filter != null) {
755            model.setFilter(model.filter);
756        }
757    }
758
759    @Override
760    public void expertChanged(boolean isExpert) {
761        addSelectionToRelationMenuItem.setVisible(isExpert);
762    }
763}
Note: See TracBrowser for help on using the repository browser.