001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.Component; 008import java.awt.event.ActionEvent; 009import java.awt.event.KeyEvent; 010import java.awt.event.MouseEvent; 011import java.beans.PropertyChangeEvent; 012import java.beans.PropertyChangeListener; 013import java.util.ArrayList; 014import java.util.Arrays; 015import java.util.Collection; 016import java.util.Collections; 017import java.util.HashSet; 018import java.util.List; 019import java.util.Set; 020 021import javax.swing.AbstractAction; 022import javax.swing.AbstractListModel; 023import javax.swing.DefaultListSelectionModel; 024import javax.swing.FocusManager; 025import javax.swing.JComponent; 026import javax.swing.JList; 027import javax.swing.JMenuItem; 028import javax.swing.JPanel; 029import javax.swing.JPopupMenu; 030import javax.swing.JScrollPane; 031import javax.swing.KeyStroke; 032import javax.swing.ListSelectionModel; 033import javax.swing.event.ListSelectionEvent; 034import javax.swing.event.ListSelectionListener; 035 036import org.openstreetmap.josm.Main; 037import org.openstreetmap.josm.actions.ExpertToggleAction; 038import org.openstreetmap.josm.actions.relation.AddSelectionToRelations; 039import org.openstreetmap.josm.actions.relation.DeleteRelationsAction; 040import org.openstreetmap.josm.actions.relation.DownloadMembersAction; 041import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction; 042import org.openstreetmap.josm.actions.relation.DuplicateRelationAction; 043import org.openstreetmap.josm.actions.relation.EditRelationAction; 044import org.openstreetmap.josm.actions.relation.RecentRelationsAction; 045import org.openstreetmap.josm.actions.relation.SelectMembersAction; 046import org.openstreetmap.josm.actions.relation.SelectRelationAction; 047import org.openstreetmap.josm.actions.search.SearchCompiler; 048import org.openstreetmap.josm.data.osm.DataSet; 049import org.openstreetmap.josm.data.osm.OsmPrimitive; 050import org.openstreetmap.josm.data.osm.Relation; 051import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent; 052import org.openstreetmap.josm.data.osm.event.DataChangedEvent; 053import org.openstreetmap.josm.data.osm.event.DataSetListener; 054import org.openstreetmap.josm.data.osm.event.DatasetEventManager; 055import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode; 056import org.openstreetmap.josm.data.osm.event.NodeMovedEvent; 057import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent; 058import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent; 059import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent; 060import org.openstreetmap.josm.data.osm.event.TagsChangedEvent; 061import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent; 062import org.openstreetmap.josm.gui.DefaultNameFormatter; 063import org.openstreetmap.josm.gui.MapView; 064import org.openstreetmap.josm.gui.MapView.LayerChangeListener; 065import org.openstreetmap.josm.gui.NavigatableComponent; 066import org.openstreetmap.josm.gui.OsmPrimitivRenderer; 067import org.openstreetmap.josm.gui.PopupMenuHandler; 068import org.openstreetmap.josm.gui.SideButton; 069import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor; 070import org.openstreetmap.josm.gui.layer.Layer; 071import org.openstreetmap.josm.gui.layer.OsmDataLayer; 072import org.openstreetmap.josm.gui.util.GuiHelper; 073import org.openstreetmap.josm.gui.util.HighlightHelper; 074import org.openstreetmap.josm.gui.widgets.CompileSearchTextDecorator; 075import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField; 076import org.openstreetmap.josm.gui.widgets.JosmTextField; 077import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 078import org.openstreetmap.josm.tools.ImageProvider; 079import org.openstreetmap.josm.tools.InputMapUtils; 080import org.openstreetmap.josm.tools.Predicate; 081import org.openstreetmap.josm.tools.Shortcut; 082import org.openstreetmap.josm.tools.Utils; 083 084/** 085 * A dialog showing all known relations, with buttons to add, edit, and 086 * delete them. 087 * 088 * We don't have such dialogs for nodes, segments, and ways, because those 089 * objects are visible on the map and can be selected there. Relations are not. 090 */ 091public class RelationListDialog extends ToggleDialog 092 implements DataSetListener, NavigatableComponent.ZoomChangeListener, ExpertToggleAction.ExpertModeChangeListener { 093 /** The display list. */ 094 private final JList<Relation> displaylist; 095 /** the list model used */ 096 private final RelationListModel model; 097 098 private final NewAction newAction; 099 100 /** the popup menu and its handler */ 101 private final JPopupMenu popupMenu = new JPopupMenu(); 102 private final transient PopupMenuHandler popupMenuHandler = new PopupMenuHandler(popupMenu); 103 104 private final JosmTextField filter; 105 106 // Actions 107 /** the edit action */ 108 private final EditRelationAction editAction = new EditRelationAction(); 109 /** the delete action */ 110 private final DeleteRelationsAction deleteRelationsAction = new DeleteRelationsAction(); 111 /** the duplicate action */ 112 private final DuplicateRelationAction duplicateAction = new DuplicateRelationAction(); 113 private final DownloadMembersAction downloadMembersAction = new DownloadMembersAction(); 114 private final DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = 115 new DownloadSelectedIncompleteMembersAction(); 116 private final SelectMembersAction selectMembersAction = new SelectMembersAction(false); 117 private final SelectMembersAction addMembersToSelectionAction = new SelectMembersAction(true); 118 private final SelectRelationAction selectRelationAction = new SelectRelationAction(false); 119 private final SelectRelationAction addRelationToSelectionAction = new SelectRelationAction(true); 120 /** add all selected primitives to the given relations */ 121 private final AddSelectionToRelations addSelectionToRelations = new AddSelectionToRelations(); 122 private transient JMenuItem addSelectionToRelationMenuItem; 123 124 private final transient HighlightHelper highlightHelper = new HighlightHelper(); 125 private final boolean highlightEnabled = Main.pref.getBoolean("draw.target-highlight", true); 126 private final transient RecentRelationsAction recentRelationsAction; 127 128 /** 129 * Constructs <code>RelationListDialog</code> 130 */ 131 public RelationListDialog() { 132 super(tr("Relations"), "relationlist", tr("Open a list of all relations."), 133 Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")), 134 KeyEvent.VK_R, Shortcut.ALT_SHIFT), 150, true); 135 136 // create the list of relations 137 // 138 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel(); 139 model = new RelationListModel(selectionModel); 140 displaylist = new JList<>(model); 141 displaylist.setSelectionModel(selectionModel); 142 displaylist.setCellRenderer(new OsmPrimitivRenderer() { 143 /** 144 * Don't show the default tooltip in the relation list. 145 */ 146 @Override 147 protected String getComponentToolTipText(OsmPrimitive value) { 148 return null; 149 } 150 }); 151 displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 152 displaylist.addMouseListener(new MouseEventHandler()); 153 154 // the new action 155 // 156 newAction = new NewAction(); 157 158 filter = setupFilter(); 159 160 displaylist.addListSelectionListener(new ListSelectionListener() { 161 @Override 162 public void valueChanged(ListSelectionEvent e) { 163 updateActionsRelationLists(); 164 } 165 }); 166 167 // Setup popup menu handler 168 setupPopupMenuHandler(); 169 170 JPanel pane = new JPanel(new BorderLayout()); 171 pane.add(filter, BorderLayout.NORTH); 172 pane.add(new JScrollPane(displaylist), BorderLayout.CENTER); 173 174 SideButton editButton = new SideButton(editAction, false); 175 recentRelationsAction = new RecentRelationsAction(editButton); 176 177 createLayout(pane, false, Arrays.asList(new SideButton[]{ 178 new SideButton(newAction, false), 179 editButton, 180 new SideButton(duplicateAction, false), 181 new SideButton(deleteRelationsAction, false), 182 new SideButton(selectRelationAction, false) 183 })); 184 185 InputMapUtils.unassignCtrlShiftUpDown(displaylist, JComponent.WHEN_FOCUSED); 186 187 // Select relation on Enter 188 InputMapUtils.addEnterAction(displaylist, selectRelationAction); 189 190 // Edit relation on Ctrl-Enter 191 displaylist.getActionMap().put("edit", editAction); 192 displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK), "edit"); 193 194 // Do not hide copy action because of default JList override (fix #9815) 195 displaylist.getActionMap().put("copy", Main.main.menu.copy); 196 displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, GuiHelper.getMenuShortcutKeyMaskEx()), "copy"); 197 198 updateActionsRelationLists(); 199 } 200 201 public void enableRecentRelations() { 202 recentRelationsAction.enableArrow(); 203 } 204 205 // inform all actions about list of relations they need 206 private void updateActionsRelationLists() { 207 List<Relation> sel = model.getSelectedRelations(); 208 popupMenuHandler.setPrimitives(sel); 209 210 Component focused = FocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 211 212 //update highlights 213 if (highlightEnabled && focused == displaylist && Main.isDisplayingMapView()) { 214 if (highlightHelper.highlightOnly(sel)) { 215 Main.map.mapView.repaint(); 216 } 217 } 218 } 219 220 @Override 221 public void showNotify() { 222 MapView.addLayerChangeListener(newAction); 223 MapView.addZoomChangeListener(this); 224 newAction.updateEnabledState(); 225 DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT); 226 DataSet.addSelectionListener(addSelectionToRelations); 227 dataChanged(null); 228 ExpertToggleAction.addExpertModeChangeListener(this); 229 expertChanged(ExpertToggleAction.isExpert()); 230 } 231 232 @Override 233 public void hideNotify() { 234 MapView.removeLayerChangeListener(newAction); 235 MapView.removeZoomChangeListener(this); 236 DatasetEventManager.getInstance().removeDatasetListener(this); 237 DataSet.removeSelectionListener(addSelectionToRelations); 238 ExpertToggleAction.removeExpertModeChangeListener(this); 239 } 240 241 private void resetFilter() { 242 filter.setText(null); 243 } 244 245 /** 246 * Initializes the relation list dialog from a layer. If <code>layer</code> is null 247 * or if it isn't an {@link OsmDataLayer} the dialog is reset to an empty dialog. 248 * Otherwise it is initialized with the list of non-deleted and visible relations 249 * in the layer's dataset. 250 * 251 * @param layer the layer. May be null. 252 */ 253 protected void initFromLayer(Layer layer) { 254 if (!(layer instanceof OsmDataLayer)) { 255 model.setRelations(null); 256 return; 257 } 258 OsmDataLayer l = (OsmDataLayer) layer; 259 model.setRelations(l.data.getRelations()); 260 model.updateTitle(); 261 updateActionsRelationLists(); 262 } 263 264 /** 265 * @return The selected relation in the list 266 */ 267 private Relation getSelected() { 268 if (model.getSize() == 1) { 269 displaylist.setSelectedIndex(0); 270 } 271 return displaylist.getSelectedValue(); 272 } 273 274 /** 275 * Selects the relation <code>relation</code> in the list of relations. 276 * 277 * @param relation the relation 278 */ 279 public void selectRelation(Relation relation) { 280 selectRelations(Collections.singleton(relation)); 281 } 282 283 /** 284 * Selects the relations in the list of relations. 285 * @param relations the relations to be selected 286 */ 287 public void selectRelations(Collection<Relation> relations) { 288 if (relations == null || relations.isEmpty()) { 289 model.setSelectedRelations(null); 290 } else { 291 model.setSelectedRelations(relations); 292 Integer i = model.getVisibleRelationIndex(relations.iterator().next()); 293 if (i != null) { 294 // Not all relations have to be in the list 295 // (for example when the relation list is hidden, it's not updated with new relations) 296 displaylist.scrollRectToVisible(displaylist.getCellBounds(i, i)); 297 } 298 } 299 } 300 301 private JosmTextField setupFilter() { 302 final JosmTextField f = new DisableShortcutsOnFocusGainedTextField(); 303 f.setToolTipText(tr("Relation list filter")); 304 final CompileSearchTextDecorator decorator = CompileSearchTextDecorator.decorate(f); 305 f.addPropertyChangeListener("filter", new PropertyChangeListener() { 306 @Override 307 public void propertyChange(PropertyChangeEvent evt) { 308 model.setFilter(decorator.getMatch()); 309 } 310 }); 311 return f; 312 } 313 314 class MouseEventHandler extends PopupMenuLauncher { 315 316 MouseEventHandler() { 317 super(popupMenu); 318 } 319 320 @Override 321 public void mouseExited(MouseEvent me) { 322 if (highlightEnabled) highlightHelper.clear(); 323 } 324 325 protected void setCurrentRelationAsSelection() { 326 Main.main.getCurrentDataSet().setSelected(displaylist.getSelectedValue()); 327 } 328 329 protected void editCurrentRelation() { 330 EditRelationAction.launchEditor(getSelected()); 331 } 332 333 @Override 334 public void mouseClicked(MouseEvent e) { 335 if (!Main.main.hasEditLayer()) return; 336 if (isDoubleClick(e)) { 337 if (e.isControlDown()) { 338 editCurrentRelation(); 339 } else { 340 setCurrentRelationAsSelection(); 341 } 342 } 343 } 344 } 345 346 /** 347 * The action for creating a new relation 348 * 349 */ 350 static class NewAction extends AbstractAction implements LayerChangeListener { 351 NewAction() { 352 putValue(SHORT_DESCRIPTION, tr("Create a new relation")); 353 putValue(NAME, tr("New")); 354 putValue(SMALL_ICON, ImageProvider.get("dialogs", "addrelation")); 355 updateEnabledState(); 356 } 357 358 public void run() { 359 RelationEditor.getEditor(Main.main.getEditLayer(), null, null).setVisible(true); 360 } 361 362 @Override 363 public void actionPerformed(ActionEvent e) { 364 run(); 365 } 366 367 protected void updateEnabledState() { 368 setEnabled(Main.main != null && Main.main.hasEditLayer()); 369 } 370 371 @Override 372 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 373 updateEnabledState(); 374 } 375 376 @Override 377 public void layerAdded(Layer newLayer) { 378 updateEnabledState(); 379 } 380 381 @Override 382 public void layerRemoved(Layer oldLayer) { 383 updateEnabledState(); 384 } 385 } 386 387 /** 388 * The list model for the list of relations displayed in the relation list dialog. 389 * 390 */ 391 private class RelationListModel extends AbstractListModel<Relation> { 392 private final transient List<Relation> relations = new ArrayList<>(); 393 private transient List<Relation> filteredRelations; 394 private final DefaultListSelectionModel selectionModel; 395 private transient SearchCompiler.Match filter; 396 397 RelationListModel(DefaultListSelectionModel selectionModel) { 398 this.selectionModel = selectionModel; 399 } 400 401 public void sort() { 402 Collections.sort( 403 relations, 404 DefaultNameFormatter.getInstance().getRelationComparator() 405 ); 406 } 407 408 private boolean isValid(Relation r) { 409 return !r.isDeleted() && r.isVisible() && !r.isIncomplete(); 410 } 411 412 public void setRelations(Collection<Relation> relations) { 413 List<Relation> sel = getSelectedRelations(); 414 this.relations.clear(); 415 this.filteredRelations = null; 416 if (relations == null) { 417 selectionModel.clearSelection(); 418 fireContentsChanged(this, 0, getSize()); 419 return; 420 } 421 for (Relation r: relations) { 422 if (isValid(r)) { 423 this.relations.add(r); 424 } 425 } 426 sort(); 427 updateFilteredRelations(); 428 fireIntervalAdded(this, 0, getSize()); 429 setSelectedRelations(sel); 430 } 431 432 /** 433 * Add all relations in <code>addedPrimitives</code> to the model for the 434 * relation list dialog 435 * 436 * @param addedPrimitives the collection of added primitives. May include nodes, 437 * ways, and relations. 438 */ 439 public void addRelations(Collection<? extends OsmPrimitive> addedPrimitives) { 440 boolean added = false; 441 for (OsmPrimitive p: addedPrimitives) { 442 if (!(p instanceof Relation)) { 443 continue; 444 } 445 446 Relation r = (Relation) p; 447 if (relations.contains(r)) { 448 continue; 449 } 450 if (isValid(r)) { 451 relations.add(r); 452 added = true; 453 } 454 } 455 if (added) { 456 List<Relation> sel = getSelectedRelations(); 457 sort(); 458 updateFilteredRelations(); 459 fireIntervalAdded(this, 0, getSize()); 460 setSelectedRelations(sel); 461 } 462 } 463 464 /** 465 * Removes all relations in <code>removedPrimitives</code> from the model 466 * 467 * @param removedPrimitives the removed primitives. May include nodes, ways, 468 * and relations 469 */ 470 public void removeRelations(Collection<? extends OsmPrimitive> removedPrimitives) { 471 if (removedPrimitives == null) return; 472 // extract the removed relations 473 // 474 Set<Relation> removedRelations = new HashSet<>(); 475 for (OsmPrimitive p: removedPrimitives) { 476 if (!(p instanceof Relation)) { 477 continue; 478 } 479 removedRelations.add((Relation) p); 480 } 481 if (removedRelations.isEmpty()) 482 return; 483 int size = relations.size(); 484 relations.removeAll(removedRelations); 485 if (filteredRelations != null) { 486 filteredRelations.removeAll(removedRelations); 487 } 488 if (size != relations.size()) { 489 List<Relation> sel = getSelectedRelations(); 490 sort(); 491 fireContentsChanged(this, 0, getSize()); 492 setSelectedRelations(sel); 493 } 494 } 495 496 private void updateFilteredRelations() { 497 if (filter != null) { 498 filteredRelations = new ArrayList<>(Utils.filter(relations, new Predicate<Relation>() { 499 @Override 500 public boolean evaluate(Relation r) { 501 return filter.match(r); 502 } 503 })); 504 } else if (filteredRelations != null) { 505 filteredRelations = null; 506 } 507 } 508 509 public void setFilter(final SearchCompiler.Match filter) { 510 this.filter = filter; 511 updateFilteredRelations(); 512 List<Relation> sel = getSelectedRelations(); 513 fireContentsChanged(this, 0, getSize()); 514 setSelectedRelations(sel); 515 updateTitle(); 516 } 517 518 private List<Relation> getVisibleRelations() { 519 return filteredRelations == null ? relations : filteredRelations; 520 } 521 522 private Relation getVisibleRelation(int index) { 523 if (index < 0 || index >= getVisibleRelations().size()) return null; 524 return getVisibleRelations().get(index); 525 } 526 527 @Override 528 public Relation getElementAt(int index) { 529 return getVisibleRelation(index); 530 } 531 532 @Override 533 public int getSize() { 534 return getVisibleRelations().size(); 535 } 536 537 /** 538 * Replies the list of selected relations. Empty list, 539 * if there are no selected relations. 540 * 541 * @return the list of selected, non-new relations. 542 */ 543 public List<Relation> getSelectedRelations() { 544 List<Relation> ret = new ArrayList<>(); 545 for (int i = 0; i < getSize(); i++) { 546 if (!selectionModel.isSelectedIndex(i)) { 547 continue; 548 } 549 ret.add(getVisibleRelation(i)); 550 } 551 return ret; 552 } 553 554 /** 555 * Sets the selected relations. 556 * 557 * @param sel the list of selected relations 558 */ 559 public void setSelectedRelations(Collection<Relation> sel) { 560 selectionModel.clearSelection(); 561 if (sel == null || sel.isEmpty()) 562 return; 563 if (!getVisibleRelations().containsAll(sel)) { 564 resetFilter(); 565 } 566 for (Relation r: sel) { 567 Integer i = getVisibleRelationIndex(r); 568 if (i != null) { 569 selectionModel.addSelectionInterval(i, i); 570 } 571 } 572 } 573 574 private Integer getVisibleRelationIndex(Relation rel) { 575 int i = getVisibleRelations().indexOf(rel); 576 if (i < 0) 577 return null; 578 return i; 579 } 580 581 public void updateTitle() { 582 if (!relations.isEmpty() && relations.size() != getSize()) { 583 RelationListDialog.this.setTitle(tr("Relations: {0}/{1}", getSize(), relations.size())); 584 } else if (getSize() > 0) { 585 RelationListDialog.this.setTitle(tr("Relations: {0}", getSize())); 586 } else { 587 RelationListDialog.this.setTitle(tr("Relations")); 588 } 589 } 590 } 591 592 private void setupPopupMenuHandler() { 593 594 // -- select action 595 popupMenuHandler.addAction(selectRelationAction); 596 popupMenuHandler.addAction(addRelationToSelectionAction); 597 598 // -- select members action 599 popupMenuHandler.addAction(selectMembersAction); 600 popupMenuHandler.addAction(addMembersToSelectionAction); 601 602 popupMenuHandler.addSeparator(); 603 // -- download members action 604 popupMenuHandler.addAction(downloadMembersAction); 605 606 // -- download incomplete members action 607 popupMenuHandler.addAction(downloadSelectedIncompleteMembersAction); 608 609 popupMenuHandler.addSeparator(); 610 popupMenuHandler.addAction(editAction).setVisible(false); 611 popupMenuHandler.addAction(duplicateAction).setVisible(false); 612 popupMenuHandler.addAction(deleteRelationsAction).setVisible(false); 613 614 addSelectionToRelationMenuItem = popupMenuHandler.addAction(addSelectionToRelations); 615 } 616 617 /* ---------------------------------------------------------------------------------- */ 618 /* Methods that can be called from plugins */ 619 /* ---------------------------------------------------------------------------------- */ 620 621 /** 622 * Replies the popup menu handler. 623 * @return The popup menu handler 624 */ 625 public PopupMenuHandler getPopupMenuHandler() { 626 return popupMenuHandler; 627 } 628 629 /** 630 * Replies the list of selected relations. Empty list, if there are no selected relations. 631 * @return the list of selected, non-new relations. 632 */ 633 public Collection<Relation> getSelectedRelations() { 634 return model.getSelectedRelations(); 635 } 636 637 /* ---------------------------------------------------------------------------------- */ 638 /* DataSetListener */ 639 /* ---------------------------------------------------------------------------------- */ 640 641 @Override 642 public void nodeMoved(NodeMovedEvent event) { 643 /* irrelevant in this context */ 644 } 645 646 @Override 647 public void wayNodesChanged(WayNodesChangedEvent event) { 648 /* irrelevant in this context */ 649 } 650 651 @Override 652 public void primitivesAdded(final PrimitivesAddedEvent event) { 653 model.addRelations(event.getPrimitives()); 654 model.updateTitle(); 655 } 656 657 @Override 658 public void primitivesRemoved(final PrimitivesRemovedEvent event) { 659 model.removeRelations(event.getPrimitives()); 660 model.updateTitle(); 661 } 662 663 @Override 664 public void relationMembersChanged(final RelationMembersChangedEvent event) { 665 List<Relation> sel = model.getSelectedRelations(); 666 model.sort(); 667 model.setSelectedRelations(sel); 668 displaylist.repaint(); 669 } 670 671 @Override 672 public void tagsChanged(TagsChangedEvent event) { 673 OsmPrimitive prim = event.getPrimitive(); 674 if (!(prim instanceof Relation)) 675 return; 676 // trigger a sort of the relation list because the display name may have changed 677 // 678 List<Relation> sel = model.getSelectedRelations(); 679 model.sort(); 680 model.setSelectedRelations(sel); 681 displaylist.repaint(); 682 } 683 684 @Override 685 public void dataChanged(DataChangedEvent event) { 686 initFromLayer(Main.main.getEditLayer()); 687 } 688 689 @Override 690 public void otherDatasetChange(AbstractDatasetChangedEvent event) { 691 /* ignore */ 692 } 693 694 @Override 695 public void zoomChanged() { 696 // re-filter relations 697 if (model.filter != null) { 698 model.setFilter(model.filter); 699 } 700 } 701 702 @Override 703 public void expertChanged(boolean isExpert) { 704 addSelectionToRelationMenuItem.setVisible(isExpert); 705 } 706}