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
.actionSystem
.ActionGroup
;
20 import com
.intellij
.openapi
.actionSystem
.DefaultActionGroup
;
21 import com
.intellij
.openapi
.project
.Project
;
22 import com
.intellij
.uiDesigner
.GridChangeUtil
;
23 import com
.intellij
.uiDesigner
.UIDesignerBundle
;
24 import com
.intellij
.uiDesigner
.UIFormXmlConstants
;
25 import com
.intellij
.uiDesigner
.XmlWriter
;
26 import com
.intellij
.uiDesigner
.actions
.*;
27 import com
.intellij
.uiDesigner
.compiler
.FormLayoutUtils
;
28 import com
.intellij
.uiDesigner
.compiler
.Utils
;
29 import com
.intellij
.uiDesigner
.core
.GridConstraints
;
30 import com
.intellij
.uiDesigner
.designSurface
.*;
31 import com
.intellij
.uiDesigner
.lw
.FormLayoutSerializer
;
32 import com
.intellij
.uiDesigner
.propertyInspector
.Property
;
33 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.AbstractInsetsProperty
;
34 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.AlignPropertyProvider
;
35 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.HorzAlignProperty
;
36 import com
.intellij
.uiDesigner
.propertyInspector
.properties
.VertAlignProperty
;
37 import com
.intellij
.uiDesigner
.snapShooter
.SnapshotContext
;
38 import com
.intellij
.util
.ArrayUtil
;
39 import com
.jgoodies
.forms
.factories
.FormFactory
;
40 import com
.jgoodies
.forms
.layout
.*;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
47 import java
.beans
.PropertyChangeEvent
;
48 import java
.beans
.PropertyChangeListener
;
50 import java
.lang
.reflect
.Method
;
51 import java
.util
.ArrayList
;
52 import java
.util
.HashMap
;
53 import java
.util
.List
;
59 public class RadFormLayoutManager
extends RadAbstractGridLayoutManager
implements AlignPropertyProvider
{
60 private FormLayoutColumnProperties myPropertiesPanel
;
61 private final Map
<RadComponent
, MyPropertyChangeListener
> myListenerMap
= new HashMap
<RadComponent
, MyPropertyChangeListener
>();
63 @NonNls private static final String ENCODED_FORMSPEC_GROW
= "d:grow";
64 private static final Size DEFAULT_NOGROW_SIZE
= new BoundedSize(Sizes
.DEFAULT
, new ConstantSize(4, ConstantSize
.PIXEL
), null);
66 @Nullable public String
getName() {
67 return UIFormXmlConstants
.LAYOUT_FORM
;
71 public LayoutManager
createLayout() {
72 return new FormLayout(ENCODED_FORMSPEC_GROW
, ENCODED_FORMSPEC_GROW
);
76 protected void changeLayoutFromGrid(final RadContainer container
, final List
<RadComponent
> contents
, final List
<Boolean
> canRowsGrow
,
77 final List
<Boolean
> canColumnsGrow
) {
78 int rowCount
= canRowsGrow
.size();
79 int columnCount
= canColumnsGrow
.size();
80 int rowCountWithGaps
= (rowCount
== 0) ?
0 : rowCount
* 2 - 1;
81 int columnCountWithGaps
= (columnCount
== 0) ?
0 : columnCount
* 2 - 1;
82 RowSpec
[] rowSpecs
= new RowSpec
[rowCountWithGaps
];
83 ColumnSpec
[] colSpecs
= new ColumnSpec
[columnCountWithGaps
];
85 for(int i
=0; i
<rowCount
; i
++) {
86 rowSpecs
[i
*2] = canRowsGrow
.get(i
).booleanValue() ?
new RowSpec(ENCODED_FORMSPEC_GROW
) : new RowSpec(DEFAULT_NOGROW_SIZE
);
87 if (i
*2+1 < rowSpecs
.length
) {
88 rowSpecs
[i
*2+1] = FormFactory
.RELATED_GAP_ROWSPEC
;
91 for(int i
=0; i
<columnCount
; i
++) {
92 colSpecs
[i
*2] = canColumnsGrow
.get(i
).booleanValue() ?
new ColumnSpec(ENCODED_FORMSPEC_GROW
) : new ColumnSpec(DEFAULT_NOGROW_SIZE
);
93 if (i
*2+1 < colSpecs
.length
) {
94 colSpecs
[i
*2+1] = FormFactory
.RELATED_GAP_COLSPEC
;
98 container
.setLayoutManager(this, new FormLayout(colSpecs
, rowSpecs
));
102 protected void changeLayoutFromIndexed(final RadContainer container
, final List
<RadComponent
> components
) {
103 int maxSizePolicy
= 0;
104 for(RadComponent c
: components
) {
105 maxSizePolicy
= Math
.max(maxSizePolicy
, c
.getConstraints().getHSizePolicy());
107 ColumnSpec
[] colSpecs
= new ColumnSpec
[components
.size() * 2 - 1];
108 for(int i
=0; i
<components
.size(); i
++) {
109 colSpecs
[i
*2] = components
.get(i
).getConstraints().getHSizePolicy() == maxSizePolicy
110 ?
new ColumnSpec(ENCODED_FORMSPEC_GROW
)
111 : FormFactory
.DEFAULT_COLSPEC
;
112 if (i
*2+1 < colSpecs
.length
) {
113 colSpecs
[i
*2+1] = FormFactory
.RELATED_GAP_COLSPEC
;
116 container
.setLayoutManager(this, new FormLayout(colSpecs
, new RowSpec
[] { FormFactory
.DEFAULT_ROWSPEC
} ));
120 public void writeLayout(final XmlWriter writer
, final RadContainer radContainer
) {
121 FormLayout layout
= (FormLayout
) radContainer
.getLayout();
122 for(int i
=1; i
<=layout
.getRowCount(); i
++) {
123 RowSpec rowSpec
= layout
.getRowSpec(i
);
124 writer
.startElement(UIFormXmlConstants
.ELEMENT_ROWSPEC
);
126 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_VALUE
, FormLayoutUtils
.getEncodedSpec(rowSpec
));
132 for(int i
=1; i
<=layout
.getColumnCount(); i
++) {
133 ColumnSpec columnSpec
= layout
.getColumnSpec(i
);
134 writer
.startElement(UIFormXmlConstants
.ELEMENT_COLSPEC
);
136 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_VALUE
, FormLayoutUtils
.getEncodedSpec(columnSpec
));
142 writeGroups(writer
, UIFormXmlConstants
.ELEMENT_ROWGROUP
, layout
.getRowGroups());
143 writeGroups(writer
, UIFormXmlConstants
.ELEMENT_COLGROUP
, layout
.getColumnGroups());
146 private static void writeGroups(final XmlWriter writer
, final String elementName
, final int[][] groups
) {
147 for(int[] group
: groups
) {
148 writer
.startElement(elementName
);
150 for(int member
: group
) {
151 writer
.startElement(UIFormXmlConstants
.ELEMENT_MEMBER
);
152 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_INDEX
, member
);
163 public void addComponentToContainer(final RadContainer container
, final RadComponent component
, final int index
) {
164 MyPropertyChangeListener listener
= new MyPropertyChangeListener(component
);
165 myListenerMap
.put(component
, listener
);
166 component
.addPropertyChangeListener(listener
);
167 final CellConstraints cc
= gridToCellConstraints(component
);
168 if (component
.getCustomLayoutConstraints() instanceof CellConstraints
) {
169 CellConstraints customCellConstraints
= (CellConstraints
) component
.getCustomLayoutConstraints();
170 cc
.insets
= customCellConstraints
.insets
;
172 component
.setCustomLayoutConstraints(cc
);
173 container
.getDelegee().add(component
.getDelegee(), cc
, index
);
177 public void removeComponentFromContainer(final RadContainer container
, final RadComponent component
) {
178 final MyPropertyChangeListener listener
= myListenerMap
.get(component
);
179 if (listener
!= null) {
180 component
.removePropertyChangeListener(listener
);
181 myListenerMap
.remove(component
);
183 super.removeComponentFromContainer(container
, component
);
186 private static CellConstraints
gridToCellConstraints(final RadComponent component
) {
187 GridConstraints gc
= component
.getConstraints();
188 CellConstraints
.Alignment hAlign
= ((gc
.getHSizePolicy() & GridConstraints
.SIZEPOLICY_WANT_GROW
) != 0)
189 ? CellConstraints
.FILL
190 : CellConstraints
.DEFAULT
;
191 CellConstraints
.Alignment vAlign
= ((gc
.getVSizePolicy() & GridConstraints
.SIZEPOLICY_WANT_GROW
) != 0)
192 ? CellConstraints
.FILL
193 : CellConstraints
.DEFAULT
;
194 if (component
.getCustomLayoutConstraints() instanceof CellConstraints
) {
195 CellConstraints cc
= (CellConstraints
) component
.getCustomLayoutConstraints();
199 return new CellConstraints(gc
.getColumn()+1, gc
.getRow()+1, gc
.getColSpan(), gc
.getRowSpan(), hAlign
, vAlign
);
203 public void writeChildConstraints(final XmlWriter writer
, final RadComponent child
) {
204 writeGridConstraints(writer
, child
);
205 if (child
.getCustomLayoutConstraints() instanceof CellConstraints
) {
206 CellConstraints cc
= (CellConstraints
) child
.getCustomLayoutConstraints();
207 writer
.startElement(UIFormXmlConstants
.ELEMENT_FORMS
);
209 if (!cc
.insets
.equals(new Insets(0, 0, 0, 0))) {
210 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_TOP
, cc
.insets
.top
);
211 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_LEFT
, cc
.insets
.left
);
212 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_BOTTOM
, cc
.insets
.bottom
);
213 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_RIGHT
, cc
.insets
.right
);
215 if (cc
.hAlign
!= CellConstraints
.DEFAULT
) {
216 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_DEFAULTALIGN_HORZ
, false);
218 if (cc
.vAlign
!= CellConstraints
.DEFAULT
) {
219 writer
.addAttribute(UIFormXmlConstants
.ATTRIBUTE_DEFAULTALIGN_VERT
, false);
228 private static FormLayout
getFormLayout(final RadContainer container
) {
229 return (FormLayout
) container
.getLayout();
232 @Override public int getGridRowCount(RadContainer container
) {
233 return getFormLayout(container
).getRowCount();
236 @Override public int getGridColumnCount(RadContainer container
) {
237 return getFormLayout(container
).getColumnCount();
240 @Override public int[] getGridCellCoords(RadContainer container
, boolean isRow
) {
241 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
242 int[] origins
= isRow ? layoutInfo
.rowOrigins
: layoutInfo
.columnOrigins
;
243 int[] result
= new int [origins
.length
-1];
244 System
.arraycopy(origins
, 0, result
, 0, result
.length
);
248 @Override public int[] getGridCellSizes(RadContainer container
, boolean isRow
) {
249 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
250 int[] origins
= isRow ? layoutInfo
.rowOrigins
: layoutInfo
.columnOrigins
;
251 int[] result
= new int [origins
.length
-1];
252 for(int i
=0; i
<result
.length
; i
++) {
253 result
[i
] = origins
[i
+1] - origins
[i
];
258 @Override public int[] getHorizontalGridLines(RadContainer container
) {
259 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
260 return layoutInfo
.rowOrigins
;
263 @Override public int[] getVerticalGridLines(RadContainer container
) {
264 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
265 return layoutInfo
.columnOrigins
;
268 @Override public int getGridRowAt(RadContainer container
, int y
) {
269 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
270 return findCell(layoutInfo
.rowOrigins
, y
);
273 @Override public int getGridColumnAt(RadContainer container
, int x
) {
274 final FormLayout
.LayoutInfo layoutInfo
= getFormLayout(container
).getLayoutInfo(container
.getDelegee());
275 return findCell(layoutInfo
.columnOrigins
, x
);
278 private static int findCell(final int[] origins
, final int coord
) {
279 for(int i
=0; i
<origins
.length
-1; i
++) {
280 if (coord
>= origins
[i
] && coord
< origins
[i
+1]) return i
;
286 public ComponentDropLocation
getDropLocation(@NotNull RadContainer container
, @Nullable final Point location
) {
287 FormLayout formLayout
= getFormLayout(container
);
288 if (formLayout
.getRowCount() == 0 || formLayout
.getColumnCount() == 0) {
289 if (location
!= null) {
290 Rectangle rc
= new Rectangle(new Point(), container
.getDelegee().getSize());
291 return new FormFirstComponentInsertLocation(container
, location
, rc
);
294 final FormLayout
.LayoutInfo layoutInfo
= formLayout
.getLayoutInfo(container
.getDelegee());
295 if (location
!= null && location
.x
> layoutInfo
.getWidth()) {
296 int row
= findCell(layoutInfo
.rowOrigins
, location
.y
);
298 return NoDropLocation
.INSTANCE
;
300 return new GridInsertLocation(container
, row
, getGridColumnCount(container
)-1, GridInsertMode
.ColumnAfter
);
302 if (location
!= null && location
.y
> layoutInfo
.getHeight()) {
303 int column
= findCell(layoutInfo
.columnOrigins
, location
.x
);
305 return NoDropLocation
.INSTANCE
;
307 return new GridInsertLocation(container
, getGridRowCount(container
)-1, column
, GridInsertMode
.RowAfter
);
310 if (container
.getGridRowCount() == 1 && container
.getGridColumnCount() == 1 &&
311 getComponentAtGrid(container
, 0, 0) == null) {
312 final Rectangle rc
= getGridCellRangeRect(container
, 0, 0, 0, 0);
313 if (location
== null) {
314 return new FormFirstComponentInsertLocation(container
, rc
, 0, 0);
316 return new FormFirstComponentInsertLocation(container
, location
, rc
);
319 return super.getDropLocation(container
, location
);
323 public CustomPropertiesPanel
getRowColumnPropertiesPanel(RadContainer container
, boolean isRow
, int[] selectedIndices
) {
324 if (myPropertiesPanel
== null) {
325 myPropertiesPanel
= new FormLayoutColumnProperties();
327 myPropertiesPanel
.showProperties(container
, isRow
, selectedIndices
);
328 return myPropertiesPanel
;
332 public ActionGroup
getCaptionActions() {
333 DefaultActionGroup group
= new DefaultActionGroup();
334 group
.add(new InsertBeforeAction());
335 group
.add(new InsertAfterAction());
336 group
.add(new SplitAction());
337 group
.add(new DeleteAction());
338 group
.add(new GroupRowsColumnsAction());
339 group
.add(new UngroupRowsColumnsAction());
344 public boolean canCellGrow(RadContainer container
, boolean isRow
, int index
) {
345 FormLayout layout
= (FormLayout
) container
.getLayout();
346 FormSpec spec
= isRow ? layout
.getRowSpec(index
+1) : layout
.getColumnSpec(index
+1);
347 return spec
.getResizeWeight() > 0.01d
;
351 public void setChildDragging(RadComponent child
, boolean dragging
) {
352 // do nothing here - otherwise the layout will jump around
356 public void paintCaptionDecoration(final RadContainer container
, final boolean isRow
, final int index
, final Graphics2D g2d
,
357 final Rectangle rc
) {
358 // don't paint gap rows/columns with red background
359 if (isGapCell(container
, isRow
, index
)) {
360 g2d
.setColor(Color
.LIGHT_GRAY
);
361 g2d
.fillRect(rc
.x
, rc
.y
, rc
.width
, rc
.height
);
364 if (canCellGrow(container
, isRow
, index
)) {
365 drawGrowMarker(isRow
, g2d
, rc
);
368 FormLayout layout
= (FormLayout
) container
.getLayout();
369 int[][] groups
= isRow ? layout
.getRowGroups() : layout
.getColumnGroups();
370 //noinspection MultipleVariablesInDeclaration
371 boolean haveTopLeft
= false, haveTopRight
= false, haveTopLine
= false;
372 //noinspection MultipleVariablesInDeclaration
373 boolean haveBottomLeft
= false, haveBottomRight
= false, haveBottomLine
= false;
374 boolean inGroup
= false;
375 for(int i
=0; i
<groups
.length
; i
++) {
376 int minMember
= Integer
.MAX_VALUE
;
378 for(int member
: groups
[i
]) {
379 minMember
= Math
.min(member
-1, minMember
);
380 maxMember
= Math
.max(member
-1, maxMember
);
381 inGroup
= inGroup
|| (member
-1 == index
);
383 if (minMember
<= index
&& index
<= maxMember
) {
385 haveTopLeft
= haveTopLeft
|| index
> minMember
;
386 haveTopRight
= haveTopRight
|| index
< maxMember
;
387 haveTopLine
= haveTopLine
|| inGroup
;
390 haveBottomLeft
= haveBottomLeft
|| index
> minMember
;
391 haveBottomRight
= haveBottomRight
|| index
< maxMember
;
392 haveBottomLine
= haveBottomLine
|| inGroup
;
397 g2d
.setColor(Color
.BLUE
);
398 drawGroupLine(rc
, isRow
, g2d
, true, haveTopLeft
, haveTopRight
, haveTopLine
);
399 drawGroupLine(rc
, isRow
, g2d
, false, haveBottomLeft
, haveBottomRight
, haveBottomLine
);
402 private static void drawGroupLine(final Rectangle rc
, final boolean isRow
, final Graphics2D g2d
, boolean isTop
,
403 final boolean haveLeft
, final boolean haveRight
, final boolean haveLine
) {
405 int maxX
= (int) rc
.getMaxX();
406 int maxY
= (int) rc
.getMaxY();
407 Point linePos
= isTop ?
new Point(rc
.x
+1, rc
.y
+1) : new Point(rc
.x
+3, rc
.y
+3);
408 Point markerPos
= new Point(rc
.x
+6, rc
.y
+6);
410 int midX
= (int) rc
.getCenterX();
411 int midY
= (int) rc
.getCenterY();
414 g2d
.drawLine(linePos
.x
, midY
, markerPos
.x
, midY
);
417 g2d
.drawLine(midX
, linePos
.y
, midX
, markerPos
.y
);
422 g2d
.drawLine(linePos
.x
, rc
.y
, linePos
.x
, midY
);
425 g2d
.drawLine(rc
.x
, linePos
.y
, midX
, linePos
.y
);
430 g2d
.drawLine(linePos
.x
, midY
, linePos
.x
, maxY
);
433 g2d
.drawLine(midX
, linePos
.y
, maxX
, linePos
.y
);
439 public Property
[] getContainerProperties(final Project project
) {
440 return Property
.EMPTY_ARRAY
;
444 public Property
[] getComponentProperties(final Project project
, final RadComponent component
) {
445 return new Property
[] {
446 HorzAlignProperty
.getInstance(project
),
447 VertAlignProperty
.getInstance(project
),
448 new ComponentInsetsProperty()
453 public int insertGridCells(final RadContainer grid
, final int cellIndex
, final boolean isRow
, final boolean isBefore
, final boolean grow
) {
456 formSpec
= grow ?
new RowSpec(ENCODED_FORMSPEC_GROW
) : new RowSpec(DEFAULT_NOGROW_SIZE
);
459 formSpec
= grow ?
new ColumnSpec(ENCODED_FORMSPEC_GROW
) : new ColumnSpec(DEFAULT_NOGROW_SIZE
);
461 insertGridCells(grid
, cellIndex
, isRow
, isBefore
, formSpec
);
462 return getGridCellCount(grid
, isRow
) == 1 ?
1 : 2;
466 public void copyGridCells(RadContainer source
, final RadContainer destination
, final boolean isRow
, int cellIndex
, int cellCount
, int targetIndex
) {
467 FormLayout sourceLayout
= getFormLayout(source
);
468 FormLayout destinationLayout
= getFormLayout(destination
);
470 insertOrAppendRow(destinationLayout
, targetIndex
+1, FormFactory
.RELATED_GAP_ROWSPEC
);
473 insertOrAppendColumn(destinationLayout
, targetIndex
+1, FormFactory
.RELATED_GAP_COLSPEC
);
476 if (targetIndex
< cellIndex
) cellIndex
++;
477 copyFormSpecs(sourceLayout
, destinationLayout
, isRow
, cellIndex
, cellCount
, targetIndex
);
478 updateGridConstraintsFromCellConstraints(destination
);
481 private static void copyFormSpecs(final FormLayout sourceLayout
,
482 final FormLayout destinationLayout
,
487 for(int i
=0; i
< cellCount
; i
++) {
489 RowSpec rowSpec
= sourceLayout
.getRowSpec(cellIndex
+ 1);
490 insertOrAppendRow(destinationLayout
, targetIndex
+1, rowSpec
);
493 ColumnSpec colSpec
= sourceLayout
.getColumnSpec(cellIndex
+ 1);
494 insertOrAppendColumn(destinationLayout
, targetIndex
+1, colSpec
);
496 cellIndex
+= (targetIndex
< cellIndex
&& sourceLayout
== destinationLayout
) ?
2 : 1;
502 public void copyGridSection(final RadContainer source
, final RadContainer destination
, final Rectangle rc
) {
503 final FormLayout destinationLayout
= new FormLayout();
504 destination
.setLayout(destinationLayout
);
505 copyFormSpecs(getFormLayout(source
), destinationLayout
, true, rc
.y
, rc
.height
, 0);
506 copyFormSpecs(getFormLayout(source
), destinationLayout
, false, rc
.x
, rc
.width
, 0);
510 public int getGapCellCount() {
515 public int getGapCellSize(final RadContainer container
, boolean isRow
) {
516 Size size
= isRow ? FormFactory
.RELATED_GAP_ROWSPEC
.getSize() : FormFactory
.RELATED_GAP_COLSPEC
.getSize();
517 if (size
instanceof ConstantSize
) {
518 return ((ConstantSize
) size
).getPixelSize(container
.getDelegee());
524 public boolean isGapCell(RadContainer grid
, boolean isRow
, int cellIndex
) {
525 if (cellIndex
< 0 || cellIndex
>= getGridCellCount(grid
, isRow
)) {
528 if (cellIndex
% 2 == 1) {
529 final GridChangeUtil
.CellStatus status
= GridChangeUtil
.canDeleteCell(grid
, cellIndex
, isRow
);
530 if (status
== GridChangeUtil
.CellStatus
.Empty
|| status
== GridChangeUtil
.CellStatus
.Redundant
) {
538 public int getCellIndexBase() {
543 * @return index where new column or row was actually inserted (0-based)
545 private int insertGridCells(RadContainer grid
, int cellIndex
, boolean isRow
, boolean isBefore
, FormSpec formSpec
) {
546 FormLayout formLayout
= (FormLayout
) grid
.getLayout();
547 int index
= isBefore ? cellIndex
+1 : cellIndex
+2;
549 if (getGridCellCount(grid
, true) > 0) {
550 insertOrAppendRow(formLayout
, index
, FormFactory
.RELATED_GAP_ROWSPEC
);
551 if (!isBefore
) index
++;
553 insertOrAppendRow(formLayout
, index
, (RowSpec
) formSpec
);
556 if (getGridCellCount(grid
, false) > 0) {
557 insertOrAppendColumn(formLayout
, index
, FormFactory
.RELATED_GAP_COLSPEC
);
558 if (!isBefore
) index
++;
560 insertOrAppendColumn(formLayout
, index
, (ColumnSpec
)formSpec
);
562 updateGridConstraintsFromCellConstraints(grid
);
566 private static void insertOrAppendRow(final FormLayout formLayout
, final int index
, final RowSpec rowSpec
) {
567 if (index
== formLayout
.getRowCount()+1) {
568 formLayout
.appendRow(rowSpec
);
571 formLayout
.insertRow(index
, rowSpec
);
575 private static void insertOrAppendColumn(final FormLayout formLayout
, final int index
, final ColumnSpec columnSpec
) {
576 if (index
== formLayout
.getColumnCount()+1) {
577 formLayout
.appendColumn(columnSpec
);
580 formLayout
.insertColumn(index
, columnSpec
);
585 public int deleteGridCells(final RadContainer grid
, final int cellIndex
, final boolean isRow
) {
587 FormLayout formLayout
= (FormLayout
) grid
.getLayout();
588 adjustDeletedCellOrigins(grid
, cellIndex
, isRow
);
590 int[][] groupIndices
= formLayout
.getRowGroups();
591 groupIndices
= removeDeletedCell(groupIndices
, cellIndex
+1);
592 formLayout
.setRowGroups(groupIndices
);
593 formLayout
.removeRow(cellIndex
+1);
594 updateGridConstraintsFromCellConstraints(grid
);
595 if (formLayout
.getRowCount() > 0 && formLayout
.getRowCount() % 2 == 0) {
596 int gapRowIndex
= (cellIndex
>= grid
.getGridRowCount()) ? cellIndex
-1 : cellIndex
;
597 if (GridChangeUtil
.isRowEmpty(grid
, gapRowIndex
)) {
598 formLayout
.removeRow(gapRowIndex
+1);
599 updateGridConstraintsFromCellConstraints(grid
);
605 int[][] groupIndices
= formLayout
.getColumnGroups();
606 groupIndices
= removeDeletedCell(groupIndices
, cellIndex
+1);
607 formLayout
.setColumnGroups(groupIndices
);
608 formLayout
.removeColumn(cellIndex
+1);
609 updateGridConstraintsFromCellConstraints(grid
);
610 if (formLayout
.getColumnCount() > 0 && formLayout
.getColumnCount() % 2 == 0) {
611 int gapColumnIndex
= (cellIndex
>= grid
.getGridColumnCount()) ? cellIndex
-1 : cellIndex
;
612 if (GridChangeUtil
.isColumnEmpty(grid
, gapColumnIndex
)) {
613 formLayout
.removeColumn(gapColumnIndex
+1);
614 updateGridConstraintsFromCellConstraints(grid
);
622 private void adjustDeletedCellOrigins(final RadContainer grid
, final int cellIndex
, final boolean isRow
) {
623 int gapCellDelta
= isGapCell(grid
, isRow
, cellIndex
+1) ?
2 : 1;
624 for(RadComponent component
: grid
.getComponents()) {
625 // ensure that we don't have component origins in the deleted cells
626 final GridConstraints gc
= component
.getConstraints();
627 if (gc
.getCell(isRow
) == cellIndex
) {
628 final int span
= gc
.getSpan(isRow
);
629 if (span
> gapCellDelta
) {
630 gc
.setCell(isRow
, cellIndex
+gapCellDelta
);
631 gc
.setSpan(isRow
, span
-gapCellDelta
);
632 updateConstraints(component
);
635 throw new IllegalArgumentException("Attempt to delete grid row/column which contains origins of 1-span components");
641 private static int[][] removeDeletedCell(final int[][] groupIndices
, final int deletedIndex
) {
642 for(int i
=0; i
<groupIndices
.length
; i
++) {
643 for(int j
=0; j
<groupIndices
[i
].length
; j
++) {
644 if (groupIndices
[i
][j
] == deletedIndex
) {
646 if (groupIndices
[i
].length
<= 2) {
647 // deleted cell is contained in a group with 1 or 2 cells => delete entire group
648 newIndices
= new int[groupIndices
.length
-1][];
649 for (int newI
= 0; newI
< i
; newI
++) {
650 newIndices
[newI
] = new int[groupIndices
[newI
].length
];
651 System
.arraycopy(groupIndices
[newI
], 0, newIndices
[newI
], 0, groupIndices
[newI
].length
);
653 for(int newI
=i
+1; newI
<groupIndices
.length
; newI
++) {
654 newIndices
[newI
-1] = new int[groupIndices
[newI
].length
];
655 System
.arraycopy(groupIndices
[newI
], 0, newIndices
[newI
-1], 0, groupIndices
[newI
].length
);
659 // deleted cell is contained in a group with more than 2 cells => keep the group and delete only the item
660 newIndices
= new int[groupIndices
.length
][];
661 for(int newI
=0; newI
<groupIndices
.length
; newI
++) {
663 newIndices
[newI
] = new int[groupIndices
[newI
].length
-1];
664 System
.arraycopy(groupIndices
[newI
], 0, newIndices
[newI
], 0, j
);
665 System
.arraycopy(groupIndices
[newI
], j
+1, newIndices
[newI
], j
, groupIndices
[i
].length
-j
-1);
668 newIndices
[newI
] = new int[groupIndices
[newI
].length
];
669 System
.arraycopy(groupIndices
[newI
], 0, newIndices
[newI
], 0, groupIndices
[i
].length
);
681 public String
getCellResizeTooltip(RadContainer container
, boolean isRow
, int cell
, int newSize
) {
682 final String size
= getUpdatedSize(container
, isRow
, cell
, newSize
).toString();
684 ? UIDesignerBundle
.message("tooltip.resize.row", cell
+getCellIndexBase(), size
)
685 : UIDesignerBundle
.message("tooltip.resize.column", cell
+getCellIndexBase(), size
);
689 public void processCellResized(RadContainer container
, final boolean isRow
, final int cell
, final int newSize
) {
690 FormLayout formLayout
= (FormLayout
) container
.getLayout();
691 final ConstantSize updatedSize
= getUpdatedSize(container
, isRow
, cell
, newSize
);
694 RowSpec rowSpec
= formLayout
.getRowSpec(cell
+1);
695 newSpec
= new RowSpec(rowSpec
.getDefaultAlignment(), updatedSize
, rowSpec
.getResizeWeight());
698 ColumnSpec colSpec
= formLayout
.getColumnSpec(cell
+1);
699 newSpec
= new ColumnSpec(colSpec
.getDefaultAlignment(), updatedSize
, colSpec
.getResizeWeight());
701 setSpec(formLayout
, newSpec
, cell
+1, isRow
);
702 resizeSameGroupCells(cell
, formLayout
, newSpec
, isRow
);
705 // Explicitly resize all cells in the group to desired size to make sure that the resize operation is effective (IDEADEV-10202)
706 private static void resizeSameGroupCells(final int cell
, final FormLayout formLayout
, final FormSpec newSpec
, final boolean isRow
) {
707 int[][] groups
= isRow ? formLayout
.getRowGroups() : formLayout
.getColumnGroups();
708 for(int[] group
: groups
) {
709 boolean foundGroup
= false;
710 for(int groupCell
: group
) {
711 if (groupCell
== cell
+1) {
717 for(int groupCell
: group
) {
718 setSpec(formLayout
, newSpec
, groupCell
, isRow
);
725 private static void setSpec(final FormLayout formLayout
, final FormSpec newSpec
, final int cell
, boolean isRow
) {
727 formLayout
.setRowSpec(cell
, (RowSpec
) newSpec
);
730 formLayout
.setColumnSpec(cell
, (ColumnSpec
) newSpec
);
734 private static ConstantSize
getUpdatedSize(RadContainer container
, boolean isRow
, int cell
, int newPx
) {
735 FormLayout formLayout
= (FormLayout
) container
.getLayout();
737 return scaleSize(formLayout
.getRowSpec(cell
+1), container
, newPx
);
740 return scaleSize(formLayout
.getColumnSpec(cell
+1), container
, newPx
);
744 private static ConstantSize
scaleSize(final FormSpec rowSpec
, final RadContainer container
, final int newPx
) {
745 if (rowSpec
.getSize() instanceof ConstantSize
) {
746 ConstantSize oldSize
= (ConstantSize
) rowSpec
.getSize();
747 int oldPx
= oldSize
.getPixelSize(container
.getDelegee());
748 double newValue
= Math
.round(oldSize
.getValue() * newPx
/ oldPx
* 10) / 10;
749 return new ConstantSize(newValue
, oldSize
.getUnit());
751 return new ConstantSize(newPx
, ConstantSize
.PIXEL
);
755 public void processCellsMoved(final RadContainer container
, final boolean isRow
, final int[] cellsToMove
, int targetCell
) {
756 for(int i
=0; i
<cellsToMove
.length
; i
++) {
757 final int sourceCell
= cellsToMove
[i
];
758 moveCells(container
, isRow
, sourceCell
, targetCell
);
759 if (sourceCell
< targetCell
) {
760 for(int j
=i
+1; j
<cellsToMove
.length
; j
++) {
761 cellsToMove
[j
] -= 2;
770 private void moveCells(final RadContainer container
, final boolean isRow
, final int cell
, int targetCell
) {
771 if (targetCell
>= cell
&& targetCell
<= cell
+2) {
774 FormLayout layout
= (FormLayout
) container
.getLayout();
775 List
<RadComponent
> componentsToMove
= new ArrayList
<RadComponent
>();
776 FormSpec oldSpec
= isRow ? layout
.getRowSpec(cell
+1) : layout
.getColumnSpec(cell
+1);
777 for(RadComponent c
: container
.getComponents()) {
778 if (c
.getConstraints().getCell(isRow
) == cell
) {
779 componentsToMove
.add(c
);
780 container
.removeComponent(c
);
783 int count
= deleteGridCells(container
, cell
, isRow
);
784 int insertCell
= (targetCell
> cell
) ? targetCell
- count
- 1 : targetCell
;
785 final boolean isBefore
= targetCell
< cell
;
786 int newIndex
= insertGridCells(container
, insertCell
, isRow
, isBefore
, oldSpec
);
787 for(RadComponent c
: componentsToMove
) {
788 c
.getConstraints().setCell(isRow
, newIndex
);
789 container
.addComponent(c
);
793 private static void updateGridConstraintsFromCellConstraints(RadContainer grid
) {
794 FormLayout layout
= (FormLayout
) grid
.getLayout();
795 for(RadComponent c
: grid
.getComponents()) {
796 CellConstraints cc
= layout
.getConstraints(c
.getDelegee());
797 GridConstraints gc
= c
.getConstraints();
798 copyCellToGridConstraints(gc
, cc
);
802 private static void copyCellToGridConstraints(final GridConstraints gc
, final CellConstraints cc
) {
803 gc
.setColumn(cc
.gridX
-1);
804 gc
.setRow(cc
.gridY
-1);
805 gc
.setColSpan(cc
.gridWidth
);
806 gc
.setRowSpan(cc
.gridHeight
);
809 public int getAlignment(RadComponent component
, boolean horizontal
) {
810 CellConstraints cc
= (CellConstraints
) component
.getCustomLayoutConstraints();
811 CellConstraints
.Alignment al
= horizontal ? cc
.hAlign
: cc
.vAlign
;
812 if (al
== CellConstraints
.DEFAULT
) {
813 FormLayout formLayout
= (FormLayout
) component
.getParent().getLayout();
814 FormSpec formSpec
= horizontal
815 ? formLayout
.getColumnSpec(component
.getConstraints().getColumn()+1)
816 : formLayout
.getRowSpec(component
.getConstraints().getRow()+1);
817 final FormSpec
.DefaultAlignment defaultAlignment
= formSpec
.getDefaultAlignment();
818 if (defaultAlignment
.equals(RowSpec
.FILL
)) {
819 return GridConstraints
.ALIGN_FILL
;
821 if (defaultAlignment
.equals(RowSpec
.TOP
) || defaultAlignment
.equals(ColumnSpec
.LEFT
)) {
822 return GridConstraints
.ALIGN_LEFT
;
824 if (defaultAlignment
.equals(RowSpec
.CENTER
)) {
825 return GridConstraints
.ALIGN_CENTER
;
827 return GridConstraints
.ALIGN_RIGHT
;
829 return Utils
.alignFromConstraints(component
.getConstraints(), horizontal
);
832 public void setAlignment(RadComponent component
, boolean horizontal
, int alignment
) {
833 CellConstraints cc
= (CellConstraints
) component
.getCustomLayoutConstraints();
835 cc
.hAlign
= FormLayoutSerializer
.ourHorizontalAlignments
[alignment
];
838 cc
.vAlign
= FormLayoutSerializer
.ourVerticalAlignments
[alignment
];
840 updateConstraints(component
);
843 public void resetAlignment(RadComponent component
, boolean horizontal
) {
844 CellConstraints cc
= (CellConstraints
) component
.getCustomLayoutConstraints();
846 cc
.hAlign
= CellConstraints
.DEFAULT
;
849 cc
.vAlign
= CellConstraints
.DEFAULT
;
851 updateConstraints(component
);
854 public boolean isAlignmentModified(RadComponent component
, boolean horizontal
) {
855 CellConstraints cc
= (CellConstraints
) component
.getCustomLayoutConstraints();
856 CellConstraints
.Alignment al
= horizontal ? cc
.hAlign
: cc
.vAlign
;
857 return al
!= CellConstraints
.DEFAULT
;
860 private static void updateConstraints(final RadComponent component
) {
861 FormLayout layout
= (FormLayout
) component
.getParent().getLayout();
862 layout
.setConstraints(component
.getDelegee(), gridToCellConstraints(component
));
863 component
.getParent().revalidate();
866 public int getMinCellCount() {
871 public void createSnapshotLayout(final SnapshotContext context
,
872 final JComponent parent
,
873 final RadContainer container
,
874 final LayoutManager layout
) {
875 ColumnSpec
[] colSpecs
;
878 int[][] columnGroups
;
880 Method method
= layout
.getClass().getMethod("getRowCount", ArrayUtil
.EMPTY_CLASS_ARRAY
);
881 int rowCount
= ((Integer
)method
.invoke(layout
, ArrayUtil
.EMPTY_OBJECT_ARRAY
)).intValue();
882 method
= layout
.getClass().getMethod("getColumnCount", ArrayUtil
.EMPTY_CLASS_ARRAY
);
883 int columnCount
= ((Integer
)method
.invoke(layout
, ArrayUtil
.EMPTY_OBJECT_ARRAY
)).intValue();
885 rowSpecs
= new RowSpec
[rowCount
];
886 colSpecs
= new ColumnSpec
[columnCount
];
888 method
= layout
.getClass().getMethod("getRowSpec", int.class);
889 for (int i
= 0; i
< rowCount
; i
++) {
890 rowSpecs
[i
] = (RowSpec
)createSerializedCopy(method
.invoke(layout
, i
+ 1));
892 method
= layout
.getClass().getMethod("getColumnSpec", int.class);
893 for (int i
= 0; i
< columnCount
; i
++) {
894 colSpecs
[i
] = (ColumnSpec
)createSerializedCopy(method
.invoke(layout
, i
+ 1));
897 method
= layout
.getClass().getMethod("getRowGroups", ArrayUtil
.EMPTY_CLASS_ARRAY
);
898 rowGroups
= (int[][])method
.invoke(layout
);
900 method
= layout
.getClass().getMethod("getColumnGroups", ArrayUtil
.EMPTY_CLASS_ARRAY
);
901 columnGroups
= (int[][])method
.invoke(layout
);
903 catch (Exception ex
) {
904 throw new RuntimeException(ex
);
907 final FormLayout formLayout
= new FormLayout(colSpecs
, rowSpecs
);
908 formLayout
.setRowGroups(rowGroups
);
909 formLayout
.setColumnGroups(columnGroups
);
910 container
.setLayout(formLayout
);
913 private static Object
createSerializedCopy(final Object original
) {
914 // FormLayout may have been loaded with a different classloader, so we need to create a copy through serialization
917 ByteArrayOutputStream baos
= new ByteArrayOutputStream();
918 ObjectOutputStream os
= new ObjectOutputStream(baos
);
920 os
.writeObject(original
);
926 InputStream bais
= new ByteArrayInputStream(baos
.toByteArray());
927 ObjectInputStream is
= new ObjectInputStream(bais
);
929 copy
= is
.readObject();
935 catch (Exception e
) {
936 throw new RuntimeException(e
);
942 public void addSnapshotComponent(final JComponent parent
,
943 final JComponent child
,
944 final RadContainer container
,
945 final RadComponent component
) {
948 LayoutManager layout
= parent
.getLayout();
949 //noinspection HardCodedStringLiteral
950 Method method
= layout
.getClass().getMethod("getConstraints", Component
.class);
951 cc
= (CellConstraints
)createSerializedCopy(method
.invoke(layout
, child
));
953 catch (Exception ex
) {
954 throw new RuntimeException(ex
);
956 copyCellToGridConstraints(component
.getConstraints(), cc
);
957 component
.setCustomLayoutConstraints(cc
);
958 container
.addComponent(component
);
961 private static class MyPropertyChangeListener
implements PropertyChangeListener
{
962 private final RadComponent myComponent
;
964 public MyPropertyChangeListener(final RadComponent component
) {
965 myComponent
= component
;
968 public void propertyChange(PropertyChangeEvent evt
) {
969 if (evt
.getPropertyName().equals(RadComponent
.PROP_CONSTRAINTS
)) {
970 updateConstraints(myComponent
);
975 private static class ComponentInsetsProperty
extends AbstractInsetsProperty
<RadComponent
> {
976 public ComponentInsetsProperty() {
977 super(null, "Insets");
980 public Insets
getValue(final RadComponent component
) {
981 if (component
.getCustomLayoutConstraints() instanceof CellConstraints
) {
982 final CellConstraints cellConstraints
= (CellConstraints
)component
.getCustomLayoutConstraints();
983 return cellConstraints
.insets
;
985 return new Insets(0, 0, 0, 0);
988 protected void setValueImpl(final RadComponent component
, final Insets value
) throws Exception
{
989 if (component
.getCustomLayoutConstraints() instanceof CellConstraints
) {
990 final CellConstraints cellConstraints
= (CellConstraints
)component
.getCustomLayoutConstraints();
991 cellConstraints
.insets
= value
;
993 FormLayout layout
= (FormLayout
) component
.getParent().getLayout();
994 CellConstraints cc
= (CellConstraints
)layout
.getConstraints(component
.getDelegee()).clone();
996 layout
.setConstraints(component
.getDelegee(), cc
);