1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javafx.scene.control;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Collections;
  30 import java.util.List;
  31 
  32 import javafx.application.Application;
  33 import javafx.beans.property.DoubleProperty;
  34 import javafx.beans.property.DoublePropertyBase;
  35 import javafx.beans.property.ObjectProperty;
  36 import javafx.beans.property.ObjectPropertyBase;
  37 import javafx.beans.property.StringProperty;
  38 import javafx.beans.value.WritableValue;
  39 import javafx.collections.FXCollections;
  40 import javafx.collections.ObservableList;
  41 import javafx.collections.ObservableSet;
  42 import javafx.css.CssParser;
  43 import javafx.scene.Node;
  44 import javafx.scene.Parent;
  45 import javafx.scene.Scene;
  46 import javafx.scene.layout.Pane;
  47 import javafx.stage.PopupWindow;
  48 import com.sun.javafx.application.PlatformImpl;
  49 import javafx.css.CssMetaData;
  50 import javafx.css.PseudoClass;
  51 import com.sun.javafx.css.StyleManager;
  52 import javafx.css.Styleable;
  53 import javafx.css.StyleableStringProperty;
  54 import javafx.css.converter.StringConverter;
  55 import com.sun.javafx.scene.control.Logging;
  56 import com.sun.javafx.stage.PopupWindowHelper;
  57 import javafx.css.StyleableProperty;
  58 import javafx.stage.Window;
  59 import sun.util.logging.PlatformLogger;
  60 import sun.util.logging.PlatformLogger.Level;
  61 
  62 /**
  63  * An extension of PopupWindow that allows for CSS styling.
  64  * @since JavaFX 2.0
  65  */
  66 public class PopupControl extends PopupWindow implements Skinnable, Styleable {
  67 
  68     /**
  69      * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
  70      * setMaxWidth() or setMaxHeight() methods to indicate that the preferred dimension
  71      * should be used for that max and/or min constraint.
  72      */
  73     public static final double USE_PREF_SIZE = Double.NEGATIVE_INFINITY;
  74 
  75      /**
  76       * Sentinel value which can be passed to a control's setMinWidth(), setMinHeight(),
  77       * setPrefWidth(), setPrefHeight(), setMaxWidth(), setMaxHeight() methods
  78       * to reset the control's size constraint back to it's intrinsic size returned
  79       * by computeMinWidth(), computeMinHeight(), computePrefWidth(), computePrefHeight(),
  80       * computeMaxWidth(), or computeMaxHeight().
  81       */
  82     public static final double USE_COMPUTED_SIZE = -1;
  83 
  84     static {
  85         // Ensures that the default application user agent stylesheet is loaded
  86         if (Application.getUserAgentStylesheet() == null) {
  87             PlatformImpl.setDefaultPlatformUserAgentStylesheet();
  88         }
  89     }
  90 
  91     /**
  92      * We need a special root node, except we can't replace the special
  93      * root node already in the PopupControl. So we'll set our own
  94      * special almost-root node that is a child of the root.
  95      *
  96      * This special root node is responsible for mapping the id, styleClass,
  97      * and style defined on the PopupControl such that CSS will read the
  98      * values from the PopupControl, and then apply CSS state to that
  99      * special node. The node will then be able to pass impl_cssSet calls
 100      * along, such that any subclass of PopupControl will be able to
 101      * use the Styleable properties  and we'll be able to style it from
 102      * CSS, in such a way that it participates and applies to the skin,
 103      * exactly the way that normal Skin's work for normal Controls.
 104      * @since JavaFX 2.1
 105      */
 106     protected CSSBridge bridge;
 107 
 108     /**
 109      * Create a new empty PopupControl.
 110      */
 111     public PopupControl() {
 112         super();
 113         this.bridge = new CSSBridge();
 114         setAnchorLocation(AnchorLocation.CONTENT_TOP_LEFT);
 115         PopupWindowHelper.getContent(this).add(bridge);
 116     }
 117 
 118     // TODO the fact that PopupWindow uses a group for auto-moving things
 119     // around means that the scene resize semantics don't work if the
 120     // child is a resizable. I will need to replicate those semantics
 121     // here sometime, such that if the Skin provides a resizable, it is
 122     // given to match the popup window's width & height.
 123 
 124     /**
 125      * The id of this {@code PopupControl}. This simple string identifier is useful for
 126      * finding a specific Node within the scene graph. While the id of a Node
 127      * should be unique within the scene graph, this uniqueness is not enforced.
 128      * This is analogous to the "id" attribute on an HTML element
 129      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 130      *
 131      * @defaultValue null
 132      */
 133     public final StringProperty idProperty() { return bridge.idProperty(); }
 134 
 135     /**
 136      * Sets the id of this {@code PopupControl}. This simple string identifier is useful for
 137      * finding a specific Node within the scene graph. While the id of a Node
 138      * should be unique within the scene graph, this uniqueness is not enforced.
 139      * This is analogous to the "id" attribute on an HTML element
 140      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 141      *
 142      * @param value  the id assigned to this {@code PopupControl} using the {@code setId}
 143      *         method or {@code null}, if no id has been assigned.
 144      * @defaultValue null
 145      */
 146     public final void setId(String value) { idProperty().set(value); }
 147 
 148     /**
 149      * The id of this {@code PopupControl}. This simple string identifier is useful for
 150      * finding a specific Node within the scene graph. While the id of a Node
 151      * should be unique within the scene graph, this uniqueness is not enforced.
 152      * This is analogous to the "id" attribute on an HTML element
 153      * (<a href="http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier">CSS ID Specification</a>).
 154      *
 155      * @return the id assigned to this {@code PopupControl} using the {@code setId}
 156      *         method or {@code null}, if no id has been assigned.
 157      * @defaultValue null
 158      */
 159     @Override public final String getId() { return idProperty().get(); }
 160 
 161     /**
 162      * Returns the list of String identifiers that make up the styleClass
 163      * for this PopupControl.
 164      */
 165     @Override public final ObservableList<String> getStyleClass() { return bridge.getStyleClass(); }
 166 
 167     /**
 168      * A string representation of the CSS style associated with this
 169      * specific {@code PopupControl}. This is analogous to the "style" attribute of an
 170      * HTML element. Note that, like the HTML style attribute, this
 171      * variable contains style properties and values and not the
 172      * selector portion of a style rule.
 173      * @param value The inline CSS style to use for this {@code PopupControl}.
 174      *         {@code null} is implicitly converted to an empty String.
 175      * @defaultValue empty string
 176      */
 177     public final void setStyle(String value) { styleProperty().set(value); }
 178 
 179     // TODO: javadoc copied from property for the sole purpose of providing a return tag
 180     /**
 181      * A string representation of the CSS style associated with this
 182      * specific {@code PopupControl}. This is analogous to the "style" attribute of an
 183      * HTML element. Note that, like the HTML style attribute, this
 184      * variable contains style properties and values and not the
 185      * selector portion of a style rule.
 186      * @defaultValue empty string
 187      * @return The inline CSS style associated with this {@code PopupControl}.
 188      *         If this {@code PopupControl} does not have an inline style,
 189      *         an empty String is returned.
 190      */
 191     @Override public final String getStyle() { return styleProperty().get(); }
 192     public final StringProperty styleProperty() { return bridge.styleProperty(); }
 193 
 194     /**
 195      * Skin is responsible for rendering this {@code PopupControl}. From the
 196      * perspective of the {@code PopupControl}, the {@code Skin} is a black box.
 197      * It listens and responds to changes in state in a {@code PopupControl}.
 198      * <p>
 199      * There is a one-to-one relationship between a {@code PopupControl} and its
 200      * {@code Skin}. Every {@code Skin} maintains a back reference to the
 201      * {@code PopupControl}.
 202      * <p>
 203      * A skin may be null.
 204      */
 205     @Override public final ObjectProperty<Skin<?>> skinProperty() {
 206         return skin;
 207     }
 208 
 209     @Override public final void setSkin(Skin<?> value) {
 210         skinProperty().setValue(value);
 211     }
 212 
 213     @Override public final Skin<?> getSkin() {
 214         return skinProperty().getValue();
 215     }
 216 
 217     private final ObjectProperty<Skin<?>> skin = new ObjectPropertyBase<Skin<?>>() {
 218         // We store a reference to the oldValue so that we can handle
 219         // changes in the skin properly in the case of binding. This is
 220         // only needed because invalidated() does not currently take
 221         // a reference to the old value.
 222         private Skin<?> oldValue;
 223 
 224         @Override
 225         public void set(Skin<?> v) {
 226 
 227             if (v == null
 228                     ? oldValue == null
 229                     : oldValue != null && v.getClass().equals(oldValue.getClass()))
 230                 return;
 231 
 232             super.set(v);
 233 
 234         }
 235 
 236         @Override protected void invalidated() {
 237             Skin<?> skin = get();
 238 
 239             // Collect the name of the currently installed skin class. We do this
 240             // so that subsequent updates from CSS to the same skin class will not
 241             // result in reinstalling the skin
 242             currentSkinClassName = skin == null ? null : skin.getClass().getName();
 243 
 244             // if someone calls setSkin, we need to make it look like they
 245             // called set on skinClassName in order to keep CSS from overwriting
 246             // the skin.
 247             skinClassNameProperty().set(currentSkinClassName);
 248 
 249             // Let CSS know that this property has been manually changed
 250             // Dispose of the old skin
 251             if (oldValue != null) {
 252                 oldValue.dispose();
 253             }
 254 
 255             // Get the new value, and save it off as the new oldValue
 256             oldValue = getValue();
 257 
 258             prefWidthCache = -1;
 259             prefHeightCache = -1;
 260             minWidthCache = -1;
 261             minHeightCache = -1;
 262             maxWidthCache = -1;
 263             maxHeightCache = -1;
 264             skinSizeComputed = false;
 265 
 266             final Node n = getSkinNode();
 267             if (n != null) {
 268                 bridge.getChildren().setAll(n);
 269             } else {
 270                 bridge.getChildren().clear();
 271             }
 272 
 273             // calling impl_reapplyCSS() as the styleable properties may now
 274             // be different, as we will now be able to return styleable properties
 275             // belonging to the skin. If impl_reapplyCSS() is not called, the
 276             // getCssMetaData() method is never called, so the
 277             // skin properties are never exposed.
 278             bridge.impl_reapplyCSS();
 279 
 280             // DEBUG: Log that we've changed the skin
 281             final PlatformLogger logger = Logging.getControlsLogger();
 282             if (logger.isLoggable(Level.FINEST)) {
 283                 logger.finest("Stored skin[" + getValue() + "] on " + this);
 284             }
 285         }
 286 
 287         @Override
 288         public Object getBean() {
 289             return PopupControl.this;
 290         }
 291 
 292         @Override
 293         public String getName() {
 294             return "skin";
 295         }
 296     };
 297     /**
 298      * Keeps a reference to the name of the class currently acting as the skin.
 299      */
 300     private String currentSkinClassName = null;
 301     /**
 302      * A property that acts as a proxy between the skin property and css.
 303      */
 304     private StringProperty skinClassName = null;
 305     private StringProperty skinClassNameProperty() {
 306         if (skinClassName == null) {
 307             skinClassName = new StyleableStringProperty() {
 308 
 309                 @Override
 310                 public void set(String v) {
 311                     // do not allow the skin to be set to null through CSS
 312                     if (v == null || v.isEmpty() || v.equals(get())) return;
 313                     super.set(v);
 314                 }
 315 
 316                 @Override
 317                 public void invalidated() {
 318 
 319                     //
 320                     // if the current skin is not null, then
 321                     // see if then check to see if the current skin's class name
 322                     // is the same as skinClassName. If it is, then there is
 323                     // no need to load the skin class. Note that the only time
 324                     // this would be the case is if someone called setSkin since
 325                     // the skin would be set ahead of the skinClassName
 326                     // (skinClassName is set from the skinProperty's invalidated
 327                     // method, so the skin would be set, then the skinClassName).
 328                     // If the skinClassName is set first (via CSS), then this
 329                     // invalidated method won't get called unless the value
 330                     // has changed (thus, we won't reload the same skin).
 331                     //
 332                     if (get() != null) {
 333                         if (!get().equals(currentSkinClassName)) {
 334                             Control.loadSkinClass(PopupControl.this, get());
 335                         }
 336                         // CSS should not set skin to null
 337                         //                    } else {
 338                         //                        setSkin(null);
 339                     }
 340                 }
 341 
 342                 @Override
 343                 public Object getBean() {
 344                     return PopupControl.this;
 345                 }
 346 
 347                 @Override
 348                 public String getName() {
 349                     return "skinClassName";
 350                 }
 351 
 352                 @Override
 353                 public CssMetaData<CSSBridge,String> getCssMetaData() {
 354                     return SKIN;
 355                 }
 356 
 357             };
 358         }
 359         return skinClassName;
 360     }
 361 
 362     /**
 363      * Gets the Skin's node, or returns null if there is no Skin.
 364      * Convenience method for getting the node of the skin. This is null-safe,
 365      * meaning if skin is null then it will return null instead of throwing
 366      * a NullPointerException.
 367      *
 368      * @return The Skin's node, or null.
 369      */
 370     private Node getSkinNode() {
 371         return getSkin() == null ? null : getSkin().getNode();
 372     }
 373 
 374     /**
 375      * Property for overriding the control's computed minimum width.
 376      * This should only be set if the control's internally computed minimum width
 377      * doesn't meet the application's layout needs.
 378      * <p>
 379      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 380      * <code>getMinWidth(forHeight)</code> will return the control's internally
 381      * computed minimum width.
 382      * <p>
 383      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 384      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 385      * enabling applications to easily restrict the resizability of the control.
 386      */
 387     private DoubleProperty minWidth;
 388 
 389     /**
 390      * Property for overriding the control's computed minimum width.
 391      * This should only be set if the control's internally computed minimum width
 392      * doesn't meet the application's layout needs.
 393      * <p>
 394      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 395      * <code>getMinWidth(forHeight)</code> will return the control's internally
 396      * computed minimum width.
 397      * <p>
 398      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 399      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 400      * enabling applications to easily restrict the resizability of the control.
 401      */
 402     public final void setMinWidth(double value) { minWidthProperty().set(value); }
 403 
 404     /**
 405      * Property for overriding the control's computed minimum width.
 406      * This should only be set if the control's internally computed minimum width
 407      * doesn't meet the application's layout needs.
 408      * <p>
 409      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 410      * <code>getMinWidth(forHeight)</code> will return the control's internally
 411      * computed minimum width.
 412      * <p>
 413      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 414      * <code>getMinWidth(forHeight)</code> to return the control's preferred width,
 415      * enabling applications to easily restrict the resizability of the control.
 416      */
 417     public final double getMinWidth() { return minWidth == null ? USE_COMPUTED_SIZE : minWidth.get(); }
 418     public final DoubleProperty minWidthProperty() {
 419         if (minWidth == null) {
 420             minWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 421                 @Override public void invalidated() {
 422                     if (isShowing()) bridge.requestLayout();
 423                 }
 424 
 425                 @Override
 426                 public Object getBean() {
 427                     return PopupControl.this;
 428                 }
 429 
 430                 @Override
 431                 public String getName() {
 432                     return "minWidth";
 433                 }
 434             };
 435         }
 436         return minWidth;
 437     }
 438 
 439 
 440     /**
 441      * Property for overriding the control's computed minimum height.
 442      * This should only be set if the control's internally computed minimum height
 443      * doesn't meet the application's layout needs.
 444      * <p>
 445      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 446      * <code>getMinHeight(forWidth)</code> will return the control's internally
 447      * computed minimum height.
 448      * <p>
 449      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 450      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 451      * enabling applications to easily restrict the resizability of the control.
 452      *
 453      */
 454     private DoubleProperty minHeight;
 455 
 456     /**
 457      * Property for overriding the control's computed minimum height.
 458      * This should only be set if the control's internally computed minimum height
 459      * doesn't meet the application's layout needs.
 460      * <p>
 461      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 462      * <code>getMinHeight(forWidth)</code> will return the control's internally
 463      * computed minimum height.
 464      * <p>
 465      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 466      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 467      * enabling applications to easily restrict the resizability of the control.
 468      *
 469      */
 470     public final void setMinHeight(double value) { minHeightProperty().set(value); }
 471 
 472     /**
 473      * Property for overriding the control's computed minimum height.
 474      * This should only be set if the control's internally computed minimum height
 475      * doesn't meet the application's layout needs.
 476      * <p>
 477      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 478      * <code>getMinHeight(forWidth)</code> will return the control's internally
 479      * computed minimum height.
 480      * <p>
 481      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 482      * <code>getMinHeight(forWidth)</code> to return the control's preferred height,
 483      * enabling applications to easily restrict the resizability of the control.
 484      *
 485      */
 486     public final double getMinHeight() { return minHeight == null ? USE_COMPUTED_SIZE : minHeight.get(); }
 487     public final DoubleProperty minHeightProperty() {
 488         if (minHeight == null) {
 489             minHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 490                 @Override public void invalidated() {
 491                     if (isShowing()) bridge.requestLayout();
 492                 }
 493 
 494                 @Override
 495                 public Object getBean() {
 496                     return PopupControl.this;
 497                 }
 498 
 499                 @Override
 500                 public String getName() {
 501                     return "minHeight";
 502                 }
 503             };
 504         }
 505         return minHeight;
 506     }
 507 
 508     /**
 509      * Convenience method for overriding the control's computed minimum width and height.
 510      * This should only be called if the control's internally computed minimum size
 511      * doesn't meet the application's layout needs.
 512      *
 513      * @see #setMinWidth
 514      * @see #setMinHeight
 515      * @param minWidth  the override value for minimum width
 516      * @param minHeight the override value for minimum height
 517      */
 518     public void setMinSize(double minWidth, double minHeight) {
 519         setMinWidth(minWidth);
 520         setMinHeight(minHeight);
 521     }
 522 
 523     /**
 524      * Property for overriding the control's computed preferred width.
 525      * This should only be set if the control's internally computed preferred width
 526      * doesn't meet the application's layout needs.
 527      * <p>
 528      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 529      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 530      * computed preferred width.
 531      */
 532     private DoubleProperty prefWidth;
 533 
 534     /**
 535      * Property for overriding the control's computed preferred width.
 536      * This should only be set if the control's internally computed preferred width
 537      * doesn't meet the application's layout needs.
 538      * <p>
 539      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 540      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 541      * computed preferred width.
 542      */
 543     public final void setPrefWidth(double value) { prefWidthProperty().set(value); }
 544 
 545     /**
 546      * Property for overriding the control's computed preferred width.
 547      * This should only be set if the control's internally computed preferred width
 548      * doesn't meet the application's layout needs.
 549      * <p>
 550      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 551      * <code>getPrefWidth(forHeight)</code> will return the control's internally
 552      * computed preferred width.
 553      */
 554     public final double getPrefWidth() { return prefWidth == null ? USE_COMPUTED_SIZE : prefWidth.get(); }
 555     public final DoubleProperty prefWidthProperty() {
 556         if (prefWidth == null) {
 557             prefWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 558                 @Override public void invalidated() {
 559                     if (isShowing()) bridge.requestLayout();
 560                 }
 561 
 562                 @Override
 563                 public Object getBean() {
 564                     return PopupControl.this;
 565                 }
 566 
 567                 @Override
 568                 public String getName() {
 569                     return "prefWidth";
 570                 }
 571             };
 572         }
 573         return prefWidth;
 574     }
 575 
 576     /**
 577      * Property for overriding the control's computed preferred height.
 578      * This should only be set if the control's internally computed preferred height
 579      * doesn't meet the application's layout needs.
 580      * <p>
 581      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 582      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 583      * computed preferred width.
 584      *
 585      */
 586     private DoubleProperty prefHeight;
 587 
 588     /**
 589      * Property for overriding the control's computed preferred height.
 590      * This should only be set if the control's internally computed preferred height
 591      * doesn't meet the application's layout needs.
 592      * <p>
 593      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 594      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 595      * computed preferred width.
 596      *
 597      */
 598     public final void setPrefHeight(double value) { prefHeightProperty().set(value); }
 599 
 600     /**
 601      * Property for overriding the control's computed preferred height.
 602      * This should only be set if the control's internally computed preferred height
 603      * doesn't meet the application's layout needs.
 604      * <p>
 605      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 606      * <code>getPrefHeight(forWidth)</code> will return the control's internally
 607      * computed preferred width.
 608      *
 609      */
 610     public final double getPrefHeight() { return prefHeight == null ? USE_COMPUTED_SIZE : prefHeight.get(); }
 611     public final DoubleProperty prefHeightProperty() {
 612         if (prefHeight == null) {
 613             prefHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 614                 @Override public void invalidated() {
 615                     if (isShowing()) bridge.requestLayout();
 616                 }
 617 
 618                 @Override
 619                 public Object getBean() {
 620                     return PopupControl.this;
 621                 }
 622 
 623                 @Override
 624                 public String getName() {
 625                     return "prefHeight";
 626                 }
 627             };
 628         }
 629         return prefHeight;
 630     }
 631 
 632     /**
 633      * Convenience method for overriding the control's computed preferred width and height.
 634      * This should only be called if the control's internally computed preferred size
 635      * doesn't meet the application's layout needs.
 636      *
 637      * @see #setPrefWidth
 638      * @see #setPrefHeight
 639      * @param prefWidth the override value for preferred width
 640      * @param prefHeight the override value for preferred height
 641      */
 642     public void setPrefSize(double prefWidth, double prefHeight) {
 643         setPrefWidth(prefWidth);
 644         setPrefHeight(prefHeight);
 645     }
 646 
 647     /**
 648      * Property for overriding the control's computed maximum width.
 649      * This should only be set if the control's internally computed maximum width
 650      * doesn't meet the application's layout needs.
 651      * <p>
 652      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 653      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 654      * computed maximum width.
 655      * <p>
 656      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 657      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 658      * enabling applications to easily restrict the resizability of the control.
 659      */
 660     private DoubleProperty maxWidth;
 661 
 662     /**
 663      * Property for overriding the control's computed maximum width.
 664      * This should only be set if the control's internally computed maximum width
 665      * doesn't meet the application's layout needs.
 666      * <p>
 667      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 668      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 669      * computed maximum width.
 670      * <p>
 671      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 672      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 673      * enabling applications to easily restrict the resizability of the control.
 674      */
 675     public final void setMaxWidth(double value) { maxWidthProperty().set(value); }
 676 
 677     /**
 678      * Property for overriding the control's computed maximum width.
 679      * This should only be set if the control's internally computed maximum width
 680      * doesn't meet the application's layout needs.
 681      * <p>
 682      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 683      * <code>getMaxWidth(forHeight)</code> will return the control's internally
 684      * computed maximum width.
 685      * <p>
 686      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 687      * <code>getMaxWidth(forHeight)</code> to return the control's preferred width,
 688      * enabling applications to easily restrict the resizability of the control.
 689      */
 690     public final double getMaxWidth() { return maxWidth == null ? USE_COMPUTED_SIZE : maxWidth.get(); }
 691     public final DoubleProperty maxWidthProperty() {
 692         if (maxWidth == null) {
 693             maxWidth = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 694                 @Override public void invalidated() {
 695                     if (isShowing()) bridge.requestLayout();
 696                 }
 697 
 698                 @Override
 699                 public Object getBean() {
 700                     return PopupControl.this;
 701                 }
 702 
 703                 @Override
 704                 public String getName() {
 705                     return "maxWidth";
 706                 }
 707             };
 708         }
 709         return maxWidth;
 710     }
 711 
 712     /**
 713      * Property for overriding the control's computed maximum height.
 714      * This should only be set if the control's internally computed maximum height
 715      * doesn't meet the application's layout needs.
 716      * <p>
 717      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 718      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 719      * computed maximum height.
 720      * <p>
 721      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 722      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 723      * enabling applications to easily restrict the resizability of the control.
 724      *
 725      */
 726     private DoubleProperty maxHeight;
 727 
 728     /**
 729      * Property for overriding the control's computed maximum height.
 730      * This should only be set if the control's internally computed maximum height
 731      * doesn't meet the application's layout needs.
 732      * <p>
 733      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 734      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 735      * computed maximum height.
 736      * <p>
 737      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 738      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 739      * enabling applications to easily restrict the resizability of the control.
 740      *
 741      */
 742     public final void setMaxHeight(double value) { maxHeightProperty().set(value); }
 743 
 744     /**
 745      * Property for overriding the control's computed maximum height.
 746      * This should only be set if the control's internally computed maximum height
 747      * doesn't meet the application's layout needs.
 748      * <p>
 749      * Defaults to the <code>USE_COMPUTED_SIZE</code> flag, which means that
 750      * <code>getMaxHeight(forWidth)</code> will return the control's internally
 751      * computed maximum height.
 752      * <p>
 753      * Setting this value to the <code>USE_PREF_SIZE</code> flag will cause
 754      * <code>getMaxHeight(forWidth)</code> to return the control's preferred height,
 755      * enabling applications to easily restrict the resizability of the control.
 756      *
 757      */
 758     public final double getMaxHeight() { return maxHeight == null ? USE_COMPUTED_SIZE : maxHeight.get(); }
 759     public final DoubleProperty maxHeightProperty() {
 760         if (maxHeight == null) {
 761             maxHeight = new DoublePropertyBase(USE_COMPUTED_SIZE) {
 762                 @Override public void invalidated() {
 763                     if (isShowing()) bridge.requestLayout();
 764                 }
 765 
 766                 @Override
 767                 public Object getBean() {
 768                     return PopupControl.this;
 769                 }
 770 
 771                 @Override
 772                 public String getName() {
 773                     return "maxHeight";
 774                 }
 775             };
 776         }
 777         return maxHeight;
 778     }
 779 
 780     /**
 781      * Convenience method for overriding the control's computed maximum width and height.
 782      * This should only be called if the control's internally computed maximum size
 783      * doesn't meet the application's layout needs.
 784      *
 785      * @see #setMaxWidth
 786      * @see #setMaxHeight
 787      * @param maxWidth  the override value for maximum width
 788      * @param maxHeight the override value for maximum height
 789      */
 790     public void setMaxSize(double maxWidth, double maxHeight) {
 791         setMaxWidth(maxWidth);
 792         setMaxHeight(maxHeight);
 793     }
 794 
 795     /**
 796      * Cached prefWidth, prefHeight, minWidth, minHeight. These
 797      * results are repeatedly sought during the layout pass,
 798      * and caching the results leads to a significant decrease
 799      * in overhead.
 800      */
 801     private double prefWidthCache = -1;
 802     private double prefHeightCache = -1;
 803     private double minWidthCache = -1;
 804     private double minHeightCache = -1;
 805     private double maxWidthCache = -1;
 806     private double maxHeightCache = -1;
 807     private boolean skinSizeComputed = false;
 808 
 809     /**
 810      * Called during layout to determine the minimum width for this node.
 811      * Returns the value from <code>minWidth(forHeight)</code> unless
 812      * the application overrode the minimum width by setting the minWidth property.
 813      *
 814      * @param height the height
 815      * @see #setMinWidth
 816      * @return the minimum width that this node should be resized to during layout
 817      */
 818     public final double minWidth(double height) {
 819         double override = getMinWidth();
 820         if (override == USE_COMPUTED_SIZE) {
 821             if (minWidthCache == -1) minWidthCache = recalculateMinWidth(height);
 822             return minWidthCache;
 823         } else if (override == USE_PREF_SIZE) {
 824             return prefWidth(height);
 825         }
 826         return override;
 827     }
 828 
 829     /**
 830      * Called during layout to determine the minimum height for this node.
 831      * Returns the value from <code>minHeight(forWidth)</code> unless
 832      * the application overrode the minimum height by setting the minHeight property.
 833      *
 834      * @param width The width
 835      * @see #setMinHeight
 836      * @return the minimum height that this node should be resized to during layout
 837      */
 838     public final double minHeight(double width) {
 839         double override = getMinHeight();
 840         if (override == USE_COMPUTED_SIZE) {
 841             if (minHeightCache == -1) minHeightCache = recalculateMinHeight(width);
 842             return minHeightCache;
 843         } else if (override == USE_PREF_SIZE) {
 844             return prefHeight(width);
 845         }
 846         return override;
 847     }
 848 
 849 
 850     /**
 851      * Called during layout to determine the preferred width for this node.
 852      * Returns the value from <code>prefWidth(forHeight)</code> unless
 853      * the application overrode the preferred width by setting the prefWidth property.
 854      *
 855      * @param height the height
 856      * @see #setPrefWidth
 857      * @return the preferred width that this node should be resized to during layout
 858      */
 859     public final double prefWidth(double height) {
 860         double override = getPrefWidth();
 861         if (override == USE_COMPUTED_SIZE) {
 862             if (prefWidthCache == -1) prefWidthCache = recalculatePrefWidth(height);
 863             return prefWidthCache;
 864         } else if (override == USE_PREF_SIZE) {
 865             return prefWidth(height);
 866         }
 867         return override;
 868     }
 869 
 870     /**
 871      * Called during layout to determine the preferred height for this node.
 872      * Returns the value from <code>prefHeight(forWidth)</code> unless
 873      * the application overrode the preferred height by setting the prefHeight property.
 874      *
 875      * @param width the width
 876      * @see #setPrefHeight
 877      * @return the preferred height that this node should be resized to during layout
 878      */
 879     public final double prefHeight(double width) {
 880         double override = getPrefHeight();
 881         if (override == USE_COMPUTED_SIZE) {
 882             if (prefHeightCache == -1) prefHeightCache = recalculatePrefHeight(width);
 883             return prefHeightCache;
 884         } else if (override == USE_PREF_SIZE) {
 885             return prefHeight(width);
 886         }
 887         return override;
 888     }
 889 
 890     /**
 891      * Called during layout to determine the maximum width for this node.
 892      * Returns the value from <code>maxWidth(forHeight)</code> unless
 893      * the application overrode the maximum width by setting the maxWidth property.
 894      *
 895      * @param height the height
 896      * @see #setMaxWidth
 897      * @return the maximum width that this node should be resized to during layout
 898      */
 899     public final double maxWidth(double height) {
 900         double override = getMaxWidth();
 901         if (override == USE_COMPUTED_SIZE) {
 902             if (maxWidthCache == -1) maxWidthCache = recalculateMaxWidth(height);
 903             return maxWidthCache;
 904         } else if (override == USE_PREF_SIZE) {
 905             return prefWidth(height);
 906         }
 907         return override;
 908     }
 909 
 910     /**
 911      * Called during layout to determine the maximum height for this node.
 912      * Returns the value from <code>maxHeight(forWidth)</code> unless
 913      * the application overrode the maximum height by setting the maxHeight property.
 914      *
 915      * @param width the width
 916      * @see #setMaxHeight
 917      * @return the maximum height that this node should be resized to during layout
 918      */
 919     public final double maxHeight(double width) {
 920         double override = getMaxHeight();
 921         if (override == USE_COMPUTED_SIZE) {
 922             if (maxHeightCache == -1) maxHeightCache = recalculateMaxHeight(width);
 923             return maxHeightCache;
 924         } else if (override == USE_PREF_SIZE) {
 925             return prefHeight(width);
 926         }
 927         return override;
 928     }
 929 
 930     // Implementation of the Resizable interface.
 931     // Because only the skin can know the min, pref, and max sizes, these
 932     // functions are implemented to delegate to skin. If there is no skin then
 933     // we simply return 0 for all the values since a Control without a Skin
 934     // doesn't render
 935     private double recalculateMinWidth(double height) {
 936         recomputeSkinSize();
 937         return getSkinNode() == null ? 0 : getSkinNode().minWidth(height);
 938     }
 939     private double recalculateMinHeight(double width) {
 940         recomputeSkinSize();
 941         return getSkinNode() == null ? 0 : getSkinNode().minHeight(width);
 942     }
 943     private double recalculateMaxWidth(double height) {
 944         recomputeSkinSize();
 945         return getSkinNode() == null ? 0 : getSkinNode().maxWidth(height);
 946     }
 947     private double recalculateMaxHeight(double width) {
 948         recomputeSkinSize();
 949         return getSkinNode() == null ? 0 : getSkinNode().maxHeight(width);
 950     }
 951     private double recalculatePrefWidth(double height) {
 952         recomputeSkinSize();
 953         return getSkinNode() == null? 0 : getSkinNode().prefWidth(height);
 954     }
 955     private double recalculatePrefHeight(double width) {
 956         recomputeSkinSize();
 957         return getSkinNode() == null? 0 : getSkinNode().prefHeight(width);
 958     }
 959 
 960     private void recomputeSkinSize() {
 961         if (!skinSizeComputed) {
 962             // RT-14094, RT-16754: We need the skins of the popup
 963             // and it children before the stage is visible so we
 964             // can calculate the popup position based on content
 965             // size.
 966             bridge.applyCss();
 967             skinSizeComputed = true;
 968         }
 969     }
 970 //    public double getBaselineOffset() { return getSkinNode() == null? 0 : getSkinNode().getBaselineOffset(); }
 971 
 972     /**
 973      * Create a new instance of the default skin for this control. This is called to create a skin for the control if
 974      * no skin is provided via CSS {@code -fx-skin} or set explicitly in a sub-class with {@code  setSkin(...)}.
 975      *
 976      * @return  new instance of default skin for this control. If null then the control will have no skin unless one
 977      *          is provided by css.
 978      * @since JavaFX 8.0
 979      */
 980     protected Skin<?> createDefaultSkin() {
 981         return null;
 982     }
 983 
 984     /***************************************************************************
 985      *                                                                         *
 986      *                         StyleSheet Handling                             *
 987      *                                                                         *
 988      **************************************************************************/
 989 
 990     private static final CssMetaData<CSSBridge,String> SKIN =
 991             new CssMetaData<CSSBridge,String>("-fx-skin",
 992                     StringConverter.getInstance()) {
 993 
 994                 @Override
 995                 public boolean isSettable(CSSBridge cssBridge) {
 996                     return !cssBridge.popupControl.skinProperty().isBound();
 997                 }
 998 
 999                 @Override
1000                 public StyleableProperty<String> getStyleableProperty(CSSBridge cssBridge) {
1001                     return (StyleableProperty<String>)(WritableValue<String>)cssBridge.popupControl.skinClassNameProperty();
1002                 }
1003             };
1004 
1005     private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
1006     static {
1007         final List<CssMetaData<? extends Styleable, ?>> styleables =
1008                 new ArrayList<CssMetaData<? extends Styleable, ?>>();
1009         Collections.addAll(styleables,
1010                 SKIN
1011         );
1012         STYLEABLES = Collections.unmodifiableList(styleables);
1013     }
1014 
1015     /**
1016      * @return The CssMetaData associated with this class, which may include the
1017      * CssMetaData of its super classes.
1018      * @since JavaFX 8.0
1019      */
1020     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
1021         return STYLEABLES;
1022     }
1023 
1024     /**
1025      * {@inheritDoc}
1026      * @since JavaFX 8.0
1027      */
1028     @Override
1029     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
1030         return getClassCssMetaData();
1031     }
1032 
1033     /**
1034      * @see Node#pseudoClassStateChanged(javafx.css.PseudoClass, boolean)
1035      * @since JavaFX 8.0
1036      */
1037     public final void pseudoClassStateChanged(PseudoClass pseudoClass, boolean active) {
1038         bridge.pseudoClassStateChanged(pseudoClass, active);
1039     }
1040 
1041     /**
1042      * {@inheritDoc}
1043      * @return "PopupControl"
1044      * @since JavaFX 8.0
1045      */
1046     @Override
1047     public String getTypeSelector() {
1048         return "PopupControl";
1049     }
1050 
1051     /**
1052      * {@inheritDoc}
1053      *
1054      * A PopupControl&apos;s styles are based on the popup &quot;owner&quot; which is the
1055      * {@link javafx.stage.PopupWindow#getOwnerNode() ownerNode} or,
1056      * if the ownerNode is not set, the root of the {@link javafx.stage.PopupWindow#getOwnerWindow() ownerWindow&apos;s}
1057      * scene. If the popup has not been shown, both ownerNode and ownerWindow will be null and {@code null} will be returned.
1058      *
1059      * Note that the PopupWindow&apos;s scene root is not returned because there is no way to guarantee that the
1060      * PopupWindow&apos;s scene root would properly return the ownerNode or ownerWindow.
1061      *
1062      * @return {@link javafx.stage.PopupWindow#getOwnerNode()}, {@link javafx.stage.PopupWindow#getOwnerWindow()},
1063      * or null.
1064      * @since JavaFX 8.0
1065      */
1066     @Override
1067     public Styleable getStyleableParent() {
1068 
1069         final Node ownerNode = getOwnerNode();
1070         if (ownerNode != null) {
1071             return ownerNode;
1072 
1073         } else {
1074 
1075             final Window ownerWindow = getOwnerWindow();
1076             if (ownerWindow != null) {
1077 
1078                 final Scene ownerScene = ownerWindow.getScene();
1079                 if (ownerScene != null) {
1080                     return ownerScene.getRoot();
1081                 }
1082             }
1083         }
1084 
1085         return bridge.getParent();
1086 
1087     }
1088 
1089     /**
1090      * {@inheritDoc}
1091      * @since JavaFX 8.0
1092      */
1093     public final ObservableSet<PseudoClass> getPseudoClassStates() {
1094         return FXCollections.emptyObservableSet();
1095     }
1096 
1097     /** {@inheritDoc} */
1098     @Override public Node getStyleableNode() {
1099         return bridge;
1100     }
1101 
1102     /**
1103      * The link between the popup window and the scenegraph.
1104      *
1105      * @since JavaFX 2.1
1106      */
1107     protected class CSSBridge extends Pane {
1108 
1109         private final PopupControl popupControl = PopupControl.this;
1110 
1111         /**
1112          * Requests a layout pass to be performed before the next scene is
1113          * rendered. This is batched up asynchronously to happen once per
1114          * "pulse", or frame of animation.
1115          * <p/>
1116          * If this parent is either a layout root or unmanaged, then it will be
1117          * added directly to the scene's dirty layout list, otherwise requestLayout
1118          * will be invoked on its parent.
1119          */
1120         @Override public void requestLayout() {
1121             prefWidthCache = -1;
1122             prefHeightCache = -1;
1123             minWidthCache = -1;
1124             minHeightCache = -1;
1125             maxWidthCache = -1;
1126             maxHeightCache = -1;
1127             //skinSizeComputed = false; -- RT-33073 disabled this
1128             super.requestLayout();
1129         }
1130 
1131         /**
1132          * This method should be treated as final and should not be overridden by any subclasses of CSSBridge.
1133          */
1134         @Override
1135         public Styleable getStyleableParent() {
1136             return PopupControl.this.getStyleableParent();
1137         }
1138 
1139         /**
1140          * @treatAsPrivate implementation detail
1141          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1142          */
1143         @Deprecated
1144         protected void setSkinClassName(String skinClassName) { /* no-op - retain for binary compatibility */ }
1145 
1146         @Override
1147         public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
1148             return PopupControl.this.getCssMetaData();
1149         }
1150 
1151         /**
1152          * @treatAsPrivate implementation detail
1153          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1154          */
1155         @Deprecated
1156         @Override public List<String> impl_getAllParentStylesheets() {
1157             Styleable styleable = getStyleableParent();
1158             if (styleable instanceof Parent) {
1159                 return ((Parent)styleable).impl_getAllParentStylesheets();
1160             }
1161             return null;
1162         }
1163 
1164         /**
1165          * @treatAsPrivate implementation detail
1166          * @deprecated This is an internal API that is not intended for use and will be removed in the next version
1167          */
1168         @Deprecated
1169         @Override protected void impl_processCSS() {
1170             super.impl_processCSS();
1171 
1172             if (getSkin() == null) {
1173                 // try to create default skin
1174                 final Skin<?> defaultSkin = createDefaultSkin();
1175                 if (defaultSkin != null) {
1176                     skinProperty().set(defaultSkin);
1177                     super.impl_processCSS();
1178                 } else {
1179                     final String msg = "The -fx-skin property has not been defined in CSS for " + this +
1180                             " and createDefaultSkin() returned null.";
1181                     final List<CssParser.ParseError> errors = StyleManager.getErrors();
1182                     if (errors != null) {
1183                         CssParser.ParseError error = new CssParser.ParseError(msg);
1184                         errors.add(error); // RT-19884
1185                     }
1186                     Logging.getControlsLogger().severe(msg);
1187                 }
1188             }
1189         }
1190 
1191     }
1192 
1193 }