IDEA-45629
[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     int colSpan = 1; // allow drop any (NxM) component to cell (1x1)
67     int rowSpan = 1;
68
69     for(int i=0; i<dragObject.getComponentCount(); i++) {
70       int relativeCol = dragObject.getRelativeCol(i);
71       int relativeRow = dragObject.getRelativeRow(i);
72
73       LOG.debug("checking component: relativeRow" + relativeRow + ", relativeCol" + relativeCol + ", colSpan=" + colSpan + ", rowSpan=" + rowSpan);
74
75       if (myRow + relativeRow < 0 ||
76           myColumn + relativeCol < 0 ||
77           myRow + relativeRow + rowSpan > myContainer.getGridRowCount() ||
78           myColumn + relativeCol + colSpan > myContainer.getGridColumnCount()) {
79         LOG.debug("RadContainer.canDrop=false because range is outside grid: row=" + (myRow +relativeRow) +
80                   ", col=" + (myColumn +relativeCol) + ", colSpan=" + colSpan + ", rowSpan=" + rowSpan);
81         return false;
82       }
83
84       final RadComponent componentInRect = findOverlappingComponent(myRow + relativeRow, myColumn + relativeCol, rowSpan, colSpan);
85       if (componentInRect != null) {
86         LOG.debug("GridDropLocation.canDrop=false because found component " + componentInRect.getId() +
87                   " in rect (row=" + (myRow +relativeRow) + ", col=" + (myColumn +relativeCol) +
88                   ", rowSpan=" + rowSpan + ", colSpan=" + colSpan + ")");
89         return false;
90       }
91     }
92     LOG.debug("canDrop=true");
93     return true;
94   }
95
96   protected RadComponent findOverlappingComponent(final int startRow, final int startCol, final int rowSpan, final int colSpan) {
97     return myContainer.findComponentInRect(startRow, startCol, rowSpan, colSpan);
98   }
99
100   public void placeFeedback(FeedbackLayer feedbackLayer, ComponentDragObject dragObject) {
101     Rectangle feedbackRect = null;
102     if (getContainer().getLayoutManager().isGrid()) {
103       feedbackRect = getGridFeedbackRect(dragObject, false, false, true);
104     }
105     if (feedbackRect != null) {
106       final JComponent component = getContainer().getDelegee();
107       StringBuilder feedbackBuilder = new StringBuilder(getContainer().getDisplayName());
108       feedbackBuilder.append(" (").append(myRow + getContainer().getGridLayoutManager().getCellIndexBase());
109       feedbackBuilder.append(", ").append(myColumn + getContainer().getGridLayoutManager().getCellIndexBase());
110       feedbackBuilder.append(")");
111       feedbackLayer.putFeedback(component, feedbackRect, feedbackBuilder.toString());
112     }
113     else {
114       feedbackLayer.removeFeedback();
115     }
116   }
117
118   @Nullable
119   protected Rectangle getGridFeedbackCellRect(ComponentDragObject dragObject,
120                                               boolean ignoreWidth,
121                                               boolean ignoreHeight,
122                                               boolean overlapping) {
123     if (dragObject.getComponentCount() == 0) {
124       return null;
125     }
126
127     Rectangle rc = calculateGridFeedbackCellRect(dragObject, ignoreWidth, ignoreHeight, true);
128
129     if (rc == null) {
130       return calculateGridFeedbackCellRect(dragObject, ignoreWidth, ignoreHeight, false);
131     }
132
133     if (overlapping && findOverlappingComponent(rc.y, rc.x, rc.height + 1, rc.width + 1) != null) {
134      return calculateGridFeedbackCellRect(dragObject, ignoreWidth, ignoreHeight, false);
135     }
136
137     return rc;
138   }
139
140   @Nullable
141   private Rectangle calculateGridFeedbackCellRect(ComponentDragObject dragObject,
142                                              boolean ignoreWidth,
143                                              boolean ignoreHeight,
144                                              boolean spans) {
145     Rectangle rc = getDragObjectDimensions(dragObject, spans);
146     int w = ignoreWidth ? 1 : rc.width;
147     int h = ignoreHeight ? 1 : rc.height;
148
149     if (rc.x < 0 || rc.y < 0 || rc.y + h > getContainer().getGridRowCount() || rc.x + w > getContainer().getGridColumnCount()) {
150       return null;
151     }
152     
153     return new Rectangle(rc.x, rc.y, w - 1, h - 1);
154   }
155
156   protected Rectangle getDragObjectDimensions(ComponentDragObject dragObject, boolean spans) {
157     int firstRow = getRow();
158     int lastRow = getRow();
159     int firstCol = getColumn();
160     int lastCol = getColumn();
161     for (int i = 0; i < dragObject.getComponentCount(); i++) {
162       int relRow = dragObject.getRelativeRow(i);
163       int relCol = dragObject.getRelativeCol(i);
164       firstRow = Math.min(firstRow, getRow() + relRow);
165       firstCol = Math.min(firstCol, getColumn() + relCol);
166       lastRow = Math.max(lastRow, getRow() + relRow + (spans ? dragObject.getRowSpan(i) : 1) - 1);
167       lastCol = Math.max(lastCol, getColumn() + relCol + (spans ? dragObject.getColSpan(i) : 1) - 1);
168     }
169     return new Rectangle(firstCol, firstRow, lastCol - firstCol + 1, lastRow - firstRow + 1);
170   }
171
172   @Nullable
173   protected Rectangle getGridFeedbackRect(ComponentDragObject dragObject, boolean ignoreWidth, boolean ignoreHeight, boolean overlapping) {
174     Rectangle cellRect = getGridFeedbackCellRect(dragObject, ignoreWidth, ignoreHeight, overlapping);
175     if (cellRect == null) return null;
176     int h = ignoreHeight ? 0 : cellRect.height;
177     int w = ignoreWidth ? 0 : cellRect.width;
178     return getContainer().getGridLayoutManager().getGridCellRangeRect(getContainer(), cellRect.y, cellRect.x,
179                                                                       cellRect.y+h, cellRect.x+w);
180   }
181
182   public void processDrop(final GuiEditor editor,
183                           final RadComponent[] components,
184                           final GridConstraints[] constraintsToAdjust,
185                           final ComponentDragObject dragObject) {
186     dropIntoGrid(myContainer, components, myRow, myColumn, dragObject);
187   }
188
189   @Nullable
190   public ComponentDropLocation getAdjacentLocation(Direction direction) {
191     switch(direction) {
192       case LEFT:  return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.ColumnBefore);
193       case UP:    return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.RowBefore);
194       case RIGHT: return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.ColumnAfter);
195       case DOWN:  return new GridInsertLocation(myContainer, myRow, myColumn, GridInsertMode.RowAfter); 
196     }
197     return null;
198   }
199
200   @NonNls @Override public String toString() {
201     return "GridDropLocation(row=" + myRow + ",col=" + myColumn + ")";
202   }
203
204   protected static void dropIntoGrid(final RadContainer container, final RadComponent[] components, int row, int column, final ComponentDragObject dragObject) {
205     assert components.length > 0;
206
207     for(int i=0; i<components.length; i++) {
208       RadComponent c = components [i];
209       if (c instanceof RadContainer) {
210         final LayoutManager layout = ((RadContainer)c).getLayout();
211         if (layout instanceof XYLayoutManager) {
212           ((XYLayoutManager)layout).setPreferredSize(c.getSize());
213         }
214       }
215
216       int relativeCol = dragObject.getRelativeCol(i);
217       int relativeRow = dragObject.getRelativeRow(i);
218       LOG.debug("dropIntoGrid: relativeRow=" + relativeRow + ", relativeCol=" + relativeCol);
219       int colSpan = dragObject.getColSpan(i);
220       int rowSpan = dragObject.getRowSpan(i);
221
222       assert row + relativeRow >= 0;
223       assert column + relativeCol >= 0;
224
225       if (rowSpan > 1 || colSpan > 1) {
226         if ((row + relativeRow + rowSpan > container.getGridRowCount() && rowSpan > 1) ||
227             (column + relativeCol + colSpan > container.getGridColumnCount() && colSpan > 1) ||
228             container.findComponentInRect(row + relativeRow, column + relativeCol, rowSpan, colSpan) != null) {
229           rowSpan = 1;
230           colSpan = 1;
231         }
232       }
233
234       if (!container.getGridLayoutManager().isGridDefinedByComponents()) {
235         assert relativeRow + rowSpan <= container.getGridRowCount();
236         assert relativeCol + colSpan <= container.getGridColumnCount();
237       }
238
239       RadComponent old = container.findComponentInRect(row + relativeRow, column + relativeCol, rowSpan, colSpan);
240       if (old != null) {
241         LOG.error("Drop rectangle not empty: (" + (row + relativeRow) + ", " + (column + relativeCol) + ", " + rowSpan + ", " + colSpan +
242                   "), component ID=" + old.getId());
243       }
244
245       final GridConstraints constraints = c.getConstraints();
246       constraints.setRow(row + relativeRow);
247       constraints.setColumn(column + relativeCol);
248       constraints.setRowSpan(rowSpan);
249       constraints.setColSpan(colSpan);
250       LOG.info("GridDropLocation.dropIntoGrid() constraints=" + constraints);
251       container.addComponent(c);
252
253       // Fill DropInfo
254       c.revalidate();
255     }
256
257     container.revalidate();
258     LOG.info("GridDropLocation.dropIntoGrid() done");
259   }
260 }