90c529db3a1b03ce1aad34b6551fb5fb765a488f
[idea/community.git] / plugins / ui-designer / src / com / intellij / uiDesigner / designSurface / GridDropLocation.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.uiDesigner.designSurface;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.uiDesigner.core.GridConstraints;
20 import com.intellij.uiDesigner.radComponents.RadComponent;
21 import com.intellij.uiDesigner.radComponents.RadContainer;
22 import com.intellij.uiDesigner.shared.XYLayoutManager;
23 import org.jetbrains.annotations.NonNls;
24 import org.jetbrains.annotations.Nullable;
25 import org.jetbrains.annotations.NotNull;
26
27 import javax.swing.*;
28 import java.awt.Rectangle;
29 import java.awt.LayoutManager;
30
31 /**
32  * @author yole
33  */
34 public class GridDropLocation implements ComponentDropLocation {
35   private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.designSurface.GridDropLocation");
36
37   protected final RadContainer myContainer;
38   protected int myRow;
39   protected int myColumn;
40
41   public GridDropLocation(@NotNull final RadContainer container, final int row, final int column) {
42     myContainer = container;
43     myRow = row;
44     myColumn = column;
45   }
46
47   public int getRow() {
48     return myRow;
49   }
50
51   public int getColumn() {
52     return myColumn;
53   }
54
55   public RadContainer getContainer() {
56     return myContainer;
57   }
58
59   public boolean canDrop(final ComponentDragObject dragObject) {
60     // If target point doesn't belong to any cell and column then do not allow drop.
61     if (myRow == -1 || myColumn == -1) {
62       LOG.debug("RadContainer.canDrop=false because no cell at mouse position");
63       return false;
64     }
65
66     for(int i=0; i<dragObject.getComponentCount(); i++) {
67       int relativeCol = dragObject.getRelativeCol(i);
68       int relativeRow = dragObject.getRelativeRow(i);
69       int colSpan = dragObject.getColSpan(i);
70       int rowSpan = dragObject.getRowSpan(i);
71
72       LOG.debug("checking component: relativeRow" + relativeRow + ", relativeCol" + relativeCol + ", colSpan=" + colSpan + ", rowSpan=" + rowSpan);
73
74       if (myRow + relativeRow < 0 ||
75           myColumn + relativeCol < 0 ||
76           myRow + relativeRow + rowSpan > myContainer.getGridRowCount() ||
77           myColumn + relativeCol + colSpan > myContainer.getGridColumnCount()) {
78         LOG.debug("RadContainer.canDrop=false because range is outside grid: row=" + (myRow +relativeRow) +
79                   ", col=" + (myColumn +relativeCol) + ", colSpan=" + colSpan + ", rowSpan=" + rowSpan);
80         return false;
81       }
82
83       final RadComponent componentInRect = findOverlappingComponent(myRow + relativeRow, myColumn + relativeCol, rowSpan, colSpan);
84       if (componentInRect != null) {
85         LOG.debug("GridDropLocation.canDrop=false because found component " + componentInRect.getId() +
86                   " in rect (row=" + (myRow +relativeRow) + ", col=" + (myColumn +relativeCol) +
87                   ", rowSpan=" + rowSpan + ", colSpan=" + colSpan + ")");
88         return false;
89       }
90     }
91     LOG.debug("canDrop=true");
92     return true;
93   }
94
95   protected RadComponent findOverlappingComponent(final int startRow, final int startCol, final int rowSpan, final int colSpan) {
96     return myContainer.findComponentInRect(startRow, startCol, rowSpan, colSpan);
97   }
98
99   public void placeFeedback(FeedbackLayer feedbackLayer, ComponentDragObject dragObject) {
100     Rectangle feedbackRect = null;
101     if (getContainer().getLayoutManager().isGrid()) {
102       feedbackRect = getGridFeedbackRect(dragObject, false, false);
103     }
104     if (feedbackRect != null) {
105       final JComponent component = getContainer().getDelegee();
106       StringBuilder feedbackBuilder = new StringBuilder(getContainer().getDisplayName());
107       feedbackBuilder.append(" (").append(myRow + getContainer().getGridLayoutManager().getCellIndexBase());
108       feedbackBuilder.append(", ").append(myColumn + getContainer().getGridLayoutManager().getCellIndexBase());
109       feedbackBuilder.append(")");
110       feedbackLayer.putFeedback(component, feedbackRect, feedbackBuilder.toString());
111     }
112     else {
113       feedbackLayer.removeFeedback();
114     }
115   }
116
117   @Nullable
118   protected Rectangle getGridFeedbackCellRect(ComponentDragObject dragObject, final boolean ignoreWidth, final boolean ignoreHeight) {
119     if (dragObject.getComponentCount() == 0) {
120       LOG.debug("no feedback rect because component count=0");
121       return null;
122     }
123
124     Rectangle rc = getDragObjectDimensions(dragObject);
125     int w = ignoreWidth ? 1 : rc.width;
126     int h = ignoreHeight ? 1 : rc.height;
127
128     if (rc.x < 0 || rc.y < 0 ||
129         rc.y + h > getContainer().getGridRowCount() || rc.x + w > getContainer().getGridColumnCount()) {
130       LOG.debug("no feedback rect because insert range is outside grid: firstRow=" + rc.y +
131                 ", firstCol=" + rc.x + ", lastRow=" + (rc.y+h-1) + ", lastCol=" + (rc.x+w-1));
132       return null;
133     }
134     return new Rectangle(rc.x, rc.y, w-1, h-1);
135   }
136
137   protected Rectangle getDragObjectDimensions(final ComponentDragObject dragObject) {
138     int firstRow = getRow();
139     int lastRow = getRow();
140     int firstCol = getColumn();
141     int lastCol = getColumn();
142     for(int i=0; i<dragObject.getComponentCount(); i++) {
143       final int relRow = dragObject.getRelativeRow(i);
144       final int relCol = dragObject.getRelativeCol(i);
145       firstRow = Math.min(firstRow, getRow() + relRow);
146       firstCol = Math.min(firstCol, getColumn() + relCol);
147       lastRow = Math.max(lastRow, getRow() + relRow + dragObject.getRowSpan(i) - 1);
148       lastCol = Math.max(lastCol, getColumn() + relCol + dragObject.getColSpan(i) - 1);
149     }
150     return new Rectangle(firstCol, firstRow, lastCol-firstCol+1, lastRow-firstRow+1);
151   }
152
153   @Nullable
154   protected Rectangle getGridFeedbackRect(ComponentDragObject dragObject, final boolean ignoreWidth, final boolean ignoreHeight) {
155     Rectangle cellRect = getGridFeedbackCellRect(dragObject, ignoreWidth, ignoreHeight);
156     if (cellRect == null) return null;
157     int h = ignoreHeight ? 0 : cellRect.height;
158     int w = ignoreWidth ? 0 : cellRect.width;
159     return getContainer().getGridLayoutManager().getGridCellRangeRect(getContainer(), cellRect.y, cellRect.x,
160                                                                       cellRect.y+h, cellRect.x+w);
161   }
162
163   public void processDrop(final GuiEditor editor,
164                           final RadComponent[] components,
165                           final GridConstraints[] constraintsToAdjust,
166                           final ComponentDragObject dragObject) {
167     dropIntoGrid(myContainer, components, myRow, myColumn, dragObject);
168   }
169
170   @Nullable
171   public ComponentDropLocation getAdjacentLocation(Direction direction) {
172     switch(direction) {
173       case LEFT:  return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.ColumnBefore);
174       case UP:    return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.RowBefore);
175       case RIGHT: return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.ColumnAfter);
176       case DOWN:  return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.RowAfter); 
177     }
178     return null;
179   }
180
181   @NonNls @Override public String toString() {
182     return "GridDropLocation(row=" + myRow + ",col=" + myColumn + ")";
183   }
184
185   protected static void dropIntoGrid(final RadContainer container, final RadComponent[] components, int row, int column, final ComponentDragObject dragObject) {
186     assert components.length > 0;
187
188     for(int i=0; i<components.length; i++) {
189       RadComponent c = components [i];
190       if (c instanceof RadContainer) {
191         final LayoutManager layout = ((RadContainer)c).getLayout();
192         if (layout instanceof XYLayoutManager) {
193           ((XYLayoutManager)layout).setPreferredSize(c.getSize());
194         }
195       }
196
197       int relativeCol = dragObject.getRelativeCol(i);
198       int relativeRow = dragObject.getRelativeRow(i);
199       LOG.debug("dropIntoGrid: relativeRow=" + relativeRow + ", relativeCol=" + relativeCol);
200       int colSpan = dragObject.getColSpan(i);
201       int rowSpan = dragObject.getRowSpan(i);
202
203       assert row + relativeRow >= 0;
204       assert column + relativeCol >= 0;
205       if (!container.getGridLayoutManager().isGridDefinedByComponents()) {
206         assert relativeRow + rowSpan <= container.getGridRowCount();
207         assert relativeCol + colSpan <= container.getGridColumnCount();
208       }
209
210       RadComponent old = container.findComponentInRect(row + relativeRow, column + relativeCol, rowSpan, colSpan);
211       if (old != null) {
212         LOG.error("Drop rectangle not empty: (" + (row + relativeRow) + ", " + (column + relativeCol) + ", " + rowSpan + ", " + colSpan +
213                   "), component ID=" + old.getId());
214       }
215
216       final GridConstraints constraints = c.getConstraints();
217       constraints.setRow(row + relativeRow);
218       constraints.setColumn(column + relativeCol);
219       constraints.setRowSpan(rowSpan);
220       constraints.setColSpan(colSpan);
221       LOG.info("GridDropLocation.dropIntoGrid() constraints=" + constraints);
222       container.addComponent(c);
223
224       // Fill DropInfo
225       c.revalidate();
226     }
227
228     container.revalidate();
229     LOG.info("GridDropLocation.dropIntoGrid() done");
230   }
231 }