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