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

Last change on this file since 3146 was 3146, checked in by jttt, 14 years ago

Use referrers when loading members of primitive in PropertiesDialog

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