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

Last change on this file since 15673 was 15673, checked in by simon04, 4 years ago

see #13901 - Reimplement Tag2Link in core

The plugin tag2link has been created and maintained by Don-vip.

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