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

Last change on this file since 3102 was 3102, checked in by Gubaer, 9 years ago

fixed #4651: Ability to download incomplete relation from selection
fixed #4098: Popup Menu entry "download relation members" in relation dialog should be "download incomplete relation members"
fixed two NPEs in RelationListDialog and SelectionListDialog
refactored SelectionListDialog to support better user feedback (enabled/disabled buttons and menu items)
Finally removed the sort() method on DataSet, marked as FIXME since a long time.

CAVEAT: DataSet.getSelected() now returns an unmodifiable list instead of a copy of the selection list. This may lead to UnsupportedOperationExceptions in the next few days. I tried to make sure the JOSM core uses getSelected() only for reading, but I didn't check the plugins.

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