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