]> source.dussan.org Git - vaadin-framework.git/commitdiff
Merge branch 'master' into layoutgraph
authorJohn Ahlroos <john@vaadin.com>
Fri, 31 Aug 2012 07:10:03 +0000 (10:10 +0300)
committerJohn Ahlroos <john@vaadin.com>
Fri, 31 Aug 2012 07:10:03 +0000 (10:10 +0300)
Conflicts:
client/src/com/vaadin/client/ComponentLocator.java
client/src/com/vaadin/client/ui/AbstractComponentConnector.java

23 files changed:
1  2 
client/src/com/vaadin/client/ComponentLocator.java
client/src/com/vaadin/client/ComputedStyle.java
client/src/com/vaadin/client/LayoutManager.java
client/src/com/vaadin/client/ServerConnector.java
client/src/com/vaadin/client/ui/AbstractComponentConnector.java
client/src/com/vaadin/client/ui/AbstractConnector.java
client/src/com/vaadin/client/ui/orderedlayout/AbstractBoxLayoutConnector.java
client/src/com/vaadin/client/ui/orderedlayout/HorizontalBoxLayoutConnector.java
client/src/com/vaadin/client/ui/orderedlayout/HorizontalLayoutConnector.java
client/src/com/vaadin/client/ui/orderedlayout/VBoxLayout.java
client/src/com/vaadin/client/ui/orderedlayout/VerticalBoxLayoutConnector.java
client/src/com/vaadin/client/ui/orderedlayout/VerticalLayoutConnector.java
client/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
shared/src/com/vaadin/shared/ComponentState.java
tests/testbench/com/vaadin/tests/components/orderedlayout/BoxLayoutTest.java
tests/testbench/com/vaadin/tests/components/orderedlayout/LayoutRenderTimeTest.java
tests/testbench/com/vaadin/tests/components/orderedlayout/VaadinTunesLayout.java
tests/testbench/com/vaadin/tests/themes/LiferayThemeTest.java
uitest/src/com/vaadin/tests/components/flash/FlashIsVisible.java
uitest/src/com/vaadin/tests/components/orderedlayout/LayoutResizeTest.java
uitest/src/com/vaadin/tests/components/orderedlayout/OrderedLayoutCases.java
uitest/src/com/vaadin/tests/components/table/TextFieldRelativeWidth.java
uitest/src/com/vaadin/tests/fieldgroup/BooleanTextField.html

