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

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

fix some minor sonar/code style issues:

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