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

Last change on this file since 10332 was 10332, checked in by stoecker, 8 years ago

Do not use deprecated EditLayerChangedListener - patch my Michael Zangl - gsoc-core - fix #12924

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