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

Revision 5137, 31.8 KB checked in by akks, 8 weeks ago (diff)

Andle Snapping improvements: activation on A (Tab can be disabled, see #7438), better projections, show activation in status line

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