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