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

Last change on this file since 5799 was 5799, checked in by akks, 11 years ago

Membership tabled in properties toggle dialog supports multiselect (and multiple membership deletion)
Property toggle dialog refactoring - methods splitting and reordering
see #7846: more RelationListDialog refactoring, all other relation-related actions separated from dialogs, @Override, JavaDocs

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