index 0000000000000000000000000000000000000000,febe871b9dfb544d189d76a1048324d375d4544b..03dd9665384634a49ce490a55b5d0d8612a86a99
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,622 +1,631 @@@
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client;
+ import java.util.ArrayList;
+ import java.util.Iterator;
+ import java.util.List;
+ import com.google.gwt.user.client.DOM;
+ import com.google.gwt.user.client.Element;
+ import com.google.gwt.user.client.ui.HasWidgets;
+ import com.google.gwt.user.client.ui.RootPanel;
+ import com.google.gwt.user.client.ui.Widget;
+ import com.vaadin.client.ui.SubPartAware;
+ import com.vaadin.client.ui.UI.VUI;
+ import com.vaadin.client.ui.gridlayout.VGridLayout;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout;
+ import com.vaadin.client.ui.orderedlayout.VMeasuringOrderedLayout;
+ import com.vaadin.client.ui.tabsheet.VTabsheetPanel;
+ import com.vaadin.client.ui.window.VWindow;
+ import com.vaadin.client.ui.window.WindowConnector;
+ import com.vaadin.shared.ComponentState;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.communication.SharedState;
+ /**
+  * ComponentLocator provides methods for generating a String locator for a given
+  * DOM element and for locating a DOM element using a String locator.
+  */
+ public class ComponentLocator {
+     /**
+      * Separator used in the String locator between a parent and a child widget.
+      */
+     private static final String PARENTCHILD_SEPARATOR = "/";
+     /**
+      * Separator used in the String locator between the part identifying the
+      * containing widget and the part identifying the target element within the
+      * widget.
+      */
+     private static final String SUBPART_SEPARATOR = "#";
+     /**
+      * String that identifies the root panel when appearing first in the String
+      * locator.
+      */
+     private static final String ROOT_ID = "Root";
+     /**
+      * Reference to ApplicationConnection instance.
+      */
+     private ApplicationConnection client;
+     /**
+      * Construct a ComponentLocator for the given ApplicationConnection.
+      * 
+      * @param client
+      *            ApplicationConnection instance for the application.
+      */
+     public ComponentLocator(ApplicationConnection client) {
+         this.client = client;
+     }
+     /**
+      * Generates a String locator which uniquely identifies the target element.
+      * The {@link #getElementByPath(String)} method can be used for the inverse
+      * operation, i.e. locating an element based on the return value from this
+      * method.
+      * <p>
+      * Note that getElementByPath(getPathForElement(element)) == element is not
+      * always true as {@link #getPathForElement(Element)} can return a path to
+      * another element if the widget determines an action on the other element
+      * will give the same result as the action on the target element.
+      * </p>
+      * 
+      * @since 5.4
+      * @param targetElement
+      *            The element to generate a path for.
+      * @return A String locator that identifies the target element or null if a
+      *         String locator could not be created.
+      */
+     public String getPathForElement(Element targetElement) {
+         String pid = null;
+         Element e = targetElement;
+         while (true) {
+             pid = ConnectorMap.get(client).getConnectorId(e);
+             if (pid != null) {
+                 break;
+             }
+             e = DOM.getParent(e);
+             if (e == null) {
+                 break;
+             }
+         }
+         Widget w = null;
+         if (pid != null) {
+             // If we found a Paintable then we use that as reference. We should
+             // find the Paintable for all but very special cases (like
+             // overlays).
+             w = ((ComponentConnector) ConnectorMap.get(client)
+                     .getConnector(pid)).getWidget();
+             /*
+              * Still if the Paintable contains a widget that implements
+              * SubPartAware, we want to use that as a reference
+              */
+             Widget targetParent = findParentWidget(targetElement, w);
+             while (targetParent != w && targetParent != null) {
+                 if (targetParent instanceof SubPartAware) {
+                     /*
+                      * The targetParent widget is a child of the Paintable and
+                      * the first parent (of the targetElement) that implements
+                      * SubPartAware
+                      */
+                     w = targetParent;
+                     break;
+                 }
+                 targetParent = targetParent.getParent();
+             }
+         }
+         if (w == null) {
+             // Check if the element is part of a widget that is attached
+             // directly to the root panel
+             RootPanel rootPanel = RootPanel.get();
+             int rootWidgetCount = rootPanel.getWidgetCount();
+             for (int i = 0; i < rootWidgetCount; i++) {
+                 Widget rootWidget = rootPanel.getWidget(i);
+                 if (rootWidget.getElement().isOrHasChild(targetElement)) {
+                     // The target element is contained by this root widget
+                     w = findParentWidget(targetElement, rootWidget);
+                     break;
+                 }
+             }
+             if (w != null) {
+                 // We found a widget but we should still see if we find a
+                 // SubPartAware implementor (we cannot find the Paintable as
+                 // there is no link from VOverlay to its paintable/owner).
+                 Widget subPartAwareWidget = findSubPartAwareParentWidget(w);
+                 if (subPartAwareWidget != null) {
+                     w = subPartAwareWidget;
+                 }
+             }
+         }
+         if (w == null) {
+             // Containing widget not found
+             return null;
+         }
+         // Determine the path for the target widget
+         String path = getPathForWidget(w);
+         if (path == null) {
+             /*
+              * No path could be determined for the target widget. Cannot create
+              * a locator string.
+              */
+             return null;
+         }
+         if (w.getElement() == targetElement) {
+             /*
+              * We are done if the target element is the root of the target
+              * widget.
+              */
+             return path;
+         } else if (w instanceof SubPartAware) {
+             /*
+              * If the widget can provide an identifier for the targetElement we
+              * let it do that
+              */
+             String elementLocator = ((SubPartAware) w)
+                     .getSubPartName(targetElement);
+             if (elementLocator != null) {
+                 return path + SUBPART_SEPARATOR + elementLocator;
+             }
+         }
+         /*
+          * If everything else fails we use the DOM path to identify the target
+          * element
+          */
+         return path + getDOMPathForElement(targetElement, w.getElement());
+     }
+     /**
+      * Finds the first widget in the hierarchy (moving upwards) that implements
+      * SubPartAware. Returns the SubPartAware implementor or null if none is
+      * found.
+      * 
+      * @param w
+      *            The widget to start from. This is returned if it implements
+      *            SubPartAware.
+      * @return The first widget (upwards in hierarchy) that implements
+      *         SubPartAware or null
+      */
+     private Widget findSubPartAwareParentWidget(Widget w) {
+         while (w != null) {
+             if (w instanceof SubPartAware) {
+                 return w;
+             }
+             w = w.getParent();
+         }
+         return null;
+     }
+     /**
+      * Returns the first widget found when going from {@code targetElement}
+      * upwards in the DOM hierarchy, assuming that {@code ancestorWidget} is a
+      * parent of {@code targetElement}.
+      * 
+      * @param targetElement
+      * @param ancestorWidget
+      * @return The widget whose root element is a parent of
+      *         {@code targetElement}.
+      */
+     private Widget findParentWidget(Element targetElement, Widget ancestorWidget) {
+         /*
+          * As we cannot resolve Widgets from the element we start from the
+          * widget and move downwards to the correct child widget, as long as we
+          * find one.
+          */
+         if (ancestorWidget instanceof HasWidgets) {
+             for (Widget w : ((HasWidgets) ancestorWidget)) {
+                 if (w.getElement().isOrHasChild(targetElement)) {
+                     return findParentWidget(targetElement, w);
+                 }
+             }
+         }
+         // No children found, this is it
+         return ancestorWidget;
+     }
+     /**
+      * Locates an element based on a DOM path and a base element.
+      * 
+      * @param baseElement
+      *            The base element which the path is relative to
+      * @param path
+      *            String locator (consisting of domChild[x] parts) that
+      *            identifies the element
+      * @return The element identified by path, relative to baseElement or null
+      *         if the element could not be found.
+      */
+     private Element getElementByDOMPath(Element baseElement, String path) {
+         String parts[] = path.split(PARENTCHILD_SEPARATOR);
+         Element element = baseElement;
+         for (String part : parts) {
+             if (part.startsWith("domChild[")) {
+                 String childIndexString = part.substring("domChild[".length(),
+                         part.length() - 1);
+                 try {
+                     int childIndex = Integer.parseInt(childIndexString);
+                     element = DOM.getChild(element, childIndex);
+                 } catch (Exception e) {
+                     return null;
+                 }
+             }
+         }
+         return element;
+     }
+     /**
+      * Generates a String locator using domChild[x] parts for the element
+      * relative to the baseElement.
+      * 
+      * @param element
+      *            The target element
+      * @param baseElement
+      *            The starting point for the locator. The generated path is
+      *            relative to this element.
+      * @return A String locator that can be used to locate the target element
+      *         using {@link #getElementByDOMPath(Element, String)} or null if
+      *         the locator String cannot be created.
+      */
+     private String getDOMPathForElement(Element element, Element baseElement) {
+         Element e = element;
+         String path = "";
+         while (true) {
+             Element parent = DOM.getParent(e);
+             if (parent == null) {
+                 return null;
+             }
+             int childIndex = -1;
+             int childCount = DOM.getChildCount(parent);
+             for (int i = 0; i < childCount; i++) {
+                 if (e == DOM.getChild(parent, i)) {
+                     childIndex = i;
+                     break;
+                 }
+             }
+             if (childIndex == -1) {
+                 return null;
+             }
+             path = PARENTCHILD_SEPARATOR + "domChild[" + childIndex + "]"
+                     + path;
+             if (parent == baseElement) {
+                 break;
+             }
+             e = parent;
+         }
+         return path;
+     }
+     /**
+      * Locates an element using a String locator (path) which identifies a DOM
+      * element. The {@link #getPathForElement(Element)} method can be used for
+      * the inverse operation, i.e. generating a string expression for a DOM
+      * element.
+      * 
+      * @since 5.4
+      * @param path
+      *            The String locater which identifies the target element.
+      * @return The DOM element identified by {@code path} or null if the element
+      *         could not be located.
+      */
+     public Element getElementByPath(String path) {
+         /*
+          * Path is of type "targetWidgetPath#componentPart" or
+          * "targetWidgetPath".
+          */
+         String parts[] = path.split(SUBPART_SEPARATOR, 2);
+         String widgetPath = parts[0];
+         Widget w = getWidgetFromPath(widgetPath);
+         if (w == null || !Util.isAttachedAndDisplayed(w)) {
+             return null;
+         }
+         if (parts.length == 1) {
+             int pos = widgetPath.indexOf("domChild");
+             if (pos == -1) {
+                 return w.getElement();
+             }
+             // Contains dom reference to a sub element of the widget
+             String subPath = widgetPath.substring(pos);
+             return getElementByDOMPath(w.getElement(), subPath);
+         } else if (parts.length == 2) {
+             if (w instanceof SubPartAware) {
+                 return ((SubPartAware) w).getSubPartElement(parts[1]);
+             }
+         }
+         return null;
+     }
+     /**
+      * Creates a locator String for the given widget. The path can be used to
+      * locate the widget using {@link #getWidgetFromPath(String)}.
+      * 
+      * Returns null if no path can be determined for the widget or if the widget
+      * is null.
+      * 
+      * @param w
+      *            The target widget
+      * @return A String locator for the widget
+      */
+     private String getPathForWidget(Widget w) {
+         if (w == null) {
+             return null;
+         }
+         if (w instanceof VUI) {
+             return "";
+         } else if (w instanceof VWindow) {
+             Connector windowConnector = ConnectorMap.get(client)
+                     .getConnector(w);
+             List<WindowConnector> subWindowList = client.getRootConnector()
+                     .getSubWindows();
+             int indexOfSubWindow = subWindowList.indexOf(windowConnector);
+             return PARENTCHILD_SEPARATOR + "VWindow[" + indexOfSubWindow + "]";
+         } else if (w instanceof RootPanel) {
+             return ROOT_ID;
+         }
+         Widget parent = w.getParent();
+         String basePath = getPathForWidget(parent);
+         if (basePath == null) {
+             return null;
+         }
+         String simpleName = Util.getSimpleName(w);
+         /*
+          * Check if the parent implements Iterable. At least VPopupView does not
+          * implement HasWdgets so we cannot check for that.
+          */
+         if (!(parent instanceof Iterable<?>)) {
+             // Parent does not implement Iterable so we cannot find out which
+             // child this is
+             return null;
+         }
+         Iterator<?> i = ((Iterable<?>) parent).iterator();
+         int pos = 0;
+         while (i.hasNext()) {
+             Object child = i.next();
+             if (child == w) {
+                 return basePath + PARENTCHILD_SEPARATOR + simpleName + "["
+                         + pos + "]";
+             }
+             String simpleName2 = Util.getSimpleName(child);
+             if (simpleName.equals(simpleName2)) {
+                 pos++;
+             }
+         }
+         return null;
+     }
+     /**
+      * Locates the widget based on a String locator.
+      * 
+      * @param path
+      *            The String locator that identifies the widget.
+      * @return The Widget identified by the String locator or null if the widget
+      *         could not be identified.
+      */
+     private Widget getWidgetFromPath(String path) {
+         Widget w = null;
+         String parts[] = path.split(PARENTCHILD_SEPARATOR);
+         for (int i = 0; i < parts.length; i++) {
+             String part = parts[i];
+             if (part.equals(ROOT_ID)) {
+                 w = RootPanel.get();
+             } else if (part.equals("")) {
+                 w = client.getRootConnector().getWidget();
+             } else if (w == null) {
+                 String id = part;
+                 // Must be old static pid (PID_S*)
+                 ServerConnector connector = ConnectorMap.get(client)
+                         .getConnector(id);
+                 if (connector == null) {
+                     // Lookup by component id
+                     // TODO Optimize this
+                     connector = findConnectorById(client.getRootConnector(),
+                             id.substring(5));
+                 }
+                 if (connector instanceof ComponentConnector) {
+                     w = ((ComponentConnector) connector).getWidget();
+                 } else {
+                     // Not found
+                     return null;
+                 }
+             } else if (part.startsWith("domChild[")) {
+                 // The target widget has been found and the rest identifies the
+                 // element
+                 break;
+             } else if (w instanceof Iterable) {
+                 // W identifies a widget that contains other widgets, as it
+                 // should. Try to locate the child
+                 Iterable<?> parent = (Iterable<?>) w;
+                 // Part is of type "VVerticalLayout[0]", split this into
+                 // VVerticalLayout and 0
+                 String[] split = part.split("\\[", 2);
+                 String widgetClassName = split[0];
+                 String indexString = split[1];
+                 int widgetPosition = Integer.parseInt(indexString.substring(0,
+                         indexString.length() - 1));
+                 // AbsolutePanel in GridLayout has been removed -> skip it
+                 if (w instanceof VGridLayout
+                         && "AbsolutePanel".equals(widgetClassName)) {
+                     continue;
+                 }
+                 if (w instanceof VTabsheetPanel && widgetPosition != 0) {
+                     // TabSheetPanel now only contains 1 connector => the index
+                     // is always 0 which indicates the widget in the active tab
+                     widgetPosition = 0;
+                 }
++                if ("VVerticalLayout".equals(widgetClassName)
++                        || "VHorizontalLayout".equals(widgetClassName)) {
++                    widgetClassName = "VBoxLayout";
++                }
++                if (w instanceof VBoxLayout
++                        && "ChildComponentContainer".equals(widgetClassName)) {
++                    widgetClassName = "VBoxLayout$Slot";
++                }
+                 /*
+                  * The new grid and ordered layotus do not contain
+                  * ChildComponentContainer widgets. This is instead simulated by
+                  * constructing a path step that would find the desired widget
+                  * from the layout and injecting it as the next search step
+                  * (which would originally have found the widget inside the
+                  * ChildComponentContainer)
+                  */
+                 if ((w instanceof VMeasuringOrderedLayout || w instanceof VGridLayout)
+                         && "ChildComponentContainer".equals(widgetClassName)
+                         && i + 1 < parts.length) {
+                     HasWidgets layout = (HasWidgets) w;
+                     String nextPart = parts[i + 1];
+                     String[] nextSplit = nextPart.split("\\[", 2);
+                     String nextWidgetClassName = nextSplit[0];
+                     // Find the n:th child and count the number of children with
+                     // the same type before it
+                     int nextIndex = 0;
+                     for (Widget child : layout) {
+                         boolean matchingType = nextWidgetClassName.equals(Util
+                                 .getSimpleName(child));
+                         if (matchingType && widgetPosition == 0) {
+                             // This is the n:th child that we looked for
+                             break;
+                         } else if (widgetPosition < 0) {
+                             // Error if we're past the desired position without
+                             // a match
+                             return null;
+                         } else if (matchingType) {
+                             // If this was another child of the expected type,
+                             // increase the count for the next step
+                             nextIndex++;
+                         }
+                         // Don't count captions
+                         if (!(child instanceof VCaption)) {
+                             widgetPosition--;
+                         }
+                     }
+                     // Advance to the next step, this time checking for the
+                     // actual child widget
+                     parts[i + 1] = nextWidgetClassName + '[' + nextIndex + ']';
+                     continue;
+                 }
+                 // Locate the child
+                 Iterator<? extends Widget> iterator;
+                 /*
+                  * VWindow and VContextMenu workarounds for backwards
+                  * compatibility
+                  */
+                 if (widgetClassName.equals("VWindow")) {
+                     List<WindowConnector> windows = client.getRootConnector()
+                             .getSubWindows();
+                     List<VWindow> windowWidgets = new ArrayList<VWindow>(
+                             windows.size());
+                     for (WindowConnector wc : windows) {
+                         windowWidgets.add(wc.getWidget());
+                     }
+                     iterator = windowWidgets.iterator();
+                 } else if (widgetClassName.equals("VContextMenu")) {
+                     return client.getContextMenu();
+                 } else {
+                     iterator = (Iterator<? extends Widget>) parent.iterator();
+                 }
+                 boolean ok = false;
+                 // Find the widgetPosition:th child of type "widgetClassName"
+                 while (iterator.hasNext()) {
+                     Widget child = iterator.next();
+                     String simpleName2 = Util.getSimpleName(child);
+                     if (widgetClassName.equals(simpleName2)) {
+                         if (widgetPosition == 0) {
+                             w = child;
+                             ok = true;
+                             break;
+                         }
+                         widgetPosition--;
+                     }
+                 }
+                 if (!ok) {
+                     // Did not find the child
+                     return null;
+                 }
+             } else {
+                 // W identifies something that is not a "HasWidgets". This
+                 // should not happen as all widget containers should implement
+                 // HasWidgets.
+                 return null;
+             }
+         }
+         return w;
+     }
+     private ServerConnector findConnectorById(ServerConnector root, String id) {
+         SharedState state = root.getState();
+         if (state instanceof ComponentState
+                 && id.equals(((ComponentState) state).getId())) {
+             return root;
+         }
+         for (ServerConnector child : root.getChildren()) {
+             ServerConnector found = findConnectorById(child, id);
+             if (found != null) {
+                 return found;
+             }
+         }
+         return null;
+     }
+ }
index 0000000000000000000000000000000000000000,b2f916dd56b5fefafe755bfa11a102b77851c391..f18b3b392c40fcfee20713d953548cf282b1bf72
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,198 +1,198 @@@
 -            if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
 -                // Remember the original values
 -                var left = style.left, rsLeft = elem.runtimeStyle.left;
 -
 -                // Put in the new values to get a computed value out
 -                elem.runtimeStyle.left = cs.left;
 -                style.left = ret || 0;
 -                ret = style.pixelLeft + "px";
 -
 -                // Revert the changed values
 -                style.left = left;
 -                elem.runtimeStyle.left = rsLeft;
 -            }
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client;
+ import com.google.gwt.core.client.JavaScriptObject;
+ import com.google.gwt.dom.client.Element;
+ public class ComputedStyle {
+     protected final JavaScriptObject computedStyle;
+     private final Element elem;
+     /**
+      * Gets this element's computed style object which can be used to gather
+      * information about the current state of the rendered node.
+      * <p>
+      * Note that this method is expensive. Wherever possible, reuse the returned
+      * object.
+      * 
+      * @param elem
+      *            the element
+      * @return the computed style
+      */
+     public ComputedStyle(Element elem) {
+         computedStyle = getComputedStyle(elem);
+         this.elem = elem;
+     }
+     private static native JavaScriptObject getComputedStyle(Element elem)
+     /*-{
+       if(elem.nodeType != 1) {
+           return {};
+       }
+       
+       if($wnd.document.defaultView && $wnd.document.defaultView.getComputedStyle) {
+           return $wnd.document.defaultView.getComputedStyle(elem, null);
+       }
+       
+       if(elem.currentStyle) {
+           return elem.currentStyle;
+       }
+     }-*/;
+     /**
+      * 
+      * @param name
+      *            name of the CSS property in camelCase
+      * @return the value of the property, normalized for across browsers (each
+      *         browser returns pixel values whenever possible).
+      */
+     public final native String getProperty(String name)
+     /*-{
+         var cs = this.@com.vaadin.client.ComputedStyle::computedStyle;
+         var elem = this.@com.vaadin.client.ComputedStyle::elem;
+         
+         // Border values need to be checked separately. The width might have a 
+         // meaningful value even if the border style is "none". In that case the 
+         // value should be 0.
+         if(name.indexOf("border") > -1 && name.indexOf("Width") > -1) {
+             var borderStyleProp = name.substring(0,name.length-5) + "Style";
+             if(cs.getPropertyValue)
+                 var borderStyle = cs.getPropertyValue(borderStyleProp);
+             else // IE
+                 var borderStyle = cs[borderStyleProp];
+             if(borderStyle == "none")
+                 return "0px";
+         }
+         if(cs.getPropertyValue) {
+         
+             // Convert name to dashed format
+             name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
+             var ret = cs.getPropertyValue(name);
+             
+         } else {
+         
+             var ret = cs[name];
+             var style = elem.style;
+             // From the awesome hack by Dean Edwards
+             // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+             // If we're not dealing with a regular pixel number
+             // but a number that has a weird ending, we need to convert it to pixels
++                if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
++                    // Remember the original values
++                    var left = style.left, rsLeft = elem.runtimeStyle.left;
++    
++                    // Put in the new values to get a computed value out
++                    elem.runtimeStyle.left = cs.left;
++                    style.left = ret || 0;
++                    ret = style.pixelLeft + "px";
++    
++                    // Revert the changed values
++                    style.left = left;
++                    elem.runtimeStyle.left = rsLeft;
++                }
+             
+         }
+         
+         // Normalize margin values. This is not totally valid, but in most cases 
+         // it is what the user wants to know.
+         if(name.indexOf("margin") > -1 && ret == "auto") {
+             return "0px";
+         }
+         
+         // Some browsers return undefined width and height values as "auto", so
+         // we need to retrieve those ourselves.
+         if (name == "width" && ret == "auto") {
+             ret = elem.clientWidth + "px";
+         } else if (name == "height" && ret == "auto") {
+             ret = elem.clientHeight + "px";
+         }
+         return ret;
+         
+     }-*/;
+     public final int getIntProperty(String name) {
+         Integer parsed = parseInt(getProperty(name));
+         if (parsed != null) {
+             return parsed.intValue();
+         }
+         return 0;
+     }
+     /**
+      * Get current margin values from the DOM. The array order is the default
+      * CSS order: top, right, bottom, left.
+      */
+     public final int[] getMargin() {
+         int[] margin = { 0, 0, 0, 0 };
+         margin[0] = getIntProperty("marginTop");
+         margin[1] = getIntProperty("marginRight");
+         margin[2] = getIntProperty("marginBottom");
+         margin[3] = getIntProperty("marginLeft");
+         return margin;
+     }
+     /**
+      * Get current padding values from the DOM. The array order is the default
+      * CSS order: top, right, bottom, left.
+      */
+     public final int[] getPadding() {
+         int[] padding = { 0, 0, 0, 0 };
+         padding[0] = getIntProperty("paddingTop");
+         padding[1] = getIntProperty("paddingRight");
+         padding[2] = getIntProperty("paddingBottom");
+         padding[3] = getIntProperty("paddingLeft");
+         return padding;
+     }
+     /**
+      * Get current border values from the DOM. The array order is the default
+      * CSS order: top, right, bottom, left.
+      */
+     public final int[] getBorder() {
+         int[] border = { 0, 0, 0, 0 };
+         border[0] = getIntProperty("borderTopWidth");
+         border[1] = getIntProperty("borderRightWidth");
+         border[2] = getIntProperty("borderBottomWidth");
+         border[3] = getIntProperty("borderLeftWidth");
+         return border;
+     }
+     /**
+      * Takes a String value e.g. "12px" and parses that to int 12.
+      * 
+      * @param String
+      *            a value starting with a number
+      * @return int the value from the string before any non-numeric characters.
+      *         If the value cannot be parsed to a number, returns
+      *         <code>null</code>.
+      */
+     public static native Integer parseInt(final String value)
+     /*-{
+         var number = parseInt(value, 10);
+         if (isNaN(number))
+             return null;
+         else
+             // $entry not needed as function is not exported
+             return @java.lang.Integer::valueOf(I)(number);
+     }-*/;
+ }
index 0000000000000000000000000000000000000000,ce1d3c9c0102319dab7f734ef5dc691094bb45fe..b456010721bd75c4ead013d040d7130b6099e08e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1227 +1,1271 @@@
 -    private void layoutLater() {
+ /* 
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client;
+ import java.util.Collection;
+ import java.util.HashMap;
+ import java.util.HashSet;
+ import java.util.Map;
+ import java.util.Set;
+ import com.google.gwt.core.client.Duration;
+ import com.google.gwt.core.client.JsArrayString;
+ import com.google.gwt.dom.client.Element;
+ import com.google.gwt.dom.client.Style;
+ import com.google.gwt.dom.client.Style.Overflow;
+ import com.google.gwt.user.client.Timer;
+ import com.vaadin.client.MeasuredSize.MeasureResult;
+ import com.vaadin.client.ui.ManagedLayout;
+ import com.vaadin.client.ui.PostLayoutListener;
+ import com.vaadin.client.ui.SimpleManagedLayout;
+ import com.vaadin.client.ui.layout.ElementResizeEvent;
+ import com.vaadin.client.ui.layout.ElementResizeListener;
+ import com.vaadin.client.ui.layout.LayoutDependencyTree;
+ import com.vaadin.client.ui.notification.VNotification;
+ public class LayoutManager {
+     private static final String LOOP_ABORT_MESSAGE = "Aborting layout after 100 passes. This would probably be an infinite loop.";
+     private static final boolean debugLogging = false;
+     private ApplicationConnection connection;
+     private final Set<Element> measuredNonConnectorElements = new HashSet<Element>();
+     private final MeasuredSize nullSize = new MeasuredSize();
+     private LayoutDependencyTree currentDependencyTree;
+     private final Collection<ManagedLayout> needsHorizontalLayout = new HashSet<ManagedLayout>();
+     private final Collection<ManagedLayout> needsVerticalLayout = new HashSet<ManagedLayout>();
+     private final Collection<ComponentConnector> needsMeasure = new HashSet<ComponentConnector>();
+     private Collection<ComponentConnector> pendingOverflowFixes = new HashSet<ComponentConnector>();
+     private final Map<Element, Collection<ElementResizeListener>> elementResizeListeners = new HashMap<Element, Collection<ElementResizeListener>>();
+     private final Set<Element> listenersToFire = new HashSet<Element>();
+     private boolean layoutPending = false;
+     private Timer layoutTimer = new Timer() {
+         @Override
+         public void run() {
+             cancel();
+             layoutNow();
+         }
+     };
+     private boolean everythingNeedsMeasure = false;
+     public void setConnection(ApplicationConnection connection) {
+         if (this.connection != null) {
+             throw new RuntimeException(
+                     "LayoutManager connection can never be changed");
+         }
+         this.connection = connection;
+     }
+     /**
+      * Gets the layout manager associated with the given
+      * {@link ApplicationConnection}.
+      * 
+      * @param connection
+      *            the application connection to get a layout manager for
+      * @return the layout manager associated with the provided application
+      *         connection
+      */
+     public static LayoutManager get(ApplicationConnection connection) {
+         return connection.getLayoutManager();
+     }
+     /**
+      * Registers that a ManagedLayout is depending on the size of an Element.
+      * This causes this layout manager to measure the element in the beginning
+      * of every layout phase and call the appropriate layout method of the
+      * managed layout if the size of the element has changed.
+      * 
+      * @param owner
+      *            the ManagedLayout that depends on an element
+      * @param element
+      *            the Element that should be measured
+      */
+     public void registerDependency(ManagedLayout owner, Element element) {
+         MeasuredSize measuredSize = ensureMeasured(element);
+         setNeedsLayout(owner);
+         measuredSize.addDependent(owner.getConnectorId());
+     }
+     private MeasuredSize ensureMeasured(Element element) {
+         MeasuredSize measuredSize = getMeasuredSize(element, null);
+         if (measuredSize == null) {
+             measuredSize = new MeasuredSize();
+             if (ConnectorMap.get(connection).getConnector(element) == null) {
+                 measuredNonConnectorElements.add(element);
+             }
+             setMeasuredSize(element, measuredSize);
+         }
+         return measuredSize;
+     }
+     private boolean needsMeasure(Element e) {
+         if (connection.getConnectorMap().getConnectorId(e) != null) {
+             return true;
+         } else if (elementResizeListeners.containsKey(e)) {
+             return true;
+         } else if (getMeasuredSize(e, nullSize).hasDependents()) {
+             return true;
+         } else {
+             return false;
+         }
+     }
+     /**
+      * Assigns a measured size to an element. Method defined as protected to
+      * allow separate implementation for IE8.
+      * 
+      * @param element
+      *            the dom element to attach the measured size to
+      * @param measuredSize
+      *            the measured size to attach to the element. If
+      *            <code>null</code>, any previous measured size is removed.
+      */
+     protected native void setMeasuredSize(Element element,
+             MeasuredSize measuredSize)
+     /*-{
+         if (measuredSize) {
+             element.vMeasuredSize = measuredSize;
+         } else {
+             delete element.vMeasuredSize;
+         }
+     }-*/;
+     /**
+      * Gets the measured size for an element. Method defined as protected to
+      * allow separate implementation for IE8.
+      * 
+      * @param element
+      *            The element to get measured size for
+      * @param defaultSize
+      *            The size to return if no measured size could be found
+      * @return The measured size for the element or {@literal defaultSize}
+      */
+     protected native MeasuredSize getMeasuredSize(Element element,
+             MeasuredSize defaultSize)
+     /*-{
+         return element.vMeasuredSize || defaultSize;
+     }-*/;
+     private final MeasuredSize getMeasuredSize(ComponentConnector connector) {
+         Element element = connector.getWidget().getElement();
+         MeasuredSize measuredSize = getMeasuredSize(element, null);
+         if (measuredSize == null) {
+             measuredSize = new MeasuredSize();
+             setMeasuredSize(element, measuredSize);
+         }
+         return measuredSize;
+     }
+     /**
+      * Registers that a ManagedLayout is no longer depending on the size of an
+      * Element.
+      * 
+      * @see #registerDependency(ManagedLayout, Element)
+      * 
+      * @param owner
+      *            the ManagedLayout no longer depends on an element
+      * @param element
+      *            the Element that that no longer needs to be measured
+      */
+     public void unregisterDependency(ManagedLayout owner, Element element) {
+         MeasuredSize measuredSize = getMeasuredSize(element, null);
+         if (measuredSize == null) {
+             return;
+         }
+         measuredSize.removeDependent(owner.getConnectorId());
+         stopMeasuringIfUnecessary(element);
+     }
+     public boolean isLayoutRunning() {
+         return currentDependencyTree != null;
+     }
+     private void countLayout(Map<ManagedLayout, Integer> layoutCounts,
+             ManagedLayout layout) {
+         Integer count = layoutCounts.get(layout);
+         if (count == null) {
+             count = Integer.valueOf(0);
+         } else {
+             count = Integer.valueOf(count.intValue() + 1);
+         }
+         layoutCounts.put(layout, count);
+         if (count.intValue() > 2) {
+             VConsole.error(Util.getConnectorString(layout)
+                     + " has been layouted " + count.intValue() + " times");
+         }
+     }
++    public void layoutLater() {
+         if (!layoutPending) {
+             layoutPending = true;
+             layoutTimer.schedule(100);
+         }
+     }
+     public void layoutNow() {
+         if (isLayoutRunning()) {
+             throw new IllegalStateException(
+                     "Can't start a new layout phase before the previous layout phase ends.");
+         }
+         layoutPending = false;
+         try {
+             currentDependencyTree = new LayoutDependencyTree();
+             doLayout();
+         } finally {
+             currentDependencyTree = null;
+         }
+     }
+     private void doLayout() {
+         VConsole.log("Starting layout phase");
+         Map<ManagedLayout, Integer> layoutCounts = new HashMap<ManagedLayout, Integer>();
+         int passes = 0;
+         Duration totalDuration = new Duration();
+         for (ManagedLayout layout : needsHorizontalLayout) {
+             currentDependencyTree.setNeedsHorizontalLayout(layout, true);
+         }
+         for (ManagedLayout layout : needsVerticalLayout) {
+             currentDependencyTree.setNeedsVerticalLayout(layout, true);
+         }
+         needsHorizontalLayout.clear();
+         needsVerticalLayout.clear();
+         for (ComponentConnector connector : needsMeasure) {
+             currentDependencyTree.setNeedsMeasure(connector, true);
+         }
+         needsMeasure.clear();
+         measureNonConnectors();
+         VConsole.log("Layout init in " + totalDuration.elapsedMillis() + " ms");
+         while (true) {
+             Duration passDuration = new Duration();
+             passes++;
+             int measuredConnectorCount = measureConnectors(
+                     currentDependencyTree, everythingNeedsMeasure);
+             everythingNeedsMeasure = false;
+             if (measuredConnectorCount == 0) {
+                 VConsole.log("No more changes in pass " + passes);
+                 break;
+             }
+             int measureTime = passDuration.elapsedMillis();
+             VConsole.log("  Measured " + measuredConnectorCount
+                     + " elements in " + measureTime + " ms");
+             if (!listenersToFire.isEmpty()) {
+                 for (Element element : listenersToFire) {
+                     Collection<ElementResizeListener> listeners = elementResizeListeners
+                             .get(element);
+                     ElementResizeListener[] array = listeners
+                             .toArray(new ElementResizeListener[listeners.size()]);
+                     ElementResizeEvent event = new ElementResizeEvent(this,
+                             element);
+                     for (ElementResizeListener listener : array) {
+                         try {
+                             listener.onElementResize(event);
+                         } catch (RuntimeException e) {
+                             VConsole.error(e);
+                         }
+                     }
+                 }
+                 int measureListenerTime = passDuration.elapsedMillis();
+                 VConsole.log("  Fired resize listeners for  "
+                         + listenersToFire.size() + " elements in "
+                         + (measureListenerTime - measureTime) + " ms");
+                 measureTime = measuredConnectorCount;
+                 listenersToFire.clear();
+             }
+             FastStringSet updatedSet = FastStringSet.create();
+             while (currentDependencyTree.hasHorizontalConnectorToLayout()
+                     || currentDependencyTree.hasVerticaConnectorToLayout()) {
+                 for (ManagedLayout layout : currentDependencyTree
+                         .getHorizontalLayoutTargets()) {
+                     if (layout instanceof DirectionalManagedLayout) {
+                         currentDependencyTree
+                                 .markAsHorizontallyLayouted(layout);
+                         DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+                         try {
+                             cl.layoutHorizontally();
+                         } catch (RuntimeException e) {
+                             VConsole.error(e);
+                         }
+                         countLayout(layoutCounts, cl);
+                     } else {
+                         currentDependencyTree
+                                 .markAsHorizontallyLayouted(layout);
+                         currentDependencyTree.markAsVerticallyLayouted(layout);
+                         SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+                         try {
+                             rr.layout();
+                         } catch (RuntimeException e) {
+                             VConsole.error(e);
+                         }
+                         countLayout(layoutCounts, rr);
+                     }
+                     if (debugLogging) {
+                         updatedSet.add(layout.getConnectorId());
+                     }
+                 }
+                 for (ManagedLayout layout : currentDependencyTree
+                         .getVerticalLayoutTargets()) {
+                     if (layout instanceof DirectionalManagedLayout) {
+                         currentDependencyTree.markAsVerticallyLayouted(layout);
+                         DirectionalManagedLayout cl = (DirectionalManagedLayout) layout;
+                         try {
+                             cl.layoutVertically();
+                         } catch (RuntimeException e) {
+                             VConsole.error(e);
+                         }
+                         countLayout(layoutCounts, cl);
+                     } else {
+                         currentDependencyTree
+                                 .markAsHorizontallyLayouted(layout);
+                         currentDependencyTree.markAsVerticallyLayouted(layout);
+                         SimpleManagedLayout rr = (SimpleManagedLayout) layout;
+                         try {
+                             rr.layout();
+                         } catch (RuntimeException e) {
+                             VConsole.error(e);
+                         }
+                         countLayout(layoutCounts, rr);
+                     }
+                     if (debugLogging) {
+                         updatedSet.add(layout.getConnectorId());
+                     }
+                 }
+             }
+             if (debugLogging) {
+                 JsArrayString changedCids = updatedSet.dump();
+                 StringBuilder b = new StringBuilder("  ");
+                 b.append(changedCids.length());
+                 b.append(" requestLayout invocations in ");
+                 b.append(passDuration.elapsedMillis() - measureTime);
+                 b.append(" ms");
+                 if (changedCids.length() < 30) {
+                     for (int i = 0; i < changedCids.length(); i++) {
+                         if (i != 0) {
+                             b.append(", ");
+                         } else {
+                             b.append(": ");
+                         }
+                         String connectorString = changedCids.get(i);
+                         if (changedCids.length() < 10) {
+                             ServerConnector connector = ConnectorMap.get(
+                                     connection).getConnector(connectorString);
+                             connectorString = Util
+                                     .getConnectorString(connector);
+                         }
+                         b.append(connectorString);
+                     }
+                 }
+                 VConsole.log(b.toString());
+             }
+             VConsole.log("Pass " + passes + " completed in "
+                     + passDuration.elapsedMillis() + " ms");
+             if (passes > 100) {
+                 VConsole.log(LOOP_ABORT_MESSAGE);
+                 VNotification.createNotification(VNotification.DELAY_FOREVER)
+                         .show(LOOP_ABORT_MESSAGE, VNotification.CENTERED,
+                                 "error");
+                 break;
+             }
+         }
+         int postLayoutStart = totalDuration.elapsedMillis();
+         for (ComponentConnector connector : connection.getConnectorMap()
+                 .getComponentConnectors()) {
+             if (connector instanceof PostLayoutListener) {
+                 ((PostLayoutListener) connector).postLayout();
+             }
+         }
+         int postLayoutDone = (totalDuration.elapsedMillis() - postLayoutStart);
+         VConsole.log("Invoke post layout listeners in " + postLayoutDone
+                 + " ms");
+         cleanMeasuredSizes();
+         int cleaningDone = (totalDuration.elapsedMillis() - postLayoutDone);
+         VConsole.log("Cleaned old measured sizes in " + cleaningDone + "ms");
+         VConsole.log("Total layout phase time: "
+                 + totalDuration.elapsedMillis() + "ms");
+     }
+     private void logConnectorStatus(int connectorId) {
+         currentDependencyTree
+                 .logDependencyStatus((ComponentConnector) ConnectorMap.get(
+                         connection).getConnector(Integer.toString(connectorId)));
+     }
+     private int measureConnectors(LayoutDependencyTree layoutDependencyTree,
+             boolean measureAll) {
+         if (!pendingOverflowFixes.isEmpty()) {
+             Duration duration = new Duration();
+             HashMap<Element, String> originalOverflows = new HashMap<Element, String>();
+             HashSet<ComponentConnector> delayedOverflowFixes = new HashSet<ComponentConnector>();
+             // First set overflow to hidden (and save previous value so it can
+             // be restored later)
+             for (ComponentConnector componentConnector : pendingOverflowFixes) {
+                 // Delay the overflow fix if the involved connectors might still
+                 // change
+                 boolean connectorChangesExpected = !currentDependencyTree
+                         .noMoreChangesExpected(componentConnector);
+                 boolean parentChangesExcpected = componentConnector.getParent() instanceof ComponentConnector
+                         && !currentDependencyTree
+                                 .noMoreChangesExpected((ComponentConnector) componentConnector
+                                         .getParent());
+                 if (connectorChangesExpected || parentChangesExcpected) {
+                     delayedOverflowFixes.add(componentConnector);
+                     continue;
+                 }
+                 if (debugLogging) {
+                     VConsole.log("Doing overflow fix for "
+                             + Util.getConnectorString(componentConnector)
+                             + " in "
+                             + Util.getConnectorString(componentConnector
+                                     .getParent()));
+                 }
+                 Element parentElement = componentConnector.getWidget()
+                         .getElement().getParentElement();
+                 Style style = parentElement.getStyle();
+                 String originalOverflow = style.getOverflow();
+                 if (originalOverflow != null
+                         && !originalOverflows.containsKey(parentElement)) {
+                     // Store original value for restore, but only the first time
+                     // the value is changed
+                     originalOverflows.put(parentElement, originalOverflow);
+                 }
+                 style.setOverflow(Overflow.HIDDEN);
+             }
+             pendingOverflowFixes.removeAll(delayedOverflowFixes);
+             // Then ensure all scrolling elements are reflowed by measuring
+             for (ComponentConnector componentConnector : pendingOverflowFixes) {
+                 componentConnector.getWidget().getElement().getParentElement()
+                         .getOffsetHeight();
+             }
+             // Finally restore old overflow value and update bookkeeping
+             for (ComponentConnector componentConnector : pendingOverflowFixes) {
+                 Element parentElement = componentConnector.getWidget()
+                         .getElement().getParentElement();
+                 parentElement.getStyle().setProperty("overflow",
+                         originalOverflows.get(parentElement));
+                 layoutDependencyTree.setNeedsMeasure(componentConnector, true);
+             }
+             if (!pendingOverflowFixes.isEmpty()) {
+                 VConsole.log("Did overflow fix for "
+                         + pendingOverflowFixes.size() + " elements  in "
+                         + duration.elapsedMillis() + " ms");
+             }
+             pendingOverflowFixes = delayedOverflowFixes;
+         }
+         int measureCount = 0;
+         if (measureAll) {
+             ComponentConnector[] connectors = ConnectorMap.get(connection)
+                     .getComponentConnectors();
+             for (ComponentConnector connector : connectors) {
+                 measureConnector(connector);
+             }
+             for (ComponentConnector connector : connectors) {
+                 layoutDependencyTree.setNeedsMeasure(connector, false);
+             }
+             measureCount += connectors.length;
+         }
+         while (layoutDependencyTree.hasConnectorsToMeasure()) {
+             Collection<ComponentConnector> measureTargets = layoutDependencyTree
+                     .getMeasureTargets();
+             for (ComponentConnector connector : measureTargets) {
+                 measureConnector(connector);
+                 measureCount++;
+             }
+             for (ComponentConnector connector : measureTargets) {
+                 layoutDependencyTree.setNeedsMeasure(connector, false);
+             }
+         }
+         return measureCount;
+     }
+     private void measureConnector(ComponentConnector connector) {
+         Element element = connector.getWidget().getElement();
+         MeasuredSize measuredSize = getMeasuredSize(connector);
+         MeasureResult measureResult = measuredAndUpdate(element, measuredSize);
+         if (measureResult.isChanged()) {
+             onConnectorChange(connector, measureResult.isWidthChanged(),
+                     measureResult.isHeightChanged());
+         }
+     }
+     private void onConnectorChange(ComponentConnector connector,
+             boolean widthChanged, boolean heightChanged) {
+         setNeedsOverflowFix(connector);
+         if (heightChanged) {
+             currentDependencyTree.markHeightAsChanged(connector);
+         }
+         if (widthChanged) {
+             currentDependencyTree.markWidthAsChanged(connector);
+         }
+     }
+     private void setNeedsOverflowFix(ComponentConnector connector) {
+         // IE9 doesn't need the original fix, but for some reason it needs this
+         if (BrowserInfo.get().requiresOverflowAutoFix()
+                 || BrowserInfo.get().isIE9()) {
+             ComponentConnector scrollingBoundary = currentDependencyTree
+                     .getScrollingBoundary(connector);
+             if (scrollingBoundary != null) {
+                 pendingOverflowFixes.add(scrollingBoundary);
+             }
+         }
+     }
+     private void measureNonConnectors() {
+         for (Element element : measuredNonConnectorElements) {
+             measuredAndUpdate(element, getMeasuredSize(element, null));
+         }
+         VConsole.log("Measured " + measuredNonConnectorElements.size()
+                 + " non connector elements");
+     }
+     private MeasureResult measuredAndUpdate(Element element,
+             MeasuredSize measuredSize) {
+         MeasureResult measureResult = measuredSize.measure(element);
+         if (measureResult.isChanged()) {
+             notifyListenersAndDepdendents(element,
+                     measureResult.isWidthChanged(),
+                     measureResult.isHeightChanged());
+         }
+         return measureResult;
+     }
+     private void notifyListenersAndDepdendents(Element element,
+             boolean widthChanged, boolean heightChanged) {
+         assert widthChanged || heightChanged;
+         MeasuredSize measuredSize = getMeasuredSize(element, nullSize);
+         JsArrayString dependents = measuredSize.getDependents();
+         for (int i = 0; i < dependents.length(); i++) {
+             String pid = dependents.get(i);
+             ManagedLayout dependent = (ManagedLayout) connection
+                     .getConnectorMap().getConnector(pid);
+             if (dependent != null) {
+                 if (heightChanged) {
+                     currentDependencyTree.setNeedsVerticalLayout(dependent,
+                             true);
+                 }
+                 if (widthChanged) {
+                     currentDependencyTree.setNeedsHorizontalLayout(dependent,
+                             true);
+                 }
+             }
+         }
+         if (elementResizeListeners.containsKey(element)) {
+             listenersToFire.add(element);
+         }
+     }
+     private static boolean isManagedLayout(ComponentConnector connector) {
+         return connector instanceof ManagedLayout;
+     }
+     public void forceLayout() {
+         ConnectorMap connectorMap = connection.getConnectorMap();
+         ComponentConnector[] componentConnectors = connectorMap
+                 .getComponentConnectors();
+         for (ComponentConnector connector : componentConnectors) {
+             if (connector instanceof ManagedLayout) {
+                 setNeedsLayout((ManagedLayout) connector);
+             }
+         }
+         setEverythingNeedsMeasure();
+         layoutNow();
+     }
+     /**
+      * Marks that a ManagedLayout should be layouted in the next layout phase
+      * even if none of the elements managed by the layout have been resized.
+      * 
+      * @param layout
+      *            the managed layout that should be layouted
+      */
+     public final void setNeedsLayout(ManagedLayout layout) {
+         setNeedsHorizontalLayout(layout);
+         setNeedsVerticalLayout(layout);
+     }
+     /**
+      * Marks that a ManagedLayout should be layouted horizontally in the next
+      * layout phase even if none of the elements managed by the layout have been
+      * resized horizontally.
+      * 
+      * For SimpleManagedLayout which is always layouted in both directions, this
+      * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+      * 
+      * @param layout
+      *            the managed layout that should be layouted
+      */
+     public final void setNeedsHorizontalLayout(ManagedLayout layout) {
+         needsHorizontalLayout.add(layout);
+     }
+     /**
+      * Marks that a ManagedLayout should be layouted vertically in the next
+      * layout phase even if none of the elements managed by the layout have been
+      * resized vertically.
+      * 
+      * For SimpleManagedLayout which is always layouted in both directions, this
+      * has the same effect as {@link #setNeedsLayout(ManagedLayout)}.
+      * 
+      * @param layout
+      *            the managed layout that should be layouted
+      */
+     public final void setNeedsVerticalLayout(ManagedLayout layout) {
+         needsVerticalLayout.add(layout);
+     }
+     /**
+      * Gets the outer height (including margins, paddings and borders) of the
+      * given element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * -1 is returned if the element has not been measured. If 0 is returned, it
+      * might indicate that the element is not attached to the DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured outer height (including margins, paddings and
+      *         borders) of the element in pixels.
+      */
+     public final int getOuterHeight(Element element) {
+         return getMeasuredSize(element, nullSize).getOuterHeight();
+     }
+     /**
+      * Gets the outer width (including margins, paddings and borders) of the
+      * given element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * -1 is returned if the element has not been measured. If 0 is returned, it
+      * might indicate that the element is not attached to the DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured outer width (including margins, paddings and
+      *         borders) of the element in pixels.
+      */
+     public final int getOuterWidth(Element element) {
+         return getMeasuredSize(element, nullSize).getOuterWidth();
+     }
+     /**
+      * Gets the inner height (excluding margins, paddings and borders) of the
+      * given element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * -1 is returned if the element has not been measured. If 0 is returned, it
+      * might indicate that the element is not attached to the DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured inner height (excluding margins, paddings and
+      *         borders) of the element in pixels.
+      */
+     public final int getInnerHeight(Element element) {
+         return getMeasuredSize(element, nullSize).getInnerHeight();
+     }
+     /**
+      * Gets the inner width (excluding margins, paddings and borders) of the
+      * given element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * -1 is returned if the element has not been measured. If 0 is returned, it
+      * might indicate that the element is not attached to the DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured inner width (excluding margins, paddings and
+      *         borders) of the element in pixels.
+      */
+     public final int getInnerWidth(Element element) {
+         return getMeasuredSize(element, nullSize).getInnerWidth();
+     }
+     /**
+      * Gets the border height (top border + bottom border) of the given element,
+      * provided that it has been measured. These elements are guaranteed to be
+      * measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured border height (top border + bottom border) of the
+      *         element in pixels.
+      */
+     public final int getBorderHeight(Element element) {
+         return getMeasuredSize(element, nullSize).getBorderHeight();
+     }
+     /**
+      * Gets the padding height (top padding + bottom padding) of the given
+      * element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured padding height (top padding + bottom padding) of the
+      *         element in pixels.
+      */
+     public int getPaddingHeight(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingHeight();
+     }
+     /**
+      * Gets the border width (left border + right border) of the given element,
+      * provided that it has been measured. These elements are guaranteed to be
+      * measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured border width (left border + right border) of the
+      *         element in pixels.
+      */
+     public int getBorderWidth(Element element) {
+         return getMeasuredSize(element, nullSize).getBorderWidth();
+     }
+     /**
+      * Gets the padding width (left padding + right padding) of the given
+      * element, provided that it has been measured. These elements are
+      * guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured padding width (left padding + right padding) of the
+      *         element in pixels.
+      */
+     public int getPaddingWidth(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingWidth();
+     }
+     /**
+      * Gets the top padding of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured top padding of the element in pixels.
+      */
+     public int getPaddingTop(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingTop();
+     }
+     /**
+      * Gets the left padding of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured left padding of the element in pixels.
+      */
+     public int getPaddingLeft(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingLeft();
+     }
+     /**
+      * Gets the bottom padding of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured bottom padding of the element in pixels.
+      */
+     public int getPaddingBottom(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingBottom();
+     }
+     /**
+      * Gets the right padding of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured right padding of the element in pixels.
+      */
+     public int getPaddingRight(Element element) {
+         return getMeasuredSize(element, nullSize).getPaddingRight();
+     }
+     /**
+      * Gets the top margin of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured top margin of the element in pixels.
+      */
+     public int getMarginTop(Element element) {
+         return getMeasuredSize(element, nullSize).getMarginTop();
+     }
+     /**
+      * Gets the right margin of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured right margin of the element in pixels.
+      */
+     public int getMarginRight(Element element) {
+         return getMeasuredSize(element, nullSize).getMarginRight();
+     }
+     /**
+      * Gets the bottom margin of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured bottom margin of the element in pixels.
+      */
+     public int getMarginBottom(Element element) {
+         return getMeasuredSize(element, nullSize).getMarginBottom();
+     }
+     /**
+      * Gets the left margin of the given element, provided that it has been
+      * measured. These elements are guaranteed to be measured:
+      * <ul>
+      * <li>ManagedLayotus and their child Connectors
+      * <li>Elements for which there is at least one ElementResizeListener
+      * <li>Elements for which at least one ManagedLayout has registered a
+      * dependency
+      * </ul>
+      * 
+      * A negative number is returned if the element has not been measured. If 0
+      * is returned, it might indicate that the element is not attached to the
+      * DOM.
+      * 
+      * @param element
+      *            the element to get the measured size for
+      * @return the measured left margin of the element in pixels.
+      */
+     public int getMarginLeft(Element element) {
+         return getMeasuredSize(element, nullSize).getMarginLeft();
+     }
++    /**
++     * Gets the combined top & bottom margin of the given element, provided that
++     * they have been measured. These elements are guaranteed to be measured:
++     * <ul>
++     * <li>ManagedLayotus and their child Connectors
++     * <li>Elements for which there is at least one ElementResizeListener
++     * <li>Elements for which at least one ManagedLayout has registered a
++     * dependency
++     * </ul>
++     * 
++     * A negative number is returned if the element has not been measured. If 0
++     * is returned, it might indicate that the element is not attached to the
++     * DOM.
++     * 
++     * @param element
++     *            the element to get the measured margin for
++     * @return the measured top+bottom margin of the element in pixels.
++     */
++    public int getMarginHeight(Element element) {
++        return getMarginTop(element) + getMarginBottom(element);
++    }
++
++    /**
++     * Gets the combined left & right margin of the given element, provided that
++     * they have been measured. These elements are guaranteed to be measured:
++     * <ul>
++     * <li>ManagedLayotus and their child Connectors
++     * <li>Elements for which there is at least one ElementResizeListener
++     * <li>Elements for which at least one ManagedLayout has registered a
++     * dependency
++     * </ul>
++     * 
++     * A negative number is returned if the element has not been measured. If 0
++     * is returned, it might indicate that the element is not attached to the
++     * DOM.
++     * 
++     * @param element
++     *            the element to get the measured margin for
++     * @return the measured left+right margin of the element in pixels.
++     */
++    public int getMarginWidth(Element element) {
++        return getMarginLeft(element) + getMarginRight(element);
++    }
++
+     /**
+      * Registers the outer height (including margins, borders and paddings) of a
+      * component. This can be used as an optimization by ManagedLayouts; by
+      * informing the LayoutManager about what size a component will have, the
+      * layout propagation can continue directly without first measuring the
+      * potentially resized elements.
+      * 
+      * @param component
+      *            the component for which the size is reported
+      * @param outerHeight
+      *            the new outer height (including margins, borders and paddings)
+      *            of the component in pixels
+      */
+     public void reportOuterHeight(ComponentConnector component, int outerHeight) {
+         MeasuredSize measuredSize = getMeasuredSize(component);
+         if (isLayoutRunning()) {
+             boolean heightChanged = measuredSize.setOuterHeight(outerHeight);
+             if (heightChanged) {
+                 onConnectorChange(component, false, true);
+                 notifyListenersAndDepdendents(component.getWidget()
+                         .getElement(), false, true);
+             }
+             currentDependencyTree.setNeedsVerticalMeasure(component, false);
+         } else if (measuredSize.getOuterHeight() != outerHeight) {
+             setNeedsMeasure(component);
+         }
+     }
+     /**
+      * Registers the height reserved for a relatively sized component. This can
+      * be used as an optimization by ManagedLayouts; by informing the
+      * LayoutManager about what size a component will have, the layout
+      * propagation can continue directly without first measuring the potentially
+      * resized elements.
+      * 
+      * @param component
+      *            the relatively sized component for which the size is reported
+      * @param assignedHeight
+      *            the inner height of the relatively sized component's parent
+      *            element in pixels
+      */
+     public void reportHeightAssignedToRelative(ComponentConnector component,
+             int assignedHeight) {
+         assert component.isRelativeHeight();
+         float percentSize = parsePercent(component.getState().getHeight());
+         int effectiveHeight = Math.round(assignedHeight * (percentSize / 100));
+         reportOuterHeight(component, effectiveHeight);
+     }
+     /**
+      * Registers the width reserved for a relatively sized component. This can
+      * be used as an optimization by ManagedLayouts; by informing the
+      * LayoutManager about what size a component will have, the layout
+      * propagation can continue directly without first measuring the potentially
+      * resized elements.
+      * 
+      * @param component
+      *            the relatively sized component for which the size is reported
+      * @param assignedWidth
+      *            the inner width of the relatively sized component's parent
+      *            element in pixels
+      */
+     public void reportWidthAssignedToRelative(ComponentConnector component,
+             int assignedWidth) {
+         assert component.isRelativeWidth();
+         float percentSize = parsePercent(component.getState().getWidth());
+         int effectiveWidth = Math.round(assignedWidth * (percentSize / 100));
+         reportOuterWidth(component, effectiveWidth);
+     }
+     private static float parsePercent(String size) {
+         return Float.parseFloat(size.substring(0, size.length() - 1));
+     }
+     /**
+      * Registers the outer width (including margins, borders and paddings) of a
+      * component. This can be used as an optimization by ManagedLayouts; by
+      * informing the LayoutManager about what size a component will have, the
+      * layout propagation can continue directly without first measuring the
+      * potentially resized elements.
+      * 
+      * @param component
+      *            the component for which the size is reported
+      * @param outerWidth
+      *            the new outer width (including margins, borders and paddings)
+      *            of the component in pixels
+      */
+     public void reportOuterWidth(ComponentConnector component, int outerWidth) {
+         MeasuredSize measuredSize = getMeasuredSize(component);
+         if (isLayoutRunning()) {
+             boolean widthChanged = measuredSize.setOuterWidth(outerWidth);
+             if (widthChanged) {
+                 onConnectorChange(component, true, false);
+                 notifyListenersAndDepdendents(component.getWidget()
+                         .getElement(), true, false);
+             }
+             currentDependencyTree.setNeedsHorizontalMeasure(component, false);
+         } else if (measuredSize.getOuterWidth() != outerWidth) {
+             setNeedsMeasure(component);
+         }
+     }
+     /**
+      * Adds a listener that will be notified whenever the size of a specific
+      * element changes. Adding a listener to an element also ensures that all
+      * sizes for that element will be available starting from the next layout
+      * phase.
+      * 
+      * @param element
+      *            the element that should be checked for size changes
+      * @param listener
+      *            an ElementResizeListener that will be informed whenever the
+      *            size of the target element has changed
+      */
+     public void addElementResizeListener(Element element,
+             ElementResizeListener listener) {
+         Collection<ElementResizeListener> listeners = elementResizeListeners
+                 .get(element);
+         if (listeners == null) {
+             listeners = new HashSet<ElementResizeListener>();
+             elementResizeListeners.put(element, listeners);
+             ensureMeasured(element);
+         }
+         listeners.add(listener);
+     }
+     /**
+      * Removes an element resize listener from the provided element. This might
+      * cause this LayoutManager to stop tracking the size of the element if no
+      * other sources are interested in the size.
+      * 
+      * @param element
+      *            the element to which the element resize listener was
+      *            previously added
+      * @param listener
+      *            the ElementResizeListener that should no longer get informed
+      *            about size changes to the target element.
+      */
+     public void removeElementResizeListener(Element element,
+             ElementResizeListener listener) {
+         Collection<ElementResizeListener> listeners = elementResizeListeners
+                 .get(element);
+         if (listeners != null) {
+             listeners.remove(listener);
+             if (listeners.isEmpty()) {
+                 elementResizeListeners.remove(element);
+                 stopMeasuringIfUnecessary(element);
+             }
+         }
+     }
+     private void stopMeasuringIfUnecessary(Element element) {
+         if (!needsMeasure(element)) {
+             measuredNonConnectorElements.remove(element);
+             setMeasuredSize(element, null);
+         }
+     }
+     /**
+      * Informs this LayoutManager that the size of a component might have
+      * changed. If there is no upcoming layout phase, a new layout phase is
+      * scheduled. This method should be used whenever a size might have changed
+      * from outside of Vaadin's normal update phase, e.g. when an icon has been
+      * loaded or when the user resizes some part of the UI using the mouse.
+      * 
+      * @param component
+      *            the component whose size might have changed.
+      */
+     public void setNeedsMeasure(ComponentConnector component) {
+         if (isLayoutRunning()) {
+             currentDependencyTree.setNeedsMeasure(component, true);
+         } else {
+             needsMeasure.add(component);
+             layoutLater();
+         }
+     }
+     public void setEverythingNeedsMeasure() {
+         everythingNeedsMeasure = true;
+     }
+     /**
+      * Clean measured sizes which are no longer needed. Only for IE8.
+      */
+     protected void cleanMeasuredSizes() {
+     }
+ }
index 0000000000000000000000000000000000000000,218055e3b89c3689cbd00c3a5c64ebad649f48e2..85eeab29ff0df5f8ee02837167ed3a416fd77d52
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,155 +1,177 @@@
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client;
+ import java.util.Collection;
+ import java.util.List;
+ import com.google.gwt.event.shared.GwtEvent;
+ import com.google.web.bindery.event.shared.HandlerRegistration;
+ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.communication.ClientRpc;
+ import com.vaadin.shared.communication.SharedState;
+ /**
+  * Interface implemented by all client side classes that can be communicate with
+  * the server. Classes implementing this interface are initialized by the
+  * framework when needed and have the ability to communicate with the server.
+  * 
+  * @author Vaadin Ltd
+  * @since 7.0.0
+  */
+ public interface ServerConnector extends Connector {
+     /**
+      * Gets ApplicationConnection instance that created this connector.
+      * 
+      * @return The ApplicationConnection as set by
+      *         {@link #doInit(String, ApplicationConnection)}
+      */
+     public ApplicationConnection getConnection();
+     /**
+      * Tests whether the connector is enabled or not. This method checks that
+      * the connector is enabled in context, i.e. if the parent connector is
+      * disabled, this method must return false.
+      * 
+      * @return true if the connector is enabled, false otherwise
+      */
+     public boolean isEnabled();
+     /**
+      * 
+      * Called once by the framework to initialize the connector.
+      * <p>
+      * Note that the shared state is not yet available at this point nor any
+      * hierarchy information.
+      */
+     public void doInit(String connectorId, ApplicationConnection connection);
+     /**
+      * For internal use by the framework: returns the registered RPC
+      * implementations for an RPC interface identifier.
+      * 
+      * TODO interface identifier type or format may change
+      * 
+      * @param rpcInterfaceId
+      *            RPC interface identifier: fully qualified interface type name
+      * @return RPC interface implementations registered for an RPC interface,
+      *         not null
+      */
+     public <T extends ClientRpc> Collection<T> getRpcImplementations(
+             String rpcInterfaceId);
+     /**
+      * Adds a handler that is called whenever any part of the state has been
+      * updated by the server.
+      * 
+      * @param handler
+      *            The handler that should be added.
+      * @return A handler registration reference that can be used to unregister
+      *         the handler
+      */
+     public HandlerRegistration addStateChangeHandler(StateChangeHandler handler);
++    /**
++     * Removes a handler that is called whenever any part of the state has been
++     * updated by the server.
++     * 
++     * @param handler
++     *            The handler that should be removed.
++     */
++    public void removeStateChangeHandler(StateChangeHandler handler);
++
+     /**
+      * Adds a handler that is called whenever the given part of the state has
+      * been updated by the server.
+      * 
+      * @param propertyName
+      *            the name of the property for which the handler should be
+      *            called
+      * @param handler
+      *            The handler that should be added.
+      * @return A handler registration reference that can be used to unregister
+      *         the handler
+      */
+     public HandlerRegistration addStateChangeHandler(String propertyName,
+             StateChangeHandler handler);
++    /**
++     * Removes a handler that is called whenever any part of the state has been
++     * updated by the server.
++     * 
++     * @param propertyName
++     *            the name of the property for which the handler should be
++     *            called
++     * @param handler
++     *            The handler that should be removed.
++     */
++    public void removeStateChangeHandler(String propertyName,
++            StateChangeHandler handler);
++
+     /**
+      * Sends the given event to all registered handlers.
+      * 
+      * @param event
+      *            The event to send.
+      */
+     public void fireEvent(GwtEvent<?> event);
+     /**
+      * Event called when connector has been unregistered.
+      */
+     public void onUnregister();
+     /**
+      * Returns the parent of this connector. Can be null for only the root
+      * connector.
+      * 
+      * @return The parent of this connector, as set by
+      *         {@link #setParent(ServerConnector)}.
+      */
+     @Override
+     public ServerConnector getParent();
+     /**
+      * Sets the parent for this connector. This method should only be called by
+      * the framework to ensure that the connector hierarchy on the client side
+      * and the server side are in sync.
+      * <p>
+      * Note that calling this method does not fire a
+      * {@link ConnectorHierarchyChangeEvent}. The event is fired only when the
+      * whole hierarchy has been updated.
+      * 
+      * @param parent
+      *            The new parent of the connector
+      */
+     public void setParent(ServerConnector parent);
+     public void updateEnabledState(boolean enabledState);
+     public void setChildren(List<ServerConnector> children);
+     public List<ServerConnector> getChildren();
+     /**
+      * Gets the current shared state of the connector.
+      * 
+      * @since 7.0.
+      * @return state The shared state object. Can be any sub type of
+      *         {@link SharedState}. Never null.
+      */
+     public SharedState getState();
+ }
index 0000000000000000000000000000000000000000,18c13b4b7f58b84e0b0623dcfa7a933a9208cc77..c886d2f94cf283ca82461cb55f07ce3ab6854d8c
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,441 +1,442 @@@
 -        implements ComponentConnector {
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client.ui;
+ import java.util.ArrayList;
+ import java.util.List;
+ import java.util.Set;
+ import com.google.gwt.dom.client.Element;
+ import com.google.gwt.user.client.ui.Focusable;
+ import com.google.gwt.user.client.ui.HasEnabled;
+ import com.google.gwt.user.client.ui.Widget;
+ import com.vaadin.client.ApplicationConnection;
+ import com.vaadin.client.ComponentConnector;
+ import com.vaadin.client.ComponentContainerConnector;
+ import com.vaadin.client.ConnectorMap;
+ import com.vaadin.client.LayoutManager;
+ import com.vaadin.client.ServerConnector;
+ import com.vaadin.client.TooltipInfo;
+ import com.vaadin.client.UIDL;
+ import com.vaadin.client.Util;
+ import com.vaadin.client.VConsole;
+ import com.vaadin.client.communication.StateChangeEvent;
+ import com.vaadin.client.metadata.NoDataException;
+ import com.vaadin.client.metadata.Type;
+ import com.vaadin.client.metadata.TypeData;
+ import com.vaadin.client.ui.UI.UIConnector;
+ import com.vaadin.client.ui.datefield.PopupDateFieldConnector;
+ import com.vaadin.shared.ComponentConstants;
+ import com.vaadin.shared.ComponentState;
+ import com.vaadin.shared.Connector;
+ import com.vaadin.shared.ui.TabIndexState;
++import com.vaadin.ui.themes.BaseTheme;
+ public abstract class AbstractComponentConnector extends AbstractConnector
 -        // Set v-connector style names for the widget
 -        getWidget().setStyleName("v-connector", true);
++implements ComponentConnector {
+     private Widget widget;
+     private String lastKnownWidth = "";
+     private String lastKnownHeight = "";
+     /**
+      * The style names from getState().getStyles() which are currently applied
+      * to the widget.
+      */
+     protected List<String> styleNames = new ArrayList<String>();
+     /**
+      * Default constructor
+      */
+     public AbstractComponentConnector() {
+     }
+     @Override
+     protected void init() {
+         super.init();
+         getConnection().getVTooltip().connectHandlersToWidget(getWidget());
 -                    e);
++        // Set the core 'v' style name for the widget
++        getWidget().setStyleName(BaseTheme.UI_WIDGET, true);
+     }
+     /**
+      * Creates and returns the widget for this VPaintableWidget. This method
+      * should only be called once when initializing the paintable.
+      * 
+      * @return
+      */
+     protected Widget createWidget() {
+         Type type = TypeData.getType(getClass());
+         try {
+             Type widgetType = type.getMethod("getWidget").getReturnType();
+             Object instance = widgetType.createInstance();
+             return (Widget) instance;
+         } catch (NoDataException e) {
+             throw new IllegalStateException(
+                     "There is no information about the widget for "
+                             + Util.getSimpleName(this)
+                             + ". Did you remember to compile the right widgetset?",
++                            e);
+         }
+     }
+     /**
+      * Returns the widget associated with this paintable. The widget returned by
+      * this method must not changed during the life time of the paintable.
+      * 
+      * @return The widget associated with this paintable
+      */
+     @Override
+     public Widget getWidget() {
+         if (widget == null) {
+             widget = createWidget();
+         }
+         return widget;
+     }
+     @Deprecated
+     public static boolean isRealUpdate(UIDL uidl) {
+         return !uidl.hasAttribute("cached");
+     }
+     @Override
+     public ComponentState getState() {
+         return (ComponentState) super.getState();
+     }
+     @Override
+     public void onStateChanged(StateChangeEvent stateChangeEvent) {
+         ConnectorMap paintableMap = ConnectorMap.get(getConnection());
+         if (getState().getId() != null) {
+             getWidget().getElement().setId(getState().getId());
+         } else {
+             getWidget().getElement().removeAttribute("id");
+         }
+         /*
+          * Disabled state may affect (override) tabindex so the order must be
+          * first setting tabindex, then enabled state (through super
+          * implementation).
+          */
+         if (getState() instanceof TabIndexState
+                 && getWidget() instanceof Focusable) {
+             ((Focusable) getWidget()).setTabIndex(((TabIndexState) getState())
+                     .getTabIndex());
+         }
+         super.onStateChanged(stateChangeEvent);
+         // Style names
+         updateWidgetStyleNames();
+         // Set captions
+         if (delegateCaptionHandling()) {
+             ServerConnector parent = getParent();
+             if (parent instanceof ComponentContainerConnector) {
+                 ((ComponentContainerConnector) parent).updateCaption(this);
+             } else if (parent == null && !(this instanceof UIConnector)) {
+                 VConsole.error("Parent of connector "
+                         + Util.getConnectorString(this)
+                         + " is null. This is typically an indication of a broken component hierarchy");
+             }
+         }
+         /*
+          * updateComponentSize need to be after caption update so caption can be
+          * taken into account
+          */
+         updateComponentSize();
+     }
+     @Override
+     public void setWidgetEnabled(boolean widgetEnabled) {
+         // add or remove v-disabled style name from the widget
+         setWidgetStyleName(ApplicationConnection.DISABLED_CLASSNAME,
+                 !widgetEnabled);
+         if (getWidget() instanceof HasEnabled) {
+             // set widget specific enabled state
+             ((HasEnabled) getWidget()).setEnabled(widgetEnabled);
+             // make sure the caption has or has not v-disabled style
+             if (delegateCaptionHandling()) {
+                 ServerConnector parent = getParent();
+                 if (parent instanceof ComponentContainerConnector) {
+                     ((ComponentContainerConnector) parent).updateCaption(this);
+                 } else if (parent == null && !(this instanceof UIConnector)) {
+                     VConsole.error("Parent of connector "
+                             + Util.getConnectorString(this)
+                             + " is null. This is typically an indication of a broken component hierarchy");
+                 }
+             }
+         }
+     }
+     private void updateComponentSize() {
+         String newWidth = getState().getWidth();
+         String newHeight = getState().getHeight();
+         // Parent should be updated if either dimension changed between relative
+         // and non-relative
+         if (newWidth.endsWith("%") != lastKnownWidth.endsWith("%")) {
+             Connector parent = getParent();
+             if (parent instanceof ManagedLayout) {
+                 getLayoutManager().setNeedsHorizontalLayout(
+                         (ManagedLayout) parent);
+             }
+         }
+         if (newHeight.endsWith("%") != lastKnownHeight.endsWith("%")) {
+             Connector parent = getParent();
+             if (parent instanceof ManagedLayout) {
+                 getLayoutManager().setNeedsVerticalLayout(
+                         (ManagedLayout) parent);
+             }
+         }
+         lastKnownWidth = newWidth;
+         lastKnownHeight = newHeight;
+         // Set defined sizes
+         Widget widget = getWidget();
+         widget.setStyleName("v-has-width", !isUndefinedWidth());
+         widget.setStyleName("v-has-height", !isUndefinedHeight());
+         widget.setHeight(newHeight);
+         widget.setWidth(newWidth);
+     }
+     @Override
+     public boolean isRelativeHeight() {
+         return getState().getHeight().endsWith("%");
+     }
+     @Override
+     public boolean isRelativeWidth() {
+         return getState().getWidth().endsWith("%");
+     }
+     @Override
+     public boolean isUndefinedHeight() {
+         return getState().getHeight().length() == 0;
+     }
+     @Override
+     public boolean isUndefinedWidth() {
+         return getState().getWidth().length() == 0;
+     }
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.ComponentConnector#delegateCaptionHandling ()
+      */
+     @Override
+     public boolean delegateCaptionHandling() {
+         return true;
+     }
+     /**
+      * Updates the user defined, read-only and error style names for the widget
+      * based the shared state. User defined style names are prefixed with the
+      * primary style name of the widget returned by {@link #getWidget()}
+      * <p>
+      * This method can be overridden to provide additional style names for the
+      * component, for example see
+      * {@link AbstractFieldConnector#updateWidgetStyleNames()}
+      * </p>
+      */
+     protected void updateWidgetStyleNames() {
+         ComponentState state = getState();
+         String primaryStyleName = getWidget().getStylePrimaryName();
+         // should be in AbstractFieldConnector ?
+         // add / remove read-only style name
+         setWidgetStyleName("v-readonly", isReadOnly());
+         // add / remove error style name
+         setWidgetStyleNameWithPrefix(primaryStyleName,
+                 ApplicationConnection.ERROR_CLASSNAME_EXT,
+                 null != state.getErrorMessage());
+         // add additional user defined style names as class names, prefixed with
+         // component default class name. remove nonexistent style names.
+         if (state.hasStyles()) {
+             // add new style names
+             List<String> newStyles = new ArrayList<String>();
+             newStyles.addAll(state.getStyles());
+             newStyles.removeAll(styleNames);
+             for (String newStyle : newStyles) {
+                 setWidgetStyleName(newStyle, true);
+                 setWidgetStyleNameWithPrefix(primaryStyleName + "-", newStyle,
+                         true);
+             }
+             // remove nonexistent style names
+             styleNames.removeAll(state.getStyles());
+             for (String oldStyle : styleNames) {
+                 setWidgetStyleName(oldStyle, false);
+                 setWidgetStyleNameWithPrefix(primaryStyleName + "-", oldStyle,
+                         false);
+             }
+             styleNames.clear();
+             styleNames.addAll(state.getStyles());
+         } else {
+             // remove all old style names
+             for (String oldStyle : styleNames) {
+                 setWidgetStyleName(oldStyle, false);
+                 setWidgetStyleNameWithPrefix(primaryStyleName + "-", oldStyle,
+                         false);
+             }
+             styleNames.clear();
+         }
+     }
+     /**
+      * This is used to add / remove state related style names from the widget.
+      * <p>
+      * Override this method for example if the style name given here should be
+      * updated in another widget in addition to the one returned by the
+      * {@link #getWidget()}.
+      * </p>
+      * 
+      * @param styleName
+      *            the style name to be added or removed
+      * @param add
+      *            <code>true</code> to add the given style, <code>false</code>
+      *            to remove it
+      */
+     protected void setWidgetStyleName(String styleName, boolean add) {
+         getWidget().setStyleName(styleName, add);
+     }
+     /**
+      * This is used to add / remove state related prefixed style names from the
+      * widget.
+      * <p>
+      * Override this method if the prefixed style name given here should be
+      * updated in another widget in addition to the one returned by the
+      * <code>Connector</code>'s {@link #getWidget()}, or if the prefix should be
+      * different. For example see
+      * {@link PopupDateFieldConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
+      * </p>
+      * 
+      * @param styleName
+      *            the style name to be added or removed
+      * @param add
+      *            <code>true</code> to add the given style, <code>false</code>
+      *            to remove it
+      * @deprecated This will be removed once styles are no longer added with
+      *             prefixes.
+      */
+     @Deprecated
+     protected void setWidgetStyleNameWithPrefix(String prefix,
+             String styleName, boolean add) {
+         if (!styleName.startsWith("-")) {
+             if (!prefix.endsWith("-")) {
+                 prefix += "-";
+             }
+         } else {
+             if (prefix.endsWith("-")) {
+                 styleName.replaceFirst("-", "");
+             }
+         }
+         getWidget().setStyleName(prefix + styleName, add);
+     }
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.ComponentConnector#isReadOnly()
+      */
+     @Override
+     @Deprecated
+     public boolean isReadOnly() {
+         return getState().isReadOnly();
+     }
+     @Override
+     public LayoutManager getLayoutManager() {
+         return LayoutManager.get(getConnection());
+     }
+     /**
+      * Checks if there is a registered server side listener for the given event
+      * identifier.
+      * 
+      * @param eventIdentifier
+      *            The identifier to check for
+      * @return true if an event listener has been registered with the given
+      *         event identifier on the server side, false otherwise
+      */
+     @Override
+     public boolean hasEventListener(String eventIdentifier) {
+         Set<String> reg = getState().getRegisteredEventListeners();
+         return (reg != null && reg.contains(eventIdentifier));
+     }
+     @Override
+     public void updateEnabledState(boolean enabledState) {
+         super.updateEnabledState(enabledState);
+         setWidgetEnabled(isEnabled());
+     }
+     @Override
+     public void onUnregister() {
+         super.onUnregister();
+         // Show an error if widget is still attached to DOM. It should never be
+         // at this point.
+         if (getWidget() != null && getWidget().isAttached()) {
+             getWidget().removeFromParent();
+             VConsole.error("Widget is still attached to the DOM after the connector ("
+                     + Util.getConnectorString(this)
+                     + ") has been unregistered. Widget was removed.");
+         }
+     }
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.ComponentConnector#getTooltipInfo(com.
+      * google.gwt.dom.client.Element)
+      */
+     @Override
+     public TooltipInfo getTooltipInfo(Element element) {
+         return new TooltipInfo(getState().getDescription(), getState()
+                 .getErrorMessage());
+     }
+     /**
+      * Gets the icon set for this component.
+      * 
+      * @return the URL of the icon, or <code>null</code> if no icon has been
+      *         defined.
+      */
+     protected String getIcon() {
+         return getResourceUrl(ComponentConstants.ICON_RESOURCE);
+     }
+ }
index 0000000000000000000000000000000000000000,989867d40e9b14d9dc84cccc1bd3448eb792fb37..2820b7b2c1705c2964708ac1544110449baab018
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,373 +1,385 @@@
 -/* 
++/*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client.ui;
+ import java.util.ArrayList;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.HashMap;
+ import java.util.List;
+ import java.util.Map;
+ import com.google.gwt.event.shared.GwtEvent;
+ import com.google.gwt.event.shared.HandlerManager;
+ import com.google.web.bindery.event.shared.HandlerRegistration;
+ import com.vaadin.client.ApplicationConnection;
+ import com.vaadin.client.ServerConnector;
+ import com.vaadin.client.Util;
+ import com.vaadin.client.VConsole;
+ import com.vaadin.client.communication.StateChangeEvent;
+ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+ import com.vaadin.client.metadata.NoDataException;
+ import com.vaadin.client.metadata.Type;
+ import com.vaadin.client.metadata.TypeData;
+ import com.vaadin.shared.communication.ClientRpc;
+ import com.vaadin.shared.communication.SharedState;
+ import com.vaadin.shared.communication.URLReference;
+ /**
+  * An abstract implementation of Connector.
+  * 
+  * @author Vaadin Ltd
+  * @since 7.0.0
+  * 
+  */
+ public abstract class AbstractConnector implements ServerConnector,
 -        StateChangeHandler {
++StateChangeHandler {
+     private ApplicationConnection connection;
+     private String id;
+     private HandlerManager handlerManager;
+     private Map<String, HandlerManager> statePropertyHandlerManagers;
+     private Map<String, Collection<ClientRpc>> rpcImplementations;
+     private final boolean debugLogging = false;
+     private SharedState state;
+     private ServerConnector parent;
+     /**
+      * Temporary storage for last enabled state to be able to see if it has
+      * changed. Can be removed once we are able to listen specifically for
+      * enabled changes in the state. Widget.isEnabled() cannot be used as all
+      * Widgets do not implement HasEnabled
+      */
+     private boolean lastEnabledState = true;
+     private List<ServerConnector> children;
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.VPaintable#getConnection()
+      */
+     @Override
+     public final ApplicationConnection getConnection() {
+         return connection;
+     }
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.Connector#getId()
+      */
+     @Override
+     public String getConnectorId() {
+         return id;
+     }
+     /**
+      * Called once by the framework to initialize the connector.
+      * <p>
+      * Note that the shared state is not yet available when this method is
+      * called.
+      * <p>
+      * Connector classes should override {@link #init()} instead of this method.
+      */
+     @Override
+     public final void doInit(String connectorId,
+             ApplicationConnection connection) {
+         this.connection = connection;
+         id = connectorId;
+         addStateChangeHandler(this);
+         init();
+     }
+     /**
+      * Called when the connector has been initialized. Override this method to
+      * perform initialization of the connector.
+      */
+     // FIXME: It might make sense to make this abstract to force users to
+     // use init instead of constructor, where connection and id has not yet been
+     // set.
+     protected void init() {
+     }
+     /**
+      * Registers an implementation for a server to client RPC interface.
+      * 
+      * Multiple registrations can be made for a single interface, in which case
+      * all of them receive corresponding RPC calls.
+      * 
+      * @param rpcInterface
+      *            RPC interface
+      * @param implementation
+      *            implementation that should receive RPC calls
+      * @param <T>
+      *            The type of the RPC interface that is being registered
+      */
+     protected <T extends ClientRpc> void registerRpc(Class<T> rpcInterface,
+             T implementation) {
+         String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+         if (null == rpcImplementations) {
+             rpcImplementations = new HashMap<String, Collection<ClientRpc>>();
+         }
+         if (null == rpcImplementations.get(rpcInterfaceId)) {
+             rpcImplementations.put(rpcInterfaceId, new ArrayList<ClientRpc>());
+         }
+         rpcImplementations.get(rpcInterfaceId).add(implementation);
+     }
+     /**
+      * Unregisters an implementation for a server to client RPC interface.
+      * 
+      * @param rpcInterface
+      *            RPC interface
+      * @param implementation
+      *            implementation to unregister
+      */
+     protected <T extends ClientRpc> void unregisterRpc(Class<T> rpcInterface,
+             T implementation) {
+         String rpcInterfaceId = rpcInterface.getName().replaceAll("\\$", ".");
+         if (null != rpcImplementations
+                 && null != rpcImplementations.get(rpcInterfaceId)) {
+             rpcImplementations.get(rpcInterfaceId).remove(implementation);
+         }
+     }
+     @Override
+     public <T extends ClientRpc> Collection<T> getRpcImplementations(
+             String rpcInterfaceId) {
+         if (null == rpcImplementations) {
+             return Collections.emptyList();
+         }
+         return (Collection<T>) rpcImplementations.get(rpcInterfaceId);
+     }
+     @Override
+     public void fireEvent(GwtEvent<?> event) {
+         if (handlerManager != null) {
+             handlerManager.fireEvent(event);
+         }
+         if (statePropertyHandlerManagers != null
+                 && event instanceof StateChangeEvent) {
+             for (String property : ((StateChangeEvent) event)
+                     .getChangedProperties()) {
+                 HandlerManager manager = statePropertyHandlerManagers
+                         .get(property);
+                 if (manager != null) {
+                     manager.fireEvent(event);
+                 }
+             }
+         }
+     }
+     protected HandlerManager ensureHandlerManager() {
+         if (handlerManager == null) {
+             handlerManager = new HandlerManager(this);
+         }
+         return handlerManager;
+     }
+     @Override
+     public HandlerRegistration addStateChangeHandler(StateChangeHandler handler) {
+         return ensureHandlerManager()
+                 .addHandler(StateChangeEvent.TYPE, handler);
+     }
++    @Override
++    public void removeStateChangeHandler(StateChangeHandler handler) {
++        ensureHandlerManager().removeHandler(StateChangeEvent.TYPE, handler);
++    }
++
+     @Override
+     public HandlerRegistration addStateChangeHandler(String propertyName,
+             StateChangeHandler handler) {
+         return ensureHandlerManager(propertyName).addHandler(
+                 StateChangeEvent.TYPE, handler);
+     }
++
++    @Override
++    public void removeStateChangeHandler(String propertyName,
++            StateChangeHandler handler) {
++        ensureHandlerManager(propertyName).removeHandler(StateChangeEvent.TYPE,
++                handler);
++    }
+     private HandlerManager ensureHandlerManager(String propertyName) {
+         if (statePropertyHandlerManagers == null) {
+             statePropertyHandlerManagers = new HashMap<String, HandlerManager>();
+         }
+         HandlerManager manager = statePropertyHandlerManagers.get(propertyName);
+         if (manager == null) {
+             manager = new HandlerManager(this);
+             statePropertyHandlerManagers.put(propertyName, manager);
+         }
+         return manager;
+     }
+     @Override
+     public void onStateChanged(StateChangeEvent stateChangeEvent) {
+         if (debugLogging) {
+             VConsole.log("State change event for "
+                     + Util.getConnectorString(stateChangeEvent.getConnector())
+                     + " received by " + Util.getConnectorString(this));
+         }
+         updateEnabledState(isEnabled());
+     }
+     /*
+      * (non-Javadoc)
+      * 
+      * @see com.vaadin.client.ServerConnector#onUnregister()
+      */
+     @Override
+     public void onUnregister() {
+         if (debugLogging) {
+             VConsole.log("Unregistered connector "
+                     + Util.getConnectorString(this));
+         }
+     }
+     /**
+      * Returns the shared state object for this connector.
+      * 
+      * Override this method to define the shared state type for your connector.
+      * 
+      * @return the current shared state (never null)
+      */
+     @Override
+     public SharedState getState() {
+         if (state == null) {
+             state = createState();
+         }
+         return state;
+     }
+     /**
+      * Creates a state object with default values for this connector. The
+      * created state object must be compatible with the return type of
+      * {@link #getState()}. The default implementation creates a state object
+      * using GWT.create() using the defined return type of {@link #getState()}.
+      * 
+      * @return A new state object
+      */
+     protected SharedState createState() {
+         try {
+             Type stateType = getStateType(this);
+             Object stateInstance = stateType.createInstance();
+             return (SharedState) stateInstance;
+         } catch (NoDataException e) {
+             throw new IllegalStateException(
+                     "There is no information about the state for "
+                             + Util.getSimpleName(this)
+                             + ". Did you remember to compile the right widgetset?",
+                     e);
+         }
+     }
+     public static Type getStateType(ServerConnector connector) {
+         try {
+             return TypeData.getType(connector.getClass()).getMethod("getState")
+                     .getReturnType();
+         } catch (NoDataException e) {
+             throw new IllegalStateException(
+                     "There is no information about the state for "
+                             + Util.getSimpleName(connector)
+                             + ". Did you remember to compile the right widgetset?",
+                     e);
+         }
+     }
+     @Override
+     public ServerConnector getParent() {
+         return parent;
+     }
+     @Override
+     public void setParent(ServerConnector parent) {
+         this.parent = parent;
+     }
+     @Override
+     public List<ServerConnector> getChildren() {
+         if (children == null) {
+             return Collections.emptyList();
+         }
+         return children;
+     }
+     @Override
+     public void setChildren(List<ServerConnector> children) {
+         this.children = children;
+     }
+     @Override
+     public boolean isEnabled() {
+         if (!getState().isEnabled()) {
+             return false;
+         }
+         if (getParent() == null) {
+             return true;
+         } else {
+             return getParent().isEnabled();
+         }
+     }
+     @Override
+     public void updateEnabledState(boolean enabledState) {
+         if (lastEnabledState == enabledState) {
+             return;
+         }
+         lastEnabledState = enabledState;
+         for (ServerConnector c : getChildren()) {
+             // Update children as they might be affected by the enabled state of
+             // their parent
+             c.updateEnabledState(c.isEnabled());
+         }
+     }
+     /**
+      * Gets the URL for a resource that has been added by the server-side
+      * connector using
+      * {@link com.vaadin.terminal.AbstractClientConnector#setResource(String, com.vaadin.terminal.Resource)}
+      * with the same key. <code>null</code> is returned if no corresponding
+      * resource is found.
+      * <p>
+      * To get an event when a resource changes, you can use
+      * {@link #addStateChangeHandler(String, StateChangeHandler)} with
+      * <code>resources.[key]</code> as the property name.
+      * 
+      * @param key
+      *            a string identifying the resource.
+      * @return the resource URL as a string, or <code>null</code> if no
+      *         corresponding resource is found.
+      */
+     public String getResourceUrl(String key) {
+         URLReference urlReference = getState().resources.get(key);
+         if (urlReference == null) {
+             return null;
+         } else {
+             return urlReference.getURL();
+         }
+     }
+ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a4fcea81f592f0be5d1a8cbaf4076626cf97c7a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,608 @@@
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import java.util.HashMap;
++import java.util.HashSet;
++import java.util.List;
++
++import com.google.gwt.dom.client.Style.Unit;
++import com.google.gwt.user.client.Element;
++import com.vaadin.client.ComponentConnector;
++import com.vaadin.client.ConnectorHierarchyChangeEvent;
++import com.vaadin.client.Util;
++import com.vaadin.client.communication.RpcProxy;
++import com.vaadin.client.communication.StateChangeEvent;
++import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
++import com.vaadin.client.ui.AbstractFieldConnector;
++import com.vaadin.client.ui.AbstractLayoutConnector;
++import com.vaadin.client.ui.LayoutClickEventHandler;
++import com.vaadin.client.ui.layout.ElementResizeEvent;
++import com.vaadin.client.ui.layout.ElementResizeListener;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout.CaptionPosition;
++import com.vaadin.client.ui.orderedlayout.VBoxLayout.Slot;
++import com.vaadin.shared.AbstractFieldState;
++import com.vaadin.shared.ComponentConstants;
++import com.vaadin.shared.communication.URLReference;
++import com.vaadin.shared.ui.AlignmentInfo;
++import com.vaadin.shared.ui.LayoutClickRpc;
++import com.vaadin.shared.ui.MarginInfo;
++import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutServerRpc;
++import com.vaadin.shared.ui.orderedlayout.AbstractOrderedLayoutState;
++
++public abstract class AbstractBoxLayoutConnector extends
++        AbstractLayoutConnector /* implements PostLayoutListener */{
++
++    AbstractOrderedLayoutServerRpc rpc;
++
++    private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
++            this) {
++
++        @Override
++        protected ComponentConnector getChildComponent(Element element) {
++            return Util.getConnectorForElement(getConnection(), getWidget(),
++                    element);
++        }
++
++        @Override
++        protected LayoutClickRpc getLayoutClickRPC() {
++            return rpc;
++        };
++
++    };
++
++    @Override
++    public void init() {
++        super.init();
++        rpc = RpcProxy.create(AbstractOrderedLayoutServerRpc.class, this);
++        getWidget().setLayoutManager(getLayoutManager());
++    }
++
++    @Override
++    public AbstractOrderedLayoutState getState() {
++        return (AbstractOrderedLayoutState) super.getState();
++    }
++
++    @Override
++    public VBoxLayout getWidget() {
++        return (VBoxLayout) super.getWidget();
++    }
++
++    /**
++     * For bookkeeping. Used to determine if extra calculations are needed for
++     * horizontal layout.
++     */
++    private HashSet<ComponentConnector> hasVerticalAlignment = new HashSet<ComponentConnector>();
++
++    /**
++     * For bookkeeping. Used to determine if extra calculations are needed for
++     * horizontal layout.
++     */
++    private HashSet<ComponentConnector> hasRelativeHeight = new HashSet<ComponentConnector>();
++
++    /**
++     * For bookkeeping. Used to determine if extra calculations are needed for
++     * horizontal layout.
++     */
++    private HashSet<ComponentConnector> hasExpandRatio = new HashSet<ComponentConnector>();
++
++    /**
++     * For bookkeeping. Used in extra calculations for horizontal layout.
++     */
++    private HashSet<Element> needsMeasure = new HashSet<Element>();
++
++    /**
++     * For bookkeeping. Used in extra calculations for horizontal layout.
++     */
++    // private HashMap<Element, Integer> childElementHeight = new
++    // HashMap<Element, Integer>();
++
++    /**
++     * For bookkeeping. Used in extra calculations for horizontal layout.
++     */
++    private HashMap<Element, Integer> childCaptionElementHeight = new HashMap<Element, Integer>();
++
++    @Override
++    public void updateCaption(ComponentConnector child) {
++        Slot slot = getWidget().getSlot(child);
++
++
++        String caption = child.getState().getCaption();
++        URLReference iconUrl = child.getState().resources
++                .get(ComponentConstants.ICON_RESOURCE);
++        String iconUrlString = iconUrl != null ? iconUrl.toString() : null;
++                List<String> styles = child.getState().getStyles();
++                String error = child.getState().getErrorMessage();
++                boolean showError = error != null;
++                if (child.getState() instanceof AbstractFieldState) {
++                    AbstractFieldState abstractFieldState = (AbstractFieldState) child
++                            .getState();
++                    showError = showError && !abstractFieldState.isHideErrors();
++                }
++                boolean required = false;
++                if (child instanceof AbstractFieldConnector) {
++                    required = ((AbstractFieldConnector) child).isRequired();
++                }
++                boolean enabled = child.getState().isEnabled();
++
++        slot.setCaption(caption, iconUrlString, styles, error, showError,
++                required,
++                        enabled);
++
++                slot.setRelativeWidth(child.isRelativeWidth());
++                slot.setRelativeHeight(child.isRelativeHeight());
++
++                if (slot.hasCaption()) {
++                    CaptionPosition pos = slot.getCaptionPosition();
++                    getLayoutManager().addElementResizeListener(
++                            slot.getCaptionElement(), slotCaptionResizeListener);
++                    if (child.isRelativeHeight()
++                            && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++                        getWidget().updateCaptionOffset(slot.getCaptionElement());
++                    } else if (child.isRelativeWidth()
++                            && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
++                        getWidget().updateCaptionOffset(slot.getCaptionElement());
++                    }
++                } else {
++                    childCaptionElementHeight.remove(child.getWidget().getElement());
++                }
++
++                updateLayoutHeight();
++
++                if (needsExpand()) {
++                    updateExpand();
++                }
++    }
++
++    @Override
++    public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) {
++        super.onConnectorHierarchyChange(event);
++
++        List<ComponentConnector> previousChildren = event.getOldChildren();
++        int currentIndex = 0;
++        VBoxLayout layout = getWidget();
++
++        for (ComponentConnector child : getChildComponents()) {
++            Slot slot = layout.getSlot(child);
++            if (slot.getParent() != layout) {
++                child.addStateChangeHandler(childStateChangeHandler);
++            }
++            layout.addOrMoveSlot(slot, currentIndex++);
++        }
++
++        for (ComponentConnector child : previousChildren) {
++            if (child.getParent() != this) {
++                Slot slot = layout.getSlot(child);
++                hasVerticalAlignment.remove(child);
++                hasRelativeHeight.remove(child);
++                hasExpandRatio.remove(child);
++                needsMeasure.remove(child.getWidget().getElement());
++                // childElementHeight.remove(child.getWidget().getElement());
++                childCaptionElementHeight
++                .remove(child.getWidget().getElement());
++                getLayoutManager().removeElementResizeListener(
++                        child.getWidget().getElement(),
++                        childComponentResizeListener);
++                if (slot.hasCaption()) {
++                    getLayoutManager()
++                    .removeElementResizeListener(
++                            slot.getCaptionElement(),
++                            slotCaptionResizeListener);
++                }
++                if (slot.getSpacingElement() != null) {
++                    getLayoutManager().removeElementResizeListener(
++                            slot.getSpacingElement(), spacingResizeListener);
++                }
++                child.removeStateChangeHandler(childStateChangeHandler);
++                layout.removeSlot(child.getWidget());
++            }
++        }
++
++        // If some component is added/removed, we need to recalculate the expand
++        if (needsExpand()) {
++            updateExpand();
++        } else {
++            getWidget().clearExpand();
++        }
++
++    }
++
++    @Override
++    public void onStateChanged(StateChangeEvent stateChangeEvent) {
++        super.onStateChanged(stateChangeEvent);
++
++        clickEventHandler.handleEventHandlerRegistration();
++        getWidget().setMargin(new MarginInfo(getState().getMarginsBitmask()));
++        getWidget().setSpacing(getState().isSpacing());
++
++        hasExpandRatio.clear();
++        hasVerticalAlignment.clear();
++        hasRelativeHeight.clear();
++        needsMeasure.clear();
++
++        boolean equalExpandRatio = getWidget().vertical ? !isUndefinedHeight()
++                : !isUndefinedWidth();
++        for (ComponentConnector child : getChildComponents()) {
++            double expandRatio = getState().getChildData().get(child)
++                    .getExpandRatio();
++            if (expandRatio > 0) {
++                equalExpandRatio = false;
++                break;
++            }
++        }
++
++        for (ComponentConnector child : getChildComponents()) {
++            Slot slot = getWidget().getSlot(child);
++
++            AlignmentInfo alignment = new AlignmentInfo(getState()
++                    .getChildData().get(child).getAlignmentBitmask());
++            slot.setAlignment(alignment);
++
++            double expandRatio = getState().getChildData().get(child)
++                    .getExpandRatio();
++            if (equalExpandRatio) {
++                expandRatio = 1;
++            } else if (expandRatio == 0) {
++                expandRatio = -1;
++            }
++            slot.setExpandRatio(expandRatio);
++
++            // Bookkeeping to identify special cases that need extra
++            // calculations
++            if (alignment.isVerticalCenter() || alignment.isBottom()) {
++                hasVerticalAlignment.add(child);
++            }
++
++            if (expandRatio > 0) {
++                hasExpandRatio.add(child);
++            }
++
++            // if (child.getState().isRelativeHeight()) {
++            // hasRelativeHeight.add(child);
++            // } else {
++            // needsMeasure.add(child.getWidget().getElement());
++            // }
++        }
++
++        updateAllSlotListeners();
++
++        updateLayoutHeight();
++    }
++
++    StateChangeHandler childStateChangeHandler = new StateChangeHandler() {
++        @Override
++        public void onStateChanged(StateChangeEvent stateChangeEvent) {
++
++            ComponentConnector child = (ComponentConnector) stateChangeEvent
++                    .getConnector();
++
++            // We need to update the slot size if the component size is changed
++            // to relative
++            Slot slot = getWidget().getSlot(child);
++            slot.setRelativeWidth(child.isRelativeWidth());
++            slot.setRelativeHeight(child.isRelativeHeight());
++
++            // For relative sized widgets, we need to set the caption offset
++            // if (slot.hasCaption()) {
++            // CaptionPosition pos = slot.getCaptionPosition();
++            // if (child.isRelativeHeight()
++            // && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM))
++            // {
++            // getWidget().updateCaptionOffset(slot.getCaptionElement());
++            // } else if (child.isRelativeWidth()
++            // && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT))
++            // {
++            // getWidget().updateCaptionOffset(slot.getCaptionElement());
++            // }
++            // }
++
++            updateSlotListeners(child);
++            // updateAllSlotListeners();
++
++            updateLayoutHeight();
++        }
++    };
++
++    private boolean needsFixedHeight() {
++        if (!getWidget().vertical
++                && isUndefinedHeight()
++                && (hasRelativeHeight.size() > 0 || (hasVerticalAlignment
++                        .size() > 0 && hasVerticalAlignment.size() < getChildren()
++                        .size()))) {
++            return true;
++        }
++        return false;
++    }
++
++    private boolean needsExpand() {
++        boolean canApplyExpand = (getWidget().vertical && !isUndefinedHeight())
++                || (!getWidget().vertical && !isUndefinedWidth());
++        return hasExpandRatio.size() > 0 && canApplyExpand;
++    }
++
++    private void updateAllSlotListeners() {
++        for (ComponentConnector child : getChildComponents()) {
++            updateSlotListeners(child);
++        }
++        // if (needsFixedHeight()) {
++        // getWidget().clearHeight();
++        // setLayoutHeightListener(true);
++        // getLayoutManager().setNeedsMeasure(AbstractBoxLayoutConnector.this);
++        // } else {
++        // setLayoutHeightListener(false);
++        // }
++    }
++
++    /**
++     * Add/remove necessary ElementResizeListeners for one slot. This should be
++     * called after each update to the slot's or it's widget.
++     */
++    private void updateSlotListeners(ComponentConnector child) {
++        Slot slot = getWidget().getSlot(child);
++
++        // Clear all possible listeners first
++        dontListen(slot.getWidget().getElement(), childComponentResizeListener);
++        if (slot.hasCaption()) {
++            dontListen(slot.getCaptionElement(), slotCaptionResizeListener);
++        }
++        if (slot.hasSpacing()) {
++            dontListen(slot.getSpacingElement(), spacingResizeListener);
++        }
++
++        // Add all necessary listeners
++        if (needsFixedHeight()) {
++            listen(slot.getWidget().getElement(), childComponentResizeListener);
++            if (slot.hasCaption()) {
++                listen(slot.getCaptionElement(), slotCaptionResizeListener);
++            }
++        } else if ((child.isRelativeHeight() || child.isRelativeWidth())
++                && slot.hasCaption()) {
++            // If the slot has caption, we need to listen for it's size changes
++            // in order to update the padding/margin offset for relative sized
++            // components
++            listen(slot.getCaptionElement(), slotCaptionResizeListener);
++        }
++
++        if (needsExpand()) {
++            listen(slot.getWidget().getElement(), childComponentResizeListener);
++            if (slot.hasSpacing()) {
++                listen(slot.getSpacingElement(), spacingResizeListener);
++            }
++        }
++
++        if (child.isRelativeHeight()) {
++            hasRelativeHeight.add(child);
++            needsMeasure.remove(child.getWidget().getElement());
++        } else {
++            hasRelativeHeight.remove(child);
++            needsMeasure.add(child.getWidget().getElement());
++        }
++
++    }
++
++    // public void postLayout() {
++    // if (needsFixedHeight()) {
++    // // Re-measure all elements that are available
++    // for (Element el : needsMeasure) {
++    // childElementHeight.put(el, getLayoutManager()
++    // .getOuterHeight(el));
++    //
++    // Element captionElement = el.getParentElement()
++    // .getFirstChildElement().cast();
++    // if (captionElement.getClassName().contains("v-caption")) {
++    // childCaptionElementHeight.put(el, getLayoutManager()
++    // .getOuterHeight(captionElement));
++    // }
++    // }
++    // // System.out.println("  ###  Child sizes: "
++    // // + childElementHeight.values().toString());
++    // // System.out.println("  ###  Caption sizes: "
++    // // + childCaptionElementHeight.values().toString());
++    //
++    // int height = getMaxHeight()
++    // + getLayoutManager().getBorderHeight(
++    // getWidget().getElement())
++    // + getLayoutManager().getPaddingHeight(
++    // getWidget().getElement());
++    // getWidget().getElement().getStyle().setHeight(height, Unit.PX);
++    // }
++    // }
++
++    // private ElementResizeListener layoutResizeListener = new
++    // ElementResizeListener() {
++    // public void onElementResize(ElementResizeEvent e) {
++    // updateLayoutHeight();
++    // if (needsExpand() && (isUndefinedHeight() || isUndefinedWidth())) {
++    // updateExpand();
++    // }
++    // }
++    // };
++
++    private ElementResizeListener slotCaptionResizeListener = new ElementResizeListener() {
++        @Override
++        public void onElementResize(ElementResizeEvent e) {
++
++            // Get all needed element references
++            Element captionElement = (Element) e.getElement().cast();
++
++            // Caption position determines if the widget element is the first or
++            // last child inside the caption wrap
++            CaptionPosition pos = getWidget().getCaptionPositionFromElement(
++                    (Element) captionElement.getParentElement().cast());
++
++            // The default is the last child
++            Element widgetElement = captionElement.getParentElement()
++                    .getLastChild().cast();
++
++            // ...but if caption position is bottom or right, the widget is the
++            // first child
++            if (pos == CaptionPosition.BOTTOM || pos == CaptionPosition.RIGHT) {
++                widgetElement = captionElement.getParentElement()
++                        .getFirstChildElement().cast();
++            }
++
++            if (captionElement == widgetElement) {
++                // Caption element already detached
++                dontListen(captionElement, slotCaptionResizeListener);
++                childCaptionElementHeight.remove(widgetElement);
++                return;
++            }
++
++            String widgetWidth = widgetElement.getStyle().getWidth();
++            String widgetHeight = widgetElement.getStyle().getHeight();
++
++            if (widgetHeight.endsWith("%")
++                    && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++                getWidget().updateCaptionOffset(captionElement);
++            } else if (widgetWidth.endsWith("%")
++                    && (pos == CaptionPosition.LEFT || pos == CaptionPosition.RIGHT)) {
++                getWidget().updateCaptionOffset(captionElement);
++            }
++
++            int h = getLayoutManager().getOuterHeight(captionElement)
++                    - getLayoutManager().getMarginHeight(captionElement);
++            childCaptionElementHeight.put(widgetElement, h);
++
++            // if (needsFixedHeight()) {
++            // getWidget().clearHeight();
++            // getLayoutManager().setNeedsMeasure(
++            // AbstractBoxLayoutConnector.this);
++            // }
++
++            updateLayoutHeight();
++
++            if (needsExpand()) {
++                updateExpand();
++            }
++        }
++    };
++
++    private ElementResizeListener childComponentResizeListener = new ElementResizeListener() {
++        @Override
++        public void onElementResize(ElementResizeEvent e) {
++            // int h = getLayoutManager().getOuterHeight(e.getElement());
++            // childElementHeight.put((Element) e.getElement().cast(), h);
++            updateLayoutHeight();
++
++            if (needsExpand()) {
++                updateExpand();
++            }
++        }
++    };
++
++    private ElementResizeListener spacingResizeListener = new ElementResizeListener() {
++        @Override
++        public void onElementResize(ElementResizeEvent e) {
++            if (needsExpand()) {
++                updateExpand();
++            }
++        }
++    };
++
++    private void updateLayoutHeight() {
++        if (needsFixedHeight()) {
++            int h = getMaxHeight();
++            h += getLayoutManager().getBorderHeight(getWidget().getElement())
++                    + getLayoutManager().getPaddingHeight(
++                            getWidget().getElement());
++            getWidget().getElement().getStyle().setHeight(h, Unit.PX);
++            getLayoutManager().setNeedsMeasure(this);
++        }
++    }
++
++    private void updateExpand() {
++        // System.out.println("All sizes: "
++        // + childElementHeight.values().toString() + " - Caption sizes: "
++        // + childCaptionElementHeight.values().toString());
++        getWidget().updateExpand();
++    }
++
++    private int getMaxHeight() {
++        int highestNonRelative = -1;
++        int highestRelative = -1;
++
++        for (ComponentConnector child : getChildComponents()) {
++            // TODO would be more efficient to measure the slot element if both
++            // caption and child widget elements need to be measured. Keeping
++            // track of what to measure is the most difficult part of this
++            // layout.
++            Element el = child.getWidget().getElement();
++            CaptionPosition pos = getWidget().getCaptionPositionFromElement(
++                    (Element) el.getParentElement().cast());
++            if (needsMeasure.contains(el)) {
++                int h = getLayoutManager().getOuterHeight(el);
++                String sHeight = el.getStyle().getHeight();
++                // Only add the caption size to the height of the slot if
++                // coption position is top or bottom
++                if (childCaptionElementHeight.containsKey(el)
++                        && (sHeight == null || !sHeight.endsWith("%"))
++                        && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++                    h += childCaptionElementHeight.get(el);
++                }
++                if (h > highestNonRelative) {
++                    highestNonRelative = h;
++                }
++            } else {
++                int h = getLayoutManager().getOuterHeight(el);
++                if (childCaptionElementHeight.containsKey(el)
++                        && (pos == CaptionPosition.TOP || pos == CaptionPosition.BOTTOM)) {
++                    h += childCaptionElementHeight.get(el);
++                }
++                if (h > highestRelative) {
++                    highestRelative = h;
++                }
++            }
++        }
++        return highestNonRelative > -1 ? highestNonRelative : highestRelative;
++    }
++
++    @Override
++    public void onUnregister() {
++        // Cleanup all ElementResizeListeners
++
++        // dontListen(getWidget().getElement(), layoutResizeListener);
++
++        for (ComponentConnector child : getChildComponents()) {
++            Slot slot = getWidget().getSlot(child);
++            if (slot.hasCaption()) {
++                dontListen(slot.getCaptionElement(), slotCaptionResizeListener);
++            }
++
++            if (slot.getSpacingElement() != null) {
++                dontListen(slot.getSpacingElement(), spacingResizeListener);
++            }
++
++            dontListen(slot.getWidget().getElement(),
++                    childComponentResizeListener);
++        }
++
++        super.onUnregister();
++    }
++
++    // private void setLayoutHeightListener(boolean add) {
++    // if (add) {
++    // listen(getWidget().getElement(), layoutResizeListener);
++    // } else {
++    // dontListen(getWidget().getElement(), layoutResizeListener);
++    // if (!needsExpand()) {
++    // System.out.println("Clearing element sizes");
++    // childElementHeight.clear();
++    // childCaptionElementHeight.clear();
++    // }
++    // }
++    // }
++
++    /*
++     * Convenience methods
++     */
++
++    private void listen(Element el, ElementResizeListener listener) {
++        getLayoutManager().addElementResizeListener(el, listener);
++    }
++
++    private void dontListen(Element el, ElementResizeListener listener) {
++        getLayoutManager().removeElementResizeListener(el, listener);
++    }
++
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13e133dfa7930a9d7fb02d05ba1252d7db2b0cbb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++/* 
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import com.vaadin.shared.ui.Connect;
++import com.vaadin.shared.ui.Connect.LoadStyle;
++import com.vaadin.ui.HorizontalLayout;
++
++@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
++public class HorizontalBoxLayoutConnector extends AbstractBoxLayoutConnector {
++
++    @Override
++    public void init() {
++        super.init();
++        getWidget().setVertical(false);
++    }
++
++}
index 0000000000000000000000000000000000000000,c17f06760b413d19d835923e3cce928e26bc421b..a86baf9ceadcfaedafc2e9285134d8634e1169ab
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,30 +1,27 @@@
 -import com.vaadin.shared.ui.Connect;
 -import com.vaadin.shared.ui.Connect.LoadStyle;
 -import com.vaadin.ui.HorizontalLayout;
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client.ui.orderedlayout;
 -@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
++//@Connect(value = HorizontalLayout.class, loadStyle = LoadStyle.EAGER)
+ public class HorizontalLayoutConnector extends AbstractOrderedLayoutConnector {
+     @Override
+     public VHorizontalLayout getWidget() {
+         return (VHorizontalLayout) super.getWidget();
+     }
+ }
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e04ef93444ae3fcf651d8d03fc7c8a1814514dbf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,758 @@@
++/*
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import java.util.HashMap;
++import java.util.List;
++import java.util.Map;
++
++import com.google.gwt.dom.client.Node;
++import com.google.gwt.dom.client.Style;
++import com.google.gwt.dom.client.Style.Unit;
++import com.google.gwt.regexp.shared.MatchResult;
++import com.google.gwt.regexp.shared.RegExp;
++import com.google.gwt.user.client.DOM;
++import com.google.gwt.user.client.Element;
++import com.google.gwt.user.client.Event;
++import com.google.gwt.user.client.ui.FlowPanel;
++import com.google.gwt.user.client.ui.SimplePanel;
++import com.google.gwt.user.client.ui.UIObject;
++import com.google.gwt.user.client.ui.Widget;
++import com.vaadin.client.ComponentConnector;
++import com.vaadin.client.LayoutManager;
++import com.vaadin.shared.ui.AlignmentInfo;
++import com.vaadin.shared.ui.MarginInfo;
++import com.vaadin.ui.themes.BaseTheme;
++
++public class VBoxLayout extends FlowPanel {
++
++    public static final String CLASSNAME = "v-boxlayout";
++
++    private static final String ALIGN_CLASS_PREFIX = "v-align-";
++
++    protected boolean spacing = false;
++
++    protected boolean vertical = true;
++
++    protected boolean definedHeight = false;
++
++    private Map<Widget, Slot> widgetToSlot = new HashMap<Widget, Slot>();
++
++    private LayoutManager layoutManager;
++
++    public VBoxLayout() {
++        setStyleName(CLASSNAME);
++        setVertical(true);
++    }
++
++    public void setVertical(boolean isVertical) {
++        vertical = isVertical;
++        if (vertical) {
++            addStyleName("v-vertical");
++            removeStyleName("v-horizontal");
++        } else {
++            addStyleName("v-horizontal");
++            removeStyleName("v-vertical");
++        }
++    }
++
++    public void addOrMoveSlot(Slot slot, int index) {
++        if (slot.getParent() == this) {
++            int currentIndex = getWidgetIndex(slot);
++            if (index == currentIndex) {
++                return;
++            }
++        }
++        insert(slot, index);
++    }
++
++    @Override
++    protected void insert(Widget child, Element container, int beforeIndex,
++            boolean domInsert) {
++        // Validate index; adjust if the widget is already a child of this
++        // panel.
++        beforeIndex = adjustIndex(child, beforeIndex);
++
++        // Detach new child.
++        child.removeFromParent();
++
++        // Logical attach.
++        getChildren().insert(child, beforeIndex);
++
++        // Physical attach.
++        container = expandWrapper != null ? expandWrapper : getElement();
++        if (domInsert) {
++            DOM.insertChild(container, child.getElement(),
++                    spacing ? beforeIndex * 2 : beforeIndex);
++        } else {
++            DOM.appendChild(container, child.getElement());
++        }
++
++        // Adopt.
++        adopt(child);
++    }
++
++    public Slot removeSlot(Widget widget) {
++        Slot slot = widgetToSlot.get(widget);
++        remove(slot);
++        widgetToSlot.remove(widget);
++        return slot;
++    }
++
++    public Slot getSlot(ComponentConnector connector) {
++        Slot slot = widgetToSlot.get(connector.getWidget());
++        if (slot == null) {
++            slot = new Slot(connector);
++            widgetToSlot.put(connector.getWidget(), slot);
++        }
++        return slot;
++    }
++
++    public enum CaptionPosition {
++        TOP, RIGHT, BOTTOM, LEFT
++    }
++
++    protected class Slot extends SimplePanel {
++
++        private ComponentConnector connector;
++
++        private Element spacer;
++
++        private Element captionWrap;
++        private Element caption;
++        private Element captionText;
++        private Icon icon;
++        private Element errorIcon;
++        private Element requiredIcon;
++
++        // Caption is placed after component unless there is some part which
++        // moves it above.
++        private CaptionPosition captionPosition = CaptionPosition.RIGHT;
++
++        private AlignmentInfo alignment;
++        private double expandRatio = -1;
++
++        public Slot(ComponentConnector connector) {
++            this.connector = connector;
++            setWidget(connector.getWidget());
++            setStylePrimaryName("v-slot");
++        }
++
++        public AlignmentInfo getAlignment() {
++            return alignment;
++        }
++
++        public void setAlignment(AlignmentInfo alignment) {
++            this.alignment = alignment;
++
++            if (alignment.isHorizontalCenter()) {
++                addStyleName(ALIGN_CLASS_PREFIX + "center");
++                removeStyleName(ALIGN_CLASS_PREFIX + "right");
++            } else if (alignment.isRight()) {
++                addStyleName(ALIGN_CLASS_PREFIX + "right");
++                removeStyleName(ALIGN_CLASS_PREFIX + "center");
++            } else {
++                removeStyleName(ALIGN_CLASS_PREFIX + "right");
++                removeStyleName(ALIGN_CLASS_PREFIX + "center");
++            }
++            if (alignment.isVerticalCenter()) {
++                addStyleName(ALIGN_CLASS_PREFIX + "middle");
++                removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
++            } else if (alignment.isBottom()) {
++                addStyleName(ALIGN_CLASS_PREFIX + "bottom");
++                removeStyleName(ALIGN_CLASS_PREFIX + "middle");
++            } else {
++                removeStyleName(ALIGN_CLASS_PREFIX + "middle");
++                removeStyleName(ALIGN_CLASS_PREFIX + "bottom");
++            }
++        }
++
++        public void setExpandRatio(double expandRatio) {
++            this.expandRatio = expandRatio;
++        }
++
++        public double getExpandRatio() {
++            return expandRatio;
++        }
++
++        public void setSpacing(boolean spacing) {
++            if (spacing && spacer == null) {
++                spacer = DOM.createDiv();
++                spacer.addClassName("v-spacing");
++                getElement().getParentElement().insertBefore(spacer,
++                        getElement());
++            } else if (!spacing && spacer != null) {
++                spacer.removeFromParent();
++                spacer = null;
++            }
++        }
++
++        public Element getSpacingElement() {
++            return spacer;
++        }
++
++        public boolean hasSpacing() {
++            return getSpacingElement() != null;
++        }
++
++        protected int getSpacingSize(boolean vertical) {
++            if (spacer == null) {
++                return 0;
++            }
++
++            if (layoutManager != null) {
++                if (vertical) {
++                    return layoutManager.getOuterHeight(spacer);
++                } else {
++                    return layoutManager.getOuterWidth(spacer);
++                }
++            }
++            // TODO place for optimization (in expense of theme
++            // flexibility): only measure one of the elements and cache the
++            // value
++            return vertical ? spacer.getOffsetHeight() : spacer
++                    .getOffsetWidth();
++            // }
++        }
++
++        public void setCaptionPosition(CaptionPosition captionPosition) {
++            if (caption == null) {
++                return;
++            }
++
++            captionWrap.removeClassName("v-caption-on-"
++                    + this.captionPosition.name().toLowerCase());
++
++            this.captionPosition = captionPosition;
++            if (captionPosition == CaptionPosition.BOTTOM
++                    || captionPosition == CaptionPosition.RIGHT) {
++                captionWrap.appendChild(caption);
++            } else {
++                captionWrap.insertFirst(caption);
++            }
++
++            captionWrap.addClassName("v-caption-on-"
++                    + captionPosition.name().toLowerCase());
++        }
++
++        public CaptionPosition getCaptionPosition() {
++            return captionPosition;
++        }
++
++        // TODO refactor VCaption and use that instead: creates a tight coupling
++        // between this layout and Vaadin, but it's already coupled
++        public void setCaption(String captionText, String iconUrl,
++                List<String> styles, String error, boolean showError,
++                boolean required, boolean enabled) {
++
++            // TODO place for optimization: check if any of these have changed
++            // since last time, and only run those changes
++
++            // Caption wrappers
++            if (captionText != null || iconUrl != null || error != null
++                    || required) {
++                if (caption == null) {
++                    caption = DOM.createDiv();
++                    captionWrap = DOM.createDiv();
++                    captionWrap.addClassName(BaseTheme.UI_WIDGET);
++                    captionWrap.addClassName("v-has-caption");
++                    getElement().appendChild(captionWrap);
++                    captionWrap.appendChild(getWidget().getElement());
++                }
++            } else if (caption != null) {
++                getElement().appendChild(getWidget().getElement());
++                captionWrap.removeFromParent();
++                caption = null;
++                captionWrap = null;
++            }
++
++            // Caption text
++            if (captionText != null) {
++                if (this.captionText == null) {
++                    this.captionText = DOM.createSpan();
++                    this.captionText.addClassName("v-captiontext");
++                    caption.appendChild(this.captionText);
++                }
++                if (captionText.trim().equals("")) {
++                    this.captionText.setInnerHTML("&nbsp;");
++                } else {
++                    this.captionText.setInnerText(captionText);
++                }
++            } else if (this.captionText != null) {
++                this.captionText.removeFromParent();
++                this.captionText = null;
++            }
++
++            // Icon
++            if (iconUrl != null) {
++                if (icon == null) {
++                    icon = new Icon();
++                    // icon = DOM.createImg();
++                    // icon.setClassName("v-icon");
++                    caption.insertFirst(icon.getElement());
++                }
++                // icon.setAttribute("src", iconUrl);
++                icon.setUri(iconUrl);
++            } else if (icon != null) {
++                icon.getElement().removeFromParent();
++                icon = null;
++            }
++
++            // Required
++            if (required) {
++                if (requiredIcon == null) {
++                    requiredIcon = DOM.createSpan();
++                    // TODO decide something better (e.g. use CSS to insert the
++                    // character)
++                    requiredIcon.setInnerHTML("*");
++                    requiredIcon.setClassName("v-required-field-indicator");
++                }
++                caption.appendChild(requiredIcon);
++            } else if (requiredIcon != null) {
++                requiredIcon.removeFromParent();
++                requiredIcon = null;
++            }
++
++            // Error
++            if (error != null && showError) {
++                if (errorIcon == null) {
++                    errorIcon = DOM.createSpan();
++                    errorIcon.setClassName("v-errorindicator");
++                }
++                caption.appendChild(errorIcon);
++            } else if (errorIcon != null) {
++                errorIcon.removeFromParent();
++                errorIcon = null;
++            }
++
++            if (caption != null) {
++                // Styles
++                caption.setClassName("v-caption");
++
++                if (styles != null) {
++                    for (String style : styles) {
++                        caption.addClassName("v-caption-" + style);
++                    }
++                }
++
++                if (enabled) {
++                    caption.removeClassName("v-disabled");
++                } else {
++                    caption.addClassName("v-disabled");
++                }
++
++                // Caption position
++                if (captionText != null || iconUrl != null) {
++                    setCaptionPosition(CaptionPosition.TOP);
++                } else {
++                    setCaptionPosition(CaptionPosition.RIGHT);
++                }
++            }
++
++            // TODO theme flexibility: add extra styles to captionWrap as well?
++
++        }
++
++        public boolean hasCaption() {
++            return caption != null;
++        }
++
++        public Element getCaptionElement() {
++            return caption;
++        }
++
++        public void setRelativeWidth(boolean relativeWidth) {
++            updateRelativeSize(relativeWidth, "width");
++        }
++
++        public void setRelativeHeight(boolean relativeHeight) {
++            updateRelativeSize(relativeHeight, "height");
++        }
++
++        private void updateRelativeSize(boolean isRelativeSize, String direction) {
++            if (isRelativeSize && hasCaption()) {
++                captionWrap.getStyle().setProperty(
++                        direction,
++                        getWidget().getElement().getStyle()
++                        .getProperty(direction));
++                captionWrap.addClassName("v-has-" + direction);
++            } else if (hasCaption()) {
++                if (direction.equals("height")) {
++                    captionWrap.getStyle().clearHeight();
++                } else {
++                    captionWrap.getStyle().clearWidth();
++                }
++                captionWrap.removeClassName("v-has-" + direction);
++                captionWrap.getStyle().clearPaddingTop();
++                captionWrap.getStyle().clearPaddingRight();
++                captionWrap.getStyle().clearPaddingBottom();
++                captionWrap.getStyle().clearPaddingLeft();
++                caption.getStyle().clearMarginTop();
++                caption.getStyle().clearMarginRight();
++                caption.getStyle().clearMarginBottom();
++                caption.getStyle().clearMarginLeft();
++            }
++        }
++
++        @Override
++        public void onBrowserEvent(Event event) {
++            super.onBrowserEvent(event);
++            if (DOM.eventGetType(event) == Event.ONLOAD
++                    && icon.getElement() == DOM.eventGetTarget(event)) {
++                if (layoutManager != null) {
++                    layoutManager.layoutLater();
++                } else {
++                    updateCaptionOffset(caption);
++                }
++            }
++        }
++
++        @Override
++        protected Element getContainerElement() {
++            if (captionWrap == null) {
++                return getElement();
++            } else {
++                return captionWrap;
++            }
++        }
++
++        @Override
++        protected void onDetach() {
++            if (spacer != null) {
++                spacer.removeFromParent();
++            }
++            super.onDetach();
++        }
++
++        @Override
++        protected void onAttach() {
++            super.onAttach();
++            if (spacer != null) {
++                getElement().getParentElement().insertBefore(spacer,
++                        getElement());
++            }
++        }
++
++    }
++
++    protected class Icon extends UIObject {
++        public static final String CLASSNAME = "v-icon";
++        private String myUrl;
++
++        public Icon() {
++            setElement(DOM.createImg());
++            DOM.setElementProperty(getElement(), "alt", "");
++            setStyleName(CLASSNAME);
++        }
++
++        public void setUri(String url) {
++            if (!url.equals(myUrl)) {
++                /*
++                 * Start sinking onload events, widgets responsibility to react.
++                 * We must do this BEFORE we set src as IE fires the event
++                 * immediately if the image is found in cache (#2592).
++                 */
++                sinkEvents(Event.ONLOAD);
++
++                DOM.setElementProperty(getElement(), "src", url);
++                myUrl = url;
++            }
++        }
++
++    }
++
++    void setLayoutManager(LayoutManager manager) {
++        layoutManager = manager;
++    }
++
++    private static final RegExp captionPositionRegexp = RegExp
++            .compile("v-caption-on-(\\S+)");
++
++    CaptionPosition getCaptionPositionFromElement(Element captionWrap) {
++        // Get caption position from the classname
++        MatchResult matcher = captionPositionRegexp.exec(captionWrap
++                .getClassName());
++        if (matcher == null || matcher.getGroupCount() < 2) {
++            return CaptionPosition.TOP;
++        }
++        String captionClass = matcher.getGroup(1);
++        CaptionPosition captionPosition = CaptionPosition.valueOf(
++                CaptionPosition.class, captionClass.toUpperCase());
++        return captionPosition;
++    }
++
++    void updateCaptionOffset(Element caption) {
++
++        Element captionWrap = caption.getParentElement().cast();
++
++        Style captionWrapStyle = captionWrap.getStyle();
++        captionWrapStyle.clearPaddingTop();
++        captionWrapStyle.clearPaddingRight();
++        captionWrapStyle.clearPaddingBottom();
++        captionWrapStyle.clearPaddingLeft();
++
++        Style captionStyle = caption.getStyle();
++        captionStyle.clearMarginTop();
++        captionStyle.clearMarginRight();
++        captionStyle.clearMarginBottom();
++        captionStyle.clearMarginLeft();
++
++        // Get caption position from the classname
++        CaptionPosition captionPosition = getCaptionPositionFromElement(captionWrap);
++
++        if (captionPosition == CaptionPosition.LEFT
++                || captionPosition == CaptionPosition.RIGHT) {
++            int captionWidth;
++            if (layoutManager != null) {
++                captionWidth = layoutManager.getOuterWidth(caption)
++                        - layoutManager.getMarginWidth(caption);
++            } else {
++                captionWidth = caption.getOffsetWidth();
++            }
++            if (captionWidth > 0) {
++                if (captionPosition == CaptionPosition.LEFT) {
++                    captionWrapStyle.setPaddingLeft(captionWidth, Unit.PX);
++                    captionStyle.setMarginLeft(-captionWidth, Unit.PX);
++                } else {
++                    captionWrapStyle.setPaddingRight(captionWidth, Unit.PX);
++                    captionStyle.setMarginRight(-captionWidth, Unit.PX);
++                }
++            }
++        }
++        if (captionPosition == CaptionPosition.TOP
++                || captionPosition == CaptionPosition.BOTTOM) {
++            int captionHeight;
++            if (layoutManager != null) {
++                captionHeight = layoutManager.getOuterHeight(caption)
++                        - layoutManager.getMarginHeight(caption);
++            } else {
++                captionHeight = caption.getOffsetHeight();
++            }
++            if (captionHeight > 0) {
++                if (captionPosition == CaptionPosition.TOP) {
++                    captionWrapStyle.setPaddingTop(captionHeight, Unit.PX);
++                    captionStyle.setMarginTop(-captionHeight, Unit.PX);
++                } else {
++                    captionWrapStyle.setPaddingBottom(captionHeight, Unit.PX);
++                    captionStyle.setMarginBottom(-captionHeight, Unit.PX);
++                }
++            }
++        }
++    }
++
++    private void toggleStyleName(String name, boolean enabled) {
++        if (enabled) {
++            addStyleName(name);
++        } else {
++            removeStyleName(name);
++        }
++    }
++
++    void setMargin(MarginInfo marginInfo) {
++        toggleStyleName("v-margin-top", marginInfo.hasTop());
++        toggleStyleName("v-margin-right", marginInfo.hasRight());
++        toggleStyleName("v-margin-bottom", marginInfo.hasBottom());
++        toggleStyleName("v-margin-left", marginInfo.hasLeft());
++    }
++
++    protected void setSpacing(boolean spacingEnabled) {
++        spacing = spacingEnabled;
++        for (Slot slot : widgetToSlot.values()) {
++            if (getWidgetIndex(slot) > 0) {
++                slot.setSpacing(spacingEnabled);
++            }
++        }
++    }
++
++    private void recalculateExpands() {
++        double total = 0;
++        for (Slot slot : widgetToSlot.values()) {
++            if (slot.getExpandRatio() > -1) {
++                total += slot.getExpandRatio();
++            } else {
++                if (vertical) {
++                    slot.getElement().getStyle().clearHeight();
++                } else {
++                    slot.getElement().getStyle().clearWidth();
++                }
++            }
++        }
++        for (Slot slot : widgetToSlot.values()) {
++            if (slot.getExpandRatio() > -1) {
++                if (vertical) {
++                    slot.setHeight((100 * (slot.getExpandRatio() / total))
++                            + "%");
++                    if (slot.connector.isRelativeHeight()) {
++                        layoutManager.setNeedsMeasure(slot.connector);
++                    }
++                } else {
++                    slot.setWidth((100 * (slot.getExpandRatio() / total)) + "%");
++                    if (slot.connector.isRelativeWidth()) {
++                        layoutManager.setNeedsMeasure(slot.connector);
++                    }
++                }
++            }
++        }
++    }
++
++    private Element expandWrapper;
++
++    void clearExpand() {
++        if (expandWrapper != null) {
++            for (; expandWrapper.getChildCount() > 0;) {
++                Element el = expandWrapper.getChild(0).cast();
++                getElement().appendChild(el);
++                if (vertical) {
++                    el.getStyle().clearHeight();
++                    el.getStyle().clearMarginTop();
++                } else {
++                    el.getStyle().clearWidth();
++                    el.getStyle().clearMarginLeft();
++                }
++            }
++            expandWrapper.removeFromParent();
++            expandWrapper = null;
++        }
++    }
++
++    public void updateExpand() {
++        boolean isExpanding = false;
++        for (Widget slot : getChildren()) {
++            if (((Slot) slot).getExpandRatio() > -1) {
++                isExpanding = true;
++            } else {
++                if (vertical) {
++                    slot.getElement().getStyle().clearHeight();
++                } else {
++                    slot.getElement().getStyle().clearWidth();
++                }
++            }
++            slot.getElement().getStyle().clearMarginLeft();
++            slot.getElement().getStyle().clearMarginTop();
++        }
++
++        if (isExpanding) {
++            if (expandWrapper == null) {
++                expandWrapper = DOM.createDiv();
++                expandWrapper.setClassName("v-expand");
++                for (; getElement().getChildCount() > 0;) {
++                    Node el = getElement().getChild(0);
++                    expandWrapper.appendChild(el);
++                }
++                getElement().appendChild(expandWrapper);
++            }
++
++            int totalSize = 0;
++            for (Widget w : getChildren()) {
++                Slot slot = (Slot) w;
++                if (slot.getExpandRatio() == -1) {
++                    if (layoutManager != null) {
++                        // TODO check caption position
++                        if (vertical) {
++                            int size = layoutManager.getOuterHeight(slot
++                                    .getWidget().getElement())
++                                    - layoutManager.getMarginHeight(slot
++                                            .getWidget().getElement());
++                            if (slot.hasCaption()) {
++                                size += layoutManager.getOuterHeight(slot
++                                        .getCaptionElement())
++                                        - layoutManager.getMarginHeight(slot
++                                                .getCaptionElement());
++                            }
++                            if (size > 0) {
++                                totalSize += size;
++                            }
++                        } else {
++                            int max = -1;
++                            max = layoutManager.getOuterWidth(slot.getWidget()
++                                    .getElement())
++                                    - layoutManager.getMarginWidth(slot
++                                            .getWidget().getElement());
++                            if (slot.hasCaption()) {
++                                int max2 = layoutManager.getOuterWidth(slot
++                                        .getCaptionElement())
++                                        - layoutManager.getMarginWidth(slot
++                                                .getCaptionElement());
++                                max = Math.max(max, max2);
++                            }
++                            if (max > 0) {
++                                totalSize += max;
++                            }
++                        }
++                    } else {
++                        totalSize += vertical ? slot.getOffsetHeight() : slot
++                                .getOffsetWidth();
++                    }
++                }
++                // TODO fails in Opera, always returns 0
++                int spacingSize = slot.getSpacingSize(vertical);
++                if (spacingSize > 0) {
++                    totalSize += spacingSize;
++                }
++            }
++
++            // When we set the margin to the first child, we don't need
++            // overflow:hidden in the layout root element, since the wrapper
++            // would otherwise be placed outside of the layout root element
++            // and block events on elements below it.
++            if (vertical) {
++                expandWrapper.getStyle().setPaddingTop(totalSize, Unit.PX);
++                expandWrapper.getFirstChildElement().getStyle()
++                .setMarginTop(-totalSize, Unit.PX);
++            } else {
++                expandWrapper.getStyle().setPaddingLeft(totalSize, Unit.PX);
++                expandWrapper.getFirstChildElement().getStyle()
++                .setMarginLeft(-totalSize, Unit.PX);
++            }
++
++            recalculateExpands();
++        }
++    }
++
++    public void recalculateLayoutHeight() {
++        // Only needed if a horizontal layout is undefined high, and contains
++        // relative height children or vertical alignments
++        if (vertical || definedHeight) {
++            return;
++        }
++
++        boolean hasRelativeHeightChildren = false;
++        boolean hasVAlign = false;
++
++        for (Widget slot : getChildren()) {
++            Widget widget = ((Slot) slot).getWidget();
++            String h = widget.getElement().getStyle().getHeight();
++            if (h != null && h.indexOf("%") > -1) {
++                hasRelativeHeightChildren = true;
++            }
++            AlignmentInfo a = ((Slot) slot).getAlignment();
++            if (a != null && (a.isVerticalCenter() || a.isBottom())) {
++                hasVAlign = true;
++            }
++        }
++
++        if (hasRelativeHeightChildren || hasVAlign) {
++            int newHeight;
++            if (layoutManager != null) {
++                newHeight = layoutManager.getOuterHeight(getElement())
++                        - layoutManager.getMarginHeight(getElement());
++            } else {
++                newHeight = getElement().getOffsetHeight();
++            }
++            VBoxLayout.this.getElement().getStyle()
++            .setHeight(newHeight, Unit.PX);
++        }
++
++    }
++
++    void clearHeight() {
++        getElement().getStyle().clearHeight();
++    }
++
++    @Override
++    public void setHeight(String height) {
++        super.setHeight(height);
++        definedHeight = (height != null && !"".equals(height));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d1e1d9eee9104ddf37ace55457a48358b6798cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,19 @@@
++/* 
++@VaadinApache2LicenseForJavaFiles@
++ */
++package com.vaadin.client.ui.orderedlayout;
++
++import com.vaadin.shared.ui.Connect;
++import com.vaadin.shared.ui.Connect.LoadStyle;
++import com.vaadin.ui.VerticalLayout;
++
++@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
++public class VerticalBoxLayoutConnector extends AbstractBoxLayoutConnector {
++
++    @Override
++    public void init() {
++        super.init();
++        getWidget().setVertical(true);
++    }
++
++}
index 0000000000000000000000000000000000000000,ed708a03e5639d73b134a5988c2871c552a8389e..455c645144291b0356305d219e488e4c195998fb
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,30 +1,27 @@@
 -import com.vaadin.shared.ui.Connect;
 -import com.vaadin.shared.ui.Connect.LoadStyle;
 -import com.vaadin.ui.VerticalLayout;
+ /*
+  * Copyright 2011 Vaadin Ltd.
+  * 
+  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+  * use this file except in compliance with the License. You may obtain a copy of
+  * the License at
+  * 
+  * http://www.apache.org/licenses/LICENSE-2.0
+  * 
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+  * License for the specific language governing permissions and limitations under
+  * the License.
+  */
+ package com.vaadin.client.ui.orderedlayout;
 -@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
++//@Connect(value = VerticalLayout.class, loadStyle = LoadStyle.EAGER)
+ public class VerticalLayoutConnector extends AbstractOrderedLayoutConnector {
+     @Override
+     public VVerticalLayout getWidget() {
+         return (VVerticalLayout) super.getWidget();
+     }
+ }
diff --cc client/src/com/vaadin/terminal/gwt/DefaultWidgetSet.gwt.xml
index 278d92f38ff65806e8097164a3d34409f6068cac,8af257fe3cd46db0fb8c7b73d27fbd00fdd1855b..0000000000000000000000000000000000000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,13 -1,9 +1,0 @@@
--<module>
-       <!-- This GWT module defines the Vaadin DefaultWidgetSet. This is the module 
-               you want to extend when creating an extended widget set, or when creating 
-               a specialized widget set with a subset of the components. -->
-       <!-- Hint for WidgetSetBuilder not to automatically update the file -->
-       <!-- WS Compiler: manually edited -->
-       <inherits name="com.vaadin.Vaadin" />
-       <entry-point class="com.vaadin.terminal.gwt.client.ApplicationConfiguration" />
 -    <!-- This GWT module is provided for backwards compatibility only. You 
 -        should inherit com.vaadin.DefaultWidgetSet instead. -->
--
 -    <!-- Hint for WidgetSetBuilder not to automatically update the file -->
 -    <!-- WS Compiler: manually edited -->
 -    <inherits name="com.vaadin.DefaultWidgetSet" />
 -    
--</module>
index d235086ca98f9d722e8bfbf49ec0ce411b8e7a68,0000000000000000000000000000000000000000..bacfc62b40709efb58b1d7f4ce395c671e7a521e
mode 100644,000000..100644
--- /dev/null
@@@ -1,487 -1,0 +1,487 @@@
- import com.vaadin.terminal.ThemeResource;
- import com.vaadin.terminal.UserError;
- import com.vaadin.terminal.WrappedRequest;
 +package com.vaadin.tests.components.orderedlayout;
 +
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +
 +import com.vaadin.annotations.Theme;
 +import com.vaadin.data.Property.ValueChangeEvent;
 +import com.vaadin.data.Property.ValueChangeListener;
 +import com.vaadin.event.LayoutEvents.LayoutClickEvent;
 +import com.vaadin.event.LayoutEvents.LayoutClickListener;
++import com.vaadin.server.ThemeResource;
++import com.vaadin.server.UserError;
++import com.vaadin.server.WrappedRequest;
 +import com.vaadin.shared.ui.label.ContentMode;
 +import com.vaadin.tests.components.AbstractTestUI;
 +import com.vaadin.ui.AbstractComponent;
 +import com.vaadin.ui.AbstractField;
 +import com.vaadin.ui.AbstractOrderedLayout;
 +import com.vaadin.ui.Alignment;
 +import com.vaadin.ui.Button;
 +import com.vaadin.ui.Button.ClickEvent;
 +import com.vaadin.ui.CheckBox;
 +import com.vaadin.ui.Component;
 +import com.vaadin.ui.GridLayout;
 +import com.vaadin.ui.HorizontalLayout;
 +import com.vaadin.ui.Label;
 +import com.vaadin.ui.NativeSelect;
 +import com.vaadin.ui.TextField;
 +import com.vaadin.ui.VerticalLayout;
 +import com.vaadin.ui.themes.Reindeer;
 +
 +@Theme("tests-components")
 +public class BoxLayoutTest extends AbstractTestUI {
 +
 +    protected AbstractOrderedLayout view;
 +
 +    protected AbstractOrderedLayout l;
 +
 +    protected AbstractComponent target;
 +
 +    protected NativeSelect componentWidth;
 +    protected NativeSelect componentHeight;
 +    protected NativeSelect componentCaption;
 +    protected NativeSelect componentIcon;
 +    protected TextField componentDescription;
 +    protected CheckBox componentError;
 +    protected CheckBox componentRequired;
 +
 +    protected NativeSelect align;
 +    protected CheckBox expand;
 +
 +    @Override
 +    protected void setup(WrappedRequest request) {
 +
 +        view = new VerticalLayout();
 +        // view.setSizeFull();
 +        view.setMargin(true);
 +        view.setSpacing(true);
 +
 +        // view.addComponent(createControls(false));
 +        // view.addComponent(createTestLayout(false));
 +        // view.setExpandRatio(view.getComponent(1), 1);
 +
 +        for (int i = 0; i < 20; i++) {
 +            view.addComponent(createHorizontalTest());
 +        }
 +
 +        setContent(view);
 +        getApplication().setUiPreserved(true);
 +    }
 +
 +    private Component createHorizontalTest() {
 +        HorizontalLayout l = new HorizontalLayout();
 +        l.setWidth("100%");
 +        l.setSpacing(true);
 +
 +        Label exp;
 +
 +        // l.addComponent(new Embedded(null, new ThemeResource(
 +        // "../runo/icons/32/document.png")));
 +        l.addComponent(exp = new Label(
 +                "Mauris iaculis porttitor posuere. Praesent id metus massa, ut blandit odio. Proin quis tortor orci. Etiam at risus et justo dignissim congue. Donec."));
 +        // exp.setWidth("300px");
 +        l.addComponent(new Button("Edit"));
 +        l.addComponent(new Button("Delete"));
 +        l.setExpandRatio(exp, 1);
 +
 +        for (int i = 0; i < l.getComponentCount(); i++) {
 +            l.setComponentAlignment(l.getComponent(i), Alignment.MIDDLE_LEFT);
 +        }
 +
 +        return l;
 +    }
 +
 +    protected AbstractOrderedLayout createControls(boolean horizontal) {
 +        VerticalLayout root = new VerticalLayout();
 +        root.setSpacing(true);
 +
 +        // First row
 +        HorizontalLayout header = new HorizontalLayout();
 +        header.setSpacing(true);
 +        root.addComponent(header);
 +
 +        Label title = new Label("BoxLayout Test");
 +        title.addStyleName(Reindeer.LABEL_H1);
 +        header.addComponent(title);
 +
 +        final CheckBox vertical = new CheckBox("Vertical", !horizontal);
 +        vertical.setImmediate(true);
 +        vertical.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                view.removeAllComponents();
 +
 +                view.addComponent(createControls(!vertical.getValue()
 +                        .booleanValue()));
 +                view.addComponent(createTestLayout(!vertical.getValue()
 +                        .booleanValue()));
 +
 +                view.setExpandRatio(view.getComponent(1), 1);
 +
 +            }
 +        });
 +        header.addComponent(vertical);
 +
 +        Button addComponent = new Button("Add Component",
 +                new Button.ClickListener() {
 +            @Override
 +            public void buttonClick(ClickEvent event) {
 +                GridLayout grid = new GridLayout(2, 2);
 +                Button grow = new Button("Grow Me",
 +                        new Button.ClickListener() {
 +                    @Override
 +                    public void buttonClick(ClickEvent event) {
 +                        if (event.getButton().getWidth() == -1) {
 +                            event.getButton().setHeight("50px");
 +                            event.getButton().setWidth("200px");
 +                        } else {
 +                            event.getButton()
 +                            .setSizeUndefined();
 +                        }
 +                    }
 +                });
 +                grid.addComponent(new Label("Grid cell 1"));
 +                grid.addComponent(new Label("Grid cell 2"));
 +                grid.addComponent(grow);
 +                grid.addComponent(new Label("Grid cell 4"));
 +                l.addComponent(grid);
 +                // l.addComponent(new TextField("Some field"));
 +            }
 +        });
 +        header.addComponent(addComponent);
 +
 +        Button removeComponent = new Button("Remove Component",
 +                new Button.ClickListener() {
 +            @Override
 +            public void buttonClick(ClickEvent event) {
 +                Component last = l.getComponent(l.getComponentCount() - 1);
 +                l.removeComponent(last);
 +            }
 +        });
 +        header.addComponent(removeComponent);
 +
 +        // Second row
 +        HorizontalLayout controls = new HorizontalLayout();
 +        controls.setSpacing(true);
 +        root.addComponent(controls);
 +
 +        // Layout controls
 +        HorizontalLayout layout = new HorizontalLayout();
 +        layout.addStyleName("fieldset");
 +        layout.setSpacing(true);
 +        controls.addComponent(layout);
 +        layout.addComponent(new Label("Layout"));
 +
 +        ArrayList<String> sizes = new ArrayList<String>();
 +        sizes.addAll(Arrays.asList("100px", "30em", "100%"));
 +
 +        final NativeSelect width = new NativeSelect(null, sizes);
 +        width.setImmediate(true);
 +        width.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (width.getValue() != null) {
 +                    l.setWidth(width.getValue().toString());
 +                } else {
 +                    l.setWidth(null);
 +                }
 +            }
 +        });
 +        layout.addComponent(width);
 +        layout.addComponent(new Label("&times;", ContentMode.XHTML));
 +        final NativeSelect height = new NativeSelect(null, sizes);
 +        height.setImmediate(true);
 +        height.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (height.getValue() != null) {
 +                    l.setHeight(height.getValue().toString());
 +                } else {
 +                    l.setHeight(null);
 +                }
 +            }
 +        });
 +        layout.addComponent(height);
 +
 +        final CheckBox margin = new CheckBox("Margin", false);
 +        margin.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                l.setMargin(margin.getValue().booleanValue());
 +            }
 +        });
 +        margin.setImmediate(true);
 +        layout.addComponent(margin);
 +        layout.addComponent(margin);
 +
 +        final CheckBox spacing = new CheckBox("Spacing", false);
 +        spacing.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                l.setSpacing(spacing.getValue().booleanValue());
 +            }
 +        });
 +        spacing.setImmediate(true);
 +        layout.addComponent(spacing);
 +
 +        // Cell controls
 +        HorizontalLayout cell = new HorizontalLayout();
 +        cell.addStyleName("fieldset");
 +        cell.setSpacing(true);
 +        controls.addComponent(cell);
 +        cell.addComponent(new Label("Cell"));
 +
 +        ArrayList<Alignment> alignments = new ArrayList<Alignment>();
 +        alignments.addAll(Arrays.asList(Alignment.TOP_LEFT,
 +                Alignment.MIDDLE_LEFT, Alignment.BOTTOM_LEFT,
 +                Alignment.TOP_CENTER, Alignment.MIDDLE_CENTER,
 +                Alignment.BOTTOM_CENTER, Alignment.TOP_RIGHT,
 +                Alignment.MIDDLE_RIGHT, Alignment.BOTTOM_RIGHT));
 +
 +        align = new NativeSelect(null, alignments);
 +        for (Alignment a : alignments) {
 +            align.setItemCaption(a,
 +                    a.getVerticalAlignment() + "-" + a.getHorizontalAlignment());
 +        }
 +        align.setImmediate(true);
 +        align.setEnabled(false);
 +        align.setNullSelectionAllowed(false);
 +        align.select(Alignment.TOP_LEFT);
 +        align.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (target == null) {
 +                    return;
 +                }
 +                l.setComponentAlignment(target, ((Alignment) align.getValue()));
 +            }
 +        });
 +        cell.addComponent(align);
 +
 +        expand = new CheckBox("Expand");
 +        expand.setImmediate(true);
 +        expand.setEnabled(false);
 +        expand.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (target != null) {
 +                    l.setExpandRatio(target, expand.getValue() ? 1 : 0);
 +                }
 +            }
 +        });
 +        cell.addComponent(expand);
 +
 +        // Component controls
 +        HorizontalLayout component = new HorizontalLayout();
 +        component.addStyleName("fieldset");
 +        component.setSpacing(true);
 +        root.addComponent(component);
 +        component.addComponent(new Label("Component"));
 +
 +        sizes = new ArrayList<String>();
 +        sizes.addAll(Arrays.asList("50px", "200px", "10em", "50%", "100%"));
 +
 +        componentWidth = new NativeSelect(null, sizes);
 +        componentWidth.setImmediate(true);
 +        componentWidth.setEnabled(false);
 +        componentWidth.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (target == null) {
 +                    return;
 +                }
 +                if (componentWidth.getValue() != null) {
 +                    target.setWidth(componentWidth.getValue().toString());
 +                } else {
 +                    target.setWidth(null);
 +                }
 +            }
 +        });
 +        component.addComponent(componentWidth);
 +        component.addComponent(new Label("&times;", ContentMode.XHTML));
 +
 +        componentHeight = new NativeSelect(null, sizes);
 +        componentHeight.setImmediate(true);
 +        componentHeight.setEnabled(false);
 +        componentHeight.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (componentHeight.getValue() != null) {
 +                    target.setHeight(componentHeight.getValue().toString());
 +                } else {
 +                    target.setHeight(null);
 +                }
 +            }
 +        });
 +        component.addComponent(componentHeight);
 +
 +        componentCaption = new NativeSelect("Caption", Arrays.asList("Short",
 +                "Slightly Longer Caption"));
 +        componentCaption.setImmediate(true);
 +        componentCaption.setEnabled(false);
 +        componentCaption.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (componentCaption.getValue() != null) {
 +                    target.setCaption(componentCaption.getValue().toString());
 +                } else {
 +                    target.setCaption(null);
 +                }
 +            }
 +        });
 +        component.addComponent(componentCaption);
 +
 +        componentIcon = new NativeSelect("Icon", Arrays.asList(
 +                "../runo/icons/16/folder.png", "../runo/icons/32/document.png"));
 +        componentIcon.setImmediate(true);
 +        componentIcon.setEnabled(false);
 +        componentIcon.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (componentIcon.getValue() != null) {
 +                    target.setIcon(new ThemeResource(componentIcon.getValue()
 +                            .toString()));
 +                } else {
 +                    target.setIcon(null);
 +                }
 +            }
 +        });
 +        component.addComponent(componentIcon);
 +
 +        componentDescription = new TextField("Description");
 +        componentDescription.setImmediate(true);
 +        componentDescription.setEnabled(false);
 +        componentDescription.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                target.setDescription(componentDescription.getValue());
 +            }
 +        });
 +        component.addComponent(componentDescription);
 +
 +        componentError = new CheckBox("Error");
 +        componentError.setImmediate(true);
 +        componentError.setEnabled(false);
 +        componentError.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (target != null) {
 +                    target.setComponentError(componentError.getValue() ? new UserError(
 +                            "Error message") : null);
 +                }
 +            }
 +        });
 +        component.addComponent(componentError);
 +
 +        componentRequired = new CheckBox("Required");
 +        componentRequired.setImmediate(true);
 +        componentRequired.setEnabled(false);
 +        componentRequired.addListener(new ValueChangeListener() {
 +            @Override
 +            public void valueChange(ValueChangeEvent event) {
 +                if (target != null && target instanceof AbstractField) {
 +                    ((AbstractField<?>) target).setRequired(componentRequired
 +                            .getValue());
 +                }
 +            }
 +        });
 +        component.addComponent(componentRequired);
 +
 +        for (int i = 0; i < component.getComponentCount(); i++) {
 +            component.setComponentAlignment(component.getComponent(i),
 +                    Alignment.MIDDLE_LEFT);
 +        }
 +
 +        return root;
 +    }
 +
 +    protected AbstractOrderedLayout createTestLayout(boolean horizontal) {
 +        l = horizontal ? new HorizontalLayout() : new VerticalLayout();
 +        l.setSizeUndefined();
 +        l.addStyleName("test");
 +
 +        Label label = new Label("Component 1");
 +        l.addComponent(label);
 +        l.addComponent(new Button("Component 2"));
 +
 +        l.addListener(new LayoutClickListener() {
 +            @Override
 +            public void layoutClick(LayoutClickEvent event) {
 +                if (event.getChildComponent() == null
 +                        || target == event.getChildComponent()) {
 +                    if (target != null) {
 +                        target.removeStyleName("target");
 +                    }
 +                    target = null;
 +                } else if (target != event.getChildComponent()) {
 +                    if (target != null) {
 +                        target.removeStyleName("target");
 +                    }
 +                    target = (AbstractComponent) event.getChildComponent();
 +                    target.addStyleName("target");
 +                }
 +                componentWidth.setEnabled(target != null);
 +                componentHeight.setEnabled(target != null);
 +                componentCaption.setEnabled(target != null);
 +                componentIcon.setEnabled(target != null);
 +                componentDescription.setEnabled(target != null);
 +                componentError.setEnabled(target != null);
 +                componentRequired.setEnabled(target != null
 +                        && target instanceof AbstractField);
 +                align.setEnabled(target != null);
 +                expand.setEnabled(target != null);
 +                if (target != null) {
 +                    if (target.getWidth() > -1) {
 +                        componentWidth.select(new Float(target.getWidth())
 +                        .intValue()
 +                        + target.getWidthUnits().getSymbol());
 +                    } else {
 +                        componentWidth.select(null);
 +                    }
 +                    if (target.getHeight() > -1) {
 +                        componentHeight.select(new Float(target.getHeight())
 +                        .intValue()
 +                        + target.getHeightUnits().getSymbol());
 +                    } else {
 +                        componentHeight.select(null);
 +                    }
 +
 +                    align.select(l.getComponentAlignment(target));
 +                    expand.setValue(new Boolean(l.getExpandRatio(target) > 0));
 +
 +                    componentCaption.select(target.getCaption());
 +                    if (target.getIcon() != null) {
 +                        componentIcon.select(((ThemeResource) target.getIcon())
 +                                .getResourceId());
 +                    } else {
 +                        componentIcon.select(null);
 +                    }
 +                    componentDescription.setValue(target.getDescription());
 +                    componentError.setValue(target.getComponentError() != null);
 +                    if (target instanceof AbstractField) {
 +                        componentRequired.setValue(((AbstractField<?>) target)
 +                                .isRequired());
 +                    }
 +                }
 +            }
 +        });
 +
 +        target = null;
 +
 +        return l;
 +    }
 +
 +    @Override
 +    protected String getTestDescription() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +    @Override
 +    protected Integer getTicketNumber() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +}
