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

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

see #7846 - Large code refactorization in management of popup menus to simplify interactions with plugins (needed at least for imagery-xml-bounds and tag2link plugins)

  • Property svn:eol-style set to native
File size: 29.9 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.DefaultListSelectionModel;
27import javax.swing.JList;
28import javax.swing.JMenuItem;
29import javax.swing.JPopupMenu;
30import javax.swing.ListSelectionModel;
31import javax.swing.SwingUtilities;
32import javax.swing.event.ListDataEvent;
33import javax.swing.event.ListDataListener;
34import javax.swing.event.ListSelectionEvent;
35import javax.swing.event.ListSelectionListener;
36
37import org.openstreetmap.josm.Main;
38import org.openstreetmap.josm.actions.AutoScaleAction;
39import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
40import org.openstreetmap.josm.actions.relation.EditRelationAction;
41import org.openstreetmap.josm.actions.relation.SelectInRelationListAction;
42import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
43import org.openstreetmap.josm.data.SelectionChangedListener;
44import org.openstreetmap.josm.data.osm.Node;
45import org.openstreetmap.josm.data.osm.OsmPrimitive;
46import org.openstreetmap.josm.data.osm.OsmPrimitiveComparator;
47import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
48import org.openstreetmap.josm.data.osm.Relation;
49import org.openstreetmap.josm.data.osm.Way;
50import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
51import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
52import org.openstreetmap.josm.data.osm.event.DataSetListener;
53import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
54import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
55import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
56import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
57import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
58import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
59import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
60import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
61import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
62import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
63import org.openstreetmap.josm.gui.DefaultNameFormatter;
64import org.openstreetmap.josm.gui.MapView;
65import org.openstreetmap.josm.gui.MapView.EditLayerChangeListener;
66import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
67import org.openstreetmap.josm.gui.PopupMenuHandler;
68import org.openstreetmap.josm.gui.SideButton;
69import org.openstreetmap.josm.gui.layer.OsmDataLayer;
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 SelectionListModel model;
84
85 private SelectAction actSelect;
86 private SearchAction actSearch;
87 private ZoomToJOSMSelectionAction actZoomToJOSMSelection;
88 private ZoomToListSelection actZoomToListSelection;
89 private SelectInRelationListAction actSetRelationSelection;
90 private EditRelationAction actEditRelationSelection;
91 private DownloadSelectedIncompleteMembersAction actDownloadSelectedIncompleteMembers;
92
93 /** the popup menu and its handler */
94 private final ListPopupMenu popupMenu;
95 private final PopupMenuHandler popupMenuHandler;
96
97 /**
98 * Builds the content panel for this dialog
99 */
100 protected void buildContentPanel() {
101 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
102 model = new SelectionListModel(selectionModel);
103 lstPrimitives = new JList(model);
104 lstPrimitives.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
105 lstPrimitives.setSelectionModel(selectionModel);
106 lstPrimitives.setCellRenderer(new OsmPrimitivRenderer());
107 lstPrimitives.setTransferHandler(null); // Fix #6290. Drag & Drop is not supported anyway and Copy/Paste is better propagated to main window
108
109 // the select action
110 final SideButton selectButton = new SideButton(actSelect = new SelectAction());
111 lstPrimitives.getSelectionModel().addListSelectionListener(actSelect);
112 selectButton.createArrow(new ActionListener() {
113 @Override
114 public void actionPerformed(ActionEvent e) {
115 SelectionHistoryPopup.launch(selectButton, model.getSelectionHistory());
116 }
117 });
118
119 // the search button
120 final SideButton searchButton = new SideButton(actSearch = new SearchAction());
121 searchButton.createArrow(new ActionListener() {
122 @Override
123 public void actionPerformed(ActionEvent e) {
124 SearchPopupMenu.launch(searchButton);
125 }
126 });
127
128 createLayout(lstPrimitives, true, Arrays.asList(new SideButton[] {
129 selectButton, searchButton
130 }));
131 }
132
133 public SelectionListDialog() {
134 super(tr("Selection"), "selectionlist", tr("Open a selection list window."),
135 Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}",
136 tr("Current Selection")), KeyEvent.VK_T, Shortcut.ALT_SHIFT),
137 150, // default height
138 true // default is "show dialog"
139 );
140
141 buildContentPanel();
142 model.addListDataListener(new TitleUpdater());
143 actZoomToJOSMSelection = new ZoomToJOSMSelectionAction();
144 model.addListDataListener(actZoomToJOSMSelection);
145
146 actZoomToListSelection = new ZoomToListSelection();
147 actSetRelationSelection = new SelectInRelationListAction();
148 actEditRelationSelection = new EditRelationAction();
149 actDownloadSelectedIncompleteMembers = new DownloadSelectedIncompleteMembersAction();
150
151 popupMenu = new ListPopupMenu(lstPrimitives);
152 popupMenuHandler = setupPopupMenuHandler();
153
154 lstPrimitives.addListSelectionListener(new ListSelectionListener() {
155 @Override
156 public void valueChanged(ListSelectionEvent e) {
157 actZoomToListSelection.valueChanged(e);
158 popupMenuHandler.setPrimitives(model.getSelected());
159 }
160 });
161
162 lstPrimitives.addMouseListener(new SelectionPopupMenuLauncher());
163 lstPrimitives.addMouseListener(new DblClickHandler());
164
165 InputMapUtils.addEnterAction(lstPrimitives, actZoomToListSelection);
166 }
167
168 @Override
169 public void showNotify() {
170 MapView.addEditLayerChangeListener(model);
171 SelectionEventManager.getInstance().addSelectionListener(model, FireMode.IN_EDT_CONSOLIDATED);
172 DatasetEventManager.getInstance().addDatasetListener(model, FireMode.IN_EDT);
173 MapView.addEditLayerChangeListener(actSearch);
174 // editLayerChanged also gets the selection history of the level
175 model.editLayerChanged(null, Main.map.mapView.getEditLayer());
176 if (Main.map.mapView.getEditLayer() != null) {
177 model.setJOSMSelection(Main.map.mapView.getEditLayer().data.getAllSelected());
178 }
179 actSearch.updateEnabledState();
180 }
181
182 @Override
183 public void hideNotify() {
184 MapView.removeEditLayerChangeListener(actSearch);
185 MapView.removeEditLayerChangeListener(model);
186 SelectionEventManager.getInstance().removeSelectionListener(model);
187 DatasetEventManager.getInstance().removeDatasetListener(model);
188 }
189
190 /**
191 * Responds to double clicks on the list of selected objects
192 */
193 class DblClickHandler extends MouseAdapter {
194 @Override
195 public void mouseClicked(MouseEvent e) {
196 if (e.getClickCount() < 2 || ! SwingUtilities.isLeftMouseButton(e)) return;
197 int idx = lstPrimitives.locationToIndex(e.getPoint());
198 if (idx < 0) return;
199 OsmDataLayer layer = Main.main.getEditLayer();
200 if(layer == null) return;
201 layer.data.setSelected(Collections.singleton((OsmPrimitive)model.getElementAt(idx)));
202 }
203 }
204
205 /**
206 * The popup menu launcher
207 */
208 class SelectionPopupMenuLauncher extends PopupMenuLauncher {
209
210 @Override
211 public void launch(MouseEvent evt) {
212 if (model.getSelected().isEmpty()) {
213 int idx = lstPrimitives.locationToIndex(evt.getPoint());
214 if (idx < 0) return;
215 model.setSelected(Collections.singleton((OsmPrimitive)model.getElementAt(idx)));
216 }
217 popupMenu.show(lstPrimitives, evt.getX(), evt.getY());
218 }
219 }
220
221 private final PopupMenuHandler setupPopupMenuHandler() {
222 PopupMenuHandler handler = new PopupMenuHandler(popupMenu);
223 handler.addAction(actZoomToJOSMSelection);
224 handler.addAction(actZoomToListSelection);
225 handler.addSeparator();
226 handler.addAction(actSetRelationSelection);
227 handler.addAction(actEditRelationSelection);
228 handler.addSeparator();
229 handler.addAction(actDownloadSelectedIncompleteMembers);
230 return handler;
231 }
232
233 /**
234 * Replies the popup menu handler.
235 * @return The popup menu handler
236 */
237 public PopupMenuHandler getPopupMenuHandler() {
238 return popupMenuHandler;
239 }
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
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
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 * Replies the collection of OSM primitives in the view
507 * of this model
508 *
509 * @return
510 */
511 public Collection<OsmPrimitive> getAllElements() {
512 return selection;
513 }
514
515 /**
516 * Sets the OSM primitives to be selected in the view of this model
517 *
518 * @param sel the collection of primitives to select
519 */
520 public void setSelected(Collection<OsmPrimitive> sel) {
521 selectionModel.clearSelection();
522 if (sel == null) return;
523 for (OsmPrimitive p: sel){
524 int i = selection.indexOf(p);
525 if (i >= 0){
526 selectionModel.addSelectionInterval(i, i);
527 }
528 }
529 }
530
531 @Override
532 protected void fireContentsChanged(Object source, int index0, int index1) {
533 Collection<OsmPrimitive> sel = getSelected();
534 super.fireContentsChanged(source, index0, index1);
535 setSelected(sel);
536 }
537
538 /**
539 * Sets the collection of currently selected OSM objects
540 *
541 * @param selection the collection of currently selected OSM objects
542 */
543 public void setJOSMSelection(Collection<? extends OsmPrimitive> selection) {
544 this.selection.clear();
545 if (selection == null) {
546 fireContentsChanged(this, 0, getSize());
547 return;
548 }
549 this.selection.addAll(selection);
550 sort();
551 fireContentsChanged(this, 0, getSize());
552 remember(selection);
553 double dist = -1;
554 SubclassFilteredCollection<OsmPrimitive, Way> ways = new SubclassFilteredCollection<OsmPrimitive, Way>(selection, OsmPrimitive.wayPredicate);
555 // Compute total length of selected way(s) until an arbitrary limit set to 250 ways
556 // in order to prevent performance issue if a large number of ways are selected (old behaviour kept in that case, see #8403)
557 int maxWays = Math.max(1, Main.pref.getInteger("selection.max-ways-for-statusline", 250));
558 if (!ways.isEmpty() && ways.size() <= maxWays) {
559 dist = 0.0;
560 for (Way w : ways) {
561 dist += w.getLength();
562 }
563 }
564 Main.map.statusLine.setDist(dist);
565 }
566
567 /**
568 * Triggers a refresh of the view for all primitives in {@code toUpdate}
569 * which are currently displayed in the view
570 *
571 * @param toUpdate the collection of primitives to update
572 */
573 public void update(Collection<? extends OsmPrimitive> toUpdate) {
574 if (toUpdate == null) return;
575 if (toUpdate.isEmpty()) return;
576 Collection<OsmPrimitive> sel = getSelected();
577 for (OsmPrimitive p: toUpdate){
578 int i = selection.indexOf(p);
579 if (i >= 0) {
580 super.fireContentsChanged(this, i,i);
581 }
582 }
583 setSelected(sel);
584 }
585
586 /**
587 * Sorts the current elements in the selection
588 */
589 public void sort() {
590 if (this.selection.size()>Main.pref.getInteger("selection.no_sort_above",100000)) return;
591 if (this.selection.size()>Main.pref.getInteger("selection.fast_sort_above",10000)) {
592 Collections.sort(this.selection, new OsmPrimitiveQuickComparator());
593 } else {
594 Collections.sort(this.selection, new OsmPrimitiveComparator());
595 }
596 }
597
598 /* ------------------------------------------------------------------------ */
599 /* interface EditLayerChangeListener */
600 /* ------------------------------------------------------------------------ */
601 @Override
602 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
603 if (newLayer == null) {
604 setJOSMSelection(null);
605 history = null;
606 } else {
607 history = newLayer.data.getSelectionHistory();
608 setJOSMSelection(newLayer.data.getAllSelected());
609 }
610 }
611
612 /* ------------------------------------------------------------------------ */
613 /* interface SelectionChangeListener */
614 /* ------------------------------------------------------------------------ */
615 @Override
616 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
617 setJOSMSelection(newSelection);
618 }
619
620 /* ------------------------------------------------------------------------ */
621 /* interface DataSetListener */
622 /* ------------------------------------------------------------------------ */
623 @Override
624 public void dataChanged(DataChangedEvent event) {
625 // refresh the whole list
626 fireContentsChanged(this, 0, getSize());
627 }
628
629 @Override
630 public void nodeMoved(NodeMovedEvent event) {
631 // may influence the display name of primitives, update the data
632 update(event.getPrimitives());
633 }
634
635 @Override
636 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
637 // may influence the display name of primitives, update the data
638 update(event.getPrimitives());
639 }
640
641 @Override
642 public void relationMembersChanged(RelationMembersChangedEvent event) {
643 // may influence the display name of primitives, update the data
644 update(event.getPrimitives());
645 }
646
647 @Override
648 public void tagsChanged(TagsChangedEvent event) {
649 // may influence the display name of primitives, update the data
650 update(event.getPrimitives());
651 }
652
653 @Override
654 public void wayNodesChanged(WayNodesChangedEvent event) {
655 // may influence the display name of primitives, update the data
656 update(event.getPrimitives());
657 }
658
659 @Override
660 public void primitivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */}
661 @Override
662 public void primitivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/}
663 }
664
665 /**
666 * A specialized {@link JMenuItem} for presenting one entry of the search history
667 *
668 * @author Jan Peter Stotz
669 */
670 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
671 final protected SearchSetting s;
672
673 public SearchMenuItem(SearchSetting s) {
674 super(s.toString());
675 this.s = s;
676 addActionListener(this);
677 }
678
679 @Override
680 public void actionPerformed(ActionEvent e) {
681 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
682 }
683 }
684
685 /**
686 * The popup menu for the search history entries
687 *
688 */
689 protected static class SearchPopupMenu extends JPopupMenu {
690 static public void launch(Component parent) {
691 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
692 return;
693 JPopupMenu menu = new SearchPopupMenu();
694 Rectangle r = parent.getBounds();
695 menu.show(parent, r.x, r.y + r.height);
696 }
697
698 public SearchPopupMenu() {
699 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
700 add(new SearchMenuItem(ss));
701 }
702 }
703 }
704
705 /**
706 * A specialized {@link JMenuItem} for presenting one entry of the selection history
707 *
708 * @author Jan Peter Stotz
709 */
710 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
711 final private DefaultNameFormatter df = DefaultNameFormatter.getInstance();
712 protected Collection<? extends OsmPrimitive> sel;
713
714 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
715 super();
716 this.sel = sel;
717 int ways = 0;
718 int nodes = 0;
719 int relations = 0;
720 for (OsmPrimitive o : sel) {
721 if (! o.isSelectable()) continue; // skip unselectable primitives
722 if (o instanceof Way) {
723 ways++;
724 } else if (o instanceof Node) {
725 nodes++;
726 } else if (o instanceof Relation) {
727 relations++;
728 }
729 }
730 StringBuffer text = new StringBuffer();
731 if(ways != 0) {
732 text.append(text.length() > 0 ? ", " : "")
733 .append(trn("{0} way", "{0} ways", ways, ways));
734 }
735 if(nodes != 0) {
736 text.append(text.length() > 0 ? ", " : "")
737 .append(trn("{0} node", "{0} nodes", nodes, nodes));
738 }
739 if(relations != 0) {
740 text.append(text.length() > 0 ? ", " : "")
741 .append(trn("{0} relation", "{0} relations", relations, relations));
742 }
743 if(ways + nodes + relations == 0) {
744 text.append(tr("Unselectable now"));
745 this.sel=new ArrayList<OsmPrimitive>(); // empty selection
746 }
747 if(ways + nodes + relations == 1)
748 {
749 text.append(": ");
750 for(OsmPrimitive o : sel) {
751 text.append(o.getDisplayName(df));
752 }
753 setText(text.toString());
754 } else {
755 setText(tr("Selection: {0}", text));
756 }
757 addActionListener(this);
758 }
759
760 @Override
761 public void actionPerformed(ActionEvent e) {
762 Main.main.getCurrentDataSet().setSelected(sel);
763 }
764 }
765
766 /**
767 * The popup menue for the JOSM selection history entries
768 *
769 */
770 protected static class SelectionHistoryPopup extends JPopupMenu {
771 static public void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
772 if (history == null || history.isEmpty()) return;
773 JPopupMenu menu = new SelectionHistoryPopup(history);
774 Rectangle r = parent.getBounds();
775 menu.show(parent, r.x, r.y + r.height);
776 }
777
778 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
779 for (Collection<? extends OsmPrimitive> sel : history) {
780 add(new SelectionMenuItem(sel));
781 }
782 }
783 }
784
785 /** Quicker comparator, comparing just by type and ID's */
786 static private class OsmPrimitiveQuickComparator implements Comparator<OsmPrimitive> {
787
788 private int compareId(OsmPrimitive a, OsmPrimitive b) {
789 long id_a=a.getUniqueId();
790 long id_b=b.getUniqueId();
791 if (id_a<id_b) return -1;
792 if (id_a>id_b) return 1;
793 return 0;
794 }
795
796 private int compareType(OsmPrimitive a, OsmPrimitive b) {
797 // show ways before relations, then nodes
798 if (a.getType().equals(OsmPrimitiveType.WAY)) return -1;
799 if (a.getType().equals(OsmPrimitiveType.NODE)) return 1;
800 // a is a relation
801 if (b.getType().equals(OsmPrimitiveType.WAY)) return 1;
802 // b is a node
803 return -1;
804 }
805
806 @Override
807 public int compare(OsmPrimitive a, OsmPrimitive b) {
808 if (a.getType().equals(b.getType()))
809 return compareId(a, b);
810 return compareType(a, b);
811 }
812 }
813
814}
Note: See TracBrowser for help on using the repository browser.