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

Last change on this file since 582 was 582, checked in by framm, 16 years ago
  • got rid of a few compiler warnings
File size: 9.9 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.AWTEvent;
7import java.awt.Cursor;
8import java.awt.Dimension;
9import java.awt.EventQueue;
10import java.awt.Font;
11import java.awt.GridBagLayout;
12import java.awt.Point;
13import java.awt.Toolkit;
14import java.awt.event.AWTEventListener;
15import java.awt.event.InputEvent;
16import java.awt.event.KeyAdapter;
17import java.awt.event.KeyEvent;
18import java.awt.event.MouseAdapter;
19import java.awt.event.MouseEvent;
20import java.awt.event.MouseMotionListener;
21import java.lang.reflect.InvocationTargetException;
22import java.text.DecimalFormat;
23import java.util.Collection;
24import java.util.ConcurrentModificationException;
25import java.util.Map.Entry;
26
27import javax.swing.BorderFactory;
28import javax.swing.JLabel;
29import javax.swing.JPanel;
30import javax.swing.JTextField;
31import javax.swing.Popup;
32import javax.swing.PopupFactory;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.HelpAction.Helpful;
36import org.openstreetmap.josm.data.coor.LatLon;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
39import org.openstreetmap.josm.tools.GBC;
40
41/**
42 * A component that manages some status information display about the map.
43 * It keeps a status line below the map up to date and displays some tooltip
44 * information if the user hold the mouse long enough at some point.
45 *
46 * All this is done in background to not disturb other processes.
47 *
48 * The background thread does not alter any data of the map (read only thread).
49 * Also it is rather fail safe. In case of some error in the data, it just do
50 * nothing instead of whining and complaining.
51 *
52 * @author imi
53 */
54public class MapStatus extends JPanel implements Helpful {
55
56 /**
57 * The MapView this status belongs.
58 */
59 final MapView mv;
60 /**
61 * The position of the mouse cursor.
62 */
63 DecimalFormat latlon = new DecimalFormat("###0.0000000");
64 JTextField positionText = new JTextField(25);
65
66 /**
67 * The field holding the name of the object under the mouse.
68 */
69 JTextField nameText = new JTextField(30);
70
71 /**
72 * The field holding information about what the user can do.
73 */
74 JTextField helpText = new JTextField();
75
76 /**
77 * The collector class that waits for notification and then update
78 * the display objects.
79 *
80 * @author imi
81 */
82 private final class Collector implements Runnable {
83 /**
84 * The last object displayed in status line.
85 */
86 Collection<OsmPrimitive> osmStatus;
87 /**
88 * The old modifiers, that was pressed the last time this collector ran.
89 */
90 private int oldModifiers;
91 /**
92 * The popup displayed to show additional information
93 */
94 private Popup popup;
95
96 private MapFrame parent;
97
98 public Collector(MapFrame parent) {
99 this.parent = parent;
100 }
101
102 /**
103 * Execution function for the Collector.
104 */
105 public void run() {
106 for (;;) {
107 MouseState ms = new MouseState();
108 synchronized (this) {
109 try {wait();} catch (InterruptedException e) {}
110 ms.modifiers = mouseState.modifiers;
111 ms.mousePos = mouseState.mousePos;
112 }
113 if (parent != Main.map)
114 return; // exit, if new parent.
115 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0 || ms.mousePos == null)
116 continue; // freeze display when holding down ctrl
117
118 if (mv.center == null)
119 continue;
120
121 // This try/catch is a hack to stop the flooding bug reports about this.
122 // The exception needed to handle with in the first place, means that this
123 // access to the data need to be restarted, if the main thread modifies
124 // the data.
125 try {
126 // Popup Information
127 if ((ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0 ) {
128 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos);
129
130 if (osms == null)
131 continue;
132 if (osms != null && osms.equals(osmStatus) && ms.modifiers == oldModifiers)
133 continue;
134 /*
135 osmStatus = osms;
136 oldModifiers = ms.modifiers;
137
138 OsmPrimitive osmNearest = null;
139 // Set the text label in the bottom status bar
140 osmNearest = mv.getNearest(ms.mousePos);
141 if (osmNearest != null) {
142 NameVisitor visitor = new NameVisitor();
143 osmNearest.visit(visitor);
144 nameText.setText(visitor.name);
145 } else
146 nameText.setText("");
147 */
148
149
150 if (popup != null) {
151 try {
152 EventQueue.invokeAndWait(new Runnable() {
153 public void run() {
154 popup.hide();
155 }
156 });
157 } catch (InterruptedException e) {
158 } catch (InvocationTargetException e) {
159 throw new RuntimeException(e);
160 }
161 }
162
163 JPanel c = new JPanel(new GridBagLayout());
164 for (final OsmPrimitive osm : osms) {
165 NameVisitor visitor = new NameVisitor();
166 osm.visit(visitor);
167 final StringBuilder text = new StringBuilder();
168 if (osm.id == 0 || osm.modified)
169 visitor.name = "<i><b>"+visitor.name+"*</b></i>";
170 text.append(visitor.name);
171 if (osm.id != 0)
172 text.append("<br>id="+osm.id);
173 for (Entry<String, String> e : osm.entrySet())
174 text.append("<br>"+e.getKey()+"="+e.getValue());
175 final JLabel l = new JLabel("<html>"+text.toString()+"</html>", visitor.icon, JLabel.HORIZONTAL);
176 l.setFont(l.getFont().deriveFont(Font.PLAIN));
177 l.setVerticalTextPosition(JLabel.TOP);
178 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
179 l.addMouseListener(new MouseAdapter(){
180 @Override public void mouseEntered(MouseEvent e) {
181 l.setText("<html><u color='blue'>"+text.toString()+"</u></html>");
182 }
183 @Override public void mouseExited(MouseEvent e) {
184 l.setText("<html>"+text.toString()+"</html>");
185 }
186 @Override public void mouseClicked(MouseEvent e) {
187 Main.ds.setSelected(osm);
188 mv.repaint();
189 }
190 });
191 c.add(l, GBC.eol());
192 }
193
194 Point p = mv.getLocationOnScreen();
195 popup = PopupFactory.getSharedInstance().getPopup(mv, c, p.x+ms.mousePos.x+16, p.y+ms.mousePos.y+16);
196 final Popup staticPopup = popup;
197 EventQueue.invokeLater(new Runnable(){
198 public void run() {
199 staticPopup.show();
200 }
201 });
202 } else if (popup != null) {
203 final Popup staticPopup = popup;
204 popup = null;
205 EventQueue.invokeLater(new Runnable(){
206 public void run() {
207 staticPopup.hide();
208 }
209 });
210 }
211 } catch (ConcurrentModificationException x) {
212 } catch (NullPointerException x) {
213 }
214 }
215 }
216 }
217
218 /**
219 * Everything, the collector is interested of. Access must be synchronized.
220 * @author imi
221 */
222 class MouseState {
223 Point mousePos;
224 int modifiers;
225 }
226 /**
227 * The last sent mouse movement event.
228 */
229 MouseState mouseState = new MouseState();
230
231 /**
232 * Construct a new MapStatus and attach it to the map view.
233 * @param mv The MapView the status line is part of.
234 */
235 public MapStatus(final MapFrame mapFrame) {
236 this.mv = mapFrame.mapView;
237
238 // Listen for mouse movements and set the position text field
239 mv.addMouseMotionListener(new MouseMotionListener(){
240 public void mouseDragged(MouseEvent e) {
241 mouseMoved(e);
242 }
243 public void mouseMoved(MouseEvent e) {
244 if (mv.center == null)
245 return;
246 // Do not update the view, if ctrl is pressed.
247 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
248 LatLon p = mv.getLatLon(e.getX(),e.getY());
249 positionText.setText(latlon.format(p.lat())+" "+latlon.format(p.lon()));
250 }
251 }
252 });
253
254 positionText.setEditable(false);
255 nameText.setEditable(false);
256 helpText.setEditable(false);
257 setLayout(new GridBagLayout());
258 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
259 add(new JLabel(tr("Lat/Lon")+" "), GBC.std());
260 add(positionText, GBC.std());
261 //add(new JLabel(" "+tr("Object")+" "));
262 //add(nameText);
263 add(helpText, GBC.eol().fill(GBC.HORIZONTAL));
264 positionText.setMinimumSize(new Dimension(positionText.getMinimumSize().height, 200));
265
266 // The background thread
267 final Collector collector = new Collector(mapFrame);
268 new Thread(collector).start();
269
270 // Listen to keyboard/mouse events for pressing/releasing alt key and
271 // inform the collector.
272 try {
273 Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener(){
274 public void eventDispatched(AWTEvent event) {
275 synchronized (collector) {
276 mouseState.modifiers = ((InputEvent)event).getModifiersEx();
277 if (event instanceof MouseEvent)
278 mouseState.mousePos = ((MouseEvent)event).getPoint();
279 collector.notify();
280 }
281 }
282 }, AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
283 } catch (SecurityException ex) {
284 mapFrame.mapView.addMouseMotionListener(new MouseMotionListener() {
285 public void mouseMoved(MouseEvent e) {
286 synchronized (collector) {
287 mouseState.modifiers = e.getModifiersEx();
288 mouseState.mousePos = e.getPoint();
289 collector.notify();
290 }
291 }
292
293 public void mouseDragged(MouseEvent e) {
294 mouseMoved(e);
295 }
296 });
297
298 mapFrame.mapView.addKeyListener(new KeyAdapter() {
299 @Override public void keyPressed(KeyEvent e) {
300 synchronized (collector) {
301 mouseState.modifiers = e.getModifiersEx();
302 collector.notify();
303 }
304 }
305
306 @Override public void keyReleased(KeyEvent e) {
307 keyReleased(e);
308 }
309 });
310 }
311 }
312
313 public String helpTopic() {
314 return "Statusline";
315 }
316
317 public void setHelpText(String t) {
318 helpText.setText(t);
319 }
320}
Note: See TracBrowser for help on using the repository browser.