1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.glass.ui;
  26 
  27 import com.sun.glass.events.MouseEvent;
  28 import com.sun.glass.events.ViewEvent;
  29 
  30 import java.lang.ref.WeakReference;
  31 import java.security.AccessController;
  32 import java.security.PrivilegedAction;
  33 import java.util.Map;
  34 
  35 public abstract class View {
  36 
  37     public final static int GESTURE_NO_VALUE = Integer.MAX_VALUE;
  38     public final static double GESTURE_NO_DOUBLE_VALUE = Double.NaN;
  39 
  40     public final static byte IME_ATTR_INPUT                 = 0x00;
  41     public final static byte IME_ATTR_TARGET_CONVERTED      = 0x01;
  42     public final static byte IME_ATTR_CONVERTED             = 0x02;
  43     public final static byte IME_ATTR_TARGET_NOTCONVERTED   = 0x03;
  44     public final static byte IME_ATTR_INPUT_ERROR           = 0x04;
  45 
  46     final static boolean accessible = AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
  47         String force = System.getProperty("glass.accessible.force");
  48         if (force != null) return Boolean.parseBoolean(force);
  49 
  50         /* By default accessibility is enabled for Mac 10.9 or greater and Windows 7 or greater. */
  51         try {
  52             String platform = Platform.determinePlatform();
  53             String major = System.getProperty("os.version").replaceFirst("(\\d+)\\.\\d+.*", "$1");
  54             String minor = System.getProperty("os.version").replaceFirst("\\d+\\.(\\d+).*", "$1");
  55             int v = Integer.parseInt(major) * 100 + Integer.parseInt(minor);
  56             return (platform.equals(Platform.MAC) && v >= 1009) ||
  57                    (platform.equals(Platform.WINDOWS) && v >= 601);
  58         } catch (Exception e) {
  59             return false;
  60         }
  61     });
  62 
  63     public static class EventHandler {
  64         public void handleViewEvent(View view, long time, int type) {
  65         }
  66         public void handleKeyEvent(View view, long time, int action,
  67                 int keyCode, char[] keyChars, int modifiers) {
  68         }
  69         public void handleMenuEvent(View view, int x, int y, int xAbs,
  70                 int yAbs, boolean isKeyboardTrigger) {
  71         }
  72         public void handleMouseEvent(View view, long time, int type, int button,
  73                                      int x, int y, int xAbs, int yAbs,
  74                                      int modifiers, boolean isPopupTrigger, boolean isSynthesized)
  75         {
  76         }
  77 
  78         /**
  79          * A Scroll event handler.
  80          *
  81          * The lines argument:
  82          * &gt; 0 - a number of lines to scroll per each 1.0 of deltaY scroll amount
  83          * == 0 - the scroll amount is in pixel units
  84          * &lt; 0 - the scrolling should be performed by pages. Each 1.0 of scroll amount
  85          * corresponds to exactly one page of scrollable content.
  86          *
  87          * Similarly, the chars argument specifies the number of characters
  88          * to scroll per 1.0 of the deltaX scrolling amount.
  89          * If the parameter is zero, the deltaX represents the number of
  90          * pixels to scroll.
  91          *
  92          * The defaultLines and defaultChars arguments contain the system-default
  93          * values of lines and chars. This can be used by the app to compute
  94          * the ratio of current settings and default settings and adjust the
  95          * pixel values accordingly.
  96          *
  97          * Multiplers are used when an app receives a non-zero unit values (i.e.
  98          * either the lines or chars are not zeroes), but wants instead get delta
  99          * values in pixels. In this case the app needs to multiply the deltas
 100          * on the provided multiplier parameter.
 101          */
 102         public void handleScrollEvent(View view, long time,
 103                 int x, int y, int xAbs, int yAbs,
 104                 double deltaX, double deltaY, int modifiers, int lines, int chars,
 105                 int defaultLines, int defaultChars,
 106                 double xMultiplier, double yMultiplier)
 107         {
 108         }
 109 
 110         public void handleInputMethodEvent(long time, String text,
 111                 int[] clauseBoundary,
 112                 int[] attrBoundary, byte[] attrValue,
 113                 int commitCount, int cursorPos) {
 114         }
 115 
 116         public double[] getInputMethodCandidatePos(int offset) {
 117             return null;
 118         }
 119 
 120         public void handleDragStart(View view, int button, int x, int y, int xAbs, int yAbs,
 121                 ClipboardAssistance dropSourceAssistant) {
 122         }
 123 
 124         public void handleDragEnd(View view, int performedAction) {
 125         }
 126 
 127         public int handleDragEnter(View view, int x, int y, int xAbs, int yAbs,
 128                 int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 129             return recommendedDropAction;
 130         }
 131 
 132         public int handleDragOver(View view, int x, int y, int xAbs, int yAbs,
 133                 int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 134             return recommendedDropAction;
 135         }
 136 
 137         public void handleDragLeave(View view, ClipboardAssistance dropTargetAssistant) {
 138         }
 139 
 140         public int handleDragDrop(View view, int x, int y, int xAbs, int yAbs,
 141                 int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 142             return Clipboard.ACTION_NONE;
 143         }
 144 
 145         /**
 146          * Touch event handler. Called when touch event occures.
 147          * Always followed with one ore more #handleNextTouchEvent() calls
 148          * and a single #handleEndTouchEvent() call.
 149          *
 150          * @param isDirect if event reported by direct or indirect touch device;
 151          *        touch screen is an example of direct touch device and
 152          *        touch pad is an example of indirect one
 153          * @param touchEventCount indicates number of #handleNextTouchEvent() calls
 154          *        that will follow this method call.
 155          */
 156         public void handleBeginTouchEvent(View view, long time, int modifiers,
 157                                           boolean isDirect, int touchEventCount) {
 158         }
 159 
 160         /**
 161          * Touch event handler. Called for every touch point in some touch event.
 162          *
 163          * If the touch event has been emitted with direct touch device
 164          * (touch screen) then x and y arguments designate touch point position
 165          * relative to the top-left corner of the view and xAbs and yAbs
 166          * arguments designate position relative to the top-left corner of the
 167          * screen. Both positions are measured in pixels.
 168          *
 169          * If the touch event has been emitted with indirect touch device
 170          * (touch pad) then x and y arguments designate normalized touch point
 171          * position. It is measured between (0,0) and (10000,10000), where (0,0)
 172          * is the top-left and (10000,10000) is the bottom-right position on
 173          * the indirect touch input device (touch pad). xAbs and yAbs
 174          * arguments are equal values of x and y arguments respectively.
 175          *
 176          * @see #handleBeginTouchEvent(com.sun.glass.ui.View, long, int, boolean, int)
 177          *
 178          * @param type touch event type. One of constants declared in
 179          *        #com.sun.glass.events.TouchEvent class.
 180          * @param touchId touch point identifier;
 181          *        every touch point has its own unique identifier;
 182          *        the identifier remains the same across multiple calls of
 183          *        #handleNextTouchEvent method for the same touch point until
 184          *        it is not released.
 185          * @param x the X coordinate of the touch point;
 186          * @param y the Y coordinate of the touch point;
 187          * @param xAbs absolute X coordinate of the touch point;
 188          * @param yAbs absolute Y coordinate of the touch point;
 189          */
 190         public void handleNextTouchEvent(View view, long time, int type,
 191                                          long touchId, int x, int y, int xAbs,
 192                                          int yAbs) {
 193         }
 194 
 195         /**
 196          * Touch event handler. Called to notify that all #handleNextTouchEvent
 197          * methods corresponding to some touch event have been called already.
 198          *
 199          * @see #handleBeginTouchEvent(com.sun.glass.ui.View, long, int, boolean, int)
 200          */
 201         public void handleEndTouchEvent(View view, long time) {
 202         }
 203 
 204         /**
 205          * Scroll gesture handler.
 206          *
 207          * If underlying system supports coordinates for gestures then x and y
 208          * arguments designate gesture position relative to the top-left
 209          * corner of the view and xAbs and yAbs designate gesture position
 210          * relative to the top-left corner of the screen. For gestures emitted
 211          * from direct touch input device (touch screen) positions are measured
 212          * in pixels. For gestures emitted from indirect touch input device
 213          * (touch pad) positions are normalized. For details of normalized
 214          * touch input position see #handleBeginTouchEvent method.
 215          *
 216          * If underlying system doesn't support coordinates for gestures then
 217          * x and y arguments designate mouse position relative to the top-left
 218          * corner of the view and xAbs and yAbs designate mouse position
 219          * relative to the top-left corner of the screen. Positions are measured
 220          * in pixels.
 221          *
 222          * If gesture handler is called to notify end of gesture, i.e. value of
 223          * type argument is equal to
 224          * com.sun.glass.events.GestureEvent.GESTURE_FINISHED constant then
 225          * x, y, xAbs and yAbs arguments may be set to View.GESTURE_NO_VALUE
 226          * constant indicating no data is available. This is implementation
 227          * specific behavior.
 228          *
 229          * Values of dx and dy arguments are always 0.0 if type argument
 230          * is set to com.sun.glass.events.GestureEvent.GESTURE_FINISHED
 231          * constant.
 232          *
 233          * For description of isDirect argument see #handleBeginTouchEvent
 234          * method.
 235          *
 236          * @param type gesture state. One of constants declared in
 237          *        #com.sun.glass.events.GestureEvent class.
 238          * @param isInertia if gesture is caused by inertia.
 239          * @param touchCount number of touch points at
 240          *        the moment of gesture execution; it is always set to
 241          *        View.GESTURE_NO_VALUE constant if value of type argument is
 242          *        set to com.sun.glass.events.GestureEvent.GESTURE_FINISHED
 243          *        constant
 244          * @param x the X coordinate of the gesture;
 245          * @param y the Y coordinate of the gesture;
 246          * @param xAbs absolute X coordinate of the gesture;
 247          * @param yAbs absolute Y coordinate of the gesture;
 248          * @param dx horizontal scroll delta. Positive if scrolling from
 249          *        left to right, non-positive otherwise
 250          * @param dy vertical scroll delta. Positive if scrolling from
 251          *        up to down, non-positive otherwise
 252          * @param totaldx total horizontal scroll calculated from all
 253          *        sequential scroll gestures, i.e. sum of all 'dx' values from
 254          *        previous sequential calls to this method
 255          * @param totaldy total vertical scroll calculated from all
 256          *        sequential scroll gestures, i.e. sum of all 'dy' values from
 257          *        previous sequential calls to this method
 258          * @param multiplierX the X multiplier
 259          * @param multiplierY the Y multiplier
 260          *
 261          * Multiplers are used when an app receives a non-zero unit values (i.e.
 262          * either the lines or chars are not zeroes), but wants instead get delta
 263          * values in pixels. In this case the app needs to multiply the deltas
 264          * on the provided multiplier parameter.
 265          */
 266         public void handleScrollGestureEvent(View view, long time, int type,
 267                                              int modifiers, boolean isDirect,
 268                                              boolean isInertia, int touchCount,
 269                                              int x, int y, int xAbs, int yAbs,
 270                                              double dx, double dy,
 271                                              double totaldx, double totaldy,
 272                                              double multiplierX, double multiplierY) {
 273         }
 274 
 275         /**
 276          * Zoom gesture handler.
 277          *
 278          * For description of isDirect argument see #handleBeginTouchEvent
 279          * method.
 280          * 
 281          * For description of isInertia argument see #handleScrollGestureEvent
 282          * method.
 283          *
 284          * For description of type, x,y, xAbs and yAbs arguments
 285          * see #handleBeginTouchEvent method.
 286          *
 287          * If underlying system doesn't support measurement of expansion value
 288          * in zoom gestures then expansion and totalexpansion arguments are
 289          * always set to View.GESTURE_NO_DOUBLE_VALUE.
 290          *
 291          * If type argument is set to
 292          * com.sun.glass.events.GestureEvent.GESTURE_FINISHED constant value of
 293          * scale argument is always set to View.GESTURE_NO_DOUBLE_VALUE constant
 294          * and expansion argument is always 0.0.
 295          *
 296          * @param scale current zoom delta; the value is multiplicative
 297          *        and not additive.
 298          * @param expansion current expansion delta. Measured in pixels on
 299          *        direct touch input devices and normalized values on indirect
 300          *        touch input devices. See #handleBeginTouchEvent for
 301          *        description of units of indirect touch input devices.
 302          * @param totalscale total zoom calculated from all
 303          *        sequential zoom gestures, i.e. sum of all 'scale' values from
 304          *        previous sequential calls to this method
 305          * @param totalexpansion total expansion calculated from all
 306          *        sequential zoom gestures, i.e. sum of all 'expansion' values
 307          *        from previous sequential calls of this method
 308          */
 309         public void handleZoomGestureEvent(View view, long time, int type,
 310                                            int modifiers, boolean isDirect,
 311                                            boolean isInertia, int x, int y,
 312                                            int xAbs, int yAbs, double scale,
 313                                            double expansion, double totalscale,
 314                                            double totalexpansion) {
 315         }
 316 
 317         /**
 318          * Rotation gesture handler.
 319          *
 320          * For description of isDirect argument see #handleBeginTouchEvent
 321          * method.
 322          * 
 323          * For description of isInertia argument see #handleScrollGestureEvent
 324          * method.
 325          *
 326          * For description of type, x,y, xAbs and yAbs arguments
 327          * see #handleBeginTouchEvent method.
 328          *
 329          * @param dangle current angle delta in degrees. Positive for clockwise
 330          *        rotation
 331          * @param totalangle total angle calculated from all
 332          *        sequential rotation gestures, i.e. sum of all 'dangle' values
 333          *        from previous sequential calls of this method
 334          */
 335         public void handleRotateGestureEvent(View view, long time, int type,
 336                                              int modifiers, boolean isDirect,
 337                                              boolean isInertia, int x, int y,
 338                                              int xAbs, int yAbs, double dangle,
 339                                              double totalangle) {
 340         }
 341 
 342         /**
 343          * Swipe gesture handler.
 344          *
 345          * For description of isDirect argument see #handleBeginTouchEvent
 346          * method.
 347          * 
 348          * For description of isInertia and touchCount arguments 
 349          * see #handleScrollGestureEvent method.
 350          *
 351          * For description of type, x,y, xAbs and yAbs arguments
 352          * see #handleBeginTouchEvent method.
 353          * 
 354          * @param dir gesture direction. 
 355          *        One of constants defined in com.sun.glass.events.SwipeGesture 
 356          *        class.
 357          */
 358         public void handleSwipeGestureEvent(View view, long time, int type,
 359                                             int modifiers, boolean isDirect,
 360                                             boolean isInertia, int touchCount,
 361                                             int dir, int x, int y, int xAbs,
 362                                             int yAbs) {
 363         }
 364 
 365         public Accessible getSceneAccessible() {
 366             return null;
 367         }
 368     }
 369 
 370     public static long getMultiClickTime() {
 371         Application.checkEventThread();
 372         return Application.GetApplication().staticView_getMultiClickTime();
 373     }
 374 
 375     public static int getMultiClickMaxX() {
 376         Application.checkEventThread();
 377         return Application.GetApplication().staticView_getMultiClickMaxX();
 378     }
 379 
 380     public static int getMultiClickMaxY() {
 381         Application.checkEventThread();
 382         return Application.GetApplication().staticView_getMultiClickMaxY();
 383     }
 384 
 385     protected abstract void _enableInputMethodEvents(long ptr, boolean enable);
 386     protected void _finishInputMethodComposition(long ptr) {
 387         // Action needed only on Windows.
 388     }
 389 
 390     /*
 391         Read by the checkNotClosed method which could be called from lock/unlock on render thread
 392      */
 393     private volatile long ptr; // Native handle (NSView*, or internal structure pointer)
 394     private Window window; // parent window
 395     private EventHandler eventHandler;
 396 
 397     private int width = -1;     // not set
 398     private int height = -1;    // not set
 399 
 400     private boolean isValid = false; // true between ViewEvent.Add & ViewEvent.REMOVE
 401     private boolean isVisible = false;
 402     private boolean inFullscreen = false;
 403 
 404     static final public class Capability {
 405         // we need these for native code
 406         static final public int k3dKeyValue                     = 0;
 407         static final public int kSyncKeyValue                   = 1;
 408         static final public int k3dProjectionKeyValue           = 2;
 409         static final public int k3dProjectionAngleKeyValue      = 3;
 410         static final public int k3dDepthKeyValue                = 4;
 411         static final public int kHiDPIAwareKeyValue             = 5;
 412 
 413         static final public Object k3dKey                       = Integer.valueOf(k3dKeyValue); // value must be Boolean
 414         static final public Object kSyncKey                     = Integer.valueOf(kSyncKeyValue); // value must be Boolean
 415         static final public Object k3dProjectionKey             = Integer.valueOf(k3dProjectionKeyValue); // value must be Boolean
 416         static final public Object k3dProjectionAngleKey        = Integer.valueOf(k3dProjectionAngleKeyValue); // value must be Float
 417         static final public Object k3dDepthKey                  = Integer.valueOf(k3dDepthKeyValue); // value must be Integer(depth), where depth = 0, 4, 8, 16, 32etc
 418         static final public Object kHiDPIAwareKey               = Integer.valueOf(kHiDPIAwareKeyValue); // value must be Boolean; default = false (i.e. NOT HiDPI-aware)
 419     }
 420 
 421 
 422     protected abstract long _create(Map capabilities);
 423     protected View() {
 424         Application.checkEventThread();
 425         this.ptr = _create(Application.GetApplication().getDeviceDetails());
 426         if (this.ptr == 0L) {
 427             throw new RuntimeException("could not create platform view");
 428         }
 429     }
 430 
 431     private void checkNotClosed() {
 432         if (this.ptr == 0L) {
 433             throw new IllegalStateException("The view has already been closed");
 434         }
 435     }
 436 
 437     public boolean isClosed() {
 438         Application.checkEventThread();
 439         return this.ptr == 0L;
 440     }
 441 
 442     protected abstract long _getNativeView(long ptr);
 443     /**
 444      * On Windows ptr is a pointer to a native structure.
 445      * However, for external clients of the API, a HWND has to be returned.
 446      * Hence the native method.
 447      */
 448     public long getNativeView() {
 449         Application.checkEventThread();
 450         checkNotClosed();
 451         return _getNativeView(this.ptr);
 452     }
 453     
 454     /** Only used on Mac when run inside a plugin */
 455     public int getNativeRemoteLayerId(String serverName) {
 456         Application.checkEventThread();
 457         throw new RuntimeException("This operation is not supported on this platform");
 458     }
 459     
 460     public Window getWindow() {
 461         Application.checkEventThread();
 462         return this.window;
 463     }
 464 
 465     protected abstract int _getX(long ptr);
 466     /** X coordinate relative to the host (window or applet). */
 467     public int getX() {
 468         Application.checkEventThread();
 469         checkNotClosed();
 470         return _getX(this.ptr);
 471     }
 472 
 473     protected abstract int _getY(long ptr);
 474     /** Y coordinate relative to the host (window or applet). */
 475     public int getY() {
 476         Application.checkEventThread();
 477         checkNotClosed();
 478         return _getY(this.ptr);
 479     }
 480 
 481     public int getWidth() {
 482         Application.checkEventThread();
 483         return this.width;
 484     }
 485 
 486     public int getHeight() {
 487         Application.checkEventThread();
 488         return this.height;
 489     }
 490 
 491     protected abstract void _setParent(long ptr, long parentPtr);
 492     // Window calls the method from Window.setView()
 493     // package private
 494     void setWindow(Window window) {
 495         Application.checkEventThread();
 496         checkNotClosed();
 497         this.window = window;
 498         _setParent(this.ptr, window == null ? 0L : window.getNativeHandle());
 499         this.isValid = this.ptr != 0 && window != null;
 500     }
 501 
 502     // package private
 503     void setVisible(boolean visible) {
 504         this.isVisible = visible;
 505     }
 506 
 507     protected abstract boolean _close(long ptr);
 508     public void close() {
 509         Application.checkEventThread();
 510         if (this.ptr == 0) {
 511             return;
 512         }
 513         if (isInFullscreen()) {
 514             _exitFullscreen(this.ptr, false);
 515         }
 516         Window host = getWindow();
 517         if (host != null) {
 518             host.setView(null); // will call this.setWindow(null)
 519         }
 520         this.isValid = false;
 521         _close(this.ptr);
 522         this.ptr = 0;
 523     }
 524 
 525     public EventHandler getEventHandler() {
 526         Application.checkEventThread();
 527         return this.eventHandler;
 528     }
 529 
 530     public void setEventHandler(EventHandler eventHandler) {
 531         Application.checkEventThread();
 532         this.eventHandler = eventHandler;
 533     }
 534 
 535     //-------- EVENTS --------//
 536 
 537     private void handleViewEvent(long time, int type) {
 538         if (this.eventHandler != null) {
 539             this.eventHandler.handleViewEvent(this, time, type);
 540         }
 541     }
 542 
 543     private void handleKeyEvent(long time, int action,
 544             int keyCode, char[] keyChars, int modifiers) {
 545         if (this.eventHandler != null) {
 546             this.eventHandler.handleKeyEvent(this, time, action, keyCode, keyChars, modifiers);
 547         }
 548     }
 549 
 550     private void handleMouseEvent(long time, int type, int button, int x, int y,
 551                                   int xAbs, int yAbs,
 552                                   int modifiers, boolean isPopupTrigger,
 553                                   boolean isSynthesized) {
 554         if (eventHandler != null) {
 555             eventHandler.handleMouseEvent(this, time, type, button, x, y, xAbs,
 556                                           yAbs, modifiers,
 557                                           isPopupTrigger, isSynthesized);
 558         }
 559     }
 560 
 561     private void handleMenuEvent(int x, int y, int xAbs, int yAbs, boolean isKeyboardTrigger) {
 562         if (this.eventHandler != null) {
 563             this.eventHandler.handleMenuEvent(this, x, y, xAbs, yAbs, isKeyboardTrigger);
 564         }
 565     }
 566 
 567     public void handleBeginTouchEvent(View view, long time, int modifiers,
 568                                       boolean isDirect, int touchEventCount) {
 569         if (eventHandler != null) {
 570             eventHandler.handleBeginTouchEvent(view, time, modifiers, isDirect,
 571                     touchEventCount);
 572         }
 573     }
 574 
 575     public void handleNextTouchEvent(View view, long time, int type,
 576                                      long touchId, int x, int y, int xAbs,
 577                                      int yAbs) {
 578         if (eventHandler != null) {
 579             eventHandler.handleNextTouchEvent(view, time, type, touchId, x, y, xAbs, yAbs);
 580         }
 581     }
 582 
 583     public void handleEndTouchEvent(View view, long time) {
 584         if (eventHandler != null) {
 585             eventHandler.handleEndTouchEvent(view, time);
 586         }
 587     }
 588 
 589     public void handleScrollGestureEvent(View view, long time, int type,
 590                                          int modifiers, boolean isDirect,
 591                                          boolean isInertia, int touchCount,
 592                                          int x, int y, int xAbs, int yAbs,
 593                                          double dx, double dy, double totaldx,
 594                                          double totaldy, double multiplierX,
 595                                          double multiplierY) {
 596         if (eventHandler != null) {
 597             eventHandler.handleScrollGestureEvent(view, time, type, modifiers, isDirect,
 598                     isInertia, touchCount, x, y, xAbs, yAbs,
 599                     dx, dy, totaldx, totaldy, multiplierX, multiplierY);
 600         }
 601     }
 602 
 603     public void handleZoomGestureEvent(View view, long time, int type,
 604                                        int modifiers, boolean isDirect,
 605                                        boolean isInertia, int originx,
 606                                        int originy, int originxAbs,
 607                                        int originyAbs, double scale,
 608                                        double expansion, double totalscale,
 609                                        double totalexpansion) {
 610         if (eventHandler != null) {
 611             eventHandler.handleZoomGestureEvent(view, time, type, modifiers, isDirect,
 612                                      isInertia, originx, originy, originxAbs,
 613                                      originyAbs, scale, expansion, totalscale,
 614                                      totalexpansion);
 615         }
 616     }
 617 
 618     public void handleRotateGestureEvent(View view, long time, int type,
 619                                          int modifiers, boolean isDirect,
 620                                          boolean isInertia, int originx,
 621                                          int originy, int originxAbs,
 622                                          int originyAbs, double dangle,
 623                                          double totalangle) {
 624         if (eventHandler != null) {
 625             eventHandler.handleRotateGestureEvent(view, time, type, modifiers, isDirect,
 626                     isInertia, originx, originy, originxAbs,
 627                     originyAbs, dangle, totalangle);
 628         }
 629     }
 630 
 631     public void handleSwipeGestureEvent(View view, long time, int type,
 632                                         int modifiers, boolean isDirect,
 633                                         boolean isInertia, int touchCount,
 634                                         int dir, int originx, int originy,
 635                                         int originxAbs, int originyAbs) {
 636         if (eventHandler != null) {
 637             eventHandler.handleSwipeGestureEvent(view, time, type, modifiers, isDirect,
 638                     isInertia, touchCount, dir, originx,
 639                     originy, originxAbs, originyAbs);
 640         }
 641     }
 642 
 643     private void handleInputMethodEvent(long time, String text, int[] clauseBoundary,
 644                 int[] attrBoundary, byte[] attrValue,
 645                 int commitCount, int cursorPos) {
 646         if (this.eventHandler != null) {
 647             this.eventHandler.handleInputMethodEvent(time, text, clauseBoundary,
 648                 attrBoundary, attrValue,
 649                 commitCount, cursorPos);
 650         }
 651     }
 652 
 653     public void enableInputMethodEvents(boolean enable) {
 654         Application.checkEventThread();
 655         checkNotClosed();
 656         _enableInputMethodEvents(this.ptr, enable);
 657     }
 658 
 659     public void finishInputMethodComposition() {
 660         Application.checkEventThread();
 661         checkNotClosed();
 662         _finishInputMethodComposition(this.ptr);
 663     }
 664 
 665     private double[] getInputMethodCandidatePos(int offset) {
 666         if (this.eventHandler != null) {
 667             return this.eventHandler.getInputMethodCandidatePos(offset);
 668         }
 669         return null;
 670     }
 671 
 672     private void handleDragStart(int button, int x, int y, int xAbs, int yAbs,
 673             ClipboardAssistance dropSourceAssistant) {
 674         if (this.eventHandler != null) {
 675             this.eventHandler.handleDragStart(this, button, x, y, xAbs, yAbs, dropSourceAssistant);
 676         }
 677     }
 678 
 679     private void handleDragEnd(int performedAction) {
 680         if (this.eventHandler != null) {
 681             this.eventHandler.handleDragEnd(this, performedAction);
 682         }
 683     }
 684 
 685     private int handleDragEnter(int x, int y, int xAbs, int yAbs,
 686             int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 687         if (this.eventHandler != null) {
 688             return this.eventHandler.handleDragEnter(this, x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
 689         } else {
 690             return recommendedDropAction;
 691         }
 692     }
 693 
 694     private int handleDragOver(int x, int y, int xAbs, int yAbs,
 695             int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 696         if (this.eventHandler != null) {
 697             return this.eventHandler.handleDragOver(this, x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
 698         } else {
 699             return recommendedDropAction;
 700         }
 701     }
 702 
 703     private void handleDragLeave(ClipboardAssistance dropTargetAssistant) {
 704         if (this.eventHandler != null) {
 705             this.eventHandler.handleDragLeave(this, dropTargetAssistant);
 706         }
 707     }
 708 
 709     private int handleDragDrop(int x, int y, int xAbs, int yAbs,
 710             int recommendedDropAction, ClipboardAssistance dropTargetAssistant) {
 711         if (this.eventHandler != null) {
 712             return this.eventHandler.handleDragDrop(this, x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
 713         } else {
 714             return Clipboard.ACTION_NONE;
 715         }
 716     }
 717 
 718     //-------- DRAWING --------//
 719     protected abstract void _scheduleRepaint(long ptr);
 720     /** marks native surface dirty, so the system itself will create repaint event
 721      * */
 722     public void scheduleRepaint() {
 723         Application.checkEventThread();
 724         checkNotClosed();
 725         _scheduleRepaint(this.ptr);
 726     }
 727     
 728     protected abstract void _begin(long ptr);
 729     /** prepares to painting by locking native surface
 730      *
 731      * Called on the render thread
 732      */
 733     public void lock() {
 734         checkNotClosed();
 735         _begin(this.ptr);
 736     }
 737 
 738     protected abstract void _end(long ptr);
 739     /** ends painting by unlocking native surface and flushing
 740      * flushes surface (if flush == true) or discard it (flush == false)
 741      *
 742      * Called on the render thread
 743      */
 744     public void unlock() {
 745         checkNotClosed();
 746         _end(this.ptr);
 747     }
 748 
 749     protected abstract void _uploadPixels(long ptr, Pixels pixels);
 750     /**
 751      * This method dumps the pixels on to the view.
 752      *
 753      * NOTE: On MS Windows calling this method is REQUIRED for
 754      * transparent windows in order to update them.
 755      */
 756     public void uploadPixels(Pixels pixels) {
 757         Application.checkEventThread();
 758         checkNotClosed();
 759         lock();
 760         try {
 761             _uploadPixels(this.ptr, pixels);
 762         } finally {
 763             unlock();
 764         }
 765     }
 766 
 767 
 768     //-------- FULLSCREEN --------//
 769 
 770     protected abstract boolean _enterFullscreen(long ptr, boolean animate, boolean keepRatio, boolean hideCursor);
 771     public boolean enterFullscreen(boolean animate, boolean keepRatio, boolean hideCursor) {
 772         Application.checkEventThread();
 773         checkNotClosed();
 774         return _enterFullscreen(this.ptr, animate, keepRatio, hideCursor);
 775     }
 776 
 777     protected abstract void _exitFullscreen(long ptr, boolean animate);
 778     public void exitFullscreen(boolean animate) {
 779         Application.checkEventThread();
 780         checkNotClosed();
 781         _exitFullscreen(this.ptr, animate);
 782     }
 783 
 784     public boolean isInFullscreen() {
 785         Application.checkEventThread();
 786         return this.inFullscreen;
 787     }
 788 
 789     public boolean toggleFullscreen(boolean animate, boolean keepRatio, boolean hideCursor) {
 790         Application.checkEventThread();
 791         checkNotClosed();
 792         if (!this.inFullscreen) {
 793             enterFullscreen(animate, keepRatio, hideCursor);
 794         } else {
 795             exitFullscreen(animate);
 796         }
 797 
 798         _scheduleRepaint(this.ptr);
 799         
 800         return this.inFullscreen;
 801     }
 802 
 803 
 804     //-------- DELEGATE NOTIFICATIONS --------//
 805     
 806     protected void notifyView(int type) {
 807         //System.err.println("    notifyView: "+ViewEvent.getTypeString(type)+" on thread"+Thread.currentThread());
 808         if (type == ViewEvent.REPAINT) {
 809             if (isValid) {
 810                 handleViewEvent(System.nanoTime(), type);
 811             }
 812         }
 813         else
 814         {
 815             boolean synthesizeMOVE = false;
 816 
 817             switch (type) {
 818                 case ViewEvent.REMOVE:
 819                     isValid = false;
 820                     synthesizeMOVE = true;
 821                     break;
 822                 case ViewEvent.ADD:
 823                     isValid = true;
 824                     synthesizeMOVE = true;
 825                     break;
 826                 case ViewEvent.FULLSCREEN_ENTER:
 827                     this.inFullscreen = true;
 828                     synthesizeMOVE = true;
 829                     if (getWindow() != null) {
 830                         getWindow().notifyFullscreen(true);
 831                     }
 832                     break;
 833                 case ViewEvent.FULLSCREEN_EXIT:
 834                     this.inFullscreen = false;
 835                     synthesizeMOVE = true;
 836                     if (getWindow() != null) {
 837                         getWindow().notifyFullscreen(false);
 838                     }
 839                     break;
 840                 case ViewEvent.MOVE:
 841                 case ViewEvent.RESIZE:
 842                     break;
 843                 default:
 844                     System.err.println("Unknown view event type: " + type);
 845                     return;
 846             }
 847 
 848             handleViewEvent(System.nanoTime(), type);
 849 
 850             if (synthesizeMOVE) {
 851                 // Generate MOVE event to update current insets. Native code may
 852                 // send additional MOVE events when it detects insets change.
 853                 handleViewEvent(System.nanoTime(), ViewEvent.MOVE);
 854             }
 855         }
 856     }
 857 
 858     protected void notifyResize(int width, int height) {
 859         if (this.width == width && this.height == height) {
 860             return;
 861         }
 862 
 863         this.width = width;
 864         this.height = height;
 865         handleViewEvent(System.nanoTime(), ViewEvent.RESIZE);
 866     }
 867 
 868     /*
 869      * x, y, width, heigth define the "dirty" rect
 870      */
 871     protected void notifyRepaint(int x, int y, int width, int height) {
 872         notifyView(ViewEvent.REPAINT);
 873     }
 874 
 875 
 876     // ------------ MENU EVENT HANDLING -----------------
 877 //    protected void notifyMenu(int type, int button, int x, int y, int xAbs, int yAbs, int keyCode, char[] keyChars, int modifiers) {
 878         protected void notifyMenu(int x, int y, int xAbs, int yAbs, boolean isKeyboardTrigger) {
 879         handleMenuEvent(x, y, xAbs, yAbs, isKeyboardTrigger);
 880     }
 881 
 882     // ------------ MOUSE EVENTS HANDLING -----------------
 883 
 884     // Synchronized on the Main thread of the underlying native system
 885     private static WeakReference<View> lastClickedView = null;
 886     private static int lastClickedButton;
 887     private static long lastClickedTime;
 888     private static int lastClickedX, lastClickedY;
 889     private static int clickCount;
 890     private static boolean dragProcessed = false;
 891 
 892     protected void notifyMouse(int type, int button, int x, int y, int xAbs,
 893                                int yAbs, int modifiers, boolean isPopupTrigger,
 894                                boolean isSynthesized) {
 895         // gznote: optimize - only call for undecorated Windows!
 896         if (this.window != null) {
 897             // handled by window (programmatical move/resize)
 898             if (this.window.handleMouseEvent(type, button, x, y, xAbs, yAbs)) {
 899                 // The evnet has been processed by Glass
 900                 return;
 901             }
 902         }
 903 
 904         long now = System.nanoTime();
 905         if (type == MouseEvent.DOWN) {
 906             View lastClickedView = View.lastClickedView == null ? null : View.lastClickedView.get();
 907 
 908             if (lastClickedView == this &&
 909                     lastClickedButton == button &&
 910                     (now - lastClickedTime) <= 1000000L*getMultiClickTime() &&
 911                     Math.abs(x - lastClickedX) <= getMultiClickMaxX() &&
 912                     Math.abs(y - lastClickedY) <= getMultiClickMaxY())
 913             {
 914                 clickCount++;
 915             } else {
 916                 clickCount = 1;
 917 
 918                 View.lastClickedView = new WeakReference<View>(this);
 919                 lastClickedButton = button;
 920                 lastClickedX = x;
 921                 lastClickedY = y;
 922             }
 923 
 924             lastClickedTime = now;
 925         }
 926 
 927         handleMouseEvent(now, type, button, x, y, xAbs, yAbs,
 928                          modifiers, isPopupTrigger, isSynthesized);
 929 
 930         if (type == MouseEvent.DRAG) {
 931             // Send the handleDragStart() only once per a drag gesture
 932             if (!dragProcessed) {
 933                 notifyDragStart(button, x, y, xAbs, yAbs);
 934                 dragProcessed = true;
 935             }
 936         } else {
 937             dragProcessed = false;
 938         }
 939     }
 940 
 941     // ------------- END OF MOUSE EVENTS -----------------
 942 
 943     protected void notifyScroll(int x, int y, int xAbs, int yAbs,
 944             double deltaX, double deltaY, int modifiers, int lines, int chars,
 945             int defaultLines, int defaultChars,
 946             double xMultiplier, double yMultiplier)
 947     {
 948         if (this.eventHandler != null) {
 949             this.eventHandler.handleScrollEvent(this, System.nanoTime(),
 950                     x, y, xAbs, yAbs, deltaX, deltaY, modifiers, lines, chars,
 951                     defaultLines, defaultChars, xMultiplier, yMultiplier);
 952         }
 953     }
 954 
 955     protected void notifyKey(int type, int keyCode, char[] keyChars, int modifiers) {
 956         handleKeyEvent(System.nanoTime(), type, keyCode, keyChars, modifiers);
 957     }
 958 
 959     protected void notifyInputMethod(String text, int[] clauseBoundary,
 960         int[] attrBoundary, byte[] attrValue,
 961         int committedTextLength, int caretPos, int visiblePos) {
 962         handleInputMethodEvent(System.nanoTime(), text, clauseBoundary,
 963                 attrBoundary, attrValue, committedTextLength, caretPos);
 964     }
 965 
 966     protected double[] notifyInputMethodCandidatePosRequest(int offset) {
 967         double[] ret = getInputMethodCandidatePos(offset);
 968         if (ret == null) {
 969             ret = new double[2];
 970             ret[0] = 0.0;
 971             ret[1] = 0.0;
 972         }
 973         return ret;
 974     }
 975 
 976     private ClipboardAssistance dropSourceAssistant;
 977     protected void notifyDragStart(int button, int x, int y, int xAbs, int yAbs) {
 978         dropSourceAssistant = new ClipboardAssistance(Clipboard.DND) {
 979             @Override public void actionPerformed(int performedAction) {
 980                 // on Windows called from DnD modal loop
 981                 // on Mac the View is the drag delegate and calls notifyDragEnd directly
 982                 notifyDragEnd(performedAction);
 983             }
 984         };
 985         //DnD loop is inside dropSourceAssistant.flush()
 986         handleDragStart(button, x, y, xAbs, yAbs, dropSourceAssistant);
 987         //utilize dropSourceAssistant if DnD was not started.
 988         if (dropSourceAssistant != null) {
 989             dropSourceAssistant.close();
 990             dropSourceAssistant = null;
 991         }
 992     }
 993 
 994     protected void notifyDragEnd(int performedAction) {
 995         handleDragEnd(performedAction);
 996         if (dropSourceAssistant != null) {
 997             dropSourceAssistant.close();
 998             dropSourceAssistant = null;
 999         }
1000     }
1001 
1002     ClipboardAssistance  dropTargetAssistant;
1003     // callback for native code
1004     protected int notifyDragEnter(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1005         dropTargetAssistant = new ClipboardAssistance(Clipboard.DND) {
1006             @Override public void flush() {
1007                 throw new UnsupportedOperationException("Flush is forbidden from target!");
1008             }
1009         };
1010         return handleDragEnter(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1011     }
1012 
1013     // callback for native code
1014     protected int notifyDragOver(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1015         return handleDragOver(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1016     }
1017 
1018     // callback for native code
1019     protected void notifyDragLeave() {
1020         handleDragLeave(dropTargetAssistant);
1021         dropTargetAssistant.close();
1022     }
1023 
1024     // callback for native code
1025     // gznote: should be renamed to notifyDragDrop/notifyDragPerformed to be consistent
1026     protected int notifyDragDrop(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1027         int performedAction = handleDragDrop(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1028         dropTargetAssistant.close();
1029         return performedAction;
1030     }
1031 
1032     public void notifyBeginTouchEvent(int modifiers, boolean isDirect,
1033                                       int touchEventCount) {
1034         handleBeginTouchEvent(this, System.nanoTime(), modifiers, isDirect,
1035                               touchEventCount);
1036     }
1037 
1038     public void notifyNextTouchEvent(int type, long touchId, int x, int y,
1039                                      int xAbs, int yAbs) {
1040         handleNextTouchEvent(this, System.nanoTime(), type, touchId, x, y, xAbs,
1041                              yAbs);
1042     }
1043 
1044     public void notifyEndTouchEvent() {
1045         handleEndTouchEvent(this, System.nanoTime());
1046     }
1047 
1048     public void notifyScrollGestureEvent(int type, int modifiers,
1049                                          boolean isDirect, boolean isInertia,
1050                                          int touchCount, int x, int y, int xAbs,
1051                                          int yAbs, double dx, double dy,
1052                                          double totaldx, double totaldy,
1053                                          double multiplierX, double multiplierY) {
1054         handleScrollGestureEvent(this, System.nanoTime(), type, modifiers,
1055                                  isDirect, isInertia, touchCount, x, y, xAbs,
1056                                  yAbs, dx, dy, totaldx, totaldy, multiplierX, multiplierY);
1057     }
1058 
1059     public void notifyZoomGestureEvent(int type, int modifiers, boolean isDirect,
1060                                        boolean isInertia, int originx,
1061                                        int originy, int originxAbs,
1062                                        int originyAbs, double scale,
1063                                        double expansion, double totalscale,
1064                                        double totalexpansion) {
1065         handleZoomGestureEvent(this, System.nanoTime(), type, modifiers,
1066                                isDirect, isInertia, originx, originy, originxAbs,
1067                                originyAbs, scale, expansion, totalscale,
1068                                totalexpansion);
1069     }
1070 
1071     public void notifyRotateGestureEvent(int type, int modifiers,
1072                                          boolean isDirect, boolean isInertia,
1073                                          int originx, int originy,
1074                                          int originxAbs, int originyAbs,
1075                                          double dangle, double totalangle) {
1076         handleRotateGestureEvent(this, System.nanoTime(), type, modifiers,
1077                                  isDirect, isInertia, originx, originy,
1078                                  originxAbs, originyAbs, dangle, totalangle);
1079     }
1080 
1081     public void notifySwipeGestureEvent(int type, int modifiers,
1082                                         boolean isDirect, boolean isInertia,
1083                                         int touchCount, int dir, int originx,
1084                                         int originy, int originxAbs,
1085                                         int originyAbs) {
1086         handleSwipeGestureEvent(this, System.nanoTime(), type, modifiers,
1087                                 isDirect, isInertia, touchCount, dir, originx,
1088                                 originy, originxAbs, originyAbs);
1089     }
1090 
1091     /**
1092      * Returns the accessible object for the view.
1093      * This method is called by JNI code when the
1094      * platform requested the accessible peer for the view.
1095      * On Windows it happens on WM_GETOBJECT.
1096      * On Mac it happens on NSView#accessibilityAttributeNames.
1097      */
1098     long getAccessible() {
1099         Application.checkEventThread();
1100         checkNotClosed();
1101         if (accessible) {
1102             Accessible acc = eventHandler.getSceneAccessible();
1103             if (acc != null) {
1104                 acc.setView(this);
1105                 return acc.getNativeAccessible();
1106             }
1107         }
1108         return 0L;
1109     }
1110 }