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

Last change on this file since 5517 was 5517, checked in by bastiK, 6 years ago

fixed #8083 - JOSM_Zone erreur sometimes

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