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

Last change on this file since 4223 was 4223, checked in by stoecker, 13 years ago

fix #6570 (revert change in r4215, see #6547), i18n update, fix typo in function names

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