| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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.intellij.uiDesigner; |
| |
| import com.intellij.uiDesigner.core.GridConstraints; |
| import com.intellij.uiDesigner.radComponents.RadAbstractGridLayoutManager; |
| import com.intellij.uiDesigner.radComponents.RadComponent; |
| import com.intellij.uiDesigner.radComponents.RadContainer; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.awt.*; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public final class GridChangeUtil { |
| private GridChangeUtil() { |
| } |
| |
| public static void splitColumn(final RadContainer grid, final int columnIndex) { |
| splitCell(grid, columnIndex, false); |
| } |
| |
| public static void splitRow(final RadContainer grid, final int rowIndex) { |
| splitCell(grid, rowIndex, true); |
| } |
| |
| public static boolean isColumnEmpty(final RadContainer grid, final int columnIndex) { |
| return canDeleteCell(grid, columnIndex, false) == CellStatus.Empty; |
| } |
| |
| public static boolean isRowEmpty(final RadContainer grid, final int rowIndex) { |
| return canDeleteCell(grid, rowIndex, true) == CellStatus.Empty; |
| } |
| |
| /** |
| * @param cellIndex column or row index, depending on isRow parameter; must be in the range 0..grid.get{Row|Column}Count()-1 |
| * @param isRow if true, row inserted, otherwise column |
| * @param isBefore if true, row/column will be inserted before row/column with given index, otherwise after |
| */ |
| public static void insertRowOrColumn(final RadContainer grid, final int cellIndex, final boolean isRow, final boolean isBefore) { |
| check(grid, isRow, cellIndex); |
| |
| final RadAbstractGridLayoutManager oldLayout = grid.getGridLayoutManager(); |
| |
| int beforeIndex = cellIndex; |
| if (!isBefore) { |
| // beforeIndex can actually be equal to get{Row|Column}Count an case we add row after the last row/column |
| beforeIndex++; |
| } |
| |
| final LayoutManager newLayout = oldLayout.copyLayout(grid.getLayout(), isRow ? 1 : 0, isRow ? 0 : 1); |
| GridConstraints[] oldConstraints = copyConstraints(grid); |
| |
| for (int i=grid.getComponentCount() - 1; i >= 0; i--){ |
| final GridConstraints constraints = grid.getComponent(i).getConstraints(); |
| adjustConstraintsOnInsert(constraints, isRow, beforeIndex, 1); |
| } |
| |
| grid.setLayout(newLayout); |
| fireAllConstraintsChanged(grid, oldConstraints); |
| } |
| |
| private static GridConstraints[] copyConstraints(RadContainer grid) { |
| final GridConstraints[] gridConstraints = new GridConstraints[grid.getComponentCount()]; |
| for (int i = 0; i < grid.getComponentCount(); i++) { |
| gridConstraints [i] = (GridConstraints) grid.getComponent(i).getConstraints().clone(); |
| } |
| return gridConstraints; |
| } |
| |
| private static void fireAllConstraintsChanged(RadContainer grid, GridConstraints[] oldConstraints) { |
| for(int i=grid.getComponentCount()-1; i >= 0; i--) { |
| grid.getComponent(i).fireConstraintsChanged(oldConstraints [i]); |
| } |
| } |
| |
| public static void adjustConstraintsOnInsert(final GridConstraints constraints, final boolean isRow, final int beforeIndex, |
| final int count) { |
| if (constraints.getCell(isRow) >= beforeIndex) { |
| addToCell(constraints, isRow, count); |
| } |
| else if (isCellInsideComponent(constraints, isRow, beforeIndex)) { |
| // component belongs to the cell being resized - increment component's span |
| addToSpan(constraints, isRow, count); |
| } |
| } |
| |
| /** |
| * @param cellIndex column or row index, depending on isRow parameter; must be in the range 0..grid.get{Row|Column}Count()-1 |
| * @param isRow if true, row is splitted, otherwise column |
| */ |
| public static void splitCell(final RadContainer grid, final int cellIndex, final boolean isRow) { |
| check(grid, isRow, cellIndex); |
| |
| int insertedCells = grid.getGridLayoutManager().insertGridCells(grid, cellIndex, isRow, false, false); |
| |
| for (int i=grid.getComponentCount() - 1; i >= 0; i--){ |
| final RadComponent component = grid.getComponent(i); |
| final GridConstraints constraints = component.getConstraints(); |
| |
| if (constraints.getCell(isRow) + constraints.getSpan(isRow) - 1 == cellIndex) { |
| // component belongs to the cell being resized - increment component's span |
| GridConstraints oldConstraints = (GridConstraints)constraints.clone(); |
| constraints.setSpan(isRow, constraints.getSpan(isRow) + insertedCells); |
| component.fireConstraintsChanged(oldConstraints); |
| } |
| } |
| } |
| |
| public enum CellStatus { |
| Empty, Redundant, CanShift, Required |
| } |
| |
| /** |
| * @param cellIndex column or row index, depending on isRow parameter; must be in the range 0..grid.get{Row|Column}Count()-1 |
| * @param isRow if true, row is deleted, otherwise column |
| * @return whether the specified column can be deleted |
| */ |
| public static CellStatus canDeleteCell(@NotNull final RadContainer grid, final int cellIndex, final boolean isRow) { |
| check(grid, isRow, cellIndex); |
| |
| // Do not allow to delete the single row/column |
| if(isRow && grid.getGridRowCount() <= grid.getGridLayoutManager().getMinCellCount()) { |
| return CellStatus.Required; |
| } |
| else if(!isRow && grid.getGridColumnCount() <= grid.getGridLayoutManager().getMinCellCount()) { |
| return CellStatus.Required; |
| } |
| |
| boolean haveComponents = false; |
| boolean haveOrigins = false; |
| boolean haveSingleSpan = false; |
| for (int i = 0; i < grid.getComponentCount(); i++) { |
| final GridConstraints constraints = grid.getComponent(i).getConstraints(); |
| final int cell = constraints.getCell(isRow); |
| final int span = constraints.getSpan(isRow); |
| |
| if (cellIndex >= cell && cellIndex < cell+span) { |
| haveComponents = true; |
| if (cellIndex == cell) { |
| haveOrigins = true; |
| if (span == 1) { |
| haveSingleSpan = true; |
| } |
| } |
| } |
| } |
| if (haveSingleSpan) |
| return CellStatus.Required; |
| if (haveOrigins) |
| return CellStatus.CanShift; |
| if (haveComponents) |
| return CellStatus.Redundant; |
| return CellStatus.Empty; |
| } |
| |
| public static boolean canDeleteCells(final RadContainer grid, final int[] cells, final boolean row) { |
| // for multiple cells, we can't determine if deleting all cells will have a correct result |
| for(int cell: cells) { |
| CellStatus status = canDeleteCell(grid, cell, row); |
| if (status != CellStatus.Empty) { |
| if (cells.length == 1 && status == CellStatus.Redundant) { |
| return true; |
| } |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * @param cellIndex column or row index, depending on isRow parameter; must be in the range 0..grid.get{Row|Column}Count()-1 |
| * @param isRow if true, row is deleted, otherwise column |
| */ |
| public static void deleteCell(final RadContainer grid, final int cellIndex, final boolean isRow) { |
| check(grid, isRow, cellIndex); |
| if (canDeleteCell(grid, cellIndex, isRow) == CellStatus.Required) { |
| throw new IllegalArgumentException("cell cannot be deleted"); |
| } |
| |
| final RadAbstractGridLayoutManager oldLayout = grid.getGridLayoutManager(); |
| |
| final LayoutManager newLayout = oldLayout.copyLayout(grid.getLayout(), isRow ? -1 : 0, isRow ? 0 : -1); |
| GridConstraints[] oldConstraints = copyConstraints(grid); |
| |
| for (int i=grid.getComponentCount() - 1; i >= 0; i--){ |
| final GridConstraints constraints = grid.getComponent(i).getConstraints(); |
| |
| if (constraints.getCell(isRow) > cellIndex) { |
| // component starts after the cell being deleted - move it |
| addToCell(constraints, isRow, -1); |
| } |
| else if (isCellInsideComponent(constraints, isRow, cellIndex)) { |
| // component belongs to the cell being deleted - decrement component's span |
| addToSpan(constraints, isRow, -1); |
| } |
| } |
| |
| grid.setLayout(newLayout); |
| fireAllConstraintsChanged(grid, oldConstraints); |
| } |
| |
| |
| private static boolean isCellInsideComponent(final GridConstraints constraints, final boolean isRow, final int cellIndex) { |
| final int cell = constraints.getCell(isRow); |
| final int span = constraints.getSpan(isRow); |
| return cell <= cellIndex && cellIndex <= cell + span - 1; |
| } |
| |
| /** |
| * check whether passed container is grid and cellIndex is in proper range |
| */ |
| private static void check(@NotNull RadContainer grid, final boolean isRow, final int cellIndex){ |
| if (!grid.getLayoutManager().isGrid()){ |
| throw new IllegalArgumentException("container must be grid"); |
| } |
| |
| final int cellCount = isRow ? grid.getGridRowCount() : grid.getGridColumnCount(); |
| if (cellIndex == 0 && cellCount == 0) return; |
| if (cellIndex < 0 || cellIndex >= cellCount) { |
| throw new IllegalArgumentException("invalid index: " + cellIndex); |
| } |
| } |
| |
| private static void addToCell(final GridConstraints constraints, final boolean isRow, final int delta){ |
| if (isRow) { |
| constraints.setRow(constraints.getRow() + delta); |
| } |
| else { |
| constraints.setColumn(constraints.getColumn() + delta); |
| } |
| } |
| |
| private static void addToSpan(final GridConstraints constraints, final boolean isRow, final int delta){ |
| if (isRow) { |
| constraints.setRowSpan(constraints.getRowSpan() + delta); |
| } |
| else { |
| constraints.setColSpan(constraints.getColSpan() + delta); |
| } |
| } |
| |
| public static void moveCells(final RadContainer container, final boolean isRow, final int[] cellsToMove, int targetCell) { |
| for(int i=0; i<cellsToMove.length; i++) { |
| final int sourceCell = cellsToMove[i]; |
| moveCell(container, isRow, sourceCell, targetCell); |
| if (sourceCell < targetCell) { |
| for(int j=i+1; j<cellsToMove.length; j++) { |
| cellsToMove [j]--; |
| } |
| } |
| else { |
| targetCell++; |
| } |
| } |
| } |
| |
| public static void moveCell(final RadContainer container, final boolean isRow, final int sourceCell, int targetCell) { |
| if (targetCell == sourceCell || targetCell == sourceCell+1) return; |
| // if column moved to left - components inbetween move to right, and vice versa |
| int delta = (sourceCell > targetCell) ? 1 : -1; |
| int startCell = Math.min(sourceCell, targetCell); |
| int endCell = Math.max(sourceCell, targetCell); |
| if (sourceCell < targetCell) targetCell--; |
| for(RadComponent c: container.getComponents()) { |
| GridConstraints constraints = c.getConstraints(); |
| GridConstraints oldConstraints = (GridConstraints) constraints.clone(); |
| final int aCell = constraints.getCell(isRow); |
| if (aCell == sourceCell) { |
| constraints.setCell(isRow, targetCell); |
| } |
| else if (aCell >= startCell && aCell < endCell) { |
| constraints.setCell(isRow, aCell + delta); |
| } |
| c.fireConstraintsChanged(oldConstraints); |
| } |
| } |
| } |