changeset 1710:36010eb79a4f

Automated merge with ssh://jfxsrc.us.oracle.com//javafx/8.0/MASTER/rt
author leifs
date Tue, 04 Sep 2012 13:52:16 -0700
parents aafe86217491 722aeb6b5389
children ce8b04d29bba d735007b5b51
files
diffstat 11 files changed, 375 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-concurrent/src/javafx/concurrent/Service.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-concurrent/src/javafx/concurrent/Service.java	Tue Sep 04 13:52:16 2012 -0700
@@ -158,39 +158,39 @@
         EXECUTOR.allowCoreThreadTimeOut(true);
     }
 
-    private ObjectProperty<State> state = new SimpleObjectProperty<State>(this, "state", State.READY);
+    private final ObjectProperty<State> state = new SimpleObjectProperty<State>(this, "state", State.READY);
     @Override public final State getState() { checkThread(); return state.get(); }
     @Override public final ReadOnlyObjectProperty<State> stateProperty() { checkThread(); return state; }
 
-    private ObjectProperty<V> value = new SimpleObjectProperty<V>(this, "value");
+    private final ObjectProperty<V> value = new SimpleObjectProperty<V>(this, "value");
     @Override public final V getValue() { checkThread(); return value.get(); }
     @Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value; }
 
-    private ObjectProperty<Throwable> exception = new SimpleObjectProperty<Throwable>(this, "exception");
+    private final ObjectProperty<Throwable> exception = new SimpleObjectProperty<Throwable>(this, "exception");
     @Override public final Throwable getException() { checkThread(); return exception.get(); }
     @Override public final ReadOnlyObjectProperty<Throwable> exceptionProperty() { checkThread(); return exception; }
 
-    private DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1);
+    private final DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1);
     @Override public final double getWorkDone() { checkThread(); return workDone.get(); }
     @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone; }
 
-    private DoubleProperty totalWorkToBeDone = new SimpleDoubleProperty(this, "totalWork", -1);
+    private final DoubleProperty totalWorkToBeDone = new SimpleDoubleProperty(this, "totalWork", -1);
     @Override public final double getTotalWork() { checkThread(); return totalWorkToBeDone.get(); }
     @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWorkToBeDone; }
 
-    private DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1);
+    private final DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1);
     @Override public final double getProgress() { checkThread(); return progress.get(); }
     @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress; }
 
-    private BooleanProperty running = new SimpleBooleanProperty(this, "running", false);
+    private final BooleanProperty running = new SimpleBooleanProperty(this, "running", false);
     @Override public final boolean isRunning() { checkThread(); return running.get(); }
     @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running; }
 
-    private StringProperty message = new SimpleStringProperty(this, "message", "");
+    private final StringProperty message = new SimpleStringProperty(this, "message", "");
     @Override public final String getMessage() { return message.get(); }
     @Override public final ReadOnlyStringProperty messageProperty() { return message; }
 
-    private StringProperty title = new SimpleStringProperty(this, "title", "");
+    private final StringProperty title = new SimpleStringProperty(this, "title", "");
     @Override public final String getTitle() { return title.get(); }
     @Override public final ReadOnlyStringProperty titleProperty() { return title; }
 
@@ -199,7 +199,7 @@
      * a new daemon thread will be created and used for running the Service using some
      * default executor.
      */
-    private ObjectProperty<Executor> executor = new SimpleObjectProperty<Executor>(this, "executor");
+    private final ObjectProperty<Executor> executor = new SimpleObjectProperty<Executor>(this, "executor");
     public final void setExecutor(Executor value) { checkThread(); executor.set(value); }
     public final Executor getExecutor() { checkThread(); return executor.get(); }
     public final ObjectProperty<Executor> executorProperty() { return executor; }
--- a/javafx-concurrent/src/javafx/concurrent/Task.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-concurrent/src/javafx/concurrent/Task.java	Tue Sep 04 13:52:16 2012 -0700
@@ -840,41 +840,41 @@
      */
     protected void failed() { }
 
-    private ObjectProperty<V> value = new SimpleObjectProperty<V>(this, "value");
+    private final ObjectProperty<V> value = new SimpleObjectProperty<V>(this, "value");
     private void setValue(V v) { checkThread(); value.set(v); }
     @Override public final V getValue() { checkThread(); return value.get(); }
     @Override public final ReadOnlyObjectProperty<V> valueProperty() { checkThread(); return value; }
 
-    private ObjectProperty<Throwable> exception = new SimpleObjectProperty<Throwable>(this, "exception");
+    private final ObjectProperty<Throwable> exception = new SimpleObjectProperty<Throwable>(this, "exception");
     private void _setException(Throwable value) { checkThread(); exception.set(value); }
     @Override public final Throwable getException() { checkThread(); return exception.get(); }
     @Override public final ReadOnlyObjectProperty<Throwable> exceptionProperty() { checkThread(); return exception; }
 
-    private DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1);
+    private final DoubleProperty workDone = new SimpleDoubleProperty(this, "workDone", -1);
     private void setWorkDone(double value) { checkThread(); workDone.set(value); }
     @Override public final double getWorkDone() { checkThread(); return workDone.get(); }
     @Override public final ReadOnlyDoubleProperty workDoneProperty() { checkThread(); return workDone; }
 
