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

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

fix #8424 - "Select" and "Delete" actions operate on wrong relations when filter is used in relation toggle dialog

  • Property svn:eol-style set to native
File size: 36.1 KB
RevLine 
[3719]1// License: GPL. For details, see LICENSE file.
[626]2package org.openstreetmap.josm.gui.dialogs;
3
[2317]4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
[626]5import static org.openstreetmap.josm.tools.I18n.tr;
[5082]6import static org.openstreetmap.josm.tools.I18n.trn;
[626]7
[5616]8import java.awt.BorderLayout;
9import java.awt.Color;
[2317]10import java.awt.Point;
[626]11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
[2126]15import java.util.ArrayList;
[4353]16import java.util.Arrays;
[1886]17import java.util.Collection;
[2126]18import java.util.Collections;
[1886]19import java.util.HashSet;
[3102]20import java.util.Iterator;
[4885]21import java.util.LinkedList;
[2317]22import java.util.List;
[2487]23import java.util.Set;
[626]24
[1778]25import javax.swing.AbstractAction;
[2126]26import javax.swing.AbstractListModel;
[4459]27import javax.swing.Action;
[2317]28import javax.swing.DefaultListSelectionModel;
[5200]29import javax.swing.JComponent;
[626]30import javax.swing.JList;
[4459]31import javax.swing.JMenuItem;
[5616]32import javax.swing.JPanel;
33import javax.swing.JScrollPane;
34import javax.swing.JTextField;
[5200]35import javax.swing.KeyStroke;
[626]36import javax.swing.ListSelectionModel;
[1916]37import javax.swing.SwingUtilities;
[5616]38import javax.swing.UIManager;
39import javax.swing.event.DocumentEvent;
40import javax.swing.event.DocumentListener;
[1419]41import javax.swing.event.ListSelectionEvent;
[997]42import javax.swing.event.ListSelectionListener;
[4536]43import javax.swing.event.PopupMenuListener;
[1778]44
[626]45import org.openstreetmap.josm.Main;
[5616]46import org.openstreetmap.josm.actions.search.SearchCompiler;
[5082]47import org.openstreetmap.josm.command.Command;
48import org.openstreetmap.josm.command.SequenceCommand;
49import org.openstreetmap.josm.data.SelectionChangedListener;
50import org.openstreetmap.josm.data.osm.DataSet;
[755]51import org.openstreetmap.josm.data.osm.OsmPrimitive;
[626]52import org.openstreetmap.josm.data.osm.Relation;
[1886]53import org.openstreetmap.josm.data.osm.RelationMember;
[2655]54import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
[2622]55import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
56import org.openstreetmap.josm.data.osm.event.DataSetListener;
57import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
[4070]58import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
[2622]59import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
60import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
61import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
62import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
63import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
64import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
[2126]65import org.openstreetmap.josm.gui.DefaultNameFormatter;
[2621]66import org.openstreetmap.josm.gui.MapView;
[4070]67import org.openstreetmap.josm.gui.MapView.LayerChangeListener;
[626]68import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
[2710]69import org.openstreetmap.josm.gui.SideButton;
[3102]70import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
[2563]71import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationTask;
[5082]72import org.openstreetmap.josm.gui.dialogs.relation.GenericRelationEditor;
[1599]73import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
[626]74import org.openstreetmap.josm.gui.layer.Layer;
75import org.openstreetmap.josm.gui.layer.OsmDataLayer;
[5696]76import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
[4459]77import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
[1778]78import org.openstreetmap.josm.tools.ImageProvider;
[5200]79import org.openstreetmap.josm.tools.InputMapUtils;
[5616]80import org.openstreetmap.josm.tools.Predicate;
[1084]81import org.openstreetmap.josm.tools.Shortcut;
[5616]82import org.openstreetmap.josm.tools.Utils;
[626]83
84/**
85 * A dialog showing all known relations, with buttons to add, edit, and
[1023]86 * delete them.
87 *
[1554]88 * We don't have such dialogs for nodes, segments, and ways, because those
[626]89 * objects are visible on the map and can be selected there. Relations are not.
90 */
[2623]91public class RelationListDialog extends ToggleDialog implements DataSetListener {
[2126]92 /** The display list. */
93 private JList displaylist;
94 /** the list model used */
95 private RelationListModel model;
[626]96
[1778]97 /** the edit action */
98 private EditAction editAction;
99 /** the delete action */
100 private DeleteAction deleteAction;
[2485]101 private NewAction newAction;
[5082]102 private AddToRelation addToRelation;
[2317]103 /** the popup menu */
104 private RelationDialogPopupMenu popupMenu;
[1677]105
[1778]106 /**
107 * constructor
108 */
[1169]109 public RelationListDialog() {
110 super(tr("Relations"), "relationlist", tr("Open a list of all relations."),
[4957]111 Shortcut.registerShortcut("subwindow:relations", tr("Toggle: {0}", tr("Relations")),
[4982]112 KeyEvent.VK_R, Shortcut.ALT_SHIFT), 150);
[1778]113
114 // create the list of relations
115 //
[2317]116 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
117 model = new RelationListModel(selectionModel);
[2126]118 displaylist = new JList(model);
[2317]119 displaylist.setSelectionModel(selectionModel);
[2885]120 displaylist.setCellRenderer(new OsmPrimitivRenderer() {
121 /**
122 * Don't show the default tooltip in the relation list.
123 */
124 @Override
125 protected String getComponentToolTipText(OsmPrimitive value) {
126 return null;
127 }
128 });
[2194]129 displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
[2317]130 displaylist.addMouseListener(new MouseEventHandler());
[626]131
[1866]132 // the new action
133 //
[2485]134 newAction = new NewAction();
[1866]135
[1778]136 // the edit action
137 //
138 editAction = new EditAction();
139 displaylist.addListSelectionListener(editAction);
[1023]140
[2194]141 // the duplicate action
[1916]142 //
143 DuplicateAction duplicateAction = new DuplicateAction();
144 displaylist.addListSelectionListener(duplicateAction);
145
[1778]146 // the delete action
147 //
148 deleteAction = new DeleteAction();
149 displaylist.addListSelectionListener(deleteAction);
[2194]150
151 // the select action
152 //
[4158]153 SelectAction selectAction = new SelectAction(false);
[2194]154 displaylist.addListSelectionListener(selectAction);
155
[5696]156 final JTextField filter = new DisableShortcutsOnFocusGainedTextField();
[5616]157 filter.setToolTipText(tr("Relation list filter"));
158 filter.getDocument().addDocumentListener(new DocumentListener() {
159
160 private void setFilter() {
161 try {
162 filter.setBackground(UIManager.getColor("TextField.background"));
163 filter.setToolTipText(tr("Relation list filter"));
164 model.setFilter(SearchCompiler.compile(filter.getText(), false, false));
165 } catch (SearchCompiler.ParseError ex) {
166 filter.setBackground(new Color(255, 224, 224));
167 filter.setToolTipText(ex.getMessage());
168 model.setFilter(new SearchCompiler.Always());
169 }
170 }
171
172 @Override
173 public void insertUpdate(DocumentEvent e) {
174 setFilter();
175 }
176
177 @Override
178 public void removeUpdate(DocumentEvent e) {
179 setFilter();
180 }
181
182 @Override
183 public void changedUpdate(DocumentEvent e) {
184 setFilter();
185 }
186 });
187
188 JPanel pane = new JPanel(new BorderLayout());
189 pane.add(filter, BorderLayout.NORTH);
190 pane.add(new JScrollPane(displaylist), BorderLayout.CENTER);
191 createLayout(pane, false, Arrays.asList(new SideButton[]{
[5028]192 new SideButton(newAction, false),
193 new SideButton(editAction, false),
194 new SideButton(duplicateAction, false),
195 new SideButton(deleteAction, false),
196 new SideButton(selectAction, false)
[4353]197 }));
[2487]198
199 // activate DEL in the list of relations
[4957]200 //displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0), "deleteRelation");
201 //displaylist.getActionMap().put("deleteRelation", deleteAction);
[1677]202
[5200]203 InputMapUtils.unassignCtrlShiftUpDown(displaylist, JComponent.WHEN_FOCUSED);
204
205 // Select relation on Ctrl-Enter
206 InputMapUtils.addEnterAction(displaylist, selectAction);
207
[5082]208 addToRelation = new AddToRelation();
[4459]209 popupMenu = new RelationDialogPopupMenu(displaylist);
[5200]210
211 // Edit relation on Ctrl-Enter
212 displaylist.getActionMap().put("edit", editAction);
213 displaylist.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_MASK), "edit");
[2485]214 }
[2317]215
[2485]216 @Override public void showNotify() {
[2621]217 MapView.addLayerChangeListener(newAction);
[2691]218 newAction.updateEnabledState();
[2741]219 DatasetEventManager.getInstance().addDatasetListener(this, FireMode.IN_EDT);
[5082]220 DataSet.addSelectionListener(addToRelation);
[2623]221 dataChanged(null);
[1169]222 }
[626]223
[2485]224 @Override public void hideNotify() {
[2621]225 MapView.removeLayerChangeListener(newAction);
[2622]226 DatasetEventManager.getInstance().removeDatasetListener(this);
[5082]227 DataSet.removeSelectionListener(addToRelation);
[1169]228 }
[1023]229
[2126]230 /**
[2487]231 * Initializes the relation list dialog from a layer. If <code>layer</code> is null
[5266]232 * or if it isn't an {@link OsmDataLayer} the dialog is reset to an empty dialog.
[2487]233 * Otherwise it is initialized with the list of non-deleted and visible relations
234 * in the layer's dataset.
[2512]235 *
[2487]236 * @param layer the layer. May be null.
[2126]237 */
[2487]238 protected void initFromLayer(Layer layer) {
239 if (layer == null || ! (layer instanceof OsmDataLayer)) {
[2126]240 model.setRelations(null);
[1897]241 return;
242 }
[2487]243 OsmDataLayer l = (OsmDataLayer)layer;
244 model.setRelations(l.data.getRelations());
[3020]245 model.updateTitle();
[1169]246 }
[1023]247
[1169]248 /**
249 * Adds a selection listener to the relation list.
250 *
251 * @param listener the listener to add
252 */
253 public void addListSelectionListener(ListSelectionListener listener) {
254 displaylist.addListSelectionListener(listener);
255 }
[997]256
[1169]257 /**
258 * Removes a selection listener from the relation list.
259 *
260 * @param listener the listener to remove
261 */
262 public void removeListSelectionListener(ListSelectionListener listener) {
263 displaylist.removeListSelectionListener(listener);
264 }
[1677]265
[1419]266 /**
267 * @return The selected relation in the list
268 */
269 private Relation getSelected() {
[2126]270 if(model.getSize() == 1) {
[1419]271 displaylist.setSelectedIndex(0);
[1763]272 }
[1419]273 return (Relation) displaylist.getSelectedValue();
274 }
[1763]275
276 /**
277 * Selects the relation <code>relation</code> in the list of relations.
[1897]278 *
[1763]279 * @param relation the relation
280 */
281 public void selectRelation(Relation relation) {
[4795]282 selectRelations(Collections.singleton(relation));
283 }
284
285 /**
286 * Selects the relations in the list of relations.
287 * @param relations the relations to be selected
288 */
289 public void selectRelations(Collection<Relation> relations) {
290 if (relations == null || relations.isEmpty()) {
[2487]291 model.setSelectedRelations(null);
[2126]292 } else {
[4795]293 model.setSelectedRelations(relations);
294 Integer i = model.getRelationIndex(relations.iterator().next());
[2914]295 if (i != null) { // Not all relations have to be in the list (for example when the relation list is hidden, it's not updated with new relations)
296 displaylist.scrollRectToVisible(displaylist.getCellBounds(i, i));
297 }
[1780]298 }
[1763]299 }
[1778]300
[2317]301 class MouseEventHandler extends MouseAdapter {
[1916]302 protected void setCurrentRelationAsSelection() {
303 Main.main.getCurrentDataSet().setSelected((Relation)displaylist.getSelectedValue());
304 }
305
306 protected void editCurrentRelation() {
307 new EditAction().launchEditor(getSelected());
308 }
309
310 @Override public void mouseClicked(MouseEvent e) {
[3102]311 if (Main.main.getEditLayer() == null) return;
[1916]312 if (e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e)) {
313 if (e.isControlDown()) {
314 editCurrentRelation();
315 } else {
316 setCurrentRelationAsSelection();
317 }
318 }
319 }
[2317]320 private void openPopup(MouseEvent e) {
321 Point p = e.getPoint();
322 int index = displaylist.locationToIndex(p);
323 if (index < 0) return;
324 if (!displaylist.getCellBounds(index, index).contains(e.getPoint()))
325 return;
326 if (! displaylist.isSelectedIndex(index)) {
327 displaylist.setSelectedIndex(index);
328 }
[2563]329 popupMenu.show(displaylist, p.x, p.y-3);
[2317]330 }
331 @Override public void mousePressed(MouseEvent e) {
[3102]332 if (Main.main.getEditLayer() == null) return;
[2317]333 if (e.isPopupTrigger()) {
334 openPopup(e);
335 }
336 }
337 @Override public void mouseReleased(MouseEvent e) {
[3102]338 if (Main.main.getEditLayer() == null) return;
[2317]339 if (e.isPopupTrigger()) {
340 openPopup(e);
341 }
342 }
[1916]343 }
344
[1778]345 /**
346 * The edit action
347 *
348 */
[1916]349 class EditAction extends AbstractAction implements ListSelectionListener{
[1778]350 public EditAction() {
351 putValue(SHORT_DESCRIPTION,tr( "Open an editor for the selected relation"));
[5028]352 putValue(NAME, tr("Edit"));
[1778]353 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
354 setEnabled(false);
355 }
[1886]356 protected Collection<RelationMember> getMembersForCurrentSelection(Relation r) {
357 Collection<RelationMember> members = new HashSet<RelationMember>();
358 Collection<OsmPrimitive> selection = Main.map.mapView.getEditLayer().data.getSelected();
[1925]359 for (RelationMember member: r.getMembers()) {
[1938]360 if (selection.contains(member.getMember())) {
[1886]361 members.add(member);
362 }
363 }
364 return members;
365 }
[1778]366
[1916]367 public void launchEditor(Relation toEdit) {
[1778]368 if (toEdit == null)
369 return;
[1886]370 RelationEditor.getEditor(Main.map.mapView.getEditLayer(),toEdit, getMembersForCurrentSelection(toEdit)).setVisible(true);
[1778]371 }
372
373 public void actionPerformed(ActionEvent e) {
[1916]374 if (!isEnabled())
375 return;
376 launchEditor(getSelected());
[1778]377 }
378
379 public void valueChanged(ListSelectionEvent e) {
[2194]380 setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length == 1);
[1778]381 }
382 }
383
384 /**
385 * The delete action
386 *
387 */
[2194]388 class DeleteAction extends AbstractAction implements ListSelectionListener {
[1856]389 class AbortException extends Exception {}
390
[1778]391 public DeleteAction() {
392 putValue(SHORT_DESCRIPTION,tr("Delete the selected relation"));
[5028]393 putValue(NAME, tr("Delete"));
[1778]394 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
395 setEnabled(false);
396 }
397
[2194]398 protected void deleteRelation(Relation toDelete) {
[1778]399 if (toDelete == null)
400 return;
[1856]401 org.openstreetmap.josm.actions.mapmode.DeleteAction.deleteRelation(
402 Main.main.getEditLayer(),
403 toDelete
[5028]404 );
[1778]405 }
406
407 public void actionPerformed(ActionEvent e) {
[5028]408 if (!isEnabled())
[4885]409 return;
410 List<Relation> toDelete = new LinkedList<Relation>();
411 for (int i : displaylist.getSelectedIndices()) {
[5709]412 toDelete.add(model.getVisibleRelation(i));
[4885]413 }
414 for (Relation r : toDelete) {
415 deleteRelation(r);
416 }
417 displaylist.clearSelection();
[1778]418 }
419
420 public void valueChanged(ListSelectionEvent e) {
421 setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length > 0);
422 }
423 }
[1866]424
425 /**
[2487]426 * The action for creating a new relation
[1866]427 *
428 */
[2632]429 static class NewAction extends AbstractAction implements LayerChangeListener{
[1866]430 public NewAction() {
431 putValue(SHORT_DESCRIPTION,tr("Create a new relation"));
[5028]432 putValue(NAME, tr("New"));
[1866]433 putValue(SMALL_ICON, ImageProvider.get("dialogs", "addrelation"));
[2632]434 updateEnabledState();
[1866]435 }
436
437 public void run() {
[2487]438 RelationEditor.getEditor(Main.main.getEditLayer(),null, null).setVisible(true);
[1866]439 }
440
441 public void actionPerformed(ActionEvent e) {
442 run();
443 }
444
445 protected void updateEnabledState() {
446 setEnabled(Main.main != null && Main.main.getEditLayer() != null);
447 }
448
449 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
450 updateEnabledState();
451 }
452
453 public void layerAdded(Layer newLayer) {
454 updateEnabledState();
455 }
456
457 public void layerRemoved(Layer oldLayer) {
458 updateEnabledState();
459 }
460 }
[1916]461
462 /**
463 * Creates a new relation with a copy of the current editor state
[1925]464 *
[1916]465 */
466 class DuplicateAction extends AbstractAction implements ListSelectionListener {
467 public DuplicateAction() {
468 putValue(SHORT_DESCRIPTION, tr("Create a copy of this relation and open it in another editor window"));
469 putValue(SMALL_ICON, ImageProvider.get("duplicate"));
[5028]470 putValue(NAME, tr("Duplicate"));
[1916]471 updateEnabledState();
472 }
473
474 public void launchEditorForDuplicate(Relation original) {
[2410]475 Relation copy = new Relation(original, true);
[2025]476 copy.setModified(true);
[1916]477 RelationEditor editor = RelationEditor.getEditor(
478 Main.main.getEditLayer(),
479 copy,
480 null /* no selected members */
[5028]481 );
[1916]482 editor.setVisible(true);
483 }
484
485 public void actionPerformed(ActionEvent e) {
486 if (!isEnabled())
487 return;
488 launchEditorForDuplicate(getSelected());
489 }
490
491 protected void updateEnabledState() {
492 setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length == 1);
493 }
494
495 public void valueChanged(ListSelectionEvent e) {
496 updateEnabledState();
497 }
498 }
[2126]499
[2194]500 /**
501 * Sets the current selection to the list of relations selected in this dialog
502 *
503 */
504 class SelectAction extends AbstractAction implements ListSelectionListener{
[4158]505 boolean add;
506 public SelectAction(boolean add) {
507 putValue(SHORT_DESCRIPTION, add ? tr("Add the selected relations to the current selection")
[4221]508 : tr("Set the current selection to the list of selected relations"));
[2194]509 putValue(SMALL_ICON, ImageProvider.get("dialogs", "select"));
[4158]510 putValue(NAME, add ? tr("Select relation (add)") : tr("Select relation"));
511 this.add = add;
512 updateEnabledState();
[2194]513 }
514
515 public void actionPerformed(ActionEvent e) {
516 if (!isEnabled()) return;
517 int [] idx = displaylist.getSelectedIndices();
518 if (idx == null || idx.length == 0) return;
519 ArrayList<OsmPrimitive> selection = new ArrayList<OsmPrimitive>(idx.length);
520 for (int i: idx) {
[5709]521 selection.add(model.getVisibleRelation(i));
[2194]522 }
[4221]523 if(add) {
[4158]524 Main.map.mapView.getEditLayer().data.addSelected(selection);
[4221]525 } else {
[4158]526 Main.map.mapView.getEditLayer().data.setSelected(selection);
[4221]527 }
[2194]528 }
529
[4158]530 protected void updateEnabledState() {
[2194]531 setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length > 0);
532 }
[4158]533
534 public void valueChanged(ListSelectionEvent e) {
535 updateEnabledState();
536 }
[2194]537 }
538
[2317]539 /**
540 * Sets the current selection to the list of relations selected in this dialog
541 *
542 */
543 class SelectMembersAction extends AbstractAction implements ListSelectionListener{
[4158]544 boolean add;
545 public SelectMembersAction(boolean add) {
546 putValue(SHORT_DESCRIPTION,add ? tr("Add the members of all selected relations to current selection")
[4221]547 : tr("Select the members of all selected relations"));
[2317]548 putValue(SMALL_ICON, ImageProvider.get("selectall"));
[4158]549 putValue(NAME, add ? tr("Select members (add)") : tr("Select members"));
550 this.add = add;
[2317]551 updateEnabledState();
552 }
553
554 public void actionPerformed(ActionEvent e) {
555 if (!isEnabled()) return;
556 List<Relation> relations = model.getSelectedRelations();
557 HashSet<OsmPrimitive> members = new HashSet<OsmPrimitive>();
558 for(Relation r: relations) {
559 members.addAll(r.getMemberPrimitives());
560 }
[4221]561 if(add) {
[4158]562 Main.map.mapView.getEditLayer().data.addSelected(members);
[4221]563 } else {
[4158]564 Main.map.mapView.getEditLayer().data.setSelected(members);
[4221]565 }
[2317]566 }
567
568 protected void updateEnabledState() {
569 setEnabled(displaylist.getSelectedIndices() != null && displaylist.getSelectedIndices().length > 0);
570 }
571
572 public void valueChanged(ListSelectionEvent e) {
573 updateEnabledState();
574 }
575 }
576
[2487]577 /**
578 * The action for downloading members of all selected relations
[2512]579 *
[2487]580 */
[2317]581 class DownloadMembersAction extends AbstractAction implements ListSelectionListener{
582
583 public DownloadMembersAction() {
584 putValue(SHORT_DESCRIPTION,tr("Download all members of the selected relations"));
585 putValue(NAME, tr("Download members"));
586 putValue(SMALL_ICON, ImageProvider.get("dialogs", "downloadincomplete"));
587 putValue("help", ht("/Dialog/RelationList#DownloadMembers"));
588 updateEnabledState();
589 }
590
591 protected void updateEnabledState() {
592 setEnabled(! model.getSelectedNonNewRelations().isEmpty());
593 }
594
595 public void valueChanged(ListSelectionEvent e) {
596 updateEnabledState();
597 }
598
599 public void actionPerformed(ActionEvent e) {
600 List<Relation> relations = model.getSelectedNonNewRelations();
601 if (relations.isEmpty())
602 return;
[2563]603 Main.worker.submit(new DownloadRelationTask(
[2317]604 model.getSelectedNonNewRelations(),
[2563]605 Main.map.mapView.getEditLayer())
[5028]606 );
[2317]607 }
608 }
609
[2487]610 /**
[3102]611 * Action for downloading incomplete members of selected relations
[3530]612 *
[3102]613 */
614 class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener{
615 public DownloadSelectedIncompleteMembersAction() {
616 putValue(SHORT_DESCRIPTION, tr("Download incomplete members of selected relations"));
617 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
618 putValue(NAME, tr("Download incomplete members"));
619 updateEnabledState();
620 }
621
622 public Set<OsmPrimitive> buildSetOfIncompleteMembers(List<Relation> rels) {
623 Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
624 for(Relation r: rels) {
625 ret.addAll(r.getIncompleteMembers());
626 }
627 return ret;
628 }
629
630 public void actionPerformed(ActionEvent e) {
631 if (!isEnabled())
632 return;
633 List<Relation> rels = model.getSelectedRelationsWithIncompleteMembers();
634 if (rels.isEmpty()) return;
635 Main.worker.submit(new DownloadRelationMemberTask(
636 rels,
637 buildSetOfIncompleteMembers(rels),
638 Main.map.mapView.getEditLayer()
[5028]639 ));
[3102]640 }
641
642 protected void updateEnabledState() {
643 setEnabled(!model.getSelectedRelationsWithIncompleteMembers().isEmpty());
644 }
645
646 public void valueChanged(ListSelectionEvent e) {
647 updateEnabledState();
648 }
649 }
650
[5082]651 class AddToRelation extends AbstractAction implements ListSelectionListener, SelectionChangedListener {
652
653 public AddToRelation() {
654 super("", ImageProvider.get("dialogs/conflict", "copyendright"));
655 putValue(SHORT_DESCRIPTION, tr("Add all objects selected in the current dataset after the last member"));
656 setEnabled(false);
657 }
658
659 @Override
660 public void actionPerformed(ActionEvent e) {
661 Collection<Command> cmds = new LinkedList<Command>();
662 for (Relation orig : getSelectedRelations()) {
663 Command c = GenericRelationEditor.addPrimitivesToRelation(orig, Main.main.getCurrentDataSet().getSelected());
664 if (c != null) {
665 cmds.add(c);
666 }
667 }
668 if (!cmds.isEmpty()) {
669 Main.main.undoRedo.add(new SequenceCommand(tr("Add selection to relation"), cmds));
670 }
671 }
672
673 @Override
674 public void valueChanged(ListSelectionEvent e) {
675 putValue(NAME, trn("Add selection to {0} relation", "Add selection to {0} relations",
676 getSelectedRelations().size(), getSelectedRelations().size()));
677 }
678
679 @Override
680 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
681 setEnabled(newSelection != null && !newSelection.isEmpty());
682 }
683 }
684
[3102]685 /**
[2487]686 * The list model for the list of relations displayed in the relation list
687 * dialog.
688 *
689 */
[3020]690 private class RelationListModel extends AbstractListModel {
[2504]691 private final ArrayList<Relation> relations = new ArrayList<Relation>();
[5616]692 private ArrayList<Relation> filteredRelations;
[2317]693 private DefaultListSelectionModel selectionModel;
[5709]694 //private SearchCompiler.Match filter;
[2126]695
[2317]696 public RelationListModel(DefaultListSelectionModel selectionModel) {
697 this.selectionModel = selectionModel;
698 }
699
[2194]700 public Relation getRelation(int idx) {
701 return relations.get(idx);
702 }
703
[2741]704 public void sort() {
[2623]705 Collections.sort(
706 relations,
[4070]707 DefaultNameFormatter.getInstance().getRelationComparator()
[5028]708 );
[2623]709 }
710
711 private boolean isValid(Relation r) {
712 return !r.isDeleted() && r.isVisible() && !r.isIncomplete();
713 }
714
[2741]715 public void setRelations(Collection<Relation> relations) {
[2488]716 List<Relation> sel = getSelectedRelations();
[2504]717 this.relations.clear();
[5619]718 this.filteredRelations = null;
[2506]719 if (relations == null) {
720 selectionModel.clearSelection();
721 fireContentsChanged(this,0,getSize());
722 return;
723
724 }
[2504]725 for (Relation r: relations) {
[2623]726 if (isValid(r)) {
[2504]727 this.relations.add(r);
[2487]728 }
729 }
730 sort();
[2126]731 fireIntervalAdded(this, 0, getSize());
[2488]732 setSelectedRelations(sel);
[2126]733 }
734
[2487]735 /**
736 * Add all relations in <code>addedPrimitives</code> to the model for the
737 * relation list dialog
[2512]738 *
[2487]739 * @param addedPrimitives the collection of added primitives. May include nodes,
740 * ways, and relations.
741 */
[2741]742 public void addRelations(Collection<? extends OsmPrimitive> addedPrimitives) {
[2484]743 boolean added = false;
744 for (OsmPrimitive p: addedPrimitives) {
745 if (! (p instanceof Relation)) {
746 continue;
747 }
[2623]748
749 Relation r = (Relation)p;
750 if (relations.contains(r)) {
[2484]751 continue;
752 }
[2623]753 if (isValid(r)) {
754 relations.add(r);
755 added = true;
756 }
[2484]757 }
758 if (added) {
[2487]759 List<Relation> sel = getSelectedRelations();
760 sort();
[2484]761 fireIntervalAdded(this, 0, getSize());
[2487]762 setSelectedRelations(sel);
[2484]763 }
764 }
765
[2487]766 /**
767 * Removes all relations in <code>removedPrimitives</code> from the model
[2512]768 *
[2487]769 * @param removedPrimitives the removed primitives. May include nodes, ways,
770 * and relations
771 */
[2741]772 public void removeRelations(Collection<? extends OsmPrimitive> removedPrimitives) {
[2487]773 if (removedPrimitives == null) return;
774 // extract the removed relations
775 //
776 Set<Relation> removedRelations = new HashSet<Relation>();
777 for (OsmPrimitive p: removedPrimitives) {
778 if (! (p instanceof Relation)) {
779 continue;
780 }
781 removedRelations.add((Relation)p);
782 }
783 if (removedRelations.isEmpty())
784 return;
785 int size = relations.size();
786 relations.removeAll(removedRelations);
[5709]787 if (filteredRelations != null) {
788 filteredRelations.removeAll(removedRelations);
789 }
[2487]790 if (size != relations.size()) {
791 List<Relation> sel = getSelectedRelations();
792 sort();
793 fireContentsChanged(this, 0, getSize());
794 setSelectedRelations(sel);
795 }
796 }
797
[3102]798 /**
799 * Replies the list of selected relations with incomplete members
[3530]800 *
[3102]801 * @return the list of selected relations with incomplete members
802 */
803 public List<Relation> getSelectedRelationsWithIncompleteMembers() {
804 List<Relation> ret = getSelectedNonNewRelations();
805 Iterator<Relation> it = ret.iterator();
806 while(it.hasNext()) {
807 Relation r = it.next();
808 if (!r.hasIncompleteMembers()) {
809 it.remove();
810 }
811 }
812 return ret;
813 }
814
[5616]815 public void setFilter(final SearchCompiler.Match filter) {
[5709]816 //this.filter = filter;
[5616]817 this.filteredRelations = new ArrayList<Relation>(Utils.filter(relations, new Predicate<Relation>() {
818 @Override
819 public boolean evaluate(Relation r) {
820 return filter.match(r);
821 }
822 }));
823 List<Relation> sel = getSelectedRelations();
824 fireContentsChanged(this, 0, getSize());
825 setSelectedRelations(sel);
826 updateTitle();
827 }
828
829 private List<Relation> getVisibleRelations() {
830 return filteredRelations == null ? relations : filteredRelations;
831 }
[5709]832
833 private Relation getVisibleRelation(int index) {
834 if (index < 0 || index >= getVisibleRelations().size()) return null;
835 return getVisibleRelations().get(index);
836 }
[5616]837
838 @Override
[2126]839 public Object getElementAt(int index) {
[5709]840 return getVisibleRelation(index);
[2126]841 }
842
[5616]843 @Override
[2126]844 public int getSize() {
[5616]845 return getVisibleRelations().size();
[2126]846 }
847
[2317]848 /**
849 * Replies the list of selected, non-new relations. Empty list,
850 * if there are no selected, non-new relations.
[2512]851 *
[2317]852 * @return the list of selected, non-new relations.
853 */
854 public List<Relation> getSelectedNonNewRelations() {
855 ArrayList<Relation> ret = new ArrayList<Relation>();
856 for (int i=0; i<getSize();i++) {
857 if (!selectionModel.isSelectedIndex(i)) {
858 continue;
859 }
860 if (relations.get(i).isNew()) {
861 continue;
862 }
863 ret.add(relations.get(i));
864 }
865 return ret;
866 }
867
868 /**
869 * Replies the list of selected relations. Empty list,
870 * if there are no selected relations.
[2512]871 *
[2317]872 * @return the list of selected, non-new relations.
873 */
874 public List<Relation> getSelectedRelations() {
875 ArrayList<Relation> ret = new ArrayList<Relation>();
876 for (int i=0; i<getSize();i++) {
877 if (!selectionModel.isSelectedIndex(i)) {
878 continue;
879 }
880 ret.add(relations.get(i));
881 }
882 return ret;
883 }
[2487]884
885 /**
886 * Sets the selected relations.
[2512]887 *
[5709]888 * @param sel the list of selected relations
[2487]889 */
[4795]890 public void setSelectedRelations(Collection<Relation> sel) {
[2487]891 selectionModel.clearSelection();
[2506]892 if (sel == null || sel.isEmpty())
[2487]893 return;
894 for (Relation r: sel) {
895 int i = relations.indexOf(r);
896 if (i<0) {
897 continue;
898 }
899 selectionModel.addSelectionInterval(i,i);
900 }
901 }
[2894]902
903 /**
904 * Returns the index of the relation
[5709]905 * @param rel The relation to look for
[2894]906 *
[2914]907 * @return index of relation (null if it cannot be found)
[2894]908 */
909 public Integer getRelationIndex(Relation rel) {
910 int i = relations.indexOf(rel);
[2914]911 if (i<0)
[2894]912 return null;
913 return i;
914 }
[2914]915
[3020]916 public void updateTitle() {
[5616]917 if (relations.size() > 0 && relations.size() != getSize()) {
918 RelationListDialog.this.setTitle(tr("Relations: {0}/{1}", getSize(), relations.size()));
919 } else if (getSize() > 0) {
[3020]920 RelationListDialog.this.setTitle(tr("Relations: {0}", getSize()));
921 } else {
922 RelationListDialog.this.setTitle(tr("Relations"));
923 }
924 }
[2126]925 }
[2317]926
[4459]927 class RelationDialogPopupMenu extends ListPopupMenu {
928
929 public RelationDialogPopupMenu(JList list) {
930 super(list);
[5028]931
[2317]932 // -- download members action
[4459]933 add(new DownloadMembersAction());
[2317]934
[3102]935 // -- download incomplete members action
[4459]936 add(new DownloadSelectedIncompleteMembersAction());
[3102]937
938 addSeparator();
939
[2317]940 // -- select members action
[4459]941 add(new SelectMembersAction(false));
942 add(new SelectMembersAction(true));
[4158]943
944 // -- select action
[4459]945 add(new SelectAction(false));
946 add(new SelectAction(true));
[5082]947
948 addSeparator();
949
950 add(addToRelation);
[2317]951 }
[4459]952 }
[2317]953
[4459]954 public void addPopupMenuSeparator() {
955 popupMenu.addSeparator();
[2317]956 }
[2439]957
[4459]958 public JMenuItem addPopupMenuAction(Action a) {
959 return popupMenu.add(a);
960 }
961
[4536]962 public void addPopupMenuListener(PopupMenuListener l) {
963 popupMenu.addPopupMenuListener(l);
964 }
965
966 public void removePopupMenuListener(PopupMenuListener l) {
967 popupMenu.addPopupMenuListener(l);
968 }
[5028]969
[4536]970 public Collection<Relation> getSelectedRelations() {
971 return model.getSelectedRelations();
972 }
973
[2487]974 /* ---------------------------------------------------------------------------------- */
975 /* DataSetListener */
976 /* ---------------------------------------------------------------------------------- */
977
[2622]978 public void nodeMoved(NodeMovedEvent event) {/* irrelevant in this context */}
[2487]979
[2622]980 public void wayNodesChanged(WayNodesChangedEvent event) {/* irrelevant in this context */}
[2487]981
[4223]982 public void primitivesAdded(final PrimitivesAddedEvent event) {
[2622]983 model.addRelations(event.getPrimitives());
[3020]984 model.updateTitle();
[2439]985 }
986
[4223]987 public void primitivesRemoved(final PrimitivesRemovedEvent event) {
[2622]988 model.removeRelations(event.getPrimitives());
[3020]989 model.updateTitle();
[2439]990 }
991
[2622]992 public void relationMembersChanged(final RelationMembersChangedEvent event) {
993 List<Relation> sel = model.getSelectedRelations();
994 model.sort();
995 model.setSelectedRelations(sel);
996 displaylist.repaint();
[2439]997 }
998
[2622]999 public void tagsChanged(TagsChangedEvent event) {
1000 OsmPrimitive prim = event.getPrimitive();
[2487]1001 if (prim == null || ! (prim instanceof Relation))
1002 return;
[2622]1003 // trigger a sort of the relation list because the display name may
1004 // have changed
1005 //
1006 List<Relation> sel = model.getSelectedRelations();
1007 model.sort();
1008 model.setSelectedRelations(sel);
1009 displaylist.repaint();
[2439]1010 }
[2488]1011
[2622]1012 public void dataChanged(DataChangedEvent event) {
[2860]1013 initFromLayer(Main.main.getEditLayer());
[2497]1014 }
[2655]1015
1016 public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignore */}
[626]1017}
Note: See TracBrowser for help on using the repository browser.