index 961ec86ab0db1c44dd3211f3de75ac809591afb0,0000000000000000000000000000000000000000..64eea46b64aca8431517d0a7ec17bcce4a5d6a36
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,58 @@@
- import com.vaadin.terminal.ThemeResource;
 +package com.vaadin.tests.components.orderedlayout;
 +
 +import com.vaadin.Application.LegacyApplication;
++import com.vaadin.server.ThemeResource;
 +import com.vaadin.ui.Button;
 +import com.vaadin.ui.Embedded;
 +import com.vaadin.ui.HorizontalLayout;
 +import com.vaadin.ui.Label;
 +import com.vaadin.ui.UI;
 +import com.vaadin.ui.VerticalLayout;
 +import com.vaadin.ui.themes.Reindeer;
 +
 +public class LayoutRenderTimeTest extends LegacyApplication {
 +
 +    @Override
 +    public void init() {
 +        UI.LegacyWindow main = new UI.LegacyWindow();
 +        setMainWindow(main);
 +
 +        VerticalLayout root = new VerticalLayout();
 +        root.setWidth("100%");
 +        main.setContent(root);
 +
 +        for (int i = 1; i <= 100; i++) {
 +            root.addComponent(getRow(i));
 +        }
 +    }
 +
 +    private HorizontalLayout getRow(int i) {
 +        HorizontalLayout row = new HorizontalLayout();
 +        // row.setWidth("100%");
 +        // row.setSpacing(true);
 +
 +        Embedded icon = new Embedded(null, new ThemeResource(
 +                "../runo/icons/32/document.png"));
 +        // row.addComponent(icon);
 +        // row.setComponentAlignment(icon, Alignment.MIDDLE_LEFT);
 +
 +        Label text = new Label(
 +                "Row content #"
 +                        + i
 +                        + ". In pellentesque faucibus vestibulum. Nulla at nulla justo, eget luctus tortor. Nulla facilisi. Duis aliquet.");
 +        // row.addComponent(text);
 +        // row.setExpandRatio(text, 1);
 +
 +        Button button = new Button("Edit");
 +        button.addStyleName(Reindeer.BUTTON_SMALL);
 +        row.addComponent(button);
 +        // row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
 +
 +        button = new Button("Delete");
 +        button.addStyleName(Reindeer.BUTTON_SMALL);
 +        row.addComponent(button);
 +        // row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
 +
 +        return row;
 +    }
 +}
