123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215 |
- /*
- @ITMillApache2LicenseForJavaFiles@
- */
-
- package com.itmill.toolkit.terminal.gwt.client.ui;
-
- import java.util.HashMap;
- import java.util.Iterator;
- 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.ICaption;
- import com.itmill.toolkit.terminal.gwt.client.Container;
- import com.itmill.toolkit.terminal.gwt.client.ContainerResizedListener;
- import com.itmill.toolkit.terminal.gwt.client.Paintable;
- import com.itmill.toolkit.terminal.gwt.client.UIDL;
- import com.itmill.toolkit.terminal.gwt.client.Util;
-
- /**
- * 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.
- *
- * @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 childWidgetWrappers = new Vector();
-
- /** 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;
-
- /**
- * 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;
-
- // Constuct base DOM-structure and clean any already attached
- // widgetwrappers from DOM.
- if (tableMode) {
- final String structure = "<table cellspacing=\"0\" cellpadding=\"0\"><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 = (WidgetWrapper) 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) {
-
- 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) {
- Util.runDescendentsLayout(this);
- childLayoutsHaveChanged = 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) {
-
- width = newWidth == null || "".equals(newWidth) ? null : newWidth;
-
- // As we use divs at root - for them using 100% width should be
- // calculated with ""
- super.setWidth("");
-
- // 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) {
-
- // Calculate the space for fixed contents minus marginals
- if (tableMode) {
-
- // If we know explicitly set pixel-size, use that
- if (height != null && height.endsWith("px")) {
- try {
- childHeightTotal = Integer.parseInt(height.substring(0,
- height.length() - 2));
-
- // For negative sizes, use measurements
- if (childHeightTotal < 0) {
- childHeightTotal = rootOffsetMeasure("offsetHeight");
- }
- } catch (NumberFormatException e) {
-
- // In case of invalid number, try to measure the size;
- childHeightTotal = rootOffsetMeasure("offsetHeight");
- }
- }
- // If not, try to measure the size
- else {
- childHeightTotal = rootOffsetMeasure("offsetHeight");
- }
-
- } else {
- childHeightTotal = DOM.getElementPropertyInt(getElement(),
- "offsetHeight");
- }
-
- 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) {
-
- // Calculate the space for fixed contents minus marginals
- // If we know explicitly set pixel-size, use that
- if (width != null && width.endsWith("px")) {
- try {
- childWidthTotal = Integer.parseInt(width.substring(0, width
- .length() - 2));
-
- // For negative sizes, use measurements
- if (childWidthTotal < 0) {
- childWidthTotal = rootOffsetMeasure("offsetWidth");
- }
-
- } catch (NumberFormatException e) {
-
- // In case of invalid number, try to measure the size;
- childWidthTotal = rootOffsetMeasure("offsetWidth");
- }
- }
- // If not, try to measure the size
- else {
- childWidthTotal = rootOffsetMeasure("offsetWidth");
- }
-
- 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);
- }
- }
-
- /**
- * Measure how much space the root element could get.
- *
- * This measures the space allocated by the parent for the root element
- * without letting root element to affect the calculation.
- *
- * @param offset
- * offsetWidth or offsetHeight
- */
- private int rootOffsetMeasure(String offset) {
- // TODO This method must be optimized!
- Element measure = DOM.createDiv();
- DOM.setStyleAttribute(measure, "height", "100%");
- Element parent = DOM.getParent(getElement());
- DOM.insertBefore(parent, measure, getElement());
- DOM.removeChild(parent, getElement());
- int size = DOM.getElementPropertyInt(measure, offset);
- DOM.insertBefore(parent, getElement(), measure);
- DOM.removeChild(parent, measure);
- // In case the no space would be given for this element
- // without pushing, use the current side of the root
- return size;
- }
-
- /** 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;
-
- /**
- * Last set pixel height for the wrapper. -1 if vertical clipping is not
- * used.
- */
- int lastForcedPixelHeight = -1;
-
- /**
- * Last set pidel width for the wrapper. -1 if horizontal clipping is
- * not used.
- */
- int lastForcedPixelWidth = -1;
-
- /** Widget Wrapper root element */
- Element wrapperElement;
-
- /** 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");
- DOM.setStyleAttribute(e, "height",
- pixelHeight < 0 ? (e == clipperDiv ? "100%" : "")
- : pixelHeight + "px");
-
- // 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 classe = DOM.getElementAttribute(e, "class");
- while (DOM.getChildCount(e) > 0) {
- final Element c = DOM.getFirstChild(e);
- DOM.removeChild(e, c);
- DOM.appendChild(clipperDiv, c);
- }
- if (classe != null && classe.length() > 0) {
- DOM.removeElementAttribute(e, "class");
- DOM.setElementAttribute(clipperDiv, "class", classe);
- }
- DOM.appendChild(e, clipperDiv);
- }
-
- /** Undo createClipperDiv() */
- private void removeClipperDiv() {
- final Element e = getElementWrappingClipperDiv();
- String classe = DOM.getElementAttribute(clipperDiv, "class");
- 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 (classe != null && classe.length() > 0) {
- DOM.setElementAttribute(e, "class", classe);
- }
- }
-
- /**
- * 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();
- } 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();
-
- // 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);
-
- 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);
- }
-
- } else
-
- // Caption exists. Move it to correct position if needed
- if (after == (DOM.getChildIndex(captionWrapper, widgetElement) > DOM
- .getChildIndex(captionWrapper, captionElement))) {
- 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");
- }
- }
-
- }
-
- // Caption is not needed
- else {
-
- // 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");
- caption.removeStyleName("i-orderedlayout-w-c");
- }
- }
- }
-
- /**
- * Set alignments for this wrapper.
- */
- void setAlignment(String verticalAlignment, String 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
- final String t = "<table cellpadding='0' cellspacing='0' width='100%' height='100%'><tbody><tr><td>"
- + "<table cellpadding='0' cellspacing='0' ><tbody><tr><td align='left'>"
- + "</td></tr></tbody></table></td></tr></tbody></table>";
- DOM.setInnerHTML(getElementWrappingWidgetAndCaption(), t);
- 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;
- }
- }
- }
-
- /** Set class for spacing */
- void setSpacingAndMargins(boolean first, boolean last) {
-
- final Element e = getElementWrappingWidgetAndCaption();
-
- if (orientationMode == ORIENTATION_HORIZONTAL) {
- DOM.setStyleAttribute(e, "paddingLeft", first ? (margins
- .hasLeft() ? marginLeft + "px" : "0")
- : (hasComponentSpacing ? hSpacing + "px" : "0"));
- DOM.setStyleAttribute(e, "paddingRight", last ? (margins
- .hasRight() ? marginRight + "px" : "0") : "");
- DOM.setStyleAttribute(e, "paddingTop",
- margins.hasTop() ? marginTop + "px" : "");
- DOM.setStyleAttribute(e, "paddingBottom",
- margins.hasBottom() ? marginBottom + "px" : "");
- } else {
- DOM.setStyleAttribute(e, "paddingLeft",
- margins.hasLeft() ? marginLeft + "px" : "0");
- DOM.setStyleAttribute(e, "paddingRight",
- margins.hasRight() ? marginRight + "px" : "0");
- DOM.setStyleAttribute(e, "paddingTop", first ? (margins
- .hasTop() ? marginTop + "px" : "")
- : (hasComponentSpacing ? vSpacing + "px" : "0"));
- DOM.setStyleAttribute(e, "paddingBottom", last
- && margins.hasBottom() ? marginBottom + "px" : "");
- }
- }
- }
-
- /* 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 = (WidgetWrapper) 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 = (WidgetWrapper) 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);
-
- 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) {
- ((WidgetWrapper) childWidgetWrappers.get(index)).updateCaption(
- uidl, component);
- }
- }
-
- /* documented at super */
- public Iterator iterator() {
- return childWidgets.iterator();
- }
-
- /* documented at super */
- public void iLayout() {
- updateChildSizes();
- Util.runDescendentsLayout(this);
- childLayoutsHaveChanged = false;
- }
- }
|