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

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