index 5ebb702baf91a240dab7779349eed009854f09d9,0000000000000000000000000000000000000000..a14f37f95ef5ac21adf127b7b60e7081fec64677
mode 100644,000000..100644
--- /dev/null
@@@ -1,347 -1,0 +1,347 @@@
- import com.vaadin.terminal.Sizeable;
- import com.vaadin.terminal.ThemeResource;
- import com.vaadin.terminal.WrappedRequest;
 +package com.vaadin.tests.components.orderedlayout;
 +
 +import com.vaadin.annotations.Theme;
++import com.vaadin.server.Sizeable;
++import com.vaadin.server.ThemeResource;
++import com.vaadin.server.WrappedRequest;
 +import com.vaadin.shared.ui.MarginInfo;
 +import com.vaadin.shared.ui.slider.SliderOrientation;
 +import com.vaadin.tests.components.AbstractTestUI;
 +import com.vaadin.ui.Alignment;
 +import com.vaadin.ui.Button;
 +import com.vaadin.ui.ComboBox;
 +import com.vaadin.ui.Embedded;
 +import com.vaadin.ui.HorizontalLayout;
 +import com.vaadin.ui.HorizontalSplitPanel;
 +import com.vaadin.ui.Label;
 +import com.vaadin.ui.NativeButton;
 +import com.vaadin.ui.NativeSelect;
 +import com.vaadin.ui.Slider;
 +import com.vaadin.ui.Table;
 +import com.vaadin.ui.VerticalLayout;
 +
 +@Theme("tests-components")
 +public class VaadinTunesLayout extends AbstractTestUI {
 +
 +    @Override
 +    public void setup(WrappedRequest request) {
 +
 +        /*
 +         * We'll build the whole UI here, since the application will not contain
 +         * any logic. Otherwise it would be more practical to separate parts of
 +         * the UI into different classes and methods.
 +         */
 +
 +        // Main (browser) window, needed in all Vaadin applications
 +        VerticalLayout rootLayout = new VerticalLayout();
 +        // final Window root = new Window("VaadinTunes", rootLayout);
 +
 +        /*
 +         * We'll attach the window to the browser view already here, so we won't
 +         * forget it later.
 +         */
 +        setContent(rootLayout);
 +
 +        // root.showNotification(
 +        // "This is an example of how you can do layouts in Vaadin.<br/>It is not a working sound player.",
 +        // Notification.TYPE_HUMANIZED_MESSAGE);
 +
 +        // Our root window contains one VerticalLayout, let's make
 +        // sure it's 100% sized, and remove unwanted margins
 +        rootLayout.setSizeFull();
 +        rootLayout.setMargin(false);
 +
 +        // Top area, containing playback and volume controls, play status, view
 +        // modes and search
 +        HorizontalLayout top = new HorizontalLayout();
 +        top.setWidth("100%");
 +        top.setMargin(new MarginInfo(false, true, false, true)); // Enable
 +        // horizontal
 +        // margins
 +        top.setSpacing(true);
 +
 +        // Let's attach that one straight away too
 +        rootLayout.addComponent(top);
 +
 +        // Create the placeholders for all the components in the top area
 +        HorizontalLayout playback = new HorizontalLayout();
 +        HorizontalLayout volume = new HorizontalLayout();
 +        HorizontalLayout status = new HorizontalLayout();
 +        HorizontalLayout viewmodes = new HorizontalLayout();
 +        ComboBox search = new ComboBox();
 +
 +        // Add the components and align them properly
 +        top.addComponent(playback);
 +        top.addComponent(volume);
 +        top.addComponent(status);
 +        top.addComponent(viewmodes);
 +        top.addComponent(search);
 +        top.setComponentAlignment(playback, Alignment.MIDDLE_LEFT);
 +        top.setComponentAlignment(volume, Alignment.MIDDLE_LEFT);
 +        top.setComponentAlignment(status, Alignment.MIDDLE_CENTER);
 +        top.setComponentAlignment(viewmodes, Alignment.MIDDLE_LEFT);
 +        top.setComponentAlignment(search, Alignment.MIDDLE_LEFT);
 +
 +        /*
 +         * We want our status area to expand if the user resizes the root
 +         * window, and we want it to accommodate as much space as there is
 +         * available. All other components in the top layout should stay fixed
 +         * sized, so we don't need to specify any expand ratios for them (they
 +         * will automatically revert to zero after the following line).
 +         */
 +        top.setExpandRatio(status, 1.0F);
 +
 +        // Playback controls
 +        Button prev = new NativeButton("Previous");
 +        Button play = new NativeButton("Play/pause");
 +        Button next = new NativeButton("Next");
 +        playback.addComponent(prev);
 +        playback.addComponent(play);
 +        playback.addComponent(next);
 +        // Set spacing between the buttons
 +        playback.setSpacing(true);
 +
 +        // Volume controls
 +        Button mute = new NativeButton("mute");
 +        Slider vol = new Slider();
 +        vol.setOrientation(SliderOrientation.HORIZONTAL);
 +        vol.setWidth("100px");
 +        Button max = new NativeButton("max");
 +        volume.addComponent(mute);
 +        volume.addComponent(vol);
 +        volume.addComponent(max);
 +
 +        // Status area
 +        status.setWidth("80%");
 +        status.setSpacing(true);
 +
 +        Button toggleVisualization = new NativeButton("Mode");
 +        Label timeFromStart = new Label("0:00");
 +
 +        // We'll need another layout to show currently playing track and
 +        // progress
 +        VerticalLayout trackDetails = new VerticalLayout();
 +        trackDetails.setWidth("100%");
 +        Label track = new Label("Track Name");
 +        Label album = new Label("Album Name - Artist");
 +        track.setWidth(null);
 +        album.setWidth(null);
 +        Slider progress = new Slider();
 +        progress.setOrientation(SliderOrientation.HORIZONTAL);
 +        progress.setWidth("100%");
 +        trackDetails.addComponent(track);
 +        trackDetails.addComponent(album);
 +        trackDetails.addComponent(progress);
 +        trackDetails.setComponentAlignment(track, Alignment.TOP_CENTER);
 +        trackDetails.setComponentAlignment(album, Alignment.TOP_CENTER);
 +
 +        Label timeToEnd = new Label("-4:46");
 +        Button jumpToTrack = new NativeButton("Show");
 +
 +        // Place all components to the status layout and align them properly
 +        status.addComponent(toggleVisualization);
 +        status.setComponentAlignment(toggleVisualization, Alignment.MIDDLE_LEFT);
 +        status.addComponent(timeFromStart);
 +        status.setComponentAlignment(timeFromStart, Alignment.BOTTOM_LEFT);
 +        status.addComponent(trackDetails);
 +        status.addComponent(timeToEnd);
 +        status.setComponentAlignment(timeToEnd, Alignment.BOTTOM_LEFT);
 +        status.addComponent(jumpToTrack);
 +        status.setComponentAlignment(jumpToTrack, Alignment.MIDDLE_LEFT);
 +
 +        // Then remember to specify the expand ratio
 +        status.setExpandRatio(trackDetails, 1.0F);
 +
 +        // View mode buttons
 +        Button viewAsTable = new NativeButton("Table");
 +        Button viewAsGrid = new NativeButton("Grid");
 +        Button coverflow = new NativeButton("Coverflow");
 +        viewmodes.addComponent(viewAsTable);
 +        viewmodes.addComponent(viewAsGrid);
 +        viewmodes.addComponent(coverflow);
 +
 +        /*
 +         * That covers the top bar. Now let's move on to the sidebar and track
 +         * listing
 +         */
 +
 +        // We'll need one splitpanel to separate the sidebar and track listing
 +        HorizontalSplitPanel bottom = new HorizontalSplitPanel();
 +        rootLayout.addComponent(bottom);
 +
 +        // The splitpanel is by default 100% x 100%, but we'll need to adjust
 +        // our main window layout to accomodate the height
 +        rootLayout.setExpandRatio(bottom, 1.0F);
 +
 +        // Give the sidebar less space than the listing
 +        bottom.setSplitPosition(200, Sizeable.UNITS_PIXELS);
 +
 +        // Let's add some content to the sidebar
 +        // First, we need a layout to but all components in
 +        VerticalLayout sidebar = new VerticalLayout();
 +        sidebar.setSizeFull();
 +        bottom.setFirstComponent(sidebar);
 +
 +        /*
 +         * Then we need some labels and buttons, and an album cover image The
 +         * labels and buttons go into their own vertical layout, since we want
 +         * the 'sidebar' layout to be expanding (cover image in the bottom).
 +         * VerticalLayout is by default 100% wide.
 +         */
 +        VerticalLayout selections = new VerticalLayout();
 +        Label library = new Label("Library");
 +        Button music = new NativeButton("Music");
 +        music.setWidth("100%");
 +
 +        Label store = new Label("Store");
 +        Button vaadinTunesStore = new NativeButton("VaadinTunes Store");
 +        vaadinTunesStore.setWidth("100%");
 +        Button purchased = new NativeButton("Purchased");
 +        purchased.setWidth("100%");
 +
 +        Label playlists = new Label("Playlists");
 +        Button genius = new NativeButton("Geniues");
 +        genius.setWidth("100%");
 +        Button recent = new NativeButton("Recently Added");
 +        recent.setWidth("100%");
 +
 +        // Lets add them to the 'selections' layout
 +        selections.addComponent(library);
 +        selections.addComponent(music);
 +        selections.addComponent(store);
 +        selections.addComponent(vaadinTunesStore);
 +        selections.addComponent(purchased);
 +        selections.addComponent(playlists);
 +        selections.addComponent(genius);
 +        selections.addComponent(recent);
 +
 +        // Then add the selections to the sidebar, and set it expanding
 +        sidebar.addComponent(selections);
 +        sidebar.setExpandRatio(selections, 1.0F);
 +
 +        // Then comes the cover artwork (we'll add the actual image in the
 +        // themeing section)
 +        Embedded cover = new Embedded("Currently Playing");
 +        sidebar.addComponent(cover);
 +
 +        /*
 +         * And lastly, we need the track listing table It should fill the whole
 +         * left side of our bottom layout
 +         */
 +        Table listing = new Table();
 +        listing.setSizeFull();
 +        listing.setSelectable(true);
 +        bottom.setSecondComponent(listing);
 +
 +        // Add the table headers
 +        listing.addContainerProperty("Name", String.class, "");
 +        listing.addContainerProperty("Time", String.class, "0:00");
 +        listing.addContainerProperty("Artist", String.class, "");
 +        listing.addContainerProperty("Album", String.class, "");
 +        listing.addContainerProperty("Genre", String.class, "");
 +        listing.addContainerProperty("Rating", NativeSelect.class,
 +                new NativeSelect());
 +
 +        // Lets populate the table with random data
 +        String[] tracks = new String[] { "Red Flag", "Millstone",
 +                "Not The Sun", "Breath", "Here We Are", "Deep Heaven",
 +                "Her Voice Resides", "Natural Tan", "End It All", "Kings",
 +                "Daylight Slaving", "Mad Man", "Resolve", "Teargas",
 +                "African Air", "Passing Bird" };
 +        String[] times = new String[] { "4:12", "6:03", "5:43", "4:32", "3:42",
 +                "4:45", "2:56", "9:34", "2:10", "3:44", "5:49", "6:30", "5:18",
 +                "7:42", "3:13", "2:52" };
 +        String[] artists = new String[] { "Billy Talent", "Brand New",
 +                "Breaking Benjamin", "Becoming The Archetype",
 +                "Bullet For My Valentine", "Chasing Victory", "Chimaira",
 +                "Danko Jones", "Deadlock", "Deftones", "From Autumn To Ashes",
 +                "Haste The Day", "Four Year Strong", "In Flames", "Kemopetrol",
 +        "John Legend" };
 +        String[] albums = new String[] { "Once Again", "The Caitiff Choir",
 +                "The Devil And God", "Light Grenades", "Dicthonomy",
 +                "Back In Black", "Dreamer", "Come Clarity", "Year Zero",
 +                "Frames", "Fortress", "Phobia", "The Poison", "Manifesto",
 +                "White Pony", "The Big Dirty" };
 +        String[] genres = new String[] { "Rock", "Metal", "Hardcore", "Indie",
 +                "Pop", "Alternative", "Blues", "Jazz", "Hip Hop",
 +                "Electronica", "Punk", "Hard Rock", "Dance", "R'n'B", "Gospel",
 +        "Country" };
 +        for (int i = 0; i < 1000; i++) {
 +            NativeSelect s = new NativeSelect();
 +            s.addItem("1 star");
 +            s.addItem("2 stars");
 +            s.addItem("3 stars");
 +            s.addItem("4 stars");
 +            s.addItem("5 stars");
 +            s.select(i % 5 + " stars");
 +            final int index = i % 16;
 +            listing.addItem(new Object[] { tracks[index], times[index],
 +                    artists[index], albums[index], genres[index], s }, i);
 +        }
 +
 +        // We'll align the track time column to right as well
 +        listing.setColumnAlignment("Time", Table.ALIGN_RIGHT);
 +
 +        // TODO the footer
 +
 +        // Now what's left to do? Themeing of course.
 +        // setTheme("vaadintunes");
 +
 +        /*
 +         * Let's give a namespace to our application window. This way, if
 +         * someone uses the same theme for different applications, we don't get
 +         * unwanted style conflicts.
 +         */
 +        // root.setStyleName("tTunes");
 +
 +        top.setStyleName("top");
 +        top.setHeight("75px"); // Same as the background image height
 +
 +        playback.setStyleName("playback");
 +        playback.setMargin(new MarginInfo(false, true, false, false)); // Add
 +                                                                       // right-side
 +                                                                       // margin
 +        play.setStyleName("play");
 +        next.setStyleName("next");
 +        prev.setStyleName("prev");
 +        playback.setComponentAlignment(prev, Alignment.MIDDLE_LEFT);
 +        playback.setComponentAlignment(next, Alignment.MIDDLE_LEFT);
 +
 +        volume.setStyleName("volume");
 +        mute.setStyleName("mute");
 +        max.setStyleName("max");
 +        vol.setWidth("78px");
 +
 +        status.setStyleName("status");
 +        status.setMargin(true);
 +        status.setHeight("46px"); // Height of the background image
 +
 +        toggleVisualization.setStyleName("toggle-vis");
 +        jumpToTrack.setStyleName("jump");
 +
 +        viewAsTable.setStyleName("viewmode-table");
 +        viewAsGrid.setStyleName("viewmode-grid");
 +        coverflow.setStyleName("viewmode-coverflow");
 +
 +        sidebar.setStyleName("sidebar");
 +
 +        music.setStyleName("selected");
 +
 +        cover.setSource(new ThemeResource("images/album-cover.jpg"));
 +        // Because this is an image, it will retain it's aspect ratio
 +        cover.setWidth("100%");
 +    }
 +
 +    @Override
 +    protected String getTestDescription() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +    @Override
 +    protected Integer getTicketNumber() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +}
