| /* |
| * 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; |
| } |
| } |