Package com.google.gdt.eclipse.designer.gwtext.model.layout.table

Source Code of com.google.gdt.eclipse.designer.gwtext.model.layout.table.TableLayoutInfo$WidgetVisitor

/*******************************************************************************
* Copyright 2011 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.google.gdt.eclipse.designer.gwtext.model.layout.table;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gdt.eclipse.designer.gwtext.Activator;
import com.google.gdt.eclipse.designer.gwtext.model.layout.LayoutInfo;
import com.google.gdt.eclipse.designer.gwtext.model.layout.assistant.TableLayoutAssistant;
import com.google.gdt.eclipse.designer.gwtext.model.widgets.ContainerInfo;
import com.google.gdt.eclipse.designer.model.widgets.WidgetInfo;

import org.eclipse.wb.core.gef.policy.layout.grid.IGridInfo;
import org.eclipse.wb.core.model.IAbstractComponentInfo;
import org.eclipse.wb.core.model.ObjectInfo;
import org.eclipse.wb.core.model.association.AssociationObjects;
import org.eclipse.wb.core.model.broadcast.ObjectInfoChildGraphical;
import org.eclipse.wb.core.model.broadcast.ObjectInfoChildTree;
import org.eclipse.wb.draw2d.geometry.Dimension;
import org.eclipse.wb.draw2d.geometry.Insets;
import org.eclipse.wb.draw2d.geometry.Interval;
import org.eclipse.wb.draw2d.geometry.Point;
import org.eclipse.wb.draw2d.geometry.Rectangle;
import org.eclipse.wb.internal.core.model.JavaInfoUtils;
import org.eclipse.wb.internal.core.model.creation.ConstructorCreationSupport;
import org.eclipse.wb.internal.core.model.creation.CreationSupport;
import org.eclipse.wb.internal.core.model.description.ComponentDescription;
import org.eclipse.wb.internal.core.model.generation.statement.PureFlatStatementGenerator;
import org.eclipse.wb.internal.core.model.variable.EmptyInvocationVariableSupport;
import org.eclipse.wb.internal.core.model.variable.EmptyVariableSupport;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.check.Assert;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.execution.RunnableEx;
import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
import org.eclipse.wb.internal.core.utils.state.GlobalState;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.Image;

import java.util.List;
import java.util.Set;

/**
* Model for <code>com.gwtext.client.widgets.layout.TableLayout</code>.
*
* @author scheglov_ke
* @coverage GWTExt.model.layout
*/
public final class TableLayoutInfo extends LayoutInfo {
  ////////////////////////////////////////////////////////////////////////////
  //
  // Constructor
  //
  ////////////////////////////////////////////////////////////////////////////
  public TableLayoutInfo(AstEditor editor,
      ComponentDescription description,
      CreationSupport creationSupport) throws Exception {
    super(editor, description, creationSupport);
    dontShowFillers();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Initialize
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  protected void initializeLayoutAssistant() {
    new TableLayoutAssistant(this);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Broadcasts
  //
  ////////////////////////////////////////////////////////////////////////////
  private void dontShowFillers() {
    addBroadcastListener(new ObjectInfoChildTree() {
      public void invoke(ObjectInfo object, boolean[] visible) throws Exception {
        handlePresentationChild(object, visible);
      }
    });
    addBroadcastListener(new ObjectInfoChildGraphical() {
      public void invoke(ObjectInfo object, boolean[] visible) throws Exception {
        handlePresentationChild(object, visible);
      }
    });
  }

  private void handlePresentationChild(ObjectInfo object, boolean[] visible) {
    if (object instanceof WidgetInfo && object.getParent() == getContainer()) {
      visible[0] &= !isFiller((WidgetInfo) object);
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Components/constraints
  //
  ////////////////////////////////////////////////////////////////////////////
  private boolean m_replaceWithFillers = true;
  private final boolean m_removeEmptyColumnsRows = true;
  private TableLayoutDataInfo m_removingTableData;

  @Override
  protected void onWidgetRemoveBefore(WidgetInfo widget) throws Exception {
    // remember TableLayoutData_Info for using later in "remove after"
    m_removingTableData = getTableData(widget);
    // continue
    super.onWidgetRemoveBefore(widget);
  }

  @Override
  protected void onWidgetRemoveAfter(WidgetInfo widget) throws Exception {
    // replace widget with fillers
    if (m_replaceWithFillers && !isFiller(widget)) {
      // replace with fillers
      {
        TableLayoutDataInfo tableData = m_removingTableData;
        for (int x = tableData.x; x < tableData.x + tableData.width; x++) {
          for (int y = tableData.y; y < tableData.y + tableData.height; y++) {
            addFiller(x, y);
          }
        }
      }
      // delete empty columns/rows
      if (m_removeEmptyColumnsRows) {
        deleteEmptyColumnsRows(m_removingTableData);
      }
      m_removingTableData = null;
    }
    // continue
    super.onWidgetRemoveAfter(widget);
  }

  @Override
  protected Object getDefaultVirtualDataObject(WidgetInfo widget) throws Exception {
    ClassLoader classLoader = JavaInfoUtils.getClassLoader(this);
    return classLoader.loadClass("com.gwtext.client.widgets.layout.TableLayoutData").newInstance();
  }

  /**
   * @return {@link TableLayoutDataInfo} association with given {@link WidgetInfo}.
   */
  public static TableLayoutDataInfo getTableData(WidgetInfo widget) {
    return (TableLayoutDataInfo) getLayoutData(widget);
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Events
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  protected void onDelete() throws Exception {
    // delete filler's
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      if (isFiller(widget)) {
        widget.delete();
      }
    }
    // delete other
    super.onDelete();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Refresh
  //
  ////////////////////////////////////////////////////////////////////////////
  @Override
  public void refresh_dispose() throws Exception {
    m_gridInfo = null;
    super.refresh_dispose();
  }

  @Override
  protected void refresh_afterCreate() throws Exception {
    super.refresh_afterCreate();
    if (!GlobalState.isParsing()) {
      fetchDimensions();
      createColumnsRows();
      ensureReasonableSizeForEmptyDimensions();
    }
  }

  @Override
  protected void refresh_fetch() throws Exception {
    super.refresh_fetch();
  }

  private void ensureReasonableSizeForEmptyDimensions() throws Exception {
    List<WidgetInfo> widgets = getContainer().getChildrenWidgets();
    int columnCount = (Integer) getPropertyByTitle("columns").getValue();
    int rowCount = (int) Math.ceil((double) widgets.size() / columnCount);
    for (int column = 0; column < columnCount; column++) {
      if (isEmptyColumn(column) && column < widgets.size()) {
        WidgetInfo widget = widgets.get(column);
        ReflectionUtils.invokeMethod(widget.getObject(), "setWidth(java.lang.String)", "25px");
      }
    }
    for (int row = 0; row < rowCount; row++) {
      if (isEmptyRow(row)) {
        WidgetInfo widget = widgets.get(row * columnCount);
        ReflectionUtils.invokeMethod(widget.getObject(), "setHeight(java.lang.String)", "25px");
      }
    }
    ReflectionUtils.invokeMethod(getContainer().getObject(), "doLayout()");
    getContainer().getState().runMessagesLoop();
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Dimensions operations
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Removes empty (only with fillers) columns/rows.
   */
  void deleteEmptyColumnsRows(TableLayoutDataInfo removingData) throws Exception {
    deleteEmptyColumns(removingData);
    deleteEmptyRows(removingData);
  }

  /**
   * Deletes empty (only with fillers) columns.
   */
  private void deleteEmptyColumns(TableLayoutDataInfo removingData) throws Exception {
    WidgetInfo[][] grid = getWidgetsGrid();
    boolean deleteOnlyIfIsRemovingColumn = false;
    for (int column = grid[0].length - 1; column >= 0; column--) {
      boolean isRemovingColumn =
          removingData != null
              && removingData.x <= column
              && column < removingData.x + removingData.width;
      // check if empty
      boolean isEmpty = true;
      for (int row = 0; row < grid.length; row++) {
        WidgetInfo widget = grid[row][column];
        isEmpty &= isFiller(widget);
      }
      // delete if empty
      if (isEmpty && (!deleteOnlyIfIsRemovingColumn || isRemovingColumn)) {
        command_deleteColumn(column, false);
      } else {
        deleteOnlyIfIsRemovingColumn = true;
      }
    }
  }

  /**
   * Deletes empty (only with fillers) rows.
   */
  private void deleteEmptyRows(TableLayoutDataInfo removingData) throws Exception {
    WidgetInfo[][] grid = getWidgetsGrid();
    boolean deleteOnlyIfIsRemovingRow = false;
    for (int row = grid.length - 1; row >= 0; row--) {
      boolean isRemovingRow =
          removingData != null
              && removingData.y <= row
              && row < removingData.y + removingData.height;
      // check if empty
      boolean isEmpty = true;
      for (int column = 0; column < grid[row].length; column++) {
        WidgetInfo widget = grid[row][column];
        isEmpty &= isFiller(widget);
      }
      // delete if empty
      if (isEmpty && (!deleteOnlyIfIsRemovingRow || isRemovingRow)) {
        command_deleteRow(row, false);
      } else {
        deleteOnlyIfIsRemovingRow = true;
      }
    }
  }

  /**
   * Deletes column with given index and all widgets that located in this column.
   */
  public void command_deleteColumn(int column, boolean deleteEmptyRows) throws Exception {
    int columnCount = getWidgetsGridSize().width;
    // update TableData, delete widgets
    m_replaceWithFillers = false;
    try {
      for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
        TableLayoutDataInfo tableData = getTableData(widget);
        //
        if (tableData.x == column) {
          widget.delete();
        } else if (tableData.x > column) {
          tableData.x--;
        } else if (tableData.x + tableData.width > column) {
          tableData.setColSpan(tableData.width - 1);
        }
      }
    } finally {
      m_replaceWithFillers = true;
    }
    // update count
    if (columnCount >= 2) {
      getPropertyByTitle("columns").setValue(columnCount - 1);
    }
    // it is possible, that we have now empty rows, so delete them too
    if (deleteEmptyRows) {
      deleteEmptyRows(null);
    }
  }

  /**
   * Deletes row with given index and all widgets that located in this row.
   */
  public void command_deleteRow(int row, boolean deleteEmptyColumn) throws Exception {
    // update TableData, delete widgets
    m_replaceWithFillers = false;
    try {
      for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
        TableLayoutDataInfo tableData = getTableData(widget);
        //
        if (tableData.y == row) {
          widget.delete();
        } else if (tableData.y > row) {
          tableData.y--;
        } else if (tableData.y + tableData.height > row) {
          tableData.setRowSpan(tableData.height - 1);
        }
      }
    } finally {
      m_replaceWithFillers = true;
    }
    // it is possible, that we have now empty columns, so delete them too
    if (deleteEmptyColumn) {
      deleteEmptyColumns(null);
    }
  }

  /**
   * Moves column from/to given index.
   */
  public void command_MOVE_COLUMN(int fromIndex, int toIndex) throws Exception {
    fixGrid();
    // move column in columns list
    {
      getColumns(); // kick to initialize columns
      ColumnInfo column = m_columns.remove(fromIndex);
      if (fromIndex < toIndex) {
        m_columns.add(toIndex - 1, column);
      } else {
        m_columns.add(toIndex, column);
      }
    }
    // prepare new column
    prepareCell(toIndex, true, -1, false);
    if (toIndex < fromIndex) {
      fromIndex++;
    }
    // move children
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      if (!isFiller(widget)) {
        TableLayoutDataInfo tableData = getTableData(widget);
        if (tableData.x == fromIndex) {
          command_setCells(widget, new Rectangle(toIndex, tableData.y, 1, tableData.height), true);
        }
      }
    }
    // delete old column
    command_deleteColumn(fromIndex, false);
    deleteEmptyColumnsRows(null);
  }

  /**
   * Moves row from/to given index.
   */
  public void command_MOVE_ROW(int fromIndex, int toIndex) throws Exception {
    fixGrid();
    // move row in rows list
    {
      getRows(); // kick to initialize rows
      RowInfo row = m_rows.remove(fromIndex);
      if (fromIndex < toIndex) {
        m_rows.add(toIndex - 1, row);
      } else {
        m_rows.add(toIndex, row);
      }
    }
    // prepare new row
    prepareCell(-1, false, toIndex, true);
    if (toIndex < fromIndex) {
      fromIndex++;
    }
    // move children
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      if (!isFiller(widget)) {
        TableLayoutDataInfo tableData = getTableData(widget);
        if (tableData.y == fromIndex) {
          command_setCells(widget, new Rectangle(tableData.x, toIndex, tableData.width, 1), true);
        }
      }
    }
    // delete old row
    command_deleteRow(fromIndex, false);
    deleteEmptyColumnsRows(null);
  }

  /**
   * If there are components that span multiple columns/rows, and no other "real" components in
   * these columns/rows, then removes these excess columns/rows.
   */
  public void command_normalizeSpanning() throws Exception {
    Dimension gridSize = getWidgetsGridSize();
    boolean[] filledColumns = new boolean[gridSize.width];
    boolean[] filledRows = new boolean[gridSize.height];
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      if (!isFiller(widget)) {
        TableLayoutDataInfo tableData = getTableData(widget);
        filledColumns[tableData.x] = true;
        filledRows[tableData.y] = true;
      }
    }
    // remove empty columns
    for (int column = filledColumns.length - 1; column >= 0; column--) {
      if (!filledColumns[column]) {
        command_deleteColumn(column, false);
      }
    }
    // remove empty rows
    for (int row = filledRows.length - 1; row >= 0; row--) {
      if (!filledRows[row]) {
        command_deleteRow(row, false);
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Dimensions
  //
  ////////////////////////////////////////////////////////////////////////////
  private int m_columnCount;
  private int m_rowCount;
  private final List<ColumnInfo> m_columns = Lists.newArrayList();
  private final List<RowInfo> m_rows = Lists.newArrayList();

  private void fetchDimensions() throws Exception {
    boolean initializeData = m_columns.isEmpty();
    TableLayoutDimensionsSupport support = new TableLayoutDimensionsSupport(this, initializeData);
    support.initialize();
    m_columnCount = support.getColumnCount();
    m_rowCount = support.getRowCount();
  }

  /**
   * Fills {@link #m_columns} and {@link #m_rows} collections.
   */
  private void createColumnsRows() {
    m_columns.clear();
    m_rows.clear();
    // add columns
    for (int i = 0; i < m_columnCount; i++) {
      m_columns.add(new ColumnInfo(this));
    }
    // add rows
    for (int i = 0; i < m_rowCount; i++) {
      m_rows.add(new RowInfo(this));
    }
  }

  /**
   * @return the list of all {@link ColumnInfo}'s.
   */
  public final List<ColumnInfo> getColumns() {
    return m_columns;
  }

  /**
   * @return the list of all {@link RowInfo}'s.
   */
  public final List<RowInfo> getRows() {
    return m_rows;
  }

  /**
   * @return <code>true</code> if given column is empty, i.e. has no components that start in it.
   */
  boolean isEmptyColumn(final int column) {
    final boolean[] isEmpty = new boolean[]{true};
    visitWidgets(new WidgetVisitor() {
      public void visit(WidgetInfo component, TableLayoutDataInfo cell) throws Exception {
        if (cell.x == column) {
          isEmpty[0] &= isFiller(component);
        }
      }
    });
    return isEmpty[0];
  }

  /**
   * @return <code>true</code> if given row is empty, i.e. has no components that start in it.
   */
  boolean isEmptyRow(final int row) {
    final boolean[] isEmpty = new boolean[]{true};
    visitWidgets(new WidgetVisitor() {
      public void visit(WidgetInfo component, TableLayoutDataInfo cell) throws Exception {
        if (cell.y == row) {
          isEmpty[0] &= isFiller(component);
        }
      }
    });
    return isEmpty[0];
  }

  /**
   * @return <code>true</code> if given {@link WidgetInfo} is filler.
   */
  public static boolean isFiller(WidgetInfo widget) {
    return widget.getVariableSupport() instanceof EmptyVariableSupport
        && widget.getDescription().getComponentClass().getName().equals(
            "com.gwtext.client.widgets.form.Label");
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Commands
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Creates new {@link WidgetInfo} in given cell.
   *
   * @param newWidget
   *          the new {@link WidgetInfo} to create.
   * @param column
   *          the column (0 based).
   * @param row
   *          the row (0 based).
   */
  public void command_CREATE(WidgetInfo newWidget,
      int column,
      boolean insertColumn,
      int row,
      boolean insertRow) throws Exception {
    startEdit();
    try {
      command_CREATE(newWidget, null);
      // move to required cell
      prepareCell(column, insertColumn, row, insertRow);
      command_setCells(newWidget, new Rectangle(column, row, 1, 1), false);
    } finally {
      endEdit();
    }
  }

  /**
   * Moves existing {@link WidgetInfo} into new cell.
   */
  public void command_MOVE(WidgetInfo widget,
      int column,
      boolean insertColumn,
      int row,
      boolean insertRow) throws Exception {
    startEdit();
    try {
      prepareCell(column, insertColumn, row, insertRow);
      command_setCells(widget, new Rectangle(column, row, 1, 1), true);
      deleteEmptyColumnsRows(null);
    } finally {
      endEdit();
    }
  }

  /**
   * Adds {@link WidgetInfo} from other parent into cell.
   */
  public void command_ADD(WidgetInfo widget,
      int column,
      boolean insertColumn,
      int row,
      boolean insertRow) throws Exception {
    startEdit();
    try {
      command_MOVE(widget, null);
      // move to required cell
      prepareCell(column, insertColumn, row, insertRow);
      command_setCells(widget, new Rectangle(column, row, 1, 1), false);
    } finally {
      endEdit();
    }
  }

  /**
   * Prepares cell with given column/row - inserts/appends columns/rows if necessary.
   */
  void prepareCell(int column, boolean insertColumn, int row, boolean insertRow) throws Exception {
    // prepare count of columns/rows
    int columnCount;
    int rowCount;
    {
      Dimension gridSize = getWidgetsGridSize();
      columnCount = gridSize.width;
      rowCount = gridSize.height;
    }
    // append
    {
      int newColumnCount = Math.max(columnCount, 1 + column);
      int newRowCount = Math.max(rowCount, 1 + row);
      // append rows
      for (int newRow = rowCount; newRow <= row; newRow++) {
        for (int columnIndex = 0; columnIndex < newColumnCount; columnIndex++) {
          addFiller(columnIndex, newRow);
        }
      }
      // append columns
      getPropertyByTitle("columns").setValue(newColumnCount);
      for (int newColumn = columnCount; newColumn <= column; newColumn++) {
        for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
          addFiller(newColumn, rowIndex);
        }
      }
      // set new count of columns/rows
      columnCount = newColumnCount;
      rowCount = newRowCount;
    }
    // insert row
    if (insertRow) {
      rowCount++;
      // update TableData for all widgets
      boolean[] columnsToIgnore = new boolean[columnCount];
      for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
        TableLayoutDataInfo tableData = getTableData(widget);
        //
        if (tableData.y >= row) {
          tableData.y++;
        } else if (tableData.y + tableData.height > row) {
          tableData.setRowSpan(tableData.height + 1);
          for (int i = tableData.x; i < tableData.x + tableData.width; i++) {
            columnsToIgnore[i] = true;
          }
        }
      }
      // add fillers for new row
      for (int i = 0; i < columnCount; i++) {
        if (!columnsToIgnore[i]) {
          addFiller(i, row);
        }
      }
    }
    // insert column
    if (insertColumn) {
      // update TableData for all widgets
      boolean[] rowsToIgnore = new boolean[rowCount];
      for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
        TableLayoutDataInfo tableData = getTableData(widget);
        //
        if (tableData.x >= column) {
          tableData.x++;
        } else if (tableData.x + tableData.width > column) {
          tableData.setColSpan(tableData.width + 1);
          for (int i = tableData.y; i < tableData.y + tableData.height; i++) {
            rowsToIgnore[i] = true;
          }
        }
      }
      // insert fillers for new column
      getPropertyByTitle("columns").setValue(columnCount + 1);
      for (int i = 0; i < rowCount; i++) {
        if (!rowsToIgnore[i]) {
          addFiller(column, i);
        }
      }
    }
  }

  /**
   * Sets the cells occupied by given {@link WidgetInfo}.
   *
   * @param forMove
   *          is <code>true</code> if we move widget and <code>false</code> if we set cells for
   *          newly added widget.
   */
  public void command_setCells(WidgetInfo widget, Rectangle cells, boolean forMove)
      throws Exception {
    TableLayoutDataInfo tableData = getTableData(widget);
    // prepare grid
    fixGrid();
    WidgetInfo[][] grid = getWidgetsGrid();
    Set<Point> cellsToAddFillers = Sets.newHashSet();
    Set<Point> cellsToRemoveFillers = Sets.newHashSet();
    // replace widget with fillers
    if (forMove) {
      for (int x = tableData.x; x < tableData.x + tableData.width; x++) {
        for (int y = tableData.y; y < tableData.y + tableData.height; y++) {
          Point cell = new Point(x, y);
          cellsToAddFillers.add(cell);
        }
      }
    }
    // remove fillers from occupied cells
    for (int x = cells.x; x < cells.right(); x++) {
      for (int y = cells.y; y < cells.bottom(); y++) {
        Point cell = new Point(x, y);
        cellsToAddFillers.remove(cell);
        if (isFiller(grid[y][x])) {
          cellsToRemoveFillers.add(cell);
        }
      }
    }
    // do edit operations
    startEdit();
    try {
      // move
      if (tableData.x != cells.x || tableData.y != cells.y) {
        // update TableData
        {
          tableData.x = cells.x;
          tableData.y = cells.y;
        }
        // move model
        {
          WidgetInfo reference = getReferenceWidget(cells.y, cells.x, widget);
          command_MOVE(widget, reference);
        }
      }
      // set span
      {
        tableData.setColSpan(cells.width);
        tableData.setRowSpan(cells.height);
      }
      // remove fillers
      for (Point cell : cellsToRemoveFillers) {
        WidgetInfo filler = grid[cell.y][cell.x];
        filler.delete();
      }
      // add fillers
      for (Point cell : cellsToAddFillers) {
        addFiller(cell.x, cell.y);
      }
    } finally {
      endEdit();
    }
  }

  /**
   * @return the {@link Point} with size of widgets grid.
   */
  private Dimension getWidgetsGridSize() {
    int columnCount = 0;
    int rowCount = 0;
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      TableLayoutDataInfo tableData = getTableData(widget);
      //
      columnCount = Math.max(columnCount, tableData.x + tableData.width);
      rowCount = Math.max(rowCount, tableData.y + tableData.height);
    }
    // OK, we have grid
    return new Dimension(columnCount, rowCount);
  }

  /**
   * @return the double array of {@link WidgetInfo} where each element contains {@link WidgetInfo}
   *         that occupies this cell.
   */
  private WidgetInfo[][] getWidgetsGrid() throws Exception {
    Dimension gridSize = getWidgetsGridSize();
    // prepare empty grid
    WidgetInfo[][] grid;
    {
      grid = new WidgetInfo[gridSize.height][];
      for (int rowIndex = 0; rowIndex < grid.length; rowIndex++) {
        grid[rowIndex] = new WidgetInfo[gridSize.width];
      }
    }
    // fill grid
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      // prepare cells
      Rectangle cells;
      {
        TableLayoutDataInfo tableData = getTableData(widget);
        cells = new Rectangle(tableData.x, tableData.y, tableData.width, tableData.height);
      }
      // fill grid cells
      for (int x = cells.x; x < cells.right(); x++) {
        for (int y = cells.y; y < cells.bottom(); y++) {
          // ignore newly added widgets without real cell
          if (x != -1 && y != -1) {
            grid[y][x] = widget;
          }
        }
      }
    }
    // OK, we have grid
    return grid;
  }

