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

Last change on this file since 36 was 36, checked in by imi, 20 years ago

added Mercator projection

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