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