  /**
   * "Fixes" grid, i.e. ensures that all cells are filled (at least with fillers), even if this is
   * not strongly required by layout itself for final cells. We do this to avoid checks for
   * <code>null</code> in many places.
   */
  void fixGrid() throws Exception {
    WidgetInfo[][] grid = getWidgetsGrid();
    for (int row = 0; row < grid.length; row++) {
      for (int column = 0; column < grid[row].length; column++) {
        if (grid[row][column] == null) {
          addFiller(column, row);
        }
      }
    }
  }

  /**
   * @return the {@link WidgetInfo} that should be used as reference of adding into specified cell.
   *
   * @param exclude
   *          the {@link WidgetInfo} that should not be checked, for example because we move it now
   */
  private WidgetInfo getReferenceWidget(int row, int column, WidgetInfo exclude) throws Exception {
    for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
      if (widget != exclude) {
        TableLayoutDataInfo tableData = getTableData(widget);
        if (tableData.y > row || tableData.y == row && tableData.x >= column) {
          return widget;
        }
      }
    }
    // no reference
    return null;
  }

  /**
   * Adds filler {@link WidgetInfo} into given cell.
   */
  private void addFiller(int column, int row) throws Exception {
    // prepare creation support
    ConstructorCreationSupport creationSupport = new ConstructorCreationSupport("empty", false);
    // prepare filler
    WidgetInfo filler =
        (WidgetInfo) JavaInfoUtils.createJavaInfo(
            getEditor(),
            "com.gwtext.client.widgets.form.Label",
            creationSupport);
    // add filler
    WidgetInfo reference = getReferenceWidget(row, column, null);
    JavaInfoUtils.add(
        filler,
        new EmptyInvocationVariableSupport(filler, "%parent%.add(%child%)", 0),
        PureFlatStatementGenerator.INSTANCE,
        AssociationObjects.invocationChildNull(),
        getContainer(),
        reference);
    // set x/y for new filler
    TableLayoutDataInfo tableData = getTableData(filler);
    tableData.x = column;
    tableData.y = row;
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Visiting
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * Visitor for {@link WidgetInfo} and their {@link TableLayoutDataInfo}.
   */
  interface WidgetVisitor {
    void visit(WidgetInfo component, TableLayoutDataInfo constraints) throws Exception;
  }

  /**
   * Visits grid {@link WidgetInfo}'s of this {@link ContainerInfo}.
   */
  void visitWidgets(final WidgetVisitor visitor) {
    ExecutionUtils.runRethrow(new RunnableEx() {
      public void run() throws Exception {
        for (WidgetInfo component : getContainer().getChildrenWidgets()) {
          TableLayoutDataInfo cell = getTableData(component);
          visitor.visit(component, cell);
        }
      }
    });
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // IGridInfo support
  //
  ////////////////////////////////////////////////////////////////////////////
  private Interval[] m_columnIntervals;
  private Interval[] m_rowIntervals;
  private IGridInfo m_gridInfo;

  /**
   * @return the {@link IGridInfo} that describes this layout.
   */
  public IGridInfo getGridInfo() {
    if (m_gridInfo == null) {
      ExecutionUtils.runRethrow(new RunnableEx() {
        public void run() throws Exception {
          createGridInfo();
        }
      });
    }
    return m_gridInfo;
  }

  /**
   * Initializes {@link #m_gridInfo}.
   */
  private void createGridInfo() throws Exception {
    {
      TableLayoutIntervalsSupport intervalsSupport = new TableLayoutIntervalsSupport(this);
      intervalsSupport.fetch();
      m_columnIntervals = intervalsSupport.getColumnIntervals();
      m_rowIntervals = intervalsSupport.getRowIntervals();
    }
    m_gridInfo = new IGridInfo() {
      ////////////////////////////////////////////////////////////////////////////
      //
      // Dimensions
      //
      ////////////////////////////////////////////////////////////////////////////
      public int getColumnCount() {
        return m_columnIntervals.length;
      }

      public int getRowCount() {
        return m_rowIntervals.length;
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Intervals
      //
      ////////////////////////////////////////////////////////////////////////////
      public Interval[] getColumnIntervals() {
        return m_columnIntervals;
      }

      public Interval[] getRowIntervals() {
        return m_rowIntervals;
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Cells
      //
      ////////////////////////////////////////////////////////////////////////////
      public Rectangle getComponentCells(IAbstractComponentInfo component) {
        Assert.instanceOf(WidgetInfo.class, component);
        TableLayoutDataInfo data = getTableData((WidgetInfo) component);
        return new Rectangle(data.getColumn(), data.getRow(), data.getColSpan(), data.getRowSpan());
      }

      public Rectangle getCellsRectangle(Rectangle cells) {
        int x = m_columnIntervals[cells.x].begin;
        int y = m_rowIntervals[cells.y].begin;
        int w = m_columnIntervals[cells.right() - 1].end() - x;
        int h = m_rowIntervals[cells.bottom() - 1].end() - y;
        return new Rectangle(x, y, w, h);
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Feedback
      //
      ////////////////////////////////////////////////////////////////////////////
      public boolean isRTL() {
        return false;
      }

      public Insets getInsets() {
        return new Insets();
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Virtual columns
      //
      ////////////////////////////////////////////////////////////////////////////
      public boolean hasVirtualColumns() {
        return true;
      }

      public int getVirtualColumnSize() {
        return 25;
      }

      public int getVirtualColumnGap() {
        return 5;
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Virtual columns
      //
      ////////////////////////////////////////////////////////////////////////////
      public boolean hasVirtualRows() {
        return true;
      }

      public int getVirtualRowSize() {
        return 25;
      }

      public int getVirtualRowGap() {
        return 5;
      }

      ////////////////////////////////////////////////////////////////////////////
      //
      // Checks
      //
      ////////////////////////////////////////////////////////////////////////////
      public IAbstractComponentInfo getOccupied(int column, int row) {
        for (WidgetInfo widget : getContainer().getChildrenWidgets()) {
          TableLayoutDataInfo data = getTableData(widget);
          if (data.getColumn() <= column
              && column < data.getColumn() + data.getColSpan()
              && data.getRow() <= row
              && row < data.getRow() + data.getRowSpan()) {
            return isFiller(widget) ? null : widget;
          }
        }
        return null;
      }
    };
  }

  ////////////////////////////////////////////////////////////////////////////
  //
  // Utils
  //
  ////////////////////////////////////////////////////////////////////////////
  /**
   * @return the {@link Image} for {@link TableLayoutInfo}.
   */
  public static Image getImage(String path) {
    return Activator.getImage("info/layout/TableLayout/" + path);
  }

  /**
   * @return the {@link ImageDescriptor} for {@link TableLayoutInfo}.
   */
  public static ImageDescriptor getImageDescriptor(String path) {
    return Activator.getImageDescriptor("info/layout/TableLayout/" + path);
  }
}
TOP

Related Classes of com.google.gdt.eclipse.designer.gwtext.model.layout.table.TableLayoutInfo$WidgetVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.