source: josm/src/org/openstreetmap/josm/gui/MapStatus.java@ 64

Last change on this file since 64 was 64, checked in by imi, 19 years ago
  • renamed track to way
  • refactored Key to be a simple String
  • fixed PropertyDialog which displayed <different> for doubled values
File size: 7.9 KB
Line 
1package org.openstreetmap.josm.gui;
2
3import java.awt.AWTEvent;
4import java.awt.Cursor;
5import java.awt.Font;
6import java.awt.GridBagLayout;
7import java.awt.Point;
8import java.awt.Toolkit;
9import java.awt.event.AWTEventListener;
10import java.awt.event.InputEvent;
11import java.awt.event.MouseAdapter;
12import java.awt.event.MouseEvent;
13import java.awt.event.MouseMotionListener;
14import java.beans.PropertyChangeEvent;
15import java.beans.PropertyChangeListener;
16import java.util.Collection;
17import java.util.ConcurrentModificationException;
18import java.util.Map.Entry;
19
20import javax.swing.BorderFactory;
21import javax.swing.BoxLayout;
22import javax.swing.JLabel;
23import javax.swing.JPanel;
24import javax.swing.JTextField;
25import javax.swing.Popup;
26import javax.swing.PopupFactory;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.data.GeoPoint;
30import org.openstreetmap.josm.data.osm.OsmPrimitive;
31import org.openstreetmap.josm.data.osm.visitor.SelectionComponentVisitor;
32
33/**
34 * A component that manages some status information display about the map.
35 * It keeps a status line below the map up to date and displays some tooltip
36 * information if the user hold the mouse long enough at some point.
37 *
38 * All this is done in background to not disturb other processes.
39 *
40 * The background thread does not alter any data of the map (read only thread).
41 * Also it is rather fail safe. In case of some error in the data, it just do
42 * nothing instead of whining and complaining.
43 *
44 * @author imi
45 */
46public class MapStatus extends JPanel {
47
48 /**
49 * The MapView this status belongs.
50 */
51 final MapView mv;
52 /**
53 * The position of the mouse cursor.
54 */
55 JTextField positionText = new JTextField("-000.00000000000000 -000.00000000000000".length());
56 /**
57 * The field holding the name of the object under the mouse.
58 */
59 JTextField nameText = new JTextField(30);
60
61 /**
62 * The collector class that waits for notification and then update
63 * the display objects.
64 *
65 * @author imi
66 */
67 private final class Collector implements Runnable {
68 /**
69 * The last object displayed in status line.
70 */
71 Collection<OsmPrimitive> osmStatus;
72 /**
73 * The old modifiers, that was pressed the last time this collector ran.
74 */
75 private int oldModifiers;
76 /**
77 * The popup displayed to show additional information
78 */
79 private Popup popup;
80 /**
81 * Signals the collector to shut down on next event.
82 */
83 boolean exitCollector = false;
84
85 /**
86 * Execution function for the Collector.
87 */
88 public void run() {
89 for (;;) {
90 MouseState ms = new MouseState();
91 synchronized (this) {
92 try {wait();} catch (InterruptedException e) {}
93 ms.modifiers = mouseState.modifiers;
94 ms.mousePos = mouseState.mousePos;
95 }
96 if (exitCollector)
97 return;
98 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null)
99 continue; // freeze display when holding down ctrl
100
101 // This try/catch is a hack to stop the flooding bug reports about this.
102 // The exception needed to handle with in the first place, means that this
103 // access to the data need to be restarted, if the main thread modifies
104 // the data.
105 try {
106 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos);
107
108 if (osms == null && osmStatus == null && ms.modifiers == oldModifiers)
109 continue;
110 if (osms != null && osms.equals(osmStatus) && ms.modifiers == oldModifiers)
111 continue;
112
113 osmStatus = osms;
114 oldModifiers = ms.modifiers;
115
116 OsmPrimitive osmNearest = null;
117 // Set the text label in the bottom status bar
118 osmNearest = mv.getNearest(ms.mousePos, (ms.modifiers & MouseEvent.ALT_DOWN_MASK) != 0);
119 if (osmNearest != null) {
120 SelectionComponentVisitor visitor = new SelectionComponentVisitor();
121 osmNearest.visit(visitor);
122 nameText.setText(visitor.name);
123 } else
124 nameText.setText("");
125
126 // Popup Information
127 if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 && osms != null) {
128 if (popup != null)
129 popup.hide();
130
131 JPanel c = new JPanel(new GridBagLayout());
132 for (final OsmPrimitive osm : osms) {
133 SelectionComponentVisitor visitor = new SelectionComponentVisitor();
134 osm.visit(visitor);
135 final StringBuilder text = new StringBuilder();
136 if (osm.id == 0 || osm.modified || osm.modifiedProperties)
137 visitor.name = "<i><b>"+visitor.name+"*</b></i>";
138 text.append(visitor.name);
139 if (osm.id != 0)
140 text.append("<br>id="+osm.id);
141 if (osm.keys != null)
142 for (Entry<String, String> e : osm.keys.entrySet())
143 text.append("<br>"+e.getKey()+"="+e.getValue());
144 final JLabel l = new JLabel("<html>"+text.toString()+"</html>", visitor.icon, JLabel.HORIZONTAL);
145 l.setFont(l.getFont().deriveFont(Font.PLAIN));
146 l.setVerticalTextPosition(JLabel.TOP);
147 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
148 l.addMouseListener(new MouseAdapter(){
149 @Override
150 public void mouseEntered(MouseEvent e) {
151 l.setText("<html><u color='blue'>"+text.toString()+"</u></html>");
152 }
153 @Override
154 public void mouseExited(MouseEvent e) {
155 l.setText("<html>"+text.toString()+"</html>");
156 }
157 @Override
158 public void mouseClicked(MouseEvent e) {
159 Main.main.ds.clearSelection();
160 osm.setSelected(true);
161 mv.repaint();
162 }
163 });
164 c.add(l, GBC.eol());
165 }
166
167 Point p = mv.getLocationOnScreen();
168 popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16);
169 popup.show();
170 } else if (popup != null) {
171 popup.hide();
172 popup = null;
173 }
174 } catch (ConcurrentModificationException x) {
175 }
176 }
177 }
178 }
179
180 /**
181 * Everything, the collector is interested of. Access must be synchronized.
182 * @author imi
183 */
184 class MouseState {
185 Point mousePos;
186 int modifiers;
187 }
188 /**
189 * The last sent mouse movement event.
190 */
191 MouseState mouseState = new MouseState();
192
193 /**
194 * Construct a new MapStatus and attach it to the map view.
195 * @param mv The MapView the status line is part of.
196 */
197 public MapStatus(final MapFrame mapFrame) {
198 this.mv = mapFrame.mapView;
199
200 // Listen for mouse movements and set the position text field
201 mv.addMouseMotionListener(new MouseMotionListener(){
202 public void mouseDragged(MouseEvent e) {
203 mouseMoved(e);
204 }
205 public void mouseMoved(MouseEvent e) {
206 // Do not update the view, if ctrl is pressed.
207 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
208 GeoPoint p = mv.getPoint(e.getX(),e.getY(),true);
209 positionText.setText(p.lat+" "+p.lon);
210 }
211 }
212 });
213
214 positionText.setEditable(false);
215 nameText.setEditable(false);
216 setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
217 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
218 add(new JLabel("Lat/Lon "));
219 add(positionText);
220 add(new JLabel(" Object "));
221 add(nameText);
222
223 // The background thread
224 final Collector collector = new Collector();
225 new Thread(collector).start();
226
227 // Listen to keyboard/mouse events for pressing/releasing alt key and
228 // inform the collector.
229 Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
230 public void eventDispatched(AWTEvent event) {
231 synchronized (collector) {
232 mouseState.modifiers = ((InputEvent)event).getModifiersEx();
233 if (event instanceof MouseEvent)
234 mouseState.mousePos = ((MouseEvent)event).getPoint();
235 collector.notify();
236 }
237 }
238 }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
239
240 // listen for shutdowns to cancel the background thread
241 mapFrame.addPropertyChangeListener("visible", new PropertyChangeListener(){
242 public void propertyChange(PropertyChangeEvent evt) {
243 if (evt.getNewValue() == Boolean.FALSE) {
244 collector.exitCollector = true;
245 synchronized (collector) {
246 collector.notify();
247 }
248 }
249 }
250 });
251 }
252}
Note: See TracBrowser for help on using the repository browser.