-    private DoubleProperty totalWork = new SimpleDoubleProperty(this, "totalWork", -1);
+    private final DoubleProperty totalWork = new SimpleDoubleProperty(this, "totalWork", -1);
     private void setTotalWork(double value) { checkThread(); totalWork.set(value); }
     @Override public final double getTotalWork() { checkThread(); return totalWork.get(); }
     @Override public final ReadOnlyDoubleProperty totalWorkProperty() { checkThread(); return totalWork; }
 
-    private DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1);
+    private final DoubleProperty progress = new SimpleDoubleProperty(this, "progress", -1);
     private void setProgress(double value) { checkThread(); progress.set(value); }
     @Override public final double getProgress() { checkThread(); return progress.get(); }
     @Override public final ReadOnlyDoubleProperty progressProperty() { checkThread(); return progress; }
 
-    private BooleanProperty running = new SimpleBooleanProperty(this, "running", false);
+    private final BooleanProperty running = new SimpleBooleanProperty(this, "running", false);
     private void setRunning(boolean value) { checkThread(); running.set(value); }
     @Override public final boolean isRunning() { checkThread(); return running.get(); }
     @Override public final ReadOnlyBooleanProperty runningProperty() { checkThread(); return running; }
 
-    private StringProperty message = new SimpleStringProperty(this, "message", "");
+    private final StringProperty message = new SimpleStringProperty(this, "message", "");
     @Override public final String getMessage() { return message.get(); }
     @Override public final ReadOnlyStringProperty messageProperty() { return message; }
 
-    private StringProperty title = new SimpleStringProperty(this, "title", "");
+    private final StringProperty title = new SimpleStringProperty(this, "title", "");
     @Override public final String getTitle() { return title.get(); }
     @Override public final ReadOnlyStringProperty titleProperty() { return title; }
 
@@ -1205,8 +1205,8 @@
      * atomically. If it was not null, then we simply update it.
      */
     private static final class ProgressUpdate {
-        private double workDone;
-        private double totalWork;
+        private final double workDone;
+        private final double totalWork;
 
         private ProgressUpdate(double p, double m) {
             this.workDone = p;
--- a/javafx-concurrent/src/javafx/concurrent/Worker.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-concurrent/src/javafx/concurrent/Worker.java	Tue Sep 04 13:52:16 2012 -0700
@@ -210,7 +210,7 @@
 
     /**
      * Indicates the current amount of work that has been completed. Zero or a
-     * positive value indicate progress toward completion. This variable's value
+     * positive value indicate progress toward completion. This variables value
      * may or may not change from its default value depending on the specific
      * Worker implementation. A value of -1 means that the current amount of work
      * done cannot be determined (ie: it is indeterminate). The value of
--- a/javafx-ui-common/src/com/sun/javafx/css/StyleConverter.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/StyleConverter.java	Tue Sep 04 13:52:16 2012 -0700
@@ -30,11 +30,9 @@
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
-
 import javafx.scene.text.Font;
-
 import com.sun.javafx.css.converters.EnumConverter;
-import java.util.List;
+import com.sun.javafx.css.converters.PaintConverter;
 
 /**
  * Converter converts ParsedValue&lt;F,T&gt; from type F to type T.
@@ -176,6 +174,10 @@
             styleConverter = com.sun.javafx.css.converters.PaintConverter.LinearGradientConverter.getInstance();
         }  else if (com.sun.javafx.css.converters.PaintConverter.RadialGradientConverter.class == converterClass) {
             styleConverter = com.sun.javafx.css.converters.PaintConverter.RadialGradientConverter.getInstance();
+        }  else if (PaintConverter.ImagePatternConverter.class == converterClass) {
+            styleConverter = PaintConverter.ImagePatternConverter.getInstance();
+        }  else if (PaintConverter.RepeatingImagePatternConverter.class == converterClass) {
+            styleConverter = PaintConverter.RepeatingImagePatternConverter.getInstance();
 
         }  else if (com.sun.javafx.css.converters.SizeConverter.class == converterClass) {
             styleConverter = com.sun.javafx.css.converters.SizeConverter.getInstance();
--- a/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/converters/PaintConverter.java	Tue Sep 04 13:52:16 2012 -0700
@@ -24,7 +24,9 @@
  */
 package com.sun.javafx.css.converters;
 
+import javafx.scene.image.Image;
 import javafx.scene.paint.CycleMethod;
+import javafx.scene.paint.ImagePattern;
 import javafx.scene.paint.LinearGradient;
 import javafx.scene.paint.Paint;
 import javafx.scene.paint.RadialGradient;
@@ -44,6 +46,8 @@
         static PaintConverter INSTANCE = new PaintConverter();
         static SequenceConverter SEQUENCE_INSTANCE = new SequenceConverter();
         static LinearGradientConverter LINEAR_GRADIENT_INSTANCE = new LinearGradientConverter();
+        static ImagePatternConverter IMAGE_PATTERN_INSTANCE = new ImagePatternConverter();
+        static RepeatingImagePatternConverter REPEATING_IMAGE_PATTERN_INSTANCE = new RepeatingImagePatternConverter();
         static RadialGradientConverter RADIAL_GRADIENT_INSTANCE = new RadialGradientConverter();
     }
 
@@ -98,7 +102,6 @@
         }
     }
 
