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

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

Highlight relation or object when user clicks the list in Relation List, Selection List or Properties dialog
[ idea by Felis Pimeja ] + added HighlighHelper class

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