|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143 |
- /*
- @VaadinApache2LicenseForJavaFiles@
- */
-
- package com.vaadin.terminal.gwt.client.ui;
-
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Set;
-
- import com.google.gwt.dom.client.DivElement;
- import com.google.gwt.dom.client.Document;
- import com.google.gwt.event.dom.client.DomEvent.Type;
- import com.google.gwt.event.shared.EventHandler;
- import com.google.gwt.event.shared.HandlerRegistration;
- import com.google.gwt.user.client.Element;
- import com.google.gwt.user.client.ui.AbsolutePanel;
- import com.google.gwt.user.client.ui.SimplePanel;
- import com.google.gwt.user.client.ui.Widget;
- import com.vaadin.terminal.gwt.client.ApplicationConnection;
- import com.vaadin.terminal.gwt.client.Container;
- import com.vaadin.terminal.gwt.client.EventId;
- import com.vaadin.terminal.gwt.client.RenderSpace;
- import com.vaadin.terminal.gwt.client.StyleConstants;
- import com.vaadin.terminal.gwt.client.UIDL;
- import com.vaadin.terminal.gwt.client.Util;
- import com.vaadin.terminal.gwt.client.VPaintableMap;
- import com.vaadin.terminal.gwt.client.VPaintableWidget;
- import com.vaadin.terminal.gwt.client.ui.layout.CellBasedLayout;
- import com.vaadin.terminal.gwt.client.ui.layout.ChildComponentContainer;
-
- public class VGridLayout extends SimplePanel implements VPaintableWidget,
- Container {
-
- public static final String CLASSNAME = "v-gridlayout";
-
- private DivElement margin = Document.get().createDivElement();
-
- private final AbsolutePanel canvas = new AbsolutePanel();
-
- private ApplicationConnection client;
-
- protected HashMap<Widget, ChildComponentContainer> widgetToComponentContainer = new HashMap<Widget, ChildComponentContainer>();
-
- private HashMap<Widget, Cell> widgetToCell = new HashMap<Widget, Cell>();
-
- private int spacingPixelsHorizontal;
- private int spacingPixelsVertical;
-
- private int[] columnWidths;
- private int[] rowHeights;
-
- private String height;
-
- private String width;
-
- private int[] colExpandRatioArray;
-
- private int[] rowExpandRatioArray;
-
- private int[] minColumnWidths;
-
- private int[] minRowHeights;
-
- private boolean rendering;
-
- private HashMap<Widget, ChildComponentContainer> nonRenderedWidgets;
-
- private boolean sizeChangedDuringRendering = false;
-
- private LayoutClickEventHandler clickEventHandler = new LayoutClickEventHandler(
- this, EventId.LAYOUT_CLICK) {
-
- @Override
- protected VPaintableWidget getChildComponent(Element element) {
- return getComponent(element);
- }
-
- @Override
- protected <H extends EventHandler> HandlerRegistration registerHandler(
- H handler, Type<H> type) {
- return addDomHandler(handler, type);
- }
- };
-
- public VGridLayout() {
- super();
- getElement().appendChild(margin);
- setStyleName(CLASSNAME);
- setWidget(canvas);
- }
-
- @Override
- protected Element getContainerElement() {
- return margin.cast();
- }
-
- /**
- * Returns the column widths measured in pixels
- *
- * @return
- */
- protected int[] getColumnWidths() {
- return columnWidths;
- }
-
- /**
- * Returns the row heights measured in pixels
- *
- * @return
- */
- protected int[] getRowHeights() {
- return rowHeights;
- }
-
- /**
- * Returns the spacing between the cells horizontally in pixels
- *
- * @return
- */
- protected int getHorizontalSpacing() {
- return spacingPixelsHorizontal;
- }
-
- /**
- * Returns the spacing between the cells vertically in pixels
- *
- * @return
- */
- protected int getVerticalSpacing() {
- return spacingPixelsVertical;
- }
-
- @SuppressWarnings("unchecked")
- public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
- rendering = true;
- this.client = client;
-
- if (client.updateComponent(this, uidl, true)) {
- rendering = false;
- return;
- }
- clickEventHandler.handleEventHandlerRegistration(client);
-
- canvas.setWidth("0px");
-
- handleMargins(uidl);
- detectSpacing(uidl);
-
- int cols = uidl.getIntAttribute("w");
- int rows = uidl.getIntAttribute("h");
-
- columnWidths = new int[cols];
- rowHeights = new int[rows];
-
- if (cells == null) {
- cells = new Cell[cols][rows];
- } else if (cells.length != cols || cells[0].length != rows) {
- Cell[][] newCells = new Cell[cols][rows];
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- if (i < cols && j < rows) {
- newCells[i][j] = cells[i][j];
- }
- }
- }
- cells = newCells;
- }
-
- nonRenderedWidgets = (HashMap<Widget, ChildComponentContainer>) widgetToComponentContainer
- .clone();
-
- final int[] alignments = uidl.getIntArrayAttribute("alignments");
- int alignmentIndex = 0;
-
- LinkedList<Cell> pendingCells = new LinkedList<Cell>();
-
- LinkedList<Cell> relativeHeighted = new LinkedList<Cell>();
-
- for (final Iterator<?> i = uidl.getChildIterator(); i.hasNext();) {
- final UIDL r = (UIDL) i.next();
- if ("gr".equals(r.getTag())) {
- for (final Iterator<?> j = r.getChildIterator(); j.hasNext();) {
- final UIDL c = (UIDL) j.next();
- if ("gc".equals(c.getTag())) {
- Cell cell = getCell(c);
- if (cell.hasContent()) {
- boolean rendered = cell.renderIfNoRelativeWidth();
- cell.alignment = alignments[alignmentIndex++];
- if (!rendered) {
- pendingCells.add(cell);
- }
-
- if (cell.colspan > 1) {
- storeColSpannedCell(cell);
- } else if (rendered) {
- // strore non-colspanned widths to columnWidth
- // array
- if (columnWidths[cell.col] < cell.getWidth()) {
- columnWidths[cell.col] = cell.getWidth();
- }
- }
- if (cell.hasRelativeHeight()) {
- relativeHeighted.add(cell);
- }
- }
- }
- }
- }
- }
-
- colExpandRatioArray = uidl.getIntArrayAttribute("colExpand");
- rowExpandRatioArray = uidl.getIntArrayAttribute("rowExpand");
- distributeColSpanWidths();
-
- minColumnWidths = cloneArray(columnWidths);
- expandColumns();
-
- renderRemainingComponentsWithNoRelativeHeight(pendingCells);
-
- detectRowHeights();
-
- expandRows();
-
- renderRemainingComponents(pendingCells);
-
- for (Cell cell : relativeHeighted) {
- // rendering done above so cell.cc should not be null
- Widget widget2 = cell.cc.getWidget();
- client.handleComponentRelativeSize(widget2);
- cell.cc.updateWidgetSize();
- }
-
- layoutCells();
-
- // clean non rendered components
- for (Widget w : nonRenderedWidgets.keySet()) {
- ChildComponentContainer childComponentContainer = widgetToComponentContainer
- .get(w);
- widgetToCell.remove(w);
- widgetToComponentContainer.remove(w);
- childComponentContainer.removeFromParent();
- VPaintableMap paintableMap = VPaintableMap.get(client);
- paintableMap.unregisterPaintable(paintableMap.getPaintable(w));
- }
- nonRenderedWidgets = null;
-
- rendering = false;
- sizeChangedDuringRendering = false;
-
- }
-
- private static int[] cloneArray(int[] toBeCloned) {
- int[] clone = new int[toBeCloned.length];
- for (int i = 0; i < clone.length; i++) {
- clone[i] = toBeCloned[i] * 1;
- }
- return clone;
- }
-
- private void expandRows() {
- if (!"".equals(height)) {
- int usedSpace = minRowHeights[0];
- for (int i = 1; i < minRowHeights.length; i++) {
- usedSpace += spacingPixelsVertical + minRowHeights[i];
- }
- int availableSpace = getOffsetHeight() - marginTopAndBottom;
- int excessSpace = availableSpace - usedSpace;
- int distributed = 0;
- if (excessSpace > 0) {
- for (int i = 0; i < rowHeights.length; i++) {
- int ew = excessSpace * rowExpandRatioArray[i] / 1000;
- rowHeights[i] = minRowHeights[i] + ew;
- distributed += ew;
- }
- excessSpace -= distributed;
- int c = 0;
- while (excessSpace > 0) {
- rowHeights[c % rowHeights.length]++;
- excessSpace--;
- c++;
- }
- }
- }
- }
-
- @Override
- public void setHeight(String height) {
- super.setHeight(height);
- if (!height.equals(this.height)) {
- this.height = height;
- if (rendering) {
- sizeChangedDuringRendering = true;
- } else {
- expandRows();
- layoutCells();
- for (Widget w : widgetToCell.keySet()) {
- client.handleComponentRelativeSize(w);
- }
- }
- }
- }
-
- @Override
- public void setWidth(String width) {
- super.setWidth(width);
- if (!width.equals(this.width)) {
- this.width = width;
- if (rendering) {
- sizeChangedDuringRendering = true;
- } else {
- int[] oldWidths = cloneArray(columnWidths);
- expandColumns();
- boolean heightChanged = false;
- HashSet<Integer> dirtyRows = null;
- for (int i = 0; i < oldWidths.length; i++) {
- if (columnWidths[i] != oldWidths[i]) {
- Cell[] column = cells[i];
- for (int j = 0; j < column.length; j++) {
- Cell c = column[j];
- if (c != null && c.cc != null
- && c.widthCanAffectHeight()) {
- c.cc.setContainerSize(c.getAvailableWidth(),
- c.getAvailableHeight());
- client.handleComponentRelativeSize(c.cc
- .getWidget());
- c.cc.updateWidgetSize();
- int newHeight = c.getHeight();
- if (columnWidths[i] < oldWidths[i]
- && newHeight > minRowHeights[j]
- && c.rowspan == 1) {
- /*
- * The width of this column was reduced and
- * this affected the height. The height is
- * now greater than the previously
- * calculated minHeight for the row.
- */
- minRowHeights[j] = newHeight;
- if (newHeight > rowHeights[j]) {
- /*
- * The new height is greater than the
- * previously calculated rowHeight -> we
- * need to recalculate heights later on
- */
- rowHeights[j] = newHeight;
- heightChanged = true;
- }
- } else if (newHeight < minRowHeights[j]) {
- /*
- * The new height of the component is less
- * than the previously calculated min row
- * height. The min row height may be
- * affected and must thus be recalculated
- */
- if (dirtyRows == null) {
- dirtyRows = new HashSet<Integer>();
- }
- dirtyRows.add(j);
- }
- }
- }
- }
- }
- if (dirtyRows != null) {
- /* flag indicating that there is a potential row shrinking */
- boolean rowMayShrink = false;
- for (Integer rowIndex : dirtyRows) {
- int oldMinimum = minRowHeights[rowIndex];
- int newMinimum = 0;
- for (int colIndex = 0; colIndex < columnWidths.length; colIndex++) {
- Cell cell = cells[colIndex][rowIndex];
- if (cell != null && !cell.hasRelativeHeight()
- && cell.getHeight() > newMinimum) {
- newMinimum = cell.getHeight();
- }
- }
- if (newMinimum < oldMinimum) {
- minRowHeights[rowIndex] = rowHeights[rowIndex] = newMinimum;
- rowMayShrink = true;
- }
- }
- if (rowMayShrink) {
- distributeRowSpanHeights();
- minRowHeights = cloneArray(rowHeights);
- heightChanged = true;
- }
-
- }
- layoutCells();
- for (Widget w : widgetToCell.keySet()) {
- client.handleComponentRelativeSize(w);
- }
- if (heightChanged && "".equals(height)) {
- Util.notifyParentOfSizeChange(this, false);
- }
- }
- }
- }
-
- private void expandColumns() {
- if (!"".equals(width)) {
- int usedSpace = minColumnWidths[0];
- for (int i = 1; i < minColumnWidths.length; i++) {
- usedSpace += spacingPixelsHorizontal + minColumnWidths[i];
- }
- canvas.setWidth("");
- int availableSpace = canvas.getOffsetWidth();
- int excessSpace = availableSpace - usedSpace;
- int distributed = 0;
- if (excessSpace > 0) {
- for (int i = 0; i < columnWidths.length; i++) {
- int ew = excessSpace * colExpandRatioArray[i] / 1000;
- columnWidths[i] = minColumnWidths[i] + ew;
- distributed += ew;
- }
- excessSpace -= distributed;
- int c = 0;
- while (excessSpace > 0) {
- columnWidths[c % columnWidths.length]++;
- excessSpace--;
- c++;
- }
- }
- }
- }
-
- private void layoutCells() {
- int x = 0;
- int y = 0;
- for (int i = 0; i < cells.length; i++) {
- y = 0;
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null) {
- cell.layout(x, y);
- }
- y += rowHeights[j] + spacingPixelsVertical;
- }
- x += columnWidths[i] + spacingPixelsHorizontal;
- }
-
- if (isUndefinedWidth()) {
- canvas.setWidth((x - spacingPixelsHorizontal) + "px");
- } else {
- // main element defines width
- canvas.setWidth("");
- }
-
- int canvasHeight;
- if (isUndefinedHeight()) {
- canvasHeight = y - spacingPixelsVertical;
- } else {
- canvasHeight = getOffsetHeight() - marginTopAndBottom;
- if (canvasHeight < 0) {
- canvasHeight = 0;
- }
- }
- canvas.setHeight(canvasHeight + "px");
- }
-
- private boolean isUndefinedHeight() {
- return "".equals(height);
- }
-
- private boolean isUndefinedWidth() {
- return "".equals(width);
- }
-
- private void renderRemainingComponents(LinkedList<Cell> pendingCells) {
- for (Cell cell : pendingCells) {
- cell.render();
- }
- }
-
- private void detectRowHeights() {
-
- // collect min rowheight from non-rowspanned cells
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null) {
- /*
- * Setting fixing container width may in some situations
- * affect height. Example: Label with wrapping text without
- * or with relative width.
- */
- if (cell.cc != null && cell.widthCanAffectHeight()) {
- cell.cc.setWidth(cell.getAvailableWidth() + "px");
- cell.cc.updateWidgetSize();
- }
- if (cell.rowspan == 1) {
- if (!cell.hasRelativeHeight()
- && rowHeights[j] < cell.getHeight()) {
- rowHeights[j] = cell.getHeight();
- }
- } else {
- storeRowSpannedCell(cell);
- }
- }
- }
- }
-
- distributeRowSpanHeights();
-
- minRowHeights = cloneArray(rowHeights);
- }
-
- private void storeRowSpannedCell(Cell cell) {
- SpanList l = null;
- for (SpanList list : rowSpans) {
- if (list.span < cell.rowspan) {
- continue;
- } else {
- // insert before this
- l = list;
- break;
- }
- }
- if (l == null) {
- l = new SpanList(cell.rowspan);
- rowSpans.add(l);
- } else if (l.span != cell.rowspan) {
- SpanList newL = new SpanList(cell.rowspan);
- rowSpans.add(rowSpans.indexOf(l), newL);
- l = newL;
- }
- l.cells.add(cell);
- }
-
- private void renderRemainingComponentsWithNoRelativeHeight(
- LinkedList<Cell> pendingCells) {
-
- for (Iterator<Cell> iterator = pendingCells.iterator(); iterator
- .hasNext();) {
- Cell cell = iterator.next();
- if (!cell.hasRelativeHeight()) {
- cell.render();
- iterator.remove();
- }
- }
-
- }
-
- /**
- * Iterates colspanned cells, ensures cols have enough space to accommodate
- * them
- */
- private void distributeColSpanWidths() {
- for (SpanList list : colSpans) {
- for (Cell cell : list.cells) {
- // cells with relative content may return non 0 here if on
- // subsequent renders
- int width = cell.hasRelativeWidth() ? 0 : cell.getWidth();
- distributeSpanSize(columnWidths, cell.col, cell.colspan,
- spacingPixelsHorizontal, width, colExpandRatioArray);
- }
- }
- }
-
- /**
- * Iterates rowspanned cells, ensures rows have enough space to accommodate
- * them
- */
- private void distributeRowSpanHeights() {
- for (SpanList list : rowSpans) {
- for (Cell cell : list.cells) {
- // cells with relative content may return non 0 here if on
- // subsequent renders
- int height = cell.hasRelativeHeight() ? 0 : cell.getHeight();
- distributeSpanSize(rowHeights, cell.row, cell.rowspan,
- spacingPixelsVertical, height, rowExpandRatioArray);
- }
- }
- }
-
- private static void distributeSpanSize(int[] dimensions,
- int spanStartIndex, int spanSize, int spacingSize, int size,
- int[] expansionRatios) {
- int allocated = dimensions[spanStartIndex];
- for (int i = 1; i < spanSize; i++) {
- allocated += spacingSize + dimensions[spanStartIndex + i];
- }
- if (allocated < size) {
- // dimensions needs to be expanded due spanned cell
- int neededExtraSpace = size - allocated;
- int allocatedExtraSpace = 0;
-
- // Divide space according to expansion ratios if any span has a
- // ratio
- int totalExpansion = 0;
- for (int i = 0; i < spanSize; i++) {
- int itemIndex = spanStartIndex + i;
- totalExpansion += expansionRatios[itemIndex];
- }
-
- for (int i = 0; i < spanSize; i++) {
- int itemIndex = spanStartIndex + i;
- int expansion;
- if (totalExpansion == 0) {
- // Divide equally among all cells if there are no
- // expansion ratios
- expansion = neededExtraSpace / spanSize;
- } else {
- expansion = neededExtraSpace * expansionRatios[itemIndex]
- / totalExpansion;
- }
- dimensions[itemIndex] += expansion;
- allocatedExtraSpace += expansion;
- }
-
- // We might still miss a couple of pixels because of
- // rounding errors...
- if (neededExtraSpace > allocatedExtraSpace) {
- for (int i = 0; i < spanSize; i++) {
- // Add one pixel to every cell until we have
- // compensated for any rounding error
- int itemIndex = spanStartIndex + i;
- dimensions[itemIndex] += 1;
- allocatedExtraSpace += 1;
- if (neededExtraSpace == allocatedExtraSpace) {
- break;
- }
- }
- }
- }
- }
-
- private LinkedList<SpanList> colSpans = new LinkedList<SpanList>();
- private LinkedList<SpanList> rowSpans = new LinkedList<SpanList>();
-
- private int marginTopAndBottom;
-
- private class SpanList {
- final int span;
- List<Cell> cells = new LinkedList<Cell>();
-
- public SpanList(int span) {
- this.span = span;
- }
- }
-
- private void storeColSpannedCell(Cell cell) {
- SpanList l = null;
- for (SpanList list : colSpans) {
- if (list.span < cell.colspan) {
- continue;
- } else {
- // insert before this
- l = list;
- break;
- }
- }
- if (l == null) {
- l = new SpanList(cell.colspan);
- colSpans.add(l);
- } else if (l.span != cell.colspan) {
-
- SpanList newL = new SpanList(cell.colspan);
- colSpans.add(colSpans.indexOf(l), newL);
- l = newL;
- }
- l.cells.add(cell);
- }
-
- private void detectSpacing(UIDL uidl) {
- DivElement spacingmeter = Document.get().createDivElement();
- spacingmeter.setClassName(CLASSNAME + "-" + "spacing-"
- + (uidl.getBooleanAttribute("spacing") ? "on" : "off"));
- spacingmeter.getStyle().setProperty("width", "0");
- spacingmeter.getStyle().setProperty("height", "0");
- canvas.getElement().appendChild(spacingmeter);
- spacingPixelsHorizontal = spacingmeter.getOffsetWidth();
- spacingPixelsVertical = spacingmeter.getOffsetHeight();
- canvas.getElement().removeChild(spacingmeter);
- }
-
- private void handleMargins(UIDL uidl) {
- final VMarginInfo margins = new VMarginInfo(
- uidl.getIntAttribute("margins"));
-
- String styles = CLASSNAME + "-margin";
- if (margins.hasTop()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_TOP;
- }
- if (margins.hasRight()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_RIGHT;
- }
- if (margins.hasBottom()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_BOTTOM;
- }
- if (margins.hasLeft()) {
- styles += " " + CLASSNAME + "-" + StyleConstants.MARGIN_LEFT;
- }
- margin.setClassName(styles);
-
- marginTopAndBottom = margin.getOffsetHeight()
- - canvas.getOffsetHeight();
- }
-
- public boolean hasChildComponent(Widget component) {
- return widgetToCell.containsKey(component);
- }
-
- public void replaceChildComponent(Widget oldComponent, Widget newComponent) {
- ChildComponentContainer componentContainer = widgetToComponentContainer
- .remove(oldComponent);
- if (componentContainer == null) {
- return;
- }
-
- componentContainer.setPaintable(VPaintableMap.get(client).getPaintable(
- newComponent));
- widgetToComponentContainer.put(newComponent, componentContainer);
-
- widgetToCell.put(newComponent, widgetToCell.get(oldComponent));
- }
-
- public void updateCaption(VPaintableWidget paintable, UIDL uidl) {
- Widget widget = paintable.getWidgetForPaintable();
- ChildComponentContainer cc = widgetToComponentContainer.get(widget);
- if (cc != null) {
- cc.updateCaption(uidl, client);
- }
- if (!rendering) {
- // ensure rel size details are updated
- widgetToCell.get(widget).updateRelSizeStatus(uidl);
- /*
- * This was a component-only update and the possible size change
- * must be propagated to the layout
- */
- client.captionSizeUpdated(widget);
- }
- }
-
- public boolean requestLayout(final Set<Widget> changedChildren) {
- boolean needsLayout = false;
- boolean reDistributeColSpanWidths = false;
- boolean reDistributeRowSpanHeights = false;
- int offsetHeight = canvas.getOffsetHeight();
- int offsetWidth = canvas.getOffsetWidth();
- if ("".equals(width) || "".equals(height)) {
- needsLayout = true;
- }
- ArrayList<Integer> dirtyColumns = new ArrayList<Integer>();
- ArrayList<Integer> dirtyRows = new ArrayList<Integer>();
- for (Widget widget : changedChildren) {
-
- Cell cell = widgetToCell.get(widget);
- if (!cell.hasRelativeHeight() || !cell.hasRelativeWidth()) {
- // cell sizes will only stay still if only relatively
- // sized components
- // check if changed child affects min col widths
- assert cell.cc != null;
- cell.cc.setWidth("");
- cell.cc.setHeight("");
-
- cell.cc.updateWidgetSize();
-
- /*
- * If this is the result of an caption icon onload event the
- * caption size may have changed
- */
- cell.cc.updateCaptionSize();
-
- int width = cell.getWidth();
- int allocated = columnWidths[cell.col];
- for (int i = 1; i < cell.colspan; i++) {
- allocated += spacingPixelsHorizontal
- + columnWidths[cell.col + i];
- }
- if (allocated < width) {
- needsLayout = true;
- if (cell.colspan == 1) {
- // do simple column width expansion
- columnWidths[cell.col] = minColumnWidths[cell.col] = width;
- } else {
- // mark that col span expansion is needed
- reDistributeColSpanWidths = true;
- }
- } else if (allocated != width) {
- // size is smaller thant allocated, column might
- // shrink
- dirtyColumns.add(cell.col);
- }
-
- int height = cell.getHeight();
-
- allocated = rowHeights[cell.row];
- for (int i = 1; i < cell.rowspan; i++) {
- allocated += spacingPixelsVertical
- + rowHeights[cell.row + i];
- }
- if (allocated < height) {
- needsLayout = true;
- if (cell.rowspan == 1) {
- // do simple row expansion
- rowHeights[cell.row] = minRowHeights[cell.row] = height;
- } else {
- // mark that row span expansion is needed
- reDistributeRowSpanHeights = true;
- }
- } else if (allocated != height) {
- // size is smaller than allocated, row might shrink
- dirtyRows.add(cell.row);
- }
- }
- }
-
- if (dirtyColumns.size() > 0) {
- for (Integer colIndex : dirtyColumns) {
- int colW = 0;
- for (int i = 0; i < rowHeights.length; i++) {
- Cell cell = cells[colIndex][i];
- if (cell != null && cell.getChildUIDL() != null
- && !cell.hasRelativeWidth() && cell.colspan == 1) {
- int width = cell.getWidth();
- if (width > colW) {
- colW = width;
- }
- }
- }
- minColumnWidths[colIndex] = colW;
- }
- needsLayout = true;
- // ensure colspanned columns have enough space
- columnWidths = cloneArray(minColumnWidths);
- distributeColSpanWidths();
- reDistributeColSpanWidths = false;
- }
-
- if (reDistributeColSpanWidths) {
- distributeColSpanWidths();
- }
-
- if (dirtyRows.size() > 0) {
- needsLayout = true;
- for (Integer rowIndex : dirtyRows) {
- // recalculate min row height
- int rowH = minRowHeights[rowIndex] = 0;
- // loop all columns on row rowIndex
- for (int i = 0; i < columnWidths.length; i++) {
- Cell cell = cells[i][rowIndex];
- if (cell != null && cell.getChildUIDL() != null
- && !cell.hasRelativeHeight() && cell.rowspan == 1) {
- int h = cell.getHeight();
- if (h > rowH) {
- rowH = h;
- }
- }
- }
- minRowHeights[rowIndex] = rowH;
- }
- // TODO could check only some row spans
- rowHeights = cloneArray(minRowHeights);
- distributeRowSpanHeights();
- reDistributeRowSpanHeights = false;
- }
-
- if (reDistributeRowSpanHeights) {
- distributeRowSpanHeights();
- }
-
- if (needsLayout) {
- expandColumns();
- expandRows();
- layoutCells();
- // loop all relative sized components and update their size
- for (int i = 0; i < cells.length; i++) {
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null
- && cell.cc != null
- && (cell.hasRelativeHeight() || cell
- .hasRelativeWidth())) {
- client.handleComponentRelativeSize(cell.cc.getWidget());
- }
- }
- }
- }
- if (canvas.getOffsetHeight() != offsetHeight
- || canvas.getOffsetWidth() != offsetWidth) {
- return false;
- } else {
- return true;
- }
- }
-
- public RenderSpace getAllocatedSpace(Widget child) {
- Cell cell = widgetToCell.get(child);
- assert cell != null;
- return cell.getAllocatedSpace();
- }
-
- private Cell[][] cells;
-
- /**
- * Private helper class.
- */
- private class Cell {
- private boolean relHeight = false;
- private boolean relWidth = false;
- private boolean widthCanAffectHeight = false;
-
- public Cell(UIDL c) {
- row = c.getIntAttribute("y");
- col = c.getIntAttribute("x");
- setUidl(c);
- }
-
- public boolean widthCanAffectHeight() {
- return widthCanAffectHeight;
- }
-
- public boolean hasRelativeHeight() {
- return relHeight;
- }
-
- public RenderSpace getAllocatedSpace() {
- return new RenderSpace(getAvailableWidth()
- - cc.getCaptionWidthAfterComponent(), getAvailableHeight()
- - cc.getCaptionHeightAboveComponent());
- }
-
- public boolean hasContent() {
- return childUidl != null;
- }
-
- /**
- * @return total of spanned cols
- */
- private int getAvailableWidth() {
- int width = columnWidths[col];
- for (int i = 1; i < colspan; i++) {
- width += spacingPixelsHorizontal + columnWidths[col + i];
- }
- return width;
- }
-
- /**
- * @return total of spanned rows
- */
- private int getAvailableHeight() {
- int height = rowHeights[row];
- for (int i = 1; i < rowspan; i++) {
- height += spacingPixelsVertical + rowHeights[row + i];
- }
- return height;
- }
-
- public void layout(int x, int y) {
- if (cc != null && cc.isAttached()) {
- canvas.setWidgetPosition(cc, x, y);
- cc.setContainerSize(getAvailableWidth(), getAvailableHeight());
- cc.setAlignment(new AlignmentInfo(alignment));
- cc.updateAlignments(getAvailableWidth(), getAvailableHeight());
- }
- }
-
- public int getWidth() {
- if (cc != null) {
- int w = cc.getWidgetSize().getWidth()
- + cc.getCaptionWidthAfterComponent();
- return w;
- } else {
- return 0;
- }
- }
-
- public int getHeight() {
- if (cc != null) {
- return cc.getWidgetSize().getHeight()
- + cc.getCaptionHeightAboveComponent();
- } else {
- return 0;
- }
- }
-
- public boolean renderIfNoRelativeWidth() {
- if (childUidl == null) {
- return false;
- }
- if (!hasRelativeWidth()) {
- render();
- return true;
- } else {
- return false;
- }
- }
-
- protected boolean hasRelativeWidth() {
- return relWidth;
- }
-
- protected void render() {
- assert childUidl != null;
-
- VPaintableWidget paintable = client.getPaintable(childUidl);
- Widget w = paintable.getWidgetForPaintable();
- assert paintable != null;
- if (cc == null || cc.getWidget() != w) {
- if (widgetToComponentContainer.containsKey(w)) {
- // Component moving from one place to another
- cc = widgetToComponentContainer.get(w);
- cc.setWidth("");
- cc.setHeight("");
- /*
- * Widget might not be set if moving from another component
- * and this layout has been hidden when moving out, see
- * #5372
- */
- cc.setPaintable(paintable);
- } else {
- // A new component
- cc = new ChildComponentContainer(paintable,
- CellBasedLayout.ORIENTATION_VERTICAL);
- widgetToComponentContainer.put(w, cc);
- cc.setWidth("");
- canvas.add(cc, 0, 0);
- }
- widgetToCell.put(w, this);
- }
- cc.renderChild(childUidl, client, -1);
- if (sizeChangedDuringRendering && Util.isCached(childUidl)) {
- client.handleComponentRelativeSize(cc.getWidget());
- }
- cc.updateWidgetSize();
- nonRenderedWidgets.remove(w);
- }
-
- public UIDL getChildUIDL() {
- return childUidl;
- }
-
- final int row;
- final int col;
- int colspan = 1;
- int rowspan = 1;
- UIDL childUidl;
- int alignment;
- // may be null after setUidl() if content has vanished or changed, set
- // in render()
- ChildComponentContainer cc;
-
- public void setUidl(UIDL c) {
- // Set cell width
- colspan = c.hasAttribute("w") ? c.getIntAttribute("w") : 1;
- // Set cell height
- rowspan = c.hasAttribute("h") ? c.getIntAttribute("h") : 1;
- // ensure we will lose reference to old cells, now overlapped by
- // this cell
- for (int i = 0; i < colspan; i++) {
- for (int j = 0; j < rowspan; j++) {
- if (i > 0 || j > 0) {
- cells[col + i][row + j] = null;
- }
- }
- }
-
- c = c.getChildUIDL(0); // we are interested about childUidl
- if (childUidl != null) {
- if (c == null) {
- // content has vanished, old content will be removed from
- // canvas later during the render phase
- cc = null;
- } else if (cc != null
- && cc.getWidget() != client.getPaintable(c)
- .getWidgetForPaintable()) {
- // content has changed
- cc = null;
- VPaintableWidget paintable = client.getPaintable(c);
- Widget w = paintable.getWidgetForPaintable();
- if (widgetToComponentContainer.containsKey(w)) {
- // cc exist for this component (moved) use that for this
- // cell
- cc = widgetToComponentContainer.get(w);
- cc.setWidth("");
- cc.setHeight("");
- widgetToCell.put(w, this);
- }
- }
- }
- childUidl = c;
- updateRelSizeStatus(c);
- }
-
- protected void updateRelSizeStatus(UIDL uidl) {
- if (uidl != null && !uidl.getBooleanAttribute("cached")) {
- if (uidl.hasAttribute("height")
- && uidl.getStringAttribute("height").contains("%")) {
- relHeight = true;
- } else {
- relHeight = false;
- }
- if (uidl.hasAttribute("width")) {
- widthCanAffectHeight = relWidth = uidl.getStringAttribute(
- "width").contains("%");
- if (uidl.hasAttribute("height")) {
- widthCanAffectHeight = false;
- }
- } else {
- widthCanAffectHeight = !uidl.hasAttribute("height");
- relWidth = false;
- }
- }
- }
- }
-
- private Cell getCell(UIDL c) {
- int row = c.getIntAttribute("y");
- int col = c.getIntAttribute("x");
- Cell cell = cells[col][row];
- if (cell == null) {
- cell = new Cell(c);
- cells[col][row] = cell;
- } else {
- cell.setUidl(c);
- }
- return cell;
- }
-
- /**
- * Returns the deepest nested child component which contains "element". The
- * child component is also returned if "element" is part of its caption.
- *
- * @param element
- * An element that is a nested sub element of the root element in
- * this layout
- * @return The Paintable which the element is a part of. Null if the element
- * belongs to the layout and not to a child.
- */
- private VPaintableWidget getComponent(Element element) {
- return Util.getPaintableForElement(client, this, element);
- }
-
- public Widget getWidgetForPaintable() {
- return this;
- }
-
- }
|