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

Last change on this file since 5886 was 5886, checked in by Don-vip, 11 years ago

see #4429 - Right click menu "undo, cut, copy, paste, delete, select all" for each text component (originally based on patch by NooN)

  • Property svn:eol-style set to native
File size: 32.1 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.Popup;
37import javax.swing.PopupFactory;
38import javax.swing.UIManager;
39
40import org.openstreetmap.josm.Main;
41import org.openstreetmap.josm.data.coor.CoordinateFormat;
42import org.openstreetmap.josm.data.coor.LatLon;
43import org.openstreetmap.josm.data.osm.DataSet;
44import org.openstreetmap.josm.data.osm.OsmPrimitive;
45import org.openstreetmap.josm.gui.help.Helpful;
46import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor;
47import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor.ProgressMonitorDialog;
48import org.openstreetmap.josm.gui.util.GuiHelper;
49import org.openstreetmap.josm.tools.GBC;
50import org.openstreetmap.josm.tools.ImageProvider;
51import org.openstreetmap.josm.gui.widgets.JosmTextField;
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 JosmTextField helpText = new JosmTextField();
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.