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
;
43 import java
.beans
.PropertyChangeEvent
;
44 import java
.beans
.PropertyChangeListener
;
45 import java
.util
.HashMap
;
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
;
63 public LayoutManager
createLayout() {
64 return new GridBagLayout();
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())) {
77 if (grid
.canCellGrow(container
, true, c
.getConstraints().getRow())) {
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
);
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
);
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
);
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
);
134 public Property
[] getContainerProperties(final Project project
) {
135 return Property
.EMPTY_ARRAY
;
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();
156 public int getGridRowCount(RadContainer container
) {
157 int[][] layoutDimensions
= getGridBag(container
).getLayoutDimensions();
158 return layoutDimensions
[1].length
;
162 public int getGridColumnCount(RadContainer container
) {
163 int[][] layoutDimensions
= getGridBag(container
).getLayoutDimensions();
164 return layoutDimensions
[0].length
;
168 public int[] getHorizontalGridLines(RadContainer container
) {
169 return getGridLines(container
, 1, 1);
173 public int[] getVerticalGridLines(RadContainer container
) {
174 return getGridLines(container
, 0, 1);
178 public int[] getGridCellCoords(RadContainer container
, boolean isRow
) {
179 return getGridLines(container
, isRow ?
1 : 0, 0);
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];
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();
222 public boolean isGridDefinedByComponents() {
227 public boolean canResizeCells() {
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;
243 public void setChildDragging(RadComponent child
, boolean dragging
) {
244 // do nothing here - setting visible to false would cause exceptions
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
);
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
;
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
;
303 if (curHeight
<= 0) {
304 curHeight
+= gridBagSize
.height
- curY
;
310 for (int i
= curY
; i
< (curY
+ curHeight
); i
++)
311 px
= Math
.max(px
, mySnapshotXMax
[i
]);
313 curX
= px
- curX
- 1;
319 curWidth
+= gridBagSize
.width
- curX
;
325 for (int i
= curX
; i
< (curX
+ curWidth
); i
++)
326 py
= Math
.max(py
, mySnapshotYMax
[i
]);
328 curY
= py
- curY
- 1;
334 curWidth
+= gridBagSize
.width
- curX
;
339 if (curHeight
<= 0) {
340 curHeight
+= gridBagSize
.height
- curY
;
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
);
370 component
.getConstraints().setHSizePolicy(0);
372 if (constraints
.weighty
>= 1.0) {
373 component
.getConstraints().setVSizePolicy(GridConstraints
.SIZEPOLICY_CAN_GROW
| GridConstraints
.SIZEPOLICY_WANT_GROW
);
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
) {
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
) {
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();
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();
445 layout
.setConstraints(component
.getDelegee(), gbc
);
450 public boolean isModified(final RadComponent component
) {
451 return !getValue(component
).equals(new Insets(0, 0, 0, 0));
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
;
478 protected void setValueImpl(final RadComponent component
, final Double value
) throws Exception
{
479 if (component
.getCustomLayoutConstraints() instanceof GridBagConstraints
) {
480 GridBagConstraints gbc
= (GridBagConstraints
) component
.getCustomLayoutConstraints();
482 gbc
.weightx
= value
.doubleValue();
485 gbc
.weighty
= value
.doubleValue();
487 ((GridBagLayout
) component
.getParent().getLayout()).setConstraints(component
.getDelegee(), gbc
);
492 public PropertyRenderer
<Double
> getRenderer() {
493 if (myRenderer
== null) {
494 myRenderer
= new LabelPropertyRenderer
<Double
>();
499 public PropertyEditor
<Double
> getEditor() {
500 if (myEditor
== null) {
501 myEditor
= new PrimitiveTypeEditor
<Double
>(Double
.class);
507 public boolean isModified(final RadComponent component
) {
508 return !(new Double(0.0).equals(getValue(component
)));
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);
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
;
533 protected void setValueImpl(final RadComponent component
, final Integer value
) throws Exception
{
534 if (component
.getCustomLayoutConstraints() instanceof GridBagConstraints
) {
535 GridBagConstraints gbc
= (GridBagConstraints
) component
.getCustomLayoutConstraints();
537 gbc
.ipadx
= value
.intValue();
540 gbc
.ipady
= value
.intValue();
542 ((GridBagLayout
) component
.getParent().getLayout()).setConstraints(component
.getDelegee(), gbc
);