index f956a93cd5a1c40d073314a92719dbcbdaf29dd5,0000000000000000000000000000000000000000..66401343263b8bca6c789dd92e91a41a6f18253c
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,37 @@@
- import com.vaadin.terminal.WrappedRequest;
 +package com.vaadin.tests.themes;
 +
 +import com.vaadin.annotations.Theme;
++import com.vaadin.server.WrappedRequest;
 +import com.vaadin.tests.components.AbstractTestUI;
 +import com.vaadin.ui.Label;
 +import com.vaadin.ui.Panel;
 +import com.vaadin.ui.themes.LiferayTheme;
 +
 +@Theme("liferay")
 +public class LiferayThemeTest extends AbstractTestUI {
 +
 +    @Override
 +    protected void setup(WrappedRequest request) {
 +        Panel p = new Panel("Panel");
 +        addComponent(p);
 +        p.addComponent(new Label("Panel content"));
 +
 +        p = new Panel("Light Panel");
 +        p.addStyleName(LiferayTheme.PANEL_LIGHT);
 +        addComponent(p);
 +        p.addComponent(new Label("Panel content"));
 +    }
 +
 +    @Override
 +    protected String getTestDescription() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +    @Override
 +    protected Integer getTicketNumber() {
 +        // TODO Auto-generated method stub
 +        return null;
 +    }
 +
 +}
