blob: 364d5879a664a9c060c5a1fb0876f53838bf6970 [file] [log] [blame]
/*
* 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.designSurface;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.uiDesigner.FormEditingUtil;
import com.intellij.uiDesigner.GridChangeUtil;
import com.intellij.uiDesigner.UIDesignerBundle;
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.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
/**
* @author yole
*/
public class GridInsertLocation extends GridDropLocation {
private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.designSurface.GridInsertLocation");
public static final int INSERT_ARROW_SIZE = 3;
public static final int INSERT_RECT_MIN_SIZE = 15; // should be larger than the insets increase on Shift
private GridInsertMode myMode;
private boolean mySpanInsertMode;
public GridInsertLocation(@NotNull final RadContainer container,
final int row,
final int column,
final GridInsertMode mode) {
super(container, row, column);
myMode = mode;
assert container.getLayoutManager().isGrid();
}
public GridInsertLocation normalize() {
final RadAbstractGridLayoutManager gridManager = myContainer.getGridLayoutManager();
if (myMode == GridInsertMode.RowBefore && myRow > 0) {
myMode = GridInsertMode.RowAfter;
myRow--;
}
else if (myMode == GridInsertMode.ColumnBefore && myColumn > 0) {
myMode = GridInsertMode.ColumnAfter;
myColumn--;
}
if (myMode == GridInsertMode.RowAfter && gridManager.isGapCell(myContainer, true, myRow)) {
myRow--;
}
else if (myMode == GridInsertMode.RowBefore && gridManager.isGapCell(myContainer, true, myRow)) {
myRow++;
}
else if (myMode == GridInsertMode.ColumnAfter && gridManager.isGapCell(myContainer, false, myColumn)) {
myColumn--;
}
else if (myMode == GridInsertMode.ColumnBefore && gridManager.isGapCell(myContainer, false, myColumn)) {
myColumn++;
}
return this;
}
public GridInsertMode getMode() {
return myMode;
}
public void setSpanInsertMode(boolean spanInsertMode) {
mySpanInsertMode = spanInsertMode;
}
private boolean isColumnInsert() {
return myMode == GridInsertMode.ColumnAfter || myMode == GridInsertMode.ColumnBefore;
}
private boolean isRowInsert() {
return myMode == GridInsertMode.RowAfter || myMode == GridInsertMode.RowBefore;
}
public boolean isInsert() {
return isColumnInsert() || isRowInsert();
}
private int getInsertCell() {
return isRowInsert() ? myRow : myColumn;
}
private int getOppositeCell() {
return isRowInsert() ? myColumn : myRow;
}
private boolean isInsertAfter() {
return myMode == GridInsertMode.ColumnAfter || myMode == GridInsertMode.RowAfter;
}
@Override public boolean canDrop(ComponentDragObject dragObject) {
Rectangle rc = getDragObjectDimensions(dragObject, true);
int size = isRowInsert() ? rc.width : rc.height;
if (isInsertInsideComponent(size)) {
LOG.debug("GridInsertLocation.canDrop()=false because insert inside component");
return false;
}
// TODO[yole]: any other conditions to check here?
LOG.debug("GridInsertLocation.canDrop()=true");
return true;
}
private static boolean isSameCell(final ComponentDragObject dragObject, boolean isRow) {
if (dragObject.getComponentCount() == 0) {
return true;
}
int cell = isRow ? dragObject.getRelativeRow(0) : dragObject.getRelativeCol(0);
for(int i=1; i<dragObject.getComponentCount(); i++) {
int cell2 = isRow ? dragObject.getRelativeRow(i) : dragObject.getRelativeCol(i);
if (cell2 != cell) {
return false;
}
}
return true;
}
private boolean isInsertInsideComponent(final int size) {
int endColumn = getInsertCell();
if (isInsertAfter()) endColumn++;
int row = getOppositeCell();
for(int r=row; r<row+size; r++) {
for(int col = 0; col<endColumn; col++) {
RadComponent component;
if (isColumnInsert()) {
component = RadAbstractGridLayoutManager.getComponentAtGrid(getContainer(), r, col);
}
else {
component = RadAbstractGridLayoutManager.getComponentAtGrid(getContainer(), col, r);
}
if (component != null) {
GridConstraints constraints = component.getConstraints();
final boolean isRow = !isColumnInsert();
if (constraints.getCell(isRow) + constraints.getSpan(isRow) > endColumn &&
constraints.getSpan(isRow) > 1) {
return true;
}
}
}
}
return false;
}
@Override public void placeFeedback(FeedbackLayer feedbackLayer, ComponentDragObject dragObject) {
final int insertCol = getColumn();
final int insertRow = getRow();
Rectangle feedbackRect = getGridFeedbackRect(dragObject, isColumnInsert(), isRowInsert(), false);
if (feedbackRect == null) {
feedbackLayer.removeFeedback();
return;
}
Rectangle cellRect = getGridFeedbackCellRect(dragObject, isColumnInsert(), isRowInsert(), false);
assert cellRect != null;
final RadAbstractGridLayoutManager layoutManager = getContainer().getGridLayoutManager();
int[] vGridLines = layoutManager.getVerticalGridLines(getContainer());
int[] hGridLines = layoutManager.getHorizontalGridLines(getContainer());
FeedbackPainter painter = (myMode == GridInsertMode.ColumnBefore ||
myMode == GridInsertMode.ColumnAfter)
? VertInsertFeedbackPainter.INSTANCE
: HorzInsertFeedbackPainter.INSTANCE;
Rectangle rc;
Rectangle rcFeedback = null;
if (dragObject.getComponentCount() == 1) {
int lastColIndex = insertCol + dragObject.getColSpan(0);
if (lastColIndex > vGridLines.length - 1) {
lastColIndex = insertCol + 1;
}
int lastRowIndex = insertRow + dragObject.getRowSpan(0);
if (lastRowIndex > hGridLines.length - 1) {
lastRowIndex = insertRow + 1;
}
int cellWidth = vGridLines [lastColIndex] - vGridLines [insertCol];
int cellHeight = hGridLines [lastRowIndex] - hGridLines [insertRow];
RadComponent component = layoutManager.getComponentAtGrid(getContainer(), insertRow, insertCol);
if (component != null && mySpanInsertMode) {
Rectangle bounds = component.getBounds();
bounds.translate(-vGridLines [insertCol], -hGridLines [insertRow]);
int spaceToRight = vGridLines [lastColIndex] - vGridLines [insertCol] - (bounds.x + bounds.width);
int spaceBelow = hGridLines [lastRowIndex] - hGridLines [insertRow] - (bounds.y + bounds.height);
if (myMode == GridInsertMode.RowBefore && bounds.y > INSERT_RECT_MIN_SIZE) {
rcFeedback = new Rectangle(0, 0, cellWidth, bounds.y);
}
else if (myMode == GridInsertMode.RowAfter && spaceBelow > INSERT_RECT_MIN_SIZE) {
rcFeedback = new Rectangle(0, bounds.y + bounds.height, cellWidth, spaceBelow);
}
else if (myMode == GridInsertMode.ColumnBefore && bounds.x > INSERT_RECT_MIN_SIZE) {
rcFeedback = new Rectangle(0, 0, bounds.x, cellHeight);
}
else if (myMode == GridInsertMode.ColumnAfter && spaceToRight > INSERT_RECT_MIN_SIZE) {
rcFeedback = new Rectangle(bounds.x + bounds.width, 0, spaceToRight, cellHeight);
}
if (rcFeedback != null) {
boolean spanInsertMode = false;
if (isRowInsert()) {
int columns = layoutManager.getGridColumnCount(getContainer());
for (int i = 0; i < columns; i++) {
if (i != insertCol && RadAbstractGridLayoutManager.getComponentAtGrid(getContainer(), insertRow, i) != null) {
spanInsertMode = true;
break;
}
}
} else {
int rows = layoutManager.getGridRowCount(getContainer());
for (int i = 0; i < rows; i++) {
if (i != insertRow && RadAbstractGridLayoutManager.getComponentAtGrid(getContainer(), i, insertCol) != null) {
spanInsertMode = true;
break;
}
}
}
if (!spanInsertMode) {
rcFeedback = null;
}
}
if (rcFeedback != null) {
rcFeedback.translate(vGridLines [insertCol], hGridLines [insertRow]);
}
}
if (rcFeedback == null) {
if (insertCol == layoutManager.getGridColumnCount(getContainer())-1 && myMode == GridInsertMode.ColumnAfter) {
final Dimension initialSize = dragObject.getInitialSize(getContainer());
int feedbackX = vGridLines [vGridLines.length-1] + layoutManager.getGapCellSize(myContainer, false);
int remainingSize = getContainer().getDelegee().getWidth() - feedbackX;
if (!dragObject.isHGrow() && remainingSize > initialSize.width) {
if (dragObject.isVGrow() || initialSize.height > cellHeight) {
rcFeedback = new Rectangle(feedbackX, hGridLines [insertRow], initialSize.width, cellHeight);
}
else {
rcFeedback = new Rectangle(feedbackX, hGridLines [insertRow] + (cellHeight - initialSize.height)/2,
initialSize.width, initialSize.height);
}
}
else if (remainingSize >= 4) {
rcFeedback = new Rectangle(feedbackX, hGridLines [insertRow], remainingSize, cellHeight);
}
}
else if (insertRow == layoutManager.getGridRowCount(getContainer())-1 && myMode == GridInsertMode.RowAfter) {
final Dimension initialSize = dragObject.getInitialSize(getContainer());
int feedbackY = hGridLines [hGridLines.length-1] + layoutManager.getGapCellSize(myContainer, true);
int remainingSize = getContainer().getDelegee().getHeight() - feedbackY;
if (!dragObject.isVGrow() && remainingSize > initialSize.height) {
rcFeedback = new Rectangle(vGridLines [insertCol], feedbackY, cellWidth, initialSize.height);
}
else if (remainingSize >= 4) {
rcFeedback = new Rectangle(vGridLines [insertCol], feedbackY, cellWidth, remainingSize);
}
}
}
}
if (rcFeedback != null) {
feedbackLayer.putFeedback(getContainer().getDelegee(), rcFeedback, getInsertFeedbackTooltip());
return;
}
rc = getInsertFeedbackPosition(myMode, getContainer(), cellRect, feedbackRect);
feedbackLayer.putFeedback(getContainer().getDelegee(), rc, painter, getInsertFeedbackTooltip());
}
private String getInsertFeedbackTooltip() {
int displayRow = myRow + getContainer().getGridLayoutManager().getCellIndexBase();
int displayColumn = myColumn + getContainer().getGridLayoutManager().getCellIndexBase();
String displayName = getContainer().getDisplayName();
switch(myMode) {
case ColumnBefore: return UIDesignerBundle.message("insert.feedback.before.col", displayName, displayRow, displayColumn);
case ColumnAfter: return UIDesignerBundle.message("insert.feedback.after.col", displayName, displayRow, displayColumn);
case RowBefore: return UIDesignerBundle.message("insert.feedback.before.row", displayName, displayColumn, displayRow);
case RowAfter: return UIDesignerBundle.message("insert.feedback.after.row", displayName, displayColumn, displayRow);
}
return null;
}
public static Rectangle getInsertFeedbackPosition(final GridInsertMode mode, final RadContainer container, final Rectangle cellRect,
final Rectangle feedbackRect) {
final RadAbstractGridLayoutManager manager = container.getGridLayoutManager();
int[] vGridLines = manager.getVerticalGridLines(container);
int[] hGridLines = manager.getHorizontalGridLines(container);
Rectangle rc = feedbackRect;
int w=4;
switch (mode) {
case ColumnBefore:
rc = new Rectangle(vGridLines [cellRect.x] - w, feedbackRect.y - INSERT_ARROW_SIZE,
2 * w, feedbackRect.height + 2 * INSERT_ARROW_SIZE);
if (cellRect.x > 0 && manager.isGapCell(container, false, cellRect.x-1)) {
rc.translate(-(vGridLines [cellRect.x] - vGridLines [cellRect.x-1]) / 2, 0);
}
break;
case ColumnAfter:
rc = new Rectangle(vGridLines [cellRect.x + cellRect.width+1] - w, feedbackRect.y - INSERT_ARROW_SIZE,
2 * w, feedbackRect.height + 2 * INSERT_ARROW_SIZE);
if (cellRect.x < manager.getGridColumnCount(container)-1 && manager.isGapCell(container, false, cellRect.x+1)) {
rc.translate((vGridLines [cellRect.x+2] - vGridLines [cellRect.x+1]) / 2, 0);
}
break;
case RowBefore:
rc = new Rectangle(feedbackRect.x - INSERT_ARROW_SIZE, hGridLines [cellRect.y] - w,
feedbackRect.width + 2 * INSERT_ARROW_SIZE, 2 * w);
if (cellRect.y > 0 && manager.isGapCell(container, true, cellRect.y-1)) {
rc.translate(0, -(hGridLines [cellRect.y] - hGridLines [cellRect.y-1]) / 2);
}
break;
case RowAfter:
rc = new Rectangle(feedbackRect.x - INSERT_ARROW_SIZE, hGridLines [cellRect.y+cellRect.height+1] - w,
feedbackRect.width + 2 * INSERT_ARROW_SIZE, 2 * w);
if (cellRect.y < manager.getGridRowCount(container)-1 && manager.isGapCell(container, true, cellRect.y+1)) {
rc.translate(0, (hGridLines [cellRect.y+2] - hGridLines [cellRect.y+1]) / 2);
}
break;
}
return rc;
}
@Override
public void processDrop(final GuiEditor editor,
final RadComponent[] components,
@Nullable final GridConstraints[] constraintsToAdjust,
final ComponentDragObject dragObject) {
int row = getRow();
int col = getColumn();
RadContainer container = getContainer();
boolean canGrow = isRowInsert() ? dragObject.isVGrow() : dragObject.isHGrow();
int cell = isRowInsert() ? getRow() : getColumn();
int cellsToInsert = 1;
if (components.length > 0) {
int cellSize = container.getGridCellCount(isRowInsert());
Rectangle rc = getDragObjectDimensions(dragObject, cell < cellSize - 1);
int size = isRowInsert() ? rc.height : rc.width;
if (size > 0) {
cellsToInsert = size;
}
}
GridSpanInsertProcessor spanInsertProcessor =
mySpanInsertMode && dragObject.getComponentCount() == 1 ? new GridSpanInsertProcessor(container, getRow(), getColumn(), myMode,
dragObject) : null;
int newCell = insertGridCells(container, cell, cellsToInsert, canGrow, isRowInsert(), !isInsertAfter(), constraintsToAdjust);
if (isRowInsert()) {
row = newCell;
}
else {
col = newCell;
}
if (components.length > 0) {
if (spanInsertProcessor != null) {
spanInsertProcessor.doBefore(newCell);
}
dropIntoGrid(container, components, row, col, dragObject);
if (spanInsertProcessor != null) {
spanInsertProcessor.doAfter(newCell);
}
}
}
private static int insertGridCells(RadContainer container, int cell, int cellsToInsert, boolean canGrow, boolean isRow, boolean isBefore,
GridConstraints[] constraintsToAdjust) {
int insertedCells = 1;
for(int i=0; i<cellsToInsert; i++) {
insertedCells = container.getGridLayoutManager().insertGridCells(container, cell, isRow, isBefore, canGrow);
}
// for insert after, shift only by one cell + possibly one gap cell, not by entire number of insertions
if (!isBefore) {
cell += insertedCells;
}
checkAdjustConstraints(constraintsToAdjust, isRow, cell, insertedCells);
return cell;
}
private static void checkAdjustConstraints(@Nullable final GridConstraints[] constraintsToAdjust,
final boolean isRow,
final int index, final int count) {
if (constraintsToAdjust != null) {
for(GridConstraints constraints: constraintsToAdjust) {
GridChangeUtil.adjustConstraintsOnInsert(constraints, isRow, index, count);
}
}
}
@NonNls @Override public String toString() {
return "GridInsertLocation(" + myMode.toString() + ", row=" + getRow() + ", col=" + getColumn() + ")";
}
@Override @Nullable
public ComponentDropLocation getAdjacentLocation(Direction direction) {
if (isRowInsert()) {
if (direction == Direction.RIGHT) {
if (getColumn() < myContainer.getGridColumnCount()-1) {
return new GridInsertLocation(myContainer, getRow(), FormEditingUtil.adjustForGap(myContainer, getColumn()+1, false, 1), getMode());
}
return new GridInsertLocation(myContainer, getRow(), getColumn(), GridInsertMode.ColumnAfter);
}
if (direction == Direction.LEFT) {
if (getColumn() > 0) {
return new GridInsertLocation(myContainer, getRow(), FormEditingUtil.adjustForGap(myContainer, getColumn()-1, false, -1), getMode());
}
return new GridInsertLocation(myContainer, getRow(), getColumn(), GridInsertMode.ColumnBefore);
}
if (direction == Direction.DOWN || direction == Direction.UP) {
int adjRow = (myMode == GridInsertMode.RowAfter) ? getRow() : getRow()-1;
if (direction == Direction.DOWN && adjRow+1 < myContainer.getGridRowCount()) {
return new GridDropLocation(myContainer, FormEditingUtil.adjustForGap(myContainer, adjRow+1, true, 1), getColumn());
}
if (direction == Direction.UP && adjRow >= 0) {
return new GridDropLocation(myContainer, FormEditingUtil.adjustForGap(myContainer, adjRow, true, -1), getColumn());
}
return getLocationAtParent(direction);
}
}
else {
if (direction == Direction.DOWN) {
if (getRow() < myContainer.getGridRowCount()-1) {
return new GridInsertLocation(myContainer, FormEditingUtil.adjustForGap(myContainer, getRow()+1, true, 1), getColumn(), getMode());
}
return new GridInsertLocation(myContainer, getRow(), getColumn(), GridInsertMode.RowAfter);
}
if (direction == Direction.UP) {
if (getRow() > 0) {
return new GridInsertLocation(myContainer, FormEditingUtil.adjustForGap(myContainer, getRow()-1, true, -1), getColumn(), getMode());
}
return new GridInsertLocation(myContainer, getRow(), getColumn(), GridInsertMode.RowBefore);
}
if (direction == Direction.LEFT || direction == Direction.RIGHT) {
int adjCol = (myMode == GridInsertMode.ColumnAfter) ? getColumn() : getColumn()-1;
if (direction == Direction.RIGHT && adjCol+1 < myContainer.getGridColumnCount()) {
return new GridDropLocation(myContainer, getRow(), FormEditingUtil.adjustForGap(myContainer, adjCol+1, false, 1));
}
if (direction == Direction.LEFT && adjCol >= 0) {
return new GridDropLocation(myContainer, getRow(), FormEditingUtil.adjustForGap(myContainer, adjCol, false, -1));
}
return getLocationAtParent(direction);
}
}
return null;
}
private ComponentDropLocation getLocationAtParent(final Direction direction) {
final RadContainer parent = myContainer.getParent();
if (parent.getLayoutManager().isGrid()) {
final GridConstraints c = myContainer.getConstraints();
switch(direction) {
case LEFT: return new GridInsertLocation(parent, c.getRow(), c.getColumn(), GridInsertMode.ColumnBefore);
case RIGHT: return new GridInsertLocation(parent, c.getRow(), c.getColumn()+c.getColSpan()-1, GridInsertMode.ColumnAfter);
case UP: return new GridInsertLocation(parent, c.getRow(), c.getColumn(), GridInsertMode.RowBefore);
case DOWN: return new GridInsertLocation(parent, c.getRow()+c.getRowSpan()-1, c.getColumn(), GridInsertMode.RowAfter);
}
}
return null;
}
}