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

Last change on this file was 14709, checked in by Don-vip, 5 weeks ago

fix #17222 - "Select relation" button in relation list doesn't work

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