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

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

fix #8574: relation list dialog side buttons din not work
Сontext menu unification (see #7846): relation list and membership table of properties dialog

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