index 0000000000000000000000000000000000000000,74bb2160020c9953a2bf3bd7473fed2b08432a62..23a4322fb7aee726a0cbe005229a8a7f3b07a0a8
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,31 +1,30 @@@
 -        // TODO Auto-generated method stub
 -        return null;
+ package com.vaadin.tests.components.flash;
+ import com.vaadin.server.ExternalResource;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Flash;
+ public class FlashIsVisible extends TestBase {
+     @Override
+     protected void setup() {
+         Flash player = new Flash();
+         player.setWidth("400px");
+         player.setHeight("300px");
+         String url = "http://www.youtube.com/v/qQ9N742QB4g&autoplay=0";
+         player.setSource(new ExternalResource(url));
+         addComponent(player);
+     }
+     @Override
+     protected String getDescription() {
++        return "Flash plugin should load and be visible on all browsers";
+     }
+     @Override
+     protected Integer getTicketNumber() {
+         // TODO Auto-generated method stub
+         return null;
+     }
+ }
index 0000000000000000000000000000000000000000,4122bd626a59ab135e04de47468f54e8401b6867..ea3b319c5c96cccc8c0be5804910ed21497d52c2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,142 +1,145 @@@
 -import com.vaadin.ui.JavaScript;
+ package com.vaadin.tests.components.orderedlayout;
+ import com.vaadin.server.ThemeResource;
+ import com.vaadin.shared.ui.label.ContentMode;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Alignment;
+ import com.vaadin.ui.Button;
+ import com.vaadin.ui.Button.ClickEvent;
+ import com.vaadin.ui.Embedded;
+ import com.vaadin.ui.HorizontalLayout;
+ import com.vaadin.ui.HorizontalSplitPanel;
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        JavaScript
 -                                .getCurrent()
 -                                .execute(
 -                                        "setTimeout(function() {window.resizeTo(700,400)}, 500)");
 -                    }
 -                });