-
     public static final class LinearGradientConverter extends StyleConverter<ParsedValue[], Paint> {
 
         public static LinearGradientConverter getInstance() {
@@ -113,10 +116,10 @@
         public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
             ParsedValue[] values = value.getValue();
             int v = 0;
-            final Size startX = (Size) ((ParsedValue<?, Size>) values[v++]).convert(font);
-            final Size startY = (Size) ((ParsedValue<?, Size>) values[v++]).convert(font);
-            final Size endX = (Size) ((ParsedValue<?, Size>) values[v++]).convert(font);
-            final Size endY = (Size) ((ParsedValue<?, Size>) values[v++]).convert(font);
+            final Size startX = ((ParsedValue<?, Size>) values[v++]).convert(font);
+            final Size startY = ((ParsedValue<?, Size>) values[v++]).convert(font);
+            final Size endX = ((ParsedValue<?, Size>) values[v++]).convert(font);
+            final Size endY = ((ParsedValue<?, Size>) values[v++]).convert(font);
             boolean proportional = startX.getUnits() == SizeUnits.PERCENT && startX.getUnits() == startY.getUnits() && startX.getUnits() == endX.getUnits() && startX.getUnits() == endY.getUnits();
             final CycleMethod cycleMethod = (CycleMethod) values[v++].convert(font);
             final Stop[] stops = new Stop[values.length - v];
@@ -132,6 +135,68 @@
         }
     }
 
