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

Last change on this file since 13453 was 13453, checked in by Don-vip, 6 years ago

fix #8039, fix #10456: final fixes for the read-only/locked layers:

  • rename "read-only" to "locked" (in XML and Java classes/interfaces)
  • add a new download policy (true/never) to allow private layers forbidding only to download data, but allowing everything else

This leads to:

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