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

Last change on this file since 6070 was 6070, checked in by stoecker, 11 years ago

see #8853 remove tabs, trailing spaces, windows line ends, strange characters

  • Property svn:eol-style set to native
File size: 29.3 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 private boolean highlightEnabled = Main.pref.getBoolean("draw.target-highlight", true);
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 (highlightEnabled && Main.isDisplayingMapView()) {
204 if (helper.highlightOnly((OsmPrimitive)model.getElementAt(idx))) {
205 Main.map.mapView.repaint();
206 }
207 }
208 }
209
210 @Override
211 public void mouseExited(MouseEvent me) {
212 if (highlightEnabled) helper.clear();
213 super.mouseExited(me);
214 }
215 }
216
217 private PopupMenuHandler setupPopupMenuHandler() {
218 PopupMenuHandler handler = new PopupMenuHandler(popupMenu);
219 handler.addAction(actZoomToJOSMSelection);
220 handler.addAction(actZoomToListSelection);
221 handler.addSeparator();
222 handler.addAction(actSetRelationSelection);
223 handler.addAction(actEditRelationSelection);
224 handler.addSeparator();
225 handler.addAction(actDownloadSelectedIncompleteMembers);
226 return handler;
227 }
228
229 /**
230 * Replies the popup menu handler.
231 * @return The popup menu handler
232 */
233 public PopupMenuHandler getPopupMenuHandler() {
234 return popupMenuHandler;
235 }
236
237 /**
238 * Replies the selected OSM primitives.
239 * @return The selected OSM primitives
240 */
241 public Collection<OsmPrimitive> getSelectedPrimitives() {
242 return model.getSelected();
243 }
244
245 /**
246 * Updates the dialog title with a summary of the current JOSM selection
247 */
248 class TitleUpdater implements ListDataListener {
249 protected void updateTitle() {
250 setTitle(model.getJOSMSelectionSummary());
251 }
252
253 @Override
254 public void contentsChanged(ListDataEvent e) {
255 updateTitle();
256 }
257
258 @Override
259 public void intervalAdded(ListDataEvent e) {
260 updateTitle();
261 }
262
263 @Override
264 public void intervalRemoved(ListDataEvent e) {
265 updateTitle();
266 }
267 }
268
269 /**
270 * Launches the search dialog
271 */
272 static class SearchAction extends AbstractAction implements EditLayerChangeListener {
273 public SearchAction() {
274 putValue(NAME, tr("Search"));
275 putValue(SHORT_DESCRIPTION, tr("Search for objects"));
276 putValue(SMALL_ICON, ImageProvider.get("dialogs","select"));
277 updateEnabledState();
278 }
279
280 @Override
281 public void actionPerformed(ActionEvent e) {
282 if (!isEnabled()) return;
283 org.openstreetmap.josm.actions.search.SearchAction.search();
284 }
285
286 public void updateEnabledState() {
287 setEnabled(Main.main != null && Main.main.getEditLayer() != null);
288 }
289
290 @Override
291 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
292 updateEnabledState();
293 }
294 }
295
296 /**
297 * Sets the current JOSM selection to the OSM primitives selected in the list
298 * of this dialog
299 */
300 class SelectAction extends AbstractAction implements ListSelectionListener {
301 public SelectAction() {
302 putValue(NAME, tr("Select"));
303 putValue(SHORT_DESCRIPTION, tr("Set the selected elements on the map to the selected items in the list above."));
304 putValue(SMALL_ICON, ImageProvider.get("dialogs","select"));
305 updateEnabledState();
306 }
307
308 @Override
309 public void actionPerformed(ActionEvent e) {
310 Collection<OsmPrimitive> sel = model.getSelected();
311 if (sel.isEmpty())return;
312 if (Main.map == null || Main.map.mapView == null || Main.map.mapView.getEditLayer() == null) return;
313 Main.map.mapView.getEditLayer().data.setSelected(sel);
314 }
315
316 public void updateEnabledState() {
317 setEnabled(!model.getSelected().isEmpty());
318 }
319
320 @Override
321 public void valueChanged(ListSelectionEvent e) {
322 updateEnabledState();
323 }
324 }
325
326 /**
327 * The action for zooming to the primitives in the current JOSM selection
328 *
329 */
330 class ZoomToJOSMSelectionAction extends AbstractAction implements ListDataListener {
331
332 public ZoomToJOSMSelectionAction() {
333 putValue(NAME,tr("Zoom to selection"));
334 putValue(SHORT_DESCRIPTION, tr("Zoom to selection"));
335 putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection"));
336 updateEnabledState();
337 }
338
339 @Override
340 public void actionPerformed(ActionEvent e) {
341 AutoScaleAction.autoScale("selection");
342 }
343
344 public void updateEnabledState() {
345 setEnabled(model.getSize() > 0);
346 }
347
348 @Override
349 public void contentsChanged(ListDataEvent e) {
350 updateEnabledState();
351 }
352
353 @Override
354 public void intervalAdded(ListDataEvent e) {
355 updateEnabledState();
356 }
357
358 @Override
359 public void intervalRemoved(ListDataEvent e) {
360 updateEnabledState();
361 }
362 }
363
364 /**
365 * The action for zooming to the primitives which are currently selected in
366 * the list displaying the JOSM selection
367 *
368 */
369 class ZoomToListSelection extends AbstractAction implements ListSelectionListener{
370 public ZoomToListSelection() {
371 putValue(NAME, tr("Zoom to selected element(s)"));
372 putValue(SHORT_DESCRIPTION, tr("Zoom to selected element(s)"));
373 putValue(SMALL_ICON, ImageProvider.get("dialogs/autoscale", "selection"));
374 updateEnabledState();
375 }
376
377 @Override
378 public void actionPerformed(ActionEvent e) {
379 BoundingXYVisitor box = new BoundingXYVisitor();
380 Collection<OsmPrimitive> sel = model.getSelected();
381 if (sel.isEmpty()) return;
382 box.computeBoundingBox(sel);
383 if (box.getBounds() == null)
384 return;
385 box.enlargeBoundingBox();
386 Main.map.mapView.recalculateCenterScale(box);
387 }
388
389 public void updateEnabledState() {
390 setEnabled(!model.getSelected().isEmpty());
391 }
392
393 @Override
394 public void valueChanged(ListSelectionEvent e) {
395 updateEnabledState();
396 }
397 }
398
399 /**
400 * The list model for the list of OSM primitives in the current JOSM selection.
401 *
402 * The model also maintains a history of the last {@link SelectionListModel#SELECTION_HISTORY_SIZE}
403 * JOSM selection.
404 *
405 */
406 static private class SelectionListModel extends AbstractListModel implements EditLayerChangeListener, SelectionChangedListener, DataSetListener{
407
408 private static final int SELECTION_HISTORY_SIZE = 10;
409
410 // Variable to store history from currentDataSet()
411 private LinkedList<Collection<? extends OsmPrimitive>> history;
412 private final List<OsmPrimitive> selection = new ArrayList<OsmPrimitive>();
413 private DefaultListSelectionModel selectionModel;
414
415 /**
416 * Constructor
417 * @param selectionModel the selection model used in the list
418 */
419 public SelectionListModel(DefaultListSelectionModel selectionModel) {
420 this.selectionModel = selectionModel;
421 }
422
423 /**
424 * Replies a summary of the current JOSM selection
425 *
426 * @return a summary of the current JOSM selection
427 */
428 public String getJOSMSelectionSummary() {
429 if (selection.isEmpty()) return tr("Selection");
430 int numNodes = 0;
431 int numWays = 0;
432 int numRelations = 0;
433 for (OsmPrimitive p: selection) {
434 switch(p.getType()) {
435 case NODE: numNodes++; break;
436 case WAY: numWays++; break;
437 case RELATION: numRelations++; break;
438 }
439 }
440 return tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", numRelations, numWays, numNodes);
441 }
442
443 /**
444 * Remembers a JOSM selection the history of JOSM selections
445 *
446 * @param selection the JOSM selection. Ignored if null or empty.
447 */
448 public void remember(Collection<? extends OsmPrimitive> selection) {
449 if (selection == null)return;
450 if (selection.isEmpty())return;
451 if (history == null) return;
452 if (history.isEmpty()) {
453 history.add(selection);
454 return;
455 }
456 if (history.getFirst().equals(selection)) return;
457 history.addFirst(selection);
458 for(int i = 1; i < history.size(); ++i) {
459 if(history.get(i).equals(selection)) {
460 history.remove(i);
461 break;
462 }
463 }
464 int maxsize = Main.pref.getInteger("select.history-size", SELECTION_HISTORY_SIZE);
465 while (history.size() > maxsize) {
466 history.removeLast();
467 }
468 }
469
470 /**
471 * Replies the history of JOSM selections
472 *
473 * @return history of JOSM selections
474 */
475 public List<Collection<? extends OsmPrimitive>> getSelectionHistory() {
476 return history;
477 }
478
479 @Override
480 public Object getElementAt(int index) {
481 return selection.get(index);
482 }
483
484 @Override
485 public int getSize() {
486 return selection.size();
487 }
488
489 /**
490 * Replies the collection of OSM primitives currently selected in the view
491 * of this model
492 *
493 * @return choosen elements in the view
494 */
495 public Collection<OsmPrimitive> getSelected() {
496 Set<OsmPrimitive> sel = new HashSet<OsmPrimitive>();
497 for(int i=0; i< getSize();i++) {
498 if (selectionModel.isSelectedIndex(i)) {
499 sel.add(selection.get(i));
500 }
501 }
502 return sel;
503 }
504
505 /**
506 * Sets the OSM primitives to be selected in the view of this model
507 *
508 * @param sel the collection of primitives to select
509 */
510 public void setSelected(Collection<OsmPrimitive> sel) {
511 selectionModel.clearSelection();
512 if (sel == null) return;
513 for (OsmPrimitive p: sel){
514 int i = selection.indexOf(p);
515 if (i >= 0){
516 selectionModel.addSelectionInterval(i, i);
517 }
518 }
519 }
520
521 @Override
522 protected void fireContentsChanged(Object source, int index0, int index1) {
523 Collection<OsmPrimitive> sel = getSelected();
524 super.fireContentsChanged(source, index0, index1);
525 setSelected(sel);
526 }
527
528 /**
529 * Sets the collection of currently selected OSM objects
530 *
531 * @param selection the collection of currently selected OSM objects
532 */
533 public void setJOSMSelection(final Collection<? extends OsmPrimitive> selection) {
534 this.selection.clear();
535 if (selection != null) {
536 this.selection.addAll(selection);
537 sort();
538 }
539 GuiHelper.runInEDTAndWait(new Runnable() {
540 @Override public void run() {
541 fireContentsChanged(this, 0, getSize());
542 if (selection != null) {
543 remember(selection);
544 Main.map.statusLine.setDist(new SubclassFilteredCollection<OsmPrimitive, Way>(selection, OsmPrimitive.wayPredicate));
545 }
546 }
547 });
548 }
549
550 /**
551 * Triggers a refresh of the view for all primitives in {@code toUpdate}
552 * which are currently displayed in the view
553 *
554 * @param toUpdate the collection of primitives to update
555 */
556 public void update(Collection<? extends OsmPrimitive> toUpdate) {
557 if (toUpdate == null) return;
558 if (toUpdate.isEmpty()) return;
559 Collection<OsmPrimitive> sel = getSelected();
560 for (OsmPrimitive p: toUpdate){
561 int i = selection.indexOf(p);
562 if (i >= 0) {
563 super.fireContentsChanged(this, i,i);
564 }
565 }
566 setSelected(sel);
567 }
568
569 /**
570 * Sorts the current elements in the selection
571 */
572 public void sort() {
573 if (this.selection.size()>Main.pref.getInteger("selection.no_sort_above",100000)) return;
574 if (this.selection.size()>Main.pref.getInteger("selection.fast_sort_above",10000)) {
575 Collections.sort(this.selection, new OsmPrimitiveQuickComparator());
576 } else {
577 Collections.sort(this.selection, new OsmPrimitiveComparator());
578 }
579 }
580
581 /* ------------------------------------------------------------------------ */
582 /* interface EditLayerChangeListener */
583 /* ------------------------------------------------------------------------ */
584 @Override
585 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
586 if (newLayer == null) {
587 setJOSMSelection(null);
588 history = null;
589 } else {
590 history = newLayer.data.getSelectionHistory();
591 setJOSMSelection(newLayer.data.getAllSelected());
592 }
593 }
594
595 /* ------------------------------------------------------------------------ */
596 /* interface SelectionChangeListener */
597 /* ------------------------------------------------------------------------ */
598 @Override
599 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
600 setJOSMSelection(newSelection);
601 }
602
603 /* ------------------------------------------------------------------------ */
604 /* interface DataSetListener */
605 /* ------------------------------------------------------------------------ */
606 @Override
607 public void dataChanged(DataChangedEvent event) {
608 // refresh the whole list
609 fireContentsChanged(this, 0, getSize());
610 }
611
612 @Override
613 public void nodeMoved(NodeMovedEvent event) {
614 // may influence the display name of primitives, update the data
615 update(event.getPrimitives());
616 }
617
618 @Override
619 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
620 // may influence the display name of primitives, update the data
621 update(event.getPrimitives());
622 }
623
624 @Override
625 public void relationMembersChanged(RelationMembersChangedEvent event) {
626 // may influence the display name of primitives, update the data
627 update(event.getPrimitives());
628 }
629
630 @Override
631 public void tagsChanged(TagsChangedEvent event) {
632 // may influence the display name of primitives, update the data
633 update(event.getPrimitives());
634 }
635
636 @Override
637 public void wayNodesChanged(WayNodesChangedEvent event) {
638 // may influence the display name of primitives, update the data
639 update(event.getPrimitives());
640 }
641
642 @Override
643 public void primitivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */}
644 @Override
645 public void primitivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/}
646 }
647
648 /**
649 * A specialized {@link JMenuItem} for presenting one entry of the search history
650 *
651 * @author Jan Peter Stotz
652 */
653 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
654 final protected SearchSetting s;
655
656 public SearchMenuItem(SearchSetting s) {
657 super(s.toString());
658 this.s = s;
659 addActionListener(this);
660 }
661
662 @Override
663 public void actionPerformed(ActionEvent e) {
664 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
665 }
666 }
667
668 /**
669 * The popup menu for the search history entries
670 *
671 */
672 protected static class SearchPopupMenu extends JPopupMenu {
673 static public void launch(Component parent) {
674 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
675 return;
676 JPopupMenu menu = new SearchPopupMenu();
677 Rectangle r = parent.getBounds();
678 menu.show(parent, r.x, r.y + r.height);
679 }
680
681 public SearchPopupMenu() {
682 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
683 add(new SearchMenuItem(ss));
684 }
685 }
686 }
687
688 /**
689 * A specialized {@link JMenuItem} for presenting one entry of the selection history
690 *
691 * @author Jan Peter Stotz
692 */
693 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
694 final private DefaultNameFormatter df = DefaultNameFormatter.getInstance();
695 protected Collection<? extends OsmPrimitive> sel;
696
697 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
698 super();
699 this.sel = sel;
700 int ways = 0;
701 int nodes = 0;
702 int relations = 0;
703 for (OsmPrimitive o : sel) {
704 if (! o.isSelectable()) continue; // skip unselectable primitives
705 if (o instanceof Way) {
706 ways++;
707 } else if (o instanceof Node) {
708 nodes++;
709 } else if (o instanceof Relation) {
710 relations++;
711 }
712 }
713 StringBuffer text = new StringBuffer();
714 if(ways != 0) {
715 text.append(text.length() > 0 ? ", " : "")
716 .append(trn("{0} way", "{0} ways", ways, ways));
717 }
718 if(nodes != 0) {
719 text.append(text.length() > 0 ? ", " : "")
720 .append(trn("{0} node", "{0} nodes", nodes, nodes));
721 }
722 if(relations != 0) {
723 text.append(text.length() > 0 ? ", " : "")
724 .append(trn("{0} relation", "{0} relations", relations, relations));
725 }
726 if(ways + nodes + relations == 0) {
727 text.append(tr("Unselectable now"));
728 this.sel=new ArrayList<OsmPrimitive>(); // empty selection
729 }
730 if(ways + nodes + relations == 1)
731 {
732 text.append(": ");
733 for(OsmPrimitive o : sel) {
734 text.append(o.getDisplayName(df));
735 }
736 setText(text.toString());
737 } else {
738 setText(tr("Selection: {0}", text));
739 }
740 addActionListener(this);
741 }
742
743 @Override
744 public void actionPerformed(ActionEvent e) {
745 Main.main.getCurrentDataSet().setSelected(sel);
746 }
747 }
748
749 /**
750 * The popup menu for the JOSM selection history entries
751 */
752 protected static class SelectionHistoryPopup extends JPopupMenu {
753 static public void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
754 if (history == null || history.isEmpty()) return;
755 JPopupMenu menu = new SelectionHistoryPopup(history);
756 Rectangle r = parent.getBounds();
757 menu.show(parent, r.x, r.y + r.height);
758 }
759
760 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
761 for (Collection<? extends OsmPrimitive> sel : history) {
762 add(new SelectionMenuItem(sel));
763 }
764 }
765 }
766
767 /** Quicker comparator, comparing just by type and ID's */
768 static private class OsmPrimitiveQuickComparator implements Comparator<OsmPrimitive> {
769
770 private int compareId(OsmPrimitive a, OsmPrimitive b) {
771 long id_a=a.getUniqueId();
772 long id_b=b.getUniqueId();
773 if (id_a<id_b) return -1;
774 if (id_a>id_b) return 1;
775 return 0;
776 }
777
778 private int compareType(OsmPrimitive a, OsmPrimitive b) {
779 // show ways before relations, then nodes
780 if (a.getType().equals(OsmPrimitiveType.WAY)) return -1;
781 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1;
782 // a is a relation
783 if (b.getType().equals(OsmPrimitiveType.WAY)) return 1;
784 // b is a node
785 return -1;
786 }
787
788 @Override
789 public int compare(OsmPrimitive a, OsmPrimitive b) {
790 if (a.getType().equals(b.getType()))
791 return compareId(a, b);
792 return compareType(a, b);
793 }
794 }
795
796}
Note: See TracBrowser for help on using the repository browser.