source: josm/branch/0.5/src/org/openstreetmap/josm/gui/MapStatus.java@ 329

Last change on this file since 329 was 329, checked in by framm, 17 years ago

This commit is a manual merge of all changes that have been made to
the intermediate "core_0.5" branch on the main OSM repository,
bevore JOSM was moved to openstreetmap.de.

Changes incorporated here:

r4464@svn.openstreetmap.org
r4466@svn.openstreetmap.org
r4468@svn.openstreetmap.org
r4469@svn.openstreetmap.org
r4479@svn.openstreetmap.org

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