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

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

Fixed #4330 java.lang.ArrayIndexOutOfBoundsException: 6 >= 6, added SelectionEventManager (similar to DatasetEventManager)

  • Property svn:eol-style set to native
File size: 13.6 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.marktr;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trn;
7
8import java.awt.BorderLayout;
9import java.awt.Color;
10import java.awt.Rectangle;
11import java.awt.event.ActionEvent;
12import java.awt.event.ActionListener;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseAdapter;
15import java.awt.event.MouseEvent;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.LinkedList;
19import java.util.NoSuchElementException;
20
21import javax.swing.BorderFactory;
22import javax.swing.DefaultListModel;
23import javax.swing.JList;
24import javax.swing.JMenuItem;
25import javax.swing.JPanel;
26import javax.swing.JPopupMenu;
27import javax.swing.JScrollPane;
28import javax.swing.ListSelectionModel;
29import javax.swing.SwingConstants;
30import javax.swing.plaf.basic.BasicArrowButton;
31
32import org.openstreetmap.josm.Main;
33import org.openstreetmap.josm.actions.AutoScaleAction;
34import org.openstreetmap.josm.actions.search.SearchAction;
35import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
36import org.openstreetmap.josm.data.SelectionChangedListener;
37import org.openstreetmap.josm.data.osm.DataSet;
38import org.openstreetmap.josm.data.osm.Node;
39import org.openstreetmap.josm.data.osm.OsmPrimitive;
40import org.openstreetmap.josm.data.osm.Relation;
41import org.openstreetmap.josm.data.osm.Way;
42import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
43import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
44import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor;
45import org.openstreetmap.josm.gui.MapView;
46import org.openstreetmap.josm.gui.OsmPrimitivRenderer;
47import org.openstreetmap.josm.gui.SideButton;
48import org.openstreetmap.josm.gui.layer.OsmDataLayer;
49import org.openstreetmap.josm.tools.Shortcut;
50
51/**
52 * A small tool dialog for displaying the current selection. The selection manager
53 * respects clicks into the selection list. Ctrl-click will remove entries from
54 * the list while single click will make the clicked entry the only selection.
55 *
56 * @author imi
57 */
58public class SelectionListDialog extends ToggleDialog implements SelectionChangedListener, MapView.EditLayerChangeListener {
59
60 private static final int SELECTION_HISTORY_SIZE = 10;
61
62 /**
63 * The selection's list data.
64 */
65 private final DefaultListModel list = new DefaultListModel();
66
67 private LinkedList<Collection<? extends OsmPrimitive>> selectionHistory;
68
69 /**
70 * The display list.
71 */
72 private JList displaylist = new JList(list);
73 private SideButton selectButton;
74 private SideButton searchButton;
75 private JPopupMenu popupMenu;
76 private JMenuItem zoomToElement;
77
78 /**
79 * If the selection changed event is triggered with newSelection equals
80 * this element, the newSelection will not be added to the selection history
81 */
82 private Collection<? extends OsmPrimitive> historyIgnoreSelection = null;
83
84 public SelectionListDialog() {
85 super(tr("Current Selection"), "selectionlist", tr("Open a selection list window."),
86 Shortcut.registerShortcut("subwindow:selection", tr("Toggle: {0}", tr("Current Selection")), KeyEvent.VK_T, Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150, true);
87
88 selectionHistory = new LinkedList<Collection<? extends OsmPrimitive>>();
89 popupMenu = new JPopupMenu();
90 displaylist.setCellRenderer(new OsmPrimitivRenderer());
91 displaylist.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
92 displaylist.addMouseListener(new MouseAdapter() {
93 @Override
94 public void mouseClicked(MouseEvent e) {
95 if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
96 updateMap();
97 }
98 }
99
100 @Override
101 public void mousePressed(MouseEvent e) {
102 showPopupMenu(e);
103 }
104
105 @Override
106 public void mouseReleased(MouseEvent e) {
107 showPopupMenu(e);
108 }
109
110 });
111
112 add(new JScrollPane(displaylist), BorderLayout.CENTER);
113
114 JPanel buttonPanel = getButtonPanel(2);
115
116 selectButton = new SideButton(marktr("Select"), "select", "SelectionList",
117 tr("Set the selected elements on the map to the selected items in the list above."),
118 new ActionListener() {
119 public void actionPerformed(ActionEvent e) {
120 updateMap();
121 }
122 });
123 buttonPanel.add(selectButton);
124 BasicArrowButton selectionHistoryMenuButton = createArrowButton(selectButton);
125 selectionHistoryMenuButton.addActionListener(new ActionListener() {
126 public void actionPerformed(ActionEvent e) {
127 showSelectionHistoryMenu();
128 }
129 });
130 add(buttonPanel, BorderLayout.SOUTH);
131
132 zoomToElement = new JMenuItem(tr("Zoom to selected element(s)"));
133 zoomToElement.addActionListener(new ActionListener() {
134 public void actionPerformed(ActionEvent e) {
135 zoomToSelectedElement();
136 }
137 });
138
139 searchButton = new SideButton(marktr("Search"), "search", "SelectionList", tr("Search for objects."),
140 Main.main.menu.search);
141 buttonPanel.add(searchButton);
142
143 BasicArrowButton searchHistoryMenuButton = createArrowButton(searchButton);
144 searchHistoryMenuButton.addActionListener(new ActionListener() {
145 public void actionPerformed(ActionEvent e) {
146 showSearchHistoryMenu();
147 }
148 });
149
150 popupMenu.add(zoomToElement);
151 JMenuItem zoomToSelection = new JMenuItem(tr("Zoom to selection"));
152 zoomToSelection.addActionListener(new ActionListener() {
153 public void actionPerformed(ActionEvent e) {
154 zoomToSelection();
155 }
156 });
157 popupMenu.add(zoomToSelection);
158
159 if (Main.main.getCurrentDataSet() != null) {
160 selectionChanged(Main.main.getCurrentDataSet().getSelected());
161 }
162
163 }
164
165 @Override
166 public void showNotify() {
167 SelectionEventManager.getInstance().addSelectionListener(this, FireMode.IN_EDT_CONSOLIDATED);
168 MapView.addEditLayerChangeListener(this);
169 updateSelection();
170 }
171
172 @Override
173 public void hideNotify() {
174 SelectionEventManager.getInstance().removeSelectionListener(this);
175 MapView.removeEditLayerChangeListener(this);
176 }
177
178 private BasicArrowButton createArrowButton(SideButton parentButton) {
179 BasicArrowButton arrowButton = new BasicArrowButton(SwingConstants.SOUTH, null, null, Color.BLACK, null);
180 arrowButton.setBorder(BorderFactory.createEmptyBorder());
181 parentButton.setLayout(new BorderLayout());
182 parentButton.add(arrowButton, BorderLayout.EAST);
183 return arrowButton;
184 }
185
186 @Override
187 public void setVisible(boolean b) {
188 super.setVisible(b);
189 if (b && Main.main.getCurrentDataSet() != null) {
190 selectionChanged(Main.main.getCurrentDataSet().getSelected());
191 }
192 }
193
194 protected void showPopupMenu(MouseEvent e) {
195 if (e.isPopupTrigger()) {
196 zoomToElement.setVisible(displaylist.getSelectedIndex() >= 0);
197 popupMenu.show(e.getComponent(), e.getX(), e.getY());
198 }
199 }
200
201 public void zoomToSelection() {
202 new AutoScaleAction("selection").actionPerformed(null);
203 }
204
205 /**
206 * Zooms to the element(s) selected in {@link #displaylist}
207 */
208 public void zoomToSelectedElement() {
209 BoundingXYVisitor box = new BoundingXYVisitor();
210 int[] selected = displaylist.getSelectedIndices();
211 if (selected.length == 0)
212 return;
213 for (int i = 0; i < selected.length; i++) {
214 Object o = list.get(selected[i]);
215 if (o instanceof OsmPrimitive) {
216 ((OsmPrimitive) o).visit(box);
217 }
218 }
219 if (box.getBounds() == null)
220 return;
221 box.enlargeBoundingBox();
222 Main.map.mapView.recalculateCenterScale(box);
223 }
224
225 private void showSelectionHistoryMenu() {
226 if (selectionHistory.size() == 0)
227 return;
228 JPopupMenu historyMenu = new JPopupMenu();
229 for (Collection<? extends OsmPrimitive> sel : selectionHistory) {
230 SelectionMenuItem item = new SelectionMenuItem(sel);
231 historyMenu.add(item);
232 }
233 Rectangle r = selectButton.getBounds();
234 historyMenu.show(selectButton, r.x, r.y + r.height);
235 }
236
237 private void showSearchHistoryMenu() {
238 if (SearchAction.searchHistory.size() == 0)
239 return;
240 JPopupMenu historyMenu = new JPopupMenu();
241 for (SearchAction.SearchSetting s : SearchAction.searchHistory) {
242 SearchMenuItem item = new SearchMenuItem(s);
243 historyMenu.add(item);
244 }
245 Rectangle r = searchButton.getBounds();
246 historyMenu.show(searchButton, r.x, r.y + r.height);
247 }
248
249 /**
250 * Called when the selection in the dataset changed.
251 * @param newSelection The new selection array.
252 */
253 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
254 if (list == null || !isVisible())
255 return; // selection changed may be received in base class constructor before init
256 OsmPrimitive selArr[] = DataSet.sort(newSelection);
257 list.setSize(selArr.length);
258 int i = 0;
259 for (OsmPrimitive osm : selArr) {
260 list.setElementAt(osm, i++);
261 }
262
263 int ways = 0;
264 int nodes = 0;
265 int relations = 0;
266 for (OsmPrimitive o : newSelection) {
267 if (o instanceof Way) {
268 ways++;
269 } else if (o instanceof Node) {
270 nodes++;
271 } else if (o instanceof Relation) {
272 relations++;
273 }
274 }
275
276 if( (nodes+ways+relations) != 0) {
277 setTitle(tr("Sel.: Rel.:{0} / Ways:{1} / Nodes:{2}", relations, ways, nodes));
278 } else {
279 setTitle(tr("Selection"));
280 }
281
282 if (selectionHistory != null && newSelection.size() > 0 && !newSelection.equals(historyIgnoreSelection)) {
283 historyIgnoreSelection = null;
284 try {
285 // Check if the newSelection has already been added to the history
286 Collection<? extends OsmPrimitive> first = selectionHistory.getFirst();
287 if (first.equals(newSelection))
288 return;
289 } catch (NoSuchElementException e) {
290 }
291 selectionHistory.addFirst(newSelection);
292 while (selectionHistory.size() > SELECTION_HISTORY_SIZE) {
293 selectionHistory.removeLast();
294 }
295 }
296 }
297
298 /**
299 * Sets the selection of the map to the current selected items.
300 */
301 public void updateMap() {
302 Collection<OsmPrimitive> sel = new LinkedList<OsmPrimitive>();
303 for (int i = 0; i < list.getSize(); ++i)
304 if (displaylist.isSelectedIndex(i)) {
305 sel.add((OsmPrimitive) list.get(i));
306 }
307 Main.main.getCurrentDataSet().setSelected(sel);
308 }
309
310 /**
311 * A specialized {@link JMenuItem} for presenting one entry of the selection history
312 *
313 * @author Jan Peter Stotz
314 */
315 protected class SelectionMenuItem extends JMenuItem implements ActionListener {
316 protected Collection<? extends OsmPrimitive> sel;
317
318 public SelectionMenuItem(Collection<? extends OsmPrimitive> sel) {
319 super();
320 this.sel = sel;
321 int ways = 0;
322 int nodes = 0;
323 int relations = 0;
324 for (OsmPrimitive o : sel) {
325 if (o instanceof Way) {
326 ways++;
327 } else if (o instanceof Node) {
328 nodes++;
329 } else if (o instanceof Relation) {
330 relations++;
331 }
332 }
333 String text = "";
334 if(ways != 0) {
335 text += (text.length() > 0 ? ", " : "")
336 + trn("{0} way", "{0} ways", ways, ways);
337 }
338 if(nodes != 0) {
339 text += (text.length() > 0 ? ", " : "")
340 + trn("{0} node", "{0} nodes", nodes, nodes);
341 }
342 if(relations != 0) {
343 text += (text.length() > 0 ? ", " : "")
344 + trn("{0} relation", "{0} relations", relations, relations);
345 }
346 setText(tr("Selection: {0}", text));
347 addActionListener(this);
348 }
349
350 public void actionPerformed(ActionEvent e) {
351 historyIgnoreSelection = sel;
352 Main.main.getCurrentDataSet().setSelected(sel);
353 }
354
355 }
356
357 /**
358 * A specialized {@link JMenuItem} for presenting one entry of the search history
359 *
360 * @author Jan Peter Stotz
361 */
362 protected static class SearchMenuItem extends JMenuItem implements ActionListener {
363 protected SearchSetting s;
364
365 public SearchMenuItem(SearchSetting s) {
366 super(s.toString());
367 this.s = s;
368 addActionListener(this);
369 }
370
371 public void actionPerformed(ActionEvent e) {
372 SearchAction.searchWithoutHistory(s);
373 }
374
375 }
376
377 private void updateSelection() {
378 if (Main.main.getCurrentDataSet() == null) {
379 selectionChanged(Collections.<OsmPrimitive>emptyList());
380 } else {
381 selectionChanged(Main.main.getCurrentDataSet().getSelected());
382 }
383 }
384
385 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
386 updateSelection();
387 }
388}
Note: See TracBrowser for help on using the repository browser.