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

Last change on this file since 5704 was 5534, checked in by bastiK, 12 years ago

increase maximum resolution of progress bar
manual repaint is required, otherwise it will display
at most 100 steps (at least in Metal L&F)

  • Property svn:eol-style set to native
File size: 32.0 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.AWTEvent;
8import java.awt.Color;
9import java.awt.Component;
10import java.awt.Cursor;
11import java.awt.Dimension;
12import java.awt.EventQueue;
13import java.awt.Font;
14import java.awt.GridBagLayout;
15import java.awt.Point;
16import java.awt.SystemColor;
17import java.awt.Toolkit;
18import java.awt.event.AWTEventListener;
19import java.awt.event.InputEvent;
20import java.awt.event.KeyAdapter;
21import java.awt.event.KeyEvent;
22import java.awt.event.MouseAdapter;
23import java.awt.event.MouseEvent;
24import java.awt.event.MouseListener;
25import java.awt.event.MouseMotionListener;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.ConcurrentModificationException;
29import java.util.List;
30
31import javax.swing.BorderFactory;
32import javax.swing.JLabel;
33import javax.swing.JPanel;
34import javax.swing.JProgressBar;
35import javax.swing.JScrollPane;
36import javax.swing.JTextField;
37import javax.swing.Popup;
38import javax.swing.PopupFactory;
39import javax.swing.UIManager;
40
41import org.openstreetmap.josm.Main;
42import org.openstreetmap.josm.data.coor.CoordinateFormat;
43import org.openstreetmap.josm.data.coor.LatLon;
44import org.openstreetmap.josm.data.osm.DataSet;
45import org.openstreetmap.josm.data.osm.OsmPrimitive;
46import org.openstreetmap.josm.gui.help.Helpful;
47import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
48import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor.ProgressMonitorDialog;
49import org.openstreetmap.josm.gui.util.GuiHelper;
50import org.openstreetmap.josm.tools.GBC;
51import org.openstreetmap.josm.tools.ImageProvider;
52
53/**
54 * A component that manages some status information display about the map.
55 * It keeps a status line below the map up to date and displays some tooltip
56 * information if the user hold the mouse long enough at some point.
57 *
58 * All this is done in background to not disturb other processes.
59 *
60 * The background thread does not alter any data of the map (read only thread).
61 * Also it is rather fail safe. In case of some error in the data, it just does
62 * nothing instead of whining and complaining.
63 *
64 * @author imi
65 */
66public class MapStatus extends JPanel implements Helpful {
67
68 /**
69 * The MapView this status belongs to.
70 */
71 final MapView mv;
72 final Collector collector;
73
74 /**
75 * A small user interface component that consists of an image label and
76 * a fixed text content to the right of the image.
77 */
78 static class ImageLabel extends JPanel {
79 static Color backColor = Color.decode("#b8cfe5");
80 static Color backColorActive = Color.decode("#aaff5e");
81
82 private JLabel tf;
83 private int chars;
84 public ImageLabel(String img, String tooltip, int chars) {
85 super();
86 setLayout(new GridBagLayout());
87 setBackground(backColor);
88 add(new JLabel(ImageProvider.get("statusline/"+img+".png")), GBC.std().anchor(GBC.WEST).insets(0,1,1,0));
89 add(tf = new JLabel(), GBC.std().fill(GBC.BOTH).anchor(GBC.WEST).insets(2,1,1,0));
90 setToolTipText(tooltip);
91 this.chars = chars;
92 }
93 public void setText(String t) {
94 tf.setText(t);
95 }
96 @Override public Dimension getPreferredSize() {
97 return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getPreferredSize().height);
98 }
99 @Override public Dimension getMinimumSize() {
100 return new Dimension(25 + chars*tf.getFontMetrics(tf.getFont()).charWidth('0'), super.getMinimumSize().height);
101 }
102 }
103
104 public class BackgroundProgressMonitor implements ProgressMonitorDialog {
105
106 private String title;
107 private String customText;
108
109 private void updateText() {
110 if (customText != null && !customText.isEmpty()) {
111 progressBar.setToolTipText(tr("{0} ({1})", title, customText));
112 } else {
113 progressBar.setToolTipText(title);
114 }
115 }
116
117 public void setVisible(boolean visible) {
118 progressBar.setVisible(visible);
119 }
120
121 public void updateProgress(int progress) {
122 progressBar.setValue(progress);
123 progressBar.repaint();
124 MapStatus.this.doLayout();
125 }
126
127 public void setCustomText(String text) {
128 this.customText = text;
129 updateText();
130 }
131
132 public void setCurrentAction(String text) {
133 this.title = text;
134 updateText();
135 }
136
137 public void setIndeterminate(boolean newValue) {
138 UIManager.put("ProgressBar.cycleTime", UIManager.getInt("ProgressBar.repaintInterval") * 100);
139 progressBar.setIndeterminate(newValue);
140 }
141
142 @Override
143 public void appendLogMessage(String message) {
144 if (message != null && !message.isEmpty()) {
145 System.out.println("appendLogMessage not implemented for background tasks. Message was: " + message);
146 }
147 }
148
149 }
150
151 final ImageLabel lonText = new ImageLabel("lon", tr("The geographic longitude at the mouse pointer."), 11);
152 final ImageLabel nameText = new ImageLabel("name", tr("The name of the object at the mouse pointer."), 20);
153 final JTextField helpText = new JTextField();
154 final ImageLabel latText = new ImageLabel("lat", tr("The geographic latitude at the mouse pointer."), 11);
155 final ImageLabel angleText = new ImageLabel("angle", tr("The angle between the previous and the current way segment."), 6);
156 final ImageLabel headingText = new ImageLabel("heading", tr("The (compass) heading of the line segment being drawn."), 6);
157 final ImageLabel distText = new ImageLabel("dist", tr("The length of the new way segment being drawn."), 10);
158 final JProgressBar progressBar = new JProgressBar();
159 public final BackgroundProgressMonitor progressMonitor = new BackgroundProgressMonitor();
160
161 /**
162 * This is the thread that runs in the background and collects the information displayed.
163 * It gets destroyed by MapFrame.java/destroy() when the MapFrame itself is destroyed.
164 */
165 public Thread thread;
166
167 private final List<StatusTextHistory> statusText = new ArrayList<StatusTextHistory>();
168
169 private static class StatusTextHistory {
170 final Object id;
171 final String text;
172
173 public StatusTextHistory(Object id, String text) {
174 this.id = id;
175 this.text = text;
176 }
177
178 @Override
179 public boolean equals(Object obj) {
180 return obj instanceof StatusTextHistory && ((StatusTextHistory)obj).id == id;
181 }
182
183 @Override
184 public int hashCode() {
185 return System.identityHashCode(id);
186 }
187 }
188
189 /**
190 * The collector class that waits for notification and then update
191 * the display objects.
192 *
193 * @author imi
194 */
195 private final class Collector implements Runnable {
196 /**
197 * the mouse position of the previous iteration. This is used to show
198 * the popup until the cursor is moved.
199 */
200 private Point oldMousePos;
201 /**
202 * Contains the labels that are currently shown in the information
203 * popup
204 */
205 private List<JLabel> popupLabels = null;
206 /**
207 * The popup displayed to show additional information
208 */
209 private Popup popup;
210
211 private MapFrame parent;
212
213 public Collector(MapFrame parent) {
214 this.parent = parent;
215 }
216
217 /**
218 * Execution function for the Collector.
219 */
220 public void run() {
221 registerListeners();
222 try {
223 for (;;) {
224
225 final MouseState ms = new MouseState();
226 synchronized (this) {
227 // TODO Would be better if the timeout wasn't necessary
228 try {wait(1000);} catch (InterruptedException e) {}
229 ms.modifiers = mouseState.modifiers;
230 ms.mousePos = mouseState.mousePos;
231 }
232 if (parent != Main.map)
233 return; // exit, if new parent.
234
235 // Do nothing, if required data is missing
236 if(ms.mousePos == null || mv.center == null) {
237 continue;
238 }
239
240 try {
241 EventQueue.invokeAndWait(new Runnable() {
242
243 @Override
244 public void run() {
245 // Freeze display when holding down CTRL
246 if ((ms.modifiers & MouseEvent.CTRL_DOWN_MASK) != 0) {
247 // update the information popup's labels though, because
248 // the selection might have changed from the outside
249 popupUpdateLabels();
250 return;
251 }
252
253 // This try/catch is a hack to stop the flooding bug reports about this.
254 // The exception needed to handle with in the first place, means that this
255 // access to the data need to be restarted, if the main thread modifies
256 // the data.
257 DataSet ds = null;
258 // The popup != null check is required because a left-click
259 // produces several events as well, which would make this
260 // variable true. Of course we only want the popup to show
261 // if the middle mouse button has been pressed in the first
262 // place
263 boolean mouseNotMoved = oldMousePos != null
264 && oldMousePos.equals(ms.mousePos);
265 boolean isAtOldPosition = mouseNotMoved && popup != null;
266 boolean middleMouseDown = (ms.modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0;
267 try {
268 ds = mv.getCurrentDataSet();
269 if (ds != null) {
270 // This is not perfect, if current dataset was changed during execution, the lock would be useless
271 if(isAtOldPosition && middleMouseDown) {
272 // Write lock is necessary when selecting in popupCycleSelection
273 // locks can not be upgraded -> if do read lock here and write lock later (in OsmPrimitive.updateFlags)
274 // then always occurs deadlock (#5814)
275 ds.beginUpdate();
276 } else {
277 ds.getReadLock().lock();
278 }
279 }
280
281 // Set the text label in the bottom status bar
282 // "if mouse moved only" was added to stop heap growing
283 if (!mouseNotMoved) statusBarElementUpdate(ms);
284
285
286 // Popup Information
287 // display them if the middle mouse button is pressed and
288 // keep them until the mouse is moved
289 if (middleMouseDown || isAtOldPosition)
290 {
291 Collection<OsmPrimitive> osms = mv.getAllNearest(ms.mousePos, OsmPrimitive.isUsablePredicate);
292
293 if (osms == null)
294 return;
295
296 final JPanel c = new JPanel(new GridBagLayout());
297 final JLabel lbl = new JLabel(
298 "<html>"+tr("Middle click again to cycle through.<br>"+
299 "Hold CTRL to select directly from this list with the mouse.<hr>")+"</html>",
300 null,
301 JLabel.HORIZONTAL
302 );
303 lbl.setHorizontalAlignment(JLabel.LEFT);
304 c.add(lbl, GBC.eol().insets(2, 0, 2, 0));
305
306 // Only cycle if the mouse has not been moved and the
307 // middle mouse button has been pressed at least twice
308 // (the reason for this is the popup != null check for
309 // isAtOldPosition, see above. This is a nice side
310 // effect though, because it does not change selection
311 // of the first middle click)
312 if(isAtOldPosition && middleMouseDown) {
313 // Hand down mouse modifiers so the SHIFT mod can be
314 // handled correctly (see funcion)
315 popupCycleSelection(osms, ms.modifiers);
316 }
317
318 // These labels may need to be updated from the outside
319 // so collect them
320 List<JLabel> lbls = new ArrayList<JLabel>();
321 for (final OsmPrimitive osm : osms) {
322 JLabel l = popupBuildPrimitiveLabels(osm);
323 lbls.add(l);
324 c.add(l, GBC.eol().fill(GBC.HORIZONTAL).insets(2, 0, 2, 2));
325 }
326
327 popupShowPopup(popupCreatePopup(c, ms), lbls);
328 } else {
329 popupHidePopup();
330 }
331
332 oldMousePos = ms.mousePos;
333 } catch (ConcurrentModificationException x) {
334 //x.printStackTrace();
335 } catch (NullPointerException x) {
336 //x.printStackTrace();
337 } finally {
338 if (ds != null) {
339 if(isAtOldPosition && middleMouseDown) {
340 ds.endUpdate();
341 } else {
342 ds.getReadLock().unlock();
343 }
344 }
345 }
346 }
347 });
348 } catch (Exception e) {
349
350 }
351 }
352 } finally {
353 unregisterListeners();
354 }
355 }
356
357 /**
358 * Creates a popup for the given content next to the cursor. Tries to
359 * keep the popup on screen and shows a vertical scrollbar, if the
360 * screen is too small.
361 * @param content
362 * @param ms
363 * @return popup
364 */
365 private final Popup popupCreatePopup(Component content, MouseState ms) {
366 Point p = mv.getLocationOnScreen();
367 Dimension scrn = Toolkit.getDefaultToolkit().getScreenSize();
368
369 // Create a JScrollPane around the content, in case there's not
370 // enough space
371 JScrollPane sp = new JScrollPane(content);
372 sp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
373 sp.setBorder(BorderFactory.createRaisedBevelBorder());
374 // Implement max-size content-independent
375 Dimension prefsize = sp.getPreferredSize();
376 int w = Math.min(prefsize.width, Math.min(800, (scrn.width/2) - 16));
377 int h = Math.min(prefsize.height, scrn.height - 10);
378 sp.setPreferredSize(new Dimension(w, h));
379
380 int xPos = p.x + ms.mousePos.x + 16;
381 // Display the popup to the left of the cursor if it would be cut
382 // off on its right, but only if more space is available
383 if(xPos + w > scrn.width && xPos > scrn.width/2) {
384 xPos = p.x + ms.mousePos.x - 4 - w;
385 }
386 int yPos = p.y + ms.mousePos.y + 16;
387 // Move the popup up if it would be cut off at its bottom but do not
388 // move it off screen on the top
389 if(yPos + h > scrn.height - 5) {
390 yPos = Math.max(5, scrn.height - h - 5);
391 }
392
393 PopupFactory pf = PopupFactory.getSharedInstance();
394 return pf.getPopup(mv, sp, xPos, yPos);
395 }
396
397 /**
398 * Calls this to update the element that is shown in the statusbar
399 * @param ms
400 */
401 private final void statusBarElementUpdate(MouseState ms) {
402 final OsmPrimitive osmNearest = mv.getNearestNodeOrWay(ms.mousePos, OsmPrimitive.isUsablePredicate, false);
403 if (osmNearest != null) {
404 nameText.setText(osmNearest.getDisplayName(DefaultNameFormatter.getInstance()));
405 } else {
406 nameText.setText(tr("(no object)"));
407 }
408 }
409
410 /**
411 * Call this with a set of primitives to cycle through them. Method
412 * will automatically select the next item and update the map
413 * @param osms
414 * @param mouse modifiers
415 */
416 private final void popupCycleSelection(Collection<OsmPrimitive> osms, int mods) {
417 DataSet ds = Main.main.getCurrentDataSet();
418 // Find some items that are required for cycling through
419 OsmPrimitive firstItem = null;
420 OsmPrimitive firstSelected = null;
421 OsmPrimitive nextSelected = null;
422 for (final OsmPrimitive osm : osms) {
423 if(firstItem == null) {
424 firstItem = osm;
425 }
426 if(firstSelected != null && nextSelected == null) {
427 nextSelected = osm;
428 }
429 if(firstSelected == null && ds.isSelected(osm)) {
430 firstSelected = osm;
431 }
432 }
433
434 // Clear previous selection if SHIFT (add to selection) is not
435 // pressed. Cannot use "setSelected()" because it will cause a
436 // fireSelectionChanged event which is unnecessary at this point.
437 if((mods & MouseEvent.SHIFT_DOWN_MASK) == 0) {
438 ds.clearSelection();
439 }
440
441 // This will cycle through the available items.
442 if(firstSelected == null) {
443 ds.addSelected(firstItem);
444 } else {
445 ds.clearSelection(firstSelected);
446 if(nextSelected != null) {
447 ds.addSelected(nextSelected);
448 }
449 }
450 }
451
452 /**
453 * Tries to hide the given popup
454 * @param popup
455 */
456 private final void popupHidePopup() {
457 popupLabels = null;
458 if(popup == null)
459 return;
460 final Popup staticPopup = popup;
461 popup = null;
462 EventQueue.invokeLater(new Runnable(){
463 public void run() { staticPopup.hide(); }});
464 }
465
466 /**
467 * Tries to show the given popup, can be hidden using popupHideOldPopup
468 * If an old popup exists, it will be automatically hidden
469 * @param popup
470 */
471 private final void popupShowPopup(Popup newPopup, List<JLabel> lbls) {
472 final Popup staticPopup = newPopup;
473 if(this.popup != null) {
474 // If an old popup exists, remove it when the new popup has been
475 // drawn to keep flickering to a minimum
476 final Popup staticOldPopup = this.popup;
477 EventQueue.invokeLater(new Runnable(){
478 public void run() {
479 staticPopup.show();
480 staticOldPopup.hide();
481 }
482 });
483 } else {
484 // There is no old popup
485 EventQueue.invokeLater(new Runnable(){
486 public void run() { staticPopup.show(); }});
487 }
488 this.popupLabels = lbls;
489 this.popup = newPopup;
490 }
491
492 /**
493 * This method should be called if the selection may have changed from
494 * outside of this class. This is the case when CTRL is pressed and the
495 * user clicks on the map instead of the popup.
496 */
497 private final void popupUpdateLabels() {
498 if(this.popup == null || this.popupLabels == null)
499 return;
500 for(JLabel l : this.popupLabels) {
501 l.validate();
502 }
503 }
504
505 /**
506 * Sets the colors for the given label depending on the selected status of
507 * the given OsmPrimitive
508 *
509 * @param lbl The label to color
510 * @param osm The primitive to derive the colors from
511 */
512 private final void popupSetLabelColors(JLabel lbl, OsmPrimitive osm) {
513 DataSet ds = Main.main.getCurrentDataSet();
514 if(ds.isSelected(osm)) {
515 lbl.setBackground(SystemColor.textHighlight);
516 lbl.setForeground(SystemColor.textHighlightText);
517 } else {
518 lbl.setBackground(SystemColor.control);
519 lbl.setForeground(SystemColor.controlText);
520 }
521 }
522
523 /**
524 * Builds the labels with all necessary listeners for the info popup for the
525 * given OsmPrimitive
526 * @param osm The primitive to create the label for
527 * @return
528 */
529 private final JLabel popupBuildPrimitiveLabels(final OsmPrimitive osm) {
530 final StringBuilder text = new StringBuilder();
531 String name = osm.getDisplayName(DefaultNameFormatter.getInstance());
532 if (osm.isNewOrUndeleted() || osm.isModified()) {
533 name = "<i><b>"+ name + "*</b></i>";
534 }
535 text.append(name);
536
537 boolean idShown = Main.pref.getBoolean("osm-primitives.showid");
538 // fix #7557 - do not show ID twice
539
540 if (!osm.isNew() && !idShown) {
541 text.append(" [id="+osm.getId()+"]");
542 }
543
544 if(osm.getUser() != null) {
545 text.append(" [" + tr("User:") + " " + osm.getUser().getName() + "]");
546 }
547
548 for (String key : osm.keySet()) {
549 text.append("<br>" + key + "=" + osm.get(key));
550 }
551
552 final JLabel l = new JLabel(
553 "<html>" +text.toString() + "</html>",
554 ImageProvider.get(osm.getDisplayType()),
555 JLabel.HORIZONTAL
556 ) {
557 // This is necessary so the label updates its colors when the
558 // selection is changed from the outside
559 @Override public void validate() {
560 super.validate();
561 popupSetLabelColors(this, osm);
562 }
563 };
564 l.setOpaque(true);
565 popupSetLabelColors(l, osm);
566 l.setFont(l.getFont().deriveFont(Font.PLAIN));
567 l.setVerticalTextPosition(JLabel.TOP);
568 l.setHorizontalAlignment(JLabel.LEFT);
569 l.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
570 l.addMouseListener(new MouseAdapter(){
571 @Override public void mouseEntered(MouseEvent e) {
572 l.setBackground(SystemColor.info);
573 l.setForeground(SystemColor.infoText);
574 }
575 @Override public void mouseExited(MouseEvent e) {
576 popupSetLabelColors(l, osm);
577 }
578 @Override public void mouseClicked(MouseEvent e) {
579 DataSet ds = Main.main.getCurrentDataSet();
580 // Let the user toggle the selection
581 ds.toggleSelected(osm);
582 l.validate();
583 }
584 });
585 // Sometimes the mouseEntered event is not catched, thus the label
586 // will not be highlighted, making it confusing. The MotionListener
587 // can correct this defect.
588 l.addMouseMotionListener(new MouseMotionListener() {
589 public void mouseMoved(MouseEvent e) {
590 l.setBackground(SystemColor.info);
591 l.setForeground(SystemColor.infoText);
592 }
593 public void mouseDragged(MouseEvent e) {
594 l.setBackground(SystemColor.info);
595 l.setForeground(SystemColor.infoText);
596 }
597 });
598 return l;
599 }
600 }
601
602 /**
603 * Everything, the collector is interested of. Access must be synchronized.
604 * @author imi
605 */
606 static class MouseState {
607 Point mousePos;
608 int modifiers;
609 }
610 /**
611 * The last sent mouse movement event.
612 */
613 MouseState mouseState = new MouseState();
614
615 private AWTEventListener awtListener = new AWTEventListener() {
616 public void eventDispatched(AWTEvent event) {
617 if (event instanceof InputEvent &&
618 ((InputEvent)event).getComponent() == mv) {
619 synchronized (collector) {
620 mouseState.modifiers = ((InputEvent)event).getModifiersEx();
621 if (event instanceof MouseEvent) {
622 mouseState.mousePos = ((MouseEvent)event).getPoint();
623 }
624 collector.notify();
625 }
626 }
627 }
628 };
629
630 private MouseMotionListener mouseMotionListener = new MouseMotionListener() {
631 public void mouseMoved(MouseEvent e) {
632 synchronized (collector) {
633 mouseState.modifiers = e.getModifiersEx();
634 mouseState.mousePos = e.getPoint();
635 collector.notify();
636 }
637 }
638
639 public void mouseDragged(MouseEvent e) {
640 mouseMoved(e);
641 }
642 };
643
644 private KeyAdapter keyAdapter = new KeyAdapter() {
645 @Override public void keyPressed(KeyEvent e) {
646 synchronized (collector) {
647 mouseState.modifiers = e.getModifiersEx();
648 collector.notify();
649 }
650 }
651
652 @Override public void keyReleased(KeyEvent e) {
653 keyPressed(e);
654 }
655 };
656
657 private void registerListeners() {
658 // Listen to keyboard/mouse events for pressing/releasing alt key and
659 // inform the collector.
660 try {
661 Toolkit.getDefaultToolkit().addAWTEventListener(awtListener,
662 AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
663 } catch (SecurityException ex) {
664 mv.addMouseMotionListener(mouseMotionListener);
665 mv.addKeyListener(keyAdapter);
666 }
667 }
668
669 private void unregisterListeners() {
670 try {
671 Toolkit.getDefaultToolkit().removeAWTEventListener(awtListener);
672 } catch (SecurityException e) {
673 // Don't care, awtListener probably wasn't registered anyway
674 }
675 mv.removeMouseMotionListener(mouseMotionListener);
676 mv.removeKeyListener(keyAdapter);
677 }
678
679
680 /**
681 * Construct a new MapStatus and attach it to the map view.
682 * @param mapFrame The MapFrame the status line is part of.
683 */
684 public MapStatus(final MapFrame mapFrame) {
685 this.mv = mapFrame.mapView;
686 this.collector = new Collector(mapFrame);
687
688 lonText.addMouseListener(Main.main.menu.jumpToAct);
689 latText.addMouseListener(Main.main.menu.jumpToAct);
690
691 // Listen for mouse movements and set the position text field
692 mv.addMouseMotionListener(new MouseMotionListener(){
693 public void mouseDragged(MouseEvent e) {
694 mouseMoved(e);
695 }
696 public void mouseMoved(MouseEvent e) {
697 if (mv.center == null)
698 return;
699 // Do not update the view if ctrl is pressed.
700 if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) == 0) {
701 CoordinateFormat mCord = CoordinateFormat.getDefaultFormat();
702 LatLon p = mv.getLatLon(e.getX(),e.getY());
703 latText.setText(p.latToString(mCord));
704 lonText.setText(p.lonToString(mCord));
705 }
706 }
707 });
708
709 setLayout(new GridBagLayout());
710 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
711
712 add(latText, GBC.std());
713 add(lonText, GBC.std().insets(3,0,0,0));
714 add(headingText, GBC.std().insets(3,0,0,0));
715 add(angleText, GBC.std().insets(3,0,0,0));
716 add(distText, GBC.std().insets(3,0,0,0));
717
718 helpText.setEditable(false);
719 add(nameText, GBC.std().insets(3,0,0,0));
720 add(helpText, GBC.std().insets(3,0,0,0).fill(GBC.HORIZONTAL));
721
722 progressBar.setMaximum(PleaseWaitProgressMonitor.PROGRESS_BAR_MAX);
723 progressBar.setVisible(false);
724 GBC gbc = GBC.eol();
725 gbc.ipadx = 100;
726 add(progressBar,gbc);
727 progressBar.addMouseListener(new MouseAdapter() {
728 @Override
729 public void mouseClicked(MouseEvent e) {
730 PleaseWaitProgressMonitor monitor = Main.currentProgressMonitor;
731 if (monitor != null) {
732 monitor.showForegroundDialog();
733 }
734 }
735 });
736
737 // The background thread
738 thread = new Thread(collector, "Map Status Collector");
739 thread.setDaemon(true);
740 thread.start();
741 }
742
743 public JPanel getAnglePanel() {
744 return angleText;
745 }
746
747 public String helpTopic() {
748 return ht("/Statusline");
749 }
750
751 @Override
752 public synchronized void addMouseListener(MouseListener ml) {
753 //super.addMouseListener(ml);
754 lonText.addMouseListener(ml);
755 latText.addMouseListener(ml);
756 }
757
758 public void setHelpText(String t) {
759 setHelpText(null, t);
760 }
761 public void setHelpText(Object id, final String text) {
762
763 StatusTextHistory entry = new StatusTextHistory(id, text);
764
765 statusText.remove(entry);
766 statusText.add(entry);
767
768 GuiHelper.runInEDT(new Runnable() {
769 @Override
770 public void run() {
771 helpText.setText(text);
772 helpText.setToolTipText(text);
773 }
774 });
775 }
776 public void resetHelpText(Object id) {
777 if (statusText.isEmpty())
778 return;
779
780 StatusTextHistory entry = new StatusTextHistory(id, null);
781 if (statusText.get(statusText.size() - 1).equals(entry)) {
782 if (statusText.size() == 1) {
783 setHelpText("");
784 } else {
785 StatusTextHistory history = statusText.get(statusText.size() - 2);
786 setHelpText(history.id, history.text);
787 }
788 }
789 statusText.remove(entry);
790 }
791 public void setAngle(double a) {
792 angleText.setText(a < 0 ? "--" : Math.round(a*10)/10.0 + " \u00B0");
793 }
794 public void setHeading(double h) {
795 headingText.setText(h < 0 ? "--" : Math.round(h*10)/10.0 + " \u00B0");
796 }
797 public void setDist(double dist) {
798 distText.setText(dist < 0 ? "--" : NavigatableComponent.getDistText(dist));
799 }
800 public void activateAnglePanel(boolean activeFlag) {
801 angleText.setBackground(activeFlag ? ImageLabel.backColorActive : ImageLabel.backColor);
802 }
803}
Note: See TracBrowser for help on using the repository browser.