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

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

fix #8547 for good

  • 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 dist = 0.0;
574 for (Way w : ways) {
575 dist += w.getLength();
576 }
577 }
578 Main.map.statusLine.setDist(dist);
579 }
580
581 /**
582 * Triggers a refresh of the view for all primitives in {@code toUpdate}
583 * which are currently displayed in the view
584 *
585 * @param toUpdate the collection of primitives to update
586 */
587 public void update(Collection<? extends OsmPrimitive> toUpdate) {
588 if (toUpdate == null) return;
589 if (toUpdate.isEmpty()) return;
590 Collection<OsmPrimitive> sel = getSelected();
591 for (OsmPrimitive p: toUpdate){
592 int i = selection.indexOf(p);
593 if (i >= 0) {
594 super.fireContentsChanged(this, i,i);
595 }
596 }
597 setSelected(sel);
598 }
599
600 /**
601 * Replies the list of selected relations with incomplete members
602 *
603 * @return the list of selected relations with incomplete members
604 */
605 public List<Relation> getSelectedRelationsWithIncompleteMembers() {
606 List<Relation> ret = new LinkedList<Relation>();
607 for(int i=0; i<getSize(); i++) {
608 if (!selectionModel.isSelectedIndex(i)) {
609 continue;
610 }
611 OsmPrimitive p = selection.get(i);
612 if (! (p instanceof Relation)) {
613 continue;
614 }
615 if (p.isNew()) {
616 continue;
617 }
618 Relation r = (Relation)p;
619 if (r.hasIncompleteMembers()) {
620 ret.add(r);
621 }
622 }
623 return ret;
624 }
625
626 /**
627 * Sorts the current elements in the selection
628 */
629 public void sort() {
630 if (this.selection.size()>Main.pref.getInteger("selection.no_sort_above",100000)) return;
631 if (this.selection.size()>Main.pref.getInteger("selection.fast_sort_above",10000)) {
632 Collections.sort(this.selection, new OsmPrimitiveQuickComparator());
633 } else {
634 Collections.sort(this.selection, new OsmPrimitiveComparator());
635 }
636 }
637
638 /* ------------------------------------------------------------------------ */
639 /* interface EditLayerChangeListener */
640 /* ------------------------------------------------------------------------ */
641 @Override
642 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
643 if (newLayer == null) {
644 setJOSMSelection(null);
645 history = null;
646 } else {
647 history = newLayer.data.getSelectionHistory();
648 setJOSMSelection(newLayer.data.getAllSelected());
649 }
650 }
651
652 /* ------------------------------------------------------------------------ */
653 /* interface SelectionChangeListener */
654 /* ------------------------------------------------------------------------ */
655 @Override
656 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
657 setJOSMSelection(newSelection);
658 }
659
660 /* ------------------------------------------------------------------------ */
661 /* interface DataSetListener */
662 /* ------------------------------------------------------------------------ */
663 @Override
664 public void dataChanged(DataChangedEvent event) {
665 // refresh the whole list
666 fireContentsChanged(this, 0, getSize());
667 }
668
669 @Override
670 public void nodeMoved(NodeMovedEvent event) {
671 // may influence the display name of primitives, update the data
672 update(event.getPrimitives());
673 }
674
675 @Override
676 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
677 // may influence the display name of primitives, update the data
678 update(event.getPrimitives());
679 }
680
681 @Override
682 public void relationMembersChanged(RelationMembersChangedEvent event) {
683 // may influence the display name of primitives, update the data
684 update(event.getPrimitives());
685 }
686
687 @Override
688 public void tagsChanged(TagsChangedEvent event) {
689 // may influence the display name of primitives, update the data
690 update(event.getPrimitives());
691 }
692
693 @Override
694 public void wayNodesChanged(WayNodesChangedEvent event) {
695 // may influence the display name of primitives, update the data
696 update(event.getPrimitives());
697 }
698
699 @Override
700 public void primitivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */}
701 @Override
702 public void primitivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/}
703 }
704
705 /**
706 * A specialized {@link JMenuItem} for presenting one entry of the search history
707 *
708 * @author Jan Peter Stotz
709 */
710 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
711 final protected SearchSetting s;
712
713 public SearchMenuItem(SearchSetting s) {
714 super(s.toString());
715 this.s = s;
716 addActionListener(this);
717 }
718
719 @Override
720 public void actionPerformed(ActionEvent e) {
721 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
722 }
723 }
724
725 /**
726 * The popup menu for the search history entries
727 *
728 */
729 protected static class SearchPopupMenu extends JPopupMenu {
730 static public void launch(Component parent) {
731 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
732 return;
733 JPopupMenu menu = new SearchPopupMenu();
734 Rectangle r = parent.getBounds();
735 menu.show(parent, r.x, r.y + r.height);
736 }
737
738 public SearchPopupMenu() {
739 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
740 add(new SearchMenuItem(ss));
741 }
742 }
743 }
744
745 /**
746 * A specialized {@link JMenuItem} for presenting one entry of the selection history
747 *
748 * @author Jan Peter Stotz
749 */
750 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
751 final private DefaultNameFormatter df = DefaultNameFormatter.getInstance();
752 protected Collection<? extends OsmPrimitive> sel;
753
754 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
755 super();
756 this.sel = sel;
757 int ways = 0;
758 int nodes = 0;
759 int relations = 0;
760 for (OsmPrimitive o : sel) {
761 if (! o.isSelectable()) continue; // skip unselectable primitives
762 if (o instanceof Way) {
763 ways++;
764 } else if (o instanceof Node) {
765 nodes++;
766 } else if (o instanceof Relation) {
767 relations++;
768 }
769 }
770 StringBuffer text = new StringBuffer();
771 if(ways != 0) {
772 text.append(text.length() > 0 ? ", " : "")
773 .append(trn("{0} way", "{0} ways", ways, ways));
774 }
775 if(nodes != 0) {
776 text.append(text.length() > 0 ? ", " : "")
777 .append(trn("{0} node", "{0} nodes", nodes, nodes));
778 }
779 if(relations != 0) {
780 text.append(text.length() > 0 ? ", " : "")
781 .append(trn("{0} relation", "{0} relations", relations, relations));
782 }
783 if(ways + nodes + relations == 0) {
784 text.append(tr("Unselectable now"));
785 this.sel=new ArrayList<OsmPrimitive>(); // empty selection
786 }
787 if(ways + nodes + relations == 1)
788 {
789 text.append(": ");
790 for(OsmPrimitive o : sel) {
791 text.append(o.getDisplayName(df));
792 }
793 setText(text.toString());
794 } else {
795 setText(tr("Selection: {0}", text));
796 }
797 addActionListener(this);
798 }
799
800 @Override
801 public void actionPerformed(ActionEvent e) {
802 Main.main.getCurrentDataSet().setSelected(sel);
803 }
804 }
805
806 /**
807 * The popup menue for the JOSM selection history entries
808 *
809 */
810 protected static class SelectionHistoryPopup extends JPopupMenu {
811 static public void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
812 if (history == null || history.isEmpty()) return;
813 JPopupMenu menu = new SelectionHistoryPopup(history);
814 Rectangle r = parent.getBounds();
815 menu.show(parent, r.x, r.y + r.height);
816 }
817
818 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
819 for (Collection<? extends OsmPrimitive> sel : history) {
820 add(new SelectionMenuItem(sel));
821 }
822 }
823 }
824
825 /** Quicker comparator, comparing just by type and ID's */
826 static private class OsmPrimitiveQuickComparator implements Comparator<OsmPrimitive> {
827
828 private int compareId(OsmPrimitive a, OsmPrimitive b) {
829 long id_a=a.getUniqueId();
830 long id_b=b.getUniqueId();
831 if (id_a<id_b) return -1;
832 if (id_a>id_b) return 1;
833 return 0;
834 }
835
836 private int compareType(OsmPrimitive a, OsmPrimitive b) {
837 // show ways before relations, then nodes
838 if (a.getType().equals(OsmPrimitiveType.WAY)) return -1;
839 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1;
840 // a is a relation
841 if (b.getType().equals(OsmPrimitiveType.WAY)) return 1;
842 // b is a node
843 return -1;
844 }
845
846 @Override
847 public int compare(OsmPrimitive a, OsmPrimitive b) {
848 if (a.getType().equals(b.getType()))
849 return compareId(a, b);
850 return compareType(a, b);
851 }
852 }
853
854}
Note: See TracBrowser for help on using the repository browser.