+ import com.vaadin.ui.Label;
+ import com.vaadin.ui.Table;
+ import com.vaadin.ui.VerticalLayout;
+ import com.vaadin.ui.VerticalSplitPanel;
+ import com.vaadin.ui.themes.Reindeer;
+ public class LayoutResizeTest extends TestBase {
+     @Override
+     protected void setup() {
+         getLayout().setSizeFull();
+         HorizontalSplitPanel split1 = new HorizontalSplitPanel();
+         split1.setSizeFull();
+         addComponent(split1);
+         VerticalLayout left = new VerticalLayout();
+         left.setSizeFull();
+         split1.setFirstComponent(left);
+         left.setSpacing(true);
+         left.setMargin(true);
+         left.addComponent(new Label("<h2>Layout resize test</h2>",
+                 ContentMode.XHTML));
+         Button resize = new Button("Resize to 700x400",
+                 new Button.ClickListener() {
 -                JavaScript
 -                        .getCurrent()
 -                        .execute(
 -                                "setTimeout(function() {window.resizeTo(900,600)}, 500)");
++            @Override
++            public void buttonClick(ClickEvent event) {
++                event.getButton()
++                .getUI()
++                .getPage()
++                .getJavaScript()
++                .execute(
++                        "setTimeout(function() {window.resizeTo(700,400)}, 500)");
++            }
++        });
+         left.addComponent(resize);
+         resize = new Button("Resize to 900x600", new Button.ClickListener() {
+             @Override
+             public void buttonClick(ClickEvent event) {
++                event.getButton()
++                        .getUI()
++                .getPage()
++                .getJavaScript()
++                .execute(
++                        "setTimeout(function() {window.resizeTo(900,600)}, 500)");
+             }
+         });
+         left.addComponent(resize);
+         left.addComponent(new Label(
+                 "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin vel ante a orci tempus eleifend ut et magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus luctus urna sed urna ultricies."));
+         Table table1 = new Table();
+         table1.setSizeFull();
+         table1.addContainerProperty("Column", String.class, "");
+         for (int i = 1; i <= 100; i++) {
+             table1.addItem(new Object[] { "Value " + i }, i);
+         }
+         left.addComponent(table1);
+         left.setExpandRatio(table1, 1);
+         VerticalSplitPanel split2 = new VerticalSplitPanel();
+         split2.setSizeFull();
+         split1.setSecondComponent(split2);
+         Table table2 = new Table();
+         table2.setSizeFull();
+         table2.addContainerProperty("Column 1", String.class, "");
+         table2.addContainerProperty("Column 2", String.class, "");
+         table2.addContainerProperty("Column 3", String.class, "");
+         table2.addContainerProperty("Column 4", String.class, "");
+         for (int i = 1; i <= 100; i++) {
+             table2.addItem(new Object[] { "Value " + i, "Value " + i,
+                     "Value " + i, "Value " + i }, i);
+         }
+         split2.setFirstComponent(table2);
+         VerticalLayout rows = new VerticalLayout();
+         rows.setWidth("100%");
+         rows.setSpacing(true);
+         rows.setMargin(true);
+         for (int i = 1; i <= 100; i++) {
+             rows.addComponent(getRow(i));
+         }
+         split2.setSecondComponent(rows);
+     }
+     private HorizontalLayout getRow(int i) {
+         HorizontalLayout row = new HorizontalLayout();
+         row.setWidth("100%");
+         row.setSpacing(true);
+         Embedded icon = new Embedded(null, new ThemeResource(
+                 "../runo/icons/32/document.png"));
+         row.addComponent(icon);
+         row.setComponentAlignment(icon, Alignment.MIDDLE_LEFT);
+         Label text = new Label(
+                 "Row content #"
+                         + i
+                         + ". In pellentesque faucibus vestibulum. Nulla at nulla justo, eget luctus tortor. Nulla facilisi. Duis aliquet.");
+         row.addComponent(text);
+         row.setExpandRatio(text, 1);
+         Button button = new Button("Edit");
+         button.addStyleName(Reindeer.BUTTON_SMALL);
+         row.addComponent(button);
+         row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+         button = new Button("Delete");
+         button.addStyleName(Reindeer.BUTTON_SMALL);
+         row.addComponent(button);
+         row.setComponentAlignment(button, Alignment.MIDDLE_LEFT);
+         return row;
+     }
+     @Override
+     protected String getDescription() {
+         // TODO Auto-generated method stub
+         return null;
+     }
+     @Override
+     protected Integer getTicketNumber() {
+         // TODO Auto-generated method stub
+         return null;
+     }
+ }
index 0000000000000000000000000000000000000000,864ca2e6dac734b20d445a41f93152258e540d64..e029e3903a759fc07b180a8cba741ae4669f1d34
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,394 +1,397 @@@
 -            "800px", "100%", "50%" };
+ package com.vaadin.tests.components.orderedlayout;
+ import java.lang.reflect.Field;
+ import java.util.ArrayList;
+ import java.util.Arrays;
+ import java.util.List;
++import com.vaadin.annotations.Theme;
+ import com.vaadin.data.Property.ValueChangeEvent;
+ import com.vaadin.data.Property.ValueChangeListener;
+ import com.vaadin.server.WrappedRequest;
+ import com.vaadin.tests.components.AbstractTestUI;
+ import com.vaadin.tests.util.TestUtils;
+ import com.vaadin.ui.AbstractOrderedLayout;
+ import com.vaadin.ui.Alignment;
+ import com.vaadin.ui.Button;
+ import com.vaadin.ui.Button.ClickEvent;
+ import com.vaadin.ui.Button.ClickListener;
+ import com.vaadin.ui.Component;
+ import com.vaadin.ui.HorizontalLayout;
+ import com.vaadin.ui.NativeSelect;
+ import com.vaadin.ui.VerticalLayout;
++@Theme("tests-components")
+ public class OrderedLayoutCases extends AbstractTestUI {
++
+     private static final String[] dimensionValues = { "-1px", "5px", "350px",
 -                        @Override
 -                        public void valueChange(ValueChangeEvent event) {
 -                            setWidth(event.getProperty().getValue().toString());
 -                        }
 -                    }, dimensionValues));
