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

Last change on this file since 5200 was 5200, checked in by akks, 12 years ago

see #7626, fix #7463: keys Ctrl-Shift-Up/Down, Enter, Spacebar work better in toggle dialogs
Enter and Spacebar = useful actions for list items (select, toggle, etc.)

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