+    public static final class ImagePatternConverter extends StyleConverter<ParsedValue[], Paint> {
+
+        public static ImagePatternConverter getInstance() {
+            return Holder.IMAGE_PATTERN_INSTANCE;
+        }
+
+        private ImagePatternConverter() {
+            super();
+        }
+
+        @Override
+        public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
+            ParsedValue[] values = value.getValue();
+            ParsedValue<ParsedValue[],String> url = values[0];
+            if (values.length == 1) {
+                return new ImagePattern(new Image(url.convert(font)));
+            }
+
+            ParsedValue<?, Size> x = values[1];
+            ParsedValue<?, Size> y = values[2];
+            ParsedValue<?, Size> w = values[3];
+            ParsedValue<?, Size> h = values[4];
+            boolean p = values.length < 6 ? true : (Boolean) values[5].getValue();
+
+            return new ImagePattern(
+                    new Image(url.convert(font)),
+                    x.convert(font).getValue(),
+                    y.convert(font).getValue(),
+                    w.convert(font).getValue(),
+                    h.convert(font).getValue(), p);
+        }
+
+        @Override
+        public String toString() {
+            return "ImagePatternConverter";
+        }
+    }
+
+    public static final class RepeatingImagePatternConverter extends StyleConverter<ParsedValue[], Paint> {
+
+        public static RepeatingImagePatternConverter getInstance() {
+            return Holder.REPEATING_IMAGE_PATTERN_INSTANCE;
+        }
+
+        private RepeatingImagePatternConverter() {
+            super();
+        }
+
+        @Override
+        public Paint convert(ParsedValue<ParsedValue[], Paint> value, Font font) {
+            ParsedValue[] values = value.getValue();
+            ParsedValue<ParsedValue[],String> url = values[0];
+            final Image image = new Image(url.convert(font));
+            return new ImagePattern(image, 0, 0, image.getWidth(), image.getHeight(), false);
+        }
+
+        @Override
+        public String toString() {
+            return "RepeatingImagePatternConverter";
+        }
+    }
+
     public static final class RadialGradientConverter extends StyleConverter<ParsedValue[], Paint> {
 
         public static RadialGradientConverter getInstance() {
@@ -151,11 +216,11 @@
             // proportional, we need to get to the Size. getValue() will
             // return ParsedValue<?,Size>, so getValue().convert(font) will
             // give us the size.
-            final Size focusAngle = values[v++] != null ? (Size) ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
-            final Size focusDistance = values[v++] != null ? (Size) ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
-            final Size centerX = values[v++] != null ? (Size) ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
-            final Size centerY = values[v++] != null ? (Size) ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
-            final Size radius = (Size) ((ParsedValue<?, Size>) values[v++]).convert(font);
+            final Size focusAngle = values[v++] != null ? ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
+            final Size focusDistance = values[v++] != null ? ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
+            final Size centerX = values[v++] != null ? ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
+            final Size centerY = values[v++] != null ? ((ParsedValue<?, Size>) values[v-1]).convert(font) : null;
+            final Size radius = ((ParsedValue<?, Size>) values[v++]).convert(font);
             boolean proportional = radius.getUnits().equals(SizeUnits.PERCENT);
             boolean unitsAgree = centerX != null ? proportional == centerX.getUnits().equals(SizeUnits.PERCENT) : true;
             unitsAgree = unitsAgree && centerY != null ? proportional == centerY.getUnits().equals(SizeUnits.PERCENT) : true;
--- a/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-common/src/com/sun/javafx/css/parser/CSSParser.java	Tue Sep 04 13:52:16 2012 -0700
@@ -24,19 +24,18 @@
  */
 package com.sun.javafx.css.parser;
 
-import com.sun.javafx.css.*;
 import java.io.BufferedReader;
 import java.io.CharArrayReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.net.URL;
+import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
 import javafx.geometry.Insets;
 import javafx.scene.effect.BlurType;
 import javafx.scene.effect.Effect;
@@ -50,7 +49,20 @@
 import javafx.scene.text.Font;
 import javafx.scene.text.FontPosture;
 import javafx.scene.text.FontWeight;
-
+import com.sun.javafx.css.Combinator;
+import com.sun.javafx.css.CompoundSelector;
+import com.sun.javafx.css.CssError;
+import com.sun.javafx.css.Declaration;
+import com.sun.javafx.css.FontUnits;
+import com.sun.javafx.css.ParsedValue;
+import com.sun.javafx.css.Rule;
+import com.sun.javafx.css.Selector;
+import com.sun.javafx.css.SimpleSelector;
+import com.sun.javafx.css.Size;
+import com.sun.javafx.css.SizeUnits;
+import com.sun.javafx.css.StyleManager;
+import com.sun.javafx.css.Styleable;
+import com.sun.javafx.css.Stylesheet;
 import com.sun.javafx.css.converters.BooleanConverter;
 import com.sun.javafx.css.converters.EffectConverter;
 import com.sun.javafx.css.converters.EnumConverter;
@@ -68,10 +80,6 @@
 import com.sun.javafx.scene.layout.region.Margins;
 import com.sun.javafx.scene.layout.region.Repeat;
 import com.sun.javafx.scene.layout.region.StrokeBorder;
-import java.net.MalformedURLException;
-import java.text.MessageFormat;
-import javafx.collections.ObservableList;
-import javafx.scene.Node;
 
 final public class CSSParser {
     static boolean EXIT_ON_ERROR = false;
@@ -468,8 +476,8 @@
         return buf.toString();
     }
     
-   // Assumes string is not a lookup!
-   private ParsedValue<Color,Color> colorValueOfString(String str) {
+    // Assumes string is not a lookup!
+    private ParsedValue<Color,Color> colorValueOfString(String str) {
 
         if(str.startsWith("#") || str.startsWith("0x")) {
 
@@ -1347,6 +1355,10 @@
             return parseLinearGradient(root);
         } else if ("radial-gradient".regionMatches(true, 0, fcn, 0, 15)) {
             return parseRadialGradient(root);
+        } else if ("image-pattern".regionMatches(true, 0, fcn, 0, 13)) {
+            return parseImagePattern(root);
+        } else if ("repeating-image-pattern".regionMatches(true, 0, fcn, 0, 23)) {
+            return parseRepeatingImagePattern(root);
         } else if ("ladder".regionMatches(true, 0, fcn, 0, 6)) {
             return parseLadder(root);
         } else if ("url".regionMatches(true, 0, fcn, 0, 3)) {
@@ -2131,6 +2143,114 @@
         
     }
     
+    // Based off ImagePattern constructor
+    //
+    // image-pattern(<uri-string>[,<size>,<size>,<size>,<size>[,<boolean>]?]?)
+    //
+    private ParsedValue<ParsedValue[], Paint> parseImagePattern(final Term root) throws ParseException {
+
+        // first term in the chain is the function name...
+        final String fn = (root.token != null) ? root.token.getText() : null;
+        // NOTE: We should put this in an assertion, so as not to do this work ordinarily, because only a
+        // bug in the parser can cause this.
+        assert !"image-pattern".regionMatches(true, 0, fn, 0, 13) : "Expected \'image-pattern\'";
+
+        Term arg;
+        if ((arg = root.firstArg) == null ||
+             arg.token == null ||
+             arg.token.getText().isEmpty()) {
+            error(root,
+                "Expected \'<uri-string>\'");
+        }
+
+        Term prev = arg;
+
+        final String uri = arg.token.getText();
+        ParsedValue[] uriValues = new ParsedValue[] {
+            new ParsedValue<String,String>(uri, StringConverter.getInstance()),
+            new ParsedValue<URL,URL>(sourceOfStylesheet, null)
+        };
+        ParsedValue parsedURI = new ParsedValue<ParsedValue[],String>(uriValues, URLConverter.getInstance());
+
+        // If nextArg is null, then there are no remaining arguments, so we are done.
+        if (arg.nextArg == null) {
+            ParsedValue[] values = new ParsedValue[1];
+            values[0] = parsedURI;
+            return new ParsedValue<ParsedValue[], Paint>(values, PaintConverter.ImagePatternConverter.getInstance());
+        }
+
+        // There must now be 4 sizes in a row.
+        Token token;
+        if ((arg = arg.nextArg) == null) error(prev, "Expected \'<size>\'");
+        ParsedValue<?, Size> x = parseSize(arg);
+
+        prev = arg;
+        if ((arg = arg.nextArg) == null) error(prev, "Expected \'<size>\'");
+        ParsedValue<?, Size> y = parseSize(arg);
+
+        prev = arg;
+        if ((arg = arg.nextArg) == null) error(prev, "Expected \'<size>\'");
+        ParsedValue<?, Size> w = parseSize(arg);
+
+        prev = arg;
+        if ((arg = arg.nextArg) == null) error(prev, "Expected \'<size>\'");
+        ParsedValue<?, Size> h = parseSize(arg);
+
+        // If there are no more args, then we are done.
+        if (arg.nextArg == null) {
+            ParsedValue[] values = new ParsedValue[5];
+            values[0] = parsedURI;
+            values[1] = x;
+            values[2] = y;
+            values[3] = w;
+            values[4] = h;
+            return new ParsedValue<ParsedValue[], Paint>(values, PaintConverter.ImagePatternConverter.getInstance());
+        }
+
+        prev = arg;
+        if ((arg = arg.nextArg) == null) error(prev, "Expected \'<boolean>\'");
+        if ((token = arg.token) == null || token.getText() == null) error(arg, "Expected \'<boolean>\'");
+
+        ParsedValue[] values = new ParsedValue[6];
+        values[0] = parsedURI;
+        values[1] = x;
+        values[2] = y;
+        values[3] = w;
+        values[4] = h;
+        values[5] = new ParsedValue<Boolean, Boolean>(Boolean.parseBoolean(token.getText()), null);
+        return new ParsedValue<ParsedValue[], Paint>(values, PaintConverter.ImagePatternConverter.getInstance());
+    }
+
+    // For tiling ImagePatterns easily.
+    //
+    // repeating-image-pattern(<uri-string>)
+    //
+    private ParsedValue<ParsedValue[], Paint> parseRepeatingImagePattern(final Term root) throws ParseException {
+        // first term in the chain is the function name...
+        final String fn = (root.token != null) ? root.token.getText() : null;
+        // NOTE: We should put this in an assertion, so as not to do this work ordinarily, because only a
+        // bug in the parser can cause this.
+        assert !"repeating-image-pattern".regionMatches(true, 0, fn, 0, 23) : "Expected \'repeating-image-pattern\'";
+
+        Term arg;
+        if ((arg = root.firstArg) == null ||
+             arg.token == null ||
+             arg.token.getText().isEmpty()) {
+            error(root,
+                "Expected \'<uri-string>\'");
+        }
+
+        final String uri = arg.token.getText();
+        ParsedValue[] uriValues = new ParsedValue[] {
+            new ParsedValue<String,String>(uri, StringConverter.getInstance()),
+            new ParsedValue<URL,URL>(sourceOfStylesheet, null)
+        };
+        ParsedValue parsedURI = new ParsedValue<ParsedValue[],String>(uriValues, URLConverter.getInstance());
+        ParsedValue[] values = new ParsedValue[1];
+        values[0] = parsedURI;
+        return new ParsedValue<ParsedValue[], Paint>(values, PaintConverter.RepeatingImagePatternConverter.getInstance());
+    }
+
     // parse a series of paint values separated by commas.
     // i.e., <paint> [, <paint>]*
     private ParsedValue<ParsedValue<?,Paint>[],Paint[]> parsePaintLayers(Term root)
@@ -3159,7 +3279,7 @@
         if (arg.token == null ||
             arg.token.getType() != CSSLexer.STRING ||
             arg.token.getText() == null ||
-            arg.token.getText().isEmpty()) error(arg, "Excpected \'url(\"<uri-string>\")\'");
+            arg.token.getText().isEmpty()) error(arg, "Expected \'url(\"<uri-string>\")\'");
 
         final String uri = arg.token.getText();
         ParsedValue[] uriValues = new ParsedValue[] {
--- a/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-common/src/javafx/scene/doc-files/cssref.html	Tue Sep 04 13:52:16 2012 -0700
@@ -659,7 +659,7 @@
     <h3><a name="typesize" id="typesize">&lt;size&gt;</a></h3>
     <p>A size is a <a href="#typenumber" class="typeref">&lt;number&gt;</a>
       with units of <a href="#typelength" class="typeref">&lt;length&gt;</a> or
-      <a href="#typepercentage" class="typeref">&lt;percentage&gt;</a>If units
+      <a href="#typepercentage" class="typeref">&lt;percentage&gt;</a>. If units
       are not specified then specified the 'px' is assumed.</p>
     <h4><a name="typelength" id="typelength">&lt;length&gt;</a></h4>
     <p class="grammar"><a href="#typenumber">&lt;number&gt;</a>[ px | mm | cm |
@@ -877,10 +877,15 @@
     </table>
     <h3><a name="typepaint" id="typepaint">&lt;paint&gt;</a></h3>
     <p>Paint values can either be a solid color specified in one of the color
-      syntaxes or they can be a linear or radial gradient.</p>
+      syntaxes, they can be a linear or radial gradient, or an image-pattern.</p>
     <p class="grammar"><a href="#typecolor" class="typelink">&lt;color&gt;</a> |
-      &lt;linear-gradient&gt; | &lt;radial-gradient&gt;</p>
-    <h4>Linear Gradients <span class="grammar" style="font-size: smaller;">&lt;linear-gradient&gt;</span></h4>
+      <a href="#typelinear-gradient" class="typelink">&lt;linear-gradient&gt;</a> |
+      <a href="#typeradial-gradient" class="typelink">&lt;radial-gradient&gt;</a> |
+      <a href="#typeimage-pattern" class="typelink">&lt;image-pattern&gt;</a></p>
+      <a href="#typeimage-pattern" class="typelink">&lt;repeating-image-pattern&gt;</a></p>
+    <h4>Linear Gradients<span class="grammar" style="font-size: smaller;">
+            <a name="typelinear-gradient" id="typelinear-gradient">&lt;linear-gradient&gt;</a>
+        </span></h4>
     <p class="grammar">linear-gradient( [ [from <a href="#typepoint" class="typelink">&lt;point&gt;</a>
       to <a href="#typepoint" class="typelink">&lt;point&gt;</a>] | [ to
       &lt;side-or-corner&gt;], ]? [ [ repeat | reflect ], ]? <a href="#color-stop"
@@ -920,7 +925,9 @@
     <p class="grammar"><font color="#af0000">linear (&lt;size&gt;, &lt;size&gt;)
         to (&lt;size&gt;, &lt;size&gt;) stops [ (&lt;number&gt;,&lt;color&gt;)
         ]+ [ repeat | reflect ]?</font></p>
-    <h4>Radial Gradients <span class="grammar" style="font-size: smaller;">&lt;radial-gradient&gt;</span></h4>
+    <h4>Radial Gradients <span class="grammar" style="font-size: smaller;">
+            <a name="typeradial-gradient" id="typeradial-gradient">&lt;radial-gradient&gt;</a>
+        </span></h4>
     <p class="grammar">radial-gradient([ focus-angle <a href="#typeangle" class="typelink">&lt;angle&gt;</a>,
       ]? [ focus-distance <a href="#typepercentage" class="typelink">&lt;percentage&gt;</a>,
       ]? [ center <a href="#typepoint" class="typelink">&lt;point&gt;</a>, ]?
@@ -946,6 +953,49 @@
         | &lt;number&gt; ] ]? [ focus-distance &lt;size&gt; ]? [ center
         &lt;size,size&gt; ]? &lt;size&gt; stops [ ( &lt;number&gt;,
         &lt;color&gt; ) ]+ [ repeat | reflect ]?</font></p>
+    <h4>Image Paint <span class="grammar" style="font-size: smaller;">
+            <a name="typeimage-pattern" id="typeimage-pattern">&lt;image-pattern&gt;</a>
+        </span></h4>
+    <p class="grammar">image-pattern(<a href="#typestring" class="typelink">&lt;string&gt;</a>,
+      [<a href="#typesize" class="typelink">&lt;size&gt;</a>,
+      <a href="#typesize" class="typelink">&lt;size&gt;</a>,
+      <a href="#typesize" class="typelink">&lt;size&gt;</a>,
+      <a href="#typesize" class="typelink">&lt;size&gt;</a>[,
+      <a href="#typeboolean" class="typelink">&lt;boolean&gt;</a>]?]?)</p>
+    <p>The parameters, in order, are:<br>
+    <p style="margin-left: 40px;">  
+    <span class="grammar"><a href="#typestring" class="typelink">&lt;string&gt;</a></span>
+      The URL of the image.<br>
+      <span class="grammar"><a href="#typesize" class="typelink">&lt;size&gt;</a></span>
+      The <i>x</i> origin of the anchor rectangle.<br>
+      <span class="grammar"><a href="#typesize" class="typelink">&lt;size&gt;</a></span>
+      The <i>y</i> origin of the anchor rectangle.<br>
+      <span class="grammar"><a href="#typesize" class="typelink">&lt;size&gt;</a></span>
+      The width of the anchor rectangle.<br>
+      <span class="grammar"><a href="#typesize" class="typelink">&lt;size&gt;</a></span>
+      The height of the anchor rectangle.<br>
+      <span class="grammar"><a href="#typeboolean" class="typelink">&lt;boolean&gt;</a></span>
+      The proportional flag which indicates whether start and end locations are proportional or absolute<br>
+    </p>
+    <p>For a full explanation of the parameters, refer to the 
+        <a href="../paint/ImagePattern.html" class="typelink">ImagePattern</a> javadoc.</p>    
+    <p>Following are examples of the use of image-pattern:</p>
+    <p class="example">image-pattern("images/Duke.png")</p>
+    <p class="example">image-pattern("images/Duke.png", 20%, 20%, 80%, 80%)</p>
+    <p class="example">image-pattern("images/Duke.png", 20%, 20%, 80%, 80%, true)</p>
+    <p class="example">image-pattern("images/Duke.png", 20, 20, 80, 80, false)</p>
+
+    <p>Related, there is the <code>repeating-image-pattern</code> function which is a shorthand
+    for producing tiled image based fills. It is equivalent to
+    <p class="example">image-pattern("images/Duke.png", 0, 0, imageWidth, imageHeight, false)</p>
+    </p>
+
+    <p class="grammar">repeating-image-pattern(<a href="#typestring" class="typelink">&lt;string&gt;</a>)</p>
+    <p>The only parameter is the uri of the image. Following is an example of the use of image-pattern:</p>
+    <p class="example">repeating-image-pattern("com/mycompany/myapp/images/Duke.png")</p>
+
+
+
     <h3><a name="typecolor" id="typecolor">&lt;color&gt;</a></h3>
     <p class="grammar">&lt;named-color&gt; | &lt;looked-up-color&gt; |
       &lt;rgb-color&gt; | &lt;hsb-color&gt; | &lt;color-function&gt;</p>
@@ -2480,8 +2530,8 @@
                 class="typelink">&lt;size&gt;</a>
               <a href="#typesize" class="typelink">&lt;size&gt;</a> ] fill? [ ,
               [ <a href="#typesize" class="typelink">&lt;size&gt;</a> | <a href="#typesize"
-                class="typelink">&lt;size&gt;</a><a
-                <a=""
+                class="typelink">&lt;size&gt;</a>
+                <a
                 href="#typesize"
                 class="typelink">&lt;size&gt;</a>
               <a href="#typesize" class="typelink">&lt;size&gt;</a> <a href="#typesize"
@@ -5367,5 +5417,6 @@
         rights reserved. Use is subject to <a href="http://download.oracle.com/javafx/2.0/api/license.html">license
           terms</a>. </small> </p>
     <br>
+    </table>
   </body>
 </html>
--- a/javafx-ui-common/test/unit/com/sun/javafx/css/StyleConverterTest.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-common/test/unit/com/sun/javafx/css/StyleConverterTest.java	Tue Sep 04 13:52:16 2012 -0700
@@ -49,16 +49,16 @@
         for (int i = 0; i < files.length; i++) {
             if (files[i].isFile()) {
                 String name = files[i].getName();
-		if (! name.endsWith("class")) continue;
-		try {
+                if (! name.endsWith("class")) continue;
+                try {
                     // strip .class
                     name = (name.substring(0, name.length()-6));
                     name = (pkgPrefix != null) ? pkgPrefix.concat(".").concat(name) : name;
                     Class cl = Class.forName(name, false, loader);
                     if (converterClass.isAssignableFrom(cl)) converterClassList.add(cl);
-		} catch (Exception any) {
+                } catch (Exception any) {
                     System.out.println(any.toString());
-		}
+                }
             } else if (files[i].isDirectory()) {
                 String pkg =
                     (pkgPrefix != null) ? pkgPrefix.concat(".").concat(files[i].getName()) : files[i].getName();
@@ -69,8 +69,7 @@
 
     @Test
     public void testGetInstance_Class_ForAllInstancesOfStyleConverter() {
-
-	try {
+        try {
             converterClass = Class.forName("com.sun.javafx.css.StyleConverter");
             loader = converterClass.getClassLoader();
             URL url = converterClass.getClassLoader().getResource("");
@@ -88,8 +87,8 @@
                     assertNotNull(cl.getName(), result);
                 }
             }
-	} catch(Exception any) {
+        } catch(Exception any) {
             fail(any.toString());
-	}
+        }
     }
 }
\ No newline at end of file
--- a/javafx-ui-controls/src/javafx/scene/control/Menu.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/Menu.java	Tue Sep 04 13:52:16 2012 -0700
@@ -160,7 +160,7 @@
     private ReadOnlyBooleanWrapper showing;
     
     private void setShowing(boolean value) {
-        if (getItems().size() == 0) return;
+        if (getItems().size() == 0 || (value && isShowing())) return;
         
         // these events will not fire if the showing property is bound
         if (value) {
--- a/javafx-ui-controls/src/javafx/scene/control/TablePosition.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-controls/src/javafx/scene/control/TablePosition.java	Tue Sep 04 13:52:16 2012 -0700
@@ -24,6 +24,8 @@
  */
 package javafx.scene.control;
 
+import java.lang.ref.WeakReference;
+
 /**
  * This class is used to represent a single row/column/cell in a TableView.
  * This is used throughout the TableView API to represent which rows/columns/cells
@@ -51,7 +53,9 @@
 
     /**
      * Constructs a TablePosition instance to represent the given row/column
-     * position in the given TableView instance.
+     * position in the given TableView instance. Both the TableView and 
+     * TableColumn are referenced weakly in this class, so it is possible that
+     * they will be null when their respective getters are called.
      * 
      * @param tableView The TableView that this position is related to.
      * @param row The row that this TablePosition is representing.
@@ -59,8 +63,8 @@
      */
     public TablePosition(TableView<S> tableView, int row, TableColumn<S,T> tableColumn) {
         this.row = row;
-        this.tableColumn = tableColumn;
-        this.tableView = tableView;
+        this.tableColumnRef = new WeakReference<TableColumn<S, T>>(tableColumn);
+        this.tableViewRef = new WeakReference<TableView<S>>(tableView);
     }
     
     
@@ -72,8 +76,8 @@
      **************************************************************************/
 
     private final int row;
-    private final TableColumn<S,T> tableColumn;
-    private final TableView<S> tableView;
+    private final WeakReference<TableColumn<S,T>> tableColumnRef;
+    private final WeakReference<TableView<S>> tableViewRef;
 
 
 
@@ -95,6 +99,8 @@
      * is -1 if the TableView or TableColumn instances are null.
      */
     public final int getColumn() {
+        TableView tableView = getTableView();
+        TableColumn tableColumn = getTableColumn();
         return tableView == null || tableColumn == null ? -1 : 
                 tableView.getVisibleLeafIndex(tableColumn);
     }
@@ -103,14 +109,14 @@
      * The TableView that this TablePosition is related to.
      */
     public final TableView<S> getTableView() {
-        return tableView;
+        return tableViewRef.get();
     }
     
     /**
      * The TableColumn that this TablePosition represents in the TableView.
      */
     public final TableColumn<S,T> getTableColumn() {
-        return tableColumn;
+        return tableColumnRef.get();
     }
 
     /**
@@ -130,10 +136,14 @@
         if (this.row != other.row) {
             return false;
         }
-        if (this.tableColumn != other.tableColumn && (this.tableColumn == null || !this.tableColumn.equals(other.tableColumn))) {
+        TableColumn tableColumn = getTableColumn();
+        TableColumn otherTableColumn = other.getTableColumn();
+        if (tableColumn != otherTableColumn && (tableColumn == null || !tableColumn.equals(otherTableColumn))) {
             return false;
         }
-        if (this.tableView != other.tableView && (this.tableView == null || !this.tableView.equals(other.tableView))) {
+        TableView tableView = getTableView();
+        TableView otherTableView = other.getTableView();
+        if (tableView != otherTableView && (tableView == null || !tableView.equals(otherTableView))) {
             return false;
         }
         return true;
@@ -146,8 +156,8 @@
     @Override public int hashCode() {
         int hash = 5;
         hash = 79 * hash + this.row;
-        hash = 79 * hash + (this.tableColumn != null ? this.tableColumn.hashCode() : 0);
-        hash = 79 * hash + (this.tableView != null ? this.tableView.hashCode() : 0);
+        hash = 79 * hash + (getTableColumn() != null ? getTableColumn().hashCode() : 0);
+        hash = 79 * hash + (getTableView() != null ? getTableView().hashCode() : 0);
         return hash;
     }
 
@@ -156,7 +166,7 @@
      * @return a string representation of this {@code TablePosition} object.
      */ 
     @Override public String toString() {
-        return "TablePosition [ row: " + row + ", column: " + tableColumn + ", "
-                + "tableView: " + tableView + " ]";
+        return "TablePosition [ row: " + row + ", column: " + getTableColumn() + ", "
+                + "tableView: " + getTableView() + " ]";
     }
 }
--- a/javafx-ui-controls/test/javafx/scene/control/MenuBarTest.java	Tue Sep 04 10:40:57 2012 -0700
+++ b/javafx-ui-controls/test/javafx/scene/control/MenuBarTest.java	Tue Sep 04 13:52:16 2012 -0700
@@ -9,6 +9,8 @@
 import com.sun.javafx.scene.control.skin.MenuBarMenuButtonRetriever;
 import com.sun.javafx.scene.control.skin.MenuBarSkin;
 import com.sun.javafx.tk.Toolkit;
+import javafx.event.Event;
+import javafx.event.EventHandler;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
 import javafx.scene.input.MouseEvent;
@@ -318,6 +320,59 @@
         assertTrue(menu3.isShowing());
     }
     
+    
+     @Test public void testMenuOnShowingEventFiringWithMenuHideOperation() {
+        final MouseEventGenerator generator = new MouseEventGenerator();
+        VBox root = new VBox();
+        Menu menu = new Menu("Menu");
+
+        MenuItem menuItem1 = new MenuItem("MenuItem1");
+        menu.getItems().addAll(menuItem1);
+        
+        menuBar.getMenus().add(menu);
+        menuBar.setLayoutX(100);
+        menuBar.setLayoutY(100);
+        
+        root.getChildren().addAll(menuBar);
+        startApp(root);
+        tk.firePulse();
+        
+        MenuBarSkin skin = (MenuBarSkin)menuBar.getSkin();
+        assertTrue(skin != null);
+        double xval = (menuBar.localToScene(menuBar.getLayoutBounds())).getMinX();
+        double yval = (menuBar.localToScene(menuBar.getLayoutBounds())).getMinY();
+        
+        boolean click = true;
+        final Boolean firstClick = new Boolean(click);
+        
+        menu.setOnShowing(new EventHandler<Event>() {
+            @Override public void handle(Event t) {
+                // we should not get here when the menu is hidden
+                assertEquals(firstClick.booleanValue(), true);
+            }
+        });
+        
+        MenuButton mb = MenuBarMenuButtonRetriever.getNodeForMenu(skin, 0);
+        mb.getScene().getWindow().requestFocus();
+        mb.requestFocus();
+        assertTrue(mb.isFocused());
+        // click on menu to show 
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_PRESSED, xval+20, yval+20));
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_RELEASED, xval+20, yval+20));
+        tk.firePulse(); 
+        assertEquals(menu.showingProperty().get(), true);
+        click = false;
+        // click on menu to hide
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_PRESSED, xval+20, yval+20));
+        scene.impl_processMouseEvent(
+            generator.generateMouseEvent(MouseEvent.MOUSE_RELEASED, xval+20, yval+20));
+        tk.firePulse(); 
+        assertEquals(menu.showingProperty().get(), false);
+    }
+    
 //    static final class MouseEventTracker {
 //        private Node node;
 //