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

Last change on this file since 5082 was 5082, checked in by simon04, 7 years ago

fix #5395 - add "Add selection to relation" to popup menu of relation toggle dialog

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