++        "800px", "100%", "50%" };
+     private static class SampleChild extends VerticalLayout {
+         public SampleChild() {
+             setStyleName("sampleChild");
+             addComponent(createSimpleSelector("Child width",
+                     new ValueChangeListener() {
 -                        @Override
 -                        public void valueChange(ValueChangeEvent event) {
 -                            setHeight(event.getProperty().getValue().toString());
 -                        }
 -                    }, dimensionValues));
++                @Override
++                public void valueChange(ValueChangeEvent event) {
++                    setWidth(event.getProperty().getValue().toString());
++                }
++            }, dimensionValues));
+             addComponent(createSimpleSelector("Child height",
+                     new ValueChangeListener() {
 -                        @Override
 -                        public void valueChange(ValueChangeEvent event) {
 -                            String value = event.getProperty().getValue()
 -                                    .toString();
 -                            if (value.length() == 0) {
 -                                setCaption(null);
 -                            } else if (value.equals("Long")) {
 -                                setCaption("A rather long caption just to see what happens");
 -                            } else {
 -                                setCaption(value);
 -                            }
 -                        }
 -                    }, "", "Short", "Long"));
++                @Override
++                public void valueChange(ValueChangeEvent event) {
++                    setHeight(event.getProperty().getValue().toString());
++                }
++            }, dimensionValues));
+             addComponent(createSimpleSelector("Caption",
+                     new ValueChangeListener() {
 -                        @Override
 -                        public void valueChange(ValueChangeEvent event) {
 -                            AbstractOrderedLayout parent = (AbstractOrderedLayout) getParent();
 -                            if (parent == null) {
 -                                return;
 -                            }
 -                            String value = event.getProperty().getValue()
 -                                    .toString();
 -                            parent.setExpandRatio(SampleChild.this,
 -                                    Float.parseFloat(value));
 -                        }
 -                    }, "0", "1", "2"));
++                @Override
++                public void valueChange(ValueChangeEvent event) {
++                    String value = event.getProperty().getValue()
++                            .toString();
++                    if (value.length() == 0) {
++                        setCaption(null);
++                    } else if (value.equals("Long")) {
++                        setCaption("A rather long caption just to see what happens");
++                    } else {
++                        setCaption(value);
++                    }
++                }
++            }, "", "Short", "Long"));
+             addComponent(createSimpleSelector("Expand ratio",
+                     new ValueChangeListener() {
 -                        @Override
 -                        public void valueChange(ValueChangeEvent event) {
 -                            String value = event.getProperty().getValue()
 -                                    .toString();
 -                            AlignmentHandler parent = (AlignmentHandler) getParent();
 -                            if (parent == null) {
 -                                return;
 -                            }
 -                            try {
 -                                Field field = Alignment.class
 -                                        .getDeclaredField(value);
 -                                Alignment alignment = (Alignment) field
 -                                        .get(null);
 -                                parent.setComponentAlignment(SampleChild.this,
 -                                        alignment);
 -                            } catch (Exception e) {
 -                                throw new RuntimeException(e);
 -                            }
 -                        }
 -                    }, alignmentValues, "TOP_LEFT")); // Sorry for not using
 -                                                      // more reflection magic
 -                                                      // just to find the
 -                                                      // default value...
++                @Override
++                public void valueChange(ValueChangeEvent event) {
++                    AbstractOrderedLayout parent = (AbstractOrderedLayout) getParent();
++                    if (parent == null) {
++                        return;
++                    }
++                    String value = event.getProperty().getValue()
++                            .toString();
++                    parent.setExpandRatio(SampleChild.this,
++                            Float.parseFloat(value));
++                }
++            }, "0", "1", "2"));
+             // Why is Alignment not an enum? Now we have to use reflection just
+             // to get the different values as hardcoding is never an option! ;)
+             List<String> alignmentValues = new ArrayList<String>();
+             Field[] fields = Alignment.class.getDeclaredFields();
+             for (Field field : fields) {
+                 if (field.getType() == Alignment.class) {
+                     alignmentValues.add(field.getName());
+                 }
+             }
+             addComponent(createSimpleSelector("Alignment",
+                     new ValueChangeListener() {
 -                .injectCSS(
 -                        getUI(),
 -                        ".sampleChild, .theLayout {border: 1px solid black;}"
 -                                + ".theLayout > div:first-child {background: aqua;}"
 -                                + ".theLayout > div:first-child + div {background: yellow;}"
 -                                + ".theLayout > div:first-child + div + div {background: lightgrey;}");
++                @Override
++                public void valueChange(ValueChangeEvent event) {
++                    String value = event.getProperty().getValue()
++                            .toString();
++                    AlignmentHandler parent = (AlignmentHandler) getParent();
++                    if (parent == null) {
++                        return;
++                    }
++                    try {
++                        Field field = Alignment.class
++                                .getDeclaredField(value);
++                        Alignment alignment = (Alignment) field
++                                .get(null);
++                        parent.setComponentAlignment(SampleChild.this,
++                                alignment);
++                    } catch (Exception e) {
++                        throw new RuntimeException(e);
++                    }
++                }
++            }, alignmentValues, "TOP_LEFT")); // Sorry for not using
++            // more reflection magic
++            // just to find the
++            // default value...
+         }
+     }
+     private AbstractOrderedLayout currentLayout;
+     private HorizontalLayout sizeBar;
+     @Override
+     protected void setup(WrappedRequest request) {
+         TestUtils
 -                    @Override
 -                    public void valueChange(ValueChangeEvent event) {
 -                        currentLayout.setWidth(event.getProperty().getValue()
 -                                .toString());
 -                    }
 -                }, dimensionValues));
++        .injectCSS(
++                getUI(),
++                ".sampleChild, .theLayout {border: 1px solid black;}"
++                        + ".theLayout > div:first-child {background: aqua;}"
++                        + ".theLayout > div:first-child + div {background: yellow;}"
++                        + ".theLayout > div:first-child + div + div {background: lightgrey;}");
+         currentLayout = new HorizontalLayout();
+         for (int i = 0; i < 3; i++) {
+             currentLayout.addComponent(new SampleChild());
+         }
+         sizeBar = new HorizontalLayout();
+         sizeBar.setSpacing(true);
+         sizeBar.addComponent(createSimpleSelector("Layout width",
+                 new ValueChangeListener() {
 -                    @Override
 -                    public void valueChange(ValueChangeEvent event) {
 -                        currentLayout.setHeight(event.getProperty().getValue()
 -                                .toString());
 -                    }
 -                }, dimensionValues));
++            @Override
++            public void valueChange(ValueChangeEvent event) {
++                currentLayout.setWidth(event.getProperty().getValue()
++                        .toString());
++            }
++        }, dimensionValues));
+         sizeBar.addComponent(createSimpleSelector("Layout height",
+                 new ValueChangeListener() {
 -                    @Override
 -                    public void valueChange(ValueChangeEvent event) {
 -                        currentLayout.setSpacing(Boolean.parseBoolean(event
 -                                .getProperty().getValue().toString()));
 -                    }
 -                }, "false", "true"));
++            @Override
++            public void valueChange(ValueChangeEvent event) {
++                currentLayout.setHeight(event.getProperty().getValue()
++                        .toString());
++            }
++        }, dimensionValues));
+         sizeBar.addComponent(createSimpleSelector("Spacing",
+                 new ValueChangeListener() {
 -                    @Override
 -                    public void valueChange(ValueChangeEvent event) {
 -                        currentLayout.setMargin(Boolean.parseBoolean(event
 -                                .getProperty().getValue().toString()));
 -                    }
 -                }, "false", "true"));
++            @Override
++            public void valueChange(ValueChangeEvent event) {
++                currentLayout.setSpacing(Boolean.parseBoolean(event
++                        .getProperty().getValue().toString()));
++            }
++        }, "false", "true"));
+         sizeBar.addComponent(createSimpleSelector("Margin",
+                 new ValueChangeListener() {
 -                    @Override
 -                    public void valueChange(ValueChangeEvent event) {
 -                        Object value = event.getProperty().getValue();
++            @Override
++            public void valueChange(ValueChangeEvent event) {
++                currentLayout.setMargin(Boolean.parseBoolean(event
++                        .getProperty().getValue().toString()));
++            }
++        }, "false", "true"));
+         sizeBar.addComponent(createSimpleSelector("Direction",
+                 new ValueChangeListener() {
 -                        AbstractOrderedLayout newLayout;
 -                        if (value.equals("Horizontal")) {
 -                            newLayout = new HorizontalLayout();
 -                        } else {
 -                            newLayout = new VerticalLayout();
 -                        }
++            @Override
++            public void valueChange(ValueChangeEvent event) {
++                Object value = event.getProperty().getValue();
 -                        while (currentLayout.getComponentCount() > 0) {
 -                            newLayout.addComponent(currentLayout
 -                                    .getComponent(0));
 -                        }
 -                        newLayout.setStyleName("theLayout");
++                AbstractOrderedLayout newLayout;
++                if (value.equals("Horizontal")) {
++                    newLayout = new HorizontalLayout();
++                } else {
++                    newLayout = new VerticalLayout();
++                }
 -                        newLayout.setHeight(currentLayout.getHeight(),
 -                                currentLayout.getHeightUnits());
 -                        newLayout.setWidth(currentLayout.getWidth(),
 -                                currentLayout.getWidthUnits());
++                while (currentLayout.getComponentCount() > 0) {
++                    newLayout.addComponent(currentLayout
++                            .getComponent(0));
++                }
++                newLayout.setStyleName("theLayout");
 -                        newLayout.setMargin(currentLayout.getMargin());
 -                        newLayout.setSpacing(currentLayout.isSpacing());
++                newLayout.setHeight(currentLayout.getHeight(),
++                        currentLayout.getHeightUnits());
++                newLayout.setWidth(currentLayout.getWidth(),
++                        currentLayout.getWidthUnits());
 -                        getLayout().replaceComponent(currentLayout, newLayout);
 -                        getLayout().setExpandRatio(newLayout, 1);
 -                        currentLayout = newLayout;
 -                    }
 -                }, "Horizontal", "Vertical"));
++                newLayout.setMargin(currentLayout.getMargin());
++                newLayout.setSpacing(currentLayout.isSpacing());
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        setState(sizeBar, 2, 1);
 -                        // width: 350px to middle child
 -                        setChildState(1, 0, 2);
 -                        // middle center allign to middle child
 -                        setChildState(1, 4, 5);
 -                        // long captions to right child
 -                        setChildState(2, 2, 2);
 -                    }
 -                }));
++                getLayout().replaceComponent(currentLayout, newLayout);
++                getLayout().setExpandRatio(newLayout, 1);
++                currentLayout = newLayout;
++            }
++        }, "Horizontal", "Vertical"));
+         HorizontalLayout caseBar = new HorizontalLayout();
+         caseBar.addComponent(new Button("Undefined without relative",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // width: 100% to middle child
 -                        setChildState(1, 0, 4);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                setState(sizeBar, 2, 1);
++                // width: 350px to middle child
++                setChildState(1, 0, 2);
++                // middle center allign to middle child
++                setChildState(1, 4, 5);
++                // long captions to right child
++                setChildState(2, 2, 2);
++            }
++        }));
+         caseBar.addComponent(new Button("Undefined with relative",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // layout width: 350px
 -                        setState(sizeBar, 0, 2);
 -                        // layout margin enabled
 -                        setState(sizeBar, 3, 1);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // width: 100% to middle child
++                setChildState(1, 0, 4);
++            }
++        }));
+         caseBar.addComponent(new Button("Fixed with overflow",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // Layout width: 800px
 -                        setState(sizeBar, 0, 3);
 -                        // layout margin enabled
 -                        setState(sizeBar, 3, 1);
 -                        // width: 350px to middle child
 -                        setChildState(1, 0, 2);
 -                        // short caption for middle child
 -                        setChildState(1, 2, 1);
 -                        // top center align for middle child
 -                        setChildState(1, 4, 2);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // layout width: 350px
++                setState(sizeBar, 0, 2);
++                // layout margin enabled
++                setState(sizeBar, 3, 1);
++            }
++        }));
+         caseBar.addComponent(new Button("Fixed with extra space",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // Layout width: 800px
 -                        setState(sizeBar, 0, 3);
 -                        // Layout height: 350px
 -                        setState(sizeBar, 1, 2);
 -                        // Expand: 1 to middle child
 -                        setChildState(1, 3, 1);
 -                        // Align bottom left to middle child
 -                        setChildState(1, 4, 6);
 -                        // Long caption to middle child
 -                        setChildState(1, 2, 2);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // Layout width: 800px
++                setState(sizeBar, 0, 3);
++                // layout margin enabled
++                setState(sizeBar, 3, 1);
++                // width: 350px to middle child
++                setChildState(1, 0, 2);
++                // short caption for middle child
++                setChildState(1, 2, 1);
++                // top center align for middle child
++                setChildState(1, 4, 2);
++            }
++        }));
+         caseBar.addComponent(new Button("Expand with alignment",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // Layout width: 800px
 -                        setState(sizeBar, 0, 3);
 -                        // Layout height: 350px
 -                        setState(sizeBar, 1, 2);
 -                        // Long caption to left child
 -                        setChildState(0, 2, 2);
 -                        // Width 350px to middle child
 -                        setChildState(1, 0, 2);
 -                        // Apply to left and middle child
 -                        for (int i = 0; i < 2; i++) {
 -                            // Expand: 1
 -                            setChildState(i, 3, 1);
 -                            // Align: middle center
 -                            setChildState(i, 4, 5);
 -                        }
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // Layout width: 800px
++                setState(sizeBar, 0, 3);
++                // Layout height: 350px
++                setState(sizeBar, 1, 2);
++                // Expand: 1 to middle child
++                setChildState(1, 3, 1);
++                // Align bottom left to middle child
++                setChildState(1, 4, 6);
++                // Long caption to middle child
++                setChildState(1, 2, 2);
++            }
++        }));
+         caseBar.addComponent(new Button("Multiple expands",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // Layout height: 100%
 -                        setState(sizeBar, 1, 4);
 -                        // Height: 350px to left child
 -                        setChildState(0, 1, 2);
 -                        // Height: 100% to middle child
 -                        setChildState(1, 1, 4);
 -                        // Short caption to middle child
 -                        setChildState(1, 2, 1);
 -                        // Alignment: bottom left to right child
 -                        setChildState(2, 4, 7);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // Layout width: 800px
++                setState(sizeBar, 0, 3);
++                // Layout height: 350px
++                setState(sizeBar, 1, 2);
++                // Long caption to left child
++                setChildState(0, 2, 2);
++                // Width 350px to middle child
++                setChildState(1, 0, 2);
++                // Apply to left and middle child
++                for (int i = 0; i < 2; i++) {
++                    // Expand: 1
++                    setChildState(i, 3, 1);
++                    // Align: middle center
++                    setChildState(i, 4, 5);
++                }
++            }
++        }));
+         caseBar.addComponent(new Button("Fixed + relative height",
+                 new ClickListener() {
 -                    @Override
 -                    public void buttonClick(ClickEvent event) {
 -                        resetState();
 -                        // Height: 350px to left child
 -                        setChildState(0, 1, 2);
 -                        // Short caption to left child
 -                        setChildState(0, 2, 1);
 -                        // Height: 100% to middle child
 -                        setChildState(1, 1, 4);
 -                        // Alignment: bottom left to right child
 -                        setChildState(2, 4, 7);
 -                    }
 -                }));
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // Layout height: 100%
++                setState(sizeBar, 1, 4);
++                // Height: 350px to left child
++                setChildState(0, 1, 2);
++                // Height: 100% to middle child
++                setChildState(1, 1, 4);
++                // Short caption to middle child
++                setChildState(1, 2, 1);
++                // Alignment: bottom left to right child
++                setChildState(2, 4, 7);
++            }
++        }));
+         caseBar.addComponent(new Button("Undefined + relative height",
+                 new ClickListener() {
++            @Override
++            public void buttonClick(ClickEvent event) {
++                resetState();
++                // Height: 350px to left child
++                setChildState(0, 1, 2);
++                // Short caption to left child
++                setChildState(0, 2, 1);
++                // Height: 100% to middle child
++                setChildState(1, 1, 4);
++                // Alignment: bottom left to right child
++                setChildState(2, 4, 7);
++            }
++        }));
+         caseBar.setSpacing(true);
+         addComponent(caseBar);
+         addComponent(sizeBar);
+         addComponent(currentLayout);
+         getLayout().setSpacing(true);
+         getContent().setSizeFull();
+         getLayout().setSizeFull();
+         getLayout().setExpandRatio(currentLayout, 1);
+     }
+     private void resetState() {
+         for (int i = 0; i < sizeBar.getComponentCount(); i++) {
+             setState(sizeBar, i, 0);
+         }
+         for (int i = 0; i < 3; i++) {
+             // Child width and height -> -1px
+             SampleChild child = (SampleChild) currentLayout.getComponent(i);
+             for (int j = 0; j < child.getComponentCount(); j++) {
+                 if (j == 4) {
+                     setState(child, j, 1);
+                 } else {
+                     setState(child, j, 0);
+                 }
+             }
+         }
+     }
+     private void setChildState(int childIndex, int selectIndex, int valueIndex) {
+         Component child = currentLayout.getComponent(childIndex);
+         setState(child, selectIndex, valueIndex);
+     }
+     private static void setState(Component container, int selectIndex, int value) {
+         NativeSelect select = (NativeSelect) ((AbstractOrderedLayout) container)
+                 .getComponent(selectIndex);
+         select.setValue(new ArrayList<Object>(select.getItemIds()).get(value));
+     }
+     private static NativeSelect createSimpleSelector(String caption,
+             ValueChangeListener listener, String... values) {
+         return createSimpleSelector(caption, listener, Arrays.asList(values),
+                 values[0]);
+     }
+     private static NativeSelect createSimpleSelector(String caption,
+             ValueChangeListener listener, List<String> values,
+             String defaultValue) {
+         NativeSelect selector = new NativeSelect(caption, values);
+         selector.setNullSelectionAllowed(false);
+         selector.setImmediate(true);
+         selector.addListener(listener);
+         selector.setValue(defaultValue);
+         return selector;
+     }
+     @Override
+     protected Integer getTicketNumber() {
+         return null;
+     }
+     @Override
+     protected String getTestDescription() {
+         return "Tester application for exploring how Horizontal/VerticalLayout reacts to various settings ";
+     }
+ }
index 0000000000000000000000000000000000000000,ae3f4c42a4816bbedc563e3b555abe638cfeac62..5c7479d060c3c247e50e9ccf7c6948ed15fe1d25
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,131 +1,130 @@@
 -        private Button addButton = new Button("Add new row",
 -                (Button.ClickListener) this);
+ package com.vaadin.tests.components.table;
+ import com.vaadin.data.Item;
+ import com.vaadin.data.Property;
+ import com.vaadin.data.util.IndexedContainer;
+ import com.vaadin.tests.components.TestBase;
+ import com.vaadin.ui.Button;
+ import com.vaadin.ui.Button.ClickEvent;
+ import com.vaadin.ui.Component;
+ import com.vaadin.ui.Table;
+ import com.vaadin.ui.TextField;
+ public class TextFieldRelativeWidth extends TestBase {
+     @Override
+     public void setup() {
+         TextField tf = new TextField("test", "testing");
+         tf.setWidth("100%");
+         EditTable t = new EditTable();
+         t.setButtonCaption("Click to add new Key Research Question");
+         t.setInputPrompt("Key Reseach question");
+         t.setInputPromptChild("Question details");
+         t.addNewRow();
+         addComponent(t);
+     }
+     public class EditTable extends Table implements Button.ClickListener {
++        private Button addButton = new Button("Add new row", this);
+         private String inputPrompt;
+         private String inputPromptChild;
+         private int nextItemIndex = 1;
+         private static final long serialVersionUID = 3326806911297977454L;
+         public EditTable() {
+             setColumnHeaderMode(Table.COLUMN_HEADER_MODE_HIDDEN);
+             inputPrompt = "";
+             setPageLength(100);
+             setHeight("100%");
+             setSizeFull();
+             addContainerProperty("id", Integer.class, null);
+             addContainerProperty("text", Component.class, null);
+             addContainerProperty("button", Button.class, null);
+             setColumnExpandRatio("text", 1);
+             Item i = getItem(addItem());
+             i.getItemProperty("text").setValue(addButton);
+             setImmediate(true);
+             setSelectable(true);
+             addListener(new Property.ValueChangeListener() {
+                 private static final long serialVersionUID = 448896474865195605L;
+                 @Override
+                 public void valueChange(
+                         com.vaadin.data.Property.ValueChangeEvent event) {
+                     // IndexedContainer idc = (IndexedContainer)
+                     // getContainerDataSource();
+                 }
+             });
+         }
+         public void addNewRow() {
+             IndexedContainer idc = (IndexedContainer) getContainerDataSource();
+             int size = idc.size();
+             Object itemId = idc.addItemAt(size - 1);
+             Item newItem = idc.getItem(itemId);
+             TextField tf = new TextField();
+             if (inputPrompt != null && inputPrompt.length() > 0) {
+                 tf.setInputPrompt(inputPrompt);
+             }
+             tf.setWidth("100%");
+             newItem.getItemProperty("id").setValue(nextItemIndex);
+             nextItemIndex++;
+             newItem.getItemProperty("text").setValue(tf);
+             setValue(itemId);
+             itemId = idc.addItemAt(size);
+             newItem = idc.getItem(itemId);
+             tf = new TextField();
+             if (inputPromptChild != null && inputPromptChild.length() > 0) {
+                 tf.setInputPrompt(inputPromptChild);
+             }
+             // tf.setRows(1);
+             // tf.setHeight("45px");
+             tf.setWidth("100%");
+             tf.addStyleName("childtf");
+             newItem.getItemProperty("text").setValue(tf);
+         }
+         public void setButtonCaption(String caption) {
+             addButton.setCaption(caption);
+         }
+         @Override
+         public void buttonClick(ClickEvent event) {
+             Button b = event.getButton();
+             if (b == addButton) {
+                 select(getNullSelectionItemId());
+                 addNewRow();
+             }
+         }
+         public void setInputPrompt(String string) {
+             inputPrompt = string;
+         }
+         public void setInputPromptChild(String string) {
+             inputPromptChild = string;
+         }
+     }
+     @Override
+     protected String getDescription() {
+         return "The table has 3 columns. The second column is expanded and contains 100% wide textfields. These should fill the available space. The third column is empty.";
+     }
+     @Override
+     protected Integer getTicketNumber() {
+         return 3145;
+     }
+ }
index 0000000000000000000000000000000000000000,3d5e1052c3c600ed296464e8762770b97e7072e4..c2c8842bcbab995c310e2b68d5f4364f74949f40
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,141 +1,141 @@@
 -      <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[7]/domChild[0]/domChild[1]</td>
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head profile="http://selenium-ide.openqa.org/profiles/test-case">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <link rel="selenium.base" href="" />
+ <title>New Test</title>
+ </head>
+ <body>
+ <table cellpadding="1" cellspacing="1" border="1">
+ <thead>
+ <tr><td rowspan="1" colspan="3">New Test</td></tr>
+ </thead><tbody>
+ <tr>
+       <td>open</td>
+       <td>/run/com.vaadin.tests.fieldgroup.BasicPersonForm?restartApplication</td>
+       <td></td>
+ </tr>
+ <tr>
+       <td>screenCapture</td>
+       <td></td>
+       <td>initial</td>
+ </tr>
+ <tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTextArea[0]</td>
+       <td>90,38</td>
+ </tr>
+ <tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[3]/VTextArea[0]</td>
+       <td>Dover</td>
+ </tr>
+ <tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+ </tr>
+ <tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+       <td>1. Person [firstName=John, lastName=Doe, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=false, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ <tr>
+       <td>drag</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>108,9</td>
+ </tr>
+ <tr>
+       <td>drop</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]</td>
+       <td>0,587</td>
+ </tr>
+ <tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>-18,9</td>
+ </tr>
+ <tr>
+       <td>enterCharacter</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>false</td>
+ </tr>
+ <tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]</td>
+       <td>239,14</td>
+ </tr>
+ <!--error indicator-->
+ <tr>
+       <td>assertCSSClass</td>
++      <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VBoxLayout[0]/VBoxLayout$Slot[1]/VBoxLayout[0]/domChild[7]/domChild[0]/domChild[0]/domChild[1]</td>
+       <td>v-errorindicator</td>
+ </tr>
+ <tr>
+       <td>assertValue</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>false</td>
+ </tr>
+ <!--show bean values-->
+ <tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+ </tr>
+ <tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+       <td>2. Person [firstName=John, lastName=Doe, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=false, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ <!--error message in tooltip-->
+ <tr>
+       <td>showTooltip</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>0,0</td>
+ </tr>
+ <tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::Root/VTooltip[0]/FlowPanel[0]/VErrorMessage[0]/HTML[0]</td>
+       <td>Could not convert value to Boolean</td>
+ </tr>
+ <tr>
+       <td>mouseClick</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>66,6</td>
+ </tr>
+ <tr>
+       <td>type</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[7]/VTextField[0]</td>
+       <td>YAY!</td>
+ </tr>
+ <!--no error indicator-->
+ <tr>
+       <td>assertElementNotPresent</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/domChild[0]/domChild[7]/domChild[0]/domChild[1]</td>
+       <td>v-errorindicator</td>
+ </tr>
+ <tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[8]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+ </tr>
+ <tr>
+       <td>closeNotification</td>
+       <td>//body/div[2]</td>
+       <td>0,0</td>
+ </tr>
+ <!--commit last name and new deceased status-->
+ <tr>
+       <td>click</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[10]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+ </tr>
+ <tr>
+       <td>assertText</td>
+       <td>vaadin=runcomvaadintestsfieldgroupBasicPersonForm::PID_SLog_row_0</td>
+       <td>4. Person [firstName=John, lastName=Dover, email=john@doe.com, age=64, sex=Male, address=Address [streetAddress=John street, postalCode=11223, city=John's town, country=USA], deceased=true, salary=null, salaryDouble=null, rent=null]</td>
+ </tr>
+ </tbody></table>
+ </body>
+ </html>