123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649 |
- /*
- @VaadinApache2LicenseForJavaFiles@
- */
-
- package com.vaadin.terminal.gwt.client.ui;
-
- import java.util.HashMap;
- import java.util.LinkedList;
- import java.util.List;
-
- import com.google.gwt.dom.client.DivElement;
- import com.google.gwt.dom.client.Document;
- import com.google.gwt.dom.client.Style;
- import com.google.gwt.dom.client.Style.Position;
- import com.google.gwt.dom.client.Style.Unit;
- 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.vaadin.terminal.gwt.client.ApplicationConnection;
- import com.vaadin.terminal.gwt.client.LayoutManager;
- import com.vaadin.terminal.gwt.client.UIDL;
- import com.vaadin.terminal.gwt.client.Util;
- import com.vaadin.terminal.gwt.client.VCaption;
- import com.vaadin.terminal.gwt.client.ConnectorMap;
- import com.vaadin.terminal.gwt.client.ComponentConnector;
- import com.vaadin.terminal.gwt.client.ui.layout.VLayoutSlot;
- import com.vaadin.terminal.gwt.client.ui.layout.VPaintableLayoutSlot;
-
- public class VGridLayout extends ComplexPanel {
-
- public static final String CLASSNAME = "v-gridlayout";
-
- ApplicationConnection client;
-
- HashMap<Widget, Cell> widgetToCell = new HashMap<Widget, Cell>();
-
- int[] columnWidths;
- int[] rowHeights;
-
- int[] colExpandRatioArray;
-
- int[] rowExpandRatioArray;
-
- int[] minColumnWidths;
-
- private int[] minRowHeights;
-
- DivElement spacingMeasureElement;
-
- public VGridLayout() {
- super();
- setElement(Document.get().createDivElement());
-
- spacingMeasureElement = Document.get().createDivElement();
- Style spacingStyle = spacingMeasureElement.getStyle();
- spacingStyle.setPosition(Position.ABSOLUTE);
- getElement().appendChild(spacingMeasureElement);
-
- setStyleName(CLASSNAME);
- }
-
- private ComponentConnector getPaintable() {
- return ConnectorMap.get(client).getConnector(this);
- }
-
- /**
- * 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 LayoutManager.get(client).getOuterWidth(spacingMeasureElement);
- }
-
- /**
- * Returns the spacing between the cells vertically in pixels
- *
- * @return
- */
- protected int getVerticalSpacing() {
- return LayoutManager.get(client).getOuterHeight(spacingMeasureElement);
- }
-
- 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;
- }
-
- void expandRows() {
- if (!isUndefinedHeight()) {
- int usedSpace = minRowHeights[0];
- int verticalSpacing = getVerticalSpacing();
- for (int i = 1; i < minRowHeights.length; i++) {
- usedSpace += verticalSpacing + minRowHeights[i];
- }
- int availableSpace = LayoutManager.get(client).getInnerHeight(
- getElement());
- 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++;
- }
- }
- }
- }
-
- void updateHeight() {
- // Detect minimum heights & calculate spans
- detectRowHeights();
-
- // Expand
- expandRows();
-
- // Position
- layoutCellsVertically();
- }
-
- void updateWidth() {
- // Detect widths & calculate spans
- detectColWidths();
- // Expand
- expandColumns();
- // Position
- layoutCellsHorizontally();
-
- }
-
- void expandColumns() {
- if (!isUndefinedWidth()) {
- int usedSpace = minColumnWidths[0];
- int horizontalSpacing = getHorizontalSpacing();
- for (int i = 1; i < minColumnWidths.length; i++) {
- usedSpace += horizontalSpacing + minColumnWidths[i];
- }
-
- int availableSpace = LayoutManager.get(client).getInnerWidth(
- getElement());
- 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++;
- }
- }
- }
- }
-
- void layoutCellsVertically() {
- int verticalSpacing = getVerticalSpacing();
- LayoutManager layoutManager = LayoutManager.get(client);
- Element element = getElement();
- int paddingTop = layoutManager.getPaddingTop(element);
- int y = paddingTop;
-
- for (int i = 0; i < cells.length; i++) {
- y = paddingTop;
- for (int j = 0; j < cells[i].length; j++) {
- Cell cell = cells[i][j];
- if (cell != null) {
- cell.layoutVertically(y);
- }
- y += rowHeights[j] + verticalSpacing;
- }
- }
-
- if (isUndefinedHeight()) {
- int outerHeight = y - verticalSpacing
- + layoutManager.getPaddingBottom(element)
- + layoutManager.getBorderHeight(element);
- element.getStyle().setHeight(outerHeight, Unit.PX);
- }
- }
-
- void layoutCellsHorizontally() {
- LayoutManager layoutManager = LayoutManager.get(client);
- Element element = getElement();
- int x = layoutManager.getPaddingLeft(element);
- int horizontalSpacing = getHorizontalSpacing();
- 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.layoutHorizontally(x);
- }
- }
- x += columnWidths[i] + horizontalSpacing;
- }
-
- if (isUndefinedWidth()) {
- int outerWidth = x - horizontalSpacing
- + layoutManager.getPaddingRight(element)
- + layoutManager.getBorderWidth(element);
- element.getStyle().setWidth(outerWidth, Unit.PX);
- }
- }
-
- private boolean isUndefinedHeight() {
- return getPaintable().isUndefinedHeight();
- }
-
- private boolean isUndefinedWidth() {
- return getPaintable().isUndefinedWidth();
- }
-
- private void detectRowHeights() {
- for (int i = 0; i < rowHeights.length; i++) {
- rowHeights[i] = 0;
- }
-
- // 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) {
- if (cell.rowspan == 1) {
- if (!cell.hasRelativeHeight()
- && rowHeights[j] < cell.getHeight()) {
- rowHeights[j] = cell.getHeight();
- }
- } else {
- storeRowSpannedCell(cell);
- }
- }
- }
- }
-
- distributeRowSpanHeights();
-
- minRowHeights = cloneArray(rowHeights);
- }
-
- private void detectColWidths() {
- // collect min colwidths from non-colspanned cells
- for (int i = 0; i < columnWidths.length; i++) {
- columnWidths[i] = 0;
- }
-
- 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) {
- if (cell.colspan == 1) {
- if (!cell.hasRelativeWidth()
- && columnWidths[i] < cell.getWidth()) {
- columnWidths[i] = cell.getWidth();
- }
- } else {
- storeColSpannedCell(cell);
- }
- }
- }
- }
-
- distributeColSpanWidths();
-
- minColumnWidths = cloneArray(columnWidths);
- }
-
- 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);
- }
-
- /**
- * Iterates colspanned cells, ensures cols have enough space to accommodate
- * them
- */
- 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,
- getHorizontalSpacing(), 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,
- getVerticalSpacing(), 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 class SpanList {
- final int span;
- List<Cell> cells = new LinkedList<Cell>();
-
- public SpanList(int span) {
- this.span = span;
- }
- }
-
- 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);
- }
-
- Cell[][] cells;
-
- /**
- * Private helper class.
- */
- class Cell {
- public Cell(UIDL c) {
- row = c.getIntAttribute("y");
- col = c.getIntAttribute("x");
- updateFromUidl(c);
- }
-
- public boolean hasContent() {
- return hasContent;
- }
-
- public boolean hasRelativeHeight() {
- if (slot != null) {
- return slot.getPaintable().isRelativeHeight();
- } else {
- return true;
- }
- }
-
- /**
- * @return total of spanned cols
- */
- private int getAvailableWidth() {
- int width = columnWidths[col];
- for (int i = 1; i < colspan; i++) {
- width += getHorizontalSpacing() + 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 += getVerticalSpacing() + rowHeights[row + i];
- }
- return height;
- }
-
- public void layoutHorizontally(int x) {
- if (slot != null) {
- slot.positionHorizontally(x, getAvailableWidth());
- }
- }
-
- public void layoutVertically(int y) {
- if (slot != null) {
- slot.positionVertically(y, getAvailableHeight());
- }
- }
-
- public int getWidth() {
- if (slot != null) {
- return slot.getUsedWidth();
- } else {
- return 0;
- }
- }
-
- public int getHeight() {
- if (slot != null) {
- return slot.getUsedHeight();
- } else {
- return 0;
- }
- }
-
- protected boolean hasRelativeWidth() {
- if (slot != null) {
- return slot.getPaintable().isRelativeWidth();
- } else {
- return true;
- }
- }
-
- final int row;
- final int col;
- int colspan = 1;
- int rowspan = 1;
-
- private boolean hasContent;
-
- private AlignmentInfo alignment;
-
- VPaintableLayoutSlot slot;
-
- public void updateFromUidl(UIDL cellUidl) {
- // Set cell width
- colspan = cellUidl.hasAttribute("w") ? cellUidl
- .getIntAttribute("w") : 1;
- // Set cell height
- rowspan = cellUidl.hasAttribute("h") ? cellUidl
- .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;
- }
- }
- }
-
- UIDL childUidl = cellUidl.getChildUIDL(0); // we are interested
- // about childUidl
- hasContent = childUidl != null;
- if (hasContent) {
- ComponentConnector paintable = client.getPaintable(childUidl);
-
- if (slot == null || slot.getPaintable() != paintable) {
- slot = new VPaintableLayoutSlot(CLASSNAME, paintable);
- Element slotWrapper = slot.getWrapperElement();
- getElement().appendChild(slotWrapper);
-
- Widget widget = paintable.getWidget();
- insert(widget, slotWrapper, getWidgetCount(), false);
- Cell oldCell = widgetToCell.put(widget, this);
- if (oldCell != null) {
- oldCell.slot.getWrapperElement().removeFromParent();
- oldCell.slot = null;
- }
- }
-
- paintable.updateFromUIDL(childUidl, client);
- }
- }
-
- public void setAlignment(AlignmentInfo alignmentInfo) {
- slot.setAlignment(alignmentInfo);
- }
- }
-
- 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.updateFromUidl(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.
- */
- ComponentConnector getComponent(Element element) {
- return Util.getPaintableForElement(client, this, element);
- }
-
- void setCaption(Widget widget, VCaption caption) {
- VLayoutSlot slot = widgetToCell.get(widget).slot;
-
- if (caption != null) {
- // Logical attach.
- getChildren().add(caption);
- }
-
- // Physical attach if not null, also removes old caption
- slot.setCaption(caption);
-
- if (caption != null) {
- // Adopt.
- adopt(caption);
- }
- }
-
- private void togglePrefixedStyleName(String name, boolean enabled) {
- if (enabled) {
- addStyleDependentName(name);
- } else {
- removeStyleDependentName(name);
- }
- }
-
- void updateMarginStyleNames(VMarginInfo marginInfo) {
- togglePrefixedStyleName("margin-top", marginInfo.hasTop());
- togglePrefixedStyleName("margin-right", marginInfo.hasRight());
- togglePrefixedStyleName("margin-bottom", marginInfo.hasBottom());
- togglePrefixedStyleName("margin-left", marginInfo.hasLeft());
- }
-
- void updateSpacingStyleName(boolean spacingEnabled) {
- String styleName = getStylePrimaryName();
- if (spacingEnabled) {
- spacingMeasureElement.addClassName(styleName + "-spacing-on");
- spacingMeasureElement.removeClassName(styleName + "-spacing-off");
- } else {
- spacingMeasureElement.removeClassName(styleName + "-spacing-on");
- spacingMeasureElement.addClassName(styleName + "-spacing-off");
- }
- }
-
- }
|