1 /*
   2  * Copyright (c) 2010, 2015, 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 int _getNativeFrameBuffer(long ptr);
 750 
 751     /**
 752      * Called on the renderer thread and must be between lock and unlock
 753      */
 754     public int getNativeFrameBuffer() {
 755         return _getNativeFrameBuffer(this.ptr);
 756     }
 757     
 758     
 759     protected abstract void _uploadPixels(long ptr, Pixels pixels);
 760     /**
 761      * This method dumps the pixels on to the view.
 762      *
 763      * NOTE: On MS Windows calling this method is REQUIRED for
 764      * transparent windows in order to update them.
 765      */
 766     public void uploadPixels(Pixels pixels) {
 767         Application.checkEventThread();
 768         checkNotClosed();
 769         lock();
 770         try {
 771             _uploadPixels(this.ptr, pixels);
 772         } finally {
 773             unlock();
 774         }
 775     }
 776 
 777 
 778     //-------- FULLSCREEN --------//
 779 
 780     protected abstract boolean _enterFullscreen(long ptr, boolean animate, boolean keepRatio, boolean hideCursor);
 781     public boolean enterFullscreen(boolean animate, boolean keepRatio, boolean hideCursor) {
 782         Application.checkEventThread();
 783         checkNotClosed();
 784         return _enterFullscreen(this.ptr, animate, keepRatio, hideCursor);
 785     }
 786 
 787     protected abstract void _exitFullscreen(long ptr, boolean animate);
 788     public void exitFullscreen(boolean animate) {
 789         Application.checkEventThread();
 790         checkNotClosed();
 791         _exitFullscreen(this.ptr, animate);
 792     }
 793 
 794     public boolean isInFullscreen() {
 795         Application.checkEventThread();
 796         return this.inFullscreen;
 797     }
 798 
 799     public boolean toggleFullscreen(boolean animate, boolean keepRatio, boolean hideCursor) {
 800         Application.checkEventThread();
 801         checkNotClosed();
 802         if (!this.inFullscreen) {
 803             enterFullscreen(animate, keepRatio, hideCursor);
 804         } else {
 805             exitFullscreen(animate);
 806         }
 807 
 808         _scheduleRepaint(this.ptr);
 809         
 810         return this.inFullscreen;
 811     }
 812 
 813 
 814     //-------- DELEGATE NOTIFICATIONS --------//
 815     
 816     protected void notifyView(int type) {
 817         //System.err.println("    notifyView: "+ViewEvent.getTypeString(type)+" on thread"+Thread.currentThread());
 818         if (type == ViewEvent.REPAINT) {
 819             if (isValid) {
 820                 handleViewEvent(System.nanoTime(), type);
 821             }
 822         }
 823         else
 824         {
 825             boolean synthesizeMOVE = false;
 826 
 827             switch (type) {
 828                 case ViewEvent.REMOVE:
 829                     isValid = false;
 830                     synthesizeMOVE = true;
 831                     break;
 832                 case ViewEvent.ADD:
 833                     isValid = true;
 834                     synthesizeMOVE = true;
 835                     break;
 836                 case ViewEvent.FULLSCREEN_ENTER:
 837                     this.inFullscreen = true;
 838                     synthesizeMOVE = true;
 839                     if (getWindow() != null) {
 840                         getWindow().notifyFullscreen(true);
 841                     }
 842                     break;
 843                 case ViewEvent.FULLSCREEN_EXIT:
 844                     this.inFullscreen = false;
 845                     synthesizeMOVE = true;
 846                     if (getWindow() != null) {
 847                         getWindow().notifyFullscreen(false);
 848                     }
 849                     break;
 850                 case ViewEvent.MOVE:
 851                 case ViewEvent.RESIZE:
 852                     break;
 853                 default:
 854                     System.err.println("Unknown view event type: " + type);
 855                     return;
 856             }
 857 
 858             handleViewEvent(System.nanoTime(), type);
 859 
 860             if (synthesizeMOVE) {
 861                 // Generate MOVE event to update current insets. Native code may
 862                 // send additional MOVE events when it detects insets change.
 863                 handleViewEvent(System.nanoTime(), ViewEvent.MOVE);
 864             }
 865         }
 866     }
 867 
 868     protected void notifyResize(int width, int height) {
 869         if (this.width == width && this.height == height) {
 870             return;
 871         }
 872 
 873         this.width = width;
 874         this.height = height;
 875         handleViewEvent(System.nanoTime(), ViewEvent.RESIZE);
 876     }
 877 
 878     /*
 879      * x, y, width, heigth define the "dirty" rect
 880      */
 881     protected void notifyRepaint(int x, int y, int width, int height) {
 882         notifyView(ViewEvent.REPAINT);
 883     }
 884 
 885 
 886     // ------------ MENU EVENT HANDLING -----------------
 887 //    protected void notifyMenu(int type, int button, int x, int y, int xAbs, int yAbs, int keyCode, char[] keyChars, int modifiers) {
 888         protected void notifyMenu(int x, int y, int xAbs, int yAbs, boolean isKeyboardTrigger) {
 889         handleMenuEvent(x, y, xAbs, yAbs, isKeyboardTrigger);
 890     }
 891 
 892     // ------------ MOUSE EVENTS HANDLING -----------------
 893 
 894     // Synchronized on the Main thread of the underlying native system
 895     private static WeakReference<View> lastClickedView = null;
 896     private static int lastClickedButton;
 897     private static long lastClickedTime;
 898     private static int lastClickedX, lastClickedY;
 899     private static int clickCount;
 900     private static boolean dragProcessed = false;
 901 
 902     protected void notifyMouse(int type, int button, int x, int y, int xAbs,
 903                                int yAbs, int modifiers, boolean isPopupTrigger,
 904                                boolean isSynthesized) {
 905         // gznote: optimize - only call for undecorated Windows!
 906         if (this.window != null) {
 907             // handled by window (programmatical move/resize)
 908             if (this.window.handleMouseEvent(type, button, x, y, xAbs, yAbs)) {
 909                 // The evnet has been processed by Glass
 910                 return;
 911             }
 912         }
 913 
 914         long now = System.nanoTime();
 915         if (type == MouseEvent.DOWN) {
 916             View lastClickedView = View.lastClickedView == null ? null : View.lastClickedView.get();
 917 
 918             if (lastClickedView == this &&
 919                     lastClickedButton == button &&
 920                     (now - lastClickedTime) <= 1000000L*getMultiClickTime() &&
 921                     Math.abs(x - lastClickedX) <= getMultiClickMaxX() &&
 922                     Math.abs(y - lastClickedY) <= getMultiClickMaxY())
 923             {
 924                 clickCount++;
 925             } else {
 926                 clickCount = 1;
 927 
 928                 View.lastClickedView = new WeakReference<View>(this);
 929                 lastClickedButton = button;
 930                 lastClickedX = x;
 931                 lastClickedY = y;
 932             }
 933 
 934             lastClickedTime = now;
 935         }
 936 
 937         handleMouseEvent(now, type, button, x, y, xAbs, yAbs,
 938                          modifiers, isPopupTrigger, isSynthesized);
 939 
 940         if (type == MouseEvent.DRAG) {
 941             // Send the handleDragStart() only once per a drag gesture
 942             if (!dragProcessed) {
 943                 notifyDragStart(button, x, y, xAbs, yAbs);
 944                 dragProcessed = true;
 945             }
 946         } else {
 947             dragProcessed = false;
 948         }
 949     }
 950 
 951     // ------------- END OF MOUSE EVENTS -----------------
 952 
 953     protected void notifyScroll(int x, int y, int xAbs, int yAbs,
 954             double deltaX, double deltaY, int modifiers, int lines, int chars,
 955             int defaultLines, int defaultChars,
 956             double xMultiplier, double yMultiplier)
 957     {
 958         if (this.eventHandler != null) {
 959             this.eventHandler.handleScrollEvent(this, System.nanoTime(),
 960                     x, y, xAbs, yAbs, deltaX, deltaY, modifiers, lines, chars,
 961                     defaultLines, defaultChars, xMultiplier, yMultiplier);
 962         }
 963     }
 964 
 965     protected void notifyKey(int type, int keyCode, char[] keyChars, int modifiers) {
 966         handleKeyEvent(System.nanoTime(), type, keyCode, keyChars, modifiers);
 967     }
 968 
 969     protected void notifyInputMethod(String text, int[] clauseBoundary,
 970         int[] attrBoundary, byte[] attrValue,
 971         int committedTextLength, int caretPos, int visiblePos) {
 972         handleInputMethodEvent(System.nanoTime(), text, clauseBoundary,
 973                 attrBoundary, attrValue, committedTextLength, caretPos);
 974     }
 975 
 976     protected double[] notifyInputMethodCandidatePosRequest(int offset) {
 977         double[] ret = getInputMethodCandidatePos(offset);
 978         if (ret == null) {
 979             ret = new double[2];
 980             ret[0] = 0.0;
 981             ret[1] = 0.0;
 982         }
 983         return ret;
 984     }
 985 
 986     private ClipboardAssistance dropSourceAssistant;
 987     protected void notifyDragStart(int button, int x, int y, int xAbs, int yAbs) {
 988         dropSourceAssistant = new ClipboardAssistance(Clipboard.DND) {
 989             @Override public void actionPerformed(int performedAction) {
 990                 // on Windows called from DnD modal loop
 991                 // on Mac the View is the drag delegate and calls notifyDragEnd directly
 992                 notifyDragEnd(performedAction);
 993             }
 994         };
 995         //DnD loop is inside dropSourceAssistant.flush()
 996         handleDragStart(button, x, y, xAbs, yAbs, dropSourceAssistant);
 997         //utilize dropSourceAssistant if DnD was not started.
 998         if (dropSourceAssistant != null) {
 999             dropSourceAssistant.close();
1000             dropSourceAssistant = null;
1001         }
1002     }
1003 
1004     protected void notifyDragEnd(int performedAction) {
1005         handleDragEnd(performedAction);
1006         if (dropSourceAssistant != null) {
1007             dropSourceAssistant.close();
1008             dropSourceAssistant = null;
1009         }
1010     }
1011 
1012     ClipboardAssistance  dropTargetAssistant;
1013     // callback for native code
1014     protected int notifyDragEnter(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1015         dropTargetAssistant = new ClipboardAssistance(Clipboard.DND) {
1016             @Override public void flush() {
1017                 throw new UnsupportedOperationException("Flush is forbidden from target!");
1018             }
1019         };
1020         return handleDragEnter(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1021     }
1022 
1023     // callback for native code
1024     protected int notifyDragOver(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1025         return handleDragOver(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1026     }
1027 
1028     // callback for native code
1029     protected void notifyDragLeave() {
1030         handleDragLeave(dropTargetAssistant);
1031         dropTargetAssistant.close();
1032     }
1033 
1034     // callback for native code
1035     // gznote: should be renamed to notifyDragDrop/notifyDragPerformed to be consistent
1036     protected int notifyDragDrop(int x, int y, int xAbs, int yAbs, int recommendedDropAction) {
1037         int performedAction = handleDragDrop(x, y, xAbs, yAbs, recommendedDropAction, dropTargetAssistant);
1038         dropTargetAssistant.close();
1039         return performedAction;
1040     }
1041 
1042     public void notifyBeginTouchEvent(int modifiers, boolean isDirect,
1043                                       int touchEventCount) {
1044         handleBeginTouchEvent(this, System.nanoTime(), modifiers, isDirect,
1045                               touchEventCount);
1046     }
1047 
1048     public void notifyNextTouchEvent(int type, long touchId, int x, int y,
1049                                      int xAbs, int yAbs) {
1050         handleNextTouchEvent(this, System.nanoTime(), type, touchId, x, y, xAbs,
1051                              yAbs);
1052     }
1053 
1054     public void notifyEndTouchEvent() {
1055         handleEndTouchEvent(this, System.nanoTime());
1056     }
1057 
1058     public void notifyScrollGestureEvent(int type, int modifiers,
1059                                          boolean isDirect, boolean isInertia,
1060                                          int touchCount, int x, int y, int xAbs,
1061                                          int yAbs, double dx, double dy,
1062                                          double totaldx, double totaldy,
1063                                          double multiplierX, double multiplierY) {
1064         handleScrollGestureEvent(this, System.nanoTime(), type, modifiers,
1065                                  isDirect, isInertia, touchCount, x, y, xAbs,
1066                                  yAbs, dx, dy, totaldx, totaldy, multiplierX, multiplierY);
1067     }
1068 
1069     public void notifyZoomGestureEvent(int type, int modifiers, boolean isDirect,
1070                                        boolean isInertia, int originx,
1071                                        int originy, int originxAbs,
1072                                        int originyAbs, double scale,
1073                                        double expansion, double totalscale,
1074                                        double totalexpansion) {
1075         handleZoomGestureEvent(this, System.nanoTime(), type, modifiers,
1076                                isDirect, isInertia, originx, originy, originxAbs,
1077                                originyAbs, scale, expansion, totalscale,
1078                                totalexpansion);
1079     }
1080 
1081     public void notifyRotateGestureEvent(int type, int modifiers,
1082                                          boolean isDirect, boolean isInertia,
1083                                          int originx, int originy,
1084                                          int originxAbs, int originyAbs,
1085                                          double dangle, double totalangle) {
1086         handleRotateGestureEvent(this, System.nanoTime(), type, modifiers,
1087                                  isDirect, isInertia, originx, originy,
1088                                  originxAbs, originyAbs, dangle, totalangle);
1089     }
1090 
1091     public void notifySwipeGestureEvent(int type, int modifiers,
1092                                         boolean isDirect, boolean isInertia,
1093                                         int touchCount, int dir, int originx,
1094                                         int originy, int originxAbs,
1095                                         int originyAbs) {
1096         handleSwipeGestureEvent(this, System.nanoTime(), type, modifiers,
1097                                 isDirect, isInertia, touchCount, dir, originx,
1098                                 originy, originxAbs, originyAbs);
1099     }
1100 
1101     /**
1102      * Returns the accessible object for the view.
1103      * This method is called by JNI code when the
1104      * platform requested the accessible peer for the view.
1105      * On Windows it happens on WM_GETOBJECT.
1106      * On Mac it happens on NSView#accessibilityAttributeNames.
1107      */
1108     long getAccessible() {
1109         Application.checkEventThread();
1110         checkNotClosed();
1111         if (accessible) {
1112             Accessible acc = eventHandler.getSceneAccessible();
1113             if (acc != null) {
1114                 acc.setView(this);
1115                 return acc.getNativeAccessible();
1116             }
1117         }
1118         return 0L;
1119     }
1120 }