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

Last change on this file since 3290 was 3290, checked in by stoecker, 14 years ago

better support for relation handling in selection list

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