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

Last change on this file since 5793 was 5793, checked in by akks, 6 years ago

Big refactoring of relation context menu actions, removing duplicate and similar code. Any behavior change is a bug!
see #7846: Harmonize Relation popup menus and actions,
Please check relation-actions in Selection List, Relation List and Properties dialogs.

  • Property svn:eol-style set to native
File size: 30.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.BorderLayout;
9import java.awt.Color;
10import java.awt.Point;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.HashSet;
20import java.util.Iterator;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Set;
24
25import javax.swing.AbstractAction;
26import javax.swing.AbstractListModel;
27import javax.swing.Action;
28import javax.swing.DefaultListSelectionModel;
29import javax.swing.JComponent;
30import javax.swing.JList;
31import javax.swing.JMenuItem;
32import javax.swing.JPanel;
33import javax.swing.JPopupMenu;
34import javax.swing.JScrollPane;
35import javax.swing.JTextField;
36import javax.swing.KeyStroke;
37import javax.swing.ListSelectionModel;
38import javax.swing.SwingUtilities;
39import javax.swing.UIManager;
40import javax.swing.event.DocumentEvent;
41import javax.swing.event.DocumentListener;
42import javax.swing.event.ListSelectionEvent;
43import javax.swing.event.ListSelectionListener;
44import javax.swing.event.PopupMenuListener;
45
46import org.openstreetmap.josm.Main;
47import org.openstreetmap.josm.actions.relation.DownloadMembersAction;
48import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
49import org.openstreetmap.josm.actions.relation.EditRelationAction;
50import org.openstreetmap.josm.actions.relation.SelectMembersAction;
51import org.openstreetmap.josm.actions.relation.SelectRelationAction;
52import org.openstreetmap.josm.actions.search.SearchCompiler;
53import org.openstreetmap.josm.command.Command;
54import org.openstreetmap.josm.command.SequenceCommand;
55import org.openstreetmap.josm.data.SelectionChangedListener;
56import org.openstreetmap.josm.data.osm.DataSet;
57import org.openstreetmap.josm.data.osm.OsmPrimitive;
58import org.openstreetmap.josm.data.osm.Relation;
59import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
60import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
61import org.openstreetmap.josm.data.osm.event.DataSetListener;
62import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
63import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
64import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
65import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
66import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
67import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
68import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
69import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
70import org.openstreetmap.josm.gui.DefaultNameFormatter;
71import org.openstreetmap.josm.gui.MapView;
72import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
73import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
74import org.openstreetmap.josm.gui.SideButton;
75import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
76import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
77import org.openstreetmap.josm.gui.layer.Layer;
78import org.openstreetmap.josm.gui.layer.OsmDataLayer;
79import org.openstreetmap.josm.gui.util.GuiHelper;
80import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
81import org.openstreetmap.josm.tools.ImageProvider;
82import org.openstreetmap.josm.tools.InputMapUtils;
83import org.openstreetmap.josm.tools.Predicate;
84import org.openstreetmap.josm.tools.Shortcut;
85import org.openstreetmap.josm.tools.Utils;
86
87/**
88 * A dialog showing all known relations, with buttons to add, edit, and
89 * delete them.
90 *
91 * We don't have such dialogs for nodes, segments, and ways, because those
92 * objects are visible on the map and can be selected there. Relations are not.
93 */
94public class RelationListDialog extends ToggleDialog implements DataSetListener {
95    /** The display list. */
96    private final JList displaylist;
97    /** the list model used */
98    private final RelationListModel model;
99
100    /** the delete action */
101    private final DeleteAction deleteAction;
102    private final NewAction newAction;
103    private final AddToRelation addToRelation;
104    private final DuplicateAction duplicateAction;
105   
106    /** the popup menu */
107    private final RelationDialogPopupMenu popupMenu;
108
109    private final JTextField filter;
110   
111    // Actions
112    /** the edit action */
113    private final EditRelationAction editAction;
114    private final DownloadMembersAction downloadMembersAction = new DownloadMembersAction();
115    private final DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
116    private final SelectMembersAction selectMemebersAction = new SelectMembersAction(false);
117    private final SelectMembersAction addMembersToSelectionAction = new SelectMembersAction(true);
118    private final SelectRelationAction selectRelationAction = new SelectRelationAction(false);
119    private final SelectRelationAction addRelationToSelectionAction = new SelectRelationAction(true);
120
121    /**
122     * constructor
123     */
124    public RelationListDialog() {
125        super(tr("Relations"), "relationlist", tr("Open a list of all relations."),
126                Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")),
127                KeyEvent.VK_R, Shortcut.ALT_SHIFT), 150);
128
129        // create the list of relations
130        //
131        DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
132        model = new RelationListModel(selectionModel);
133        displaylist = new JList(model);
134        displaylist.setSelectionModel(selectionModel);
135        displaylist.setCellRenderer(new OsmPrimitivRenderer() {
136            /**
137             * Don't show the default tooltip in the relation list.
138             */
139            @Override
140            protected String getComponentToolTipText(OsmPrimitive value) {
141                return null;
142            }
143        });
144        displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
145        displaylist.addMouseListener(new MouseEventHandler());
146
147        // the new action
148        //
149        newAction = new NewAction();
150
151        // the edit action
152        //
153        editAction = new EditRelationAction();
154       
155        // the duplicate action
156        //
157        duplicateAction = new DuplicateAction();
158       
159        // the delete action
160        //
161        deleteAction = new DeleteAction();
162
163        // Add to realaion action
164        //
165        addToRelation = new AddToRelation();
166
167        filter = setupFilter();
168
169        displaylist.addListSelectionListener(new ListSelectionListener() {
170            @Override
171            public void valueChanged(ListSelectionEvent e) {
172                duplicateAction.valueChanged(e);
173                deleteAction.valueChanged(e);
174                addToRelation.valueChanged(e);
175
176                List<Relation> rels;
177                rels = model.getSelectedNonNewRelations();
178                downloadMembersAction.setRelations(rels);
179
180                rels = model.getSelectedRelationsWithIncompleteMembers();
181                downloadSelectedIncompleteMembersAction.setRelations(rels); 
182
183                rels = model.getSelectedRelations();
184                selectMemebersAction.setRelations(rels);
185                addMembersToSelectionAction.setRelations(rels);
186                selectRelationAction.setRelations(rels);
187                addRelationToSelectionAction.setRelations(rels);
188            }
189        });
190       
191        JPanel pane = new JPanel(new BorderLayout());
192        pane.add(filter, BorderLayout.NORTH);
193        pane.add(new JScrollPane(displaylist), BorderLayout.CENTER);
194        createLayout(pane, false, Arrays.asList(new SideButton[]{
195                new SideButton(newAction, false),
196                new SideButton(editAction, false),
197                new SideButton(duplicateAction, false),
198                new SideButton(deleteAction, false),
199                new SideButton(selectRelationAction, false)
200        }));
201
202        // activate DEL in the list of relations
203        //displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "deleteRelation");
204        //displaylist.getActionMap().put("deleteRelation", deleteAction);
205
206        InputMapUtils.unassignCtrlShiftUpDown(displaylist, JComponent.WHEN_FOCUSED);
207       
208        // Select relation on Ctrl-Enter
209        InputMapUtils.addEnterAction(displaylist, selectRelationAction);
210
211        popupMenu = new RelationDialogPopupMenu();
212
213        // Edit relation on Ctrl-Enter
214        displaylist.getActionMap().put("edit", editAction);
215        displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK), "edit");
216    }
217   
218    @Override public void showNotify() {
219        MapView.addLayerChangeListener(newAction);
220        newAction.updateEnabledState();
221        DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT);
222        DataSet.addSelectionListener(addToRelation);
223        dataChanged(null);
224    }
225
226    @Override public void hideNotify() {
227        MapView.removeLayerChangeListener(newAction);
228        DatasetEventManager.getInstance().removeDatasetListener(this);
229        DataSet.removeSelectionListener(addToRelation);
230    }
231   
232    private void resetFilter() {
233        filter.setText(null);
234    }
235
236    /**
237     * Initializes the relation list dialog from a layer. If <code>layer</code> is null
238     * or if it isn't an {@link OsmDataLayer} the dialog is reset to an empty dialog.
239     * Otherwise it is initialized with the list of non-deleted and visible relations
240     * in the layer's dataset.
241     *
242     * @param layer the layer. May be null.
243     */
244    protected void initFromLayer(Layer layer) {
245        if (layer == null || ! (layer instanceof OsmDataLayer)) {
246            model.setRelations(null);
247            return;
248        }
249        OsmDataLayer l = (OsmDataLayer)layer;
250        model.setRelations(l.data.getRelations());
251        model.updateTitle();
252    }
253
254    /**
255     * @return The selected relation in the list
256     */
257    private Relation getSelected() {
258        if(model.getSize() == 1) {
259            displaylist.setSelectedIndex(0);
260        }
261        return (Relation) displaylist.getSelectedValue();
262    }
263
264    /**
265     * Selects the relation <code>relation</code> in the list of relations.
266     *
267     * @param relation  the relation
268     */
269    public void selectRelation(Relation relation) {
270        selectRelations(Collections.singleton(relation));
271    }
272
273    /**
274     * Selects the relations in the list of relations.
275     * @param relations  the relations to be selected
276     */
277    public void selectRelations(Collection<Relation> relations) {
278        if (relations == null || relations.isEmpty()) {
279            model.setSelectedRelations(null);
280        } else {
281            model.setSelectedRelations(relations);
282            Integer i = model.getVisibleRelationIndex(relations.iterator().next());
283            if (i != null) { // Not all relations have to be in the list (for example when the relation list is hidden, it's not updated with new relations)
284                displaylist.scrollRectToVisible(displaylist.getCellBounds(i, i));
285            }
286        }
287    }
288
289    private JTextField  setupFilter() {
290        final JTextField f = new DisableShortcutsOnFocusGainedTextField();
291        f.setToolTipText(tr("Relation list filter"));
292        f.getDocument().addDocumentListener(new DocumentListener() {
293
294            private void setFilter() {
295                try {
296                    f.setBackground(UIManager.getColor("TextField.background"));
297                    f.setToolTipText(tr("Relation list filter"));
298                    model.setFilter(SearchCompiler.compile(filter.getText(), false, false));
299                } catch (SearchCompiler.ParseError ex) {
300                    f.setBackground(new Color(255, 224, 224));
301                    f.setToolTipText(ex.getMessage());
302                    model.setFilter(new SearchCompiler.Always());
303                }
304            }
305
306            @Override
307            public void insertUpdate(DocumentEvent e) {
308                setFilter();
309            }
310
311            @Override
312            public void removeUpdate(DocumentEvent e) {
313                setFilter();
314            }
315
316            @Override
317            public void changedUpdate(DocumentEvent e) {
318                setFilter();
319            }
320        });
321        return f;
322    }
323
324    class MouseEventHandler extends MouseAdapter {
325        protected void setCurrentRelationAsSelection() {
326            Main.main.getCurrentDataSet().setSelected((Relation)displaylist.getSelectedValue());
327        }
328
329        protected void editCurrentRelation() {
330            EditRelationAction.launchEditor(getSelected());
331        }
332
333        @Override public void mouseClicked(MouseEvent e) {
334            if (Main.main.getEditLayer() == null) return;
335            if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
336                if (e.isControlDown()) {
337                    editCurrentRelation();
338                } else {
339                    setCurrentRelationAsSelection();
340                }
341            }
342        }
343        private void openPopup(MouseEvent e) {
344            Point p = e.getPoint();
345            int index = displaylist.locationToIndex(p);
346            if (index < 0) return;
347            if (!displaylist.getCellBounds(index, index).contains(e.getPoint()))
348                return;
349            if (! displaylist.isSelectedIndex(index)) {
350                displaylist.setSelectedIndex(index);
351            }
352            popupMenu.show(displaylist, p.x, p.y-3);
353        }
354        @Override public void mousePressed(MouseEvent e) {
355            if (Main.main.getEditLayer() == null) return;
356            if (e.isPopupTrigger()) {
357                openPopup(e);
358            }
359        }
360        @Override public void mouseReleased(MouseEvent e) {
361            if (Main.main.getEditLayer() == null) return;
362            if (e.isPopupTrigger()) {
363                openPopup(e);
364            }
365        }
366    }
367   
368    /**
369     * The delete action
370     *
371     */
372    class DeleteAction extends AbstractAction implements ListSelectionListener {
373        class AbortException extends Exception {}
374
375        public DeleteAction() {
376            putValue(SHORT_DESCRIPTION,tr("Delete the selected relation"));
377            putValue(NAME, tr("Delete"));
378            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
379            setEnabled(false);
380        }
381
382        protected void deleteRelation(Relation toDelete) {
383            if (toDelete == null)
384                return;
385            org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation(
386                    Main.main.getEditLayer(),
387                    toDelete
388                    );
389        }
390
391        public void actionPerformed(ActionEvent e) {
392            if (!isEnabled())
393                return;
394            List<Relation> toDelete = new LinkedList<Relation>();
395            for (int i : displaylist.getSelectedIndices()) {
396                toDelete.add(model.getVisibleRelation(i));
397            }
398            for (Relation r : toDelete) {
399                deleteRelation(r);
400            }
401            displaylist.clearSelection();
402        }
403
404        public void valueChanged(ListSelectionEvent e) {
405            setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length > 0);
406        }
407    }
408
409    /**
410     * The action for creating a new relation
411     *
412     */
413    static class NewAction extends AbstractAction implements LayerChangeListener{
414        public NewAction() {
415            putValue(SHORT_DESCRIPTION,tr("Create a new relation"));
416            putValue(NAME, tr("New"));
417            putValue(SMALL_ICON, ImageProvider.get("dialogs", "addrelation"));
418            updateEnabledState();
419        }
420
421        public void run() {
422            RelationEditor.getEditor(Main.main.getEditLayer(),null, null).setVisible(true);
423        }
424
425        public void actionPerformed(ActionEvent e) {
426            run();
427        }
428
429        protected void updateEnabledState() {
430            setEnabled(Main.main != null && Main.main.getEditLayer() != null);
431        }
432
433        public void activeLayerChange(Layer oldLayer, Layer newLayer) {
434            updateEnabledState();
435        }
436
437        public void layerAdded(Layer newLayer) {
438            updateEnabledState();
439        }
440
441        public void layerRemoved(Layer oldLayer) {
442            updateEnabledState();
443        }
444    }
445
446    /**
447     * Creates a new relation with a copy of the current editor state
448     *
449     */
450    class DuplicateAction extends AbstractAction implements ListSelectionListener {
451        public DuplicateAction() {
452            putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
453            putValue(SMALL_ICON, ImageProvider.get("duplicate"));
454            putValue(NAME, tr("Duplicate"));
455            updateEnabledState();
456        }
457
458        public void launchEditorForDuplicate(Relation original) {
459            Relation copy = new Relation(original, true);
460            copy.setModified(true);
461            RelationEditor editor = RelationEditor.getEditor(
462                    Main.main.getEditLayer(),
463                    copy,
464                    null /* no selected members */
465                    );
466            editor.setVisible(true);
467        }
468
469        public void actionPerformed(ActionEvent e) {
470            if (!isEnabled())
471                return;
472            launchEditorForDuplicate(getSelected());
473        }
474
475        protected void updateEnabledState() {
476            setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length == 1);
477        }
478
479        public void valueChanged(ListSelectionEvent e) {
480            updateEnabledState();
481        }
482    }
483
484    class AddToRelation extends AbstractAction implements ListSelectionListener, SelectionChangedListener {
485
486        public AddToRelation() {
487            super("", ImageProvider.get("dialogs/conflict", "copyendright"));
488            putValue(SHORT_DESCRIPTION, tr("Add all objects selected in the current dataset after the last member"));
489            setEnabled(false);
490        }
491
492        @Override
493        public void actionPerformed(ActionEvent e) {
494            Collection<Command> cmds = new LinkedList<Command>();
495            for (Relation orig : getSelectedRelations()) {
496                Command c = GenericRelationEditor.addPrimitivesToRelation(orig, Main.main.getCurrentDataSet().getSelected());
497                if (c != null) {
498                    cmds.add(c);
499                }
500            }
501            if (!cmds.isEmpty()) {
502                Main.main.undoRedo.add(new SequenceCommand(tr("Add selection to relation"), cmds));
503            }
504        }
505
506        @Override
507        public void valueChanged(ListSelectionEvent e) {
508            putValue(NAME, trn("Add selection to {0} relation", "Add selection to {0} relations",
509                    getSelectedRelations().size(), getSelectedRelations().size()));
510        }
511
512        @Override
513        public void selectionChanged(final Collection<? extends OsmPrimitive> newSelection) {
514            GuiHelper.runInEDT(new Runnable() {
515                @Override
516                public void run() {
517                    setEnabled(newSelection != null && !newSelection.isEmpty());
518                }
519            });
520        }
521    }
522
523    /**
524     * The list model for the list of relations displayed in the relation list
525     * dialog.
526     *
527     */
528    private class RelationListModel extends AbstractListModel {
529        private final ArrayList<Relation> relations = new ArrayList<Relation>();
530        private ArrayList<Relation> filteredRelations;
531        private DefaultListSelectionModel selectionModel;
532        private SearchCompiler.Match filter;
533
534        public RelationListModel(DefaultListSelectionModel selectionModel) {
535            this.selectionModel = selectionModel;
536        }
537
538        public Relation getRelation(int idx) {
539            return relations.get(idx);
540        }
541
542        public void sort() {
543            Collections.sort(
544                    relations,
545                    DefaultNameFormatter.getInstance().getRelationComparator()
546                    );
547        }
548
549        private boolean isValid(Relation r) {
550            return !r.isDeleted() && r.isVisible() && !r.isIncomplete();
551        }
552
553        public void setRelations(Collection<Relation> relations) {
554            List<Relation> sel =  getSelectedRelations();
555            this.relations.clear();
556            this.filteredRelations = null;
557            if (relations == null) {
558                selectionModel.clearSelection();
559                fireContentsChanged(this,0,getSize());
560                return;
561            }
562            for (Relation r: relations) {
563                if (isValid(r)) {
564                    this.relations.add(r);
565                }
566            }
567            sort();
568            updateFilteredRelations();
569            fireIntervalAdded(this, 0, getSize());
570            setSelectedRelations(sel);
571        }
572
573        /**
574         * Add all relations in <code>addedPrimitives</code> to the model for the
575         * relation list dialog
576         *
577         * @param addedPrimitives the collection of added primitives. May include nodes,
578         * ways, and relations.
579         */
580        public void addRelations(Collection<? extends OsmPrimitive> addedPrimitives) {
581            boolean added = false;
582            for (OsmPrimitive p: addedPrimitives) {
583                if (! (p instanceof Relation)) {
584                    continue;
585                }
586
587                Relation r = (Relation)p;
588                if (relations.contains(r)) {
589                    continue;
590                }
591                if (isValid(r)) {
592                    relations.add(r);
593                    added = true;
594                }
595            }
596            if (added) {
597                List<Relation> sel = getSelectedRelations();
598                sort();
599                updateFilteredRelations();
600                fireIntervalAdded(this, 0, getSize());
601                setSelectedRelations(sel);
602            }
603        }
604
605        /**
606         * Removes all relations in <code>removedPrimitives</code> from the model
607         *
608         * @param removedPrimitives the removed primitives. May include nodes, ways,
609         *   and relations
610         */
611        public void removeRelations(Collection<? extends OsmPrimitive> removedPrimitives) {
612            if (removedPrimitives == null) return;
613            // extract the removed relations
614            //
615            Set<Relation> removedRelations = new HashSet<Relation>();
616            for (OsmPrimitive p: removedPrimitives) {
617                if (! (p instanceof Relation)) {
618                    continue;
619                }
620                removedRelations.add((Relation)p);
621            }
622            if (removedRelations.isEmpty())
623                return;
624            int size = relations.size();
625            relations.removeAll(removedRelations);
626            if (filteredRelations != null) {
627                filteredRelations.removeAll(removedRelations);
628            }
629            if (size != relations.size()) {
630                List<Relation> sel = getSelectedRelations();
631                sort();
632                fireContentsChanged(this, 0, getSize());
633                setSelectedRelations(sel);
634            }
635        }
636
637        /**
638         * Replies the list of selected relations with incomplete members
639         *
640         * @return the list of selected relations with incomplete members
641         */
642        public List<Relation> getSelectedRelationsWithIncompleteMembers() {
643            List<Relation> ret = getSelectedNonNewRelations();
644            Iterator<Relation> it = ret.iterator();
645            while(it.hasNext()) {
646                Relation r = it.next();
647                if (!r.hasIncompleteMembers()) {
648                    it.remove();
649                }
650            }
651            return ret;
652        }
653       
654        private void updateFilteredRelations() {
655            if (filter != null) {
656                filteredRelations = new ArrayList<Relation>(Utils.filter(relations, new Predicate<Relation>() {
657                    @Override
658                    public boolean evaluate(Relation r) {
659                        return filter.match(r);
660                    }
661                }));
662            } else if (filteredRelations != null) {
663                filteredRelations = null;
664            }
665        }
666
667        public void setFilter(final SearchCompiler.Match filter) {
668            this.filter = filter;
669            updateFilteredRelations();
670            List<Relation> sel = getSelectedRelations();
671            fireContentsChanged(this, 0, getSize());
672            setSelectedRelations(sel);
673            updateTitle();
674        }
675
676        private List<Relation> getVisibleRelations() {
677            return filteredRelations == null ? relations : filteredRelations;
678        }
679       
680        private Relation getVisibleRelation(int index) {
681            if (index < 0 || index >= getVisibleRelations().size()) return null;
682            return getVisibleRelations().get(index);
683        }
684
685        @Override
686        public Object getElementAt(int index) {
687            return getVisibleRelation(index);
688        }
689
690        @Override
691        public int getSize() {
692            return getVisibleRelations().size();
693        }
694
695        /**
696         * Replies the list of selected, non-new relations. Empty list,
697         * if there are no selected, non-new relations.
698         *
699         * @return the list of selected, non-new relations.
700         */
701        public List<Relation> getSelectedNonNewRelations() {
702            ArrayList<Relation> ret = new ArrayList<Relation>();
703            for (int i=0; i<getSize();i++) {
704                if (!selectionModel.isSelectedIndex(i)) {
705                    continue;
706                }
707                if (getVisibleRelation(i).isNew()) {
708                    continue;
709                }
710                ret.add(getVisibleRelation(i));
711            }
712            return ret;
713        }
714
715        /**
716         * Replies the list of selected relations. Empty list,
717         * if there are no selected relations.
718         *
719         * @return the list of selected, non-new relations.
720         */
721        public List<Relation> getSelectedRelations() {
722            ArrayList<Relation> ret = new ArrayList<Relation>();
723            for (int i=0; i<getSize();i++) {
724                if (!selectionModel.isSelectedIndex(i)) {
725                    continue;
726                }
727                ret.add(getVisibleRelation(i));
728            }
729            return ret;
730        }
731
732        /**
733         * Sets the selected relations.
734         *
735         * @param sel the list of selected relations
736         */
737        public void setSelectedRelations(Collection<Relation> sel) {
738            selectionModel.clearSelection();
739            if (sel == null || sel.isEmpty())
740                return;
741            if (!getVisibleRelations().containsAll(sel)) {
742                resetFilter();
743            }
744            for (Relation r: sel) {
745                Integer i = getVisibleRelationIndex(r);
746                if (i != null) {
747                    selectionModel.addSelectionInterval(i,i);
748                }
749            }
750        }
751
752        /**
753         * Returns the index of the relation
754         * @param rel The relation to look for
755         *
756         * @return index of relation (null if it cannot be found)
757         */
758        public Integer getRelationIndex(Relation rel) {
759            int i = relations.indexOf(rel);
760            if (i<0)
761                return null;
762            return i;
763        }
764       
765        private Integer getVisibleRelationIndex(Relation rel) {
766            int i = getVisibleRelations().indexOf(rel);
767            if (i<0)
768                return null;
769            return i;
770        }
771
772        public void updateTitle() {
773            if (relations.size() > 0 && relations.size() != getSize()) {
774                RelationListDialog.this.setTitle(tr("Relations: {0}/{1}", getSize(), relations.size()));
775            } else if (getSize() > 0) {
776                RelationListDialog.this.setTitle(tr("Relations: {0}", getSize()));
777            } else {
778                RelationListDialog.this.setTitle(tr("Relations"));
779            }
780        }
781    }
782
783    class RelationDialogPopupMenu extends JPopupMenu {
784
785        public RelationDialogPopupMenu() {
786            // -- download members action
787            add(downloadMembersAction);
788
789            // -- download incomplete members action
790            add(downloadSelectedIncompleteMembersAction);
791
792            addSeparator();
793
794            // -- select members action
795            add(selectMemebersAction);
796            add(addMembersToSelectionAction);
797
798            // -- select action
799            add(selectRelationAction);
800            add(addRelationToSelectionAction);
801
802            addSeparator();
803
804            add(addToRelation);
805        }
806    }
807
808    public void addPopupMenuSeparator() {
809        popupMenu.addSeparator();
810    }
811
812    public JMenuItem addPopupMenuAction(Action a) {
813        return popupMenu.add(a);
814    }
815
816    public void addPopupMenuListener(PopupMenuListener l) {
817        popupMenu.addPopupMenuListener(l);
818    }
819
820    public void removePopupMenuListener(PopupMenuListener l) {
821        popupMenu.addPopupMenuListener(l);
822    }
823
824    public Collection<Relation> getSelectedRelations() {
825        return model.getSelectedRelations();
826    }
827
828    /* ---------------------------------------------------------------------------------- */
829    /* DataSetListener                                                                    */
830    /* ---------------------------------------------------------------------------------- */
831
832    public void nodeMoved(NodeMovedEvent event) {/* irrelevant in this context */}
833
834    public void wayNodesChanged(WayNodesChangedEvent event) {/* irrelevant in this context */}
835
836    public void primitivesAdded(final PrimitivesAddedEvent event) {
837        model.addRelations(event.getPrimitives());
838        model.updateTitle();
839    }
840
841    public void primitivesRemoved(final PrimitivesRemovedEvent event) {
842        model.removeRelations(event.getPrimitives());
843        model.updateTitle();
844    }
845
846    public void relationMembersChanged(final RelationMembersChangedEvent event) {
847        List<Relation> sel = model.getSelectedRelations();
848        model.sort();
849        model.setSelectedRelations(sel);
850        displaylist.repaint();
851    }
852
853    public void tagsChanged(TagsChangedEvent event) {
854        OsmPrimitive prim = event.getPrimitive();
855        if (prim == null || ! (prim instanceof Relation))
856            return;
857        // trigger a sort of the relation list because the display name may
858        // have changed
859        //
860        List<Relation> sel = model.getSelectedRelations();
861        model.sort();
862        model.setSelectedRelations(sel);
863        displaylist.repaint();
864    }
865
866    public void dataChanged(DataChangedEvent event) {
867        initFromLayer(Main.main.getEditLayer());
868    }
869
870    public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignore */}
871}
Note: See TracBrowser for help on using the repository browser.