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