2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.intellij.uiDesigner.radComponents;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.uiDesigner.UIFormXmlConstants;
21 import com.intellij.uiDesigner.XmlWriter;
22 import com.intellij.uiDesigner.compiler.GridBagConverter;
23 import com.intellij.uiDesigner.core.GridConstraints;
24 import com.intellij.uiDesigner.core.Util;
25 import com.intellij.uiDesigner.designSurface.ComponentDropLocation;
26 import com.intellij.uiDesigner.designSurface.FirstComponentInsertLocation;
27 import com.intellij.uiDesigner.propertyInspector.Property;
28 import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
29 import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
30 import com.intellij.uiDesigner.propertyInspector.editors.PrimitiveTypeEditor;
31 import com.intellij.uiDesigner.propertyInspector.properties.AbstractInsetsProperty;
32 import com.intellij.uiDesigner.propertyInspector.properties.AbstractIntProperty;
33 import com.intellij.uiDesigner.propertyInspector.properties.HorzAlignProperty;
34 import com.intellij.uiDesigner.propertyInspector.properties.VertAlignProperty;
35 import com.intellij.uiDesigner.propertyInspector.renderers.LabelPropertyRenderer;
36 import com.intellij.uiDesigner.snapShooter.SnapshotContext;
37 import com.intellij.util.IncorrectOperationException;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
47 public class RadGridBagLayoutManager extends RadAbstractGridLayoutManager {
48 private int myLastSnapshotRow = -1;
49 private int myLastSnapshotCol = -1;
50 private final int[] mySnapshotXMax = new int[512];
51 private final int[] mySnapshotYMax = new int[512];
54 public String getName() {
55 return UIFormXmlConstants.LAYOUT_GRIDBAG;
59 public LayoutManager createLayout() {
60 return new GridBagLayout();
64 public void changeContainerLayout(RadContainer container) throws IncorrectOperationException {
65 if (container.getLayoutManager().isGrid()) {
66 // preprocess: store weights in GridBagConstraints
67 RadAbstractGridLayoutManager grid = container.getGridLayoutManager();
68 for (RadComponent c : container.getComponents()) {
69 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(c);
70 if (grid.canCellGrow(container, false, c.getConstraints().getColumn())) {
73 if (grid.canCellGrow(container, true, c.getConstraints().getRow())) {
76 c.setCustomLayoutConstraints(gbc);
79 super.changeContainerLayout(container);
82 public void writeChildConstraints(final XmlWriter writer, final RadComponent child) {
83 writeGridConstraints(writer, child);
84 if (child.getCustomLayoutConstraints() instanceof GridBagConstraints) {
85 GridBagConstraints gbc = (GridBagConstraints)child.getCustomLayoutConstraints();
86 writer.startElement(UIFormXmlConstants.ELEMENT_GRIDBAG);
88 if (!gbc.insets.equals(new Insets(0, 0, 0, 0))) {
89 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TOP, gbc.insets.top);
90 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_LEFT, gbc.insets.left);
91 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BOTTOM, gbc.insets.bottom);
92 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_RIGHT, gbc.insets.right);
94 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTX, gbc.weightx);
95 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTY, gbc.weighty);
97 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADX, gbc.ipadx);
100 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADY, gbc.ipady);
110 public void addComponentToContainer(final RadContainer container, final RadComponent component, final int index) {
111 super.addComponentToContainer(container, component, index);
112 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
113 component.setCustomLayoutConstraints(gbc);
114 container.getDelegee().add(component.getDelegee(), gbc, index);
118 public void refresh(RadContainer container) {
119 checkEmptyCells(container);
122 private static final int MINIMUM_GRID_SIZE = 15;
124 private static void checkEmptyCells(RadContainer container) {
125 JComponent jComponent = container.getDelegee();
126 GridBagLayout layout = (GridBagLayout)jComponent.getLayout();
127 Dimension oldSize = jComponent.getSize();
130 layout.columnWidths = null;
131 layout.rowHeights = null;
133 // calculate layout fields
134 jComponent.setSize(500, 400);
135 jComponent.doLayout();
136 jComponent.setSize(oldSize);
138 int[][] dimensions = layout.getLayoutDimensions();
140 // check empty columns
141 int[] columnWidths = dimensions[0];
142 boolean doLayoutColumns = false;
144 for (int i = 0; i < columnWidths.length; i++) {
145 if (columnWidths[i] == 0) {
146 columnWidths[i] = MINIMUM_GRID_SIZE;
147 doLayoutColumns = true;
152 int[] rowHeights = dimensions[1];
153 boolean doLayoutRows = false;
155 for (int i = 0; i < rowHeights.length; i++) {
156 if (rowHeights[i] == 0) {
157 rowHeights[i] = MINIMUM_GRID_SIZE;
163 if (doLayoutColumns) {
164 layout.columnWidths = columnWidths;
167 layout.rowHeights = rowHeights;
169 if (doLayoutColumns || doLayoutRows) {
170 jComponent.doLayout();
175 public Property[] getContainerProperties(final Project project) {
176 return Property.EMPTY_ARRAY;
180 public Property[] getComponentProperties(final Project project, final RadComponent component) {
181 return new Property[]{
182 new HorzAlignProperty(),
183 new VertAlignProperty(),
184 new ComponentInsetsProperty(),
185 new WeightProperty(true),
186 new WeightProperty(false),
187 new IPadProperty(true),
188 new IPadProperty(false)
192 private static GridBagLayout getGridBag(RadContainer container) {
193 return (GridBagLayout)container.getLayout();
197 public int getGridRowCount(RadContainer container) {
198 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
199 return layoutDimensions[1].length;
203 public int getGridColumnCount(RadContainer container) {
204 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
205 return layoutDimensions[0].length;
209 public int[] getHorizontalGridLines(RadContainer container) {
210 return getGridLines(container, 1, 1);
214 public int[] getVerticalGridLines(RadContainer container) {
215 return getGridLines(container, 0, 1);
219 public int[] getGridCellCoords(RadContainer container, boolean isRow) {
220 return getGridLines(container, isRow ? 1 : 0, 0);
224 public int[] getGridCellSizes(RadContainer container, boolean isRow) {
225 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
226 return layoutDimensions[isRow ? 1 : 0];
229 private static int[] getGridLines(final RadContainer container, final int rowColIndex, final int delta) {
230 final GridBagLayout gridBag = getGridBag(container);
231 Point layoutOrigin = gridBag.getLayoutOrigin();
232 int[][] layoutDimensions = gridBag.getLayoutDimensions();
233 int[] result = new int[layoutDimensions[rowColIndex].length + delta];
234 if (result.length > 0) {
235 result[0] = (rowColIndex == 0) ? layoutOrigin.x : layoutOrigin.y;
236 for (int i = 1; i < result.length; i++) {
237 result[i] = result[i - 1] + layoutDimensions[rowColIndex][i - 1];
245 public ComponentDropLocation getDropLocation(@NotNull RadContainer container, @Nullable final Point location) {
246 if (getGridRowCount(container) == 0 && getGridColumnCount(container) == 0) {
247 return new FirstComponentInsertLocation(container, new Rectangle(0, 0, container.getWidth(), container.getHeight()), 0, 0);
249 return super.getDropLocation(container, location);
252 public void copyGridSection(final RadContainer source, final RadContainer destination, final Rectangle rc) {
253 destination.setLayout(new GridBagLayout());
257 protected void updateConstraints(final RadComponent component) {
258 GridBagLayout layout = (GridBagLayout)component.getParent().getLayout();
259 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
260 layout.setConstraints(component.getDelegee(), gbc);
261 super.updateConstraints(component);
265 public boolean isGridDefinedByComponents() {
270 public boolean canResizeCells() {
275 public boolean canCellGrow(RadContainer container, boolean isRow, int i) {
276 GridBagLayout gridBag = getGridBag(container);
277 double[][] weights = gridBag.getLayoutWeights();
278 if (weights != null) {
279 double[] cellWeights = weights[isRow ? 1 : 0];
280 return i >= 0 && i < cellWeights.length && cellWeights[i] >= 0.1;
286 public void setChildDragging(RadComponent child, boolean dragging) {
287 // do nothing here - setting visible to false would cause exceptions
291 public void createSnapshotLayout(final SnapshotContext context,
292 final JComponent parent,
293 final RadContainer container,
294 final LayoutManager layout) {
295 container.setLayout(new GridBagLayout());
298 public static Dimension getGridBagSize(final JComponent parent) {
299 GridBagLayout gridBag = (GridBagLayout)parent.getLayout();
300 gridBag.layoutContainer(parent);
301 int[][] layoutDimensions = gridBag.getLayoutDimensions();
303 int rowCount = layoutDimensions[1].length;
304 int colCount = layoutDimensions[0].length;
306 // account for invisible components
307 for (Component component : parent.getComponents()) {
308 final GridBagConstraints constraints = gridBag.getConstraints(component);
309 colCount = Math.max(colCount, constraints.gridx + constraints.gridwidth);
310 rowCount = Math.max(rowCount, constraints.gridy + constraints.gridheight);
313 return new Dimension(colCount, rowCount);
317 public void addSnapshotComponent(final JComponent parent,
318 final JComponent child,
319 final RadContainer container,
320 final RadComponent component) {
321 Dimension gridBagSize = getGridBagSize(parent);
323 // logic copied from GridBagLayout.java
325 GridBagLayout gridBag = (GridBagLayout)parent.getLayout();
326 final GridBagConstraints constraints = gridBag.getConstraints(child);
328 int curX = constraints.gridx;
329 int curY = constraints.gridy;
330 int curWidth = constraints.gridwidth;
331 int curHeight = constraints.gridheight;
335 /* If x or y is negative, then use relative positioning: */
336 if (curX < 0 && curY < 0) {
337 if (myLastSnapshotRow >= 0) {
338 curY = myLastSnapshotRow;
340 else if (myLastSnapshotCol >= 0) {
341 curX = myLastSnapshotCol;
349 if (curHeight <= 0) {
350 curHeight += gridBagSize.height - curY;
357 for (int i = curY; i < (curY + curHeight); i++) {
358 px = Math.max(px, mySnapshotXMax[i]);
361 curX = px - curX - 1;
368 curWidth += gridBagSize.width - curX;
375 for (int i = curX; i < (curX + curWidth); i++) {
376 py = Math.max(py, mySnapshotYMax[i]);
379 curY = py - curY - 1;
386 curWidth += gridBagSize.width - curX;
392 if (curHeight <= 0) {
393 curHeight += gridBagSize.height - curY;
399 /* Adjust xMax and yMax */
400 for (int i = curX; i < (curX + curWidth); i++) {
401 mySnapshotYMax[i] = curY + curHeight;
403 for (int i = curY; i < (curY + curHeight); i++) {
404 mySnapshotXMax[i] = curX + curWidth;
407 /* Make negative sizes start a new row/column */
408 if (constraints.gridheight == 0 && constraints.gridwidth == 0) {
409 myLastSnapshotRow = myLastSnapshotCol = -1;
411 if (constraints.gridheight == 0 && myLastSnapshotRow < 0) {
412 myLastSnapshotCol = curX + curWidth;
414 else if (constraints.gridwidth == 0 && myLastSnapshotCol < 0) {
415 myLastSnapshotRow = curY + curHeight;
418 component.getConstraints().setColumn(curX);
419 component.getConstraints().setRow(curY);
420 component.getConstraints().setColSpan(curWidth);
421 component.getConstraints().setRowSpan(curHeight);
423 if (constraints.weightx >= 1.0) {
424 component.getConstraints().setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
427 component.getConstraints().setHSizePolicy(0);
429 if (constraints.weighty >= 1.0) {
430 component.getConstraints().setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
433 component.getConstraints().setVSizePolicy(0);
435 if (constraints.insets.right == 0 && constraints.insets.top == 0 && constraints.insets.bottom == 0) {
436 component.getConstraints().setIndent(constraints.insets.left / Util.DEFAULT_INDENT);
439 component.getConstraints().setAnchor(convertAnchor(constraints));
440 component.getConstraints().setFill(convertFill(constraints));
441 component.setCustomLayoutConstraints(constraints.clone());
442 container.addComponent(component);
445 private static int convertAnchor(final GridBagConstraints gbc) {
446 switch (gbc.anchor) {
447 case GridBagConstraints.NORTHWEST:
448 return GridConstraints.ANCHOR_NORTHWEST;
449 case GridBagConstraints.NORTH:
450 return GridConstraints.ANCHOR_NORTH;
451 case GridBagConstraints.NORTHEAST:
452 return GridConstraints.ANCHOR_NORTHEAST;
453 case GridBagConstraints.EAST:
454 return GridConstraints.ANCHOR_EAST;
455 case GridBagConstraints.SOUTHEAST:
456 return GridConstraints.ANCHOR_SOUTHEAST;
457 case GridBagConstraints.SOUTH:
458 return GridConstraints.ANCHOR_SOUTH;
459 case GridBagConstraints.SOUTHWEST:
460 return GridConstraints.ANCHOR_SOUTHWEST;
462 return GridConstraints.ANCHOR_WEST;
466 private static int convertFill(final GridBagConstraints gbc) {
468 case GridBagConstraints.HORIZONTAL:
469 return GridConstraints.FILL_HORIZONTAL;
470 case GridBagConstraints.VERTICAL:
471 return GridConstraints.FILL_VERTICAL;
472 case GridBagConstraints.BOTH:
473 return GridConstraints.FILL_BOTH;
475 return GridConstraints.FILL_NONE;
479 private static class ComponentInsetsProperty extends AbstractInsetsProperty<RadComponent> {
480 public ComponentInsetsProperty() {
481 super(null, "Insets");
484 public Insets getValue(final RadComponent component) {
485 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
486 final GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
489 return new Insets(0, 0, 0, 0);
492 protected void setValueImpl(final RadComponent component, final Insets value) throws Exception {
493 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
494 final GridBagConstraints cellConstraints = (GridBagConstraints)component.getCustomLayoutConstraints();
495 cellConstraints.insets = value;
497 GridBagLayout layout = (GridBagLayout)component.getParent().getLayout();
498 GridBagConstraints gbc = (GridBagConstraints)layout.getConstraints(component.getDelegee()).clone();
500 layout.setConstraints(component.getDelegee(), gbc);
505 public boolean isModified(final RadComponent component) {
506 return !getValue(component).equals(new Insets(0, 0, 0, 0));
510 public void resetValue(final RadComponent component) throws Exception {
511 setValue(component, new Insets(0, 0, 0, 0));
515 private static class WeightProperty extends Property<RadComponent, Double> {
516 private final boolean myIsWeightX;
517 private LabelPropertyRenderer<Double> myRenderer;
518 private PropertyEditor<Double> myEditor;
520 public WeightProperty(final boolean isWeightX) {
521 super(null, isWeightX ? "Weight X" : "Weight Y");
522 myIsWeightX = isWeightX;
525 public Double getValue(final RadComponent component) {
526 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
527 GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
528 return myIsWeightX ? gbc.weightx : gbc.weighty;
533 protected void setValueImpl(final RadComponent component, final Double value) throws Exception {
534 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
535 GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
537 gbc.weightx = value.doubleValue();
540 gbc.weighty = value.doubleValue();
542 ((GridBagLayout)component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);
547 public PropertyRenderer<Double> getRenderer() {
548 if (myRenderer == null) {
549 myRenderer = new LabelPropertyRenderer<Double>();
554 public PropertyEditor<Double> getEditor() {
555 if (myEditor == null) {
556 myEditor = new PrimitiveTypeEditor<Double>(Double.class);
562 public boolean isModified(final RadComponent component) {
563 return !(new Double(0.0).equals(getValue(component)));
567 public void resetValue(final RadComponent component) throws Exception {
568 setValue(component, 0.0);
572 private static class IPadProperty extends AbstractIntProperty<RadComponent> {
573 private final boolean myIsIpadX;
575 public IPadProperty(final boolean isIpadX) {
576 super(null, isIpadX ? "Ipad X" : "Ipad Y", 0);
580 public Integer getValue(final RadComponent component) {
581 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
582 GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
583 return myIsIpadX ? gbc.ipadx : gbc.ipady;
588 protected void setValueImpl(final RadComponent component, final Integer value) throws Exception {
589 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
590 GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
592 gbc.ipadx = value.intValue();
595 gbc.ipady = value.intValue();
597 ((GridBagLayout)component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);