1 /*
   2  * Copyright (c) 2010, 2014, 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 
  26 package javafx.stage;
  27 
  28 import java.util.ArrayList;
  29 import java.util.List;
  30 
  31 import javafx.beans.property.BooleanProperty;
  32 import javafx.beans.property.BooleanPropertyBase;
  33 import javafx.beans.property.SimpleBooleanProperty;
  34 import javafx.beans.property.StringProperty;
  35 import javafx.beans.property.StringPropertyBase;
  36 import javafx.collections.FXCollections;
  37 import javafx.collections.ListChangeListener.Change;
  38 import javafx.collections.ObservableList;
  39 import javafx.geometry.NodeOrientation;
  40 import javafx.scene.Scene;
  41 import javafx.scene.image.Image;
  42 import javafx.scene.input.KeyCombination;
  43 
  44 import com.sun.javafx.beans.annotations.Default;
  45 import com.sun.javafx.collections.TrackableObservableList;
  46 import com.sun.javafx.robot.impl.FXRobotHelper;
  47 import com.sun.javafx.scene.SceneHelper;
  48 import com.sun.javafx.stage.StageHelper;
  49 import com.sun.javafx.stage.StagePeerListener;
  50 import com.sun.javafx.tk.TKPulseListener;
  51 import com.sun.javafx.tk.TKStage;
  52 import com.sun.javafx.tk.Toolkit;
  53 import java.security.AllPermission;
  54 import javafx.beans.property.DoubleProperty;
  55 import javafx.beans.property.DoublePropertyBase;
  56 import javafx.beans.property.ObjectProperty;
  57 import javafx.beans.property.Property;
  58 import javafx.beans.property.ReadOnlyBooleanProperty;
  59 import javafx.beans.property.ReadOnlyBooleanWrapper;
  60 import javafx.beans.property.SimpleObjectProperty;
  61 import javafx.beans.value.ObservableValue;
  62 
  63 /**
  64  * The JavaFX {@code Stage} class is the top level JavaFX container.
  65  * The primary Stage is constructed by the platform. Additional Stage
  66  * objects may be constructed by the application.
  67  *
  68  * <p>
  69  * Stage objects must be constructed and modified on the
  70  * JavaFX Application Thread.
  71  * </p>
  72  * <p>
  73  * Many of the {@code Stage} properties are read only because they can
  74  * be changed externally by the underlying platform and therefore must
  75  * not be bindable.
  76  * </p>
  77  * 
  78  * <p><b>Style</b></p>
  79  * <p>
  80  * A stage has one of the following styles:
  81  * <ul>
  82  * <li>{@link StageStyle#DECORATED} - a stage with a solid white background and
  83  * platform decorations.</li>
  84  * <li>{@link StageStyle#UNDECORATED} - a stage with a solid white background
  85  * and no decorations.</li>
  86  * <li>{@link StageStyle#TRANSPARENT} - a stage with a transparent background
  87  * and no decorations.</li>
  88  * <li>{@link StageStyle#UTILITY} - a stage with a solid white background and
  89  * minimal platform decorations.</li>
  90  * </ul>
  91  * <p>The style must be initialized before the stage is made visible.</p>
  92  * <p>On some platforms decorations might not be available. For example, on
  93  * some mobile or embedded devices. In these cases a request for a DECORATED or
  94  * UTILITY window will be accepted, but no decorations will be shown. </p>
  95  * 
  96  * <p><b>Owner</b></p>
  97  * <p>
  98  * A stage can optionally have an owner Window.
  99  * When a window is a stage's owner, it is said to be the parent of that stage.
 100  * When a parent window is closed, all its descendant windows are closed. 
 101  * The same chained behavior applied for a parent window that is iconified. 
 102  * A stage will always be on top of its parent window.
 103  * The owner must be initialized before the stage is made visible.
 104  *
 105  * <p><b>Modality</b></p>
 106  * <p>
 107  * A stage has one of the following modalities:
 108  * <ul>
 109  * <li>{@link Modality#NONE} - a stage that does not block any other window.</li>
 110  * <li>{@link Modality#WINDOW_MODAL} - a stage that blocks input events from 
 111  * being delivered to all windows from its owner (parent) to its root.
 112  * Its root is the closest ancestor window without an owner.</li>
 113  * <li>{@link Modality#APPLICATION_MODAL} - a stage that blocks input events from 
 114  * being delivered to all windows from the same application, except for those
 115  * from its child hierarchy.</li>
 116  * </ul> 
 117  * 
 118  * <p>When a window is blocked by a modal stage its Z-order relative to its ancestors
 119  * is preserved, and it receives no input events and no window activation events,
 120  * but continues to animate and render normally.
 121  * Note that showing a modal stage does not necessarily block the caller. The
 122  * {@link #show} method returns immediately regardless of the modality of the stage.
 123  * Use the {@link #showAndWait} method if you need to block the caller until
 124  * the modal stage is hidden (closed).
 125  * The modality must be initialized before the stage is made visible.</p> 
 126  *
 127  * <p><b>Example:</b></p>
 128  *
 129  *
 130 <pre><code>
 131 import javafx.application.Application;
 132 import javafx.scene.Group;
 133 import javafx.scene.Scene;
 134 import javafx.scene.text.Font;
 135 import javafx.scene.text.Text;
 136 import javafx.stage.Stage;
 137 
 138 public class HelloWorld extends Application {
 139 
 140     &#64;Override public void start(Stage stage) {
 141         Text text = new Text(10, 40, "Hello World!");
 142         text.setFont(new Font(40));
 143         Scene scene = new Scene(new Group(text));
 144 
 145         stage.setTitle("Welcome to JavaFX!"); 
 146         stage.setScene(scene); 
 147         stage.sizeToScene(); 
 148         stage.show(); 
 149     }
 150 
 151     public static void main(String[] args) {
 152         Application.launch(args);
 153     }
 154 }
 155 
 156  * </code></pre>
 157  * <p>produces the following on Windows:</p>
 158  * <p><img src="doc-files/Stage-win.png"/></p>
 159  *
 160  * <p>produces the following on Mac OSX:</p>
 161  * <p><img src="doc-files/Stage-mac.png"/></p>
 162  *
 163  * <p>produces the following on Linux:</p>
 164  * <p><img src="doc-files/Stage-linux.png"/></p>
 165  * @since JavaFX 2.0
 166  */
 167 public class Stage extends Window {
 168 
 169     private boolean inNestedEventLoop = false;
 170 
 171     private static ObservableList<Stage> stages = FXCollections.<Stage>observableArrayList();
 172 
 173     static {
 174         FXRobotHelper.setStageAccessor(new FXRobotHelper.FXRobotStageAccessor() {
 175             @Override public ObservableList<Stage> getStages() {
 176                 return stages;
 177             }
 178         });
 179         StageHelper.setStageAccessor(new StageHelper.StageAccessor() {
 180             @Override public ObservableList<Stage> getStages() {
 181                 return stages;
 182             }
 183         });
 184     }
 185     
 186     private static final StagePeerListener.StageAccessor STAGE_ACCESSOR = new StagePeerListener.StageAccessor() {
 187 
 188         @Override
 189         public void setIconified(Stage stage, boolean iconified) {
 190             stage.iconifiedPropertyImpl().set(iconified);
 191         }
 192 
 193         @Override
 194         public void setMaximized(Stage stage, boolean maximized) {
 195             stage.maximizedPropertyImpl().set(maximized);
 196         }
 197 
 198         @Override
 199         public void setResizable(Stage stage, boolean resizable) {
 200             ((ResizableProperty)stage.resizableProperty()).setNoInvalidate(resizable);
 201         }
 202 
 203         @Override
 204         public void setFullScreen(Stage stage, boolean fs) {
 205             stage.fullScreenPropertyImpl().set(fs);
 206         }
 207 
 208         @Override
 209         public void setAlwaysOnTop(Stage stage, boolean aot) {
 210             stage.alwaysOnTopPropertyImpl().set(aot);
 211         }
 212     };
 213 
 214     /**
 215      * Creates a new instance of decorated {@code Stage}.
 216      *
 217      * @throws IllegalStateException if this constructor is called on a thread
 218      * other than the JavaFX Application Thread.
 219      */
 220     public Stage() {
 221         this(StageStyle.DECORATED);
 222     }
 223 
 224     /**
 225      * Creates a new instance of {@code Stage}.
 226      *
 227      * @param style The style of the {@code Stage}
 228      *
 229      * @throws IllegalStateException if this constructor is called on a thread
 230      * other than the JavaFX Application Thread.
 231      */
 232     public Stage(@Default("javafx.stage.StageStyle.DECORATED") StageStyle style) {
 233         super();
 234 
 235         Toolkit.getToolkit().checkFxUserThread();
 236 
 237         // Set the style
 238         initStyle(style);
 239     }
 240     
 241     /**
 242      * Specify the scene to be used on this stage.
 243      */
 244     @Override final public void setScene(Scene value) {
 245         super.setScene(value);
 246     }
 247     
 248     /**
 249      * @inheritDoc
 250      */
 251     @Override public final void show() {
 252         super.show();
 253     }
 254     
 255     private boolean primary = false;
 256 
 257     /**
 258      * sets this stage to be the primary stage.
 259      * When run as an applet, this stage will appear in the broswer
 260      * @treatAsPrivate implementation detail
 261      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 262      */
 263     @Deprecated
 264     public void impl_setPrimary(boolean primary) {
 265         this.primary = primary;
 266     }
 267 
 268     /**
 269      * Returns whether this stage is the primary stage.
 270      * When run as an applet, the primary stage will appear in the broswer
 271      *
 272      * @return true if this stage is the primary stage for the application.
 273      */
 274     boolean isPrimary() {
 275         return primary;
 276     }
 277     
 278     /**
 279      * @treatAsPrivate implementation detail
 280      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 281      */
 282     @Deprecated
 283     @Override
 284     public String impl_getMXWindowType() {
 285         return (primary) ? "PrimaryStage" : getClass().getSimpleName();
 286     }
 287 
 288     private boolean important = true;
 289 
 290     /**
 291      * Sets a flag indicating whether this stage is an "important" window for
 292      * the purpose of determining whether the application is idle and should
 293      * exit. The application is considered finished when the last important
 294      * window is closed.
 295      * @treatAsPrivate implementation detail
 296      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 297      */
 298     @Deprecated
 299     public void impl_setImportant(boolean important) {
 300         this.important = important;
 301     }
 302 
 303     private boolean isImportant() {
 304         return important;
 305     }
 306 
 307     /**
 308      * Shows this stage and waits for it to be hidden (closed) before returning
 309      * to the caller. This method temporarily blocks processing of the current
 310      * event, and starts a nested event loop to handle other events.
 311      * This method must be called on the FX Application thread.
 312      * <p>
 313      * A Stage is hidden (closed) by one of the following means:
 314      * <ul>
 315      * <li>the application calls the {@link #hide} or {@link #close} method on
 316      * this stage</li>
 317      * <li>this stage has a non-null owner window, and its owner is closed</li>
 318      * <li>the user closes the window via the window system (for example,
 319      * by pressing the close button in the window decoration)</li>
 320      * </ul>
 321      * </p>
 322      *
 323      * <p>
 324      * After the Stage is hidden, and the application has returned from the
 325      * event handler to the event loop, the nested event loop terminates
 326      * and this method returns to the caller.
 327      * </p>
 328      * <p>
 329      * For example, consider the following sequence of operations for different
 330      * event handlers, assumed to execute in the order shown below:
 331      * <pre>void evtHander1(...) {
 332      *     stage1.showAndWait();
 333      *     doSomethingAfterStage1Closed(...)
 334      * }
 335      *
 336      * void evtHander2(...) {
 337      *     stage1.hide();
 338      *     doSomethingElseHere(...)
 339      * }</pre>
 340      * evtHandler1 will block at the call to showAndWait. It will resume execution
 341      * after stage1 is hidden and the current event handler, in this case evtHandler2,
 342      * returns to the event loop. This means that doSomethingElseHere will
 343      * execute before doSomethingAfterStage1Closed.
 344      * </p>
 345      *
 346      * <p>
 347      * More than one stage may be shown with showAndWait. Each call
 348      * will start a new nested event loop. The stages may be hidden in any order,
 349      * but a particular nested event loop (and thus the showAndWait method
 350      * for the associated stage) will only terminate after all inner event loops
 351      * have also terminated.
 352      * </p>
 353      * <p>
 354      * For example, consider the following sequence of operations for different
 355      * event handlers, assumed to execute in the order shown below:
 356      * <ul>
 357      * <pre>void evtHander1() {
 358      *     stage1.showAndWait();
 359      *     doSomethingAfterStage1Closed(...)
 360      * }
 361      *
 362      * void evtHander2() {
 363      *     stage2.showAndWait();
 364      *     doSomethingAfterStage2Closed(...)
 365      * }
 366      *
 367      * void evtHander3() {
 368      *     stage1.hide();
 369      *     doSomethingElseHere(...)
 370      * }
 371      *
 372      * void evtHander4() {
 373      *     stage2.hide();
 374      *     doSomethingElseHereToo(...)
 375      * }</pre>
 376      * </ul>
 377      * evtHandler1 will block at the call to stage1.showAndWait, starting up
 378      * a nested event loop just like in the previous example. evtHandler2 will
 379      * then block at the call to stage2.showAndWait, starting up another (inner)
 380      * nested event loop. The first call to stage1.showAndWait will resume execution
 381      * after stage1 is hidden, but only after the inner nested event loop started
 382      * by stage2.showAndWait has terminated. This means that the call to
 383      * stage1.showAndWait won't return until after evtHandler2 has returned.
 384      * The order of execution is: stage1.showAndWait, stage2.showAndWait,
 385      * stage1.hide, doSomethingElseHere, stage2.hide, doSomethingElseHereToo,
 386      * doSomethingAfterStage2Closed, doSomethingAfterStage1Closed.
 387      * </p>
 388      *
 389      * <p>
 390      * This method must not be called on the primary stage or on a stage that
 391      * is already visible.
 392      * </p>
 393      *
 394      * @throws IllegalStateException if this method is called on a thread
 395      *     other than the JavaFX Application Thread.
 396      * @throws IllegalStateException if this method is called on the
 397      *     primary stage.
 398      * @throws IllegalStateException if this stage is already showing.
 399      * @since JavaFX 2.2
 400      */
 401     public void showAndWait() {
 402 
 403         Toolkit.getToolkit().checkFxUserThread();
 404 
 405         if (isPrimary()) {
 406             throw new IllegalStateException("Cannot call this method on primary stage");
 407         }
 408 
 409         if (isShowing()) {
 410             throw new IllegalStateException("Stage already visible");
 411         }
 412 
 413         // TODO: file a new bug; the following assertion can fail if this
 414         // method is called from an event handler that is listening to a
 415         // WindowEvent.WINDOW_HIDING event.
 416         assert !inNestedEventLoop;
 417 
 418         show();
 419         inNestedEventLoop = true;
 420         Toolkit.getToolkit().enterNestedEventLoop(this);
 421     }
 422 
 423     private StageStyle style; // default is set in constructor
 424 
 425     /**
 426      * Specifies the style for this stage. This must be done prior to making
 427      * the stage visible. The style is one of: StageStyle.DECORATED,
 428      * StageStyle.UNDECORATED, StageStyle.TRANSPARENT, or StageStyle.UTILITY.
 429      *
 430      * @param style the style for this stage.
 431      *
 432      * @throws IllegalStateException if this property is set after the stage
 433      * has ever been made visible.
 434      *
 435      * @defaultValue StageStyle.DECORATED
 436      */
 437     public final void initStyle(StageStyle style) {
 438         if (hasBeenVisible) {
 439             throw new IllegalStateException("Cannot set style once stage has been set visible");
 440         }
 441         this.style = style;
 442     }
 443 
 444     /**
 445      * Retrieves the style attribute for this stage.
 446      *
 447      * @return the stage style.
 448      */
 449     public final StageStyle getStyle() {
 450         return style;
 451     }
 452 
 453     private Modality modality = Modality.NONE;
 454 
 455     /**
 456      * Specifies the modality for this stage. This must be done prior to making
 457      * the stage visible. The modality is one of: Modality.NONE,
 458      * Modality.WINDOW_MODAL, or Modality.APPLICATION_MODAL.
 459      *
 460      * @param modality the modality for this stage.
 461      *
 462      * @throws IllegalStateException if this property is set after the stage
 463      * has ever been made visible.
 464      *
 465      * @throws IllegalStateException if this stage is the primary stage.
 466      *
 467      * @defaultValue Modality.NONE
 468      */
 469     public final void initModality(Modality modality) {
 470         if (hasBeenVisible) {
 471             throw new IllegalStateException("Cannot set modality once stage has been set visible");
 472         }
 473 
 474         if (isPrimary()) {
 475             throw new IllegalStateException("Cannot set modality for the primary stage");
 476         }
 477 
 478         this.modality = modality;
 479     }
 480 
 481     /**
 482      * Retrieves the modality attribute for this stage.
 483      *
 484      * @return the modality.
 485      */
 486     public final Modality getModality() {
 487         return modality;
 488     }
 489 
 490     private Window owner = null;
 491 
 492     /**
 493      * Specifies the owner Window for this stage, or null for a top-level,
 494      * unowned stage. This must be done prior to making the stage visible.
 495      *
 496      * @param owner the owner for this stage.
 497      *
 498      * @throws IllegalStateException if this property is set after the stage
 499      * has ever been made visible.
 500      *
 501      * @throws IllegalStateException if this stage is the primary stage.
 502      *
 503      * @defaultValue null
 504      */
 505     public final void initOwner(Window owner) {
 506         if (hasBeenVisible) {
 507             throw new IllegalStateException("Cannot set owner once stage has been set visible");
 508         }
 509 
 510         if (isPrimary()) {
 511             throw new IllegalStateException("Cannot set owner for the primary stage");
 512         }
 513 
 514         this.owner = owner;
 515         
 516         final Scene sceneValue = getScene();
 517         if (sceneValue != null) {
 518             SceneHelper.parentEffectiveOrientationInvalidated(sceneValue);
 519         }
 520     }
 521 
 522     /**
 523      * Retrieves the owner Window for this stage, or null for an unowned stage.
 524      *
 525      * @return the owner Window.
 526      */
 527     public final Window getOwner() {
 528         return owner;
 529     }
 530 
 531     /**
 532      * Specifies whether this {@code Stage} should be a full-screen,
 533      * undecorated window.
 534      * <p>
 535      * The implementation of full-screen mode is platform and profile-dependent.
 536      * </p>
 537      * <p>
 538      * When set to {@code true}, the {@code Stage} will attempt to enter
 539      * full-screen mode when visible. Set to {@code false} to return {@code Stage}
 540      * to windowed mode.
 541      * An {@link IllegalStateException} is thrown if this property is set
 542      * on a thread other than the JavaFX Application Thread.
 543      * </p>
 544      * <p>
 545      * The full-screen mode will be exited (and the {@code fullScreen} attribute
 546      * will be set to {@code false}) if the full-screen
 547      * {@code Stage} loses focus or if another {@code Stage} enters
 548      * full-screen mode on the same {@link Screen}. Note that a {@code Stage}
 549      * in full-screen mode can become invisible without losing its
 550      * full-screen status and will again enter full-screen mode when the
 551      * {@code Stage} becomes visible.
 552      * </p>
 553      * If the platform supports multiple screens an application can control
 554      * which {@code Screen} the Stage will enter full-screen mode on by
 555      * setting its position to be within the bounds of that {@code Screen}
 556      * prior to entering full-screen mode.
 557      * <p>
 558      * However once in full-screen mode, {@code Stage}'s {@code x}, {@code y},
 559      * {@code width}, and {@code height} variables will continue to represent
 560      * the non-full-screen position and size of the window; the same for
 561      * {@code iconified}, {@code resizable}, {@code style}, and {@code
 562      * opacity}. If changes are made to any of these attributes while in
 563      * full-screen mode, upon exiting full-screen mode the {@code Stage} will
 564      * assume those attributes.
 565      * </p>
 566      * <p>
 567      * In case that more {@code Stage} modes are set simultaneously their order
 568      * of importance is {@code iconified}, fullScreen, {@code maximized} (from
 569      * strongest to weakest).
 570      * </p>
 571      * <p>
 572      * The property is read only because it can be changed externally
 573      * by the underlying platform and therefore must not be bindable.
 574      * </p>
 575      *
 576      * Notes regarding desktop profile implementation.
 577      * <p>
 578      * For desktop profile the runtime will attempt to enter full-screen
 579      * exclusive mode (FSEM) if such is supported by the platform and it is
 580      * allowed for this application. If either is not the case a
 581      * simulated full-screen window will be used instead; the window will be
 582      * maximized, made undecorated if possible, and moved to the front.
 583      * </p>
 584      * For desktop profile the user can unconditionally exit full-screen mode
 585      * at any time by pressing {@code ESC}.
 586      * <p>
 587      * There are differences in behavior between applications if a security 
 588      * manager is present. Applications with permissions are allowed to enter 
 589      * full-screen exclusive mode unrestricted. Applications without the proper 
 590      * permissions will have the following restrictions:
 591      * </p>
 592      * <ul>
 593      *  <li>Applications can only enter FSEM in response
 594      *   to user input. More specifically, entering is allowed from mouse
 595      *   ({@code Node.mousePressed/mouseReleased/mouseClicked}) or keyboard
 596      *   ({@code Node.keyPressed/keyReleased/keyTyped}) event handlers. It is
 597      *   not allowed to enter FSEM in response to {@code ESC}
 598      *   key. Attempting to enter FSEM from any other context will result in
 599      *   emulated full-screen mode.
 600      *   <p>
 601      *   If {@code Stage} was constructed as full-screen but not visible
 602      *   it will enter full-screen mode upon becoming visible, with the same
 603      *   limitations to when this is allowed to happen as when setting
 604      *   {@code fullScreen} to {@code true}.
 605      *   </p>
 606      *  </li>
 607      *  <li> If the application was allowed to enter FSEM
 608      *   it will have limited keyboard input. It will only receive KEY_PRESSED
 609      *   and KEY_RELEASED events from the following keys:
 610      *   {@code UP, DOWN, LEFT, RIGHT, SPACE, TAB, PAGE_UP, PAGE_DOWN, HOME, END, ENTER}
 611      *  </li>
 612      * </ul>
 613      * @defaultValue false
 614      */
 615     private ReadOnlyBooleanWrapper fullScreen;
 616 
 617     public final void setFullScreen(boolean value) {
 618         Toolkit.getToolkit().checkFxUserThread();
 619         fullScreenPropertyImpl().set(value);
 620         if (impl_peer != null)
 621             impl_peer.setFullScreen(value);
 622     }
 623 
 624     public final boolean isFullScreen() {
 625         return fullScreen == null ? false : fullScreen.get();
 626     }
 627 
 628     public final ReadOnlyBooleanProperty fullScreenProperty() {
 629         return fullScreenPropertyImpl().getReadOnlyProperty();
 630     }
 631 
 632     private ReadOnlyBooleanWrapper fullScreenPropertyImpl () {
 633         if (fullScreen == null) {
 634             fullScreen = new ReadOnlyBooleanWrapper(Stage.this, "fullScreen");
 635         }
 636         return fullScreen;
 637     }
 638 
 639     /**
 640      * Defines the icon images to be used in the window decorations and when
 641      * minimized. The images should be different sizes of the same image and
 642      * the best size will be chosen, eg. 16x16, 32,32.
 643      *
 644      * @defaultValue empty
 645      */
 646     private ObservableList<Image> icons = new TrackableObservableList<Image>() {
 647         @Override protected void onChanged(Change<Image> c) {
 648             List<Object> platformImages = new ArrayList<Object>();
 649             for (Image icon : icons) {
 650                 platformImages.add(icon.impl_getPlatformImage());
 651             }
 652             if (impl_peer != null) {
 653                 impl_peer.setIcons(platformImages);
 654             }
 655         }
 656     };
 657 
 658     /**
 659      * Gets the icon images to be used in the window decorations and when
 660      * minimized. The images should be different sizes of the same image and
 661      * the best size will be chosen, eg. 16x16, 32,32.
 662      * @return An observable list of icons of this window
 663      */
 664     public final ObservableList<Image> getIcons() {
 665         return icons;
 666     }
 667 
 668     /**
 669      * Defines the title of the {@code Stage}.
 670      *
 671      * @defaultValue empty string
 672      */
 673     private StringProperty title;
 674 
 675     public final void setTitle(String value) {
 676         titleProperty().set(value);
 677     }
 678 
 679     public final String getTitle() {
 680         return title == null ? null : title.get();
 681     }
 682 
 683     public final StringProperty titleProperty() {
 684         if (title == null) {
 685             title = new StringPropertyBase() {
 686 
 687                 @Override
 688                 protected void invalidated() {
 689                     if (impl_peer != null) {
 690                         impl_peer.setTitle(get());
 691                     }
 692                 }
 693 
 694                 @Override
 695                 public Object getBean() {
 696                     return Stage.this;
 697                 }
 698 
 699                 @Override
 700                 public String getName() {
 701                     return "title";
 702                 }
 703             };
 704         }
 705         return title;
 706     }
 707 
 708     /**
 709      * Defines whether the {@code Stage} is iconified or not.
 710      * <p>
 711      * In case that more {@code Stage} modes are set simultaneously their order
 712      * of importance is iconified} {@code fullScreen}, {@code maximized} (from
 713      * strongest to weakest).
 714      * </p>
 715      * <p>
 716      * On some mobile and embedded platforms setting this property to true will
 717      * hide the {@code Stage} but not show an icon for it.
 718      * </p>
 719      * <p>
 720      * The property is read only because it can be changed externally
 721      * by the underlying platform and therefore must not be bindable.
 722      * </p>
 723      *
 724      * @defaultValue false
 725      */
 726     private ReadOnlyBooleanWrapper iconified;
 727 
 728     public final void setIconified(boolean value) {
 729         iconifiedPropertyImpl().set(value);
 730         if (impl_peer != null)
 731             impl_peer.setIconified(value);
 732     }
 733 
 734     public final boolean isIconified() {
 735         return iconified == null ? false : iconified.get();
 736     }
 737 
 738     public final ReadOnlyBooleanProperty iconifiedProperty() {
 739         return iconifiedPropertyImpl().getReadOnlyProperty();
 740     }
 741 
 742     private final ReadOnlyBooleanWrapper iconifiedPropertyImpl() {
 743         if (iconified == null) {
 744             iconified = new ReadOnlyBooleanWrapper(Stage.this, "iconified");
 745         }
 746         return iconified;
 747     }
 748 
 749     /**
 750      * Defines whether the {@code Stage} is maximized or not.
 751      * <p>
 752      * In case that more {@code Stage} modes are set simultaneously their order
 753      * of importance is {@code iconified}, {@code fullScreen}, maximized (from
 754      * strongest to weakest).
 755      * </p>
 756      * <p>
 757      * The property is read only because it can be changed externally
 758      * by the underlying platform and therefore must not be bindable.
 759      * </p>
 760      *
 761      * @defaultValue false
 762      * @since JavaFX 8.0
 763      */
 764     private ReadOnlyBooleanWrapper maximized;
 765 
 766     public final void setMaximized(boolean value) {
 767         maximizedPropertyImpl().set(value);
 768         if (impl_peer != null)
 769             impl_peer.setMaximized(value);
 770     }
 771 
 772     public final boolean isMaximized() {
 773         return maximized == null ? false : maximized.get();
 774     }
 775 
 776     public final ReadOnlyBooleanProperty maximizedProperty() {
 777         return maximizedPropertyImpl().getReadOnlyProperty();
 778     }
 779 
 780     private final ReadOnlyBooleanWrapper maximizedPropertyImpl() {
 781         if (maximized == null) {
 782             maximized = new ReadOnlyBooleanWrapper(Stage.this, "maximized");
 783         }
 784         return maximized;
 785     }
 786     
 787     /**
 788      * Defines whether this {@code Stage} is kept on top of other windows.
 789      * <p>
 790      * If some other window is already always-on-top then the
 791      * relative order between these windows is unspecified (depends on
 792      * platform).
 793      * </p>
 794      * <p>
 795      * There are differences in behavior between applications if a security 
 796      * manager is present. Applications with permissions are allowed to set
 797      * "always on top" flag on a Stage. In applications without the proper 
 798      * permissions, an attempt to set the flag will be ignored and the property
 799      * value will be restored to "false".
 800      * </p>
 801      * <p>
 802      * The property is read only because it can be changed externally
 803      * by the underlying platform and therefore must not be bindable.
 804      * </p>
 805      * 
 806      * @defaultValue false
 807      * @since JavaFX 8u20
 808      */
 809     private ReadOnlyBooleanWrapper alwaysOnTop;
 810     
 811     public final void setAlwaysOnTop(boolean value) {
 812         alwaysOnTopPropertyImpl().set(value);
 813         if (impl_peer != null) {
 814             impl_peer.setAlwaysOnTop(value);
 815         }
 816     }
 817 
 818     public final boolean isAlwaysOnTop() {
 819         return alwaysOnTop == null ? false : alwaysOnTop.get();
 820     }
 821 
 822     public final ReadOnlyBooleanProperty alwaysOnTopProperty() {
 823         return alwaysOnTopPropertyImpl().getReadOnlyProperty();
 824     }
 825 
 826     private ReadOnlyBooleanWrapper alwaysOnTopPropertyImpl() {
 827         if (alwaysOnTop == null) {
 828             alwaysOnTop = new ReadOnlyBooleanWrapper(Stage.this, "alwaysOnTop");
 829         }
 830         return alwaysOnTop;
 831     }
 832 
 833     /**
 834      * Defines whether the {@code Stage} is resizable or not by the user.
 835      * Programatically you may still change the size of the Stage. This is
 836      * a hint which allows the implementation to optionally make the Stage
 837      * resizable by the user.
 838      * <p>
 839      * <b>Warning:</b> Since 8.0 the property cannot be bound and will throw
 840      * {@code RuntimeException} on an attempt to do so. This is because
 841      * the setting of resizable is asynchronous on some systems or generally
 842      * might be set by the system / window manager.
 843      * <br>
 844      * Bidirectional binds are still allowed, as they don't block setting of the
 845      * property by the system.
 846      * 
 847      * @defaultValue true
 848      */
 849     private BooleanProperty resizable;
 850 
 851     public final void setResizable(boolean value) {
 852         resizableProperty().set(value);
 853     }
 854 
 855     public final boolean isResizable() {
 856         return resizable == null ? true : resizable.get();
 857     }
 858 
 859     public final BooleanProperty resizableProperty() {
 860         if (resizable == null) {
 861             resizable = new ResizableProperty();
 862         }
 863         return resizable;
 864     }
 865 
 866     //We cannot return ReadOnlyProperty in resizable, as this would be
 867     // backward incompatible. All we can do is to create this custom property
 868     // implementation that disallows binds
 869     private class ResizableProperty extends SimpleBooleanProperty {
 870         private boolean noInvalidate;
 871 
 872         public ResizableProperty() {
 873             super(Stage.this, "resizable", true);
 874         }
 875 
 876         void setNoInvalidate(boolean value) {
 877             noInvalidate = true;
 878             set(value);
 879             noInvalidate = false;
 880         }
 881 
 882         @Override
 883         protected void invalidated() {
 884             if (noInvalidate) {
 885                 return;
 886             }
 887             if (impl_peer != null) {
 888                 applyBounds();
 889                 impl_peer.setResizable(get());
 890             }
 891         }
 892 
 893         @Override
 894         public void bind(ObservableValue<? extends Boolean> rawObservable) {
 895             throw new RuntimeException("Resizable property cannot be bound");
 896         }
 897 
 898     }
 899 
 900     /**
 901      * Defines the minimum width of this {@code Stage}.
 902      *
 903      * @defaultValue 0
 904      * @since JavaFX 2.1
 905      */
 906     private DoubleProperty minWidth;
 907 
 908     public final void setMinWidth(double value) {
 909         minWidthProperty().set(value);
 910     }
 911 
 912     public final double getMinWidth() {
 913         return minWidth == null ? 0 : minWidth.get();
 914     }
 915 
 916     public final DoubleProperty minWidthProperty() {
 917         if (minWidth == null) {
 918             minWidth = new DoublePropertyBase(0) {
 919 
 920                 @Override
 921                 protected void invalidated() {
 922                     if (impl_peer != null) {
 923                         impl_peer.setMinimumSize((int) Math.ceil(get()),
 924                                 (int) Math.ceil(getMinHeight()));
 925                     }
 926                     if (getWidth() < getMinWidth()) {
 927                         setWidth(getMinWidth());
 928                     }
 929                 }
 930 
 931                 @Override
 932                 public Object getBean() {
 933                     return Stage.this;
 934                 }
 935 
 936                 @Override
 937                 public String getName() {
 938                     return "minWidth";
 939                 }
 940             };
 941         }
 942         return minWidth;
 943     }
 944 
 945     /**
 946      * Defines the minimum height of this {@code Stage}.
 947      *
 948      * @defaultValue 0
 949      * @since JavaFX 2.1
 950      */
 951     private DoubleProperty minHeight;
 952 
 953     public final void setMinHeight(double value) {
 954         minHeightProperty().set(value);
 955     }
 956 
 957     public final double getMinHeight() {
 958         return minHeight == null ? 0 : minHeight.get();
 959     }
 960 
 961     public final DoubleProperty minHeightProperty() {
 962         if (minHeight == null) {
 963             minHeight = new DoublePropertyBase(0) {
 964 
 965                 @Override
 966                 protected void invalidated() {
 967                     if (impl_peer != null) {
 968                         impl_peer.setMinimumSize(
 969                                 (int) Math.ceil(getMinWidth()),
 970                                 (int) Math.ceil(get()));
 971                     }
 972                     if (getHeight() < getMinHeight()) {
 973                         setHeight(getMinHeight());
 974                     }
 975                 }
 976 
 977                 @Override
 978                 public Object getBean() {
 979                     return Stage.this;
 980                 }
 981 
 982                 @Override
 983                 public String getName() {
 984                     return "minHeight";
 985                 }
 986             };
 987         }
 988         return minHeight;
 989     }
 990 
 991     /**
 992      * Defines the maximum width of this {@code Stage}.
 993      *
 994      * @defaultValue Double.MAX_VALUE
 995      * @since JavaFX 2.1
 996      */
 997     private DoubleProperty maxWidth;
 998 
 999     public final void setMaxWidth(double value) {
1000         maxWidthProperty().set(value);
1001     }
1002 
1003     public final double getMaxWidth() {
1004         return maxWidth == null ? Double.MAX_VALUE : maxWidth.get();
1005     }
1006 
1007     public final DoubleProperty maxWidthProperty() {
1008         if (maxWidth == null) {
1009             maxWidth = new DoublePropertyBase(Double.MAX_VALUE) {
1010 
1011                 @Override
1012                 protected void invalidated() {
1013                     if (impl_peer != null) {
1014                         impl_peer.setMaximumSize((int) Math.floor(get()),
1015                                 (int) Math.floor(getMaxHeight()));
1016                     }
1017                     if (getWidth() > getMaxWidth()) {
1018                         setWidth(getMaxWidth());
1019                     }
1020                 }
1021 
1022                 @Override
1023                 public Object getBean() {
1024                     return Stage.this;
1025                 }
1026 
1027                 @Override
1028                 public String getName() {
1029                     return "maxWidth";
1030                 }
1031             };
1032         }
1033         return maxWidth;
1034     }
1035 
1036     /**
1037      * Defines the maximum height of this {@code Stage}.
1038      *
1039      * @defaultValue Double.MAX_VALUE
1040      * @since JavaFX 2.1
1041      */
1042     private DoubleProperty maxHeight;
1043 
1044     public final void setMaxHeight(double value) {
1045         maxHeightProperty().set(value);
1046     }
1047 
1048     public final double getMaxHeight() {
1049         return maxHeight == null ? Double.MAX_VALUE : maxHeight.get();
1050     }
1051 
1052     public final DoubleProperty maxHeightProperty() {
1053         if (maxHeight == null) {
1054             maxHeight = new DoublePropertyBase(Double.MAX_VALUE) {
1055 
1056                 @Override
1057                 protected void invalidated() {
1058                     if (impl_peer != null) {
1059                         impl_peer.setMaximumSize(
1060                                 (int) Math.floor(getMaxWidth()),
1061                                 (int) Math.floor(get()));
1062                     }
1063                     if (getHeight() > getMaxHeight()) {
1064                         setHeight(getMaxHeight());
1065                     }
1066                 }
1067 
1068                 @Override
1069                 public Object getBean() {
1070                     return Stage.this;
1071                 }
1072 
1073                 @Override
1074                 public String getName() {
1075                     return "maxHeight";
1076                 }
1077             };
1078         }
1079         return maxHeight;
1080     }
1081 
1082     
1083     /**
1084      * @treatAsPrivate implementation detail
1085      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1086      */
1087     @Deprecated
1088     @Override protected void impl_visibleChanging(boolean value) {
1089         super.impl_visibleChanging(value);
1090         Toolkit toolkit = Toolkit.getToolkit();
1091         if (value && (impl_peer == null)) {
1092             // Setup the peer
1093             Window window = getOwner();
1094             TKStage tkStage = (window == null ? null : window.impl_getPeer());
1095             Scene scene = getScene();
1096             boolean rtl = scene != null && scene.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT;
1097 
1098             StageStyle stageStyle = getStyle();
1099             if (stageStyle == StageStyle.TRANSPARENT) {
1100                 final SecurityManager securityManager =
1101                         System.getSecurityManager();
1102                 if (securityManager != null) {
1103                     try {
1104                         securityManager.checkPermission(new AllPermission());
1105                     } catch (final SecurityException e) {
1106                         stageStyle = StageStyle.UNDECORATED;
1107                     }
1108                 }
1109             }
1110             impl_peer = toolkit.createTKStage(this, stageStyle, isPrimary(),
1111                                               getModality(), tkStage, rtl, acc);
1112             impl_peer.setMinimumSize((int) Math.ceil(getMinWidth()),
1113                     (int) Math.ceil(getMinHeight()));
1114             impl_peer.setMaximumSize((int) Math.floor(getMaxWidth()),
1115                     (int) Math.floor(getMaxHeight()));
1116             peerListener = new StagePeerListener(this, STAGE_ACCESSOR);
1117            // Insert this into stages so we have a references to all created stages
1118             stages.add(this);
1119         }
1120     }
1121 
1122     
1123     /**
1124      * @treatAsPrivate implementation detail
1125      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1126      */
1127     @Deprecated
1128     @Override protected void impl_visibleChanged(boolean value) {
1129         super.impl_visibleChanged(value);
1130 
1131         if (value) {
1132             // Finish initialization
1133             impl_peer.setImportant(isImportant());
1134             impl_peer.setResizable(isResizable());
1135             impl_peer.setFullScreen(isFullScreen());
1136             impl_peer.setAlwaysOnTop(isAlwaysOnTop());
1137             impl_peer.setIconified(isIconified());
1138             impl_peer.setMaximized(isMaximized());
1139             impl_peer.setTitle(getTitle());
1140 
1141             List<Object> platformImages = new ArrayList<Object>();
1142             for (Image icon : icons) {
1143                 platformImages.add(icon.impl_getPlatformImage());
1144             }
1145             if (impl_peer != null) {
1146                 impl_peer.setIcons(platformImages);
1147             }
1148         }
1149 
1150         if (!value) {
1151             // Remove form active stage list
1152             stages.remove(this);
1153         }
1154 
1155         if (!value && inNestedEventLoop) {
1156             inNestedEventLoop = false;
1157             Toolkit.getToolkit().exitNestedEventLoop(this, null);
1158         }
1159     }
1160 
1161     /**
1162      * Bring the {@code Window} to the foreground.  If the {@code Window} is
1163      * already in the foreground there is no visible difference.
1164      */
1165     public void toFront() {
1166         if (impl_peer != null) {
1167             impl_peer.toFront();
1168         }
1169     }
1170 
1171     /**
1172      * Send the {@code Window} to the background.  If the {@code Window} is
1173      * already in the background there is no visible difference.  This action
1174      * places this {@code Window} at the bottom of the stacking order on
1175      * platforms that support stacking.
1176      */
1177     public void toBack() {
1178         if (impl_peer != null) {
1179             impl_peer.toBack();
1180         }
1181     }
1182 
1183     /**
1184      * Closes this {@code Stage}.
1185      * This call is equivalent to {@code hide()}.
1186      */
1187     public void close() {
1188         hide();
1189     }
1190 
1191     @Override
1192     Window getWindowOwner() {
1193         return getOwner();
1194     }
1195 
1196     
1197     private final ObjectProperty<KeyCombination> fullScreenExitCombination =
1198             new SimpleObjectProperty<KeyCombination>(this, "fullScreenExitCombination", null);
1199 
1200     /**
1201      * Specifies the KeyCombination that will allow the user to exit full screen
1202      * mode. A value of KeyCombination.NO_MATCH will not match any KeyEvent and
1203      * will make it so the user is not able to escape from Full Screen mode.
1204      * A value of null indicates that the default platform specific key combination
1205      * should be used. 
1206      * <p>
1207      * An internal copy of this value is made when entering FullScreen mode and will be 
1208      * used to trigger the exit from the mode. If an application does not have
1209      * the proper permissions, this setting will be ignored.
1210      * </p>
1211      * @param keyCombination the key combination to exit on
1212      * @since JavaFX 8.0 
1213      */
1214     public final void setFullScreenExitKeyCombination(KeyCombination keyCombination) {
1215         fullScreenExitCombination.set(keyCombination);
1216     }
1217 
1218     /**
1219      * Get the current sequence used to exit Full Screen mode.
1220      * @return the current setting (null for system default)
1221      * @since JavaFX 8.0 
1222      */
1223     public final KeyCombination getFullScreenExitKeyCombination() {
1224         return fullScreenExitCombination.get();
1225     }
1226 
1227     /**
1228      * Get the property for the Full Screen exit key combination.
1229      * @return the property.
1230      * @since JavaFX 8.0 
1231      */
1232     public final ObjectProperty<KeyCombination> fullScreenExitKeyProperty() {
1233         return fullScreenExitCombination;
1234     }
1235 
1236     private final ObjectProperty<String> fullScreenExitHint = 
1237             new SimpleObjectProperty<String>(this, "fullScreenExitHint", null);
1238                 
1239     /**
1240      * Specifies the text to show when a user enters full screen mode, usually
1241      * used to indicate the way a user should go about exiting out of full
1242      * screen mode. A value of null will result in the default per-locale
1243      * message being displayed. 
1244      * If set to the empty string, then no message will be displayed. 
1245      * <p>
1246      * If an application does not have the proper permissions, this setting 
1247      * will be ignored.
1248      * </p>
1249      * @param value the string to be displayed.
1250      * @since JavaFX 8.0 
1251      */
1252     public final void setFullScreenExitHint(String value) {
1253         fullScreenExitHint.set(value);
1254     }
1255 
1256     public final String getFullScreenExitHint() {
1257         return fullScreenExitHint.get();
1258     }
1259 
1260     public final ObjectProperty<String> fullScreenExitHintProperty() {
1261         return fullScreenExitHint;
1262     }
1263 }