]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/MapChart.java
Version 5, May 2008
[GpsPrune.git] / tim / prune / gui / MapChart.java
1 package tim.prune.gui;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.FontMetrics;
6 import java.awt.Graphics;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.awt.event.KeyEvent;
10 import java.awt.event.KeyListener;
11 import java.awt.event.MouseEvent;
12 import java.awt.event.MouseMotionListener;
13 import java.awt.event.MouseWheelEvent;
14 import java.awt.event.MouseWheelListener;
15 import java.awt.image.BufferedImage;
16
17 import javax.swing.JCheckBoxMenuItem;
18 import javax.swing.JMenuItem;
19 import javax.swing.JPopupMenu;
20
21 import tim.prune.App;
22 import tim.prune.DataSubscriber;
23 import tim.prune.I18nManager;
24 import tim.prune.data.DataPoint;
25 import tim.prune.data.TrackInfo;
26
27
28 /**
29  * Display component for the main map
30  */
31 public class MapChart extends GenericChart implements MouseWheelListener, KeyListener, MouseMotionListener
32 {
33         // Constants
34         private static final int POINT_RADIUS = 4;
35         private static final int CLICK_SENSITIVITY = 10;
36         private static final double ZOOM_SCALE_FACTOR = 1.4;
37         private static final int PAN_DISTANCE = 10;
38         private static final int LIMIT_WAYPOINT_NAMES = 40;
39
40         // Colours
41         private static final Color COLOR_BG         = Color.WHITE;
42         private static final Color COLOR_POINT      = Color.BLUE;
43         private static final Color COLOR_CURR_RANGE = Color.GREEN;
44         private static final Color COLOR_CROSSHAIRS = Color.RED;
45         private static final Color COLOR_WAYPT_NAME = Color.BLACK;
46
47         // Instance variables
48         private App _app = null;
49         private BufferedImage _image = null;
50         private JPopupMenu _popup = null;
51         private JCheckBoxMenuItem _autoPanMenuItem = null;
52         private JCheckBoxMenuItem _connectPointsMenuItem = null;
53         private int _numPoints = -1;
54         private double _scale;
55         private double _offsetX, _offsetY, _zoomScale;
56         private int _lastSelectedPoint = -1;
57         private int _dragStartX = -1, _dragStartY = -1;
58         private int _zoomDragFromX = -1, _zoomDragFromY = -1;
59         private int _zoomDragToX = -1, _zoomDragToY = -1;
60         private boolean _zoomDragging = false;
61
62
63         /**
64          * Constructor
65          * @param inApp App object for callbacks
66          * @param inTrackInfo track info object
67          */
68         public MapChart(App inApp, TrackInfo inTrackInfo)
69         {
70                 super(inTrackInfo);
71                 _app = inApp;
72                 makePopup();
73                 addMouseListener(this);
74                 addMouseWheelListener(this);
75                 addMouseMotionListener(this);
76                 setFocusable(true);
77                 addKeyListener(this);
78                 MINIMUM_SIZE = new Dimension(200, 250);
79                 _zoomScale = 1.0;
80         }
81
82
83         /**
84          * Override track updating to refresh image
85          */
86         public void dataUpdated(byte inUpdateType)
87         {
88                 // Check if number of points has changed or data has been edited
89                 if (_track.getNumPoints() != _numPoints || (inUpdateType & DATA_EDITED) > 0)
90                 {
91                         _image = null;
92                         _lastSelectedPoint = -1;
93                         _numPoints = _track.getNumPoints();
94                 }
95                 super.dataUpdated(inUpdateType);
96         }
97
98
99         /**
100          * Override paint method to draw map
101          * @param inG graphics object
102          */
103         public void paint(Graphics inG)
104         {
105                 if (_track == null)
106                 {
107                         super.paint(inG);
108                         return;
109                 }
110
111                 int width = getWidth();
112                 int height = getHeight();
113                 int x, y;
114
115                 // Find x and y ranges, and scale to fit
116                 double scaleX = (_track.getXRange().getMaximum() - _track.getXRange().getMinimum())
117                   / (width - 2 * (BORDER_WIDTH + POINT_RADIUS));
118                 double scaleY = (_track.getYRange().getMaximum() - _track.getYRange().getMinimum())
119                   / (height - 2 * (BORDER_WIDTH + POINT_RADIUS));
120                 _scale = scaleX;
121                 if (scaleY > _scale) _scale = scaleY;
122
123                 // Autopan if necessary
124                 int selectedPoint = _trackInfo.getSelection().getCurrentPointIndex();
125                 if (_autoPanMenuItem.isSelected() && selectedPoint >= 0 && selectedPoint != _lastSelectedPoint)
126                 {
127                         // Autopan is enabled and a point is selected - work out x and y to see if it's within range
128                         x = width/2 + (int) ((_track.getX(selectedPoint) - _offsetX) / _scale * _zoomScale);
129                         y = height/2 - (int) ((_track.getY(selectedPoint) - _offsetY) / _scale * _zoomScale);
130                         if (x <= BORDER_WIDTH)
131                         {
132                                 // autopan left
133                                 _offsetX -= (width / 4 - x) * _scale / _zoomScale;
134                                 _image = null;
135                         }
136                         else if (x >= (width - BORDER_WIDTH))
137                         {
138                                 // autopan right
139                                 _offsetX += (x - width * 3/4) * _scale / _zoomScale;
140                                 _image = null;
141                         }
142                         if (y <= BORDER_WIDTH)
143                         {
144                                 // autopan up
145                                 _offsetY += (height / 4 - y) * _scale / _zoomScale;
146                                 _image = null;
147                         }
148                         else if (y >= (height - BORDER_WIDTH))
149                         {
150                                 // autopan down
151                                 _offsetY -= (y - height * 3/4) * _scale / _zoomScale;
152                                 _image = null;
153                         }
154                 }
155                 _lastSelectedPoint = selectedPoint;
156
157                 // Create background if necessary
158                 if (_image == null || width != _image.getWidth() || height != _image.getHeight())
159                 {
160                         createBackgroundImage();
161                 }
162                 // return if image has been set to null by other thread
163                 if (_image == null) {return;}
164
165                 // draw buffered image onto g
166                 inG.drawImage(_image, 0, 0, width, height, COLOR_BG, null);
167
168                 // draw selected range, if any
169                 if (_trackInfo.getSelection().hasRangeSelected() && !_zoomDragging)
170                 {
171                         int rangeStart = _trackInfo.getSelection().getStart();
172                         int rangeEnd = _trackInfo.getSelection().getEnd();
173                         inG.setColor(COLOR_CURR_RANGE);
174                         for (int i=rangeStart; i<=rangeEnd; i++)
175                         {
176                                 x = width/2 + (int) ((_track.getX(i) - _offsetX) / _scale * _zoomScale);
177                                 y = height/2 - (int) ((_track.getY(i) - _offsetY) / _scale * _zoomScale);
178                                 if (x > BORDER_WIDTH && x < (width - BORDER_WIDTH)
179                                         && y < (height - BORDER_WIDTH) && y > BORDER_WIDTH)
180                                 {
181                                         inG.drawRect(x - 2, y - 2, 4, 4);
182                                 }
183                         }
184                 }
185
186                 // Highlight selected point
187                 if (selectedPoint >= 0 && !_zoomDragging)
188                 {
189                         inG.setColor(COLOR_CROSSHAIRS);
190                         x = width/2 + (int) ((_track.getX(selectedPoint) - _offsetX) / _scale * _zoomScale);
191                         y = height/2 - (int) ((_track.getY(selectedPoint) - _offsetY) / _scale * _zoomScale);
192                         if (x > BORDER_WIDTH && x < (width - BORDER_WIDTH)
193                                 && y < (height - BORDER_WIDTH) && y > BORDER_WIDTH)
194                         {
195                                 // Draw cross-hairs for current point
196                                 inG.drawLine(x, BORDER_WIDTH, x, height - BORDER_WIDTH);
197                                 inG.drawLine(BORDER_WIDTH, y, width - BORDER_WIDTH, y);
198
199                                 // Show selected point afterwards to make sure it's on top
200                                 inG.drawOval(x - 2, y - 2, 4, 4);
201                                 inG.drawOval(x - 3, y - 3, 6, 6);
202                         }
203                 }
204
205                 // Draw rectangle for dragging zoom area
206                 if (_zoomDragging)
207                 {
208                         inG.setColor(COLOR_CROSSHAIRS);
209                         inG.drawLine(_zoomDragFromX, _zoomDragFromY, _zoomDragFromX, _zoomDragToY);
210                         inG.drawLine(_zoomDragFromX, _zoomDragFromY, _zoomDragToX, _zoomDragFromY);
211                         inG.drawLine(_zoomDragToX, _zoomDragFromY, _zoomDragToX, _zoomDragToY);
212                         inG.drawLine(_zoomDragFromX, _zoomDragToY, _zoomDragToX, _zoomDragToY);
213                 }
214
215                 // Attempt to grab keyboard focus if possible
216                 //requestFocus();  (causes problems here)
217         }
218
219
220         /**
221          * Plot the points onto an offscreen image
222          * which doesn't have to be redrawn when the selection changes
223          */
224         private void createBackgroundImage()
225         {
226                 int width = getWidth();
227                 int height = getHeight();
228                 int x, y;
229                 int lastX = 0, lastY = 0;
230                 // Initialise image
231                 if (_image == null || _image.getWidth() != width || _image.getHeight() != height) {
232                         _image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
233                 }
234                 Graphics bufferedG = _image.getGraphics();
235                 super.paint(bufferedG);
236
237                 // Loop and show all points
238                 int numPoints = _track.getNumPoints();
239                 bufferedG.setColor(COLOR_POINT);
240                 int halfWidth = width/2;
241                 int halfHeight = height/2;
242                 boolean currPointTrackpoint = false, lastPointTrackpoint = false;
243                 for (int i=0; i<numPoints; i++)
244                 {
245                         x = halfWidth + (int) ((_track.getX(i) - _offsetX) / _scale * _zoomScale);
246                         y = halfHeight - (int) ((_track.getY(i) - _offsetY) / _scale * _zoomScale);
247                         if (x > BORDER_WIDTH && x < (width - BORDER_WIDTH)
248                                 && y < (height - BORDER_WIDTH) && y > BORDER_WIDTH)
249                         {
250                                 // draw block for point (a bit faster than circles)
251                                 bufferedG.drawRect(x - 2, y - 2, 3, 3);
252
253                                 // See whether to connect the point with previous one or not
254                                 DataPoint point = _track.getPoint(i);
255                                 currPointTrackpoint = !point.isWaypoint() && point.getPhoto() == null;
256                                 if (_connectPointsMenuItem.isSelected() && currPointTrackpoint && lastPointTrackpoint
257                                         && !point.getSegmentStart())
258                                 {
259                                         bufferedG.drawLine(lastX, lastY, x, y);
260                                 }
261                                 lastPointTrackpoint = currPointTrackpoint;
262                         }
263                         else {
264                                 lastPointTrackpoint = false;
265                         }
266                         lastX = x; lastY = y;
267                 }
268
269                 // Loop again and show waypoints with names
270                 bufferedG.setColor(COLOR_WAYPT_NAME);
271                 FontMetrics fm = bufferedG.getFontMetrics();
272                 int nameHeight = fm.getHeight();
273                 int numWaypointNamesShown = 0;
274                 for (int i=0; i<numPoints; i++)
275                 {
276                         DataPoint point = _track.getPoint(i);
277                         String waypointName = point.getWaypointName();
278                         if (waypointName != null && !waypointName.equals(""))
279                         {
280                                 // escape if nothing more to do
281                                 if (numWaypointNamesShown >= LIMIT_WAYPOINT_NAMES || _image == null) {break;}
282                                 // calculate coordinates of point
283                                 x = halfWidth + (int) ((_track.getX(i) - _offsetX) / _scale * _zoomScale);
284                                 y = halfHeight - (int) ((_track.getY(i) - _offsetY) / _scale * _zoomScale);
285                                 if (x > BORDER_WIDTH && x < (width - BORDER_WIDTH)
286                                                 && y < (height - BORDER_WIDTH) && y > BORDER_WIDTH)
287                                 {
288                                         bufferedG.fillOval(x - 3, y - 3, 6, 6);
289                                         // Figure out where to draw name so it doesn't obscure track
290                                         int nameWidth = fm.stringWidth(waypointName);
291                                         if (nameWidth < (width - 2 * BORDER_WIDTH))
292                                         {
293                                                 boolean drawnName = false;
294                                                 // Make arrays for coordinates right left up down
295                                                 int[] nameXs = {x + 2, x - nameWidth - 2, x - nameWidth/2, x - nameWidth/2};
296                                                 int[] nameYs = {y + (nameHeight/2), y + (nameHeight/2), y - 2, y + nameHeight + 2};
297                                                 for (int extraSpace = 4; extraSpace < 13 && !drawnName; extraSpace+=2)
298                                                 {
299                                                         // Shift arrays for coordinates right left up down
300                                                         nameXs[0] += 2; nameXs[1] -= 2;
301                                                         nameYs[2] -= 2; nameYs[3] += 2;
302                                                         // Check each direction in turn right left up down
303                                                         for (int a=0; a<4; a++)
304                                                         {
305                                                                 if (nameXs[a] > BORDER_WIDTH && (nameXs[a] + nameWidth) < (width - BORDER_WIDTH)
306                                                                         && nameYs[a] < (height - BORDER_WIDTH) && (nameYs[a] - nameHeight) > BORDER_WIDTH
307                                                                         && !overlapsPoints(nameXs[a], nameYs[a], nameWidth, nameHeight))
308                                                                 {
309                                                                         // Found a rectangle to fit - draw name here and quit
310                                                                         bufferedG.drawString(waypointName, nameXs[a], nameYs[a]);
311                                                                         drawnName = true;
312                                                                         break;
313                                                                 }
314                                                         }
315                                                 }
316                                         }
317                                 }
318                         }
319                 }
320                 bufferedG.dispose();
321         }
322
323
324         /**
325          * Tests whether there are any data points within the specified x,y rectangle
326          * @param inX left X coordinate
327          * @param inY bottom Y coordinate
328          * @param inWidth width of rectangle
329          * @param inHeight height of rectangle
330          * @return true if there's at least one data point in the rectangle
331          */
332         private boolean overlapsPoints(int inX, int inY, int inWidth, int inHeight)
333         {
334                 try
335                 {
336                         // loop over x coordinate of rectangle
337                         for (int x=0; x<inWidth; x++)
338                         {
339                                 // loop over y coordinate of rectangle
340                                 for (int y=0; y<inHeight; y++)
341                                 {
342                                         int pixelColor = _image.getRGB(inX + x, inY - y);
343                                         if (pixelColor != -1) return true;
344                                 }
345                         }
346                 }
347                 catch (NullPointerException e) {
348                         // ignore null pointers, just return false
349                 }
350                 return false;
351         }
352
353
354         /**
355          * Make the popup menu for right-clicking the map
356          */
357         private void makePopup()
358         {
359                 _popup = new JPopupMenu();
360                 JMenuItem zoomIn = new JMenuItem(I18nManager.getText("menu.map.zoomin"));
361                 zoomIn.addActionListener(new ActionListener() {
362                         public void actionPerformed(ActionEvent e)
363                         {
364                                 zoomMap(true);
365                         }});
366                 zoomIn.setEnabled(true);
367                 _popup.add(zoomIn);
368                 JMenuItem zoomOut = new JMenuItem(I18nManager.getText("menu.map.zoomout"));
369                 zoomOut.addActionListener(new ActionListener() {
370                         public void actionPerformed(ActionEvent e)
371                         {
372                                 zoomMap(false);
373                         }});
374                 zoomOut.setEnabled(true);
375                 _popup.add(zoomOut);
376                 JMenuItem zoomFull = new JMenuItem(I18nManager.getText("menu.map.zoomfull"));
377                 zoomFull.addActionListener(new ActionListener() {
378                         public void actionPerformed(ActionEvent e)
379                         {
380                                 zoomToFullScale();
381                         }});
382                 zoomFull.setEnabled(true);
383                 _popup.add(zoomFull);
384                 _connectPointsMenuItem = new JCheckBoxMenuItem(I18nManager.getText("menu.map.connect"));
385                 _connectPointsMenuItem.addActionListener(new ActionListener() {
386                         public void actionPerformed(ActionEvent e)
387                         {
388                                 // redraw map
389                                 dataUpdated(DataSubscriber.ALL);
390                         }
391                 });
392                 _connectPointsMenuItem.setSelected(true);
393                 _popup.add(_connectPointsMenuItem);
394                 _autoPanMenuItem = new JCheckBoxMenuItem(I18nManager.getText("menu.map.autopan"));
395                 _autoPanMenuItem.setSelected(true);
396                 _popup.add(_autoPanMenuItem);
397         }
398
399
400         /**
401          * Zoom map to full scale
402          */
403         private void zoomToFullScale()
404         {
405                 _zoomScale = 1.0;
406                 _offsetX = 0.0;
407                 _offsetY = 0.0;
408                 _numPoints = 0;
409                 dataUpdated(DataSubscriber.ALL);
410         }
411
412
413         /**
414          * Zoom map either in or out by one step
415          * @param inZoomIn true to zoom in, false for out
416          */
417         private void zoomMap(boolean inZoomIn)
418         {
419                 if (inZoomIn)
420                 {
421                         // Zoom in
422                         _zoomScale *= ZOOM_SCALE_FACTOR;
423                 }
424                 else
425                 {
426                         // Zoom out
427                         _zoomScale /= ZOOM_SCALE_FACTOR;
428                         if (_zoomScale < 0.5) _zoomScale = 0.5;
429                 }
430                 _numPoints = 0;
431                 dataUpdated(DataSubscriber.ALL);
432         }
433
434
435         /**
436          * Pan the map by the specified amounts
437          * @param inUp upwards pan
438          * @param inRight rightwards pan
439          */
440         private void panMap(int inUp, int inRight)
441         {
442                 double panFactor = _scale / _zoomScale;
443                 _offsetY = _offsetY + (inUp * panFactor);
444                 _offsetX = _offsetX - (inRight * panFactor);
445                 // Limit pan to sensible range??
446                 _numPoints = 0;
447                 _image = null;
448                 repaint();
449         }
450
451
452         /**
453          * React to click on map display
454          * @param inE mouse event
455          */
456         public void mouseClicked(MouseEvent inE)
457         {
458                 this.requestFocus();
459                 if (_track != null)
460                 {
461                         int xClick = inE.getX();
462                         int yClick = inE.getY();
463                         // Check click is within main area (not in border)
464                         if (xClick > BORDER_WIDTH && yClick > BORDER_WIDTH && xClick < (getWidth() - BORDER_WIDTH)
465                                 && yClick < (getHeight() - BORDER_WIDTH))
466                         {
467                                 // Check left click or right click
468                                 if (inE.isMetaDown())
469                                 {
470                                         // Only show popup if track has data
471                                         if (_track != null && _track.getNumPoints() > 0)
472                                                 _popup.show(this, xClick, yClick);
473                                 }
474                                 else
475                                 {
476                                         // Find point within range of click point
477                                         double pointX = (xClick - getWidth()/2) * _scale / _zoomScale + _offsetX;
478                                         double pointY = (getHeight()/2 - yClick) * _scale / _zoomScale + _offsetY;
479                                         int selectedPointIndex = _track.getNearestPointIndex(
480                                                 pointX, pointY, CLICK_SENSITIVITY * _scale, false);
481                                         // Select the given point (or deselect if no point was found)
482                                         _trackInfo.getSelection().selectPoint(selectedPointIndex);
483                                 }
484                         }
485                 }
486         }
487
488
489         /**
490          * Respond to mouse released to reset dragging
491          * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
492          */
493         public void mouseReleased(MouseEvent e)
494         {
495                 _dragStartX = _dragStartY = -1;
496                 if (e.isMetaDown())
497                 {
498                         if (_zoomDragFromX >= 0 || _zoomDragFromY >= 0)
499                         {
500                                 // zoom area marked out - calculate offset and zoom
501                                 int xPan = (getWidth() - _zoomDragFromX - e.getX()) / 2;
502                                 int yPan = (getHeight() - _zoomDragFromY - e.getY()) / 2;
503                                 double xZoom = Math.abs(getWidth() * 1.0 / (e.getX() - _zoomDragFromX));
504                                 double yZoom = Math.abs(getHeight() * 1.0 / (e.getY() - _zoomDragFromY));
505                                 double extraZoom = (xZoom>yZoom?yZoom:xZoom);
506                                 // deselect point if selected (to stop autopan)
507                                 _trackInfo.getSelection().selectPoint(-1);
508                                 // Pan first to ensure pan occurs with correct scale
509                                 panMap(yPan, xPan);
510                                 // Then zoom in and request repaint
511                                 _zoomScale = _zoomScale * extraZoom;
512                                 _image = null;
513                                 repaint();
514                         }
515                         _zoomDragFromX = _zoomDragFromY = -1;
516                         _zoomDragging = false;
517                 }
518         }
519
520
521         /**
522          * Respond to mouse wheel events to zoom the map
523          * @see java.awt.event.MouseWheelListener#mouseWheelMoved(java.awt.event.MouseWheelEvent)
524          */
525         public void mouseWheelMoved(MouseWheelEvent e)
526         {
527                 zoomMap(e.getWheelRotation() < 0);
528         }
529
530
531         /**
532          * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
533          */
534         public void keyPressed(KeyEvent e)
535         {
536                 int code = e.getKeyCode();
537                 // Check for meta key
538                 if (e.isControlDown())
539                 {
540                         // Check for arrow keys to zoom in and out
541                         if (code == KeyEvent.VK_UP)
542                                 zoomMap(true);
543                         else if (code == KeyEvent.VK_DOWN)
544                                 zoomMap(false);
545                         // Key nav for next/prev point
546                         else if (code == KeyEvent.VK_LEFT)
547                                 _trackInfo.getSelection().selectPreviousPoint();
548                         else if (code == KeyEvent.VK_RIGHT)
549                                 _trackInfo.getSelection().selectNextPoint();
550                 }
551                 else
552                 {
553                         // Check for arrow keys to pan
554                         int upwardsPan = 0;
555                         if (code == KeyEvent.VK_UP)
556                                 upwardsPan = PAN_DISTANCE;
557                         else if (code == KeyEvent.VK_DOWN)
558                                 upwardsPan = -PAN_DISTANCE;
559                         int rightwardsPan = 0;
560                         if (code == KeyEvent.VK_RIGHT)
561                                 rightwardsPan = -PAN_DISTANCE;
562                         else if (code == KeyEvent.VK_LEFT)
563                                 rightwardsPan = PAN_DISTANCE;
564                         panMap(upwardsPan, rightwardsPan);
565                         // Check for delete key to delete current point
566                         if (code == KeyEvent.VK_DELETE && _trackInfo.getSelection().getCurrentPointIndex() >= 0)
567                         {
568                                 _app.deleteCurrentPoint();
569                                 // reset last selected point to trigger autopan
570                                 _lastSelectedPoint = -1;
571                         }
572                 }
573         }
574
575
576         /**
577          * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
578          */
579         public void keyReleased(KeyEvent e)
580         {
581                 // ignore
582         }
583
584
585         /**
586          * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
587          */
588         public void keyTyped(KeyEvent e)
589         {
590                 // ignore
591         }
592
593
594         /**
595          * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
596          */
597         public void mouseDragged(MouseEvent e)
598         {
599                 if (!e.isMetaDown())
600                 {
601                         if (_dragStartX > 0)
602                         {
603                                 int xShift = e.getX() - _dragStartX;
604                                 int yShift = e.getY() - _dragStartY;
605                                 panMap(yShift, xShift);
606                         }
607                         _dragStartX = e.getX();
608                         _dragStartY = e.getY();
609                 }
610                 else
611                 {
612                         // Right click-and-drag for zoom
613                         if (_zoomDragFromX < 0 || _zoomDragFromY < 0)
614                         {
615                                 _zoomDragFromX = e.getX();
616                                 _zoomDragFromY = e.getY();
617                         }
618                         else
619                         {
620                                 _zoomDragToX = e.getX();
621                                 _zoomDragToY = e.getY();
622                                 _zoomDragging = true;
623                         }
624                         repaint();
625                 }
626         }
627
628
629         /**
630          * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
631          */
632         public void mouseMoved(MouseEvent e)
633         {
634                 // ignore
635         }
636 }