/* * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.oracle.javafx.scenebuilder.kit.editor.panel.css; import com.oracle.javafx.scenebuilder.kit.fxom.FXOMObject; import com.oracle.javafx.scenebuilder.kit.util.CssInternal; import com.oracle.javafx.scenebuilder.kit.util.Deprecation; import java.util.Set; import javafx.css.CssMetaData; import javafx.css.Styleable; import javafx.css.StyleableProperty; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.control.MenuItem; import javafx.scene.control.PopupControl; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import javafx.scene.layout.Pane; /** * CSS support for Scene Builder. * * @treatAsPrivate */ public class CssUtils { private CssUtils() { assert false; } // /** // * This method is used to create a barrier between the SB and the user scene graph. // * @param parent // */ // public static Group createCSSFrontier(){ // Group grp = Deprecation.createGroupWithNullParentStylesheets(); // /* // * Doing so, when caspian lookup resolution occurs for a Node D&D from a SB user, // * if there is no redefinition in the Author space, // * then this container is reached and the caspian lookups are resolved. // */ // grp.getStyleClass().add("root"); //NOI18N // return grp; // } // // // Reseting is the process to set the value to its initial value. // private static void resetCssProperty(N node, CssMetaData p) { // try { // Object val = getResetValue(node, p); // @SuppressWarnings("unchecked") //NOI18N // CssMetaData sp = (CssMetaData) p; // if (sp.isSettable(node)) { // sp.getStyleableProperty(node).applyStyle(StyleOrigin.USER_AGENT, val); // } // } catch (RuntimeException ex) { // Utils.println("Can't reset property " + p.getProperty() + " on " + node.getClass() + ": " + ex.toString()); //NOI18N // } // } // // private static Object getResetValue(N node, CssMetaData sp) { // // The best value depends on the nature of the property. // // If this is a styleable only property with no bean property associated // // then we need to use the initialValue. // // If there is a Prop, then use it as the reset value. // // See DTL-4049 and DTL-4087 // StyleableProperty property = sp.getStyleableProperty(node); // Object value = sp.getInitialValue(node); // StyleOrigin orig = property.getStyleOrigin(); // if (StyleOrigin.AUTHOR != orig && StyleOrigin.INLINE != orig) { // // Do we have a Prop? // String name = ((ReadOnlyProperty) property).getName(); // try { // Prop.of(node, name); // value = property.getValue(); // } catch (IllegalArgumentException ex) { // //OK, no prop, need initial value. // } // } // return value; // } // // /** // * Will try first to put the property in a state that makes it OK // * to receive USER AGENT styling. // * If this is not possible, will call prop.set(bean, value); // * @param bean // * @param prop // * @param value // */ // public static void setProperty(Object bean, Prop prop, Object value) { // if (bean != null) { // boolean set = false; // try { // ObservableValue beanProp = prop.model(bean); // if (beanProp instanceof StyleableProperty) { // Styleable styleable = getStyleable(bean); // if(styleable != null){ // Node stylableNode = Deprecation.getNode(styleable); // if (stylableNode == null) { // stylableNode = getNode(bean); // } // @SuppressWarnings("unchecked") //NOI18N // CssMetaData sp = (CssMetaData) ((StyleableProperty)beanProp).getCssMetaData(); // if (sp != null && stylableNode != null) { // if (sp.isSettable(stylableNode)) { // sp.getStyleableProperty(stylableNode).applyStyle(StyleOrigin.USER_AGENT, value); // } // set = true; // } // } // } // } catch (RuntimeException ex) { // Utils.println("can't set Bean property " + prop.name + " on " + bean.getClass() + " :" + ex.toString()); //NOI18N // } // // if(!set){ // prop.set(bean, value); // } // } // } static String getBeanPropertyName(Node node, CssMetaData sp) { String property = null; try { @SuppressWarnings("unchecked") CssMetaData raw = (CssMetaData) sp; final StyleableProperty val = raw.getStyleableProperty(node); property = CssInternal.getBeanPropertyName(val); } catch (RuntimeException ex) { System.out.println("Can't retrieve property " + ex); //NOI18N } return property; } // private static void resetSkinNode(Node node) { // for (CssMetaData p : node.getCssMetaData()) { // @SuppressWarnings("unchecked") //NOI18N // final CssMetaData sp = (CssMetaData) p; // resetCssProperty(node, sp); // } // } // // private static void resetSubStructure(Node n) { // // We need to skip Elements tha tcan be present in the Skin (eg: Content // // of the Tab. // if (!n.getStyleClass().isEmpty() && Element.forNode(n) == null) { // resetSkinNode(n); // } // if (n instanceof Parent) { // Parent parentNode = (Parent) n; // for (Node child : parentNode.getChildrenUnmodifiable()) { // resetSubStructure(child); // } // } // } // // Reseting the style of a Node. Any property ruled by a Bean.property // // are not set. This is handled by the SB model (TargetPropertyValue class). // static void resetStyle(Node node) throws RuntimeException { // if (node.getScene() == null) { // return; // } // // // First reset the skin // if (node instanceof Control) { // Node skinNode = getSkinNode((Control) node); // if (skinNode != null) { // // We need to deep dive into the skin to reset properties. // // The caspian ruled properties would be aumaticaly reset // // but the (eg:shape Text used in LabeledSkin) non caspian ruled ones will keep their // // styling. // resetSubStructure(skinNode); // } // } // // // Then properties that are in the control that could suppercede the // // previously set values. // @SuppressWarnings("rawtypes") // final List> lst = node.getCssMetaData(); // for(CssMetaData stp : lst){ // @SuppressWarnings("unchecked") //NOI18N // final CssMetaData st = (CssMetaData)stp; // // // Skip the skin // if(st.getProperty().equals("-fx-skin")) { //NOI18N // continue; // } // // @SuppressWarnings("unchecked") //NOI18N // StyleableProperty val = st.getStyleableProperty(node); // boolean needsReset = false; // if(val == null){ // reset property that have no Bean property. // needsReset = true; // } else { // if(val instanceof ReadOnlyProperty){ // // Do we have a Prop? // String name = ((ReadOnlyProperty)val).getName(); // try { // Prop.of(node, name); // }catch(IllegalArgumentException ex){ // //OK, no prop, need reset // needsReset = true; // } // } else {// No prop associated. // needsReset = true; // } // } // if(needsReset){ // resetCssProperty(node, st); // } // } // // // Clear the map from any collected value. // // TODO FX8: the statement below started to trigger NPE since FX8 b68, // // hence the temporary wrap in null check test. Still have to ensure // // it doesn't mask something suspicious. // if (Deprecation.getStyleMap(node) != null) { // Deprecation.getStyleMap(node).clear(); // } // } // // private static Node getSkinNode(Control control) { // Node n = null; // Skin skin = control.getSkin(); // if (skin != null) { // // can happen in unit test // n = skin.getNode(); // } // return n; // } // // public static void editCssRule(Frame frame, CssPropAuthorInfo info) { // try { // if (info.getMainUrl() == null) { // return; // } // File file = new File(info.getMainUrl().toURI()); // Utils.editCssFile(file); // } catch (Exception ex) { // frame.printWarning("messagebar.cannot.edit", ex, info); // } // } // // public static File retrieveCssFile(Frame frame, ComponentPath path, String styleclass) { // // First check in the parent chain. The nearest first. // ComponentPath truncated = path.getNearestParentNodePath(); // for(ComponentReference ref : truncated.getPath()){ // if(ref.getChildComponent() instanceof Parent){ // Parent p = (Parent) ref.getChildComponent(); // // The last element has more priority in CSS application // for (int i = p.getStylesheets().size() - 1; i >= 0; i--) { // String ss = p.getStylesheets().get(i); // try { // URL url = new URL(ss); // Set classes = getStyleClasses(url); // if (classes.contains(styleclass)) { // return new File(url.toURI()); // } // } catch (Exception ex) { // Utils.println("Exception parsing Stylesheet " + ex); //NOI18N // } // } // } // } // List styleClasses = STYLECLASSES_SETS.get(getParent(frame)); // File ret = null; // String url = null; // if (styleClasses != null) { // // The last element has more priority in CSS application // for (int i = styleClasses.size()-1; i >= 0; i--) { // StyleClasses sc = styleClasses.get(i); // if (sc.styleClasses.contains(styleclass)) { // url = sc.url; // } // } // if (url != null) { // try { // URI uri = new URI(url); // ret = new File(uri); // } catch (Exception ex) { // // XXX Issue with this URL. // frame.printWarning("messagebar.cannot.retrieve.css.file", url); // } // } // } // return ret; // } // // public static boolean isAuthorStyleClass(Frame frame, ComponentPath path, String item) { // return retrieveCssFile(frame, path, item) != null; // } // // private static class StyleClasses { // // String url; // Set styleClasses; // } // // /* // * Returns the CSS property value defined in the specified style sheet. // * Note that the method returns the first CSS property found in the style sheet : // * - no check is performed on the style class containing the property // * - if the property is defined in several style classes, the first one is returned // */ // public static ParsedValue getValueFor(Stylesheet stylesheet, String property) { // for (Rule rule : stylesheet.getRules()) { // for (Declaration decl : rule.getDeclarations()) { // if (property.equals(decl.getProperty())) { // return decl.getParsedValue(); // } // } // } // return null; // } // // public static ParsedValue getValueFor(String styleClass, String property) { // return getValueFor(STYLE_SHEET_TOOL_CSS, styleClass, property); // } // // public static final String TOOL; // static { // if (Utils.IS_MAC) { // TOOL = "tool-mac"; //NOI18N // } else if (Utils.IS_WINDOWS_XP) { // TOOL = "tool-win-xp"; //NOI18N // } else { // TOOL = "tool"; //NOI18N // } // } // // // public static final String THEME_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/SceneBuilderTheme.css"); //NOI18N // // public static final String TOOL_ROOT_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/ToolRoot.css"); //NOI18N // public static final String CONTENT_VIEW_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/ContentView.css"); //NOI18N // public static final String MESSAGE_BAR_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/MessageBar.css"); //NOI18N // public static final String LIBRARY_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/Library.css"); //NOI18N // public static final String HIERARCHY_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/Hierarchy.css"); //NOI18N // public static final String INSPECTOR_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/Inspector.css"); //NOI18N // public static final String CSS_VIEWER_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/CssViewer.css"); //NOI18N // // public static final String POPUP_CSS = Utils.getResourceURL(Frame.class, "css_stylesheets/Popup.css"); //NOI18N // // public static final String SCENE_BUILDER_THEME_STYLECLASS = "SCENE_BUILDER_THEME"; //NOI18N // private static final String SCENE_BUILDER_WIN_FONT_STYLECLASS = "SCENE_BUILDER_WIN_FONT"; //NOI18N // private static final String SCENE_BUILDER_WINXP_FONT_STYLECLASS = "SCENE_BUILDER_WINXP_FONT"; //NOI18N // // public static final String CONTENT_AREA_ID = "JFX_SB_ContentArea"; //NOI18N // private static final String ROOT_STYLECLASS = "root"; //NOI18N // private static Stylesheet STYLE_SHEET_TOOL_CSS = null; // private static final Map, Set> ALTERNATE_STYLECLASSES = new HashMap<>(); // // static Set SS_EXTENSIONS = // Collections.singleton(new FileChooser.ExtensionFilter(Utils.getI18N().getString("popup.style.sheets"), "*.css", "*.bss")); //NOI18N // // static { // try { // STYLE_SHEET_TOOL_CSS = new CssParser().parse(new URL(TOOL_ROOT_CSS)); // } catch (IOException ex) { // Utils.println("Failed to parse " + TOOL_ROOT_CSS, ex); //NOI18N // } // Set alternates = new HashSet<>(); // alternates.add("floating"); //NOI18N // ALTERNATE_STYLECLASSES.put(TabPane.class, alternates); // } // //Properties that are impacting CSS/Pages. // private static final Set CSS_IMPACT = // SetBuilder.make(). // add(PropUtils.Parent_STYLESHEETS). // add(PropUtils.Node_ID).add(PropUtils.Styleable_STYLE_CLASS).add(PropUtils.Node_STYLE). // add(PropUtils.Tab_ID).add(PropUtils.Tab_STYLE). // add(PropUtils.PopupControl_ID).add(PropUtils.PopupControl_STYLE). // add(PropUtils.Menu_ITEMS).buildUnmodifiable(); // private static final WeakIdentityHashMap> STYLECLASSES_SETS = WeakIdentityHashMap.make(); // // public static void trackNode(final Node node) { // attachMapToNode(node); // } // // public static void stopTrackingNode(Node node) { // @SuppressWarnings("rawtypes") // Map, List