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

Last change on this file since 3103 was 3103, checked in by Gubaer, 14 years ago

fixed #4699: selectionlist gets out of sync

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