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

Last change on this file since 10465 was 10453, checked in by Don-vip, 8 years ago

fix #13023 - Replace uses of hasEditLayer() with new layer manager (patch by michael2402, modified) - gsoc-core

  • Property svn:eol-style set to native
File size: 33.2 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.getLayerManager().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 new ImageProvider("dialogs", "search").getResource().attachImageIcon(this, true);
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.getLayerManager().getEditLayer() != null);
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.getLayerManager().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.isSelectionEmpty());
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 new ImageProvider("dialogs", "history").getResource().attachImageIcon(this, true);
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.isSelectionEmpty() || 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 new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this, true);
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 new ImageProvider("dialogs/autoscale", "selection").getResource().attachImageIcon(this, true);
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.isSelectionEmpty());
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 * Determines if no OSM primitives are currently selected.
564 * @return {@code true} if no OSM primitives are currently selected
565 * @since 10383
566 */
567 public boolean isSelectionEmpty() {
568 return selectionModel.isSelectionEmpty();
569 }
570
571 /**
572 * Replies the collection of OSM primitives currently selected in the view of this model
573 *
574 * @return choosen elements in the view
575 */
576 public synchronized Collection<OsmPrimitive> getSelected() {
577 Set<OsmPrimitive> sel = new HashSet<>();
578 for (int i = 0; i < getSize(); i++) {
579 if (selectionModel.isSelectedIndex(i)) {
580 sel.add(selection.get(i));
581 }
582 }
583 return sel;
584 }
585
586 /**
587 * Sets the OSM primitives to be selected in the view of this model
588 *
589 * @param sel the collection of primitives to select
590 */
591 public synchronized void setSelected(Collection<OsmPrimitive> sel) {
592 selectionModel.clearSelection();
593 if (sel == null) return;
594 for (OsmPrimitive p: sel) {
595 int i = selection.indexOf(p);
596 if (i >= 0) {
597 selectionModel.addSelectionInterval(i, i);
598 }
599 }
600 }
601
602 @Override
603 protected void fireContentsChanged(Object source, int index0, int index1) {
604 Collection<OsmPrimitive> sel = getSelected();
605 super.fireContentsChanged(source, index0, index1);
606 setSelected(sel);
607 }
608
609 /**
610 * Sets the collection of currently selected OSM objects
611 *
612 * @param selection the collection of currently selected OSM objects
613 */
614 public void setJOSMSelection(final Collection<? extends OsmPrimitive> selection) {
615 synchronized (this) {
616 this.selection.clear();
617 if (selection != null) {
618 this.selection.addAll(selection);
619 sort();
620 }
621 }
622 GuiHelper.runInEDTAndWait(new Runnable() {
623 @Override public void run() {
624 fireContentsChanged(this, 0, getSize());
625 if (selection != null) {
626 remember(selection);
627 if (selection.size() == 2) {
628 Iterator<? extends OsmPrimitive> it = selection.iterator();
629 OsmPrimitive n1 = it.next();
630 OsmPrimitive n2 = it.next();
631 // show distance between two selected nodes with coordinates
632 if (n1 instanceof Node && n2 instanceof Node) {
633 LatLon c1 = ((Node) n1).getCoor();
634 LatLon c2 = ((Node) n2).getCoor();
635 if (c1 != null && c2 != null) {
636 Main.map.statusLine.setDist(c1.greatCircleDistance(c2));
637 return;
638 }
639 }
640 }
641 Main.map.statusLine.setDist(
642 new SubclassFilteredCollection<OsmPrimitive, Way>(selection, OsmPrimitive.wayPredicate));
643 }
644 }
645 });
646 }
647
648 /**
649 * Triggers a refresh of the view for all primitives in {@code toUpdate}
650 * which are currently displayed in the view
651 *
652 * @param toUpdate the collection of primitives to update
653 */
654 public synchronized void update(Collection<? extends OsmPrimitive> toUpdate) {
655 if (toUpdate == null) return;
656 if (toUpdate.isEmpty()) return;
657 Collection<OsmPrimitive> sel = getSelected();
658 for (OsmPrimitive p: toUpdate) {
659 int i = selection.indexOf(p);
660 if (i >= 0) {
661 super.fireContentsChanged(this, i, i);
662 }
663 }
664 setSelected(sel);
665 }
666
667 /**
668 * Sorts the current elements in the selection
669 */
670 public synchronized void sort() {
671 if (this.selection.size() <= Main.pref.getInteger("selection.no_sort_above", 100000)) {
672 boolean quick = this.selection.size() > Main.pref.getInteger("selection.fast_sort_above", 10000);
673 Collections.sort(this.selection, new OsmPrimitiveComparator(quick, false));
674 }
675 }
676
677 /* ------------------------------------------------------------------------ */
678 /* interface ActiveLayerChangeListener */
679 /* ------------------------------------------------------------------------ */
680 @Override
681 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
682 DataSet newData = e.getSource().getEditDataSet();
683 if (newData == null) {
684 setJOSMSelection(null);
685 history = null;
686 } else {
687 history = newData.getSelectionHistory();
688 setJOSMSelection(newData.getAllSelected());
689 }
690 }
691
692 /* ------------------------------------------------------------------------ */
693 /* interface SelectionChangeListener */
694 /* ------------------------------------------------------------------------ */
695 @Override
696 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
697 setJOSMSelection(newSelection);
698 }
699
700 /* ------------------------------------------------------------------------ */
701 /* interface DataSetListener */
702 /* ------------------------------------------------------------------------ */
703 @Override
704 public void dataChanged(DataChangedEvent event) {
705 // refresh the whole list
706 fireContentsChanged(this, 0, getSize());
707 }
708
709 @Override
710 public void nodeMoved(NodeMovedEvent event) {
711 // may influence the display name of primitives, update the data
712 update(event.getPrimitives());
713 }
714
715 @Override
716 public void otherDatasetChange(AbstractDatasetChangedEvent event) {
717 // may influence the display name of primitives, update the data
718 update(event.getPrimitives());
719 }
720
721 @Override
722 public void relationMembersChanged(RelationMembersChangedEvent event) {
723 // may influence the display name of primitives, update the data
724 update(event.getPrimitives());
725 }
726
727 @Override
728 public void tagsChanged(TagsChangedEvent event) {
729 // may influence the display name of primitives, update the data
730 update(event.getPrimitives());
731 }
732
733 @Override
734 public void wayNodesChanged(WayNodesChangedEvent event) {
735 // may influence the display name of primitives, update the data
736 update(event.getPrimitives());
737 }
738
739 @Override
740 public void primitivesAdded(PrimitivesAddedEvent event) {
741 /* ignored - handled by SelectionChangeListener */
742 }
743
744 @Override
745 public void primitivesRemoved(PrimitivesRemovedEvent event) {
746 /* ignored - handled by SelectionChangeListener*/
747 }
748 }
749
750 /**
751 * A specialized {@link JMenuItem} for presenting one entry of the search history
752 *
753 * @author Jan Peter Stotz
754 */
755 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
756 protected final transient SearchSetting s;
757
758 public SearchMenuItem(SearchSetting s) {
759 super(Utils.shortenString(s.toString(),
760 org.openstreetmap.josm.actions.search.SearchAction.MAX_LENGTH_SEARCH_EXPRESSION_DISPLAY));
761 this.s = s;
762 addActionListener(this);
763 }
764
765 @Override
766 public void actionPerformed(ActionEvent e) {
767 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(s);
768 }
769 }
770
771 /**
772 * The popup menu for the search history entries
773 *
774 */
775 protected static class SearchPopupMenu extends JPopupMenu {
776 public static void launch(Component parent) {
777 if (org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory().isEmpty())
778 return;
779 JPopupMenu menu = new SearchPopupMenu();
780 Rectangle r = parent.getBounds();
781 menu.show(parent, r.x, r.y + r.height);
782 }
783
784 /**
785 * Constructs a new {@code SearchPopupMenu}.
786 */
787 public SearchPopupMenu() {
788 for (SearchSetting ss: org.openstreetmap.josm.actions.search.SearchAction.getSearchHistory()) {
789 add(new SearchMenuItem(ss));
790 }
791 }
792 }
793
794 /**
795 * A specialized {@link JMenuItem} for presenting one entry of the selection history
796 *
797 * @author Jan Peter Stotz
798 */
799 protected static class SelectionMenuItem extends JMenuItem implements ActionListener {
800 protected transient Collection<? extends OsmPrimitive> sel;
801
802 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
803 this.sel = sel;
804 int ways = 0;
805 int nodes = 0;
806 int relations = 0;
807 for (OsmPrimitive o : sel) {
808 if (!o.isSelectable()) continue; // skip unselectable primitives
809 if (o instanceof Way) {
810 ways++;
811 } else if (o instanceof Node) {
812 nodes++;
813 } else if (o instanceof Relation) {
814 relations++;
815 }
816 }
817 StringBuilder text = new StringBuilder();
818 if (ways != 0) {
819 text.append(text.length() > 0 ? ", " : "")
820 .append(trn("{0} way", "{0} ways", ways, ways));
821 }
822 if (nodes != 0) {
823 text.append(text.length() > 0 ? ", " : "")
824 .append(trn("{0} node", "{0} nodes", nodes, nodes));
825 }
826 if (relations != 0) {
827 text.append(text.length() > 0 ? ", " : "")
828 .append(trn("{0} relation", "{0} relations", relations, relations));
829 }
830 if (ways + nodes + relations == 0) {
831 text.append(tr("Unselectable now"));
832 this.sel = new ArrayList<>(); // empty selection
833 }
834 DefaultNameFormatter df = DefaultNameFormatter.getInstance();
835 if (ways + nodes + relations == 1) {
836 text.append(": ");
837 for (OsmPrimitive o : sel) {
838 text.append(o.getDisplayName(df));
839 }
840 setText(text.toString());
841 } else {
842 setText(tr("Selection: {0}", text));
843 }
844 addActionListener(this);
845 }
846
847 @Override
848 public void actionPerformed(ActionEvent e) {
849 Main.getLayerManager().getEditDataSet().setSelected(sel);
850 }
851 }
852
853 /**
854 * The popup menu for the JOSM selection history entries
855 */
856 protected static class SelectionHistoryPopup extends JPopupMenu {
857 public static void launch(Component parent, Collection<Collection<? extends OsmPrimitive>> history) {
858 if (history == null || history.isEmpty()) return;
859 JPopupMenu menu = new SelectionHistoryPopup(history);
860 Rectangle r = parent.getBounds();
861 menu.show(parent, r.x, r.y + r.height);
862 }
863
864 public SelectionHistoryPopup(Collection<Collection<? extends OsmPrimitive>> history) {
865 for (Collection<? extends OsmPrimitive> sel : history) {
866 add(new SelectionMenuItem(sel));
867 }
868 }
869 }
870
871 /**
872 * A transfer handler class for drag-and-drop support.
873 */
874 protected class SelectionTransferHandler extends TransferHandler {
875
876 @Override
877 public int getSourceActions(JComponent c) {
878 return COPY;
879 }
880
881 @Override
882 protected Transferable createTransferable(JComponent c) {
883 return new PrimitiveTransferable(getSelectedPrimitives());
884 }
885 }
886}
Note: See TracBrowser for help on using the repository browser.