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

Last change on this file since 4408 was 4408, checked in by simon04, 13 years ago

fix #6773 - shortcuts for History and Advanced info dialog

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