source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/SelectionListDialog.java@ 5716

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

fix #8403 - make limit customizable in advanced preferences

  • Property svn:eol-style set to native
File size: 33.6 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Component;
8import java.awt.Rectangle;
9import java.awt.event.ActionEvent;
10import java.awt.event.ActionListener;
11import java.awt.event.KeyEvent;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.util.ArrayList;
15import java.util.Arrays;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.Comparator;
19import java.util.HashSet;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Set;
23
24import javax.swing.AbstractAction;
25import javax.swing.AbstractListModel;
26import javax.swing.Action;
27import javax.swing.DefaultListSelectionModel;
28import javax.swing.JList;
29import javax.swing.JMenuItem;
30import javax.swing.JPopupMenu;
31import javax.swing.ListSelectionModel;
32import javax.swing.SwingUtilities;
33import javax.swing.event.ListDataEvent;
34import javax.swing.event.ListDataListener;
35import javax.swing.event.ListSelectionEvent;
36import javax.swing.event.ListSelectionListener;
37import javax.swing.event.PopupMenuListener;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.actions.AutoScaleAction;
41import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
42import org.openstreetmap.josm.data.SelectionChangedListener;
43import org.openstreetmap.josm.data.osm.Node;
44import org.openstreetmap.josm.data.osm.OsmPrimitive;
45import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
46import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
47import org.openstreetmap.josm.data.osm.Relation;
48import org.openstreetmap.josm.data.osm.RelationMember;
49import org.openstreetmap.josm.data.osm.Way;
50import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
51import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
52import org.openstreetmap.josm.data.osm.event.DataSetListener;
53import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
54import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
55import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
56import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
57import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
58import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
59import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
60import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
61import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
62import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
63import org.openstreetmap.josm.gui.DefaultNameFormatter;
64import org.openstreetmap.josm.gui.MapView;
65import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener;
66import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
67import org.openstreetmap.josm.gui.SideButton;
68import org.openstreetmap.josm.gui.dialogs.relation.DownloadRelationMemberTask;
69import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
70import org.openstreetmap.josm.gui.layer.OsmDataLayer;
71import org.openstreetmap.josm.gui.widgets.ListPopupMenu;
72import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
73import org.openstreetmap.josm.tools.ImageProvider;
74import org.openstreetmap.josm.tools.InputMapUtils;
75import org.openstreetmap.josm.tools.Shortcut;
76import org.openstreetmap.josm.tools.SubclassFilteredCollection;
77
78/**
79 * A small tool dialog for displaying the current selection.
80 *
81 */
82public class SelectionListDialog extends ToggleDialog {
83 private JList lstPrimitives;
84 private SelectionListModel model;
85
86 private SelectAction actSelect;
87 private SearchAction actSearch;
88 private ZoomToJOSMSelectionAction actZoomToJOSMSelection;
89 private ZoomToListSelection actZoomToListSelection;
90 private SetRelationSelection actSetRelationSelection;
91 private EditRelationSelection actEditRelationSelection;
92 private DownloadSelectedIncompleteMembersAction actDownloadSelectedIncompleteMembers;
93
94 private SelectionPopup popupMenu;
95
96 /**
97 * Builds the content panel for this dialog
98 */
99 protected void buildContentPanel() {
100 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
101 model = new SelectionListModel(selectionModel);
102 lstPrimitives = new JList(model);
103 lstPrimitives.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
104 lstPrimitives.setSelectionModel(selectionModel);
105 lstPrimitives.setCellRenderer(new OsmPrimitivRenderer());
106 lstPrimitives.setTransferHandler(null); // Fix #6290. Drag & Drop is not supported anyway and Copy/Paste is better propagated to main window
107
108 // the select action
109 final SideButton selectButton = new SideButton(actSelect = new SelectAction());
110 lstPrimitives.getSelectionModel().addListSelectionListener(actSelect);
111 selectButton.createArrow(new ActionListener() {
112 public void actionPerformed(ActionEvent e) {
113 SelectionHistoryPopup.launch(selectButton, model.getSelectionHistory());
114 }
115 });
116
117 // the search button
118 final SideButton searchButton = new SideButton(actSearch = new SearchAction());
119 searchButton.createArrow(new ActionListener() {
120 public void actionPerformed(ActionEvent e) {
121 SearchPopupMenu.launch(searchButton);
122 }
123 });
124
125 createLayout(lstPrimitives, true, Arrays.asList(new SideButton[] {
126 selectButton, searchButton
127 }));
128 }
129
130 public SelectionListDialog() {
131 super(tr("Selection"), "selectionlist", tr("Open a selection list window."),
132 Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}",
133 tr("Current Selection")), KeyEvent.VK_T, Shortcut.ALT_SHIFT),
134 150, // default height
135 true // default is "show dialog"
136 );
137
138 buildContentPanel();
139 model.addListDataListener(new TitleUpdater());
140 actZoomToJOSMSelection = new ZoomToJOSMSelectionAction();
141 model.addListDataListener(actZoomToJOSMSelection);
142
143 actZoomToListSelection = new ZoomToListSelection();
144 actSetRelationSelection = new SetRelationSelection();
145 actEditRelationSelection = new EditRelationSelection();
146 actDownloadSelectedIncompleteMembers = new DownloadSelectedIncompleteMembersAction();
147
148 lstPrimitives.addMouseListener(new SelectionPopupMenuLauncher());
149 lstPrimitives.addMouseListener(new DblClickHandler());
150
151 popupMenu = new SelectionPopup(lstPrimitives);
152 InputMapUtils.addEnterAction(lstPrimitives, actZoomToListSelection);
153 }
154
155 @Override
156 public void showNotify() {
157 MapView.addEditLayerChangeListener(model);
158 SelectionEventManager.getInstance().addSelectionListener(model, FireMode.IN_EDT_CONSOLIDATED);
159 DatasetEventManager.getInstance().addDatasetListener(model, FireMode.IN_EDT);
160 MapView.addEditLayerChangeListener(actSearch);
161 // editLayerChanged also gets the selection history of the level
162 model.editLayerChanged(null, Main.map.mapView.getEditLayer());
163 if (Main.map.mapView.getEditLayer() != null) {
164 model.setJOSMSelection(Main.map.mapView.getEditLayer().data.getAllSelected());
165 }
166 actSearch.updateEnabledState();
167 }
168
169 @Override
170 public void hideNotify() {
171 MapView.removeEditLayerChangeListener(actSearch);
172 MapView.removeEditLayerChangeListener(model);
173 SelectionEventManager.getInstance().removeSelectionListener(model);
174 DatasetEventManager.getInstance().removeDatasetListener(model);
175 }
176
177 /**
178 * Responds to double clicks on the list of selected objects
179 */
180 class DblClickHandler extends MouseAdapter {
181 @Override
182 public void mouseClicked(MouseEvent e) {
183 if (e.getClickCount() < 2 || ! SwingUtilities.isLeftMouseButton(e)) return;
184 int idx = lstPrimitives.locationToIndex(e.getPoint());
185 if (idx < 0) return;
186 OsmDataLayer layer = Main.main.getEditLayer();
187 if(layer == null) return;
188 layer.data.setSelected(Collections.singleton((OsmPrimitive)model.getElementAt(idx)));
189 }
190 }
191
192 /**
193 * The popup menu launcher
194 */
195 class SelectionPopupMenuLauncher extends PopupMenuLauncher {
196
197 @Override
198 public void launch(MouseEvent evt) {
199 if (model.getSelected().isEmpty()) {
200 int idx = lstPrimitives.locationToIndex(evt.getPoint());
201 if (idx < 0) return;
202 model.setSelected(Collections.singleton((OsmPrimitive)model.getElementAt(idx)));
203 }
204 popupMenu.show(lstPrimitives, evt.getX(), evt.getY());
205 }
206 }
207
208 /**
209 * The popup menu for the selection list
210 */
211 class SelectionPopup extends ListPopupMenu {
212 public SelectionPopup(JList list) {
213 super(list);
214 add(actZoomToJOSMSelection);
215 add(actZoomToListSelection);
216 addSeparator();
217 add(actSetRelationSelection);
218 add(actEditRelationSelection);
219 addSeparator();
220 add(actDownloadSelectedIncompleteMembers);
221 }
222 }
223
224 public void addPopupMenuSeparator() {
225 popupMenu.addSeparator();
226 }
227
228 public JMenuItem addPopupMenuAction(Action a) {
229 return popupMenu.add(a);
230 }
231
232 public void addPopupMenuListener(PopupMenuListener l) {
233 popupMenu.addPopupMenuListener(l);
234 }
235
236 public void removePopupMenuListener(PopupMenuListener l) {
237 popupMenu.addPopupMenuListener(l);
238 }
239
240 public Collection<OsmPrimitive> getSelectedPrimitives() {
241 return model.getSelected();
242 }
243
244 /**
245 * Updates the dialog title with a summary of the current JOSM selection
246 */
247 class TitleUpdater implements ListDataListener {
248 protected void updateTitle() {
249 setTitle(model.getJOSMSelectionSummary());
250 }
251
252 public void contentsChanged(ListDataEvent e) {
253 updateTitle();
254 }
255
256 public void intervalAdded(ListDataEvent e) {
257 updateTitle();
258 }
259
260 public void intervalRemoved(ListDataEvent e) {
261 updateTitle();
262 }
263 }
264
265 /**
266 * Launches the search dialog
267 */
268 static class SearchAction extends AbstractAction implements EditLayerChangeListener {
269 public SearchAction() {
270 putValue(NAME, tr("Search"));
271 putValue(SHORT_DESCRIPTION, tr("Search for objects"));
272 putValue(SMALL_ICON, ImageProvider.get("dialogs","select"));
273 updateEnabledState();
274 }
275
276 public void actionPerformed(ActionEvent e) {
277 if (!isEnabled()) return;
278 org.openstreetmap.josm.actions.search.SearchAction.search();
279 }
280
281 public void updateEnabledState() {
282 setEnabled(Main.main != null && Main.main.getEditLayer() != null);
283 }
284
285 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
286 updateEnabledState();
287 }
288 }
289
290 /**
291 * Sets the current JOSM selection to the OSM primitives selected in the list
292 * of this dialog
293 */
294 class SelectAction extends AbstractAction implements ListSelectionListener {
295 public SelectAction() {
296 putValue(NAME, tr("Select"));
297 putValue(SHORT_DESCRIPTION, tr("Set the selected elements on the map to the selected items in the list above."));
298 putValue(SMALL_ICON, ImageProvider.get("dialogs","select"));
299 updateEnabledState();
300 }
301
302 public void actionPerformed(ActionEvent e) {
303 Collection<OsmPrimitive> sel = model.getSelected();
304 if (sel.isEmpty())return;
305 if (Main.map == null || Main.map.mapView == null || Main.map.mapView.getEditLayer() == null) return;
306 Main.map.mapView.getEditLayer().data.setSelected(sel);
307 }
308
309 public void updateEnabledState() {
310 setEnabled(!model.getSelected().isEmpty());
311 }
312
313 public void valueChanged(ListSelectionEvent e) {
314 updateEnabledState();
315 }
316 }
317
318 /**
319 * The action for zooming to the primitives in the current JOSM selection
320 *
321 */
322 class ZoomToJOSMSelectionAction extends AbstractAction implements ListDataListener {
323
324 public ZoomToJOSMSelectionAction() {
325 putValue(NAME,tr("Zoom to selection"));
326 putValue(SHORT_DESCRIPTION, tr("Zoom to selection"));
327 putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection"));
328 updateEnabledState();
329 }
330
331 public void actionPerformed(ActionEvent e) {
332 AutoScaleAction.autoScale("selection");
333 }
334
335 public void updateEnabledState() {
336 setEnabled(model.getSize() > 0);
337 }
338
339 public void contentsChanged(ListDataEvent e) {
340 updateEnabledState();
341 }
342
343 public void intervalAdded(ListDataEvent e) {
344 updateEnabledState();
345 }
346
347 public void intervalRemoved(ListDataEvent e) {
348 updateEnabledState();
349 }
350 }
351
352 /**
353 * The action for zooming to the primitives which are currently selected in
354 * the list displaying the JOSM selection
355 *
356 */
357 class ZoomToListSelection extends AbstractAction implements ListSelectionListener{
358 public ZoomToListSelection() {
359 putValue(NAME, tr("Zoom to selected element(s)"));
360 putValue(SHORT_DESCRIPTION, tr("Zoom to selected element(s)"));
361 putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection"));
362 updateEnabledState();
363 }
364
365 public void actionPerformed(ActionEvent e) {
366 BoundingXYVisitor box = new BoundingXYVisitor();
367 Collection<OsmPrimitive> sel = model.getSelected();
368 if (sel.isEmpty()) return;
369 box.computeBoundingBox(sel);
370 if (box.getBounds() == null)
371 return;
372 box.enlargeBoundingBox();
373 Main.map.mapView.recalculateCenterScale(box);
374 }
375
376 public void updateEnabledState() {
377 setEnabled(!model.getSelected().isEmpty());
378 }
379
380 public void valueChanged(ListSelectionEvent e) {
381 updateEnabledState();
382 }
383 }
384
385 /**
386 * The action for setting and editing a relation in relation list dialog
387 *
388 */
389 class EditRelationSelection extends SetRelationSelection {
390 public EditRelationSelection() {
391 putValue(NAME, tr("Call editor for relation"));
392 putValue(SHORT_DESCRIPTION, tr("Call relation editor for selected relation"));
393 putValue(SMALL_ICON, ImageProvider.get("dialogs", "edit"));
394 updateEnabledState();
395 }
396
397 @Override
398 public void actionPerformed(ActionEvent e) {
399 Relation relation = (Relation)model.getSelected().toArray()[0];
400 Collection<RelationMember> members = new HashSet<RelationMember>();
401 Collection<OsmPrimitive> selection = model.getAllElements();
402 for (RelationMember member: relation.getMembers()) {
403 if (selection.contains(member.getMember())) {
404 members.add(member);
405 }
406 }
407 Main.map.relationListDialog.selectRelation(relation);
408 RelationEditor.getEditor(Main.map.mapView.getEditLayer(), relation,
409 members).setVisible(true);
410 }
411 }
412
413 /**
414 * The action for setting a relation in relation list dialog
415 *
416 */
417 class SetRelationSelection extends AbstractAction implements ListSelectionListener{
418 public SetRelationSelection() {
419 putValue(NAME, tr("Select in relation list"));
420 putValue(SHORT_DESCRIPTION, tr("Select relation in relation list."));
421 putValue(SMALL_ICON, ImageProvider.get("dialogs", "selectionlist"));
422 updateEnabledState();
423 }
424
425 public void actionPerformed(ActionEvent e) {
426 Relation relation = (Relation)model.getSelected().toArray()[0];
427 Main.map.relationListDialog.selectRelation(relation);
428 }
429
430 public void updateEnabledState() {
431 Object[] sel = model.getSelected().toArray();
432 setEnabled(sel.length == 1 && sel[0] instanceof Relation);
433 }
434
435 public void valueChanged(ListSelectionEvent e) {
436 updateEnabledState();
437 }
438 }
439
440 /**
441 * The list model for the list of OSM primitives in the current JOSM selection.
442 *
443 * The model also maintains a history of the last {@link SelectionListModel#SELECTION_HISTORY_SIZE}
444 * JOSM selection.
445 *
446 */
447 static private class SelectionListModel extends AbstractListModel implements EditLayerChangeListener, SelectionChangedListener, DataSetListener{
448
449 private static final int SELECTION_HISTORY_SIZE = 10;
450
451 // Variable to store history from currentDataSet()
452 private LinkedList<Collection<? extends OsmPrimitive>> history;
453 private final List<OsmPrimitive> selection = new ArrayList<OsmPrimitive>();
454 private DefaultListSelectionModel selectionModel;
455
456 /**
457 * Constructor
458 * @param selectionModel the selection model used in the list
459 */
460 public SelectionListModel(DefaultListSelectionModel selectionModel) {
461 this.selectionModel = selectionModel;
462 }
463
464 /**
465 * Replies a summary of the current JOSM selection
466 *
467 * @return a summary of the current JOSM selection
468 */
469 public String getJOSMSelectionSummary() {
470 if (selection.isEmpty()) return tr("Selection");
471 int numNodes = 0;
472 int numWays = 0;
473 int numRelations = 0;
474 for (OsmPrimitive p: selection) {
475 switch(p.getType()) {
476 case NODE: numNodes++; break;
477 case WAY: numWays++; break;
478 case RELATION: numRelations++; break;
479 }
480 }
481 return tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", numRelations, numWays, numNodes);
482 }
483
484 /**
485 * Remembers a JOSM selection the history of JOSM selections
486 *
487 * @param selection the JOSM selection. Ignored if null or empty.
488 */
489 public void remember(Collection<? extends OsmPrimitive> selection) {
490 if (selection == null)return;
491 if (selection.isEmpty())return;
492 if (history == null) return;
493 if (history.isEmpty()) {
494 history.add(selection);
495 return;
496 }
497 if (history.getFirst().equals(selection)) return;
498 history.addFirst(selection);
499 for(int i = 1; i < history.size(); ++i) {
500 if(history.get(i).equals(selection)) {
501 history.remove(i);
502 break;
503 }
504 }
505 int maxsize = Main.pref.getInteger("select.history-size", SELECTION_HISTORY_SIZE);
506 while (history.size() > maxsize) {
507 history.removeLast();
508 }
509 }
510
511 /**
512 * Replies the history of JOSM selections
513 *
514 * @return
515 */
516 public List<Collection<? extends OsmPrimitive>> getSelectionHistory() {
517 return history;
518 }
519
520 public Object getElementAt(int index) {
521 return selection.get(index);
522 }
523
524 public int getSize() {
525 return selection.size();
526 }
527
528 /**
529 * Replies the collection of OSM primitives currently selected in the view
530 * of this model
531 *
532 * @return
533 */
534 public Collection<OsmPrimitive> getSelected() {
535 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
536 for(int i=0; i< getSize();i++) {
537 if (selectionModel.isSelectedIndex(i)) {
538 sel.add(selection.get(i));
539 }
540 }
541 return sel;
542 }
543
544 /**
545 * Replies the collection of OSM primitives in the view
546 * of this model
547 *
548 * @return
549 */
550 public Collection<OsmPrimitive> getAllElements() {
551 return selection;
552 }
553
554 /**
555 * Sets the OSM primitives to be selected in the view of this model
556 *
557 * @param sel the collection of primitives to select
558 */
559 public void setSelected(Collection<OsmPrimitive> sel) {
560 selectionModel.clearSelection();
561 if (sel == null) return;
562 for (OsmPrimitive p: sel){
563 int i = selection.indexOf(p);
564 if (i >= 0){
565 selectionModel.addSelectionInterval(i, i);
566 }
567 }
568 }
569
570 @Override
571 protected void fireContentsChanged(Object source, int index0, int index1) {
572 Collection<OsmPrimitive> sel = getSelected();
573 super.fireContentsChanged(source, index0, index1);
574 setSelected(sel);
575 }
576
577 /**
578 * Sets the collection of currently selected OSM objects
579 *
580 * @param selection the collection of currently selected OSM objects
581 */
582 public void setJOSMSelection(Collection<? extends OsmPrimitive> selection) {
583 this.selection.clear();
584 if (selection == null) {
585 fireContentsChanged(this, 0, getSize());
586 return;
587 }
588 this.selection.addAll(selection);
589 sort();
590 fireContentsChanged(this, 0, getSize());
591 remember(selection);
592 double dist = -1;
593 SubclassFilteredCollection<OsmPrimitive, Way> ways = new SubclassFilteredCollection<OsmPrimitive, Way>(selection, OsmPrimitive.wayPredicate);
594 // Compute total length of selected way(s) until an arbitrary limit set to 250 ways
595 // in order to prevent performance issue if a large number of ways are selected (old behaviour kept in that case, see #8403)
596 int maxWays = Math.max(1, Main.pref.getInteger("selection.max-ways-for-statusline", 250));
597 if (!ways.isEmpty() && ways.size() <= maxWays) {
598 for (Way w : ways) {
599 dist += w.getLength();
600 }
601 }
602 Main.map.statusLine.setDist(dist);
603 }
604
605 /**
606 * Triggers a refresh of the view for all primitives in {@code toUpdate}
607 * which are currently displayed in the view
608 *
609 * @param toUpdate the collection of primitives to update
610 */
611 public void update(Collection<? extends OsmPrimitive> toUpdate) {
612 if (toUpdate == null) return;
613 if (toUpdate.isEmpty()) return;
614 Collection<OsmPrimitive> sel = getSelected();
615 for (OsmPrimitive p: toUpdate){
616 int i = selection.indexOf(p);
617 if (i >= 0) {
618 super.fireContentsChanged(this, i,i);
619 }
620 }
621 setSelected(sel);
622 }
623
624 /**
625 * Replies the list of selected relations with incomplete members
626 *
627 * @return the list of selected relations with incomplete members
628 */
629 public List<Relation> getSelectedRelationsWithIncompleteMembers() {
630 List<Relation> ret = new LinkedList<Relation>();
631 for(int i=0; i<getSize(); i++) {
632 if (!selectionModel.isSelectedIndex(i)) {
633 continue;
634 }
635 OsmPrimitive p = selection.get(i);
636 if (! (p instanceof Relation)) {
637 continue;
638 }
639 if (p.isNew()) {
640 continue;
641 }
642 Relation r = (Relation)p;
643 if (r.hasIncompleteMembers()) {
644 ret.add(r);
645 }
646 }
647 return ret;
648 }
649
650 /**
651 * Sorts the current elements in the selection
652 */
653 public void sort() {
654 if (this.selection.size()>Main.pref.getInteger("selection.no_sort_above",100000)) return;
655 if (this.selection.size()>Main.pref.getInteger("selection.fast_sort_above",10000)) {
656 Collections.sort(this.selection, new OsmPrimitiveQuickComparator());
657 } else {
658 Collections.sort(this.selection, new OsmPrimitiveComparator());
659 }
660 }
661
662 /* ------------------------------------------------------------------------ */
663 /* interface EditLayerChangeListener */
664 /* ------------------------------------------------------------------------ */
665 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
666 if (newLayer == null) {
667 setJOSMSelection(null);
668 history = null;
669 } else {
670 history = newLayer.data.getSelectionHistory();
671 setJOSMSelection(newLayer.data.getAllSelected());
672 }
673 }
674
675 /* ------------------------------------------------------------------------ */
676 /* interface SelectionChangeListener */
677 /* ------------------------------------------------------------------------ */
678 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
679 setJOSMSelection(newSelection);
680 }
681
682 /* ------------------------------------------------------------------------ */
683 /* interface DataSetListener */
684 /* ------------------------------------------------------------------------ */
685 public void dataChanged(DataChangedEvent event) {
686 // refresh the whole list
687 fireContentsChanged(this, 0, getSize());
688 }
689
690 public void nodeMoved(NodeMovedEvent event) {
691 // may influence the display name of primitives, update the data
692 update(event.getPrimitives());
693 }
694
695 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
696 // may influence the display name of primitives, update the data
697 update(event.getPrimitives());
698 }
699
700 public void relationMembersChanged(RelationMembersChangedEvent event) {
701 // may influence the display name of primitives, update the data
702 update(event.getPrimitives());
703 }
704
705 public void tagsChanged(TagsChangedEvent event) {
706 // may influence the display name of primitives, update the data
707 update(event.getPrimitives());
708 }
709
710 public void wayNodesChanged(WayNodesChangedEvent event) {
711 // may influence the display name of primitives, update the data
712 update(event.getPrimitives());
713 }
714
715 public void primitivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */}
716 public void primitivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/}
717 }
718
719 /**
720 * A specialized {@link JMenuItem} for presenting one entry of the search history
721 *
722 * @author Jan Peter Stotz
723 */
724 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
725 final protected SearchSetting s;
726
727 public SearchMenuItem(SearchSetting s) {
728 super(s.toString());
729 this.s = s;
730 addActionListener(this);
731 }
732
733 public void actionPerformed(ActionEvent e) {
734 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
735 }
736 }
737
738 /**
739 * The popup menu for the search history entries
740 *
741 */
742 protected static class SearchPopupMenu extends JPopupMenu {
743 static public void launch(Component parent) {
744 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
745 return;
746 JPopupMenu menu = new SearchPopupMenu();
747 Rectangle r = parent.getBounds();
748 menu.show(parent, r.x, r.y + r.height);
749 }
750
751 public SearchPopupMenu() {
752 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
753 add(new SearchMenuItem(ss));
754 }
755 }
756 }
757
758 /**
759 * A specialized {@link JMenuItem} for presenting one entry of the selection history
760 *
761 * @author Jan Peter Stotz
762 */
763 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
764 final private DefaultNameFormatter df = DefaultNameFormatter.getInstance();
765 protected Collection<? extends OsmPrimitive> sel;
766
767 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
768 super();
769 this.sel = sel;
770 int ways = 0;
771 int nodes = 0;
772 int relations = 0;
773 for (OsmPrimitive o : sel) {
774 if (! o.isSelectable()) continue; // skip unselectable primitives
775 if (o instanceof Way) {
776 ways++;
777 } else if (o instanceof Node) {
778 nodes++;
779 } else if (o instanceof Relation) {
780 relations++;
781 }
782 }
783 StringBuffer text = new StringBuffer();
784 if(ways != 0) {
785 text.append(text.length() > 0 ? ", " : "")
786 .append(trn("{0} way", "{0} ways", ways, ways));
787 }
788 if(nodes != 0) {
789 text.append(text.length() > 0 ? ", " : "")
790 .append(trn("{0} node", "{0} nodes", nodes, nodes));
791 }
792 if(relations != 0) {
793 text.append(text.length() > 0 ? ", " : "")
794 .append(trn("{0} relation", "{0} relations", relations, relations));
795 }
796 if(ways + nodes + relations == 0) {
797 text.append(tr("Unselectable now"));
798 this.sel=new ArrayList<OsmPrimitive>(); // empty selection
799 }
800 if(ways + nodes + relations == 1)
801 {
802 text.append(": ");
803 for(OsmPrimitive o : sel) {
804 text.append(o.getDisplayName(df));
805 }
806 setText(text.toString());
807 } else {
808 setText(tr("Selection: {0}", text));
809 }
810 addActionListener(this);
811 }
812
813 public void actionPerformed(ActionEvent e) {
814 Main.main.getCurrentDataSet().setSelected(sel);
815 }
816 }
817
818 /**
819 * The popup menue for the JOSM selection history entries
820 *
821 */
822 protected static class SelectionHistoryPopup extends JPopupMenu {
823 static public void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
824 if (history == null || history.isEmpty()) return;
825 JPopupMenu menu = new SelectionHistoryPopup(history);
826 Rectangle r = parent.getBounds();
827 menu.show(parent, r.x, r.y + r.height);
828 }
829
830 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
831 for (Collection<? extends OsmPrimitive> sel : history) {
832 add(new SelectionMenuItem(sel));
833 }
834 }
835 }
836
837 /**
838 * Action for downloading incomplete members of selected relations
839 *
840 */
841 class DownloadSelectedIncompleteMembersAction extends AbstractAction implements ListSelectionListener {
842 public DownloadSelectedIncompleteMembersAction() {
843 putValue(SHORT_DESCRIPTION, tr("Download incomplete members of selected relations"));
844 putValue(SMALL_ICON, ImageProvider.get("dialogs/relation", "downloadincompleteselected"));
845 putValue(NAME, tr("Download incomplete members"));
846 updateEnabledState();
847 }
848
849 public Set<OsmPrimitive> buildSetOfIncompleteMembers(List<Relation> rels) {
850 Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
851 for(Relation r: rels) {
852 ret.addAll(r.getIncompleteMembers());
853 }
854 return ret;
855 }
856
857 public void actionPerformed(ActionEvent e) {
858 if (!isEnabled())
859 return;
860 List<Relation> rels = model.getSelectedRelationsWithIncompleteMembers();
861 if (rels.isEmpty()) return;
862 Main.worker.submit(new DownloadRelationMemberTask(
863 rels,
864 buildSetOfIncompleteMembers(rels),
865 Main.map.mapView.getEditLayer()
866 ));
867 }
868
869 protected void updateEnabledState() {
870 setEnabled(!model.getSelectedRelationsWithIncompleteMembers().isEmpty());
871 }
872
873 public void valueChanged(ListSelectionEvent e) {
874 updateEnabledState();
875 }
876 }
877
878 /** Quicker comparator, comparing just by type and ID's */
879 static private class OsmPrimitiveQuickComparator implements Comparator<OsmPrimitive> {
880
881 private int compareId(OsmPrimitive a, OsmPrimitive b) {
882 long id_a=a.getUniqueId();
883 long id_b=b.getUniqueId();
884 if (id_a<id_b) return -1;
885 if (id_a>id_b) return 1;
886 return 0;
887 }
888
889 private int compareType(OsmPrimitive a, OsmPrimitive b) {
890 // show ways before relations, then nodes
891 if (a.getType().equals(OsmPrimitiveType.WAY)) return -1;
892 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1;
893 // a is a relation
894 if (b.getType().equals(OsmPrimitiveType.WAY)) return 1;
895 // b is a node
896 return -1;
897 }
898
899 public int compare(OsmPrimitive a, OsmPrimitive b) {
900 if (a.getType().equals(b.getType()))
901 return compareId(a, b);
902 return compareType(a, b);
903 }
904 }
905
906}
Note: See TracBrowser for help on using the repository browser.