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