aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Signell <artur.signell@itmill.com>2008-10-20 08:47:49 +0000
committerArtur Signell <artur.signell@itmill.com>2008-10-20 08:47:49 +0000
commitcf75d2d5d12618852787d4a9346b66a934133cfd (patch)
tree69ab8cb14e3d4c85c7467698e8cd08041f7d9131
parentefe75a19f87aebcf3f4bc1c72f25d3954036b4c9 (diff)
downloadvaadin-framework-cf75d2d5d12618852787d4a9346b66a934133cfd.tar.gz
vaadin-framework-cf75d2d5d12618852787d4a9346b66a934133cfd.zip
New OrderedLayout implementation and related fixes
svn changeset:5671/svn branch:trunk
-rw-r--r--WebContent/ITMILL/themes/default/caption/caption.css6
-rw-r--r--WebContent/ITMILL/themes/default/orderedlayout/orderedlayout.css2
-rw-r--r--WebContent/ITMILL/themes/default/select/select.css7
-rwxr-xr-xsrc/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java70
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/Container.java4
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java6
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ICaption.java143
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/Util.java42
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/IFilterSelect.java10
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/IOldOrderedLayout.java1603
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java2239
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java30
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/layout/CellBasedLayout.java287
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/layout/ChildComponentContainer.java613
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/layout/Margins.java83
-rw-r--r--src/com/itmill/toolkit/ui/ExpandLayout.java103
-rw-r--r--src/com/itmill/toolkit/ui/OldOrderedLayout.java18
17 files changed, 3518 insertions, 1748 deletions
diff --git a/WebContent/ITMILL/themes/default/caption/caption.css b/WebContent/ITMILL/themes/default/caption/caption.css
index 761162d753..959b3ae37d 100644
--- a/WebContent/ITMILL/themes/default/caption/caption.css
+++ b/WebContent/ITMILL/themes/default/caption/caption.css
@@ -7,10 +7,9 @@
}
.i-errorindicator {
- width: 10px;
+ width: 12px;
height: 16px;
- padding-right:13px;
- display: inline;
+ float: left;
background: transparent url(../icons/16/error.png) no-repeat top right;
}
@@ -22,7 +21,6 @@
*+html .i-errorindicator {
margin-left:-3px;
}
-
.i-caption .i-icon {
padding-right: 2px;
vertical-align: middle;
diff --git a/WebContent/ITMILL/themes/default/orderedlayout/orderedlayout.css b/WebContent/ITMILL/themes/default/orderedlayout/orderedlayout.css
index 6e9ef37f92..43c26d19d3 100644
--- a/WebContent/ITMILL/themes/default/orderedlayout/orderedlayout.css
+++ b/WebContent/ITMILL/themes/default/orderedlayout/orderedlayout.css
@@ -12,7 +12,7 @@
}
.i-orderedlayout-vspacing {
- margin-top: 8px;
+ padding-top: 8px;
}
.i-orderedlayout-hspacing {
padding-left: 8px;
diff --git a/WebContent/ITMILL/themes/default/select/select.css b/WebContent/ITMILL/themes/default/select/select.css
index 66803dc01f..fc4ca30b28 100644
--- a/WebContent/ITMILL/themes/default/select/select.css
+++ b/WebContent/ITMILL/themes/default/select/select.css
@@ -68,16 +68,13 @@
.i-filterselect {
height: 23px;
- background: transparent url(img/bg-left-filter.png) no-repeat;
- margin-right: 1px;
white-space: nowrap;
text-align: left /* Force default alignment */
}
.i-filterselect-input {
- width: 99%;
+ background: transparent url(img/bg-left-filter.png) no-repeat;
border: none;
- background: transparent;
height: 20px;
margin: 0;
padding: 3px 0 0 4px;
@@ -90,7 +87,7 @@
.i-filterselect-button {
float: right;
- margin: -23px -1px 0 0;
+ margin-left: -23px;
width: 25px;
height: 23px;
cursor: pointer;
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
index f26badf08a..00b72f9a42 100755
--- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
@@ -4,6 +4,7 @@
package com.itmill.toolkit.terminal.gwt.client;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -119,6 +120,8 @@ public class ApplicationConnection {
/** redirectTimer scheduling interval in seconds */
private int sessionExpirationInterval;
+ private ArrayList<Widget> relativeSizeChanges = new ArrayList<Widget>();;
+
public ApplicationConnection(WidgetSet widgetSet,
ApplicationConfiguration cnf) {
this.widgetSet = widgetSet;
@@ -552,6 +555,7 @@ public class ApplicationConnection {
.get("changes");
Vector<Widget> updatedWidgets = new Vector<Widget>();
+ relativeSizeChanges.clear();
for (int i = 0; i < changes.size(); i++) {
try {
@@ -591,6 +595,8 @@ public class ApplicationConnection {
// Check which widgets' size has been updated
Set<Widget> sizeUpdatedWidgets = new HashSet<Widget>();
+ updatedWidgets.addAll(relativeSizeChanges);
+
for (Widget widget : updatedWidgets) {
Size oldSize = componentOffsetSizes.get(widget);
Size newSize = new Size(widget.getOffsetWidth(), widget
@@ -624,7 +630,7 @@ public class ApplicationConnection {
}
if (html.length() != 0) {
- INotification n = new INotification(1000 * 60 * 45); //45min
+ INotification n = new INotification(1000 * 60 * 45); // 45min
n.addEventListener(new NotificationRedirect(url));
n.show(html, INotification.CENTERED_TOP,
INotification.STYLE_SYSTEM);
@@ -1016,11 +1022,17 @@ public class ApplicationConnection {
// One or both is relative
FloatSize relativeSize = new FloatSize(relativeWidth,
relativeHeight);
- componentRelativeSizes.put(component, relativeSize);
+ if (componentRelativeSizes.put(component, relativeSize) == null) {
+ // The component has changed from absolute size to relative size
+ relativeSizeChanges.add(component);
+ }
handleComponentRelativeSize(component);
} else if (relativeHeight < 0.0 && relativeWidth < 0.0) {
- // No relative sizes
- componentRelativeSizes.remove(component);
+
+ if (componentRelativeSizes.remove(component) != null) {
+ // The component has changed from relative size to absolute size
+ relativeSizeChanges.add(component);
+ }
}
}
@@ -1042,24 +1054,36 @@ public class ApplicationConnection {
final Widget child = (Widget) childWidgets.next();
if (child instanceof Paintable) {
- handleComponentRelativeSize(child);
- }
- if (child instanceof ContainerResizedListener) {
- ((ContainerResizedListener) child).iLayout();
- } else if (child instanceof HasWidgets) {
- final HasWidgets childContainer = (HasWidgets) child;
- runDescendentsLayout(childContainer);
+ if (handleComponentRelativeSize(child)) {
+ /*
+ * Only need to propagate event if "child" has a relative
+ * size
+ */
+
+ if (child instanceof ContainerResizedListener) {
+ ((ContainerResizedListener) child).iLayout();
+ } else if (child instanceof HasWidgets) {
+ final HasWidgets childContainer = (HasWidgets) child;
+ runDescendentsLayout(childContainer);
+ }
+ }
}
}
}
- public void handleComponentRelativeSize(Widget child) {
+ /**
+ * Converts relative sizes into pixel sizes.
+ *
+ * @param child
+ * @return true if the child has a relative size
+ */
+ public boolean handleComponentRelativeSize(Widget child) {
Widget widget = child;
- FloatSize relativeSize = componentRelativeSizes.get(widget);
+ FloatSize relativeSize = getRelativeSize(child);
if (relativeSize == null) {
- return;
+ return false;
}
boolean horizontalScrollBar = false;
@@ -1104,8 +1128,10 @@ public class ApplicationConnection {
// getConsole().log(
// "Widget " + widget.getClass().getName() + "/"
// + widget.hashCode() + " relative height "
- // + relativeSize.getHeight() + "%: " + height
- // + "px");
+ // + relativeSize.getHeight() + "% of "
+ // + renderSpace.getHeight() + "px ("
+ // + renderSpace.getReservedHeight()
+ // + "px reserved): " + height + "px");
widget.setHeight(height + "px");
} else {
widget.setHeight(relativeSize.getHeight() + "%");
@@ -1145,8 +1171,10 @@ public class ApplicationConnection {
// getConsole().log(
// "Widget " + widget.getClass().getName() + "/"
// + widget.hashCode() + " relative width "
- // + relativeSize.getWidth() + "%: " + width
- // + "px");
+ // + relativeSize.getWidth() + "% of "
+ // + renderSpace.getWidth() + "px ("
+ // + renderSpace.getReservedWidth()
+ // + "px reserved): " + width + "px");
widget.setWidth(width + "px");
} else {
widget.setWidth(relativeSize.getWidth() + "%");
@@ -1157,6 +1185,12 @@ public class ApplicationConnection {
}
}
+ return true;
+
+ }
+
+ private FloatSize getRelativeSize(Widget widget) {
+ return componentRelativeSizes.get(widget);
}
/**
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/Container.java b/src/com/itmill/toolkit/terminal/gwt/client/Container.java
index 1a22a87d18..7d67c2f082 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/Container.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/Container.java
@@ -55,12 +55,12 @@ public interface Container extends Paintable {
* Called when a child components size has been updated in the rendering
* phase.
*
- * @param child
+ * @param children
* Set of child widgets whose size have changed
* @return true if the size of the Container remains the same, false if the
* event need to be propagated to the Containers parent
*/
- boolean requestLayout(Set<Paintable> child);
+ boolean requestLayout(Set<Paintable> children);
/**
* Returns the size currently allocated for the child component.
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java
index e68a7bac7e..bc094a527a 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/DefaultWidgetSet.java
@@ -24,6 +24,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.ILink;
import com.itmill.toolkit.terminal.gwt.client.ui.IListSelect;
import com.itmill.toolkit.terminal.gwt.client.ui.IMenuBar;
import com.itmill.toolkit.terminal.gwt.client.ui.INativeSelect;
+import com.itmill.toolkit.terminal.gwt.client.ui.IOldOrderedLayout;
import com.itmill.toolkit.terminal.gwt.client.ui.IOptionGroup;
import com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayout;
import com.itmill.toolkit.terminal.gwt.client.ui.IPanel;
@@ -70,6 +71,9 @@ public class DefaultWidgetSet implements WidgetSet {
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IWindow"
.equals(className)) {
return new IWindow();
+ } else if ("com.itmill.toolkit.terminal.gwt.client.ui.IOldOrderedLayout"
+ .equals(className)) {
+ return new IOldOrderedLayout();
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayout"
.equals(className)) {
return new IOrderedLayout();
@@ -207,6 +211,8 @@ public class DefaultWidgetSet implements WidgetSet {
return "com.itmill.toolkit.terminal.gwt.client.ui.IWindow";
} else if ("orderedlayout".equals(tag)) {
return "com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayout";
+ } else if ("oldorderedlayout".equals(tag)) {
+ return "com.itmill.toolkit.terminal.gwt.client.ui.IOldOrderedLayout";
} else if ("label".equals(tag)) {
return "com.itmill.toolkit.terminal.gwt.client.ui.ILabel";
} else if ("link".equals(tag)) {
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java b/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
index 644d05c5ed..1033c128c2 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ICaption.java
@@ -8,9 +8,9 @@ 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.HTML;
-import com.itmill.toolkit.terminal.gwt.client.RenderInformation.Size;
import com.itmill.toolkit.terminal.gwt.client.ui.Icon;
+//TODO Move styles to CSS
public class ICaption extends HTML {
public static final String CLASSNAME = "i-caption";
@@ -25,11 +25,13 @@ public class ICaption extends HTML {
private Element captionText;
+ private Element clearElement;
+
private final ApplicationConnection client;
private boolean placedAfterComponent = false;
- private Size requiredSpace = new Size(0, 0);
+ private int maxWidth = -1;
/**
*
@@ -44,20 +46,35 @@ public class ICaption extends HTML {
owner = component;
setStyleName(CLASSNAME);
sinkEvents(ITooltip.TOOLTIP_EVENTS);
+
+ DOM.setStyleAttribute(getElement(), "whiteSpace", "nowrap");
+
}
- public void updateCaption(UIDL uidl) {
+ /**
+ * Updates the caption from UIDL.
+ *
+ * @param uidl
+ * @return true if the position where the caption should be placed has
+ * changed
+ */
+ public boolean updateCaption(UIDL uidl) {
setVisible(!uidl.getBooleanAttribute("invisible"));
setStyleName(getElement(), "i-disabled", uidl.hasAttribute("disabled"));
boolean isEmpty = true;
+ boolean wasPlacedAfterComponent = placedAfterComponent;
+
placedAfterComponent = true;
if (uidl.hasAttribute("icon")) {
if (icon == null) {
icon = new Icon(client);
+ DOM.sinkEvents(icon.getElement(), Event.ONLOAD);
+
+ Util.setFloat(icon.getElement(), "left");
placedAfterComponent = false;
DOM.insertChild(getElement(), icon.getElement(), 0);
}
@@ -72,7 +89,11 @@ public class ICaption extends HTML {
if (uidl.hasAttribute("caption")) {
if (captionText == null) {
- captionText = DOM.createSpan();
+ captionText = DOM.createDiv();
+ Util.setFloat(captionText, "left");
+ DOM.setStyleAttribute(captionText, "overflow", "hidden");
+ // DOM.setStyleAttribute(captionText, "textOverflow",
+ // "ellipsis");
DOM
.insertChild(getElement(), captionText,
icon == null ? 0 : 1);
@@ -86,7 +107,7 @@ public class ICaption extends HTML {
}
DOM.setInnerText(captionText, c);
} else {
- // TODO should span also be removed
+ // TODO should element also be removed
}
if (uidl.hasAttribute("description")) {
@@ -100,10 +121,13 @@ public class ICaption extends HTML {
if (uidl.getBooleanAttribute("required")) {
isEmpty = false;
if (requiredFieldIndicator == null) {
- requiredFieldIndicator = DOM.createSpan();
+ requiredFieldIndicator = DOM.createDiv();
+ Util.setFloat(requiredFieldIndicator, "left");
DOM.setInnerText(requiredFieldIndicator, "*");
DOM.setElementProperty(requiredFieldIndicator, "className",
"i-required-field-indicator");
+
+ // TODO Insert before if errorIndicatorElement exists
DOM.appendChild(getElement(), requiredFieldIndicator);
}
} else {
@@ -127,6 +151,14 @@ public class ICaption extends HTML {
errorIndicatorElement = null;
}
+ if (clearElement == null) {
+ clearElement = DOM.createDiv();
+ DOM.setStyleAttribute(clearElement, "clear", "both");
+ DOM.setStyleAttribute(clearElement, "width", "0px");
+ DOM.setStyleAttribute(clearElement, "height", "0px");
+ DOM.setStyleAttribute(clearElement, "overflow", "hidden");
+ DOM.appendChild(getElement(), clearElement);
+ }
// Workaround for IE weirdness, sometimes returns bad height in some
// circumstances when Caption is empty. See #1444
// IE7 bugs more often. I wonder what happens when IE8 arrives...
@@ -141,8 +173,7 @@ public class ICaption extends HTML {
}
- requiredSpace.setWidth(getOffsetWidth());
- requiredSpace.setHeight(getOffsetHeight());
+ return (wasPlacedAfterComponent != placedAfterComponent);
}
public void onBrowserEvent(Event event) {
@@ -151,6 +182,17 @@ public class ICaption extends HTML {
if (client != null && !DOM.compare(target, getElement())) {
client.handleTooltipEvent(event, owner);
}
+
+ if (DOM.eventGetType(event) == Event.ONLOAD) {
+ setMaxWidth(maxWidth);
+
+ // TODO: What if the caption's height changes drastically. Should we
+ // send the size updated message?
+ // Set<Widget> w = new HashSet<Widget>();
+ // w.add(this);
+ // Util.componentSizeUpdated(w);
+ }
+
}
public static boolean isNeeded(UIDL uidl) {
@@ -183,10 +225,6 @@ public class ICaption extends HTML {
return placedAfterComponent;
}
- public Size getRequiredSpace() {
- return requiredSpace;
- }
-
public int getWidth() {
int width = 0;
if (icon != null) {
@@ -202,22 +240,89 @@ public class ICaption extends HTML {
width += errorIndicatorElement.getOffsetWidth();
}
- if (BrowserInfo.get().isIE6() && shouldBePlacedAfterComponent()) {
- // FIXME: Should find out what the real issue is
- // Workaround for IE6 weirdness
- width += 3;
- }
-
return width;
}
public int getHeight() {
- return getOffsetHeight();
+ return clearElement.getOffsetTop() - getElement().getOffsetTop();
}
public void setAlignment(String alignment) {
DOM.setStyleAttribute(getElement(), "textAlign", alignment);
+ }
+
+ public void setMaxWidth(int maxWidth) {
+
+ this.maxWidth = maxWidth;
+ DOM.setStyleAttribute(getElement(), "width", "");
+
+ if (icon != null) {
+ DOM.setStyleAttribute(icon.getElement(), "width", "");
+ }
+
+ if (captionText != null) {
+ DOM.setStyleAttribute(captionText, "width", "");
+ }
+
+ if (maxWidth < 0) {
+ return;
+ }
+
+ int currentWidth = getWidth();
+ if (currentWidth > maxWidth) {
+ // Needs to truncate and clip
+ int availableWidth = maxWidth;
+
+ // ApplicationConnection.getConsole().log(
+ // "Caption maxWidth: " + maxWidth);
+
+ DOM.setStyleAttribute(getElement(), "width", maxWidth + "px");
+ if (requiredFieldIndicator != null) {
+ // ApplicationConnection.getConsole().log(
+ // "requiredFieldIndicator width: "
+ // + requiredFieldIndicator.getOffsetWidth());
+ availableWidth -= requiredFieldIndicator.getOffsetWidth();
+ }
+
+ if (errorIndicatorElement != null) {
+ // ApplicationConnection.getConsole().log(
+ // "errorIndicatorElement width: "
+ // + errorIndicatorElement.getOffsetWidth());
+ availableWidth -= errorIndicatorElement.getOffsetWidth();
+ }
+ if (icon != null) {
+ if (availableWidth > icon.getOffsetWidth()) {
+ // ApplicationConnection.getConsole().log(
+ // "icon width: " + icon.getOffsetWidth());
+ availableWidth -= icon.getOffsetWidth();
+ } else {
+ // ApplicationConnection.getConsole().log(
+ // "icon forced width: " + availableWidth);
+ DOM.setStyleAttribute(icon.getElement(), "width",
+ availableWidth + "px");
+ availableWidth = 0;
+ }
+ }
+ if (captionText != null) {
+ if (availableWidth > captionText.getOffsetWidth()) {
+ // ApplicationConnection.getConsole().log(
+ // "captionText width: "
+ // + captionText.getOffsetWidth());
+ availableWidth -= captionText.getOffsetWidth();
+
+ } else {
+ // ApplicationConnection.getConsole().log(
+ // "captionText forced width: " + availableWidth);
+ DOM.setStyleAttribute(captionText, "width", availableWidth
+ + "px");
+ availableWidth = 0;
+ }
+
+ }
+
+ }
}
+
}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/Util.java b/src/com/itmill/toolkit/terminal/gwt/client/Util.java
index 10c3bcaee0..64ff918a12 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/Util.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/Util.java
@@ -16,6 +16,7 @@ import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.FloatSize;
public class Util {
@@ -195,9 +196,9 @@ public class Util {
public static int measureHorizontalPadding(Element element, int paddingGuess) {
String originalWidth = DOM.getStyleAttribute(element, "width");
int originalOffsetWidth = element.getOffsetWidth();
- int widthGuess = (originalOffsetWidth + paddingGuess);
+ int widthGuess = (originalOffsetWidth - paddingGuess);
DOM.setStyleAttribute(element, "width", widthGuess + "px");
- int padding = widthGuess - element.getOffsetWidth();
+ int padding = element.getOffsetWidth() - widthGuess;
DOM.setStyleAttribute(element, "width", originalWidth);
return padding;
@@ -225,6 +226,23 @@ public class Util {
}
+ public static String getSimpleName(Widget widget) {
+ if (widget == null) {
+ return "(null)";
+ }
+
+ String name = widget.getClass().getName();
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+
+ public static void setFloat(Element element, String value) {
+ if (BrowserInfo.get().isIE()) {
+ DOM.setStyleAttribute(element, "styleFloat", value);
+ } else {
+ DOM.setStyleAttribute(element, "cssFloat", value);
+ }
+ }
+
private static int detectedScrollbarSize = -1;
public static int getNativeScrollbarSize() {
@@ -272,4 +290,24 @@ public class Util {
}
+ public static FloatSize parseRelativeSize(UIDL uidl) {
+ String w = uidl.hasAttribute("width") ? uidl
+ .getStringAttribute("width") : "";
+
+ String h = uidl.hasAttribute("height") ? uidl
+ .getStringAttribute("height") : "";
+
+ float relativeWidth = Util.parseRelativeSize(w);
+ float relativeHeight = Util.parseRelativeSize(h);
+
+ if (relativeHeight >= 0.0 || relativeWidth >= 0.0) {
+ // One or both is relative
+ FloatSize relativeSize = new FloatSize(relativeWidth,
+ relativeHeight);
+ return relativeSize;
+ } else {
+ return null;
+ }
+
+ }
}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IFilterSelect.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IFilterSelect.java
index dd2d3286d5..8e565212ae 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IFilterSelect.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IFilterSelect.java
@@ -812,4 +812,14 @@ public class IFilterSelect extends Composite implements Paintable, Field,
public void focus() {
tb.setFocus(true);
}
+
+ @Override
+ public void setWidth(String width) {
+ super.setWidth(width);
+
+ int padding = Util.measureHorizontalPadding(tb.getElement(), 4);
+ tb.setWidth((getOffsetWidth() - padding - popupOpener.getOffsetWidth())
+ + "px");
+
+ }
}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IOldOrderedLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOldOrderedLayout.java
new file mode 100644
index 0000000000..f6d9848952
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOldOrderedLayout.java
@@ -0,0 +1,1603 @@
+/*
+@ITMillApache2LicenseForJavaFiles@
+ */
+
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Vector;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
+import com.itmill.toolkit.terminal.gwt.client.Container;
+import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
+import com.itmill.toolkit.terminal.gwt.client.ICaption;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation;
+import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.Size;
+
+/**
+ * Full implementation of OrderedLayout client peer.
+ *
+ * This class implements all features of OrderedLayout. It currently only
+ * supports use through UIDL updates. Direct client side use is not (currently)
+ * suported in all operation modes.
+ *
+ *
+ * <h2>Features</h2>
+ *
+ * <h3>Orientation</h3>
+ *
+ * <p>
+ * Orientation of the ordered layout declared whether the children are layouted
+ * horizontally or vertically.
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_horizontal.png"/> <img
+ * src="doc-files/IOrderedLayout_vertical.png"/>
+ *
+ * <h3>Spacing</h3>
+ *
+ * <p>
+ * Spacing determines if there should be space between the children. Note that
+ * this does not imply margin.
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_horizontal_spacing.png"/> <img
+ * src="doc-files/IOrderedLayout_vertical_spacing.png"/>
+ *
+ * <h3>Margin</h3>
+ *
+ * <p>
+ * Margin determines if there should be margin around children. Note that this
+ * does not imply spacing.
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_margin.png"/>
+ *
+ * <h3>Positioning the caption, icon, required indicator and error</h3>
+ *
+ * <p>
+ * If the child lets the layout to handle captions, by icon, caption, required
+ * marker (*) and error icon are placed on top of the component area. Icon will
+ * be first and is followed by the caption. Required marker is placed right
+ * after the caption text and error icon is placed last. Note that all of these
+ * are optional:
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_normal_caption.png"/>
+ *
+ * <p>
+ * If the child lets the layout to handle captions, but the caption and icon are
+ * both missing, no line is reserved for the required marker (*) and error icon.
+ * Instead they are placed on the right side of the top of the component area.
+ * Required marker is placed right after the component text and error icon is
+ * placed last. If the component is tall, the indicators are aligned along the
+ * top of the component. Note that both of these indicators are optional:
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_no_caption.png"/>
+ *
+ * <p>
+ * In case the child want to handle the caption by itself, layout does not
+ * repeat the caption.
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_component_handles_the_caption.png"/>
+ *
+ * <h3>Aligning the children</h3>
+ *
+ * <p>
+ * The children of the layout can be aligned horizontally and vertically:
+ * </p>
+ *
+ * <img src="doc-files/IOrderedLayout_alignment.png"/>
+ *
+ * <h3>Fixed height, width or both</h3>
+ *
+ * <p>
+ * When no size is explicitly specified, the size of the layout depends on the
+ * size of its children. If the size if specified, either explicitly or as
+ * percertages of the parent size, the size is equally divided between the
+ * children. In case some children might overflow out of the given space, they
+ * are cut to fit the given space. Note that the size can be independently
+ * specified for horizontal and vertical dimensions and is independent of the
+ * orientation. For example, layout can be horizontal and have fixed 300px
+ * height, but still measure its width from the child sizes.
+ * </p>
+ *
+ * <p>
+ * Horizontal layout with fixed width of 300px and height of 150px:
+ * </p>
+ * <img src="doc-files/IOrderedLayout_w300_h150.png"/>
+ *
+ * <p>
+ * Horizontal layout with fixed width of 300px:
+ * </p>
+ * <img src="doc-files/IOrderedLayout_w300.png"/>
+ *
+ * <p>
+ * Horizontal layout with fixed height of 150px:
+ * </p>
+ * <img src="doc-files/IOrderedLayout_h150.png"/>
+ *
+ *
+ * <h3>CSS attributes</h3>
+ *
+ * <p>
+ * Sizes for marginals and spacing can be specified for the ordered layout in
+ * CSS. For example, here are the defaults for OrderedLayout:
+ * </p>
+ *
+ * <pre>
+ * .i-orderedlayout-margin-top {
+ * padding-top: 15px;
+ * }
+ * .i-orderedlayout-margin-right {
+ * padding-right: 18px;
+ * }
+ * .i-orderedlayout-margin-bottom {
+ * padding-bottom: 15px;
+ * }
+ * .i-orderedlayout-margin-left {
+ * padding-left: 18px;
+ * }
+ *
+ * .i-orderedlayout-vspacing {
+ * margin-top: 8px;
+ * }
+ * .i-orderedlayout-hspacing {
+ * padding-left: 8px;
+ * }
+ * </pre>
+ *
+ * <p>
+ * When a style-name is set for the layout, this name is included in the style.
+ * Note that the unspecified dimensions still default to the values given for
+ * the layout without style. For example, if we would like to give each layout
+ * with "tested-layout" style quite a bit larger right margin:
+ * </p>
+ *
+ * <pre>
+ * .i-orderedlayout-tested-layout-margin-right {
+ * padding-right: 100px;
+ * }
+ * </pre>
+ *
+ * <p>
+ * Here is the rendering with getMargin(true). Note that all the other margins
+ * are set to the default values defined for the layout without stylename:
+ * </p>
+ * <img src="doc-files/IOrderedLayout_special-margin.png"/>
+ *
+ *
+ * <h3>DOM-structure</h3>
+ *
+ * Note that DOM-structure is an implementation specific and might change in the
+ * future versions of IT Mill Toolkit. The purpose of this documentation is to
+ * to ease reading of the implementation and thus to make implementation of your
+ * own layouts easier.
+ *
+ * <div style="border: 1px solid black; padding: 3px;">OUTERDIV
+ *
+ * <div style="border: 1px solid black; padding: 3px;">Optional STRUCTURE
+ *
+ * <div style="border: 1px solid black; padding: 3px;">CHILDWRAPPER (for each
+ * child)
+ *
+ * <div style="border: 1px solid black; padding: 3px;">Optional ALIGNMENTWRAPPER
+ *
+ * <div style="border: 1px solid black; padding: 3px;">Optional CLIPPER
+ *
+ * <div style="border: 1px solid black; padding: 3px;">CAPTION <span
+ * style="border: 1px solid black; padding: 3px;">ICON-IMG</span> <span
+ * style="border: 1px solid black; padding: 3px;">CAPTION-SPAN</span> <span
+ * style="border: 1px solid black; padding: 3px;">REQUIRED-SPAN</span> <span
+ * style="border: 1px solid black; padding: 3px;">ERRORINDICATOR-DIV</span>
+ * </div>
+ *
+ * <div style="border: 1px solid black; padding: 3px; margin-top:3px;">Widget
+ * component</div>
+ *
+ * </div></div></div>
+ *
+ * </div></div>
+ *
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>If caption and icon are missing from child, <i>Widget component</i> and
+ * <i>CAPTION</i> elements are swithched</li>
+ * <li>If either child manages caption, or it has no caption, icon, required or
+ * error, <i>CAPTION</i> element is not needed at all</li>
+ * <li>If layout is vertical and its width is specified, <i>Optional
+ * STRUCTURE</i> is not present. Otherwise it looks like <div
+ * style="border: 1px solid black; padding: 3px;">TABLE <div
+ * style="border: 1px solid black; padding: 3px;">TBODY <div
+ * style="border: 1px solid black; padding: 3px;">Optional TR only included in
+ * case of horizontal layouts </div></div></div></li>
+ * <li><i>CHILDWRAPPER</i> is a DIV in case of the layout is vertical and width
+ * is specified. For vertical layouts with unknown width it is TR-TD. For
+ * horizontal layouts, it is TR-TD.</li>
+ * <li><i>Optionasl ALIGNMENTWRAPPER</i> are only used alignment is not the
+ * default - top-left. Alignment wrapper structure is
+ * TABLE-TBODY-TR-TD-TABLE-TBODY-TR-TD, where the outer table td is used to
+ * specify the alignments and inner table td to reset the table defaults to
+ * top-left.</li>
+ * <li><i>Optional CLIPPERDIV</i> included in the structure only if alignment
+ * structure is in place and <i>CHILDWRAPPER</i> is not a div and thus can not
+ * be used for clipping</li>
+ * </ul>
+ * </p>
+ *
+ *
+ * @author IT Mill Ltd
+ */
+public class IOldOrderedLayout extends Panel implements Container,
+ ContainerResizedListener {
+
+ public static final String CLASSNAME = "i-orderedlayout";
+
+ public static final int ORIENTATION_VERTICAL = 0;
+ public static final int ORIENTATION_HORIZONTAL = 1;
+
+ /**
+ * If margin and spacing values has been calculated, this holds the values
+ * for the given UIDL style attribute .
+ */
+ private static HashMap measuredMargins = new HashMap();
+
+ /**
+ * Spacing. Correct values will be set in
+ * updateMarginAndSpacingFromCSS(UIDL)
+ */
+ private int hSpacing, vSpacing;
+
+ /**
+ * Margin. Correct values will be set in updateMarginAndSpacingFromCSS(UIDL)
+ */
+ private int marginTop, marginBottom, marginLeft, marginRight;
+
+ int orientationMode = ORIENTATION_VERTICAL;
+
+ protected ApplicationConnection client;
+
+ /**
+ * Reference to Element where wrapped childred are contained. Normally a
+ * DIV, TR or a TBODY element.
+ */
+ private Element wrappedChildContainer;
+
+ /**
+ * List of child widgets. This is not the list of wrappers, but the actual
+ * widgets
+ */
+ private final Vector childWidgets = new Vector();
+
+ /**
+ * In table mode, the root element is table instead of div.
+ */
+ private boolean tableMode = false;
+
+ /**
+ * Root element. This element points to the outmost table-element (in table
+ * mode) or outmost div (in non-table-mode). This non-table-mode this equals
+ * to the getElement().
+ */
+ private Element root = null;
+
+ /**
+ * Last set width of the component. Null if undefined (instead of being "").
+ */
+ private String width = null;
+
+ /**
+ * Last set height of the component. Null if undefined (instead of being
+ * "").
+ */
+ private String height = null;
+ /**
+ * List of child widget wrappers. These wrappers are in exact same indexes
+ * as the widgets in childWidgets list.
+ */
+ private final Vector<WidgetWrapper> childWidgetWrappers = new Vector<WidgetWrapper>();
+
+ /** Whether the component has spacing enabled. */
+ private boolean hasComponentSpacing;
+
+ /** Information about margin states. */
+ private MarginInfo margins = new MarginInfo(0);
+
+ /**
+ * Flag that indicates that the child layouts must be updated as soon as
+ * possible. This will be done in the end of updateFromUIDL.
+ */
+ private boolean childLayoutsHaveChanged = false;
+
+ private boolean isRendering = false;
+
+ private RenderInformation renderInformation = new RenderInformation();
+
+ /**
+ * Construct the DOM of the orderder layout.
+ *
+ * <p>
+ * There are two modes - vertical and horizontal.
+ * <ul>
+ * <li>Vertical mode uses structure: div-root ( div-wrap ( child ) div-wrap
+ * ( child ))).</li>
+ * <li>Horizontal mode uses structure: table ( tbody ( tr-childcontainer (
+ * td-wrap ( child ) td-wrap ( child) )) )</li>
+ * </ul>
+ * where root and childcontainer refer to the root element and the element
+ * that contain WidgetWrappers.
+ * </p>
+ *
+ */
+ public IOldOrderedLayout() {
+ wrappedChildContainer = root = DOM.createDiv();
+ setElement(wrappedChildContainer);
+ setStyleName(CLASSNAME);
+ }
+
+ /**
+ * Update orientation, if it has changed.
+ *
+ * @param newOrientationMode
+ */
+ private void rebuildRootDomStructure(int oldOrientationMode) {
+
+ // Should we have table as a root element?
+ boolean newTableMode = !(orientationMode == ORIENTATION_VERTICAL && width != null);
+
+ // Already in correct mode?
+ if (oldOrientationMode == orientationMode && newTableMode == tableMode) {
+ return;
+ }
+ boolean oldTableMode = tableMode;
+ tableMode = newTableMode;
+
+ /*
+ * If the child are not detached before the parent is cleared with
+ * setInnerHTML the children will also be cleared in IE
+ */
+ if (BrowserInfo.get().isIE()) {
+ while (true) {
+ Element child = DOM.getFirstChild(getElement());
+ if (child != null) {
+ DOM.removeChild(getElement(), child);
+ } else {
+ break;
+ }
+ }
+ }
+
+ // Constuct base DOM-structure and clean any already attached
+ // widgetwrappers from DOM.
+ if (tableMode) {
+ String structure = "<table cellspacing=\"0\" cellpadding=\"0\"";
+
+ if (orientationMode == ORIENTATION_HORIZONTAL) {
+ // Needed for vertical alignment to work
+ structure += " height=\"100%\"";
+ }
+ structure += "><tbody>"
+ + (orientationMode == ORIENTATION_HORIZONTAL ? "<tr valign=\"top\"></tr>"
+ : "") + "</tbody></table>";
+
+ DOM.setInnerHTML(getElement(), structure);
+ root = DOM.getFirstChild(getElement());
+ // set TBODY to be the wrappedChildContainer
+ wrappedChildContainer = DOM.getFirstChild(root);
+ // In case of horizontal layouts, we must user TR instead of TBODY
+ if (orientationMode == ORIENTATION_HORIZONTAL) {
+ wrappedChildContainer = DOM
+ .getFirstChild(wrappedChildContainer);
+ }
+ } else {
+ root = wrappedChildContainer = getElement();
+ DOM.setInnerHTML(getElement(), "");
+ }
+
+ // Reinsert all widget wrappers to this container
+ final int currentOrientationMode = orientationMode;
+ for (int i = 0; i < childWidgetWrappers.size(); i++) {
+ WidgetWrapper wr = childWidgetWrappers.get(i);
+ orientationMode = oldOrientationMode;
+ tableMode = oldTableMode;
+ Element oldWrElement = wr.getElementWrappingWidgetAndCaption();
+ orientationMode = currentOrientationMode;
+ tableMode = newTableMode;
+ String classe = DOM.getElementAttribute(oldWrElement, "class");
+ wr.resetRootElement();
+ Element newWrElement = wr.getElementWrappingWidgetAndCaption();
+ if (classe != null && classe.length() > 0) {
+ DOM.setElementAttribute(newWrElement, "class", classe);
+ }
+ while (DOM.getChildCount(oldWrElement) > 0) {
+ Element c = DOM.getFirstChild(oldWrElement);
+ DOM.removeChild(oldWrElement, c);
+ DOM.appendChild(newWrElement, c);
+ }
+
+ DOM.appendChild(wrappedChildContainer, wr.getElement());
+ }
+
+ // Update child layouts
+ childLayoutsHaveChanged = true;
+ }
+
+ /** Update the contents of the layout from UIDL. */
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ isRendering = true;
+ this.client = client;
+
+ // Only non-cached UIDL:s can introduce changes
+ if (uidl.getBooleanAttribute("cached")) {
+ return;
+ }
+
+ updateMarginAndSpacingSizesFromCSS(uidl);
+
+ // Update sizes, ...
+ if (client.updateComponent(this, uidl, true)) {
+ return;
+ }
+
+ // Rebuild DOM tree root if necessary
+ int oldO = orientationMode;
+ orientationMode = "horizontal".equals(uidl
+ .getStringAttribute("orientation")) ? ORIENTATION_HORIZONTAL
+ : ORIENTATION_VERTICAL;
+ rebuildRootDomStructure(oldO);
+
+ // Handle component spacing later in handleAlignments() method
+ hasComponentSpacing = uidl.getBooleanAttribute("spacing");
+
+ // Collect the list of contained widgets after this update
+ final Vector newWidgets = new Vector();
+ for (final Iterator it = uidl.getChildIterator(); it.hasNext();) {
+ final UIDL uidlForChild = (UIDL) it.next();
+ final Paintable child = client.getPaintable(uidlForChild);
+ newWidgets.add(child);
+ }
+
+ // Iterator for old widgets
+ final Iterator oldWidgetsIterator = (new Vector(childWidgets))
+ .iterator();
+
+ // Iterator for new widgets
+ final Iterator newWidgetsIterator = newWidgets.iterator();
+
+ // Iterator for new UIDL
+ final Iterator newUIDLIterator = uidl.getChildIterator();
+
+ // List to collect all now painted widgets to in order to remove
+ // unpainted ones later
+ final Vector paintedWidgets = new Vector();
+
+ final Vector childsToPaint = new Vector();
+
+ // Add any new widgets to the ordered layout
+ Widget oldChild = null;
+ while (newWidgetsIterator.hasNext()) {
+
+ final Widget newChild = (Widget) newWidgetsIterator.next();
+ final UIDL newChildUIDL = (UIDL) newUIDLIterator.next();
+
+ // Remove any unneeded old widgets
+ if (oldChild == null && oldWidgetsIterator.hasNext()) {
+ // search for next old Paintable which still exists in layout
+ // and delete others
+ while (oldWidgetsIterator.hasNext()) {
+ oldChild = (Widget) oldWidgetsIterator.next();
+ // now oldChild is an instance of Paintable
+ if (paintedWidgets.contains(oldChild)) {
+ continue;
+ } else if (newWidgets.contains(oldChild)) {
+ break;
+ } else {
+ remove(oldChild);
+ oldChild = null;
+ }
+ }
+ }
+
+ if (oldChild == null) {
+ // we are adding components to the end of layout
+ add(newChild);
+ } else if (newChild == oldChild) {
+ // child already attached in correct position
+ oldChild = null;
+ } else if (hasChildComponent(newChild)) {
+
+ // current child has been moved, re-insert before current
+ // oldChild
+ add(newChild, childWidgets.indexOf(oldChild));
+
+ } else {
+ // insert new child before old one
+ add(newChild, childWidgets.indexOf(oldChild));
+ }
+
+ // Update the child component
+ childsToPaint.add(new Object[] { newChild, newChildUIDL });
+
+ // Add this newly handled component to the list of painted
+ // components
+ paintedWidgets.add(newChild);
+ }
+
+ // Remove possibly remaining old widgets which were not in painted UIDL
+ while (oldWidgetsIterator.hasNext()) {
+ oldChild = (Widget) oldWidgetsIterator.next();
+ if (!newWidgets.contains(oldChild)) {
+ remove(oldChild);
+ }
+ }
+
+ // Handle component alignments
+ handleAlignmentsSpacingAndMargins(uidl);
+
+ // Reset sizes for the children
+ updateChildSizes();
+
+ // Paint children
+ for (int i = 0; i < childsToPaint.size(); i++) {
+ Object[] t = (Object[]) childsToPaint.get(i);
+ ((Paintable) t[0]).updateFromUIDL((UIDL) t[1], client);
+ }
+
+ // Update child layouts
+ // TODO This is most probably unnecessary and should be done within
+ // update Child H/W
+ // if (childLayoutsHaveChanged) {
+ // client.runDescendentsLayout(this);
+ // childLayoutsHaveChanged = false;
+ // }
+
+ /* Store the rendered size so we later can see if it has changed */
+ if (renderInformation.updateSize(root)) {
+ client.runDescendentsLayout(this);
+ }
+
+ isRendering = false;
+
+ }
+
+ private void updateMarginAndSpacingSizesFromCSS(UIDL uidl) {
+
+ // Style for this layout
+ String style = uidl.getStringAttribute("style");
+ if (style == null) {
+ style = "";
+ }
+
+ // Try to find measured from cache
+ int[] r = (int[]) measuredMargins.get(style);
+
+ // Measure from DOM
+ if (r == null) {
+ r = new int[] { 0, 0, 0, 0, 0, 0 };
+
+ // Construct DOM for measurements
+ Element e1 = DOM.createTable();
+ DOM.setStyleAttribute(e1, "position", "absolute");
+ DOM.setElementProperty(e1, "cellSpacing", "0");
+ DOM.setElementProperty(e1, "cellPadding", "0");
+ Element e11 = DOM.createTBody();
+ Element e12 = DOM.createTR();
+ Element e13 = DOM.createTD();
+ Element e2 = DOM.createDiv();
+ Element e3 = DOM.createDiv();
+ DOM.setStyleAttribute(e3, "width", "100px");
+ DOM.setStyleAttribute(e3, "height", "100px");
+ DOM.appendChild(getElement(), e1);
+ DOM.appendChild(e1, e11);
+ DOM.appendChild(e11, e12);
+ DOM.appendChild(e12, e13);
+ DOM.appendChild(e13, e2);
+ DOM.appendChild(e2, e3);
+ DOM.setInnerText(e3, ".");
+
+ // Measure different properties
+ final String[] classes = { "margin-top", "margin-right",
+ "margin-bottom", "margin-left", "vspacing", "hspacing" };
+ for (int c = 0; c < 6; c++) {
+ StringBuffer styleBuf = new StringBuffer();
+ final String primaryName = getStylePrimaryName();
+ styleBuf.append(primaryName + "-" + classes[c]);
+ if (style.length() > 0) {
+ final String[] styles = style.split(" ");
+ for (int i = 0; i < styles.length; i++) {
+ styleBuf.append(" ");
+ styleBuf.append(primaryName);
+ styleBuf.append("-");
+ styleBuf.append(styles[i]);
+ styleBuf.append("-");
+ styleBuf.append(classes[c]);
+ }
+ }
+ DOM.setElementProperty(e2, "className", styleBuf.toString());
+
+ // Measure
+ r[c] = DOM.getElementPropertyInt(e1,
+ (c % 2) == 1 ? "offsetWidth" : "offsetHeight") - 100;
+ }
+
+ // Clean-up
+ DOM.removeChild(getElement(), e1);
+
+ // Cache for further use
+ measuredMargins.put(style, r);
+ }
+
+ // Set the properties
+ marginTop = r[0];
+ marginRight = r[1];
+ marginBottom = r[2];
+ marginLeft = r[3];
+ vSpacing = r[4];
+ hSpacing = r[5];
+ }
+
+ /**
+ * While setting width, ensure that margin div is also resized properly.
+ * Furthermore, enable/disable fixed mode
+ */
+ public void setWidth(String newWidth) {
+ super.setWidth(newWidth);
+
+ if (newWidth == null || newWidth.equals("")) {
+ width = null;
+ } else {
+ width = newWidth;
+ }
+
+ // Update child layouts
+ childLayoutsHaveChanged = true;
+ }
+
+ /**
+ * While setting height, ensure that margin div is also resized properly.
+ * Furthermore, enable/disable fixed mode
+ */
+ public void setHeight(String newHeight) {
+ super.setHeight(newHeight);
+ height = newHeight == null || "".equals(newHeight) ? null : newHeight;
+
+ // Update child layouts
+ childLayoutsHaveChanged = true;
+ }
+
+ /** Recalculate and apply the space given for each child in this layout. */
+ private void updateChildSizes() {
+
+ int numChild = childWidgets.size();
+ int childHeightTotal = -1;
+ int childHeightDivisor = 1;
+ int childWidthTotal = -1;
+ int childWidthDivisor = 1;
+
+ // Vertical layout is calculated by us
+ if (height != null) {
+
+ if (height.endsWith("px")) {
+ childHeightTotal = Integer.parseInt(height.substring(0, height
+ .length() - 2));
+ } else {
+ // TODO This might be wrong but only reached if height is
+ // specified by other means than pixels or %
+ childHeightTotal = getElement().getOffsetHeight();
+ }
+
+ // Calculate the space for fixed contents minus marginals
+ childHeightTotal = getElement().getOffsetHeight();
+
+ childHeightTotal -= margins.hasTop() ? marginTop : 0;
+ childHeightTotal -= margins.hasBottom() ? marginBottom : 0;
+
+ // Reduce spacing from the size
+ if (hasComponentSpacing) {
+ childHeightTotal -= ((orientationMode == ORIENTATION_HORIZONTAL) ? hSpacing
+ : vSpacing)
+ * (numChild - 1);
+ }
+
+ // Total space is divided among the children
+ if (orientationMode == ORIENTATION_VERTICAL) {
+ childHeightDivisor = numChild;
+ }
+ }
+
+ // layout is calculated by us
+ if (width != null) {
+
+ if (width.endsWith("px")) {
+ childWidthTotal = Integer.parseInt(width.substring(0, width
+ .length() - 2));
+ } else {
+ // TODO This might be wrong but only reached if width is
+ // specified by other means than pixels or %
+ childWidthTotal = getElement().getOffsetWidth();
+ }
+
+ childWidthTotal -= margins.hasLeft() ? marginLeft : 0;
+ childWidthTotal -= margins.hasRight() ? marginRight : 0;
+
+ // Reduce spacing from the size
+ if (hasComponentSpacing
+ && orientationMode == ORIENTATION_HORIZONTAL) {
+ childWidthTotal -= hSpacing * (numChild - 1);
+ }
+
+ // Total space is divided among the children
+ if (orientationMode == ORIENTATION_HORIZONTAL) {
+ childWidthDivisor = numChild;
+ }
+ }
+
+ // Set the sizes for each child
+ for (Iterator i = childWidgetWrappers.iterator(); i.hasNext();) {
+ int w, h;
+ if (childHeightDivisor > 1) {
+ h = Math.round(((float) childHeightTotal)
+ / (childHeightDivisor--));
+ childHeightTotal -= h;
+ } else {
+ h = childHeightTotal;
+ }
+ if (childWidthDivisor > 1) {
+ w = Math.round(((float) childWidthTotal)
+ / (childWidthDivisor--));
+ childWidthTotal -= w;
+ } else {
+ w = childWidthTotal;
+ }
+ WidgetWrapper ww = (WidgetWrapper) i.next();
+ ww.forceSize(w, h);
+
+ }
+ }
+
+ /** Parse alignments from UIDL and pass whem to correct widgetwrappers */
+ private void handleAlignmentsSpacingAndMargins(UIDL uidl) {
+
+ // Only update margins when they have changed
+ // TODO this should be optimized to avoid reupdating these
+ margins = new MarginInfo(uidl.getIntAttribute("margins"));
+
+ // Component alignments as a comma separated list.
+ // See com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo.java for
+ // possible values.
+ final int[] alignments = uidl.getIntArrayAttribute("alignments");
+ int alignmentIndex = 0;
+
+ // Insert alignment attributes
+ final Iterator it = childWidgetWrappers.iterator();
+
+ while (it.hasNext()) {
+
+ // Calculate alignment info
+ final AlignmentInfo ai = new AlignmentInfo(
+ alignments[alignmentIndex++]);
+
+ final WidgetWrapper wr = (WidgetWrapper) it.next();
+
+ wr.setAlignment(ai.getVerticalAlignment(), ai
+ .getHorizontalAlignment());
+
+ // Handle spacing and margins in this loop as well
+ wr.setSpacingAndMargins(alignmentIndex == 1,
+ alignmentIndex == alignments.length);
+ }
+ }
+
+ /**
+ * Wrapper around single child in the layout.
+ *
+ * This helper also manages spacing, margins and alignment for individual
+ * cells handling. It also can put hard size limits for its contens by
+ * clipping the content to given pixel size.
+ *
+ */
+ class WidgetWrapper {
+
+ /**
+ * When alignment table structure is used, these elements correspond to
+ * the TD elements within the structure. If alignment is not used, these
+ * are null.
+ */
+ Element alignmentTD, innermostTDinAlignmnetStructure;
+
+ /**
+ * When clipping must be done and the element wrapping clipped content
+ * would be TD instead of DIV, this element points to additional DIV
+ * that is used for clipping.
+ */
+ Element clipperDiv;
+
+ /** Caption element when used. */
+ ICaption caption = null;
+ Size captionSize = new Size(-1, -1);
+
+ /**
+ * Last set pixel height for the wrapper. -1 if vertical clipping is not
+ * used.
+ */
+ int lastForcedPixelHeight = -1;
+
+ /**
+ * Last set pixel width for the wrapper. -1 if horizontal clipping is
+ * not used.
+ */
+ int lastForcedPixelWidth = -1;
+
+ int horizontalPadding = 0, verticalPadding = 0;
+
+ /** Widget Wrapper root element */
+ Element wrapperElement;
+
+ private String horizontalAlignment = "left";
+
+ private String verticalAlignment = "top";
+
+ /** Set the root element */
+ public WidgetWrapper() {
+ resetRootElement();
+ }
+
+ public Element getElement() {
+ return wrapperElement;
+ }
+
+ /**
+ * Set the width and height given for the wrapped widget in pixels.
+ *
+ * -1 if unconstrained.
+ */
+ public void forceSize(int pixelWidth, int pixelHeight) {
+
+ // If we are already at the correct size, do nothing
+ if (lastForcedPixelHeight == pixelHeight
+ && lastForcedPixelWidth == pixelWidth) {
+ return;
+ }
+
+ // Clipper DIV is needed?
+ if (tableMode && (pixelHeight >= 0 || pixelWidth >= 0)) {
+ if (clipperDiv == null) {
+ createClipperDiv();
+ }
+ }
+
+ // ClipperDiv is not needed, remove if necessary
+ else if (clipperDiv != null) {
+ removeClipperDiv();
+ }
+
+ Element e = clipperDiv != null ? clipperDiv
+ : getElementWrappingAlignmentStructures();
+
+ // Overflow
+ DOM.setStyleAttribute(e, "overflow", pixelWidth < 0
+ && pixelHeight < 0 ? "" : "hidden");
+
+ // Set size
+ DOM.setStyleAttribute(e, "width", pixelWidth < 0 ? "" : pixelWidth
+ + "px");
+
+ if (pixelHeight >= 0) {
+ DOM.setStyleAttribute(e, "height", pixelHeight + "px");
+ } else {
+ if (e == clipperDiv && !BrowserInfo.get().isIE()) {
+ DOM.setStyleAttribute(e, "height", "100%");
+ }
+
+ }
+
+ // Set cached values
+ lastForcedPixelWidth = pixelWidth;
+ lastForcedPixelHeight = pixelHeight;
+ }
+
+ /** Create a DIV for clipping the child */
+ private void createClipperDiv() {
+ clipperDiv = DOM.createDiv();
+ final Element e = getElementWrappingClipperDiv();
+ String clipperClass = "i-orderedlayout-cl-"
+ + (orientationMode == ORIENTATION_HORIZONTAL ? "h" : "v");
+
+ String elementClass = e.getClassName();
+
+ if (elementClass != null && elementClass.length() > 0) {
+ clipperClass += " " + elementClass;
+ }
+
+ while (DOM.getChildCount(e) > 0) {
+ final Element c = DOM.getFirstChild(e);
+ DOM.removeChild(e, c);
+ DOM.appendChild(clipperDiv, c);
+ }
+
+ if (elementClass != null && elementClass.length() > 0) {
+ e.setClassName("");
+ }
+
+ DOM.appendChild(e, clipperDiv);
+ clipperDiv.setClassName(clipperClass);
+
+ }
+
+ /** Undo createClipperDiv() */
+ private void removeClipperDiv() {
+ final Element e = getElementWrappingClipperDiv();
+ String clipperClass = clipperDiv.getClassName();
+
+ String elementClass = clipperClass.replaceAll(
+ "i-orderedlayout-cl-.", "").trim();
+ while (DOM.getChildCount(clipperDiv) > 0) {
+ final Element c = DOM.getFirstChild(clipperDiv);
+ DOM.removeChild(clipperDiv, c);
+ DOM.appendChild(e, c);
+ }
+ DOM.removeChild(e, clipperDiv);
+ clipperDiv = null;
+ if (elementClass != null && elementClass.length() > 0) {
+ e.setClassName(elementClass);
+ }
+ }
+
+ /**
+ * Get the element containing the caption and the wrapped widget.
+ * Returned element can one of the following:
+ * <ul>
+ * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
+ * <li>(b) TD in just below the root TR of the WrapperElement when in
+ * tableMode</li>
+ * <li>(c) clipperDiv inside the (a) or (b)</li>
+ * <li>(d) The innermost TD within alignment structures located in (a),
+ * (b) or (c)</li>
+ * </ul>
+ *
+ * @return Element described above
+ */
+ private Element getElementWrappingWidgetAndCaption() {
+
+ // When alignment is used, we will can safely return the innermost
+ // TD
+ if (innermostTDinAlignmnetStructure != null) {
+ return innermostTDinAlignmnetStructure;
+ }
+
+ // In all other cases element wrapping the potential alignment
+ // structures is the correct one
+ return getElementWrappingAlignmentStructures();
+ }
+
+ /**
+ * Get the element where alignment structures should be placed in if
+ * they are in use.
+ *
+ * Returned element can one of the following:
+ * <ul>
+ * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
+ * <li>(b) TD in just below the root TR of the WrapperElement when in
+ * tableMode</li>
+ * <li>(c) clipperDiv inside the (a) or (b)</li>
+ * </ul>
+ *
+ * @return Element described above
+ */
+ private Element getElementWrappingAlignmentStructures() {
+
+ // Clipper DIV wraps the alignment structures if present
+ if (clipperDiv != null) {
+ return clipperDiv;
+ }
+
+ // When Clipper DIV is not used, we just give the element
+ // that would wrap it if it would be used
+ return getElementWrappingClipperDiv();
+ }
+
+ /**
+ * Get the element where clipperDiv should be placed in if they it is in
+ * use.
+ *
+ * Returned element can one of the following:
+ * <ul>
+ * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
+ * <li>(b) TD in just below the root TR of the WrapperElement when in
+ * tableMode</li>
+ * </ul>
+ *
+ * @return Element described above
+ */
+ private Element getElementWrappingClipperDiv() {
+
+ // Only vertical layouts in non-table mode use TR as root, for the
+ // rest we can safely give root element
+ if (!tableMode || orientationMode == ORIENTATION_HORIZONTAL) {
+ return wrapperElement;
+ }
+
+ // The root is TR, we'll thus give the TD that is immediately within
+ // the root
+ return DOM.getFirstChild(wrapperElement);
+ }
+
+ /**
+ * Create tr, td or div - depending on the orientation of the layout and
+ * set it as root.
+ *
+ * All contents of the wrapper are cleared. Caller is responsible for
+ * preserving the contents and moving them into new root.
+ *
+ * @return Previous root element.
+ */
+ private void resetRootElement() {
+ // TODO Should we remove the existing element?
+ if (tableMode) {
+ if (orientationMode == ORIENTATION_HORIZONTAL) {
+ wrapperElement = DOM.createTD();
+ DOM.setStyleAttribute(wrapperElement, "height", "100%");
+ } else {
+ wrapperElement = DOM.createTR();
+ DOM.appendChild(wrapperElement, DOM.createTD());
+ }
+ } else {
+ wrapperElement = DOM.createDiv();
+ // Apply 'hasLayout' for IE (needed to get accurate dimension
+ // calculations)
+ if (BrowserInfo.get().isIE()) {
+ DOM.setStyleAttribute(wrapperElement, "zoom", "1");
+ }
+ }
+
+ // Clear any references to intermediate elements
+ clipperDiv = alignmentTD = innermostTDinAlignmnetStructure = null;
+ }
+
+ /** Update the caption of the element contained in this wrapper. */
+ public void updateCaption(UIDL uidl, Paintable paintable) {
+
+ final Widget widget = (Widget) paintable;
+ final Element captionWrapper = getElementWrappingWidgetAndCaption();
+
+ boolean captionDimensionOrPositionUpdated = false;
+
+ // The widget needs caption
+ if (ICaption.isNeeded(uidl)) {
+
+ // If the caption element is missing, create it
+ boolean justAdded = false;
+ if (caption == null) {
+ justAdded = true;
+ caption = new ICaption(paintable, client);
+ }
+
+ // Update caption contents
+ caption.updateCaption(uidl);
+
+ caption.setAlignment(horizontalAlignment);
+
+ int captionHeight = caption.getElement().getOffsetHeight();
+ int captionWidth = caption.getElement().getOffsetWidth();
+ if (captionHeight != captionSize.getHeight()
+ || captionWidth != captionSize.getWidth()) {
+ captionSize = new Size(captionWidth, captionHeight);
+ captionDimensionOrPositionUpdated = true;
+ }
+
+ final boolean after = caption.shouldBePlacedAfterComponent();
+ final Element captionElement = caption.getElement();
+ final Element widgetElement = widget.getElement();
+
+ if (justAdded) {
+
+ // As the caption has just been created, insert it to DOM
+ if (after) {
+ DOM.appendChild(captionWrapper, captionElement);
+ DOM.setElementAttribute(captionWrapper, "class",
+ "i-orderedlayout-w");
+ caption.addStyleName("i-orderedlayout-c");
+ widget.addStyleName("i-orderedlayout-w-e");
+ } else {
+ DOM.insertChild(captionWrapper, captionElement, 0);
+ }
+
+ captionDimensionOrPositionUpdated = true;
+ } else if (after == (DOM.getChildIndex(captionWrapper,
+ widgetElement) > DOM.getChildIndex(captionWrapper,
+ captionElement))) {
+ // Caption exists. Move it to correct position if it is not
+ // there
+ Element firstElement = DOM.getChild(captionWrapper, DOM
+ .getChildCount(captionWrapper) - 2);
+ if (firstElement != null) {
+ DOM.removeChild(captionWrapper, firstElement);
+ DOM.appendChild(captionWrapper, firstElement);
+ }
+ DOM.setElementAttribute(captionWrapper, "class",
+ after ? "i-orderedlayout-w" : "");
+ if (after) {
+ caption.addStyleName("i-orderedlayout-c");
+ widget.addStyleName("i-orderedlayout-w-e");
+ } else {
+ widget.removeStyleName("i-orderedlayout-w-e");
+ caption.removeStyleName("i-orderedlayout-w-c");
+ }
+
+ captionDimensionOrPositionUpdated = true;
+ } else {
+ if (after) {
+ widget.addStyleName("i-orderedlayout-w-e");
+ } else {
+ widget.removeStyleName("i-orderedlayout-w-e");
+ }
+ }
+
+ } else {
+ // Caption is not needed
+
+ // Remove existing caption from DOM
+ if (caption != null) {
+ DOM.removeChild(captionWrapper, caption.getElement());
+ caption = null;
+ DOM.setElementAttribute(captionWrapper, "class", "");
+ widget.removeStyleName("i-orderedlayout-w-e");
+
+ captionDimensionOrPositionUpdated = true;
+ }
+ }
+
+ if (captionDimensionOrPositionUpdated) {
+ client.handleComponentRelativeSize((Widget) paintable);
+ }
+ }
+
+ /**
+ * Set alignments for this wrapper.
+ */
+ void setAlignment(String verticalAlignment, String horizontalAlignment) {
+ this.verticalAlignment = verticalAlignment;
+ this.horizontalAlignment = horizontalAlignment;
+
+ // use one-cell table to implement horizontal alignments, only
+ // for values other than top-left (which is default)
+ if (!horizontalAlignment.equals("left")
+ || !verticalAlignment.equals("top")) {
+
+ // The previous positioning has been left (or unspecified).
+ // Thus we need to create a one-cell-table to position
+ // this element.
+ if (alignmentTD == null) {
+
+ // Store and remove the current childs (widget and caption)
+ Element c1 = DOM
+ .getFirstChild(getElementWrappingWidgetAndCaption());
+ if (c1 != null) {
+ DOM.removeChild(getElementWrappingWidgetAndCaption(),
+ c1);
+ }
+ Element c2 = DOM
+ .getFirstChild(getElementWrappingWidgetAndCaption());
+ if (c2 != null) {
+ DOM.removeChild(getElementWrappingWidgetAndCaption(),
+ c2);
+ }
+
+ // Construct table structure to align children
+ String alignmentTableStructure = "<table cellpadding='0' cellspacing='0' width='100%'";
+ if (BrowserInfo.get().isIE()) {
+ alignmentTableStructure += " style='height: expression(this.parentElement.offsetHeight+\"px\")'";
+ } else {
+ alignmentTableStructure += " height='100%'";
+ }
+ alignmentTableStructure += "><tbody><tr><td>"
+ + "<table cellpadding='0' cellspacing='0' ><tbody><tr><td align='left'>"
+ + "</td></tr></tbody></table></td></tr></tbody></table>";
+ DOM.setInnerHTML(getElementWrappingWidgetAndCaption(),
+ alignmentTableStructure);
+ alignmentTD = DOM
+ .getFirstChild(DOM
+ .getFirstChild(DOM
+ .getFirstChild(DOM
+ .getFirstChild(getElementWrappingWidgetAndCaption()))));
+ innermostTDinAlignmnetStructure = DOM.getFirstChild(DOM
+ .getFirstChild(DOM.getFirstChild(DOM
+ .getFirstChild(alignmentTD))));
+
+ // Restore children inside the
+ if (c1 != null) {
+ DOM.appendChild(innermostTDinAlignmnetStructure, c1);
+ if (c2 != null) {
+ DOM
+ .appendChild(
+ innermostTDinAlignmnetStructure, c2);
+ }
+ }
+
+ } else {
+
+ // Go around optimization bug in WebKit and ensure repaint
+ if (BrowserInfo.get().isSafari()) {
+ String prevValue = DOM.getElementAttribute(alignmentTD,
+ "align");
+ if (!horizontalAlignment.equals(prevValue)) {
+ Element parent = DOM.getParent(alignmentTD);
+ DOM.removeChild(parent, alignmentTD);
+ DOM.appendChild(parent, alignmentTD);
+ }
+ }
+
+ }
+
+ // Set the alignment in td
+ DOM.setElementAttribute(alignmentTD, "align",
+ horizontalAlignment);
+ DOM.setElementAttribute(alignmentTD, "vAlign",
+ verticalAlignment);
+
+ } else {
+
+ // In this case we are requested to position this left
+ // while as it has had some other position in the past.
+ // Thus the one-cell wrapper table must be removed.
+ if (alignmentTD != null) {
+
+ // Move content to main container
+ final Element itd = innermostTDinAlignmnetStructure;
+ final Element alignmentTable = DOM.getParent(DOM
+ .getParent(DOM.getParent(alignmentTD)));
+ final Element target = DOM.getParent(alignmentTable);
+ while (DOM.getChildCount(itd) > 0) {
+ Element content = DOM.getFirstChild(itd);
+ if (content != null) {
+ DOM.removeChild(itd, content);
+ DOM.appendChild(target, content);
+ }
+ }
+
+ // Remove unneeded table element
+ DOM.removeChild(target, alignmentTable);
+
+ alignmentTD = innermostTDinAlignmnetStructure = null;
+ }
+ }
+
+ DOM.setStyleAttribute(getElementWrappingWidgetAndCaption(),
+ "textAlign", horizontalAlignment);
+
+ }
+
+ /** Set class for spacing */
+ void setSpacingAndMargins(boolean first, boolean last) {
+
+ final Element e = getElementWrappingWidgetAndCaption();
+
+ int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
+
+ if (orientationMode == ORIENTATION_HORIZONTAL) {
+ if (first) {
+ if (margins.hasLeft()) {
+ paddingLeft = marginLeft;
+ }
+ } else if (hasComponentSpacing) {
+ paddingLeft = hSpacing;
+ }
+
+ if (last) {
+ if (margins.hasRight()) {
+ paddingRight = marginRight;
+ }
+ }
+
+ if (margins.hasTop()) {
+ paddingTop = marginTop;
+ }
+ if (margins.hasBottom()) {
+ paddingBottom = marginBottom;
+ }
+
+ } else {
+ if (margins.hasLeft()) {
+ paddingLeft = marginLeft;
+ }
+ if (margins.hasRight()) {
+ paddingRight = marginRight;
+ }
+
+ if (first) {
+ if (margins.hasTop()) {
+ paddingTop = marginTop;
+ }
+ } else if (hasComponentSpacing) {
+ paddingTop = vSpacing;
+ }
+ if (last && margins.hasBottom()) {
+ paddingBottom = marginBottom;
+ }
+
+ }
+
+ horizontalPadding = paddingLeft + paddingRight;
+ verticalPadding = paddingTop + paddingBottom;
+
+ DOM.setStyleAttribute(e, "paddingLeft", paddingLeft + "px");
+ DOM.setStyleAttribute(e, "paddingRight", paddingRight + "px");
+
+ DOM.setStyleAttribute(e, "paddingTop", paddingTop + "px");
+ DOM.setStyleAttribute(e, "paddingBottom", paddingBottom + "px");
+ }
+
+ public int getAllocatedHeight() {
+ int reduce = verticalPadding;
+ if (caption != null) {
+ if (orientationMode == ORIENTATION_VERTICAL
+ || (orientationMode == ORIENTATION_HORIZONTAL && !caption
+ .shouldBePlacedAfterComponent())) {
+ reduce += caption.getHeight();
+ }
+ }
+
+ if (lastForcedPixelHeight == -1) {
+ if (height == null) {
+ /*
+ * We have no height specified so return the space allocated
+ * by components so far
+ */
+
+ return getElementWrappingClipperDiv().getOffsetHeight()
+ - reduce;
+ }
+
+ // This should not be possible...
+ return 0;
+ } else {
+ return lastForcedPixelHeight;
+ }
+
+ }
+
+ public int getAllocatedWidth() {
+ int reduce = horizontalPadding;
+ if (caption != null && caption.shouldBePlacedAfterComponent()) {
+ reduce += caption.getWidth();
+ }
+
+ if (width == null) {
+ /*
+ * We have no width specified so return the space allocated by
+ * components so far
+ */
+ return getElementWrappingClipperDiv().getOffsetWidth() - reduce;
+ } else if (lastForcedPixelWidth > -1) {
+ return lastForcedPixelWidth;
+ } else {
+ return 0;
+ }
+ }
+
+ }
+
+ /* documented at super */
+ public void add(Widget child) {
+ add(child, childWidgets.size());
+ }
+
+ /**
+ * Add widget to this layout at given position.
+ *
+ * This methods supports reinserting exiting child into layout - it just
+ * moves the position of the child in the layout.
+ */
+ public void add(Widget child, int atIndex) {
+ /*
+ * <b>Validate:</b> Perform any sanity checks to ensure the Panel can
+ * accept a new Widget. Examples: checking for a valid index on
+ * insertion; checking that the Panel is not full if there is a max
+ * capacity.
+ */
+ if (atIndex < 0 || atIndex > childWidgets.size()) {
+ return;
+ }
+
+ /*
+ * <b>Adjust for Reinsertion:</b> Some Panels need to handle the case
+ * where the Widget is already a child of this Panel. Example: when
+ * performing a reinsert, the index might need to be adjusted to account
+ * for the Widget's removal. See {@link ComplexPanel#adjustIndex(Widget,
+ * int)}.
+ */
+ if (childWidgets.contains(child)) {
+ if (childWidgets.indexOf(child) == atIndex) {
+ return;
+ }
+
+ final int removeFromIndex = childWidgets.indexOf(child);
+ final WidgetWrapper wrapper = childWidgetWrappers
+ .get(removeFromIndex);
+ Element wrapperElement = wrapper.getElement();
+ final int nonWidgetChildElements = DOM
+ .getChildCount(wrappedChildContainer)
+ - childWidgets.size();
+ DOM.removeChild(wrappedChildContainer, wrapperElement);
+ DOM.insertChild(wrappedChildContainer, wrapperElement, atIndex
+ + nonWidgetChildElements);
+ childWidgets.remove(removeFromIndex);
+ childWidgetWrappers.remove(removeFromIndex);
+ childWidgets.insertElementAt(child, atIndex);
+ childWidgetWrappers.insertElementAt(wrapper, atIndex);
+ return;
+ }
+
+ /*
+ * <b>Detach Child:</b> Remove the Widget from its existing parent, if
+ * any. Most Panels will simply call {@link Widget#removeFromParent()}
+ * on the Widget.
+ */
+ child.removeFromParent();
+
+ /*
+ * <b>Logical Attach:</b> Any state variables of the Panel should be
+ * updated to reflect the addition of the new Widget. Example: the
+ * Widget is added to the Panel's {@link WidgetCollection} at the
+ * appropriate index.
+ */
+ childWidgets.insertElementAt(child, atIndex);
+
+ /*
+ * <b>Physical Attach:</b> The Widget's Element must be physically
+ * attached to the Panel's Element, either directly or indirectly.
+ */
+ final WidgetWrapper wrapper = new WidgetWrapper();
+ final int nonWidgetChildElements = DOM
+ .getChildCount(wrappedChildContainer)
+ - childWidgetWrappers.size();
+ childWidgetWrappers.insertElementAt(wrapper, atIndex);
+ DOM.insertChild(wrappedChildContainer, wrapper.getElement(), atIndex
+ + nonWidgetChildElements);
+ DOM.appendChild(wrapper.getElementWrappingWidgetAndCaption(), child
+ .getElement());
+
+ /*
+ * <b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
+ * very last step.
+ */
+ adopt(child);
+ }
+
+ /* documented at super */
+ public boolean remove(Widget child) {
+
+ /*
+ * <b>Validate:</b> Make sure this Panel is actually the parent of the
+ * child Widget; return <code>false</code> if it is not.
+ */
+ if (!childWidgets.contains(child)) {
+ return false;
+ }
+
+ /*
+ * <b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
+ * Widget is still attached.
+ */
+ orphan(child);
+
+ /*
+ * <b>Physical Detach:</b> Adjust the DOM to account for the removal of
+ * the child Widget. The Widget's Element must be physically removed
+ * from the DOM.
+ */
+ final int index = childWidgets.indexOf(child);
+ final WidgetWrapper wrapper = childWidgetWrappers.get(index);
+ DOM.removeChild(wrappedChildContainer, wrapper.getElement());
+ childWidgetWrappers.remove(index);
+
+ /*
+ * <b>Logical Detach:</b> Update the Panel's state variables to reflect
+ * the removal of the child Widget. Example: the Widget is removed from
+ * the Panel's {@link WidgetCollection}.
+ */
+ childWidgets.remove(index);
+
+ if (child instanceof Paintable) {
+ client.unregisterPaintable((Paintable) child);
+ }
+
+ return true;
+ }
+
+ /* documented at super */
+ public boolean hasChildComponent(Widget component) {
+ return childWidgets.contains(component);
+ }
+
+ /* documented at super */
+ public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
+ final int index = childWidgets.indexOf(oldComponent);
+ if (index >= 0) {
+ client.unregisterPaintable((Paintable) oldComponent);
+ remove(oldComponent);
+ add(newComponent, index);
+ }
+ }
+
+ /* documented at super */
+ public void updateCaption(Paintable component, UIDL uidl) {
+ final int index = childWidgets.indexOf(component);
+ if (index >= 0) {
+ childWidgetWrappers.get(index).updateCaption(uidl, component);
+ }
+ }
+
+ /* documented at super */
+ public Iterator iterator() {
+ return childWidgets.iterator();
+ }
+
+ /* documented at super */
+ public void iLayout() {
+ if (isRendering) {
+ return;
+ }
+
+ updateChildSizes();
+ if (client != null) {
+ client.runDescendentsLayout(this);
+ }
+ childLayoutsHaveChanged = false;
+ }
+
+ public boolean requestLayout(Set<Paintable> children) {
+ if (height != null && width != null) {
+ /*
+ * If the height and width has been specified for this container the
+ * child components cannot make the size of the layout change
+ */
+
+ return true;
+ }
+
+ if (renderInformation.updateSize(root)) {
+ /*
+ * Size has changed so we let the child components know about the
+ * new size.
+ */
+ iLayout();
+
+ return false;
+ } else {
+ /*
+ * Size has not changed so we do not need to propagate the event
+ * further
+ */
+ return true;
+ }
+
+ }
+
+ public RenderSpace getAllocatedSpace(Widget child) {
+ final int index = childWidgets.indexOf(child);
+ if (index >= 0) {
+ WidgetWrapper wrapper = childWidgetWrappers.get(index);
+ int w = wrapper.getAllocatedWidth();
+ int h = wrapper.getAllocatedHeight();
+
+ return new RenderSpace(w, h);
+
+ } else {
+ return new RenderSpace();
+ }
+ }
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java
index 6422f369e4..5a8fd0f1df 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IOrderedLayout.java
@@ -1,1603 +1,636 @@
-/*
-@ITMillApache2LicenseForJavaFiles@
- */
-
-package com.itmill.toolkit.terminal.gwt.client.ui;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.Vector;
-
-import com.google.gwt.user.client.DOM;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.ui.Panel;
-import com.google.gwt.user.client.ui.Widget;
-import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
-import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
-import com.itmill.toolkit.terminal.gwt.client.Container;
-import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
-import com.itmill.toolkit.terminal.gwt.client.ICaption;
-import com.itmill.toolkit.terminal.gwt.client.Paintable;
-import com.itmill.toolkit.terminal.gwt.client.RenderInformation;
-import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
-import com.itmill.toolkit.terminal.gwt.client.UIDL;
-import com.itmill.toolkit.terminal.gwt.client.RenderInformation.Size;
-
-/**
- * Full implementation of OrderedLayout client peer.
- *
- * This class implements all features of OrderedLayout. It currently only
- * supports use through UIDL updates. Direct client side use is not (currently)
- * suported in all operation modes.
- *
- *
- * <h2>Features</h2>
- *
- * <h3>Orientation</h3>
- *
- * <p>
- * Orientation of the ordered layout declared whether the children are layouted
- * horizontally or vertically.
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_horizontal.png"/> <img
- * src="doc-files/IOrderedLayout_vertical.png"/>
- *
- * <h3>Spacing</h3>
- *
- * <p>
- * Spacing determines if there should be space between the children. Note that
- * this does not imply margin.
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_horizontal_spacing.png"/> <img
- * src="doc-files/IOrderedLayout_vertical_spacing.png"/>
- *
- * <h3>Margin</h3>
- *
- * <p>
- * Margin determines if there should be margin around children. Note that this
- * does not imply spacing.
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_margin.png"/>
- *
- * <h3>Positioning the caption, icon, required indicator and error</h3>
- *
- * <p>
- * If the child lets the layout to handle captions, by icon, caption, required
- * marker (*) and error icon are placed on top of the component area. Icon will
- * be first and is followed by the caption. Required marker is placed right
- * after the caption text and error icon is placed last. Note that all of these
- * are optional:
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_normal_caption.png"/>
- *
- * <p>
- * If the child lets the layout to handle captions, but the caption and icon are
- * both missing, no line is reserved for the required marker (*) and error icon.
- * Instead they are placed on the right side of the top of the component area.
- * Required marker is placed right after the component text and error icon is
- * placed last. If the component is tall, the indicators are aligned along the
- * top of the component. Note that both of these indicators are optional:
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_no_caption.png"/>
- *
- * <p>
- * In case the child want to handle the caption by itself, layout does not
- * repeat the caption.
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_component_handles_the_caption.png"/>
- *
- * <h3>Aligning the children</h3>
- *
- * <p>
- * The children of the layout can be aligned horizontally and vertically:
- * </p>
- *
- * <img src="doc-files/IOrderedLayout_alignment.png"/>
- *
- * <h3>Fixed height, width or both</h3>
- *
- * <p>
- * When no size is explicitly specified, the size of the layout depends on the
- * size of its children. If the size if specified, either explicitly or as
- * percertages of the parent size, the size is equally divided between the
- * children. In case some children might overflow out of the given space, they
- * are cut to fit the given space. Note that the size can be independently
- * specified for horizontal and vertical dimensions and is independent of the
- * orientation. For example, layout can be horizontal and have fixed 300px
- * height, but still measure its width from the child sizes.
- * </p>
- *
- * <p>
- * Horizontal layout with fixed width of 300px and height of 150px:
- * </p>
- * <img src="doc-files/IOrderedLayout_w300_h150.png"/>
- *
- * <p>
- * Horizontal layout with fixed width of 300px:
- * </p>
- * <img src="doc-files/IOrderedLayout_w300.png"/>
- *
- * <p>
- * Horizontal layout with fixed height of 150px:
- * </p>
- * <img src="doc-files/IOrderedLayout_h150.png"/>
- *
- *
- * <h3>CSS attributes</h3>
- *
- * <p>
- * Sizes for marginals and spacing can be specified for the ordered layout in
- * CSS. For example, here are the defaults for OrderedLayout:
- * </p>
- *
- * <pre>
- * .i-orderedlayout-margin-top {
- * padding-top: 15px;
- * }
- * .i-orderedlayout-margin-right {
- * padding-right: 18px;
- * }
- * .i-orderedlayout-margin-bottom {
- * padding-bottom: 15px;
- * }
- * .i-orderedlayout-margin-left {
- * padding-left: 18px;
- * }
- *
- * .i-orderedlayout-vspacing {
- * margin-top: 8px;
- * }
- * .i-orderedlayout-hspacing {
- * padding-left: 8px;
- * }
- * </pre>
- *
- * <p>
- * When a style-name is set for the layout, this name is included in the style.
- * Note that the unspecified dimensions still default to the values given for
- * the layout without style. For example, if we would like to give each layout
- * with "tested-layout" style quite a bit larger right margin:
- * </p>
- *
- * <pre>
- * .i-orderedlayout-tested-layout-margin-right {
- * padding-right: 100px;
- * }
- * </pre>
- *
- * <p>
- * Here is the rendering with getMargin(true). Note that all the other margins
- * are set to the default values defined for the layout without stylename:
- * </p>
- * <img src="doc-files/IOrderedLayout_special-margin.png"/>
- *
- *
- * <h3>DOM-structure</h3>
- *
- * Note that DOM-structure is an implementation specific and might change in the
- * future versions of IT Mill Toolkit. The purpose of this documentation is to
- * to ease reading of the implementation and thus to make implementation of your
- * own layouts easier.
- *
- * <div style="border: 1px solid black; padding: 3px;">OUTERDIV
- *
- * <div style="border: 1px solid black; padding: 3px;">Optional STRUCTURE
- *
- * <div style="border: 1px solid black; padding: 3px;">CHILDWRAPPER (for each
- * child)
- *
- * <div style="border: 1px solid black; padding: 3px;">Optional ALIGNMENTWRAPPER
- *
- * <div style="border: 1px solid black; padding: 3px;">Optional CLIPPER
- *
- * <div style="border: 1px solid black; padding: 3px;">CAPTION <span
- * style="border: 1px solid black; padding: 3px;">ICON-IMG</span> <span
- * style="border: 1px solid black; padding: 3px;">CAPTION-SPAN</span> <span
- * style="border: 1px solid black; padding: 3px;">REQUIRED-SPAN</span> <span
- * style="border: 1px solid black; padding: 3px;">ERRORINDICATOR-DIV</span>
- * </div>
- *
- * <div style="border: 1px solid black; padding: 3px; margin-top:3px;">Widget
- * component</div>
- *
- * </div></div></div>
- *
- * </div></div>
- *
- * <p>
- * Notes:
- * <ul>
- * <li>If caption and icon are missing from child, <i>Widget component</i> and
- * <i>CAPTION</i> elements are swithched</li>
- * <li>If either child manages caption, or it has no caption, icon, required or
- * error, <i>CAPTION</i> element is not needed at all</li>
- * <li>If layout is vertical and its width is specified, <i>Optional
- * STRUCTURE</i> is not present. Otherwise it looks like <div
- * style="border: 1px solid black; padding: 3px;">TABLE <div
- * style="border: 1px solid black; padding: 3px;">TBODY <div
- * style="border: 1px solid black; padding: 3px;">Optional TR only included in
- * case of horizontal layouts </div></div></div></li>
- * <li><i>CHILDWRAPPER</i> is a DIV in case of the layout is vertical and width
- * is specified. For vertical layouts with unknown width it is TR-TD. For
- * horizontal layouts, it is TR-TD.</li>
- * <li><i>Optionasl ALIGNMENTWRAPPER</i> are only used alignment is not the
- * default - top-left. Alignment wrapper structure is
- * TABLE-TBODY-TR-TD-TABLE-TBODY-TR-TD, where the outer table td is used to
- * specify the alignments and inner table td to reset the table defaults to
- * top-left.</li>
- * <li><i>Optional CLIPPERDIV</i> included in the structure only if alignment
- * structure is in place and <i>CHILDWRAPPER</i> is not a div and thus can not
- * be used for clipping</li>
- * </ul>
- * </p>
- *
- *
- * @author IT Mill Ltd
- */
-public class IOrderedLayout extends Panel implements Container,
- ContainerResizedListener {
-
- public static final String CLASSNAME = "i-orderedlayout";
-
- public static final int ORIENTATION_VERTICAL = 0;
- public static final int ORIENTATION_HORIZONTAL = 1;
-
- /**
- * If margin and spacing values has been calculated, this holds the values
- * for the given UIDL style attribute .
- */
- private static HashMap measuredMargins = new HashMap();
-
- /**
- * Spacing. Correct values will be set in
- * updateMarginAndSpacingFromCSS(UIDL)
- */
- private int hSpacing, vSpacing;
-
- /**
- * Margin. Correct values will be set in updateMarginAndSpacingFromCSS(UIDL)
- */
- private int marginTop, marginBottom, marginLeft, marginRight;
-
- int orientationMode = ORIENTATION_VERTICAL;
-
- protected ApplicationConnection client;
-
- /**
- * Reference to Element where wrapped childred are contained. Normally a
- * DIV, TR or a TBODY element.
- */
- private Element wrappedChildContainer;
-
- /**
- * List of child widgets. This is not the list of wrappers, but the actual
- * widgets
- */
- private final Vector childWidgets = new Vector();
-
- /**
- * In table mode, the root element is table instead of div.
- */
- private boolean tableMode = false;
-
- /**
- * Root element. This element points to the outmost table-element (in table
- * mode) or outmost div (in non-table-mode). This non-table-mode this equals
- * to the getElement().
- */
- private Element root = null;
-
- /**
- * Last set width of the component. Null if undefined (instead of being "").
- */
- private String width = null;
-
- /**
- * Last set height of the component. Null if undefined (instead of being
- * "").
- */
- private String height = null;
- /**
- * List of child widget wrappers. These wrappers are in exact same indexes
- * as the widgets in childWidgets list.
- */
- private final Vector<WidgetWrapper> childWidgetWrappers = new Vector<WidgetWrapper>();
-
- /** Whether the component has spacing enabled. */
- private boolean hasComponentSpacing;
-
- /** Information about margin states. */
- private MarginInfo margins = new MarginInfo(0);
-
- /**
- * Flag that indicates that the child layouts must be updated as soon as
- * possible. This will be done in the end of updateFromUIDL.
- */
- private boolean childLayoutsHaveChanged = false;
-
- private boolean isRendering = false;
-
- private RenderInformation renderInformation = new RenderInformation();
-
- /**
- * Construct the DOM of the orderder layout.
- *
- * <p>
- * There are two modes - vertical and horizontal.
- * <ul>
- * <li>Vertical mode uses structure: div-root ( div-wrap ( child ) div-wrap
- * ( child ))).</li>
- * <li>Horizontal mode uses structure: table ( tbody ( tr-childcontainer (
- * td-wrap ( child ) td-wrap ( child) )) )</li>
- * </ul>
- * where root and childcontainer refer to the root element and the element
- * that contain WidgetWrappers.
- * </p>
- *
- */
- public IOrderedLayout() {
- wrappedChildContainer = root = DOM.createDiv();
- setElement(wrappedChildContainer);
- setStyleName(CLASSNAME);
- }
-
- /**
- * Update orientation, if it has changed.
- *
- * @param newOrientationMode
- */
- private void rebuildRootDomStructure(int oldOrientationMode) {
-
- // Should we have table as a root element?
- boolean newTableMode = !(orientationMode == ORIENTATION_VERTICAL && width != null);
-
- // Already in correct mode?
- if (oldOrientationMode == orientationMode && newTableMode == tableMode) {
- return;
- }
- boolean oldTableMode = tableMode;
- tableMode = newTableMode;
-
- /*
- * If the child are not detached before the parent is cleared with
- * setInnerHTML the children will also be cleared in IE
- */
- if (BrowserInfo.get().isIE()) {
- while (true) {
- Element child = DOM.getFirstChild(getElement());
- if (child != null) {
- DOM.removeChild(getElement(), child);
- } else {
- break;
- }
- }
- }
-
- // Constuct base DOM-structure and clean any already attached
- // widgetwrappers from DOM.
- if (tableMode) {
- String structure = "<table cellspacing=\"0\" cellpadding=\"0\"";
-
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- // Needed for vertical alignment to work
- structure += " height=\"100%\"";
- }
- structure += "><tbody>"
- + (orientationMode == ORIENTATION_HORIZONTAL ? "<tr valign=\"top\"></tr>"
- : "") + "</tbody></table>";
-
- DOM.setInnerHTML(getElement(), structure);
- root = DOM.getFirstChild(getElement());
- // set TBODY to be the wrappedChildContainer
- wrappedChildContainer = DOM.getFirstChild(root);
- // In case of horizontal layouts, we must user TR instead of TBODY
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- wrappedChildContainer = DOM
- .getFirstChild(wrappedChildContainer);
- }
- } else {
- root = wrappedChildContainer = getElement();
- DOM.setInnerHTML(getElement(), "");
- }
-
- // Reinsert all widget wrappers to this container
- final int currentOrientationMode = orientationMode;
- for (int i = 0; i < childWidgetWrappers.size(); i++) {
- WidgetWrapper wr = childWidgetWrappers.get(i);
- orientationMode = oldOrientationMode;
- tableMode = oldTableMode;
- Element oldWrElement = wr.getElementWrappingWidgetAndCaption();
- orientationMode = currentOrientationMode;
- tableMode = newTableMode;
- String classe = DOM.getElementAttribute(oldWrElement, "class");
- wr.resetRootElement();
- Element newWrElement = wr.getElementWrappingWidgetAndCaption();
- if (classe != null && classe.length() > 0) {
- DOM.setElementAttribute(newWrElement, "class", classe);
- }
- while (DOM.getChildCount(oldWrElement) > 0) {
- Element c = DOM.getFirstChild(oldWrElement);
- DOM.removeChild(oldWrElement, c);
- DOM.appendChild(newWrElement, c);
- }
-
- DOM.appendChild(wrappedChildContainer, wr.getElement());
- }
-
- // Update child layouts
- childLayoutsHaveChanged = true;
- }
-
- /** Update the contents of the layout from UIDL. */
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- isRendering = true;
- this.client = client;
-
- // Only non-cached UIDL:s can introduce changes
- if (uidl.getBooleanAttribute("cached")) {
- return;
- }
-
- updateMarginAndSpacingSizesFromCSS(uidl);
-
- // Update sizes, ...
- if (client.updateComponent(this, uidl, true)) {
- return;
- }
-
- // Rebuild DOM tree root if necessary
- int oldO = orientationMode;
- orientationMode = "horizontal".equals(uidl
- .getStringAttribute("orientation")) ? ORIENTATION_HORIZONTAL
- : ORIENTATION_VERTICAL;
- rebuildRootDomStructure(oldO);
-
- // Handle component spacing later in handleAlignments() method
- hasComponentSpacing = uidl.getBooleanAttribute("spacing");
-
- // Collect the list of contained widgets after this update
- final Vector newWidgets = new Vector();
- for (final Iterator it = uidl.getChildIterator(); it.hasNext();) {
- final UIDL uidlForChild = (UIDL) it.next();
- final Paintable child = client.getPaintable(uidlForChild);
- newWidgets.add(child);
- }
-
- // Iterator for old widgets
- final Iterator oldWidgetsIterator = (new Vector(childWidgets))
- .iterator();
-
- // Iterator for new widgets
- final Iterator newWidgetsIterator = newWidgets.iterator();
-
- // Iterator for new UIDL
- final Iterator newUIDLIterator = uidl.getChildIterator();
-
- // List to collect all now painted widgets to in order to remove
- // unpainted ones later
- final Vector paintedWidgets = new Vector();
-
- final Vector childsToPaint = new Vector();
-
- // Add any new widgets to the ordered layout
- Widget oldChild = null;
- while (newWidgetsIterator.hasNext()) {
-
- final Widget newChild = (Widget) newWidgetsIterator.next();
- final UIDL newChildUIDL = (UIDL) newUIDLIterator.next();
-
- // Remove any unneeded old widgets
- if (oldChild == null && oldWidgetsIterator.hasNext()) {
- // search for next old Paintable which still exists in layout
- // and delete others
- while (oldWidgetsIterator.hasNext()) {
- oldChild = (Widget) oldWidgetsIterator.next();
- // now oldChild is an instance of Paintable
- if (paintedWidgets.contains(oldChild)) {
- continue;
- } else if (newWidgets.contains(oldChild)) {
- break;
- } else {
- remove(oldChild);
- oldChild = null;
- }
- }
- }
-
- if (oldChild == null) {
- // we are adding components to the end of layout
- add(newChild);
- } else if (newChild == oldChild) {
- // child already attached in correct position
- oldChild = null;
- } else if (hasChildComponent(newChild)) {
-
- // current child has been moved, re-insert before current
- // oldChild
- add(newChild, childWidgets.indexOf(oldChild));
-
- } else {
- // insert new child before old one
- add(newChild, childWidgets.indexOf(oldChild));
- }
-
- // Update the child component
- childsToPaint.add(new Object[] { newChild, newChildUIDL });
-
- // Add this newly handled component to the list of painted
- // components
- paintedWidgets.add(newChild);
- }
-
- // Remove possibly remaining old widgets which were not in painted UIDL
- while (oldWidgetsIterator.hasNext()) {
- oldChild = (Widget) oldWidgetsIterator.next();
- if (!newWidgets.contains(oldChild)) {
- remove(oldChild);
- }
- }
-
- // Handle component alignments
- handleAlignmentsSpacingAndMargins(uidl);
-
- // Reset sizes for the children
- updateChildSizes();
-
- // Paint children
- for (int i = 0; i < childsToPaint.size(); i++) {
- Object[] t = (Object[]) childsToPaint.get(i);
- ((Paintable) t[0]).updateFromUIDL((UIDL) t[1], client);
- }
-
- // Update child layouts
- // TODO This is most probably unnecessary and should be done within
- // update Child H/W
- // if (childLayoutsHaveChanged) {
- // client.runDescendentsLayout(this);
- // childLayoutsHaveChanged = false;
- // }
-
- /* Store the rendered size so we later can see if it has changed */
- if (renderInformation.updateSize(root)) {
- client.runDescendentsLayout(this);
- }
-
- isRendering = false;
-
- }
-
- private void updateMarginAndSpacingSizesFromCSS(UIDL uidl) {
-
- // Style for this layout
- String style = uidl.getStringAttribute("style");
- if (style == null) {
- style = "";
- }
-
- // Try to find measured from cache
- int[] r = (int[]) measuredMargins.get(style);
-
- // Measure from DOM
- if (r == null) {
- r = new int[] { 0, 0, 0, 0, 0, 0 };
-
- // Construct DOM for measurements
- Element e1 = DOM.createTable();
- DOM.setStyleAttribute(e1, "position", "absolute");
- DOM.setElementProperty(e1, "cellSpacing", "0");
- DOM.setElementProperty(e1, "cellPadding", "0");
- Element e11 = DOM.createTBody();
- Element e12 = DOM.createTR();
- Element e13 = DOM.createTD();
- Element e2 = DOM.createDiv();
- Element e3 = DOM.createDiv();
- DOM.setStyleAttribute(e3, "width", "100px");
- DOM.setStyleAttribute(e3, "height", "100px");
- DOM.appendChild(getElement(), e1);
- DOM.appendChild(e1, e11);
- DOM.appendChild(e11, e12);
- DOM.appendChild(e12, e13);
- DOM.appendChild(e13, e2);
- DOM.appendChild(e2, e3);
- DOM.setInnerText(e3, ".");
-
- // Measure different properties
- final String[] classes = { "margin-top", "margin-right",
- "margin-bottom", "margin-left", "vspacing", "hspacing" };
- for (int c = 0; c < 6; c++) {
- StringBuffer styleBuf = new StringBuffer();
- final String primaryName = getStylePrimaryName();
- styleBuf.append(primaryName + "-" + classes[c]);
- if (style.length() > 0) {
- final String[] styles = style.split(" ");
- for (int i = 0; i < styles.length; i++) {
- styleBuf.append(" ");
- styleBuf.append(primaryName);
- styleBuf.append("-");
- styleBuf.append(styles[i]);
- styleBuf.append("-");
- styleBuf.append(classes[c]);
- }
- }
- DOM.setElementProperty(e2, "className", styleBuf.toString());
-
- // Measure
- r[c] = DOM.getElementPropertyInt(e1,
- (c % 2) == 1 ? "offsetWidth" : "offsetHeight") - 100;
- }
-
- // Clean-up
- DOM.removeChild(getElement(), e1);
-
- // Cache for further use
- measuredMargins.put(style, r);
- }
-
- // Set the properties
- marginTop = r[0];
- marginRight = r[1];
- marginBottom = r[2];
- marginLeft = r[3];
- vSpacing = r[4];
- hSpacing = r[5];
- }
-
- /**
- * While setting width, ensure that margin div is also resized properly.
- * Furthermore, enable/disable fixed mode
- */
- public void setWidth(String newWidth) {
- super.setWidth(newWidth);
-
- if (newWidth == null || newWidth.equals("")) {
- width = null;
- } else {
- width = newWidth;
- }
-
- // Update child layouts
- childLayoutsHaveChanged = true;
- }
-
- /**
- * While setting height, ensure that margin div is also resized properly.
- * Furthermore, enable/disable fixed mode
- */
- public void setHeight(String newHeight) {
- super.setHeight(newHeight);
- height = newHeight == null || "".equals(newHeight) ? null : newHeight;
-
- // Update child layouts
- childLayoutsHaveChanged = true;
- }
-
- /** Recalculate and apply the space given for each child in this layout. */
- private void updateChildSizes() {
-
- int numChild = childWidgets.size();
- int childHeightTotal = -1;
- int childHeightDivisor = 1;
- int childWidthTotal = -1;
- int childWidthDivisor = 1;
-
- // Vertical layout is calculated by us
- if (height != null) {
-
- if (height.endsWith("px")) {
- childHeightTotal = Integer.parseInt(height.substring(0, height
- .length() - 2));
- } else {
- // TODO This might be wrong but only reached if height is
- // specified by other means than pixels or %
- childHeightTotal = getElement().getOffsetHeight();
- }
-
- // Calculate the space for fixed contents minus marginals
- childHeightTotal = getElement().getOffsetHeight();
-
- childHeightTotal -= margins.hasTop() ? marginTop : 0;
- childHeightTotal -= margins.hasBottom() ? marginBottom : 0;
-
- // Reduce spacing from the size
- if (hasComponentSpacing) {
- childHeightTotal -= ((orientationMode == ORIENTATION_HORIZONTAL) ? hSpacing
- : vSpacing)
- * (numChild - 1);
- }
-
- // Total space is divided among the children
- if (orientationMode == ORIENTATION_VERTICAL) {
- childHeightDivisor = numChild;
- }
- }
-
- // layout is calculated by us
- if (width != null) {
-
- if (width.endsWith("px")) {
- childWidthTotal = Integer.parseInt(width.substring(0, width
- .length() - 2));
- } else {
- // TODO This might be wrong but only reached if width is
- // specified by other means than pixels or %
- childWidthTotal = getElement().getOffsetWidth();
- }
-
- childWidthTotal -= margins.hasLeft() ? marginLeft : 0;
- childWidthTotal -= margins.hasRight() ? marginRight : 0;
-
- // Reduce spacing from the size
- if (hasComponentSpacing
- && orientationMode == ORIENTATION_HORIZONTAL) {
- childWidthTotal -= hSpacing * (numChild - 1);
- }
-
- // Total space is divided among the children
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- childWidthDivisor = numChild;
- }
- }
-
- // Set the sizes for each child
- for (Iterator i = childWidgetWrappers.iterator(); i.hasNext();) {
- int w, h;
- if (childHeightDivisor > 1) {
- h = Math.round(((float) childHeightTotal)
- / (childHeightDivisor--));
- childHeightTotal -= h;
- } else {
- h = childHeightTotal;
- }
- if (childWidthDivisor > 1) {
- w = Math.round(((float) childWidthTotal)
- / (childWidthDivisor--));
- childWidthTotal -= w;
- } else {
- w = childWidthTotal;
- }
- WidgetWrapper ww = (WidgetWrapper) i.next();
- ww.forceSize(w, h);
-
- }
- }
-
- /** Parse alignments from UIDL and pass whem to correct widgetwrappers */
- private void handleAlignmentsSpacingAndMargins(UIDL uidl) {
-
- // Only update margins when they have changed
- // TODO this should be optimized to avoid reupdating these
- margins = new MarginInfo(uidl.getIntAttribute("margins"));
-
- // Component alignments as a comma separated list.
- // See com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo.java for
- // possible values.
- final int[] alignments = uidl.getIntArrayAttribute("alignments");
- int alignmentIndex = 0;
-
- // Insert alignment attributes
- final Iterator it = childWidgetWrappers.iterator();
-
- while (it.hasNext()) {
-
- // Calculate alignment info
- final AlignmentInfo ai = new AlignmentInfo(
- alignments[alignmentIndex++]);
-
- final WidgetWrapper wr = (WidgetWrapper) it.next();
-
- wr.setAlignment(ai.getVerticalAlignment(), ai
- .getHorizontalAlignment());
-
- // Handle spacing and margins in this loop as well
- wr.setSpacingAndMargins(alignmentIndex == 1,
- alignmentIndex == alignments.length);
- }
- }
-
- /**
- * Wrapper around single child in the layout.
- *
- * This helper also manages spacing, margins and alignment for individual
- * cells handling. It also can put hard size limits for its contens by
- * clipping the content to given pixel size.
- *
- */
- class WidgetWrapper {
-
- /**
- * When alignment table structure is used, these elements correspond to
- * the TD elements within the structure. If alignment is not used, these
- * are null.
- */
- Element alignmentTD, innermostTDinAlignmnetStructure;
-
- /**
- * When clipping must be done and the element wrapping clipped content
- * would be TD instead of DIV, this element points to additional DIV
- * that is used for clipping.
- */
- Element clipperDiv;
-
- /** Caption element when used. */
- ICaption caption = null;
- Size captionSize = new Size(-1, -1);
-
- /**
- * Last set pixel height for the wrapper. -1 if vertical clipping is not
- * used.
- */
- int lastForcedPixelHeight = -1;
-
- /**
- * Last set pixel width for the wrapper. -1 if horizontal clipping is
- * not used.
- */
- int lastForcedPixelWidth = -1;
-
- int horizontalPadding = 0, verticalPadding = 0;
-
- /** Widget Wrapper root element */
- Element wrapperElement;
-
- private String horizontalAlignment = "left";
-
- private String verticalAlignment = "top";
-
- /** Set the root element */
- public WidgetWrapper() {
- resetRootElement();
- }
-
- public Element getElement() {
- return wrapperElement;
- }
-
- /**
- * Set the width and height given for the wrapped widget in pixels.
- *
- * -1 if unconstrained.
- */
- public void forceSize(int pixelWidth, int pixelHeight) {
-
- // If we are already at the correct size, do nothing
- if (lastForcedPixelHeight == pixelHeight
- && lastForcedPixelWidth == pixelWidth) {
- return;
- }
-
- // Clipper DIV is needed?
- if (tableMode && (pixelHeight >= 0 || pixelWidth >= 0)) {
- if (clipperDiv == null) {
- createClipperDiv();
- }
- }
-
- // ClipperDiv is not needed, remove if necessary
- else if (clipperDiv != null) {
- removeClipperDiv();
- }
-
- Element e = clipperDiv != null ? clipperDiv
- : getElementWrappingAlignmentStructures();
-
- // Overflow
- DOM.setStyleAttribute(e, "overflow", pixelWidth < 0
- && pixelHeight < 0 ? "" : "hidden");
-
- // Set size
- DOM.setStyleAttribute(e, "width", pixelWidth < 0 ? "" : pixelWidth
- + "px");
-
- if (pixelHeight >= 0) {
- DOM.setStyleAttribute(e, "height", pixelHeight + "px");
- } else {
- if (e == clipperDiv && !BrowserInfo.get().isIE()) {
- DOM.setStyleAttribute(e, "height", "100%");
- }
-
- }
-
- // Set cached values
- lastForcedPixelWidth = pixelWidth;
- lastForcedPixelHeight = pixelHeight;
- }
-
- /** Create a DIV for clipping the child */
- private void createClipperDiv() {
- clipperDiv = DOM.createDiv();
- final Element e = getElementWrappingClipperDiv();
- String clipperClass = "i-orderedlayout-cl-"
- + (orientationMode == ORIENTATION_HORIZONTAL ? "h" : "v");
-
- String elementClass = e.getClassName();
-
- if (elementClass != null && elementClass.length() > 0) {
- clipperClass += " " + elementClass;
- }
-
- while (DOM.getChildCount(e) > 0) {
- final Element c = DOM.getFirstChild(e);
- DOM.removeChild(e, c);
- DOM.appendChild(clipperDiv, c);
- }
-
- if (elementClass != null && elementClass.length() > 0) {
- e.setClassName("");
- }
-
- DOM.appendChild(e, clipperDiv);
- clipperDiv.setClassName(clipperClass);
-
- }
-
- /** Undo createClipperDiv() */
- private void removeClipperDiv() {
- final Element e = getElementWrappingClipperDiv();
- String clipperClass = clipperDiv.getClassName();
-
- String elementClass = clipperClass.replaceAll(
- "i-orderedlayout-cl-.", "").trim();
- while (DOM.getChildCount(clipperDiv) > 0) {
- final Element c = DOM.getFirstChild(clipperDiv);
- DOM.removeChild(clipperDiv, c);
- DOM.appendChild(e, c);
- }
- DOM.removeChild(e, clipperDiv);
- clipperDiv = null;
- if (elementClass != null && elementClass.length() > 0) {
- e.setClassName(elementClass);
- }
- }
-
- /**
- * Get the element containing the caption and the wrapped widget.
- * Returned element can one of the following:
- * <ul>
- * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
- * <li>(b) TD in just below the root TR of the WrapperElement when in
- * tableMode</li>
- * <li>(c) clipperDiv inside the (a) or (b)</li>
- * <li>(d) The innermost TD within alignment structures located in (a),
- * (b) or (c)</li>
- * </ul>
- *
- * @return Element described above
- */
- private Element getElementWrappingWidgetAndCaption() {
-
- // When alignment is used, we will can safely return the innermost
- // TD
- if (innermostTDinAlignmnetStructure != null) {
- return innermostTDinAlignmnetStructure;
- }
-
- // In all other cases element wrapping the potential alignment
- // structures is the correct one
- return getElementWrappingAlignmentStructures();
- }
-
- /**
- * Get the element where alignment structures should be placed in if
- * they are in use.
- *
- * Returned element can one of the following:
- * <ul>
- * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
- * <li>(b) TD in just below the root TR of the WrapperElement when in
- * tableMode</li>
- * <li>(c) clipperDiv inside the (a) or (b)</li>
- * </ul>
- *
- * @return Element described above
- */
- private Element getElementWrappingAlignmentStructures() {
-
- // Clipper DIV wraps the alignment structures if present
- if (clipperDiv != null) {
- return clipperDiv;
- }
-
- // When Clipper DIV is not used, we just give the element
- // that would wrap it if it would be used
- return getElementWrappingClipperDiv();
- }
-
- /**
- * Get the element where clipperDiv should be placed in if they it is in
- * use.
- *
- * Returned element can one of the following:
- * <ul>
- * <li>(a) Root DIV of the WrapperElement when not in tableMode</li>
- * <li>(b) TD in just below the root TR of the WrapperElement when in
- * tableMode</li>
- * </ul>
- *
- * @return Element described above
- */
- private Element getElementWrappingClipperDiv() {
-
- // Only vertical layouts in non-table mode use TR as root, for the
- // rest we can safely give root element
- if (!tableMode || orientationMode == ORIENTATION_HORIZONTAL) {
- return wrapperElement;
- }
-
- // The root is TR, we'll thus give the TD that is immediately within
- // the root
- return DOM.getFirstChild(wrapperElement);
- }
-
- /**
- * Create tr, td or div - depending on the orientation of the layout and
- * set it as root.
- *
- * All contents of the wrapper are cleared. Caller is responsible for
- * preserving the contents and moving them into new root.
- *
- * @return Previous root element.
- */
- private void resetRootElement() {
- // TODO Should we remove the existing element?
- if (tableMode) {
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- wrapperElement = DOM.createTD();
- DOM.setStyleAttribute(wrapperElement, "height", "100%");
- } else {
- wrapperElement = DOM.createTR();
- DOM.appendChild(wrapperElement, DOM.createTD());
- }
- } else {
- wrapperElement = DOM.createDiv();
- // Apply 'hasLayout' for IE (needed to get accurate dimension
- // calculations)
- if (BrowserInfo.get().isIE()) {
- DOM.setStyleAttribute(wrapperElement, "zoom", "1");
- }
- }
-
- // Clear any references to intermediate elements
- clipperDiv = alignmentTD = innermostTDinAlignmnetStructure = null;
- }
-
- /** Update the caption of the element contained in this wrapper. */
- public void updateCaption(UIDL uidl, Paintable paintable) {
-
- final Widget widget = (Widget) paintable;
- final Element captionWrapper = getElementWrappingWidgetAndCaption();
-
- boolean captionDimensionOrPositionUpdated = false;
-
- // The widget needs caption
- if (ICaption.isNeeded(uidl)) {
-
- // If the caption element is missing, create it
- boolean justAdded = false;
- if (caption == null) {
- justAdded = true;
- caption = new ICaption(paintable, client);
- }
-
- // Update caption contents
- caption.updateCaption(uidl);
-
- caption.setAlignment(horizontalAlignment);
-
- int captionHeight = caption.getElement().getOffsetHeight();
- int captionWidth = caption.getElement().getOffsetWidth();
- if (captionHeight != captionSize.getHeight()
- || captionWidth != captionSize.getWidth()) {
- captionSize = new Size(captionWidth, captionHeight);
- captionDimensionOrPositionUpdated = true;
- }
-
- final boolean after = caption.shouldBePlacedAfterComponent();
- final Element captionElement = caption.getElement();
- final Element widgetElement = widget.getElement();
-
- if (justAdded) {
-
- // As the caption has just been created, insert it to DOM
- if (after) {
- DOM.appendChild(captionWrapper, captionElement);
- DOM.setElementAttribute(captionWrapper, "class",
- "i-orderedlayout-w");
- caption.addStyleName("i-orderedlayout-c");
- widget.addStyleName("i-orderedlayout-w-e");
- } else {
- DOM.insertChild(captionWrapper, captionElement, 0);
- }
-
- captionDimensionOrPositionUpdated = true;
- } else if (after == (DOM.getChildIndex(captionWrapper,
- widgetElement) > DOM.getChildIndex(captionWrapper,
- captionElement))) {
- // Caption exists. Move it to correct position if it is not
- // there
- Element firstElement = DOM.getChild(captionWrapper, DOM
- .getChildCount(captionWrapper) - 2);
- if (firstElement != null) {
- DOM.removeChild(captionWrapper, firstElement);
- DOM.appendChild(captionWrapper, firstElement);
- }
- DOM.setElementAttribute(captionWrapper, "class",
- after ? "i-orderedlayout-w" : "");
- if (after) {
- caption.addStyleName("i-orderedlayout-c");
- widget.addStyleName("i-orderedlayout-w-e");
- } else {
- widget.removeStyleName("i-orderedlayout-w-e");
- caption.removeStyleName("i-orderedlayout-w-c");
- }
-
- captionDimensionOrPositionUpdated = true;
- } else {
- if (after) {
- widget.addStyleName("i-orderedlayout-w-e");
- } else {
- widget.removeStyleName("i-orderedlayout-w-e");
- }
- }
-
- } else {
- // Caption is not needed
-
- // Remove existing caption from DOM
- if (caption != null) {
- DOM.removeChild(captionWrapper, caption.getElement());
- caption = null;
- DOM.setElementAttribute(captionWrapper, "class", "");
- widget.removeStyleName("i-orderedlayout-w-e");
-
- captionDimensionOrPositionUpdated = true;
- }
- }
-
- if (captionDimensionOrPositionUpdated) {
- client.handleComponentRelativeSize((Widget) paintable);
- }
- }
-
- /**
- * Set alignments for this wrapper.
- */
- void setAlignment(String verticalAlignment, String horizontalAlignment) {
- this.verticalAlignment = verticalAlignment;
- this.horizontalAlignment = horizontalAlignment;
-
- // use one-cell table to implement horizontal alignments, only
- // for values other than top-left (which is default)
- if (!horizontalAlignment.equals("left")
- || !verticalAlignment.equals("top")) {
-
- // The previous positioning has been left (or unspecified).
- // Thus we need to create a one-cell-table to position
- // this element.
- if (alignmentTD == null) {
-
- // Store and remove the current childs (widget and caption)
- Element c1 = DOM
- .getFirstChild(getElementWrappingWidgetAndCaption());
- if (c1 != null) {
- DOM.removeChild(getElementWrappingWidgetAndCaption(),
- c1);
- }
- Element c2 = DOM
- .getFirstChild(getElementWrappingWidgetAndCaption());
- if (c2 != null) {
- DOM.removeChild(getElementWrappingWidgetAndCaption(),
- c2);
- }
-
- // Construct table structure to align children
- String alignmentTableStructure = "<table cellpadding='0' cellspacing='0' width='100%'";
- if (BrowserInfo.get().isIE()) {
- alignmentTableStructure += " style='height: expression(this.parentElement.offsetHeight+\"px\")'";
- } else {
- alignmentTableStructure += " height='100%'";
- }
- alignmentTableStructure += "><tbody><tr><td>"
- + "<table cellpadding='0' cellspacing='0' ><tbody><tr><td align='left'>"
- + "</td></tr></tbody></table></td></tr></tbody></table>";
- DOM.setInnerHTML(getElementWrappingWidgetAndCaption(),
- alignmentTableStructure);
- alignmentTD = DOM
- .getFirstChild(DOM
- .getFirstChild(DOM
- .getFirstChild(DOM
- .getFirstChild(getElementWrappingWidgetAndCaption()))));
- innermostTDinAlignmnetStructure = DOM.getFirstChild(DOM
- .getFirstChild(DOM.getFirstChild(DOM
- .getFirstChild(alignmentTD))));
-
- // Restore children inside the
- if (c1 != null) {
- DOM.appendChild(innermostTDinAlignmnetStructure, c1);
- if (c2 != null) {
- DOM
- .appendChild(
- innermostTDinAlignmnetStructure, c2);
- }
- }
-
- } else {
-
- // Go around optimization bug in WebKit and ensure repaint
- if (BrowserInfo.get().isSafari()) {
- String prevValue = DOM.getElementAttribute(alignmentTD,
- "align");
- if (!horizontalAlignment.equals(prevValue)) {
- Element parent = DOM.getParent(alignmentTD);
- DOM.removeChild(parent, alignmentTD);
- DOM.appendChild(parent, alignmentTD);
- }
- }
-
- }
-
- // Set the alignment in td
- DOM.setElementAttribute(alignmentTD, "align",
- horizontalAlignment);
- DOM.setElementAttribute(alignmentTD, "vAlign",
- verticalAlignment);
-
- } else {
-
- // In this case we are requested to position this left
- // while as it has had some other position in the past.
- // Thus the one-cell wrapper table must be removed.
- if (alignmentTD != null) {
-
- // Move content to main container
- final Element itd = innermostTDinAlignmnetStructure;
- final Element alignmentTable = DOM.getParent(DOM
- .getParent(DOM.getParent(alignmentTD)));
- final Element target = DOM.getParent(alignmentTable);
- while (DOM.getChildCount(itd) > 0) {
- Element content = DOM.getFirstChild(itd);
- if (content != null) {
- DOM.removeChild(itd, content);
- DOM.appendChild(target, content);
- }
- }
-
- // Remove unneeded table element
- DOM.removeChild(target, alignmentTable);
-
- alignmentTD = innermostTDinAlignmnetStructure = null;
- }
- }
-
- DOM.setStyleAttribute(getElementWrappingWidgetAndCaption(),
- "textAlign", horizontalAlignment);
-
- }
-
- /** Set class for spacing */
- void setSpacingAndMargins(boolean first, boolean last) {
-
- final Element e = getElementWrappingWidgetAndCaption();
-
- int paddingLeft = 0, paddingRight = 0, paddingTop = 0, paddingBottom = 0;
-
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- if (first) {
- if (margins.hasLeft()) {
- paddingLeft = marginLeft;
- }
- } else if (hasComponentSpacing) {
- paddingLeft = hSpacing;
- }
-
- if (last) {
- if (margins.hasRight()) {
- paddingRight = marginRight;
- }
- }
-
- if (margins.hasTop()) {
- paddingTop = marginTop;
- }
- if (margins.hasBottom()) {
- paddingBottom = marginBottom;
- }
-
- } else {
- if (margins.hasLeft()) {
- paddingLeft = marginLeft;
- }
- if (margins.hasRight()) {
- paddingRight = marginRight;
- }
-
- if (first) {
- if (margins.hasTop()) {
- paddingTop = marginTop;
- }
- } else if (hasComponentSpacing) {
- paddingTop = vSpacing;
- }
- if (last && margins.hasBottom()) {
- paddingBottom = marginBottom;
- }
-
- }
-
- horizontalPadding = paddingLeft + paddingRight;
- verticalPadding = paddingTop + paddingBottom;
-
- DOM.setStyleAttribute(e, "paddingLeft", paddingLeft + "px");
- DOM.setStyleAttribute(e, "paddingRight", paddingRight + "px");
-
- DOM.setStyleAttribute(e, "paddingTop", paddingTop + "px");
- DOM.setStyleAttribute(e, "paddingBottom", paddingBottom + "px");
- }
-
- public int getAllocatedHeight() {
- int reduce = verticalPadding;
- if (caption != null) {
- if (orientationMode == ORIENTATION_VERTICAL
- || (orientationMode == ORIENTATION_HORIZONTAL && !caption
- .shouldBePlacedAfterComponent())) {
- reduce += caption.getHeight();
- }
- }
-
- if (lastForcedPixelHeight == -1) {
- if (height == null) {
- /*
- * We have no height specified so return the space allocated
- * by components so far
- */
-
- return getElementWrappingClipperDiv().getOffsetHeight()
- - reduce;
- }
-
- // This should not be possible...
- return 0;
- } else {
- return lastForcedPixelHeight;
- }
-
- }
-
- public int getAllocatedWidth() {
- int reduce = horizontalPadding;
- if (caption != null && caption.shouldBePlacedAfterComponent()) {
- reduce += caption.getWidth();
- }
-
- if (width == null) {
- /*
- * We have no width specified so return the space allocated by
- * components so far
- */
- return getElementWrappingClipperDiv().getOffsetWidth() - reduce;
- } else if (lastForcedPixelWidth > -1) {
- return lastForcedPixelWidth;
- } else {
- return 0;
- }
- }
-
- }
-
- /* documented at super */
- public void add(Widget child) {
- add(child, childWidgets.size());
- }
-
- /**
- * Add widget to this layout at given position.
- *
- * This methods supports reinserting exiting child into layout - it just
- * moves the position of the child in the layout.
- */
- public void add(Widget child, int atIndex) {
- /*
- * <b>Validate:</b> Perform any sanity checks to ensure the Panel can
- * accept a new Widget. Examples: checking for a valid index on
- * insertion; checking that the Panel is not full if there is a max
- * capacity.
- */
- if (atIndex < 0 || atIndex > childWidgets.size()) {
- return;
- }
-
- /*
- * <b>Adjust for Reinsertion:</b> Some Panels need to handle the case
- * where the Widget is already a child of this Panel. Example: when
- * performing a reinsert, the index might need to be adjusted to account
- * for the Widget's removal. See {@link ComplexPanel#adjustIndex(Widget,
- * int)}.
- */
- if (childWidgets.contains(child)) {
- if (childWidgets.indexOf(child) == atIndex) {
- return;
- }
-
- final int removeFromIndex = childWidgets.indexOf(child);
- final WidgetWrapper wrapper = childWidgetWrappers
- .get(removeFromIndex);
- Element wrapperElement = wrapper.getElement();
- final int nonWidgetChildElements = DOM
- .getChildCount(wrappedChildContainer)
- - childWidgets.size();
- DOM.removeChild(wrappedChildContainer, wrapperElement);
- DOM.insertChild(wrappedChildContainer, wrapperElement, atIndex
- + nonWidgetChildElements);
- childWidgets.remove(removeFromIndex);
- childWidgetWrappers.remove(removeFromIndex);
- childWidgets.insertElementAt(child, atIndex);
- childWidgetWrappers.insertElementAt(wrapper, atIndex);
- return;
- }
-
- /*
- * <b>Detach Child:</b> Remove the Widget from its existing parent, if
- * any. Most Panels will simply call {@link Widget#removeFromParent()}
- * on the Widget.
- */
- child.removeFromParent();
-
- /*
- * <b>Logical Attach:</b> Any state variables of the Panel should be
- * updated to reflect the addition of the new Widget. Example: the
- * Widget is added to the Panel's {@link WidgetCollection} at the
- * appropriate index.
- */
- childWidgets.insertElementAt(child, atIndex);
-
- /*
- * <b>Physical Attach:</b> The Widget's Element must be physically
- * attached to the Panel's Element, either directly or indirectly.
- */
- final WidgetWrapper wrapper = new WidgetWrapper();
- final int nonWidgetChildElements = DOM
- .getChildCount(wrappedChildContainer)
- - childWidgetWrappers.size();
- childWidgetWrappers.insertElementAt(wrapper, atIndex);
- DOM.insertChild(wrappedChildContainer, wrapper.getElement(), atIndex
- + nonWidgetChildElements);
- DOM.appendChild(wrapper.getElementWrappingWidgetAndCaption(), child
- .getElement());
-
- /*
- * <b>Adopt:</b> Call {@link #adopt(Widget)} to finalize the add as the
- * very last step.
- */
- adopt(child);
- }
-
- /* documented at super */
- public boolean remove(Widget child) {
-
- /*
- * <b>Validate:</b> Make sure this Panel is actually the parent of the
- * child Widget; return <code>false</code> if it is not.
- */
- if (!childWidgets.contains(child)) {
- return false;
- }
-
- /*
- * <b>Orphan:</b> Call {@link #orphan(Widget)} first while the child
- * Widget is still attached.
- */
- orphan(child);
-
- /*
- * <b>Physical Detach:</b> Adjust the DOM to account for the removal of
- * the child Widget. The Widget's Element must be physically removed
- * from the DOM.
- */
- final int index = childWidgets.indexOf(child);
- final WidgetWrapper wrapper = childWidgetWrappers.get(index);
- DOM.removeChild(wrappedChildContainer, wrapper.getElement());
- childWidgetWrappers.remove(index);
-
- /*
- * <b>Logical Detach:</b> Update the Panel's state variables to reflect
- * the removal of the child Widget. Example: the Widget is removed from
- * the Panel's {@link WidgetCollection}.
- */
- childWidgets.remove(index);
-
- if (child instanceof Paintable) {
- client.unregisterPaintable((Paintable) child);
- }
-
- return true;
- }
-
- /* documented at super */
- public boolean hasChildComponent(Widget component) {
- return childWidgets.contains(component);
- }
-
- /* documented at super */
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- final int index = childWidgets.indexOf(oldComponent);
- if (index >= 0) {
- client.unregisterPaintable((Paintable) oldComponent);
- remove(oldComponent);
- add(newComponent, index);
- }
- }
-
- /* documented at super */
- public void updateCaption(Paintable component, UIDL uidl) {
- final int index = childWidgets.indexOf(component);
- if (index >= 0) {
- childWidgetWrappers.get(index).updateCaption(uidl, component);
- }
- }
-
- /* documented at super */
- public Iterator iterator() {
- return childWidgets.iterator();
- }
-
- /* documented at super */
- public void iLayout() {
- if (isRendering) {
- return;
- }
-
- updateChildSizes();
- if (client != null) {
- client.runDescendentsLayout(this);
- }
- childLayoutsHaveChanged = false;
- }
-
- public boolean requestLayout(Set<Paintable> children) {
- if (height != null && width != null) {
- /*
- * If the height and width has been specified for this container the
- * child components cannot make the size of the layout change
- */
-
- return true;
- }
-
- if (renderInformation.updateSize(root)) {
- /*
- * Size has changed so we let the child components know about the
- * new size.
- */
- iLayout();
-
- return false;
- } else {
- /*
- * Size has not changed so we do not need to propagate the event
- * further
- */
- return true;
- }
-
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- final int index = childWidgets.indexOf(child);
- if (index >= 0) {
- WidgetWrapper wrapper = childWidgetWrappers.get(index);
- int w = wrapper.getAllocatedWidth();
- int h = wrapper.getAllocatedHeight();
-
- return new RenderSpace(w, h);
-
- } else {
- return new RenderSpace();
- }
- }
-}
+package com.itmill.toolkit.terminal.gwt.client.ui;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.RenderSpace;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+import com.itmill.toolkit.terminal.gwt.client.Util;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.FloatSize;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.Size;
+import com.itmill.toolkit.terminal.gwt.client.ui.layout.CellBasedLayout;
+import com.itmill.toolkit.terminal.gwt.client.ui.layout.ChildComponentContainer;
+
+public class IOrderedLayout extends CellBasedLayout {
+
+ public static final String CLASSNAME = "i-orderedlayout";
+
+ private static final String MARGIN_TOP_LEFT_CLASSNAMES = "i-orderedlayout-margin-top i-orderedlayout-margin-left";
+ private static final String MARGIN_BOTTOM_RIGHT_CLASSNAMES = "i-orderedlayout-margin-bottom i-orderedlayout-margin-right";
+ private static final String SPACING_CLASSNAMES = "i-orderedlayout-hspacing i-orderedlayout-vspacing";
+
+ private String marginsMeasureStyleName = "";
+ private int orientation = ORIENTATION_HORIZONTAL;
+
+ /**
+ * Size of the layout excluding any margins.
+ */
+ private Size activeLayoutSize = new Size(0, 0);
+
+ // private int spaceForExpansion = 0;
+ private int spaceNobodyWantedToUse = 0;
+
+ private boolean isRendering = false;
+
+ @Override
+ public void setStyleName(String styleName) {
+ super.setStyleName(styleName);
+
+ if (marginsMeasureStyleName.equals(styleName)) {
+ return;
+ }
+
+ String spacingStyleNames = styleName + " " + SPACING_CLASSNAMES;
+ String marginBottomRightStyleNames = styleName + " "
+ + MARGIN_BOTTOM_RIGHT_CLASSNAMES;
+ String marginTopLeftStyleNames = styleName + " "
+ + MARGIN_TOP_LEFT_CLASSNAMES;
+ if (measureMarginsAndSpacing(styleName, marginTopLeftStyleNames,
+ marginBottomRightStyleNames, spacingStyleNames)) {
+ marginsMeasureStyleName = styleName;
+ }
+ }
+
+ public IOrderedLayout() {
+ setStyleName(CLASSNAME);
+ }
+
+ @Override
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ isRendering = true;
+ super.updateFromUIDL(uidl, client);
+
+ // Only non-cached UIDL:s can introduce changes
+ if (uidl.getBooleanAttribute("cached")) {
+ return;
+ }
+
+ handleOrientationUpdate(uidl);
+
+ // IStopWatch w = new IStopWatch("OrderedLayout.updateFromUIDL");
+
+ ArrayList<Widget> uidlWidgets = new ArrayList<Widget>(uidl
+ .getChildCount());
+ ArrayList<ChildComponentContainer> relativeSizeComponents = new ArrayList<ChildComponentContainer>();
+ ArrayList<UIDL> relativeSizeComponentUIDL = new ArrayList<UIDL>();
+ ArrayList<Widget> relativeSizeWidgets = new ArrayList<Widget>();
+
+ int pos = 0;
+ for (final Iterator<UIDL> it = uidl.getChildIterator(); it.hasNext();) {
+ final UIDL childUIDL = it.next();
+ final Paintable child = client.getPaintable(childUIDL);
+ Widget widget = (Widget) child;
+
+ // Create container for component
+ ChildComponentContainer childComponentContainer = getComponentContainer(widget);
+
+ if (childComponentContainer == null) {
+ // This is a new component
+ childComponentContainer = createChildContainer(widget);
+ }
+
+ addOrMoveChild(childComponentContainer, pos++);
+
+ /*
+ * Components which are to be expanded in the same orientation as
+ * the layout are rendered later when it is clear how much space
+ * they can use
+ */
+ FloatSize relativeSize = Util.parseRelativeSize(childUIDL);
+ childComponentContainer.setRelativeSize(relativeSize);
+
+ if (hasRelativeSize(relativeSize, orientation)) {
+ relativeSizeComponents.add(childComponentContainer);
+ relativeSizeComponentUIDL.add(childUIDL);
+ relativeSizeWidgets.add(widget);
+ } else {
+ childComponentContainer.renderChild(childUIDL, client);
+ }
+
+ uidlWidgets.add(widget);
+
+ }
+
+ // w.mark("Rendering of "
+ // + (uidlWidgets.size() - relativeSizeComponents.size())
+ // + " absolute size components done");
+
+ /*
+ * Remove any children after pos. These are the ones that previously
+ * were in the layout but have now been removed
+ */
+ removeChildrenAfter(pos);
+
+ // w.mark("Old children removed");
+
+ /* Fetch alignments and expand ratio from UIDL */
+ updateAlignmentsAndExpandRatios(uidl, uidlWidgets);
+
+ /* Fetch widget sizes from rendered components */
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+
+ /*
+ * Update widget size from DOM
+ */
+ childComponentContainer.updateWidgetSize();
+ }
+
+ recalculateLayout();
+
+ /* Render relative size components */
+ for (int i = 0; i < relativeSizeComponents.size(); i++) {
+ ChildComponentContainer childComponentContainer = relativeSizeComponents
+ .get(i);
+ UIDL childUIDL = relativeSizeComponentUIDL.get(i);
+
+ childComponentContainer.renderChild(childUIDL, client);
+ // childComponentContainer.updateWidgetSize();
+ }
+
+ /* Fetch widget sizes for relative size components */
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+
+ /* Update widget size from DOM */
+ childComponentContainer.updateWidgetSize();
+ }
+
+ // w.mark("Rendering of " + (relativeSizeComponents.size())
+ // + " relative size components done");
+
+ /* Recalculate component sizes and alignments */
+ recalculateComponentSizesAndAlignments();
+ // w.mark("recalculateComponentSizesAndAlignments done");
+
+ /* Must inform child components about possible size updates */
+ client.runDescendentsLayout(this);
+ // w.mark("runDescendentsLayout done");
+
+ isRendering = false;
+ }
+
+ private void recalculateLayout() {
+ /* Calculate space for relative size components */
+ int spaceForExpansion = calculateLayoutDimensions();
+
+ /* Divide expansion space between component containers */
+ expandComponentContainers(spaceForExpansion);
+
+ /* Update container sizes */
+ calculateContainerSize();
+
+ }
+
+ private void expandComponentContainers(int spaceForExpansion) {
+
+ int remaining = spaceForExpansion;
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+ remaining -= childComponentContainer.expand(orientation,
+ spaceForExpansion);
+ }
+
+ if (remaining > 0) {
+ // Some left-over pixels due to rounding errors
+
+ // Add extra pixels to first container
+ getFirstChildComponentContainer().expandExtra(orientation,
+ remaining);
+
+ }
+
+ }
+
+ private static boolean hasRelativeSize(FloatSize relativeSize,
+ int orientation) {
+ if (relativeSize == null) {
+ return false;
+ }
+ if (orientation == ORIENTATION_HORIZONTAL) {
+ return relativeSize.getWidth() >= 0;
+ } else {
+ return relativeSize.getHeight() >= 0;
+ }
+ }
+
+ private void handleOrientationUpdate(UIDL uidl) {
+ int newOrientation = ORIENTATION_VERTICAL;
+ if ("horizontal".equals(uidl.getStringAttribute("orientation"))) {
+ newOrientation = ORIENTATION_HORIZONTAL;
+ }
+
+ if (orientation != newOrientation) {
+ orientation = newOrientation;
+
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+ childComponentContainer.setOrientation(orientation);
+ }
+ }
+
+ }
+
+ private void recalculateComponentSizesAndAlignments() {
+ if (widgetToComponentContainer.isEmpty()) {
+ return;
+ }
+
+ updateContainerMargins();
+
+ /*
+ * Update the height of relative height components in a horizontal
+ * layout or the width for relative width components in a vertical
+ * layout
+ */
+ updateRelativeSizesInNonMainDirection();
+
+ /* Calculate alignments */
+ calculateAlignments();
+
+ }
+
+ private void updateRelativeSizesInNonMainDirection() {
+ int updateDirection = 1 - orientation;
+ for (ChildComponentContainer componentContainer : widgetToComponentContainer
+ .values()) {
+ if (componentContainer.isComponentRelativeSized(updateDirection)) {
+ client.handleComponentRelativeSize(componentContainer
+ .getWidget());
+ }
+ }
+
+ }
+
+ private int calculateLayoutDimensions() {
+ int summedWidgetWidth = 0;
+ int summedWidgetHeight = 0;
+
+ int maxWidgetWidth = 0;
+ int maxWidgetHeight = 0;
+
+ // Calculate layout dimensions from component dimensions
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+
+ if (childComponentContainer.isComponentRelativeSized(orientation)) {
+ continue;
+ }
+
+ Size s = childComponentContainer.getWidgetSize();
+ int widgetWidth = s.getWidth()
+ + childComponentContainer.getCaptionWidthAfterComponent();
+
+ if (isDynamicWidth()) {
+ /*
+ * For a dynamic width layout the max of caption/widget defines
+ * the required size
+ */
+ int captionWidth = childComponentContainer.getCaptionWidth();
+ if (captionWidth > widgetWidth) {
+ widgetWidth = captionWidth;
+ }
+ }
+
+ int widgetHeight = s.getHeight()
+ + childComponentContainer.getCaptionHeightAboveComponent();
+
+ // ApplicationConnection.getConsole().log(
+ // "Container width: " + widgetWidth);
+
+ summedWidgetWidth += widgetWidth;
+ summedWidgetHeight += widgetHeight;
+
+ maxWidgetHeight = Math.max(maxWidgetHeight, widgetHeight);
+ maxWidgetWidth = Math.max(maxWidgetWidth, widgetWidth);
+ }
+
+ if (isHorizontal()) {
+ summedWidgetWidth += activeSpacing.hSpacing
+ * (widgetToComponentContainer.size() - 1);
+ } else {
+ summedWidgetHeight += activeSpacing.vSpacing
+ * (widgetToComponentContainer.size() - 1);
+ }
+
+ Size layoutSize = updateLayoutDimensions(summedWidgetWidth,
+ summedWidgetHeight, maxWidgetWidth, maxWidgetHeight);
+
+ int remainingSpace;
+ if (isHorizontal()) {
+ remainingSpace = layoutSize.getWidth() - summedWidgetWidth;
+ } else {
+ remainingSpace = layoutSize.getHeight() - summedWidgetHeight;
+ }
+ if (remainingSpace < 0) {
+ remainingSpace = 0;
+ }
+
+ return remainingSpace;
+ }
+
+ private void calculateAlignments() {
+ int w = 0;
+ int h = 0;
+
+ if (isHorizontal()) {
+ // HORIZONTAL
+ h = activeLayoutSize.getHeight();
+ if (!isDynamicWidth()) {
+ w = -1;
+ }
+
+ } else {
+ // VERTICAL
+ w = activeLayoutSize.getWidth();
+ if (!isDynamicHeight()) {
+ h = -1;
+ }
+ }
+
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+ childComponentContainer.updateAlignments(w, h);
+ }
+
+ }
+
+ private void calculateContainerSize() {
+
+ /*
+ * Container size here means the size the container gets from the
+ * component. The expansion size is not include in this but taken
+ * separately into account.
+ */
+ int height = 0, width = 0;
+ if (isHorizontal()) {
+ height = activeLayoutSize.getHeight();
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+ if (!childComponentContainer
+ .isComponentRelativeSized(ORIENTATION_HORIZONTAL)) {
+ /*
+ * Only components with non-relative size in the main
+ * direction has a container size
+ */
+ width = childComponentContainer.getWidgetSize().getWidth()
+ + childComponentContainer
+ .getCaptionWidthAfterComponent();
+ int captionWidth = childComponentContainer
+ .getCaptionWidth();
+ if (captionWidth > width) {
+ width = captionWidth;
+ }
+ } else {
+ width = 0;
+ }
+
+ childComponentContainer.setContainerSize(width, height);
+ }
+ } else {
+ width = activeLayoutSize.getWidth();
+ for (ChildComponentContainer childComponentContainer : widgetToComponentContainer
+ .values()) {
+
+ if (!childComponentContainer
+ .isComponentRelativeSized(ORIENTATION_VERTICAL)) {
+ /*
+ * Only components with non-relative size in the main
+ * direction has a container size
+ */
+ height = childComponentContainer.getWidgetSize()
+ .getHeight()
+ + childComponentContainer
+ .getCaptionHeightAboveComponent();
+ } else {
+ height = 0;
+ }
+
+ childComponentContainer.setContainerSize(width, height);
+ }
+
+ }
+
+ }
+
+ private Size updateLayoutDimensions(int totalComponentWidth,
+ int totalComponentHeight, int maxComponentWidth,
+ int maxComponentHeight) {
+
+ int activeLayoutWidth = 0;
+ int activeLayoutHeight = 0;
+
+ // Update layout dimensions
+ if (isHorizontal()) {
+ // Horizontal
+ if (isDynamicWidth()) {
+ activeLayoutWidth = totalComponentWidth;
+ setOuterLayoutWidth(activeLayoutWidth);
+ } else {
+ activeLayoutWidth = getOffsetWidth()
+ - activeMargins.getHorizontal();
+ }
+
+ if (isDynamicHeight()) {
+ activeLayoutHeight = maxComponentHeight;
+ setOuterLayoutHeight(maxComponentHeight);
+ } else {
+ activeLayoutHeight = getOffsetHeight()
+ - activeMargins.getVertical();
+
+ }
+
+ } else {
+ // Vertical
+ if (isDynamicHeight()) {
+ activeLayoutWidth = maxComponentWidth;
+ setOuterLayoutWidth(maxComponentWidth);
+ } else {
+ activeLayoutWidth = getOffsetWidth()
+ - activeMargins.getHorizontal();
+ }
+
+ if (isDynamicHeight()) {
+ activeLayoutHeight = totalComponentHeight;
+ setOuterLayoutHeight(totalComponentHeight);
+ } else {
+ activeLayoutHeight = getOffsetHeight()
+ - activeMargins.getVertical();
+ }
+ }
+
+ activeLayoutSize.setWidth(activeLayoutWidth);
+ activeLayoutSize.setHeight(activeLayoutHeight);
+
+ return activeLayoutSize;
+ }
+
+ private void setOuterLayoutWidth(int activeLayoutWidth) {
+ super.setWidth((activeLayoutWidth + activeMargins.getHorizontal())
+ + "px");
+
+ }
+
+ private void setOuterLayoutHeight(int activeLayoutHeight) {
+ super.setHeight((activeLayoutHeight + activeMargins.getVertical())
+ + "px");
+
+ }
+
+ private void updateContainerMargins() {
+ ChildComponentContainer firstChildComponent = getFirstChildComponentContainer();
+
+ firstChildComponent.setMarginLeft(0);
+ firstChildComponent.setMarginTop(0);
+
+ for (ChildComponentContainer childComponent : widgetToComponentContainer
+ .values()) {
+ if (childComponent == firstChildComponent) {
+ continue;
+ }
+
+ if (isHorizontal()) {
+ childComponent.setMarginLeft(activeSpacing.hSpacing);
+ } else {
+ childComponent.setMarginTop(activeSpacing.vSpacing);
+ }
+ }
+ }
+
+ private boolean isHorizontal() {
+ return orientation == ORIENTATION_HORIZONTAL;
+ }
+
+ private boolean isVertical() {
+ return orientation == ORIENTATION_VERTICAL;
+ }
+
+ private ChildComponentContainer createChildContainer(Widget child) {
+
+ // Create a container DIV for the child
+ ChildComponentContainer childComponent = new ChildComponentContainer(
+ child, orientation);
+
+ return childComponent;
+
+ }
+
+ public RenderSpace getAllocatedSpace(Widget child) {
+ int width = 0;
+ int height = 0;
+ ChildComponentContainer childComponentContainer = getComponentContainer(child);
+ // WIDTH CALCULATION
+ if (isVertical()) {
+ width = activeLayoutSize.getWidth();
+ width -= childComponentContainer.getCaptionWidthAfterComponent();
+ } else if (!isDynamicWidth()) {
+ // HORIZONTAL
+ width = childComponentContainer.getContSize().getWidth();
+ width -= childComponentContainer.getCaptionWidthAfterComponent();
+ }
+
+ // HEIGHT CALCULATION
+ if (isHorizontal()) {
+ height = activeLayoutSize.getHeight();
+ height -= childComponentContainer.getCaptionHeightAboveComponent();
+ } else if (!isDynamicHeight()) {
+ // VERTICAL
+ height = childComponentContainer.getContSize().getHeight();
+ height -= childComponentContainer.getCaptionHeightAboveComponent();
+ }
+
+ // ApplicationConnection.getConsole().log(
+ // "allocatedSpace for " + Util.getSimpleName(child) + ": "
+ // + width + "," + height);
+ RenderSpace space = new RenderSpace(width, height);
+ return space;
+ }
+
+ private boolean recalculateLayoutAndComponentSizes() {
+ Size sizeBefore = new Size(activeLayoutSize.getWidth(),
+ activeLayoutSize.getHeight());
+
+ recalculateLayout();
+
+ recalculateComponentSizesAndAlignments();
+
+ boolean sameSize = (sizeBefore.equals(activeLayoutSize));
+
+ return sameSize;
+ }
+
+ public boolean requestLayout(Set<Paintable> children) {
+ for (Paintable p : children) {
+ /* Update widget size from DOM */
+ getComponentContainer((Widget) p).updateWidgetSize();
+ }
+
+ boolean sameSize = recalculateLayoutAndComponentSizes();
+ if (!sameSize) {
+ /* Must inform child components about possible size updates */
+ client.runDescendentsLayout(this);
+ }
+
+ /* Automatically propagated upwards if the size has changed */
+
+ return sameSize;
+ }
+
+ @Override
+ public void setHeight(String height) {
+ super.setHeight(height);
+
+ if (!isRendering) {
+ if (recalculateLayoutAndComponentSizes()) {
+ /* Must inform child components about possible size updates */
+ client.runDescendentsLayout(this);
+ }
+ }
+ }
+
+ @Override
+ public void setWidth(String width) {
+ super.setWidth(width);
+
+ if (!isRendering) {
+ if (recalculateLayoutAndComponentSizes()) {
+ /* Must inform child components about possible size updates */
+ client.runDescendentsLayout(this);
+ }
+ }
+ }
+
+ protected void updateAlignmentsAndExpandRatios(UIDL uidl,
+ ArrayList<Widget> renderedWidgets) {
+
+ /*
+ * UIDL contains component alignments as a comma separated list.
+ *
+ * See com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo.java for
+ * possible values.
+ */
+ final int[] alignments = uidl.getIntArrayAttribute("alignments");
+
+ /*
+ * UIDL contains normalized expand ratios as a comma separated list.
+ */
+ final int[] expandRatios = uidl.getIntArrayAttribute("expandRatios");
+
+ for (int i = 0; i < renderedWidgets.size(); i++) {
+ Widget widget = renderedWidgets.get(i);
+
+ ChildComponentContainer container = getComponentContainer(widget);
+
+ // Calculate alignment info
+ container.setAlignment(new AlignmentInfo(alignments[i]));
+
+ // Update expand ratio
+ container.setExpandRatio(expandRatios[i]);
+ }
+ }
+
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java
index 7e4297a7bd..2847900a88 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/IPanel.java
@@ -152,7 +152,7 @@ public class IPanel extends SimplePanel implements Container,
}
// Height adjustment
- iLayout(false);
+ // iLayout(false);
// Render content
final UIDL layoutUidl = uidl.getChildUIDL(0);
@@ -164,8 +164,13 @@ public class IPanel extends SimplePanel implements Container,
setWidget((Widget) newLayout);
layout = newLayout;
}
- (layout).updateFromUIDL(layoutUidl, client);
+ layout.updateFromUIDL(layoutUidl, client);
+
+ if (BrowserInfo.get().isIE7()) {
+ // IE is not able to make the offsetWidth for contentNode correct for some reason...
+ iLayout(false);
+ }
// We may have actions attached to this panel
if (uidl.getChildCount() > 1) {
final int cnt = uidl.getChildCount();
@@ -279,6 +284,25 @@ public class IPanel extends SimplePanel implements Container,
}
+ if (BrowserInfo.get().isIE7() && (width == null || width.equals(""))) {
+ //FIXME This won't work if the panel's content gets narrower later on...
+ int captionMarginLeft = captionNode.getAbsoluteLeft()
+ - getElement().getAbsoluteLeft();
+ int captionWidth = captionNode.getOffsetWidth() + captionMarginLeft;
+ int contentWidth = contentNode.getOffsetWidth();
+ int layoutWidth = ((Widget) layout).getOffsetWidth()
+ + getContainerBorderWidth();
+ int width = contentWidth;
+ if (captionWidth > width) {
+ width = captionWidth;
+ }
+ if (layoutWidth > width) {
+ width = layoutWidth;
+ }
+
+ super.setWidth(width + "px");
+ }
+
if (runGeckoFix && BrowserInfo.get().isGecko()) {
// workaround for #1764
if (width == null || width.equals("")) {
@@ -447,7 +471,7 @@ public class IPanel extends SimplePanel implements Container,
}
public boolean requestLayout(Set<Paintable> child) {
- if (height != null && width != null) {
+ if (height != null && height != "" && width != null && width != "") {
/*
* If the height and width has been specified the child components
* cannot make the size of the layout change
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/CellBasedLayout.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/CellBasedLayout.java
new file mode 100644
index 0000000000..121cdf9ad8
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/CellBasedLayout.java
@@ -0,0 +1,287 @@
+package com.itmill.toolkit.terminal.gwt.client.ui.layout;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.ComplexPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.Container;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+import com.itmill.toolkit.terminal.gwt.client.ui.MarginInfo;
+
+public abstract class CellBasedLayout extends ComplexPanel implements Container {
+
+ protected Map<Widget, ChildComponentContainer> widgetToComponentContainer = new HashMap<Widget, ChildComponentContainer>();
+
+ protected ApplicationConnection client = null;
+
+ private Element root;
+
+ public static final int ORIENTATION_VERTICAL = 0;
+ public static final int ORIENTATION_HORIZONTAL = 1;
+
+ protected final Margins marginsFromCSS = new Margins(10, 10, 10, 10);
+ protected final Margins activeMargins = new Margins(0, 0, 0, 0);
+ protected MarginInfo activeMarginsInfo = new MarginInfo(false, false,
+ false, false);
+
+ protected boolean spacingEnabled = false;
+ protected final Spacing spacingFromCSS = new Spacing(12, 12);
+ protected final Spacing activeSpacing = new Spacing(0, 0);
+
+ private Widget clearWidget;
+
+ private boolean dynamicWidth;
+
+ private boolean dynamicHeight;
+
+ private static class ClearWidget extends Widget {
+ public ClearWidget() {
+ Element clearElement = DOM.createDiv();
+ DOM.setStyleAttribute(clearElement, "clear", "both");
+ DOM.setStyleAttribute(clearElement, "width", "0px");
+ DOM.setStyleAttribute(clearElement, "height", "0px");
+ setElement(clearElement);
+ }
+ }
+
+ public static class Spacing {
+
+ public int hSpacing = 0;
+ public int vSpacing = 0;
+
+ public Spacing(int hSpacing, int vSpacing) {
+ this.hSpacing = hSpacing;
+ this.vSpacing = vSpacing;
+ }
+
+ @Override
+ public String toString() {
+ return "Spacing [hSpacing=" + hSpacing + ",vSpacing=" + vSpacing
+ + "]";
+ }
+
+ }
+
+ public CellBasedLayout() {
+ super();
+
+ setElement(DOM.createDiv());
+ DOM.setStyleAttribute(getElement(), "overflow", "hidden");
+
+ root = DOM.createDiv();
+ DOM.setStyleAttribute(root, "width", "500%");
+ getElement().appendChild(root);
+
+ clearWidget = new ClearWidget();
+ add(clearWidget, root);
+
+ }
+
+ public boolean hasChildComponent(Widget component) {
+ return widgetToComponentContainer.containsKey(component);
+ }
+
+ public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
+ // TODO: Must support
+ throw new UnsupportedOperationException();
+ }
+
+ public void updateCaption(Paintable component, UIDL uidl) {
+ ChildComponentContainer componentContainer = getComponentContainer((Widget) component);
+ componentContainer.updateCaption(uidl, client);
+ }
+
+ public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
+ this.client = client;
+
+ // Only non-cached UIDL:s can introduce changes
+ if (uidl.getBooleanAttribute("cached")) {
+ return;
+ }
+
+ // This call should be made first. Ensure correct implementation,
+ // and don't let the containing coordinateLayout manage caption, etc.
+
+ if (client.updateComponent(this, uidl, true)) {
+ return;
+ }
+
+ handleMarginsAndSpacing(uidl);
+
+ handleDynamicDimensions(uidl);
+
+ }
+
+ private void handleDynamicDimensions(UIDL uidl) {
+ String w = uidl.hasAttribute("width") ? uidl
+ .getStringAttribute("width") : "";
+
+ String h = uidl.hasAttribute("height") ? uidl
+ .getStringAttribute("height") : "";
+
+ if (w.equals("")) {
+ dynamicWidth = true;
+ } else {
+ dynamicWidth = false;
+ }
+
+ if (h.equals("")) {
+ dynamicHeight = true;
+ } else {
+ dynamicHeight = false;
+ }
+
+ }
+
+ protected void addOrMoveChild(ChildComponentContainer childComponent,
+ int position) {
+ widgetToComponentContainer.put(childComponent.getWidget(),
+ childComponent);
+ super.insert(childComponent, root, position, true);
+
+ }
+
+ protected ChildComponentContainer getComponentContainer(Widget child) {
+ return widgetToComponentContainer.get(child);
+ }
+
+ protected boolean isDynamicWidth() {
+ return dynamicWidth;
+ }
+
+ protected boolean isDynamicHeight() {
+ return dynamicHeight;
+ }
+
+ private void handleMarginsAndSpacing(UIDL uidl) {
+ MarginInfo newMargins = new MarginInfo(uidl.getIntAttribute("margins"));
+ updateMargins(newMargins);
+ boolean spacing = uidl.getBooleanAttribute("spacing");
+ updateSpacing(spacing);
+ }
+
+ private void updateSpacing(boolean spacing) {
+ if (spacing != spacingEnabled) {
+ spacingEnabled = spacing;
+ if (spacing) {
+ activeSpacing.hSpacing = spacingFromCSS.hSpacing;
+ activeSpacing.vSpacing = spacingFromCSS.vSpacing;
+ } else {
+ activeSpacing.hSpacing = 0;
+ activeSpacing.vSpacing = 0;
+ }
+ }
+
+ }
+
+ private void updateMargins(MarginInfo newMargins) {
+ if (newMargins.equals(activeMarginsInfo)) {
+ return;
+ }
+
+ // Update active margins
+ activeMarginsInfo = newMargins;
+ if (newMargins.hasTop()) {
+ activeMargins.setMarginTop(marginsFromCSS.getMarginTop());
+ } else {
+ activeMargins.setMarginTop(0);
+ }
+ if (newMargins.hasBottom()) {
+ activeMargins.setMarginBottom(marginsFromCSS.getMarginBottom());
+ } else {
+ activeMargins.setMarginBottom(0);
+ }
+ if (newMargins.hasLeft()) {
+ activeMargins.setMarginLeft(marginsFromCSS.getMarginLeft());
+ } else {
+ activeMargins.setMarginLeft(0);
+ }
+ if (newMargins.hasRight()) {
+ activeMargins.setMarginRight(marginsFromCSS.getMarginRight());
+ } else {
+ activeMargins.setMarginRight(0);
+ }
+
+ DOM.setStyleAttribute(root, "marginLeft", activeMargins.getMarginLeft()
+ + "px");
+ DOM.setStyleAttribute(root, "marginRight", activeMargins
+ .getMarginRight()
+ + "px");
+ DOM.setStyleAttribute(root, "marginTop", activeMargins.getMarginTop()
+ + "px");
+ DOM.setStyleAttribute(root, "marginBottom", activeMargins
+ .getMarginBottom()
+ + "px");
+
+ }
+
+ protected boolean measureMarginsAndSpacing(String styleName,
+ String marginTopLeftStyleNames, String marginBottomRightStyleNames,
+ String spacingStyleNames) {
+ if (!isAttached()) {
+ return false;
+ }
+
+ Element measurement = DOM.createDiv();
+ DOM.setStyleAttribute(measurement, "position", "absolute");
+ DOM.setStyleAttribute(measurement, "width", "1px");
+ DOM.setStyleAttribute(measurement, "height", "1px");
+ DOM.setStyleAttribute(measurement, "visibility", "hidden");
+
+ root.appendChild(measurement);
+
+ // Measure top and left margins (actually CSS padding)
+ measurement.setClassName(marginTopLeftStyleNames);
+
+ marginsFromCSS.setMarginTop(measurement.getOffsetHeight() - 1);
+ marginsFromCSS.setMarginLeft(measurement.getOffsetWidth() - 1);
+
+ // Measure bottom and right margins (actually CSS padding)
+ measurement.setClassName(marginBottomRightStyleNames);
+
+ marginsFromCSS.setMarginBottom(measurement.getOffsetHeight() - 1);
+ marginsFromCSS.setMarginRight(measurement.getOffsetWidth() - 1);
+
+ // Measure spacing (actually CSS padding)
+ measurement.setClassName(spacingStyleNames);
+
+ spacingFromCSS.vSpacing = measurement.getOffsetHeight() - 1;
+ spacingFromCSS.hSpacing = measurement.getOffsetWidth() - 1;
+
+ ApplicationConnection.getConsole().log("Margins: " + marginsFromCSS);
+ ApplicationConnection.getConsole().log("Spacing: " + spacingFromCSS);
+
+ root.removeChild(measurement);
+
+ return true;
+ }
+
+ protected ChildComponentContainer getFirstChildComponentContainer() {
+ int size = getChildren().size();
+ if (size < 2) {
+ return null;
+ }
+
+ return (ChildComponentContainer) getChildren().get(0);
+ }
+
+ protected void removeChildrenAfter(int pos) {
+ // Remove all children after position "pos" but leave the clear element
+ // in place
+
+ int toRemove = getChildren().size() - pos - 1;
+ while (toRemove-- > 0) {
+ ChildComponentContainer child = (ChildComponentContainer) getChildren()
+ .get(pos);
+ widgetToComponentContainer.remove(child.getWidget());
+ remove(child);
+ }
+
+ }
+
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/ChildComponentContainer.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/ChildComponentContainer.java
new file mode 100644
index 0000000000..81c41a5949
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/ChildComponentContainer.java
@@ -0,0 +1,613 @@
+package com.itmill.toolkit.terminal.gwt.client.ui.layout;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection;
+import com.itmill.toolkit.terminal.gwt.client.BrowserInfo;
+import com.itmill.toolkit.terminal.gwt.client.ICaption;
+import com.itmill.toolkit.terminal.gwt.client.Paintable;
+import com.itmill.toolkit.terminal.gwt.client.UIDL;
+import com.itmill.toolkit.terminal.gwt.client.Util;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.FloatSize;
+import com.itmill.toolkit.terminal.gwt.client.RenderInformation.Size;
+import com.itmill.toolkit.terminal.gwt.client.ui.AlignmentInfo;
+
+public class ChildComponentContainer extends Panel {
+
+ /**
+ * Size of the container DIV excluding any margins and also excluding the
+ * expansion amount (containerExpansion)
+ */
+ private Size contSize = new Size(0, 0);
+
+ /**
+ * Size of the widget inside the container DIV
+ */
+ private Size widgetSize = new Size(0, 0);
+
+ /**
+ * Padding added to the container when it is larger than the component.
+ */
+ private Size containerExpansion = new Size(0, 0);
+
+ private float expandRatio;
+
+ private int containerMarginLeft = 0;
+ private int containerMarginTop = 0;
+
+ AlignmentInfo alignment = new AlignmentInfo(AlignmentInfo.ALIGNMENT_LEFT,
+ AlignmentInfo.ALIGNMENT_TOP);
+ private int alignmentLeftOffsetForWidget = 0;
+ private int alignmentLeftOffsetForCaption = 0;
+ /**
+ * Top offset for implementing alignment. Top offset is set to the container
+ * DIV as it otherwise would have to be set to either the Caption or the
+ * Widget depending on whether there is a caption and where the caption is
+ * located.
+ */
+ private int alignmentTopOffset = 0;
+
+ // private Margins alignmentOffset = new Margins(0, 0, 0, 0);
+ private ICaption caption = null;
+ private Element containerDIV;
+ private Element widgetDIV;
+ private Widget widget;
+ private FloatSize relativeSize = null;
+
+ public ChildComponentContainer(Widget widget, int orientation) {
+ super();
+
+ containerDIV = DOM.createDiv();
+ setElement(containerDIV);
+
+ DOM.setStyleAttribute(containerDIV, "height", "0px");
+ // DOM.setStyleAttribute(containerDIV, "width", "0px");
+ DOM.setStyleAttribute(containerDIV, "overflow", "hidden");
+
+ widgetDIV = DOM.createDiv();
+ setFloat(widgetDIV, "left");
+
+ containerDIV.appendChild(widgetDIV);
+
+ setOrientation(orientation);
+
+ setWidget(widget);
+
+ }
+
+ private void setWidget(Widget w) {
+ // Validate
+ if (w == widget) {
+ return;
+ }
+
+ // Detach new child.
+ if (w != null) {
+ w.removeFromParent();
+ }
+
+ // Remove old child.
+ if (widget != null) {
+ remove(widget);
+ }
+
+ // Logical attach.
+ widget = w;
+
+ if (w != null) {
+ // Physical attach.
+ DOM.appendChild(widgetDIV, widget.getElement());
+
+ adopt(w);
+ }
+ }
+
+ private static void setFloat(Element e, String floatString) {
+ Util.setFloat(e, floatString);
+ if (BrowserInfo.get().isIE()) {
+ // IE requires display:inline for margin-left to work together
+ // with float:left
+ if (floatString.equals("left")) {
+ DOM.setStyleAttribute(e, "display", "inline");
+ } else {
+ DOM.setStyleAttribute(e, "display", "block");
+ }
+
+ }
+ }
+
+ public void setOrientation(int orientation) {
+ if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
+ setFloat(containerDIV, "left");
+ } else {
+ setFloat(containerDIV, "");
+ }
+ setHeight("0px");
+ // setWidth("0px");
+ contSize.setHeight(0);
+ contSize.setWidth(0);
+ containerMarginLeft = 0;
+ containerMarginTop = 0;
+ DOM.setStyleAttribute(getElement(), "paddingLeft", "0px");
+ DOM.setStyleAttribute(getElement(), "paddingTop", "0px");
+
+ containerExpansion.setHeight(0);
+ containerExpansion.setWidth(0);
+
+ // Clear old alignments
+ clearAlignments();
+
+ }
+
+ public void renderChild(UIDL childUIDL, ApplicationConnection client) {
+ /*
+ * Must remove width specification from container before rendering to
+ * allow components to grow in horizontal direction
+ */
+ DOM.setStyleAttribute(containerDIV, "width", "");
+ ((Paintable) widget).updateFromUIDL(childUIDL, client);
+ }
+
+ public void updateWidgetSize() {
+ int w = widget.getOffsetWidth();
+ int h = widget.getOffsetHeight();
+
+ widgetSize.setHeight(h);
+ widgetSize.setWidth(w);
+ }
+
+ public void setMarginLeft(int marginLeft) {
+ containerMarginLeft = marginLeft;
+ DOM.setStyleAttribute(getElement(), "paddingLeft", marginLeft + "px");
+ }
+
+ public void setMarginTop(int marginTop) {
+ containerMarginTop = marginTop;
+ DOM.setStyleAttribute(getElement(), "paddingTop", marginTop
+ + alignmentTopOffset + "px");
+
+ updateContainerDOMSize();
+ }
+
+ public void updateAlignments(int parentWidth, int parentHeight) {
+ if (parentHeight == -1) {
+ parentHeight = contSize.getHeight();
+ }
+ if (parentWidth == -1) {
+ parentWidth = contSize.getWidth();
+ }
+
+ alignmentTopOffset = calculateVerticalAlignmentTopOffset(parentHeight);
+
+ calculateHorizontalAlignment(parentWidth);
+
+ applyAlignments();
+
+ }
+
+ private void applyAlignments() {
+
+ // Update top margin to take alignment into account
+ setMarginTop(containerMarginTop);
+
+ if (caption != null) {
+ DOM.setStyleAttribute(caption.getElement(), "marginLeft",
+ alignmentLeftOffsetForCaption + "px");
+ }
+ DOM.setStyleAttribute(widgetDIV, "marginLeft",
+ alignmentLeftOffsetForWidget + "px");
+ }
+
+ public int getCaptionWidth() {
+ if (caption == null) {
+ return 0;
+ }
+
+ return caption.getWidth();
+ }
+
+ public int getCaptionHeight() {
+ if (caption == null) {
+ return 0;
+ }
+
+ return caption.getHeight();
+ }
+
+ public int getCaptionWidthAfterComponent() {
+ if (caption == null || !caption.shouldBePlacedAfterComponent()) {
+ return 0;
+ }
+
+ return caption.getWidth();
+ }
+
+ public int getCaptionHeightAboveComponent() {
+ if (caption == null || caption.shouldBePlacedAfterComponent()) {
+ return 0;
+ }
+
+ return caption.getHeight();
+ }
+
+ public int calculateVerticalAlignmentTopOffset(int emptySpace) {
+ if (alignment.isTop()) {
+ return 0;
+ }
+
+ if (caption != null) {
+ if (caption.shouldBePlacedAfterComponent()) {
+ /*
+ * Take into account the rare case that the caption on the right
+ * side of the component AND is higher than the component
+ */
+ emptySpace -= Math.max(widgetSize.getHeight(), caption
+ .getHeight());
+ } else {
+ emptySpace -= widgetSize.getHeight();
+ emptySpace -= caption.getHeight();
+ }
+ } else {
+ /*
+ * There is no caption and thus we do not need to take anything but
+ * the widget into account
+ */
+ emptySpace -= widgetSize.getHeight();
+ }
+
+ int top = 0;
+ if (alignment.isVerticalCenter()) {
+ top = emptySpace / 2;
+ } else if (alignment.isBottom()) {
+ top = emptySpace;
+ }
+
+ if (top < 0) {
+ top = 0;
+ }
+ return top;
+ }
+
+ private void calculateHorizontalAlignment(int emptySpace) {
+ alignmentLeftOffsetForCaption = 0;
+ alignmentLeftOffsetForWidget = 0;
+
+ if (alignment.isLeft()) {
+ return;
+ }
+
+ int captionSpace = emptySpace;
+ int widgetSpace = emptySpace;
+
+ if (caption != null) {
+ // There is a caption
+ if (caption.shouldBePlacedAfterComponent()) {
+ /*
+ * The caption is after component. In this case the caption
+ * needs no alignment.
+ */
+ captionSpace = 0;
+ widgetSpace -= widgetSize.getWidth();
+ widgetSpace -= caption.getWidth();
+ } else {
+ /*
+ * The caption is above the component. Caption and widget needs
+ * separate alignment offsets.
+ */
+ widgetSpace -= widgetSize.getWidth();
+ captionSpace -= caption.getWidth();
+ }
+ } else {
+ /*
+ * There is no caption and thus we do not need to take anything but
+ * the widget into account
+ */
+ captionSpace = 0;
+ widgetSpace -= widgetSize.getWidth();
+ }
+
+ if (alignment.isHorizontalCenter()) {
+ alignmentLeftOffsetForCaption = captionSpace / 2;
+ alignmentLeftOffsetForWidget = widgetSpace / 2;
+ } else if (alignment.isRight()) {
+ alignmentLeftOffsetForCaption = captionSpace;
+ alignmentLeftOffsetForWidget = widgetSpace;
+ }
+
+ if (alignmentLeftOffsetForCaption < 0) {
+ alignmentLeftOffsetForCaption = 0;
+ }
+ if (alignmentLeftOffsetForWidget < 0) {
+ alignmentLeftOffsetForWidget = 0;
+ }
+
+ }
+
+ public void setAlignment(AlignmentInfo alignmentInfo) {
+ alignment = alignmentInfo;
+
+ }
+
+ public Size getWidgetSize() {
+ return widgetSize;
+ }
+
+ public void updateCaption(UIDL uidl, ApplicationConnection client) {
+ if (ICaption.isNeeded(uidl)) {
+ // We need a caption
+
+ ICaption newCaption = caption;
+
+ if (newCaption == null) {
+ newCaption = new ICaption((Paintable) widget, client);
+ }
+
+ boolean positionChanged = newCaption.updateCaption(uidl);
+
+ if (newCaption != caption || positionChanged) {
+ setCaption(newCaption);
+ }
+
+ } else {
+ // Caption is not needed
+ if (caption != null) {
+ remove(caption);
+ }
+
+ }
+
+ }
+
+ private void setCaption(ICaption newCaption) {
+ // Validate
+ // if (newCaption == caption) {
+ // return;
+ // }
+
+ // Detach new child.
+ if (newCaption != null) {
+ newCaption.removeFromParent();
+ }
+
+ // Remove old child.
+ if (caption != null && newCaption != caption) {
+ remove(caption);
+ }
+
+ // Logical attach.
+ caption = newCaption;
+
+ if (caption != null) {
+ // Physical attach.
+ if (caption.shouldBePlacedAfterComponent()) {
+ Util.setFloat(caption.getElement(), "left");
+ containerDIV.appendChild(caption.getElement());
+ } else {
+ Util.setFloat(caption.getElement(), "");
+ containerDIV.insertBefore(caption.getElement(), widgetDIV);
+ }
+
+ adopt(caption);
+ }
+
+ }
+
+ @Override
+ public boolean remove(Widget child) {
+ // Validate
+ if (child != caption && child != widget) {
+ return false;
+ }
+
+ // Orphan
+ orphan(child);
+
+ // Physical && Logical Detach
+ if (child == caption) {
+ containerDIV.removeChild(caption.getElement());
+ caption = null;
+ } else {
+ containerDIV.removeChild(widget.getElement());
+ widget = null;
+ }
+
+ return true;
+ }
+
+ public Iterator<Widget> iterator() {
+ return new ChildComponentContainerIterator<Widget>();
+ }
+
+ public class ChildComponentContainerIterator<T> implements Iterator<Widget> {
+ private int id = 0;
+
+ public boolean hasNext() {
+ return (id < size());
+ }
+
+ public Widget next() {
+ Widget w = get(id);
+ id++;
+ return w;
+ }
+
+ private Widget get(int i) {
+ if (i == 0) {
+ if (widget != null) {
+ return widget;
+ } else if (caption != null) {
+ return caption;
+ } else {
+ throw new NoSuchElementException();
+ }
+ } else if (i == 1) {
+ if (widget != null && caption != null) {
+ return caption;
+ } else {
+ throw new NoSuchElementException();
+ }
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ public void remove() {
+ int toRemove = id - 1;
+ if (toRemove == 0) {
+ if (widget != null) {
+ ChildComponentContainer.this.remove(widget);
+ } else if (caption != null) {
+ ChildComponentContainer.this.remove(caption);
+ } else {
+ throw new IllegalStateException();
+ }
+
+ } else if (toRemove == 1) {
+ if (widget != null && caption != null) {
+ ChildComponentContainer.this.remove(caption);
+ } else {
+ throw new IllegalStateException();
+ }
+ } else {
+ throw new IllegalStateException();
+ }
+
+ id--;
+ }
+ }
+
+ public int size() {
+ if (widget != null) {
+ if (caption != null) {
+ return 2;
+ } else {
+ return 1;
+ }
+ } else {
+ if (caption != null) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public Widget getWidget() {
+ return widget;
+ }
+
+ public boolean isComponentRelativeSized(int orientation) {
+ if (relativeSize == null) {
+ return false;
+ }
+ if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
+ return relativeSize.getWidth() >= 0;
+ } else {
+ return relativeSize.getHeight() >= 0;
+ }
+ }
+
+ public void setRelativeSize(FloatSize relativeSize) {
+ this.relativeSize = relativeSize;
+ }
+
+ public Size getContSize() {
+ return contSize;
+ }
+
+ public void clearAlignments() {
+ alignmentLeftOffsetForCaption = 0;
+ alignmentLeftOffsetForWidget = 0;
+ alignmentTopOffset = 0;
+ applyAlignments();
+
+ }
+
+ public void setExpandRatio(int expandRatio) {
+ this.expandRatio = (expandRatio / 1000.0f);
+ }
+
+ public int expand(int orientation, int spaceForExpansion) {
+ int expansionAmount = (int) ((double) spaceForExpansion * expandRatio);
+
+ if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
+ // HORIZONTAL
+ containerExpansion.setWidth(expansionAmount);
+ } else {
+ // VERTICAL
+ containerExpansion.setHeight(expansionAmount);
+ }
+
+ return expansionAmount;
+ }
+
+ public void expandExtra(int orientation, int extra) {
+ if (orientation == CellBasedLayout.ORIENTATION_HORIZONTAL) {
+ // HORIZONTAL
+ containerExpansion.setWidth(containerExpansion.getWidth() + extra);
+ } else {
+ // VERTICAL
+ containerExpansion
+ .setHeight(containerExpansion.getHeight() + extra);
+ }
+
+ }
+
+ public void setContainerSize(int widgetAndCaptionWidth,
+ int widgetAndCaptionHeight) {
+
+ int containerWidth = widgetAndCaptionWidth;
+ containerWidth += containerExpansion.getWidth();
+
+ int containerHeight = widgetAndCaptionHeight;
+ containerHeight += containerExpansion.getHeight();
+
+ ApplicationConnection.getConsole().log(
+ "Setting container size for " + Util.getSimpleName(widget)
+ + " to " + containerWidth + "," + containerHeight);
+
+ if (containerWidth < 0) {
+ ApplicationConnection.getConsole().error(
+ "containerWidth should never be negative: "
+ + containerWidth);
+ containerWidth = 0;
+ }
+ if (containerHeight < 0) {
+ ApplicationConnection.getConsole().error(
+ "containerHeight should never be negative: "
+ + containerHeight);
+ containerHeight = 0;
+ }
+
+ contSize.setWidth(containerWidth);
+ contSize.setHeight(containerHeight);
+
+ updateContainerDOMSize();
+ }
+
+ public void updateContainerDOMSize() {
+ int width = contSize.getWidth();
+ int height = contSize.getHeight() - alignmentTopOffset;
+ if (width < 0) {
+ width = 0;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+
+ setWidth(width + "px");
+ setHeight(height + "px");
+
+ // Also update caption max width
+ if (caption != null) {
+ caption.setMaxWidth(width);
+ }
+
+ }
+
+}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/Margins.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/Margins.java
new file mode 100644
index 0000000000..06332c93ea
--- /dev/null
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/layout/Margins.java
@@ -0,0 +1,83 @@
+package com.itmill.toolkit.terminal.gwt.client.ui.layout;
+
+public class Margins {
+
+ private int marginTop;
+ private int marginBottom;
+ private int marginLeft;
+ private int marginRight;
+
+ private int horizontal = 0;
+ private int vertical = 0;
+
+ public Margins(int marginTop, int marginBottom, int marginLeft,
+ int marginRight) {
+ super();
+ this.marginTop = marginTop;
+ this.marginBottom = marginBottom;
+ this.marginLeft = marginLeft;
+ this.marginRight = marginRight;
+
+ updateHorizontal();
+ updateVertical();
+ }
+
+ public int getMarginTop() {
+ return marginTop;
+ }
+
+ public int getMarginBottom() {
+ return marginBottom;
+ }
+
+ public int getMarginLeft() {
+ return marginLeft;
+ }
+
+ public int getMarginRight() {
+ return marginRight;
+ }
+
+ public int getHorizontal() {
+ return horizontal;
+ }
+
+ public int getVertical() {
+ return vertical;
+ }
+
+ public void setMarginTop(int marginTop) {
+ this.marginTop = marginTop;
+ updateVertical();
+ }
+
+ public void setMarginBottom(int marginBottom) {
+ this.marginBottom = marginBottom;
+ updateVertical();
+ }
+
+ public void setMarginLeft(int marginLeft) {
+ this.marginLeft = marginLeft;
+ updateHorizontal();
+ }
+
+ public void setMarginRight(int marginRight) {
+ this.marginRight = marginRight;
+ updateHorizontal();
+ }
+
+ private void updateVertical() {
+ vertical = marginTop + marginBottom;
+ }
+
+ private void updateHorizontal() {
+ horizontal = marginLeft + marginRight;
+ }
+
+ @Override
+ public String toString() {
+ return "Margins [marginLeft=" + marginLeft + ",marginTop=" + marginTop
+ + ",marginRight=" + marginRight + ",marginBottom="
+ + marginBottom + "]";
+ }
+}
diff --git a/src/com/itmill/toolkit/ui/ExpandLayout.java b/src/com/itmill/toolkit/ui/ExpandLayout.java
index 2377c1f052..7530d7c241 100644
--- a/src/com/itmill/toolkit/ui/ExpandLayout.java
+++ b/src/com/itmill/toolkit/ui/ExpandLayout.java
@@ -4,11 +4,6 @@
package com.itmill.toolkit.ui;
-import java.util.Iterator;
-
-import com.itmill.toolkit.terminal.PaintException;
-import com.itmill.toolkit.terminal.PaintTarget;
-
/**
* A layout that will give one of it's components as much space as possible,
* while still showing the other components in the layout. The other components
@@ -21,18 +16,21 @@ import com.itmill.toolkit.terminal.PaintTarget;
* fixed size. If the layout fails to show up, check that the parent layout is
* actually giving some space.
*
+ * @deprecated Deprecated in favor of new OrderedLayout
*/
+@Deprecated
public class ExpandLayout extends OrderedLayout {
- private Component expanded;
+ private Component expanded = null;
public ExpandLayout() {
- setSizeFull();
+ this(ORIENTATION_VERTICAL);
}
public ExpandLayout(int orientation) {
- this();
- setOrientation(orientation);
+ super(orientation);
+
+ setSizeFull();
}
/**
@@ -40,90 +38,13 @@ public class ExpandLayout extends OrderedLayout {
* Component which container will be maximized
*/
public void expand(Component c) {
- expanded = c;
- requestRepaint();
- }
-
- public String getTag() {
- return "expandlayout";
- }
-
- public void paintContent(PaintTarget target) throws PaintException {
-
- // Add margin info. Defaults to false.
- target.addAttribute("margins", margins.getBitMask());
-
- // Add spacing attribute (omitted if false)
- if (isSpacingEnabled()) {
- target.addAttribute("spacing", true);
- }
-
- // Adds the attributes: orientation
- // note that the default values (b/vertival) are omitted
- if (getOrientation() == ORIENTATION_HORIZONTAL) {
- target.addAttribute("orientation", "horizontal");
- }
-
- final String[] alignmentsArray = new String[components.size()];
-
- // Adds all items in all the locations
- int index = 0;
- for (final Iterator i = getComponentIterator(); i.hasNext();) {
- final Component c = (Component) i.next();
- if (c != null) {
- target.startTag("cc");
- if (c == expanded) {
- target.addAttribute("expanded", true);
- }
- c.paint(target);
- target.endTag("cc");
- }
- alignmentsArray[index++] = String.valueOf(getComponentAlignment(c));
-
+ if (expanded != null) {
+ setExpandRatio(expanded, 0.0f);
}
- // Add child component alignment info to layout tag
- target.addAttribute("alignments", alignmentsArray);
-
- }
-
- public void addComponent(Component c, int index) {
- if (expanded == null) {
- expanded = c;
- }
- super.addComponent(c, index);
- }
-
- public void addComponent(Component c) {
- if (expanded == null) {
- expanded = c;
- }
- super.addComponent(c);
- }
-
- public void addComponentAsFirst(Component c) {
- if (expanded == null) {
- expanded = c;
- }
- super.addComponentAsFirst(c);
- }
-
- public void removeComponent(Component c) {
- super.removeComponent(c);
- if (c == expanded) {
- if (getComponentIterator().hasNext()) {
- expanded = (Component) getComponentIterator().next();
- } else {
- expanded = null;
- }
- }
- }
-
- public void replaceComponent(Component oldComponent, Component newComponent) {
- super.replaceComponent(oldComponent, newComponent);
- if (oldComponent == expanded) {
- expanded = newComponent;
- }
+ expanded = c;
+ setExpandRatio(expanded, 1.0f);
+ requestRepaint();
}
}
diff --git a/src/com/itmill/toolkit/ui/OldOrderedLayout.java b/src/com/itmill/toolkit/ui/OldOrderedLayout.java
new file mode 100644
index 0000000000..37b1e1b174
--- /dev/null
+++ b/src/com/itmill/toolkit/ui/OldOrderedLayout.java
@@ -0,0 +1,18 @@
+package com.itmill.toolkit.ui;
+
+public class OldOrderedLayout extends OrderedLayout {
+
+ public OldOrderedLayout() {
+ super();
+ }
+
+ public OldOrderedLayout(int orientation) {
+ super(orientation);
+ }
+
+ @Override
+ public String getTag() {
+ return "oldorderedlayout";
+ }
+
+}