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

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

fix synchronization issue seen in unit test

  • Property svn:eol-style set to native
File size: 32.0 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 synchronized void setJOSMSelection(final Collection<? extends OsmPrimitive> selection) {
601 this.selection.clear();
602 if (selection != null) {
603 this.selection.addAll(selection);
604 sort();
605 }
606 GuiHelper.runInEDTAndWait(new Runnable() {
607 @Override public void run() {
608 fireContentsChanged(this, 0, getSize());
609 if (selection != null) {
610 remember(selection);
611 if (selection.size() == 2) {
612 Iterator<? extends OsmPrimitive> it = selection.iterator();
613 OsmPrimitive n1 = it.next();
614 OsmPrimitive n2 = it.next();
615 // show distance between two selected nodes with coordinates
616 if (n1 instanceof Node && n2 instanceof Node) {
617 LatLon c1 = ((Node) n1).getCoor();
618 LatLon c2 = ((Node) n2).getCoor();
619 if (c1 != null && c2 != null) {
620 Main.map.statusLine.setDist(c1.greatCircleDistance(c2));
621 return;
622 }
623 }
624 }
625 Main.map.statusLine.setDist(
626 new SubclassFilteredCollection<OsmPrimitive, Way>(selection, OsmPrimitive.wayPredicate));
627 }
628 }
629 });
630 }
631
632 /**
633 * Triggers a refresh of the view for all primitives in {@code toUpdate}
634 * which are currently displayed in the view
635 *
636 * @param toUpdate the collection of primitives to update
637 */
638 public synchronized void update(Collection<? extends OsmPrimitive> toUpdate) {
639 if (toUpdate == null) return;
640 if (toUpdate.isEmpty()) return;
641 Collection<OsmPrimitive> sel = getSelected();
642 for (OsmPrimitive p: toUpdate){
643 int i = selection.indexOf(p);
644 if (i >= 0) {
645 super.fireContentsChanged(this, i,i);
646 }
647 }
648 setSelected(sel);
649 }
650
651 /**
652 * Sorts the current elements in the selection
653 */
654 public synchronized void sort() {
655 if (this.selection.size() <= Main.pref.getInteger("selection.no_sort_above", 100000)) {
656 boolean quick = this.selection.size() > Main.pref.getInteger("selection.fast_sort_above", 10000);
657 Collections.sort(this.selection, new OsmPrimitiveComparator(quick, false));
658 }
659 }
660
661 /* ------------------------------------------------------------------------ */
662 /* interface EditLayerChangeListener */
663 /* ------------------------------------------------------------------------ */
664 @Override
665 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
666 if (newLayer == null) {
667 setJOSMSelection(null);
668 history = null;
669 } else {
670 history = newLayer.data.getSelectionHistory();
671 setJOSMSelection(newLayer.data.getAllSelected());
672 }
673 }
674
675 /* ------------------------------------------------------------------------ */
676 /* interface SelectionChangeListener */
677 /* ------------------------------------------------------------------------ */
678 @Override
679 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
680 setJOSMSelection(newSelection);
681 }
682
683 /* ------------------------------------------------------------------------ */
684 /* interface DataSetListener */
685 /* ------------------------------------------------------------------------ */
686 @Override
687 public void dataChanged(DataChangedEvent event) {
688 // refresh the whole list
689 fireContentsChanged(this, 0, getSize());
690 }
691
692 @Override
693 public void nodeMoved(NodeMovedEvent event) {
694 // may influence the display name of primitives, update the data
695 update(event.getPrimitives());
696 }
697
698 @Override
699 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
700 // may influence the display name of primitives, update the data
701 update(event.getPrimitives());
702 }
703
704 @Override
705 public void relationMembersChanged(RelationMembersChangedEvent event) {
706 // may influence the display name of primitives, update the data
707 update(event.getPrimitives());
708 }
709
710 @Override
711 public void tagsChanged(TagsChangedEvent event) {
712 // may influence the display name of primitives, update the data
713 update(event.getPrimitives());
714 }
715
716 @Override
717 public void wayNodesChanged(WayNodesChangedEvent event) {
718 // may influence the display name of primitives, update the data
719 update(event.getPrimitives());
720 }
721
722 @Override
723 public void primitivesAdded(PrimitivesAddedEvent event) {
724 /* ignored - handled by SelectionChangeListener */
725 }
726
727 @Override
728 public void primitivesRemoved(PrimitivesRemovedEvent event) {
729 /* ignored - handled by SelectionChangeListener*/
730 }
731 }
732
733 /**
734 * A specialized {@link JMenuItem} for presenting one entry of the search history
735 *
736 * @author Jan Peter Stotz
737 */
738 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
739 protected final transient SearchSetting s;
740
741 public SearchMenuItem(SearchSetting s) {
742 super(Utils.shortenString(s.toString(),
743 org.openstreetmap.josm.actions.search.SearchAction.MAX_LENGTH_SEARCH_EXPRESSION_DISPLAY));
744 this.s = s;
745 addActionListener(this);
746 }
747
748 @Override
749 public void actionPerformed(ActionEvent e) {
750 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
751 }
752 }
753
754 /**
755 * The popup menu for the search history entries
756 *
757 */
758 protected static class SearchPopupMenu extends JPopupMenu {
759 public static void launch(Component parent) {
760 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
761 return;
762 JPopupMenu menu = new SearchPopupMenu();
763 Rectangle r = parent.getBounds();
764 menu.show(parent, r.x, r.y + r.height);
765 }
766
767 public SearchPopupMenu() {
768 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
769 add(new SearchMenuItem(ss));
770 }
771 }
772 }
773
774 /**
775 * A specialized {@link JMenuItem} for presenting one entry of the selection history
776 *
777 * @author Jan Peter Stotz
778 */
779 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
780 private final transient DefaultNameFormatter df = DefaultNameFormatter.getInstance();
781 protected transient Collection<? extends OsmPrimitive> sel;
782
783 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
784 super();
785 this.sel = sel;
786 int ways = 0;
787 int nodes = 0;
788 int relations = 0;
789 for (OsmPrimitive o : sel) {
790 if (! o.isSelectable()) continue; // skip unselectable primitives
791 if (o instanceof Way) {
792 ways++;
793 } else if (o instanceof Node) {
794 nodes++;
795 } else if (o instanceof Relation) {
796 relations++;
797 }
798 }
799 StringBuilder text = new StringBuilder();
800 if(ways != 0) {
801 text.append(text.length() > 0 ? ", " : "")
802 .append(trn("{0} way", "{0} ways", ways, ways));
803 }
804 if(nodes != 0) {
805 text.append(text.length() > 0 ? ", " : "")
806 .append(trn("{0} node", "{0} nodes", nodes, nodes));
807 }
808 if(relations != 0) {
809 text.append(text.length() > 0 ? ", " : "")
810 .append(trn("{0} relation", "{0} relations", relations, relations));
811 }
812 if(ways + nodes + relations == 0) {
813 text.append(tr("Unselectable now"));
814 this.sel=new ArrayList<>(); // empty selection
815 }
816 if(ways + nodes + relations == 1)
817 {
818 text.append(": ");
819 for(OsmPrimitive o : sel) {
820 text.append(o.getDisplayName(df));
821 }
822 setText(text.toString());
823 } else {
824 setText(tr("Selection: {0}", text));
825 }
826 addActionListener(this);
827 }
828
829 @Override
830 public void actionPerformed(ActionEvent e) {
831 Main.main.getCurrentDataSet().setSelected(sel);
832 }
833 }
834
835 /**
836 * The popup menu for the JOSM selection history entries
837 */
838 protected static class SelectionHistoryPopup extends JPopupMenu {
839 public static void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
840 if (history == null || history.isEmpty()) return;
841 JPopupMenu menu = new SelectionHistoryPopup(history);
842 Rectangle r = parent.getBounds();
843 menu.show(parent, r.x, r.y + r.height);
844 }
845
846 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
847 for (Collection<? extends OsmPrimitive> sel : history) {
848 add(new SelectionMenuItem(sel));
849 }
850 }
851 }
852}
Note: See TracBrowser for help on using the repository browser.