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

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

some shortcut fixes

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