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

Last change on this file since 6742 was 6742, checked in by simon04, 10 years ago

fix #7686 - Shorten long search expression for display purposes (in dialogs, tooltips)

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