1 /*
   2  * Copyright (c) 2010, 2018, 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.WindowEvent;
  29 import com.sun.prism.impl.PrismSettings;
  30 
  31 import java.util.Collections;
  32 import java.util.LinkedList;
  33 import java.util.List;
  34 import java.util.Map;
  35 
  36 public abstract class Window {
  37 
  38     public static class EventHandler {
  39         public void handleWindowEvent(Window window, long time, int type) {
  40         }
  41 
  42         /**
  43          * Notifies a listener that the screen object for this Window instance
  44          * has been updated.
  45          *
  46          * Note that while the old and new screen objects may be different,
  47          * they can still represent the same physical screen. This can happen
  48          * if e.g. only a certain parameter of the screen has been updated such
  49          * as its scale factor.
  50          *
  51          * On some platforms when a window is moved to another physical screen
  52          * an app can receive this event twice. One representing the physical
  53          * screen change, and another - the display's parameters change. Note
  54          * that sending two events instead of just one is platform-specific.
  55          *
  56          * The event handler can use the {@link Screen#getNativeScreen} method
  57          * to determine if this is the same physical screen or not. If the
  58          * native system always creates new native screen instances, there's no
  59          * way for the app to distinguish between a real move to another screen
  60          * or jsut a parameters update event. Since this is a somewhat rare
  61          * event, an app is advised to always process it the same way.
  62          *
  63          * @see Window#getScreen
  64          */
  65         public void handleScreenChangedEvent(Window window, long time, Screen oldScreen, Screen newScreen) {
  66         }
  67 
  68         /**
  69          * Notifies the listener that the window level has changed. The Level should be one of
  70          * {@link com.sun.glass.ui.Window.Level#NORMAL}, {@link com.sun.glass.ui.Window.Level#FLOATING},
  71          * {@link com.sun.glass.ui.Window.Level#TOPMOST}.
  72          * @param level Level from {@link com.sun.glass.ui.Window.Level} class
  73          */
  74         public void handleLevelEvent(int level) {
  75         }
  76     }
  77 
  78     // Native object handle (HWND, or NSWindow*, etc.)
  79     private long ptr;
  80 
  81     // 'Delegate window' ptr. Used in e.g. the Full Screen mode.
  82     private volatile long delegatePtr = 0L;
  83 
  84     // window list
  85     static private final LinkedList<Window> visibleWindows = new LinkedList<Window>();
  86      // Return a list of all visible windows.  Note that on platforms without a native window manager,
  87      // this list will be sorted in proper z-order
  88     static public synchronized List<Window> getWindows() {
  89         Application.checkEventThread();
  90         return Collections.unmodifiableList(Window.visibleWindows);
  91     }
  92 
  93     static public List<Window> getWindowsClone() {
  94         Application.checkEventThread();
  95         return (List<Window>)visibleWindows.clone();
  96     }
  97 
  98     // used by Lens Native
  99     static protected void add(Window window) {
 100         visibleWindows.add(window);
 101     }
 102 
 103     static protected void addFirst(Window window) {
 104         visibleWindows.addFirst(window);
 105     }
 106 
 107     // used by Lens Native
 108     static protected void remove(Window window) {
 109         visibleWindows.remove(window);
 110     }
 111 
 112     // window style mask
 113 
 114     // visual kind: mutually exclusive
 115     public static final int UNTITLED        = 0;
 116     public static final int TITLED          = 1 << 0;
 117     public static final int TRANSPARENT     = 1 << 1;
 118 
 119     // functional type: mutually exclusive
 120     /**
 121      * Normal window.
 122      *
 123      * Usual top-level window.
 124      */
 125     public static final int NORMAL          = 0;
 126     /**
 127      * An utility window.
 128      *
 129      * Often used for floating toolbars. It has smaller than usual decorations
 130      * and doesn't display a taskbar button.
 131      */
 132     public static final int UTILITY         = 1 << 2;
 133     /**
 134      * A popup window.
 135      *
 136      * Used to display popups (tooltips, popup menus, etc.) Note that by
 137      * default it may display a task-bar button. To hide it the window must be
 138      * owned.
 139      */
 140     public static final int POPUP           = 1 << 3;
 141 
 142     // These affect window decorations as well as system menu actions,
 143     // so applicable to both decorated and undecorated windows
 144     public static final int CLOSABLE        = 1 << 4;
 145     public static final int MINIMIZABLE     = 1 << 5;
 146     public static final int MAXIMIZABLE     = 1 << 6;
 147 
 148     /**
 149      * Indicates that the window trim will draw from right to left.
 150      */
 151     public static final int RIGHT_TO_LEFT     = 1 << 7;
 152 
 153     /**
 154      * Indicates that a window will have a client area textured the same way as the platform decorations
 155      * and will not have a border between decorations and the client area.
 156      * This is supported not on all platforms, the client should check if the feature is supported by using
 157      * {@link com.sun.glass.ui.Application#supportsUnifiedWindows()}
 158      */
 159     public static final int UNIFIED = 1 << 8;
 160 
 161     final static public class State {
 162         public static final int NORMAL = 1;
 163         public static final int MINIMIZED = 2;
 164         public static final int MAXIMIZED = 3;
 165     }
 166 
 167     /**
 168      * Available window levels.
 169      *
 170      * Note that on some platforms both {@code FLOATING} and {@code TOPMOST}
 171      * may represent the same window level.
 172      *
 173      * @see #setLevel
 174      */
 175     public static final class Level {
 176         private static final int _MIN = 1;
 177 
 178         /** Normal window level. */
 179         public static final int NORMAL = 1;
 180 
 181         /** A window level that is above all other NORMAL windows. */
 182         public static final int FLOATING = 2;
 183 
 184         /** A very topmost window level. May cover system UI elements such as dock, taskbar, etc. */
 185         public static final int TOPMOST = 3;
 186 
 187         private static final int _MAX = 3;
 188     }
 189 
 190     private final Window owner;
 191     private final long parent;
 192     private final int styleMask;
 193     private final boolean isDecorated;
 194     private boolean shouldStartUndecoratedMove = false;
 195 
 196     private View view = null;
 197     private Screen screen = null;
 198     private MenuBar menubar = null;
 199     private String title = "";
 200     private UndecoratedMoveResizeHelper helper = null;
 201 
 202     private int state = State.NORMAL;
 203     private int level = Level.NORMAL;
 204     private int x = 0;
 205     private int y = 0;
 206     private int width = 0;
 207     private int height = 0;
 208     private float alpha = 1.0f;
 209     private float platformScale = 1.0f;
 210     private float renderScale = 1.0f;
 211 
 212     // This is a workaround for RT-15970: as for embedded windows we don't
 213     // receive any MOVE notifications from the native platform, we poll
 214     // the window location on screen from timer and post synthetic events
 215     // if it has changed
 216     private Timer embeddedLocationTimer = null;
 217     private int lastKnownEmbeddedX = 0;
 218     private int lastKnownEmbeddedY = 0;
 219 
 220     private volatile boolean isResizable = false;
 221     private volatile boolean isVisible = false;
 222     private volatile boolean isFocused = false;
 223     private volatile boolean isFocusable = true;
 224     private volatile boolean isModal = false;
 225 
 226     // Indicates how many times setEnabled(false) has been called.
 227     // A value of 0 means the window is enabled.
 228     private volatile int disableCount = 0;
 229 
 230     private int minimumWidth = 0, minimumHeight = 0;
 231     private int maximumWidth = Integer.MAX_VALUE, maximumHeight = Integer.MAX_VALUE;
 232 
 233     private EventHandler eventHandler;
 234 
 235     protected abstract long _createWindow(long ownerPtr, long screenPtr, int mask);
 236     protected Window(Window owner, Screen screen, int styleMask) {
 237         Application.checkEventThread();
 238         switch (styleMask & (TITLED | TRANSPARENT)) {
 239             case UNTITLED:
 240             case TITLED:
 241             case TRANSPARENT:
 242                 break;
 243             default:
 244                 throw new RuntimeException("The visual kind should be UNTITLED, TITLED, or TRANSPARENT, but not a combination of these");
 245         }
 246         switch (styleMask & (POPUP | UTILITY)) {
 247             case NORMAL:
 248             case POPUP:
 249             case UTILITY:
 250                 break;
 251             default:
 252                 throw new RuntimeException("The functional type should be NORMAL, POPUP, or UTILITY, but not a combination of these");
 253         }
 254 
 255         if (((styleMask & UNIFIED) != 0)
 256                 && !Application.GetApplication().supportsUnifiedWindows()) {
 257            styleMask &= ~UNIFIED;
 258         }
 259 
 260         if (((styleMask & TRANSPARENT) != 0)
 261                 && !Application.GetApplication().supportsTransparentWindows()) {
 262             styleMask &= ~TRANSPARENT;
 263         }
 264 
 265 
 266         this.owner = owner;
 267         this.parent = 0L;
 268         this.styleMask = styleMask;
 269         this.isDecorated = (this.styleMask & Window.TITLED) != 0;
 270 
 271         this.screen = screen != null ? screen : Screen.getMainScreen();
 272 
 273         this.ptr = _createWindow(owner != null ? owner.getNativeHandle() : 0L,
 274                 this.screen.getNativeScreen(), this.styleMask);
 275         if (this.ptr == 0L) {
 276             throw new RuntimeException("could not create platform window");
 277         }
 278     }
 279 
 280     protected abstract long _createChildWindow(long parent);
 281     /**
 282      * Constructs a child window of the specified native parent window.
 283      */
 284     protected Window(long parent) {
 285         Application.checkEventThread();
 286         this.owner = null;
 287         this.parent = parent;
 288         this.styleMask = Window.UNTITLED;
 289         this.isDecorated = false;
 290 
 291         // Note: we can't always catch screen changes when parent is moved...
 292         this.screen = null; // should infer from the parent
 293 
 294         this.ptr = _createChildWindow(parent);
 295         if (this.ptr == 0L) {
 296             throw new RuntimeException("could not create platform window");
 297         }
 298         if (screen == null) {
 299             screen = Screen.getMainScreen(); // start with a default
 300         }
 301     }
 302 
 303     public boolean isClosed() {
 304         Application.checkEventThread();
 305         return this.ptr == 0L;
 306     }
 307 
 308     private void checkNotClosed() {
 309         if (this.ptr == 0L) {
 310             throw new IllegalStateException("The window has already been closed");
 311         }
 312     }
 313 
 314     protected abstract boolean _close(long ptr);
 315     public void close() {
 316         Application.checkEventThread();
 317         if (this.view != null) {
 318             if (this.ptr != 0L) {
 319                 _setView(this.ptr, null);
 320             }
 321             this.view.setWindow(null);
 322             this.view.close();
 323             this.view = null;
 324         }
 325         if (this.ptr != 0L) {
 326             _close(this.ptr);
 327         }
 328     }
 329 
 330     private boolean isChild() {
 331         Application.checkEventThread();
 332         return this.parent != 0L;
 333     }
 334 
 335     /** This method returns "lowest-level" native window handle
 336      * (HWND on Windows, NSWindow on Mac, X11 Window handle on linux-gtk etc.)
 337      */
 338     public long getNativeWindow() {
 339         Application.checkEventThread();
 340         checkNotClosed();
 341         return this.delegatePtr != 0L ? this.delegatePtr : this.ptr;
 342     }
 343 
 344     /**
 345      * This method returns "higher-level" native window handle.
 346      * glass-mat-lib-gtk GtkWindow.java returns GtkWindow pointer for example.
 347      */
 348     public long getNativeHandle() {
 349         Application.checkEventThread();
 350         return this.delegatePtr != 0L ? this.delegatePtr : this.ptr;
 351     }
 352 
 353     /**
 354      * return the "raw' pointer needed by subclasses to pass to native routines
 355      * @return the native pointer.
 356      */
 357     public long getRawHandle() {
 358         return ptr;
 359     }
 360 
 361     public Window getOwner() {
 362         Application.checkEventThread();
 363         return this.owner;
 364     }
 365 
 366     public View getView() {
 367         Application.checkEventThread();
 368         return this.view;
 369     }
 370 
 371     protected abstract boolean _setView(long ptr, View view);
 372     public void setView(final View view) {
 373         Application.checkEventThread();
 374         checkNotClosed();
 375         View oldView = getView();
 376         if (oldView == view) {
 377             return;
 378         }
 379 
 380         if (oldView != null) {
 381             oldView.setWindow(null);
 382         }
 383         if (view != null) {
 384             Window host = view.getWindow();
 385             if (host != null) {
 386                 host.setView(null);
 387             }
 388         }
 389 
 390         if (view != null && _setView(this.ptr, view)) {
 391             this.view = view;
 392             this.view.setWindow(this);
 393             if (this.isDecorated == false) {
 394                 this.helper = new UndecoratedMoveResizeHelper();
 395             }
 396         } else {
 397             _setView(this.ptr, null);
 398             this.view = null;
 399         }
 400     }
 401 
 402     public Screen getScreen() {
 403         Application.checkEventThread();
 404         return this.screen;
 405     }
 406 
 407     protected void setScreen(Screen screen) {
 408         Application.checkEventThread();
 409 
 410         final Screen old = this.screen;
 411         this.screen = screen;
 412 
 413         if (this.eventHandler != null) {
 414             if ((old == null && this.screen != null) ||
 415                 (old != null && !old.equals(this.screen))) {
 416                 this.eventHandler.handleScreenChangedEvent(this, System.nanoTime(), old, this.screen);
 417             }
 418         }
 419     }
 420 
 421     public int getStyleMask() {
 422         Application.checkEventThread();
 423         return this.styleMask;
 424     }
 425 
 426     public MenuBar getMenuBar() {
 427         Application.checkEventThread();
 428         return this.menubar;
 429     }
 430 
 431     protected abstract boolean _setMenubar(long ptr, long menubarPtr);
 432     public void setMenuBar(final MenuBar menubar) {
 433         Application.checkEventThread();
 434         checkNotClosed();
 435         if (_setMenubar(this.ptr, menubar.getNativeMenu())) {
 436             this.menubar = menubar;
 437         }
 438     }
 439 
 440     public boolean isDecorated() {
 441         Application.checkEventThread();
 442         return this.isDecorated;
 443     }
 444 
 445     public boolean isMinimized() {
 446         Application.checkEventThread();
 447         return (this.state == State.MINIMIZED);
 448     }
 449 
 450     protected abstract boolean _minimize(long ptr, boolean minimize);
 451     public boolean minimize(final boolean minimize) {
 452         Application.checkEventThread();
 453         checkNotClosed();
 454         _minimize(this.ptr, minimize);
 455         //XXX: this is synchronous? On X11 this may not work
 456         return isMinimized();
 457     }
 458 
 459     public boolean isMaximized() {
 460         Application.checkEventThread();
 461         return (this.state == State.MAXIMIZED);
 462     }
 463 
 464     protected abstract boolean _maximize(long ptr, boolean maximize, boolean wasMaximized);
 465     public boolean maximize(final boolean maximize) {
 466         Application.checkEventThread();
 467         checkNotClosed();
 468         _maximize(ptr, maximize, isMaximized());
 469         return isMaximized();
 470     }
 471 
 472     public void setPlatformScale(float platformScale) {
 473         if (!PrismSettings.allowHiDPIScaling) return;
 474         this.platformScale = platformScale;
 475     }
 476 
 477     /**
 478      * Return the scale used to communicate window locations, sizes, and event
 479      * coordinates to/from the platform.
 480      * @return the platform scaling for screen locations
 481      */
 482     public final float getPlatformScale() {
 483         return platformScale;
 484     }
 485 
 486     public void setRenderScale(float renderScale) {
 487         if (!PrismSettings.allowHiDPIScaling) return;
 488         this.renderScale = renderScale;
 489     }
 490 
 491     /**
 492      * Return the scale that should be used to render content on this window.
 493      * This is usually similar to the platform scale, but may be different
 494      * depending on how the platform manages events vs. rendering buffers
 495      * and/or whether the system can handle non-integer rendering scales.
 496      * @return the pixel scaling to be used during rendering
 497      */
 498     public final float getRenderScale() {
 499         return renderScale;
 500     }
 501 
 502     public float getOutputScale() {
 503         return platformScale;
 504     }
 505 
 506     protected abstract int _getEmbeddedX(long ptr);
 507     protected abstract int _getEmbeddedY(long ptr);
 508 
 509     private void checkScreenLocation() {
 510         this.x = _getEmbeddedX(ptr);
 511         this.y = _getEmbeddedY(ptr);
 512         if ((this.x != lastKnownEmbeddedX) || (this.y != lastKnownEmbeddedY)) {
 513             lastKnownEmbeddedX = this.x;
 514             lastKnownEmbeddedY = this.y;
 515             handleWindowEvent(System.nanoTime(), WindowEvent.MOVE);
 516         }
 517     }
 518 
 519     public int getX() {
 520         Application.checkEventThread();
 521         return this.x;
 522     }
 523 
 524     public int getY() {
 525         Application.checkEventThread();
 526         return this.y;
 527     }
 528 
 529     public int getWidth() {
 530         Application.checkEventThread();
 531         return this.width;
 532     }
 533 
 534     public int getHeight() {
 535         Application.checkEventThread();
 536         return this.height;
 537     }
 538 
 539     protected abstract void _setBounds(long ptr, int x, int y, boolean xSet, boolean ySet,
 540                                        int w, int h, int cw, int ch,
 541                                        float xGravity, float yGravity);
 542 
 543     /**
 544      * Sets the window bounds to the specified values.
 545      *
 546      * Gravity values specify how to correct window location if only its size
 547      * changes (for example when stage decorations are added). User initiated
 548      * resizing should be ignored and must not influence window location through
 549      * this mechanism.
 550      *
 551      * The corresponding correction formulas are:
 552      *
 553      * {@code x -= xGravity * deltaW}
 554      * {@code y -= yGravity * deltaH}
 555      *
 556      * @param x the new window horizontal position, ignored if xSet is set to
 557      *          false
 558      * @param y the new window vertical position, ignored if ySet is set to
 559      *          false
 560      * @param xSet indicates whether the x parameter is valid
 561      * @param ySet indicates whether the y parameter is valid
 562      * @param w the new window width, ignored if set to -1
 563      * @param h the new window height, ignored if set to -1
 564      * @param cw the new window content width, ignored if set to -1
 565      * @param ch the new window content height, ignored if set to -1
 566      * @param xGravity the xGravity coefficient
 567      * @param yGravity the yGravity coefficient
 568      */
 569     public void setBounds(int x, int y, boolean xSet, boolean ySet,
 570                           int w, int h, int cw, int ch,
 571                           float xGravity, float yGravity)
 572     {
 573         Application.checkEventThread();
 574         checkNotClosed();
 575         _setBounds(ptr, x, y, xSet, ySet, w, h, cw, ch, xGravity, yGravity);
 576     }
 577 
 578     public void setPosition(int x, int y) {
 579         Application.checkEventThread();
 580         setBounds(x, y, true, true, 0, 0, 0, 0, 0, 0);
 581     }
 582 
 583     public void setSize(int w, int h) {
 584         Application.checkEventThread();
 585         setBounds(0, 0, false, false, w, h, 0, 0, 0, 0);
 586     }
 587 
 588     public void setContentSize(int cw, int ch) {
 589         Application.checkEventThread();
 590         setBounds(0, 0, false, false, 0, 0, cw, ch, 0, 0);
 591     }
 592 
 593     public boolean isVisible() {
 594         Application.checkEventThread();
 595         return this.isVisible;
 596     }
 597 
 598     /**
 599      * Generates a ViewEvent.MOVE aka insets (might have) changed.
 600      */
 601     private void synthesizeViewMoveEvent() {
 602         final View view = getView();
 603         if (view != null) {
 604             view.notifyView(com.sun.glass.events.ViewEvent.MOVE);
 605         }
 606     }
 607 
 608     protected abstract boolean _setVisible(long ptr, boolean visible);
 609     public void setVisible(final boolean visible) {
 610         Application.checkEventThread();
 611         if (this.isVisible != visible) {
 612             if (!visible) {
 613                 if (getView() != null) {
 614                     getView().setVisible(visible);
 615                 }
 616                 // Avoid native call if the window has been closed already
 617                 if (this.ptr != 0L) {
 618                     this.isVisible = _setVisible(this.ptr, visible);
 619                 } else {
 620                     this.isVisible = visible;
 621                 }
 622                 remove(this);
 623                 if (parent != 0) {
 624                     embeddedLocationTimer.stop();
 625                 }
 626             } else {
 627                 checkNotClosed();
 628                 this.isVisible = _setVisible(this.ptr, visible);
 629 
 630                 if (getView() != null) {
 631                     getView().setVisible(this.isVisible);
 632                 }
 633                 add(this);
 634                 if (parent != 0) {
 635                     final Runnable checkRunnable = () -> checkScreenLocation();
 636                     final Runnable timerRunnable = () -> Application.invokeLater(checkRunnable);
 637                     embeddedLocationTimer =
 638                            Application.GetApplication().createTimer(timerRunnable);
 639                     embeddedLocationTimer.start(16);
 640                 }
 641 
 642                 synthesizeViewMoveEvent();
 643             }
 644         }
 645     }
 646 
 647     protected abstract boolean _setResizable(long ptr, boolean resizable);
 648     public boolean setResizable(final boolean resizable) {
 649         Application.checkEventThread();
 650         checkNotClosed();
 651         if (this.isResizable != resizable) {
 652             if (_setResizable(this.ptr, resizable)) {
 653                 this.isResizable = resizable;
 654                 synthesizeViewMoveEvent();
 655             }
 656         }
 657         return isResizable;
 658     }
 659 
 660     public boolean isResizable() {
 661         Application.checkEventThread();
 662         return this.isResizable;
 663     }
 664 
 665     public boolean isUnifiedWindow() {
 666         //The UNIFIED flag is set only if it is supported
 667         return (this.styleMask & Window.UNIFIED) != 0;
 668     }
 669 
 670     public boolean isTransparentWindow() {
 671         //The TRANSPARENT flag is set only if it is supported
 672         return (this.styleMask & Window.TRANSPARENT) != 0;
 673     }
 674 
 675     private static volatile Window focusedWindow = null;
 676     public static Window getFocusedWindow() {
 677         Application.checkEventThread();
 678         return Window.focusedWindow;
 679     }
 680 
 681     private static void setFocusedWindow(final Window window) {
 682         Window.focusedWindow = window;
 683     }
 684 
 685     public boolean isFocused() {
 686         Application.checkEventThread();
 687         return this.isFocused;
 688     }
 689 
 690     protected abstract boolean _requestFocus(long ptr, int event);
 691     /**
 692      * Requests or resigns focus on this window.
 693      *
 694      * If this is a top-level window (owned or not), then the only possible
 695      * value for the {@code event} argument is WindowEvent.FOCUS_GAINED.
 696      * Otherwise, if the window is a child window, the argument may be
 697      * WindowEvent.FOCUS_LOST, FOCUS_GAINED, FOCUS_GAINED_FORWARD, or
 698      * FOCUS_GAINED_BACKWARD.
 699      *
 700      * @param event one of WindowEvent.FOCUS_LOST, FOCUS_GAINED, FOCUS_GAINED_FORWARD, FOCUS_GAINED_BACKWARD
 701      *
 702      * @throws IllegalArgumentException if the argument value is invalid for this window
 703      *
 704      * @return {@code true} if the operation succeeded
 705      */
 706     public boolean requestFocus(int event) {
 707         Application.checkEventThread();
 708         checkNotClosed();
 709 
 710         if (!isChild() && event != WindowEvent.FOCUS_GAINED) {
 711             throw new IllegalArgumentException("Invalid focus event ID for top-level window");
 712         }
 713 
 714         if (isChild() && (event < WindowEvent._FOCUS_MIN || event > WindowEvent._FOCUS_MAX)) {
 715             throw new IllegalArgumentException("Invalid focus event ID for child window");
 716         }
 717 
 718         if (event == WindowEvent.FOCUS_LOST && !isFocused()) {
 719             // Already unfocused, nothing to do
 720             return true;
 721         }
 722 
 723         // At this point either A) the user requests focus for a focused or unfocused window,
 724         // or B) the window is focused and the user requests FOCUS_LOST
 725         if (!this.isFocusable) {
 726             // It's obviously A). Fail.
 727             return false;
 728         }
 729 
 730         return _requestFocus(this.ptr, event);
 731     }
 732 
 733     public boolean requestFocus() {
 734         Application.checkEventThread();
 735         return requestFocus(WindowEvent.FOCUS_GAINED);
 736     }
 737 
 738     protected abstract void _setFocusable(long ptr, boolean isFocusable);
 739     /**
 740      * Sets whether this window is focusable.
 741      *
 742      * Clicking an unfocusable window doesn't activate it.
 743      */
 744     public void setFocusable(final boolean isFocusable) {
 745         Application.checkEventThread();
 746         checkNotClosed();
 747         this.isFocusable = isFocusable;
 748         if (isEnabled()) {
 749             _setFocusable(this.ptr, isFocusable);
 750         }
 751     }
 752 
 753     protected abstract boolean _grabFocus(long ptr);
 754     protected abstract void _ungrabFocus(long ptr);
 755     /**
 756      * Grabs focus on this window.
 757      *
 758      * All mouse clicks that occur in this window's client area or client-areas
 759      * of any of its unfocusable owned windows are delivered as usual. Whenever
 760      * a click occurs on another app's window (not related via the ownership
 761      * relation with this one, or a focusable owned window), or on non-client
 762      * area of any window (titlebar, etc.), or any third-party app's window, or
 763      * native OS GUI (e.g. a taskbar), the grab is automatically reset, and the
 764      * window that held the grab receives the FOCUS_UNGRAB event.
 765      *
 766      * Note that for this functionality to work correctly, the window must have
 767      * a focus upon calling this method. All owned popup windows that should be
 768      * operable during the grabbed focus state (e.g. nested popup menus) must
 769      * be unfocusable (see {@link #setFocusable}). Clicking a focusable owned
 770      * window will reset the grab due to a focus transfer.
 771      *
 772      * The click that occurs in another window and causes resetting of the grab
 773      * may or may not be delivered to that other window depending on the native
 774      * OS behavior.
 775      *
 776      * If any of the application's windows already holds the grab, it is reset
 777      * prior to grabbing the focus for this window. The method may be called
 778      * multiple times for one window. Subsequent calls do not affect the grab
 779      * status unless it is reset between the calls, in which case the focus
 780      * is grabbed again.
 781      *
 782      * Note that grabbing the focus on an application window may prevent
 783      * delivering certain events to other applications until the grab is reset.
 784      * Therefore, if the application has finished showing popup windows based
 785      * on a user action (e.g. clicking a menu item), and doesn't require the
 786      * grab any more, it should call the {@link #ungrabFocus} method. The
 787      * FOCUS_UNGRAB event signals that the grab has been reset.
 788      *
 789      * A user event handler associated with a menu item must be invoked after
 790      * resetting the grab. Otherwise, if a developer debugs the application and
 791      * has installed a breakpoint in the event handler, the debugger may become
 792      * unoperable due to events blocking for other applications on some
 793      * platforms.
 794      *
 795      * @return {@code true} if the operation is successful
 796      * @throws IllegalStateException if the window isn't focused currently
 797      */
 798     public boolean grabFocus() {
 799         Application.checkEventThread();
 800         checkNotClosed();
 801 
 802         if (!isFocused()) {
 803             throw new IllegalStateException("The window must be focused when calling grabFocus()");
 804         }
 805 
 806         return _grabFocus(this.ptr);
 807     }
 808 
 809     /**
 810      * Manually ungrabs focus grabbed on this window previously.
 811      *
 812      * This method resets the grab, and forces sending of the FOCUS_UNGRAB
 813      * event. It should be used when popup windows (such as menus) should be
 814      * dismissed manually, e.g. when a user clicks a menu item which usually
 815      * causes the menus to hide.
 816      *
 817      * @see #grabFocus
 818      */
 819     public void ungrabFocus() {
 820         Application.checkEventThread();
 821         checkNotClosed();
 822         _ungrabFocus(this.ptr);
 823     }
 824 
 825     public String getTitle() {
 826         Application.checkEventThread();
 827         return this.title;
 828     }
 829 
 830     protected abstract boolean _setTitle(long ptr, String title);
 831     public void setTitle(String title) {
 832         Application.checkEventThread();
 833         checkNotClosed();
 834         if (title == null) {
 835             title = "";
 836         }
 837         if (!title.equals(this.title)) {
 838             if (_setTitle(this.ptr, title)) {
 839                 this.title = title;
 840             }
 841         }
 842     }
 843 
 844     protected abstract void _setLevel(long ptr, int level);
 845     /**
 846      * Set the level of this window in the z-order.
 847      *
 848      * @param level one of the constants from {@link Window.Level}
 849      * @see Window.Level
 850      */
 851     public void setLevel(final int level) {
 852         Application.checkEventThread();
 853         checkNotClosed();
 854         if (level < Level._MIN || level > Level._MAX) {
 855             throw new IllegalArgumentException("Level should be in the range [" + Level._MIN + ".." + Level._MAX + "]");
 856         }
 857         if (this.level != level) {
 858             _setLevel(this.ptr, level);
 859             this.level = level;
 860         }
 861     }
 862 
 863     public int getLevel() {
 864         Application.checkEventThread();
 865         return this.level;
 866     }
 867 
 868     private boolean isInFullscreen() {
 869         final View view = getView();
 870         return view == null ? false : view.isInFullscreen();
 871     }
 872 
 873     // Invoked from the View class before sending FULLSCREEN_ to the View.EventHandler
 874     void notifyFullscreen(boolean entered) {
 875         final float alpha = getAlpha();
 876         if (alpha < 1f) {
 877             if (entered) {
 878                 // Reset alpha at native level
 879                 _setAlpha(this.ptr, 1f);
 880             } else {
 881                 // restore the current opacity level
 882                 setAlpha(alpha);
 883             }
 884         }
 885     }
 886 
 887     protected abstract void _setAlpha(long ptr, float alpha);
 888     /**
 889      * Sets the uniform translucency level for this window.
 890      *
 891      * In the full screen mode the native window is always fully opaque.
 892      * The requested opacity level is applied upon exiting the full screen
 893      * mode only.
 894      *
 895      * @param alpha a value in the range [0..1f] (transparent..fully-opaque)
 896      */
 897     public void setAlpha(final float alpha) {
 898         Application.checkEventThread();
 899         checkNotClosed();
 900         if (alpha < 0f || alpha > 1f) {
 901             throw new IllegalArgumentException("Alpha should be in the range [0f..1f]");
 902         }
 903 
 904         this.alpha = alpha;
 905 
 906         if (alpha < 1f && isInFullscreen()) {
 907             return;
 908         }
 909 
 910         _setAlpha(this.ptr, this.alpha);
 911     }
 912 
 913     public float getAlpha() {
 914         Application.checkEventThread();
 915         return this.alpha;
 916     }
 917 
 918     protected abstract boolean _setBackground(long ptr, float r, float g, float b);
 919     /**
 920      * Set the background of the window.
 921      *
 922      * In most cases the View covers the whole window, so the background color
 923      * of the window is never seen by the user. However, a window w/o a view
 924      * does display the background color in its content area.
 925      *
 926      * On some platforms setting the background color may produce flickering
 927      * when painting the content area of the View (even though the View covers
 928      * the whole window).  Therefore it is recommended to set the background
 929      * color to windows w/o views only.
 930      */
 931     public boolean setBackground(final float r, final float g, final float b) {
 932         Application.checkEventThread();
 933         checkNotClosed();
 934         return _setBackground(this.ptr, r, g, b);
 935     }
 936 
 937     public boolean isEnabled() {
 938         Application.checkEventThread();
 939         return this.disableCount == 0;
 940     }
 941 
 942     protected abstract void _setEnabled(long ptr, boolean enabled);
 943     /**
 944      * Enables or disables the window.
 945      *
 946      * A disabled window is unfocusable by definition.
 947      * Also, key or mouse events aren't generated for disabled windows.
 948      *
 949      * When a user tries to activate a disabled window, or the window gets
 950      * accidentally brought to the top of the stacking order, the window
 951      * generates the FOCUS_DISABLED window event. A Glass client should react
 952      * to this event and bring the currently active modal blocker of the
 953      * disabled window to top by calling blocker's minimize(false), toFront(),
 954      * and requestFocus() methods. It may also 'blink' the blocker window to
 955      * further attract user's attention.
 956      *
 957      * It's strongly recommended to process the FOCUS_DISABLED event
 958      * synchronously and as fast as possible to avoid any possible visual and
 959      * behavioral artifacts. Note that a disabled window may by no means gain
 960      * the input focus. The purpose of this event is to make sure that the
 961      * current modal blocker window is always visible to the user, and the user
 962      * understands why he can't interact with a disabled window.
 963      *
 964      * The method supports nested calls. If you disable the window twice
 965      * with two calls to setEnabled(false), you must call setEnabled(true)
 966      * twice as well in order to enable it afterwards. This is to support
 967      * 'nested' modal dialogs when one modal dialog opens another one.
 968      */
 969     public void setEnabled(boolean enabled) {
 970         Application.checkEventThread();
 971         checkNotClosed();
 972         if (!enabled) {
 973             if (++this.disableCount > 1) {
 974                 // already disabled
 975                 return;
 976             }
 977         } else {
 978             if (this.disableCount == 0) {
 979                 //should report a warning about an extra enable call ?
 980                 return;
 981             }
 982             if (--this.disableCount > 0) {
 983                 // not yet enabled
 984                 return;
 985             }
 986         }
 987 
 988         //TODO: on Windows _setFocusable(this.ptr, isEnabled() ? this.isFocusable : false);
 989         _setEnabled(this.ptr, isEnabled());
 990     }
 991 
 992     public int getMinimumWidth() {
 993         Application.checkEventThread();
 994         return this.minimumWidth;
 995     }
 996 
 997     public int getMinimumHeight() {
 998         Application.checkEventThread();
 999         return this.minimumHeight;
1000     }
1001 
1002     public int getMaximumWidth() {
1003         Application.checkEventThread();
1004         return this.maximumWidth;
1005     }
1006 
1007     public int getMaximumHeight() {
1008         Application.checkEventThread();
1009         return this.maximumHeight;
1010     }
1011 
1012     protected abstract boolean _setMinimumSize(long ptr, int width, int height);
1013     /**
1014      * Sets the minimum size for this window.
1015      * A value of zero indicates no restriction.
1016      * If the native platform is unable to apply the constraints,
1017      * the values returned by getMinimumWidth()/Height() won't change.
1018      *
1019      * @throws IllegalArgumentException if width or height < 0
1020      */
1021     public void setMinimumSize(int width, int height) {
1022         Application.checkEventThread();
1023         if (width < 0 || height < 0) {
1024             throw new IllegalArgumentException("The width and height must be >= 0. Got: width=" + width + "; height=" + height);
1025         }
1026         checkNotClosed();
1027         if (_setMinimumSize(this.ptr, width, height)) {
1028             this.minimumWidth = width;
1029             this.minimumHeight = height;
1030         }
1031     }
1032 
1033     protected abstract boolean _setMaximumSize(long ptr, int width, int height);
1034     /**
1035      * Sets the maximum size for this window.
1036      * A value of {@code Integer.MAX_VALUE} indicates no restriction.
1037      * If the native platform is unable to apply the constraints,
1038      * the values returned by getMaximumWidth()/Height() won't change.
1039      *
1040      * @throws IllegalArgumentException if width or height < 0
1041      */
1042     public void setMaximumSize(int width, int height) {
1043         Application.checkEventThread();
1044         if (width < 0 || height < 0) {
1045             throw new IllegalArgumentException("The width and height must be >= 0. Got: width=" + width + "; height=" + height);
1046         }
1047         checkNotClosed();
1048         if (_setMaximumSize(this.ptr,
1049                     // for easier handling in native:
1050                     width == Integer.MAX_VALUE ? -1 : width,
1051                     height == Integer.MAX_VALUE ? -1 : height))
1052         {
1053             this.maximumWidth = width;
1054             this.maximumHeight = height;
1055         }
1056     }
1057 
1058 
1059     protected abstract void _setIcon(long ptr, Pixels pixels);
1060 
1061     // In the future we may want to pass a collection of Pixels, so that
1062     // the native platform could pick up the icon with the best dimensions
1063     public void setIcon(final Pixels pixels) {
1064         Application.checkEventThread();
1065         checkNotClosed();
1066         _setIcon(this.ptr, pixels);
1067     }
1068 
1069     protected abstract void _setCursor(long ptr, Cursor cursor);
1070 
1071     /**
1072      * Sets given cursor as the cursor for this window.
1073      * If the cursor is NONE, it is automatically hidden,
1074      * otherwise it is automatically shown.
1075      * @see Cursor#setVisible(boolean)
1076      */
1077     public void setCursor(Cursor cursor) {
1078         Application.checkEventThread();
1079         _setCursor(this.ptr, cursor);
1080     }
1081 
1082     protected abstract void _toFront(long ptr);
1083     /**
1084      * Bring the window to front in the z-order.
1085      * This method DOES NOT activate the window. To make it active use
1086      * the requestFocus() method right after calling toFront().
1087      */
1088     public void toFront() {
1089         Application.checkEventThread();
1090         checkNotClosed();
1091         _toFront(ptr);
1092     }
1093 
1094     protected abstract void _toBack(long ptr);
1095     /**
1096      * Send the window to the bottom of the stacking order.
1097      * This method may or may not de-focus this window
1098      * depending on the native platform. To make sure some other
1099      * window is activated, call requestFocus() on that other window.
1100      */
1101     public void toBack() {
1102         Application.checkEventThread();
1103         checkNotClosed();
1104         _toBack(this.ptr);
1105     }
1106 
1107     // *****************************************************
1108     // modality (prototype using native platform feature)
1109     // *****************************************************
1110     protected abstract void _enterModal(long ptr);
1111     /**
1112      * Enter modal state blocking everything except our window.
1113      */
1114     public void enterModal() {
1115         checkNotClosed();
1116         if (this.isModal == false) {
1117             this.isModal = true;
1118             _enterModal(this.ptr);
1119         }
1120     }
1121 
1122     protected abstract void _enterModalWithWindow(long dialog, long window);
1123     /**
1124      * Enter modal state only blocking the given window.
1125      * On Mac OS X this is done using a dialog sheet.
1126      */
1127     public void enterModal(final Window window) {
1128         checkNotClosed();
1129         if (this.isModal == false) {
1130             this.isModal = true;
1131             _enterModalWithWindow(this.ptr, window.getNativeHandle());
1132         }
1133     }
1134 
1135     protected abstract void _exitModal(long ptr);
1136     public void exitModal() {
1137         checkNotClosed();
1138         if (this.isModal == true) {
1139             _exitModal(this.ptr);
1140             this.isModal = false;
1141         }
1142     }
1143 
1144     public boolean isModal() {
1145         return this.isModal;
1146     }
1147 
1148     /** Only used on Mac when run inside a plugin */
1149     public void dispatchNpapiEvent(Map eventInfo) {
1150         Application.checkEventThread();
1151         throw new RuntimeException("This operation is not supported on this platform");
1152     }
1153 
1154     public EventHandler getEventHandler() {
1155         Application.checkEventThread();
1156         return eventHandler;
1157     }
1158 
1159     public void setEventHandler(EventHandler eventHandler) {
1160         Application.checkEventThread();
1161         this.eventHandler = eventHandler;
1162     }
1163 
1164     /**
1165      * Enables unconditional start of window move operation when
1166      * mouse is dragged in the client area.
1167      */
1168     public void setShouldStartUndecoratedMove(boolean v) {
1169         Application.checkEventThread();
1170         this.shouldStartUndecoratedMove = v;
1171     }
1172 
1173     // *****************************************************
1174     // notification callbacks
1175     // *****************************************************
1176     protected void notifyClose() {
1177         handleWindowEvent(System.nanoTime(), WindowEvent.CLOSE);
1178     }
1179 
1180     protected void notifyDestroy() {
1181         // Mac is known to send multiple WillClose notifications for some reason
1182         if (this.ptr == 0) {
1183             return;
1184         }
1185 
1186         handleWindowEvent(System.nanoTime(), WindowEvent.DESTROY);
1187 
1188         this.ptr = 0;
1189 
1190         // Do this after setting ptr to 0 to avoid a call to _setVisible()
1191         setVisible(false);
1192     }
1193 
1194     protected void notifyMove(final int x, final int y) {
1195         this.x = x;
1196         this.y = y;
1197         handleWindowEvent(System.nanoTime(), WindowEvent.MOVE);
1198     }
1199 
1200     protected void notifyMoveToAnotherScreen(Screen newScreen) {
1201         setScreen(newScreen);
1202     }
1203 
1204     protected void setState(int state) {
1205         this.state = state;
1206     }
1207 
1208     /**
1209      * type values:
1210      *   - WindowEvent.RESIZE
1211      *   - WindowEvent.MINIMIZE
1212      *   - WindowEvent.MAXIMIZE
1213      *   - WindowEvent.RESTORE
1214      */
1215     protected void notifyResize(final int type, final int width, final int height) {
1216         if (type == WindowEvent.MINIMIZE) {
1217             this.state = State.MINIMIZED;
1218         } else {
1219             if (type == WindowEvent.MAXIMIZE) {
1220                 this.state = State.MAXIMIZED;
1221             } else { // WindowEvent.RESIZE or WindowEvent.RESTORE
1222                 this.state = State.NORMAL;
1223             }
1224             this.width = width;
1225             this.height = height;
1226 
1227             // update moveRect/resizeRect
1228             if (this.helper != null){
1229                 this.helper.updateRectangles();
1230             }
1231         }
1232         handleWindowEvent(System.nanoTime(), type);
1233 
1234         /*
1235          * Send RESIZE notification as MAXIMIZE and RESTORE change the window size
1236          */
1237         if (type == WindowEvent.MAXIMIZE || type == WindowEvent.RESTORE) {
1238             handleWindowEvent(System.nanoTime(), WindowEvent.RESIZE);
1239         }
1240     }
1241 
1242     protected void notifyFocus(int event) {
1243         final boolean focused = event != WindowEvent.FOCUS_LOST;
1244 
1245         if (this.isFocused != focused) {
1246             this.isFocused = focused;
1247             if (this.isFocused) {
1248                 setFocusedWindow(this);
1249             } else {
1250                 setFocusedWindow(null);
1251             }
1252             handleWindowEvent(System.nanoTime(), event);
1253         }
1254     }
1255 
1256     protected void notifyFocusDisabled() {
1257         handleWindowEvent(System.nanoTime(), WindowEvent.FOCUS_DISABLED);
1258     }
1259 
1260     protected void notifyFocusUngrab() {
1261         handleWindowEvent(System.nanoTime(), WindowEvent.FOCUS_UNGRAB);
1262     }
1263 
1264     protected void notifyDelegatePtr(long ptr) {
1265         this.delegatePtr = ptr;
1266     }
1267 
1268     // *****************************************************
1269     // window event handlers
1270     // *****************************************************
1271     protected void handleWindowEvent(long time, int type) {
1272         if (this.eventHandler != null) {
1273             this.eventHandler.handleWindowEvent(this, time, type);
1274         }
1275     }
1276 
1277     // *****************************************************
1278     // programmatical move/resize
1279     // *****************************************************
1280     /** Sets "programmatical move" rectangle.
1281      * The rectangle is measured from top of the View:
1282      * width is View.width, height is size.
1283      *
1284      * throws RuntimeException for decorated window.
1285      */
1286     public void setUndecoratedMoveRectangle(int size) {
1287         Application.checkEventThread();
1288         if (this.isDecorated == true) {
1289             //throw new RuntimeException("setUndecoratedMoveRectangle is only valid for Undecorated Window");
1290             System.err.println("Glass Window.setUndecoratedMoveRectangle is only valid for Undecorated Window. In the future this will be hard error.");
1291             Thread.dumpStack();
1292             return;
1293         }
1294 
1295         if (this.helper != null) {
1296             this.helper.setMoveRectangle(size);
1297         }
1298     }
1299     /** The method called only for undecorated windows
1300      * x, y: mouse coordinates (in View space).
1301      *
1302      * throws RuntimeException for decorated window.
1303      */
1304     public boolean shouldStartUndecoratedMove(final int x, final int y) {
1305         Application.checkEventThread();
1306         if (this.shouldStartUndecoratedMove == true) {
1307             return true;
1308         }
1309         if (this.isDecorated == true) {
1310             return false;
1311         }
1312 
1313         if (this.helper != null) {
1314             return this.helper.shouldStartMove(x, y);
1315         } else {
1316             return false;
1317         }
1318     }
1319 
1320     /** Sets "programmatical resize" rectangle.
1321      * The rectangle is measured from top of the View:
1322      * width is View.width, height is size.
1323      *
1324      * throws RuntimeException for decorated window.
1325      */
1326     public void setUndecoratedResizeRectangle(int size) {
1327         Application.checkEventThread();
1328         if ((this.isDecorated == true) || (this.isResizable == false)) {
1329             //throw new RuntimeException("setUndecoratedMoveRectangle is only valid for Undecorated Resizable Window");
1330             System.err.println("Glass Window.setUndecoratedResizeRectangle is only valid for Undecorated Resizable Window. In the future this will be hard error.");
1331             Thread.dumpStack();
1332             return;
1333         }
1334 
1335         if (this.helper != null) {
1336             this.helper.setResizeRectangle(size);
1337         }
1338     }
1339 
1340     /** The method called only for undecorated windows
1341      * x, y: mouse coordinates (in View space).
1342      *
1343      * throws RuntimeException for decorated window.
1344      */
1345     public boolean shouldStartUndecoratedResize(final int x, final int y) {
1346         Application.checkEventThread();
1347         if ((this.isDecorated == true) || (this.isResizable == false)) {
1348             return false;
1349         }
1350 
1351         if (this.helper != null) {
1352             return this.helper.shouldStartResize(x, y);
1353         }  else {
1354             return false;
1355         }
1356     }
1357 
1358     /** Mouse event handler for processing programmatical resize/move
1359      * (for undecorated windows only).
1360      * Must be called by View.
1361      * x & y are View coordinates.
1362      * NOTE: it's package private!
1363      * @return true if the event is processed by the window,
1364      *         false if it has to be delivered to the app
1365      */
1366     boolean handleMouseEvent(int type, int button, int x, int y, int xAbs, int yAbs) {
1367         if (this.isDecorated == false) {
1368             return this.helper.handleMouseEvent(type, button, x, y, xAbs, yAbs);
1369         }
1370         return false;
1371     }
1372 
1373     @Override
1374     public String toString() {
1375         Application.checkEventThread();
1376         return  "Window:"+"\n"
1377                 + "    ptr: " + getNativeWindow() + "\n"
1378                 + "    screen ptr: " + (screen != null ? screen.getNativeScreen() : "null") + "\n"
1379                 + "    isDecorated: " + isDecorated() + "\n"
1380                 + "    title: " + getTitle() + "\n"
1381                 + "    visible: " + isVisible() + "\n"
1382                 + "    focused: " + isFocused() + "\n"
1383                 + "    modal: " + isModal() + "\n"
1384                 + "    state: " + state + "\n"
1385                 + "    x: " + getX() + ", y: " + getY() + ", w: " + getWidth() + ", h: " + getHeight() + "\n"
1386                 + "";
1387     }
1388 
1389     // "programmical" move/resize support for undecorated windows
1390 
1391     static private class TrackingRectangle {
1392         int size = 0;
1393         int x = 0, y = 0, width = 0, height = 0;
1394         boolean contains(final int x, final int y) {
1395             return ((size > 0) &&
1396                     (x >= this.x) && (x < (this.x + this.width)) &&
1397                         (y >= this.y) && (y < (this.y + this.height)));
1398         }
1399     }
1400 
1401     protected void notifyLevelChanged(int level) {
1402         this.level = level;
1403         if (this.eventHandler != null) {
1404             this.eventHandler.handleLevelEvent(level);
1405         }
1406     }
1407 
1408     private class UndecoratedMoveResizeHelper {
1409         TrackingRectangle moveRect = null;
1410         TrackingRectangle resizeRect = null;
1411 
1412         boolean inMove = false;         // we are in "move" mode
1413         boolean inResize = false;       // we are in "resize" mode
1414 
1415         int startMouseX, startMouseY;   // start mouse coords
1416         int startX, startY;             // start window location (for move)
1417         int startWidth, startHeight;    // start window size (for resize)
1418 
1419         UndecoratedMoveResizeHelper() {
1420             this.moveRect = new TrackingRectangle();
1421             this.resizeRect = new TrackingRectangle();
1422         }
1423 
1424         void setMoveRectangle(final int size) {
1425             this.moveRect.size = size;
1426 
1427             this.moveRect.x = 0;
1428             this.moveRect.y = 0;
1429             this.moveRect.width = getWidth();
1430             this.moveRect.height = this.moveRect.size;
1431         }
1432 
1433         boolean shouldStartMove(final int x, final int y) {
1434             return this.moveRect.contains(x, y);
1435         }
1436 
1437         boolean inMove() {
1438             return this.inMove;
1439         }
1440 
1441         void startMove(final int x, final int y) {
1442             this.inMove = true;
1443 
1444             this.startMouseX = x;
1445             this.startMouseY = y;
1446 
1447             this.startX = getX();
1448             this.startY = getY();
1449         }
1450 
1451         void deltaMove(final int x, final int y) {
1452             int deltaX = x - this.startMouseX;
1453             int deltaY = y - this.startMouseY;
1454 
1455             setPosition(this.startX + deltaX, this.startY + deltaY);
1456         }
1457 
1458         void stopMove() {
1459             this.inMove = false;
1460         }
1461 
1462         void setResizeRectangle(final int size) {
1463             this.resizeRect.size = size;
1464 
1465             // set the rect (bottom right corner of the Window)
1466             this.resizeRect.x = getWidth() - this.resizeRect.size;
1467             this.resizeRect.y = getHeight() - this.resizeRect.size;
1468             this.resizeRect.width = this.resizeRect.size;
1469             this.resizeRect.height = this.resizeRect.size;
1470         }
1471 
1472         boolean shouldStartResize(final int x, final int y) {
1473             return this.resizeRect.contains(x, y);
1474         }
1475 
1476         boolean inResize() {
1477             return this.inResize;
1478         }
1479 
1480         void startResize(final int x, final int y) {
1481             this.inResize = true;
1482 
1483             this.startMouseX = x;
1484             this.startMouseY = y;
1485 
1486             this.startWidth = getWidth();
1487             this.startHeight = getHeight();
1488         }
1489 
1490         void deltaResize(final int x, final int y) {
1491             int deltaX = x - this.startMouseX;
1492             int deltaY = y - this.startMouseY;
1493 
1494             setSize(this.startWidth + deltaX, this.startHeight + deltaY);
1495         }
1496 
1497         protected void stopResize() {
1498             this.inResize = false;
1499         }
1500 
1501         void updateRectangles() {
1502             if (this.moveRect.size > 0) {
1503                 setMoveRectangle(this.moveRect.size);
1504             }
1505             if (this.resizeRect.size > 0) {
1506                 setResizeRectangle(this.resizeRect.size);
1507             }
1508         }
1509 
1510         boolean handleMouseEvent(final int type, final int button, final int x, final int y, final int xAbs, final int yAbs) {
1511             switch (type) {
1512                 case MouseEvent.DOWN:
1513                     if (button == MouseEvent.BUTTON_LEFT) {
1514                         if (shouldStartUndecoratedMove(x, y) == true) {
1515                             startMove(xAbs, yAbs);
1516                             return true;
1517                         } else if (shouldStartUndecoratedResize(x, y) == true) {
1518                             startResize(xAbs, yAbs);
1519                             return true;
1520                         }
1521                     }
1522                     break;
1523 
1524                 case MouseEvent.MOVE:
1525                 case MouseEvent.DRAG:
1526                     if (inMove() == true) {
1527                         deltaMove(xAbs, yAbs);
1528                         return true;
1529                     } else if (inResize() == true) {
1530                         deltaResize(xAbs, yAbs);
1531                         return true;
1532                     }
1533                     break;
1534 
1535                 case MouseEvent.UP:
1536                     boolean wasProcessed = inMove() || inResize();
1537                     stopResize();
1538                     stopMove();
1539                     return wasProcessed;
1540             }
1541             return false;
1542         }
1543     }
1544 
1545     /**
1546      * Requests text input in form of native keyboard for text component
1547      * contained by this Window. Native text input component is drawn on the place
1548      * of JavaFX component to cover it completely and to provide native text editing
1549      * techniques. Any change of text is immediately reflected in JavaFX text component.
1550      *
1551      * @param text text to be shown in the native text input component
1552      * @param type type of text input component @see com.sun.javafx.scene.control.behavior.TextInputTypes
1553      * @param width width of JavaFX text input component
1554      * @param height height of JavaFX text input component
1555      * @param M standard transformation matrix for drawing the native text component derived from JavaFX component
1556      */
1557     public void requestInput(String text, int type, double width, double height,
1558                                 double Mxx, double Mxy, double Mxz, double Mxt,
1559                                 double Myx, double Myy, double Myz, double Myt,
1560                                 double Mzx, double Mzy, double Mzz, double Mzt) {
1561         Application.checkEventThread();
1562         _requestInput(this.ptr, text, type, width, height,
1563                         Mxx, Mxy, Mxz, Mxt,
1564                         Myx, Myy, Myz, Myt,
1565                         Mzx, Mzy, Mzz, Mzt);
1566     }
1567 
1568     /**
1569      * Native keyboard for text input is no longer necessary.
1570      * Keyboard will be hidden and native text input component too.
1571      */
1572     public void releaseInput() {
1573         Application.checkEventThread();
1574         _releaseInput(this.ptr);
1575     }
1576 
1577     protected abstract void _requestInput(long ptr, String text, int type, double width, double height,
1578                                             double Mxx, double Mxy, double Mxz, double Mxt,
1579                                             double Myx, double Myy, double Myz, double Myt,
1580                                             double Mzx, double Mzy, double Mzz, double Mzt);
1581 
1582     protected abstract void _releaseInput(long ptr);
1583 
1584 }