update copyright
[fedora-idea.git] / plugins / ui-designer / src / com / intellij / uiDesigner / radComponents / RadGridBagLayoutManager.java
blobdf00ab50322633b01485acf51f3ac5d34785d18f
1 /*
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;
41 import javax.swing.*;
42 import java.awt.*;
43 import java.beans.PropertyChangeEvent;
44 import java.beans.PropertyChangeListener;
45 import java.util.HashMap;
46 import java.util.Map;
48 /**
49 * @author yole
51 public class RadGridBagLayoutManager extends RadAbstractGridLayoutManager {
52 private int myLastSnapshotRow = -1;
53 private int myLastSnapshotCol = -1;
54 private final int[] mySnapshotXMax = new int[512];
55 private final int[] mySnapshotYMax = new int[512];
56 private final Map<RadComponent, MyPropertyChangeListener> myListenerMap = new HashMap<RadComponent, MyPropertyChangeListener>();
58 @Override public String getName() {
59 return UIFormXmlConstants.LAYOUT_GRIDBAG;
62 @Override
63 public LayoutManager createLayout() {
64 return new GridBagLayout();
67 @Override
68 public void changeContainerLayout(RadContainer container) throws IncorrectOperationException {
69 if (container.getLayoutManager().isGrid()) {
70 // preprocess: store weights in GridBagConstraints
71 RadAbstractGridLayoutManager grid = container.getGridLayoutManager();
72 for(RadComponent c: container.getComponents()) {
73 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(c);
74 if (grid.canCellGrow(container, false, c.getConstraints().getColumn())) {
75 gbc.weightx = 1.0;
77 if (grid.canCellGrow(container, true, c.getConstraints().getRow())) {
78 gbc.weighty = 1.0;
80 c.setCustomLayoutConstraints(gbc);
83 super.changeContainerLayout(container);
86 public void writeChildConstraints(final XmlWriter writer, final RadComponent child) {
87 writeGridConstraints(writer, child);
88 if (child.getCustomLayoutConstraints() instanceof GridBagConstraints) {
89 GridBagConstraints gbc = (GridBagConstraints) child.getCustomLayoutConstraints();
90 writer.startElement(UIFormXmlConstants.ELEMENT_GRIDBAG);
91 try {
92 if (!gbc.insets.equals(new Insets(0, 0, 0, 0))) {
93 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TOP, gbc.insets.top);
94 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_LEFT, gbc.insets.left);
95 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BOTTOM, gbc.insets.bottom);
96 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_RIGHT, gbc.insets.right);
98 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTX, gbc.weightx);
99 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_WEIGHTY, gbc.weighty);
100 if (gbc.ipadx != 0) {
101 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADX, gbc.ipadx);
103 if (gbc.ipady != 0) {
104 writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_IPADY, gbc.ipady);
107 finally {
108 writer.endElement();
113 @Override
114 public void addComponentToContainer(final RadContainer container, final RadComponent component, final int index) {
115 MyPropertyChangeListener listener = new MyPropertyChangeListener(component);
116 myListenerMap.put(component, listener);
117 component.addPropertyChangeListener(listener);
118 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
119 component.setCustomLayoutConstraints(gbc);
120 container.getDelegee().add(component.getDelegee(), gbc, index);
123 @Override
124 public void removeComponentFromContainer(final RadContainer container, final RadComponent component) {
125 final MyPropertyChangeListener listener = myListenerMap.get(component);
126 if (listener != null) {
127 component.removePropertyChangeListener(listener);
128 myListenerMap.remove(component);
130 super.removeComponentFromContainer(container, component);
133 @Override
134 public Property[] getContainerProperties(final Project project) {
135 return Property.EMPTY_ARRAY;
138 @Override
139 public Property[] getComponentProperties(final Project project, final RadComponent component) {
140 return new Property[] {
141 new HorzAlignProperty(),
142 new VertAlignProperty(),
143 new ComponentInsetsProperty(),
144 new WeightProperty(true),
145 new WeightProperty(false),
146 new IPadProperty(true),
147 new IPadProperty(false)
151 private static GridBagLayout getGridBag(RadContainer container) {
152 return (GridBagLayout) container.getLayout();
155 @Override
156 public int getGridRowCount(RadContainer container) {
157 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
158 return layoutDimensions [1].length;
161 @Override
162 public int getGridColumnCount(RadContainer container) {
163 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
164 return layoutDimensions [0].length;
167 @Override
168 public int[] getHorizontalGridLines(RadContainer container) {
169 return getGridLines(container, 1, 1);
172 @Override
173 public int[] getVerticalGridLines(RadContainer container) {
174 return getGridLines(container, 0, 1);
177 @Override
178 public int[] getGridCellCoords(RadContainer container, boolean isRow) {
179 return getGridLines(container, isRow ? 1 : 0, 0);
182 @Override
183 public int[] getGridCellSizes(RadContainer container, boolean isRow) {
184 int[][] layoutDimensions = getGridBag(container).getLayoutDimensions();
185 return layoutDimensions [isRow ? 1 : 0];
188 private static int[] getGridLines(final RadContainer container, final int rowColIndex, final int delta) {
189 final GridBagLayout gridBag = getGridBag(container);
190 Point layoutOrigin = gridBag.getLayoutOrigin();
191 int[][] layoutDimensions = gridBag.getLayoutDimensions();
192 int[] result = new int [layoutDimensions [rowColIndex].length+delta];
193 if (result.length > 0) {
194 result [0] = (rowColIndex == 0) ? layoutOrigin.x : layoutOrigin.y;
195 for(int i=1; i<result.length; i++) {
196 result [i] = result [i-1]+layoutDimensions [rowColIndex] [i-1];
199 return result;
202 @NotNull @Override
203 public ComponentDropLocation getDropLocation(@NotNull RadContainer container, @Nullable final Point location) {
204 if (getGridRowCount(container) == 0 && getGridColumnCount(container) == 0) {
205 return new FirstComponentInsertLocation(container, container.getBounds(), 0, 0);
207 return super.getDropLocation(container, location);
210 public void copyGridSection(final RadContainer source, final RadContainer destination, final Rectangle rc) {
211 destination.setLayout(new GridBagLayout());
214 private static void updateConstraints(final RadComponent component) {
215 GridBagLayout layout = (GridBagLayout) component.getParent().getLayout();
216 GridBagConstraints gbc = GridBagConverter.getGridBagConstraints(component);
217 layout.setConstraints(component.getDelegee(), gbc);
218 component.getParent().revalidate();
221 @Override
222 public boolean isGridDefinedByComponents() {
223 return true;
226 @Override
227 public boolean canResizeCells() {
228 return false;
231 @Override
232 public boolean canCellGrow(RadContainer container, boolean isRow, int i) {
233 GridBagLayout gridBag = getGridBag(container);
234 double[][] weights = gridBag.getLayoutWeights();
235 if (weights != null) {
236 double[] cellWeights = weights [isRow ? 1 : 0];
237 return i >= 0 && i < cellWeights.length && cellWeights [i] >= 0.1;
239 return false;
242 @Override
243 public void setChildDragging(RadComponent child, boolean dragging) {
244 // do nothing here - setting visible to false would cause exceptions
247 @Override
248 public void createSnapshotLayout(final SnapshotContext context,
249 final JComponent parent,
250 final RadContainer container,
251 final LayoutManager layout) {
252 container.setLayout(new GridBagLayout());
255 public static Dimension getGridBagSize(final JComponent parent) {
256 GridBagLayout gridBag = (GridBagLayout) parent.getLayout();
257 gridBag.layoutContainer(parent);
258 int[][] layoutDimensions = gridBag.getLayoutDimensions();
260 int rowCount = layoutDimensions[1].length;
261 int colCount = layoutDimensions[0].length;
263 // account for invisible components
264 for(Component component: parent.getComponents()) {
265 final GridBagConstraints constraints = gridBag.getConstraints(component);
266 colCount = Math.max(colCount, constraints.gridx + constraints.gridwidth);
267 rowCount = Math.max(rowCount, constraints.gridy + constraints.gridheight);
270 return new Dimension(colCount, rowCount);
273 @Override
274 public void addSnapshotComponent(final JComponent parent,
275 final JComponent child,
276 final RadContainer container,
277 final RadComponent component) {
278 Dimension gridBagSize = getGridBagSize(parent);
280 // logic copied from GridBagLayout.java
282 GridBagLayout gridBag = (GridBagLayout) parent.getLayout();
283 final GridBagConstraints constraints = gridBag.getConstraints(child);
285 int curX = constraints.gridx;
286 int curY = constraints.gridy;
287 int curWidth = constraints.gridwidth;
288 int curHeight = constraints.gridheight;
289 int px;
290 int py;
292 /* If x or y is negative, then use relative positioning: */
293 if (curX < 0 && curY < 0) {
294 if(myLastSnapshotRow >= 0)
295 curY = myLastSnapshotRow;
296 else if(myLastSnapshotCol >= 0)
297 curX = myLastSnapshotCol;
298 else
299 curY = 0;
302 if (curX < 0) {
303 if (curHeight <= 0) {
304 curHeight += gridBagSize.height - curY;
305 if (curHeight < 1)
306 curHeight = 1;
309 px = 0;
310 for (int i = curY; i < (curY + curHeight); i++)
311 px = Math.max(px, mySnapshotXMax[i]);
313 curX = px - curX - 1;
314 if(curX < 0)
315 curX = 0;
317 else if (curY < 0) {
318 if (curWidth <= 0) {
319 curWidth += gridBagSize.width - curX;
320 if (curWidth < 1)
321 curWidth = 1;
324 py = 0;
325 for (int i = curX; i < (curX + curWidth); i++)
326 py = Math.max(py, mySnapshotYMax[i]);
328 curY = py - curY - 1;
329 if(curY < 0)
330 curY = 0;
333 if (curWidth <= 0) {
334 curWidth += gridBagSize.width - curX;
335 if (curWidth < 1)
336 curWidth = 1;
339 if (curHeight <= 0) {
340 curHeight += gridBagSize.height - curY;
341 if (curHeight < 1)
342 curHeight = 1;
345 /* Adjust xMax and yMax */
346 for (int i = curX; i < (curX + curWidth); i++) {
347 mySnapshotYMax[i] = curY + curHeight;
349 for (int i = curY; i < (curY + curHeight); i++) {
350 mySnapshotXMax[i] = curX + curWidth;
353 /* Make negative sizes start a new row/column */
354 if (constraints.gridheight == 0 && constraints.gridwidth == 0)
355 myLastSnapshotRow = myLastSnapshotCol = -1;
356 if (constraints.gridheight == 0 && myLastSnapshotRow < 0)
357 myLastSnapshotCol = curX + curWidth;
358 else if (constraints.gridwidth == 0 && myLastSnapshotCol < 0)
359 myLastSnapshotRow = curY + curHeight;
361 component.getConstraints().setColumn(curX);
362 component.getConstraints().setRow(curY);
363 component.getConstraints().setColSpan(curWidth);
364 component.getConstraints().setRowSpan(curHeight);
366 if (constraints.weightx >= 1.0) {
367 component.getConstraints().setHSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
369 else {
370 component.getConstraints().setHSizePolicy(0);
372 if (constraints.weighty >= 1.0) {
373 component.getConstraints().setVSizePolicy(GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW);
375 else {
376 component.getConstraints().setVSizePolicy(0);
378 if (constraints.insets.right == 0 && constraints.insets.top == 0 && constraints.insets.bottom == 0) {
379 component.getConstraints().setIndent(constraints.insets.left / Util.DEFAULT_INDENT);
382 component.getConstraints().setAnchor(convertAnchor(constraints));
383 component.getConstraints().setFill(convertFill(constraints));
384 component.setCustomLayoutConstraints(constraints.clone());
385 container.addComponent(component);
388 private static int convertAnchor(final GridBagConstraints gbc) {
389 switch(gbc.anchor) {
390 case GridBagConstraints.NORTHWEST: return GridConstraints.ANCHOR_NORTHWEST;
391 case GridBagConstraints.NORTH: return GridConstraints.ANCHOR_NORTH;
392 case GridBagConstraints.NORTHEAST: return GridConstraints.ANCHOR_NORTHEAST;
393 case GridBagConstraints.EAST: return GridConstraints.ANCHOR_EAST;
394 case GridBagConstraints.SOUTHEAST: return GridConstraints.ANCHOR_SOUTHEAST;
395 case GridBagConstraints.SOUTH: return GridConstraints.ANCHOR_SOUTH;
396 case GridBagConstraints.SOUTHWEST: return GridConstraints.ANCHOR_SOUTHWEST;
397 default: return GridConstraints.ANCHOR_WEST;
401 private static int convertFill(final GridBagConstraints gbc) {
402 switch(gbc.fill) {
403 case GridBagConstraints.HORIZONTAL: return GridConstraints.FILL_HORIZONTAL;
404 case GridBagConstraints.VERTICAL: return GridConstraints.FILL_VERTICAL;
405 case GridBagConstraints.BOTH: return GridConstraints.FILL_BOTH;
406 default: return GridConstraints.FILL_NONE;
410 private static class MyPropertyChangeListener implements PropertyChangeListener {
411 private final RadComponent myComponent;
413 public MyPropertyChangeListener(final RadComponent component) {
414 myComponent = component;
417 public void propertyChange(PropertyChangeEvent evt) {
418 if (evt.getPropertyName().equals(RadComponent.PROP_CONSTRAINTS)) {
419 updateConstraints(myComponent);
424 private static class ComponentInsetsProperty extends AbstractInsetsProperty<RadComponent> {
425 public ComponentInsetsProperty() {
426 super(null, "Insets");
429 public Insets getValue(final RadComponent component) {
430 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
431 final GridBagConstraints gbc = (GridBagConstraints)component.getCustomLayoutConstraints();
432 return gbc.insets;
434 return new Insets(0, 0, 0, 0);
437 protected void setValueImpl(final RadComponent component, final Insets value) throws Exception {
438 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
439 final GridBagConstraints cellConstraints = (GridBagConstraints)component.getCustomLayoutConstraints();
440 cellConstraints.insets = value;
442 GridBagLayout layout = (GridBagLayout) component.getParent().getLayout();
443 GridBagConstraints gbc = (GridBagConstraints)layout.getConstraints(component.getDelegee()).clone();
444 gbc.insets = value;
445 layout.setConstraints(component.getDelegee(), gbc);
449 @Override
450 public boolean isModified(final RadComponent component) {
451 return !getValue(component).equals(new Insets(0, 0, 0, 0));
454 @Override
455 public void resetValue(final RadComponent component) throws Exception {
456 setValue(component, new Insets(0, 0, 0, 0));
460 private static class WeightProperty extends Property<RadComponent, Double> {
461 private final boolean myIsWeightX;
462 private LabelPropertyRenderer<Double> myRenderer;
463 private PropertyEditor<Double> myEditor;
465 public WeightProperty(final boolean isWeightX) {
466 super(null, isWeightX ? "Weight X" : "Weight Y");
467 myIsWeightX = isWeightX;
470 public Double getValue(final RadComponent component) {
471 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
472 GridBagConstraints gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
473 return myIsWeightX ? gbc.weightx : gbc.weighty;
475 return 0.0;
478 protected void setValueImpl(final RadComponent component, final Double value) throws Exception {
479 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
480 GridBagConstraints gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
481 if (myIsWeightX) {
482 gbc.weightx = value.doubleValue();
484 else {
485 gbc.weighty = value.doubleValue();
487 ((GridBagLayout) component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);
491 @NotNull
492 public PropertyRenderer<Double> getRenderer() {
493 if (myRenderer == null) {
494 myRenderer = new LabelPropertyRenderer<Double>();
496 return myRenderer;
499 public PropertyEditor<Double> getEditor() {
500 if (myEditor == null) {
501 myEditor = new PrimitiveTypeEditor<Double>(Double.class);
503 return myEditor;
506 @Override
507 public boolean isModified(final RadComponent component) {
508 return !(new Double(0.0).equals(getValue(component)));
511 @Override
512 public void resetValue(final RadComponent component) throws Exception {
513 setValue(component, 0.0);
517 private static class IPadProperty extends AbstractIntProperty<RadComponent> {
518 private final boolean myIsIpadX;
520 public IPadProperty(final boolean isIpadX) {
521 super(null, isIpadX ? "Ipad X" : "Ipad Y", 0);
522 myIsIpadX = isIpadX;
525 public Integer getValue(final RadComponent component) {
526 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
527 GridBagConstraints gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
528 return myIsIpadX ? gbc.ipadx : gbc.ipady;
530 return 0;
533 protected void setValueImpl(final RadComponent component, final Integer value) throws Exception {
534 if (component.getCustomLayoutConstraints() instanceof GridBagConstraints) {
535 GridBagConstraints gbc = (GridBagConstraints) component.getCustomLayoutConstraints();
536 if (myIsIpadX) {
537 gbc.ipadx = value.intValue();
539 else {
540 gbc.ipady = value.intValue();
542 ((GridBagLayout) component.getParent().getLayout()).setConstraints(component.getDelegee(), gbc);