001    /* MouseEvent.java -- a mouse event
002       Copyright (C) 1999, 2002, 2004, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.awt.event;
040    
041    import gnu.java.awt.EventModifier;
042    
043    import java.awt.Component;
044    import java.awt.Point;
045    import java.awt.PopupMenu;
046    import java.io.IOException;
047    import java.io.ObjectInputStream;
048    
049    /**
050     * This event is generated for a mouse event. There are three main categories
051     * of mouse events: Regular events include pressing, releasing, and clicking
052     * buttons, as well as moving over the boundary of the unobscured portion of
053     * a component. Motion events include movement and dragging. Wheel events are
054     * covered separately by the subclass MouseWheelEvent.
055     *
056     * <p>A mouse event is tied to the unobstructed visible component that the
057     * mouse cursor was over at the time of the action. The button that was
058     * most recently pressed is the only one that shows up in
059     * <code>getModifiers</code>, and is returned by <code>getButton</code>,
060     * while all buttons that are down show up in <code>getModifiersEx</code>.
061     *
062     * <p>Drag events may be cut short if native drag-and-drop operations steal
063     * the event. Likewise, if a mouse drag exceeds the bounds of a window or
064     * virtual device, some platforms may clip the path to fit in the bounds of
065     * the component.
066     *
067     * @author Aaron M. Renn (arenn@urbanophile.com)
068     * @author Eric Blake (ebb9@email.byu.edu)
069     * @see MouseAdapter
070     * @see MouseListener
071     * @see MouseMotionAdapter
072     * @see MouseMotionListener
073     * @see MouseWheelListener
074     * @since 1.1
075     * @status updated to 1.4
076     */
077    public class MouseEvent extends InputEvent
078    {
079      /**
080       * Compatible with JDK 1.1+.
081       */
082      private static final long serialVersionUID = -991214153494842848L;
083    
084      /** This is the first id in the range of event ids used by this class. */
085      public static final int MOUSE_FIRST = 500;
086    
087      /** This is the last id in the range of event ids used by this class. */
088      public static final int MOUSE_LAST = 507;
089    
090      /** This event id indicates that the mouse was clicked. */
091      public static final int MOUSE_CLICKED = 500;
092    
093      /** This event id indicates that the mouse was pressed. */
094      public static final int MOUSE_PRESSED = 501;
095    
096      /** This event id indicates that the mouse was released. */
097      public static final int MOUSE_RELEASED = 502;
098    
099      /** This event id indicates that the mouse was moved. */
100      public static final int MOUSE_MOVED = 503;
101    
102      /** This event id indicates that the mouse entered a component. */
103      public static final int MOUSE_ENTERED = 504;
104    
105      /** This event id indicates that the mouse exited a component. */
106      public static final int MOUSE_EXITED = 505;
107    
108      /**
109       * This indicates that no button changed state.
110       *
111       * @see #getButton()
112       * @since 1.4
113       */
114      public static final int NOBUTTON = 0;
115    
116      /**
117       * This indicates that button 1 changed state.
118       *
119       * @see #getButton()
120       * @since 1.4
121       */
122      public static final int BUTTON1 = 1;
123    
124      /**
125       * This indicates that button 2 changed state.
126       *
127       * @see #getButton()
128       * @since 1.4
129       */
130      public static final int BUTTON2 = 2;
131    
132      /**
133       * This indicates that button 3 changed state.
134       *
135       * @see #getButton()
136       * @since 1.4
137       */
138      public static final int BUTTON3 = 3;
139    
140      /** This event id indicates that the mouse was dragged over a component. */
141      public static final int MOUSE_DRAGGED = 506;
142    
143      /**
144       * This event id indicates that the mouse wheel was rotated.
145       *
146       * @since 1.4
147       */
148      public static final int MOUSE_WHEEL = 507;
149    
150      /**
151       * The X coordinate of the mouse cursor at the time of the event.
152       *
153       * @see #getX()
154       * @serial the x coordinate
155      */
156      private int x;
157    
158      /**
159       * The Y coordinate of the mouse cursor at the time of the event.
160       *
161       * @see #getY()
162       * @serial the y coordinate
163       */
164      private int y;
165    
166      /**
167       * The screen position of that mouse event, X coordinate.
168       */
169      private int absX;
170    
171      /**
172       * The screen position of that mouse event, Y coordinate.
173       */
174      private int absY;
175    
176      /**
177       * The number of clicks that took place. For MOUSE_CLICKED, MOUSE_PRESSED,
178       * and MOUSE_RELEASED, this will be at least 1; otherwise it is 0.
179       *
180       * see #getClickCount()
181       * @serial the number of clicks
182       */
183      private final int clickCount;
184    
185      /**
186       * Indicates which mouse button changed state. Can only be one of
187       * {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or
188       * {@link #BUTTON3}.
189       *
190       * @see #getButton()
191       * @since 1.4
192       */
193      private int button;
194    
195      /**
196       * Whether or not this event should trigger a popup menu.
197       *
198       * @see PopupMenu
199       * @see #isPopupTrigger()
200       * @serial true if this is a popup trigger
201       */
202      private final boolean popupTrigger;
203    
204      /**
205       * Initializes a new instance of <code>MouseEvent</code> with the specified
206       * information. Note that an invalid id leads to unspecified results.
207       *
208       * @param source the source of the event
209       * @param id the event id
210       * @param when the timestamp of when the event occurred
211       * @param modifiers the modifier keys during the event, in old or new style
212       * @param x the X coordinate of the mouse point
213       * @param y the Y coordinate of the mouse point
214       * @param clickCount the number of mouse clicks for this event
215       * @param popupTrigger true if this event triggers a popup menu
216       * @param button the most recent mouse button to change state
217       * @throws IllegalArgumentException if source is null or button is invalid
218       * @since 1.4
219       */
220      public MouseEvent(Component source, int id, long when, int modifiers,
221                        int x, int y, int clickCount, boolean popupTrigger,
222                        int button)
223      {
224        super(source, id, when, modifiers);
225    
226        this.x = x;
227        this.y = y;
228        this.clickCount = clickCount;
229        this.popupTrigger = popupTrigger;
230        this.button = button;
231        if (button < NOBUTTON || button > BUTTON3)
232          throw new IllegalArgumentException();
233        if ((modifiers & EventModifier.OLD_MASK) != 0)
234          {
235            if ((modifiers & BUTTON1_MASK) != 0)
236              this.button = BUTTON1;
237            else if ((modifiers & BUTTON2_MASK) != 0)
238              this.button = BUTTON2;
239            else if ((modifiers & BUTTON3_MASK) != 0)
240              this.button = BUTTON3;
241          }
242        // clear the mouse button modifier masks if this is a button
243        // release event.
244        if (id == MOUSE_RELEASED)
245          this.modifiersEx &= ~(BUTTON1_DOWN_MASK
246                                | BUTTON2_DOWN_MASK
247                                | BUTTON3_DOWN_MASK);
248    
249        if (source != null)
250          {
251            Point screenLoc = source.getLocationOnScreen();
252            absX = screenLoc.x + x;
253            absY = screenLoc.y + y;
254          }
255      }
256    
257      /**
258       * Initializes a new instance of <code>MouseEvent</code> with the specified
259       * information. Note that an invalid id leads to unspecified results.
260       *
261       * @param source the source of the event
262       * @param id the event id
263       * @param when the timestamp of when the event occurred
264       * @param modifiers the modifier keys during the event, in old or new style
265       * @param x the X coordinate of the mouse point
266       * @param y the Y coordinate of the mouse point
267       * @param clickCount the number of mouse clicks for this event
268       * @param popupTrigger true if this event triggers a popup menu
269       * @throws IllegalArgumentException if source is null
270       */
271      public MouseEvent(Component source, int id, long when, int modifiers,
272                        int x, int y, int clickCount, boolean popupTrigger)
273      {
274        this(source, id, when, modifiers, x, y, clickCount, popupTrigger,
275             NOBUTTON);
276      }
277    
278      /**
279       * Creates a new MouseEvent. This is like the other constructors and adds
280       * specific absolute coordinates.
281       *
282       * @param source the source of the event
283       * @param id the event id
284       * @param when the timestamp of when the event occurred
285       * @param modifiers the modifier keys during the event, in old or new style
286       * @param x the X coordinate of the mouse point
287       * @param y the Y coordinate of the mouse point
288       * @param absX the absolute X screen coordinate of this event
289       * @param absY the absolute Y screen coordinate of this event
290       * @param clickCount the number of mouse clicks for this event
291       * @param popupTrigger true if this event triggers a popup menu
292       * @param button the most recent mouse button to change state
293       *
294       * @throws IllegalArgumentException if source is null or button is invalid
295       *
296       * @since 1.6
297       */
298      public MouseEvent(Component source, int id, long when, int modifiers,
299                        int x, int y, int absX, int absY, int clickCount,
300                        boolean popupTrigger, int button)
301      {
302        super(source, id, when, modifiers);
303    
304        this.x = x;
305        this.y = y;
306        this.clickCount = clickCount;
307        this.popupTrigger = popupTrigger;
308        this.button = button;
309        if (button < NOBUTTON || button > BUTTON3)
310          throw new IllegalArgumentException();
311        if ((modifiers & EventModifier.OLD_MASK) != 0)
312          {
313            if ((modifiers & BUTTON1_MASK) != 0)
314              this.button = BUTTON1;
315            else if ((modifiers & BUTTON2_MASK) != 0)
316              this.button = BUTTON2;
317            else if ((modifiers & BUTTON3_MASK) != 0)
318              this.button = BUTTON3;
319          }
320        // clear the mouse button modifier masks if this is a button
321        // release event.
322        if (id == MOUSE_RELEASED)
323          this.modifiersEx &= ~(BUTTON1_DOWN_MASK
324                                | BUTTON2_DOWN_MASK
325                                | BUTTON3_DOWN_MASK);
326    
327        this.absX = absX;
328        this.absY = absY;
329      }
330    
331      /**
332       * This method returns the X coordinate of the mouse position. This is
333       * relative to the source component.
334       *
335       * @return the x coordinate
336       */
337      public int getX()
338      {
339        return x;
340      }
341    
342      /**
343       * This method returns the Y coordinate of the mouse position. This is
344       * relative to the source component.
345       *
346       * @return the y coordinate
347       */
348      public int getY()
349      {
350        return y;
351      }
352    
353      /**
354       * @since 1.6
355       */
356      public Point getLocationOnScreen()
357      {
358        return new Point(absX, absY);
359      }
360    
361      /**
362       * @since 1.6
363       */
364      public int getXOnScreen()
365      {
366        return absX;
367      }
368    
369      /**
370       * @since 1.6
371       */
372      public int getYOnScreen()
373      {
374        return absY;
375      }
376    
377      /**
378       * This method returns a <code>Point</code> for the x,y position of
379       * the mouse pointer. This is relative to the source component.
380       *
381       * @return a <code>Point</code> for the event position
382       */
383      public Point getPoint()
384      {
385        return new Point(x, y);
386      }
387    
388      /**
389       * Translates the event coordinates by the specified x and y offsets.
390       *
391       * @param dx the value to add to the X coordinate of this event
392       * @param dy the value to add to the Y coordiante of this event
393       */
394      public void translatePoint(int dx, int dy)
395      {
396        x += dx;
397        y += dy;
398      }
399    
400      /**
401       * This method returns the number of mouse clicks associated with this
402       * event.
403       *
404       * @return the number of mouse clicks for this event
405       */
406      public int getClickCount()
407      {
408        return clickCount;
409      }
410    
411      /**
412       * Returns which button, if any, was the most recent to change state. This
413       * will be one of {@link #NOBUTTON}, {@link #BUTTON1}, {@link #BUTTON2}, or
414       * {@link #BUTTON3}.
415       *
416       * @return the button that changed state
417       * @since 1.4
418       */
419      public int getButton()
420      {
421        return button;
422      }
423    
424      /**
425       * This method tests whether or not the event is a popup menu trigger. This
426       * should be checked in both MousePressed and MouseReleased to be
427       * cross-platform compatible, as different systems have different popup
428       * triggers.
429       *
430       * @return true if the event is a popup menu trigger
431       */
432      public boolean isPopupTrigger()
433      {
434        return popupTrigger;
435      }
436    
437      /**
438       * Returns a string describing the modifiers, such as "Shift" or
439       * "Ctrl+Button1".
440       *
441       * XXX Sun claims this can be localized via the awt.properties file - how
442       * do we implement that?
443       *
444       * @param modifiers the old-style modifiers to convert to text
445       * @return a string representation of the modifiers in this bitmask
446       */
447      public static String getMouseModifiersText(int modifiers)
448      {
449        modifiers &= EventModifier.OLD_MASK;
450        if ((modifiers & BUTTON2_MASK) != 0)
451          modifiers |= BUTTON2_DOWN_MASK;
452        if ((modifiers & BUTTON3_MASK) != 0)
453          modifiers |= BUTTON3_DOWN_MASK;
454        return getModifiersExText(EventModifier.extend(modifiers));
455      }
456    
457      /**
458       * Returns a string identifying this event. This is formatted as the field
459       * name of the id type, followed by the (x,y) point, the most recent button
460       * changed, modifiers (if any), extModifiers (if any), and clickCount.
461       *
462       * @return a string identifying this event
463       */
464      public String paramString()
465      {
466        StringBuffer s = new StringBuffer();
467        switch (id)
468          {
469          case MOUSE_CLICKED:
470            s.append("MOUSE_CLICKED,(");
471            break;
472          case MOUSE_PRESSED:
473            s.append("MOUSE_PRESSED,(");
474            break;
475          case MOUSE_RELEASED:
476            s.append("MOUSE_RELEASED,(");
477            break;
478          case MOUSE_MOVED:
479            s.append("MOUSE_MOVED,(");
480            break;
481          case MOUSE_ENTERED:
482            s.append("MOUSE_ENTERED,(");
483            break;
484          case MOUSE_EXITED:
485            s.append("MOUSE_EXITED,(");
486            break;
487          case MOUSE_DRAGGED:
488            s.append("MOUSE_DRAGGED,(");
489            break;
490          case MOUSE_WHEEL:
491            s.append("MOUSE_WHEEL,(");
492            break;
493          default:
494            s.append("unknown type,(");
495          }
496        s.append(x).append(',').append(y).append("),button=").append(button);
497        // FIXME: need a mauve test for this method
498        if (modifiersEx != 0)
499          s.append(",extModifiers=").append(getModifiersExText(modifiersEx));
500        
501        s.append(",clickCount=").append(clickCount);
502        s.append(",consumed=").append(consumed);
503        
504        return s.toString();
505      }
506    
507      /**
508       * Reads in the object from a serial stream.
509       *
510       * @param s the stream to read from
511       * @throws IOException if deserialization fails
512       * @throws ClassNotFoundException if deserialization fails
513       * @serialData default, except that the modifiers are converted to new style
514       */
515      private void readObject(ObjectInputStream s)
516        throws IOException, ClassNotFoundException
517      {
518        s.defaultReadObject();
519        if ((modifiers & EventModifier.OLD_MASK) != 0)
520          {
521            if ((modifiers & BUTTON1_MASK) != 0)
522              button = BUTTON1;
523            else if ((modifiers & BUTTON2_MASK) != 0)
524              button = BUTTON2;
525            else if ((modifiers & BUTTON3_MASK) != 0)
526              button = BUTTON3;
527            modifiersEx = EventModifier.extend(modifiers) & EventModifier.NEW_MASK;
528          }
529      }
530    } // class MouseEvent