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

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

Fix #3952 Search history not remembered across restarts

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