blob: 117688eb7d6f0241a2b22d95d87ca5a129ac5173 [file] [log] [blame]
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.uiDesigner.radComponents;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
import com.intellij.uiDesigner.GridChangeUtil;
import com.intellij.uiDesigner.UIDesignerBundle;
import com.intellij.uiDesigner.UIFormXmlConstants;
import com.intellij.uiDesigner.XmlWriter;
import com.intellij.uiDesigner.actions.*;
import com.intellij.uiDesigner.compiler.FormLayoutUtils;
import com.intellij.uiDesigner.compiler.Utils;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.designSurface.*;
import com.intellij.uiDesigner.lw.FormLayoutSerializer;
import com.intellij.uiDesigner.propertyInspector.Property;
import com.intellij.uiDesigner.propertyInspector.properties.AbstractInsetsProperty;
import com.intellij.uiDesigner.propertyInspector.properties.AlignPropertyProvider;
import com.intellij.uiDesigner.propertyInspector.properties.HorzAlignProperty;
import com.intellij.uiDesigner.propertyInspector.properties.VertAlignProperty;
import com.intellij.uiDesigner.snapShooter.SnapshotContext;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.PlatformColors;
import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* @author yole
*/
public class RadFormLayoutManager extends RadAbstractGridLayoutManager implements AlignPropertyProvider {
private FormLayoutColumnProperties myPropertiesPanel;
@NonNls private static final String ENCODED_FORMSPEC_GROW = "d:grow";
private static final Size DEFAULT_NOGROW_SIZE = new BoundedSize(Sizes.DEFAULT, new ConstantSize(4, ConstantSize.PIXEL), null);
@Nullable public String getName() {
return UIFormXmlConstants.LAYOUT_FORM;
}
@Override @Nullable
public LayoutManager createLayout() {
return new FormLayout(ENCODED_FORMSPEC_GROW, ENCODED_FORMSPEC_GROW);
}
@Override
protected void changeLayoutFromGrid(final RadContainer container, final List<RadComponent> contents, final List<Boolean> canRowsGrow,
final List<Boolean> canColumnsGrow) {
int rowCount = canRowsGrow.size();
int columnCount = canColumnsGrow.size();
int rowCountWithGaps = (rowCount == 0) ? 0 : rowCount * 2 - 1;
int columnCountWithGaps = (columnCount == 0) ? 0 : columnCount * 2 - 1;
RowSpec[] rowSpecs = new RowSpec [rowCountWithGaps];
ColumnSpec[] colSpecs = new ColumnSpec [columnCountWithGaps];
for(int i=0; i<rowCount; i++) {
rowSpecs [i*2] = canRowsGrow.get(i).booleanValue() ? new RowSpec(ENCODED_FORMSPEC_GROW) : new RowSpec(DEFAULT_NOGROW_SIZE);
if (i*2+1 < rowSpecs.length) {
rowSpecs [i*2+1] = FormFactory.RELATED_GAP_ROWSPEC;
}
}
for(int i=0; i<columnCount; i++) {
colSpecs [i*2] = canColumnsGrow.get(i).booleanValue() ? new ColumnSpec(ENCODED_FORMSPEC_GROW) : new ColumnSpec(DEFAULT_NOGROW_SIZE);
if (i*2+1 < colSpecs.length) {
colSpecs [i*2+1] = FormFactory.RELATED_GAP_COLSPEC;
}
}
container.setLayoutManager(this, new FormLayout(colSpecs, rowSpecs));
}
@Override
protected void changeLayoutFromIndexed(final RadContainer container, final List<RadComponent> components) {
int maxSizePolicy = 0;
for(RadComponent c: components) {
maxSizePolicy = Math.max(maxSizePolicy, c.getConstraints().getHSizePolicy());
}
ColumnSpec[] colSpecs = new ColumnSpec [components.size() * 2 - 1];
for(int i=0; i<components.size(); i++) {
colSpecs [i*2] = components.get(i).getConstraints().getHSizePolicy() == maxSizePolicy
? new ColumnSpec(ENCODED_FORMSPEC_GROW)
: FormFactory.DEFAULT_COLSPEC;
if (i*2+1 < colSpecs.length) {
colSpecs [i*2+1] = FormFactory.RELATED_GAP_COLSPEC;
}
}
container.setLayoutManager(this, new FormLayout(colSpecs, new RowSpec[] { FormFactory.DEFAULT_ROWSPEC } ));
}
@Override
public void writeLayout(final XmlWriter writer, final RadContainer radContainer) {
FormLayout layout = (FormLayout) radContainer.getLayout();
for(int i=1; i<=layout.getRowCount(); i++) {
RowSpec rowSpec = layout.getRowSpec(i);
writer.startElement(UIFormXmlConstants.ELEMENT_ROWSPEC);
try {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, FormLayoutUtils.getEncodedSpec(rowSpec));
}
finally {
writer.endElement();
}
}
for(int i=1; i<=layout.getColumnCount(); i++) {
ColumnSpec columnSpec = layout.getColumnSpec(i);
writer.startElement(UIFormXmlConstants.ELEMENT_COLSPEC);
try {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_VALUE, FormLayoutUtils.getEncodedSpec(columnSpec));
}
finally {
writer.endElement();
}
}
writeGroups(writer, UIFormXmlConstants.ELEMENT_ROWGROUP, layout.getRowGroups());
writeGroups(writer, UIFormXmlConstants.ELEMENT_COLGROUP, layout.getColumnGroups());
}
private static void writeGroups(final XmlWriter writer, final String elementName, final int[][] groups) {
for(int[] group: groups) {
writer.startElement(elementName);
try {
for(int member: group) {
writer.startElement(UIFormXmlConstants.ELEMENT_MEMBER);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_INDEX, member);
writer.endElement();
}
}
finally {
writer.endElement();
}
}
}
@Override
public void addComponentToContainer(final RadContainer container, final RadComponent component, final int index) {
super.addComponentToContainer(container, component, index);
final CellConstraints cc = gridToCellConstraints(component);
if (component.getCustomLayoutConstraints() instanceof CellConstraints) {
CellConstraints customCellConstraints = (CellConstraints) component.getCustomLayoutConstraints();
cc.insets = customCellConstraints.insets;
}
component.setCustomLayoutConstraints(cc);
container.getDelegee().add(component.getDelegee(), cc, index);
}
private static CellConstraints gridToCellConstraints(final RadComponent component) {
GridConstraints gc = component.getConstraints();
CellConstraints.Alignment hAlign = ((gc.getHSizePolicy() & GridConstraints.SIZEPOLICY_WANT_GROW) != 0)
? CellConstraints.FILL
: CellConstraints.DEFAULT;
CellConstraints.Alignment vAlign = ((gc.getVSizePolicy() & GridConstraints.SIZEPOLICY_WANT_GROW) != 0)
? CellConstraints.FILL
: CellConstraints.DEFAULT;
if (component.getCustomLayoutConstraints() instanceof CellConstraints) {
CellConstraints cc = (CellConstraints) component.getCustomLayoutConstraints();
hAlign = cc.hAlign;
vAlign = cc.vAlign;
}
return new CellConstraints(gc.getColumn()+1, gc.getRow()+1, gc.getColSpan(), gc.getRowSpan(), hAlign, vAlign);
}
@Override
public void writeChildConstraints(final XmlWriter writer, final RadComponent child) {
writeGridConstraints(writer, child);
if (child.getCustomLayoutConstraints() instanceof CellConstraints) {
CellConstraints cc = (CellConstraints) child.getCustomLayoutConstraints();
writer.startElement(UIFormXmlConstants.ELEMENT_FORMS);
try {
if (!cc.insets.equals(new Insets(0, 0, 0, 0))) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_TOP, cc.insets.top);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_LEFT, cc.insets.left);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_BOTTOM, cc.insets.bottom);
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_RIGHT, cc.insets.right);
}
if (cc.hAlign != CellConstraints.DEFAULT) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_DEFAULTALIGN_HORZ, false);
}
if (cc.vAlign != CellConstraints.DEFAULT) {
writer.addAttribute(UIFormXmlConstants.ATTRIBUTE_DEFAULTALIGN_VERT, false);
}
}
finally {
writer.endElement();
}
}
}
private static FormLayout getFormLayout(final RadContainer container) {
return (FormLayout) container.getLayout();
}
@Override public int getGridRowCount(RadContainer container) {
return getFormLayout(container).getRowCount();
}
@Override public int getGridColumnCount(RadContainer container) {
return getFormLayout(container).getColumnCount();
}
@Override public int[] getGridCellCoords(RadContainer container, boolean isRow) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
int[] origins = isRow ? layoutInfo.rowOrigins : layoutInfo.columnOrigins;
int[] result = new int [origins.length-1];
System.arraycopy(origins, 0, result, 0, result.length);
return result;
}
@Override public int[] getGridCellSizes(RadContainer container, boolean isRow) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
int[] origins = isRow ? layoutInfo.rowOrigins : layoutInfo.columnOrigins;
int[] result = new int [origins.length-1];
for(int i=0; i<result.length; i++) {
result [i] = origins [i+1] - origins [i];
}
return result;
}
@Override public int[] getHorizontalGridLines(RadContainer container) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
return layoutInfo.rowOrigins;
}
@Override public int[] getVerticalGridLines(RadContainer container) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
return layoutInfo.columnOrigins;
}
@Override public int getGridRowAt(RadContainer container, int y) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
return findCell(layoutInfo.rowOrigins, y);
}
@Override public int getGridColumnAt(RadContainer container, int x) {
final FormLayout.LayoutInfo layoutInfo = getFormLayout(container).getLayoutInfo(container.getDelegee());
return findCell(layoutInfo.columnOrigins, x);
}
private static int findCell(final int[] origins, final int coord) {
for(int i=0; i<origins.length-1; i++) {
if (coord >= origins [i] && coord < origins [i+1]) return i;
}
return -1;
}
@NotNull @Override
public ComponentDropLocation getDropLocation(@NotNull RadContainer container, @Nullable final Point location) {
FormLayout formLayout = getFormLayout(container);
if (formLayout.getRowCount() == 0 || formLayout.getColumnCount() == 0) {
if (location != null) {
Rectangle rc = new Rectangle(new Point(), container.getDelegee().getSize());
return new FormFirstComponentInsertLocation(container, location, rc);
}
}
final FormLayout.LayoutInfo layoutInfo = formLayout.getLayoutInfo(container.getDelegee());
if (location != null && location.x > layoutInfo.getWidth()) {
int row = findCell(layoutInfo.rowOrigins, location.y);
if (row == -1) {
return NoDropLocation.INSTANCE;
}
return new GridInsertLocation(container, row, getGridColumnCount(container)-1, GridInsertMode.ColumnAfter);
}
if (location != null && location.y > layoutInfo.getHeight()) {
int column = findCell(layoutInfo.columnOrigins, location.x);
if (column == -1) {
return NoDropLocation.INSTANCE;
}
return new GridInsertLocation(container, getGridRowCount(container)-1, column, GridInsertMode.RowAfter);
}
if (container.getGridRowCount() == 1 && container.getGridColumnCount() == 1 &&
getComponentAtGrid(container, 0, 0) == null) {
final Rectangle rc = getGridCellRangeRect(container, 0, 0, 0, 0);
if (location == null) {
return new FormFirstComponentInsertLocation(container, rc, 0, 0);
}
return new FormFirstComponentInsertLocation(container, location, rc);
}
return super.getDropLocation(container, location);
}
@Override
public CustomPropertiesPanel getRowColumnPropertiesPanel(RadContainer container, boolean isRow, int[] selectedIndices) {
if (myPropertiesPanel == null) {
myPropertiesPanel = new FormLayoutColumnProperties();
}
myPropertiesPanel.showProperties(container, isRow, selectedIndices);
return myPropertiesPanel;
}
@Override
public ActionGroup getCaptionActions() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new InsertBeforeAction());
group.add(new InsertAfterAction());
group.add(new SplitAction());
group.add(new DeleteAction());
group.add(new GroupRowsColumnsAction());
group.add(new UngroupRowsColumnsAction());
return group;
}
@Override
public boolean canCellGrow(RadContainer container, boolean isRow, int index) {
FormLayout layout = (FormLayout) container.getLayout();
FormSpec spec = isRow ? layout.getRowSpec(index+1) : layout.getColumnSpec(index+1);
return spec.getResizeWeight() > 0.01d;
}
@Override
public void setChildDragging(RadComponent child, boolean dragging) {
// do nothing here - otherwise the layout will jump around
}
@Override
public void paintCaptionDecoration(final RadContainer container, final boolean isRow, final int index, final Graphics2D g2d,
final Rectangle rc) {
// don't paint gap rows/columns with red background
if (isGapCell(container, isRow, index)) {
g2d.setColor(Color.LIGHT_GRAY);
g2d.fillRect(rc.x, rc.y, rc.width, rc.height);
}
if (canCellGrow(container, isRow, index)) {
drawGrowMarker(isRow, g2d, rc);
}
FormLayout layout = (FormLayout) container.getLayout();
int[][] groups = isRow ? layout.getRowGroups() : layout.getColumnGroups();
//noinspection MultipleVariablesInDeclaration
boolean haveTopLeft = false, haveTopRight = false, haveTopLine = false;
//noinspection MultipleVariablesInDeclaration
boolean haveBottomLeft = false, haveBottomRight = false, haveBottomLine = false;
boolean inGroup = false;
for(int i=0; i<groups.length; i++) {
int minMember = Integer.MAX_VALUE;
int maxMember = -1;
for(int member: groups [i]) {
minMember = Math.min(member-1, minMember);
maxMember = Math.max(member-1, maxMember);
inGroup = inGroup || (member-1 == index);
}
if (minMember <= index && index <= maxMember) {
if (i % 2 == 0) {
haveTopLeft = haveTopLeft || index > minMember;
haveTopRight = haveTopRight || index < maxMember;
haveTopLine = haveTopLine || inGroup;
}
else {
haveBottomLeft = haveBottomLeft || index > minMember;
haveBottomRight = haveBottomRight || index < maxMember;
haveBottomLine = haveBottomLine || inGroup;
}
}
}
g2d.setColor(PlatformColors.BLUE);
drawGroupLine(rc, isRow, g2d, true, haveTopLeft, haveTopRight, haveTopLine);
drawGroupLine(rc, isRow, g2d, false, haveBottomLeft, haveBottomRight, haveBottomLine);
}
private static void drawGroupLine(final Rectangle rc, final boolean isRow, final Graphics2D g2d, boolean isTop,
final boolean haveLeft, final boolean haveRight, final boolean haveLine) {
int maxX = (int) rc.getMaxX();
int maxY = (int) rc.getMaxY();
Point linePos = isTop ? new Point(rc.x+1, rc.y+1) : new Point(rc.x+3, rc.y+3);
Point markerPos = new Point(rc.x+6, rc.y+6);
int midX = (int) rc.getCenterX();
int midY = (int) rc.getCenterY();
if (haveLine) {
if (isRow) {
g2d.drawLine(linePos.x, midY, markerPos.x, midY);
}
else {
g2d.drawLine(midX, linePos.y, midX, markerPos.y);
}
}
if (haveLeft) {
if (isRow) {
g2d.drawLine(linePos.x, rc.y, linePos.x, midY);
}
else {
g2d.drawLine(rc.x, linePos.y, midX, linePos.y);
}
}
if (haveRight) {
if (isRow) {
g2d.drawLine(linePos.x, midY, linePos.x, maxY);
}
else {
g2d.drawLine(midX, linePos.y, maxX, linePos.y);
}
}
}
@Override
public Property[] getContainerProperties(final Project project) {
return Property.EMPTY_ARRAY;
}
@Override
public Property[] getComponentProperties(final Project project, final RadComponent component) {
return new Property[] {
HorzAlignProperty.getInstance(project),
VertAlignProperty.getInstance(project),
new ComponentInsetsProperty()
};
}
@Override
public int insertGridCells(final RadContainer grid, final int cellIndex, final boolean isRow, final boolean isBefore, final boolean grow) {
FormSpec formSpec;
if (isRow) {
formSpec = grow ? new RowSpec(ENCODED_FORMSPEC_GROW) : new RowSpec(DEFAULT_NOGROW_SIZE);
}
else {
formSpec = grow ? new ColumnSpec(ENCODED_FORMSPEC_GROW) : new ColumnSpec(DEFAULT_NOGROW_SIZE);
}
insertGridCells(grid, cellIndex, isRow, isBefore, formSpec);
return getGridCellCount(grid, isRow) == 1 ? 1 : 2;
}
@Override
public void copyGridCells(RadContainer source, final RadContainer destination, final boolean isRow, int cellIndex, int cellCount, int targetIndex) {
FormLayout sourceLayout = getFormLayout(source);
FormLayout destinationLayout = getFormLayout(destination);
if (isRow) {
insertOrAppendRow(destinationLayout, targetIndex+1, FormFactory.RELATED_GAP_ROWSPEC);
}
else {
insertOrAppendColumn(destinationLayout, targetIndex+1, FormFactory.RELATED_GAP_COLSPEC);
}
targetIndex++;
if (targetIndex < cellIndex) cellIndex++;
copyFormSpecs(sourceLayout, destinationLayout, isRow, cellIndex, cellCount, targetIndex);
updateGridConstraintsFromCellConstraints(destination);
}
private static void copyFormSpecs(final FormLayout sourceLayout,
final FormLayout destinationLayout,
final boolean isRow,
int cellIndex,
int cellCount,
int targetIndex) {
for(int i=0; i < cellCount; i++) {
if (isRow) {
RowSpec rowSpec = sourceLayout.getRowSpec(cellIndex + 1);
insertOrAppendRow(destinationLayout, targetIndex+1, rowSpec);
}
else {
ColumnSpec colSpec = sourceLayout.getColumnSpec(cellIndex + 1);
insertOrAppendColumn(destinationLayout, targetIndex+1, colSpec);
}
cellIndex += (targetIndex < cellIndex && sourceLayout == destinationLayout) ? 2 : 1;
targetIndex++;
}
}
@Override
public void copyGridSection(final RadContainer source, final RadContainer destination, final Rectangle rc) {
final FormLayout destinationLayout = new FormLayout();
destination.setLayout(destinationLayout);
copyFormSpecs(getFormLayout(source), destinationLayout, true, rc.y, rc.height, 0);
copyFormSpecs(getFormLayout(source), destinationLayout, false, rc.x, rc.width, 0);
}
@Override
public int getGapCellCount() {
return 1;
}
@Override
public int getGapCellSize(final RadContainer container, boolean isRow) {
Size size = isRow ? FormFactory.RELATED_GAP_ROWSPEC.getSize() : FormFactory.RELATED_GAP_COLSPEC.getSize();
if (size instanceof ConstantSize) {
return ((ConstantSize) size).getPixelSize(container.getDelegee());
}
return 0;
}
@Override
public boolean isGapCell(RadContainer grid, boolean isRow, int cellIndex) {
if (cellIndex < 0 || cellIndex >= getGridCellCount(grid, isRow)) {
return false;
}
if (cellIndex % 2 == 1) {
final GridChangeUtil.CellStatus status = GridChangeUtil.canDeleteCell(grid, cellIndex, isRow);
if (status == GridChangeUtil.CellStatus.Empty || status == GridChangeUtil.CellStatus.Redundant) {
return true;
}
}
return false;
}
@Override
public int getCellIndexBase() {
return 1;
}
/**
* @return index where new column or row was actually inserted (0-based)
*/
private int insertGridCells(RadContainer grid, int cellIndex, boolean isRow, boolean isBefore, FormSpec formSpec) {
FormLayout formLayout = (FormLayout) grid.getLayout();
int index = isBefore ? cellIndex+1 : cellIndex+2;
if (isRow) {
if (getGridCellCount(grid, true) > 0) {
insertOrAppendRow(formLayout, index, FormFactory.RELATED_GAP_ROWSPEC);
if (!isBefore) index++;
}
insertOrAppendRow(formLayout, index, (RowSpec) formSpec);
}
else {
if (getGridCellCount(grid, false) > 0) {
insertOrAppendColumn(formLayout, index, FormFactory.RELATED_GAP_COLSPEC);
if (!isBefore) index++;
}
insertOrAppendColumn(formLayout, index, (ColumnSpec)formSpec);
}
updateGridConstraintsFromCellConstraints(grid);
return index-1;
}
private static void insertOrAppendRow(final FormLayout formLayout, final int index, final RowSpec rowSpec) {
if (index == formLayout.getRowCount()+1) {
formLayout.appendRow(rowSpec);
}
else {
formLayout.insertRow(index, rowSpec);
}
}
private static void insertOrAppendColumn(final FormLayout formLayout, final int index, final ColumnSpec columnSpec) {
if (index == formLayout.getColumnCount()+1) {
formLayout.appendColumn(columnSpec);
}
else {
formLayout.insertColumn(index, columnSpec);
}
}
@Override
public int deleteGridCells(final RadContainer grid, final int cellIndex, final boolean isRow) {
int result = 1;
FormLayout formLayout = (FormLayout) grid.getLayout();
adjustDeletedCellOrigins(grid, cellIndex, isRow);
if (isRow) {
int[][] groupIndices = formLayout.getRowGroups();
groupIndices = removeDeletedCell(groupIndices, cellIndex+1);
formLayout.setRowGroups(groupIndices);
formLayout.removeRow(cellIndex+1);
updateGridConstraintsFromCellConstraints(grid);
if (formLayout.getRowCount() > 0 && formLayout.getRowCount() % 2 == 0) {
int gapRowIndex = (cellIndex >= grid.getGridRowCount()) ? cellIndex-1 : cellIndex;
if (GridChangeUtil.isRowEmpty(grid, gapRowIndex)) {
formLayout.removeRow(gapRowIndex+1);
updateGridConstraintsFromCellConstraints(grid);
result++;
}
}
}
else {
int[][] groupIndices = formLayout.getColumnGroups();
groupIndices = removeDeletedCell(groupIndices, cellIndex+1);
formLayout.setColumnGroups(groupIndices);
formLayout.removeColumn(cellIndex+1);
updateGridConstraintsFromCellConstraints(grid);
if (formLayout.getColumnCount() > 0 && formLayout.getColumnCount() % 2 == 0) {
int gapColumnIndex = (cellIndex >= grid.getGridColumnCount()) ? cellIndex-1 : cellIndex;
if (GridChangeUtil.isColumnEmpty(grid, gapColumnIndex)) {
formLayout.removeColumn(gapColumnIndex+1);
updateGridConstraintsFromCellConstraints(grid);
result++;
}
}
}
return result;
}
private void adjustDeletedCellOrigins(final RadContainer grid, final int cellIndex, final boolean isRow) {
int gapCellDelta = isGapCell(grid, isRow, cellIndex+1) ? 2 : 1;
for(RadComponent component: grid.getComponents()) {
// ensure that we don't have component origins in the deleted cells
final GridConstraints gc = component.getConstraints();
if (gc.getCell(isRow) == cellIndex) {
final int span = gc.getSpan(isRow);
if (span > gapCellDelta) {
gc.setCell(isRow, cellIndex+gapCellDelta);
gc.setSpan(isRow, span -gapCellDelta);
updateConstraints(component);
}
else {
throw new IllegalArgumentException("Attempt to delete grid row/column which contains origins of 1-span components");
}
}
}
}
private static int[][] removeDeletedCell(final int[][] groupIndices, final int deletedIndex) {
for(int i=0; i<groupIndices.length; i++) {
for(int j=0; j<groupIndices [i].length; j++) {
if (groupIndices [i][j] == deletedIndex) {
int[][] newIndices;
if (groupIndices [i].length <= 2) {
// deleted cell is contained in a group with 1 or 2 cells => delete entire group
newIndices = new int[groupIndices.length-1][];
for (int newI = 0; newI < i; newI++) {
newIndices [newI] = new int[groupIndices [newI].length];
System.arraycopy(groupIndices [newI], 0, newIndices [newI], 0, groupIndices [newI].length);
}
for(int newI=i+1; newI<groupIndices.length; newI++) {
newIndices [newI-1] = new int[groupIndices [newI].length];
System.arraycopy(groupIndices [newI], 0, newIndices [newI-1], 0, groupIndices [newI].length);
}
}
else {
// deleted cell is contained in a group with more than 2 cells => keep the group and delete only the item
newIndices = new int[groupIndices.length][];
for(int newI=0; newI<groupIndices.length; newI++) {
if (newI == i) {
newIndices [newI] = new int[groupIndices [newI].length-1];
System.arraycopy(groupIndices [newI], 0, newIndices [newI], 0, j);
System.arraycopy(groupIndices [newI], j+1, newIndices [newI], j, groupIndices [i].length-j-1);
}
else {
newIndices [newI] = new int[groupIndices [newI].length];
System.arraycopy(groupIndices [newI], 0, newIndices [newI], 0, groupIndices [i].length);
}
}
}
return newIndices;
}
}
}
return groupIndices;
}
@Override @Nullable
public String getCellResizeTooltip(RadContainer container, boolean isRow, int cell, int newSize) {
final String size = getUpdatedSize(container, isRow, cell, newSize).toString();
return isRow
? UIDesignerBundle.message("tooltip.resize.row", cell+getCellIndexBase(), size)
: UIDesignerBundle.message("tooltip.resize.column", cell+getCellIndexBase(), size);
}
@Override
public void processCellResized(RadContainer container, final boolean isRow, final int cell, final int newSize) {
FormLayout formLayout = (FormLayout) container.getLayout();
final ConstantSize updatedSize = getUpdatedSize(container, isRow, cell, newSize);
FormSpec newSpec;
if (isRow) {
RowSpec rowSpec = formLayout.getRowSpec(cell+1);
newSpec = new RowSpec(rowSpec.getDefaultAlignment(), updatedSize, rowSpec.getResizeWeight());
}
else {
ColumnSpec colSpec = formLayout.getColumnSpec(cell+1);
newSpec = new ColumnSpec(colSpec.getDefaultAlignment(), updatedSize, colSpec.getResizeWeight());
}
setSpec(formLayout, newSpec, cell+1, isRow);
resizeSameGroupCells(cell, formLayout, newSpec, isRow);
}
// Explicitly resize all cells in the group to desired size to make sure that the resize operation is effective (IDEADEV-10202)
private static void resizeSameGroupCells(final int cell, final FormLayout formLayout, final FormSpec newSpec, final boolean isRow) {
int[][] groups = isRow ? formLayout.getRowGroups() : formLayout.getColumnGroups();
for(int[] group: groups) {
boolean foundGroup = false;
for(int groupCell: group) {
if (groupCell == cell+1) {
foundGroup = true;
break;
}
}
if (foundGroup) {
for(int groupCell: group) {
setSpec(formLayout, newSpec, groupCell, isRow);
}
break;
}
}
}
private static void setSpec(final FormLayout formLayout, final FormSpec newSpec, final int cell, boolean isRow) {
if (isRow) {
formLayout.setRowSpec(cell, (RowSpec) newSpec);
}
else {
formLayout.setColumnSpec(cell, (ColumnSpec) newSpec);
}
}
private static ConstantSize getUpdatedSize(RadContainer container, boolean isRow, int cell, int newPx) {
FormLayout formLayout = (FormLayout) container.getLayout();
if (isRow) {
return scaleSize(formLayout.getRowSpec(cell+1), container, newPx);
}
else {
return scaleSize(formLayout.getColumnSpec(cell+1), container, newPx);
}
}
private static ConstantSize scaleSize(final FormSpec rowSpec, final RadContainer container, final int newPx) {
if (rowSpec.getSize() instanceof ConstantSize) {
ConstantSize oldSize = (ConstantSize) rowSpec.getSize();
int oldPx = oldSize.getPixelSize(container.getDelegee());
double newValue = Math.round(oldSize.getValue() * newPx / oldPx * 10) / 10;
return new ConstantSize(newValue, oldSize.getUnit());
}
return new ConstantSize(newPx, ConstantSize.PIXEL);
}
@Override
public void processCellsMoved(final RadContainer container, final boolean isRow, final int[] cellsToMove, int targetCell) {
for(int i=0; i<cellsToMove.length; i++) {
final int sourceCell = cellsToMove[i];
moveCells(container, isRow, sourceCell, targetCell);
if (sourceCell < targetCell) {
for(int j=i+1; j<cellsToMove.length; j++) {
cellsToMove [j] -= 2;
}
}
else {
targetCell += 2;
}
}
}
private void moveCells(final RadContainer container, final boolean isRow, final int cell, int targetCell) {
if (targetCell >= cell && targetCell <= cell+2) {
return;
}
FormLayout layout = (FormLayout) container.getLayout();
List<RadComponent> componentsToMove = new ArrayList<RadComponent>();
FormSpec oldSpec = isRow ? layout.getRowSpec(cell+1) : layout.getColumnSpec(cell+1);
for(RadComponent c: container.getComponents()) {
if (c.getConstraints().getCell(isRow) == cell) {
componentsToMove.add(c);
container.removeComponent(c);
}
}
int count = deleteGridCells(container, cell, isRow);
int insertCell = (targetCell > cell) ? targetCell - count - 1 : targetCell;
final boolean isBefore = targetCell < cell;
int newIndex = insertGridCells(container, insertCell, isRow, isBefore, oldSpec);
for(RadComponent c: componentsToMove) {
c.getConstraints().setCell(isRow, newIndex);
container.addComponent(c);
}
}
private static void updateGridConstraintsFromCellConstraints(RadContainer grid) {
FormLayout layout = (FormLayout) grid.getLayout();
for(RadComponent c: grid.getComponents()) {
CellConstraints cc = layout.getConstraints(c.getDelegee());
GridConstraints gc = c.getConstraints();
copyCellToGridConstraints(gc, cc);
}
}
private static void copyCellToGridConstraints(final GridConstraints gc, final CellConstraints cc) {
gc.setColumn(cc.gridX-1);
gc.setRow(cc.gridY-1);
gc.setColSpan(cc.gridWidth);
gc.setRowSpan(cc.gridHeight);
}
public int getAlignment(RadComponent component, boolean horizontal) {
CellConstraints cc = (CellConstraints) component.getCustomLayoutConstraints();
CellConstraints.Alignment al = horizontal ? cc.hAlign : cc.vAlign;
if (al == CellConstraints.DEFAULT) {
FormLayout formLayout = (FormLayout) component.getParent().getLayout();
FormSpec formSpec = horizontal
? formLayout.getColumnSpec(component.getConstraints().getColumn()+1)
: formLayout.getRowSpec(component.getConstraints().getRow()+1);
final FormSpec.DefaultAlignment defaultAlignment = formSpec.getDefaultAlignment();
if (defaultAlignment.equals(RowSpec.FILL)) {
return GridConstraints.ALIGN_FILL;
}
if (defaultAlignment.equals(RowSpec.TOP) || defaultAlignment.equals(ColumnSpec.LEFT)) {
return GridConstraints.ALIGN_LEFT;
}
if (defaultAlignment.equals(RowSpec.CENTER)) {
return GridConstraints.ALIGN_CENTER;
}
return GridConstraints.ALIGN_RIGHT;
}
return Utils.alignFromConstraints(component.getConstraints(), horizontal);
}
public void setAlignment(RadComponent component, boolean horizontal, int alignment) {
CellConstraints cc = (CellConstraints) component.getCustomLayoutConstraints();
if (horizontal) {
cc.hAlign = FormLayoutSerializer.ourHorizontalAlignments [alignment];
}
else {
cc.vAlign = FormLayoutSerializer.ourVerticalAlignments [alignment];
}
updateConstraints(component);
}
public void resetAlignment(RadComponent component, boolean horizontal) {
CellConstraints cc = (CellConstraints) component.getCustomLayoutConstraints();
if (horizontal) {
cc.hAlign = CellConstraints.DEFAULT;
}
else {
cc.vAlign = CellConstraints.DEFAULT;
}
updateConstraints(component);
}
public boolean isAlignmentModified(RadComponent component, boolean horizontal) {
CellConstraints cc = (CellConstraints) component.getCustomLayoutConstraints();
CellConstraints.Alignment al = horizontal ? cc.hAlign : cc.vAlign;
return al != CellConstraints.DEFAULT;
}
@Override
protected void updateConstraints(final RadComponent component) {
FormLayout layout = (FormLayout) component.getParent().getLayout();
layout.setConstraints(component.getDelegee(), gridToCellConstraints(component));
super.updateConstraints(component);
}
public int getMinCellCount() {
return 0;
}
@Override
public void createSnapshotLayout(final SnapshotContext context,
final JComponent parent,
final RadContainer container,
final LayoutManager layout) {
ColumnSpec[] colSpecs;
RowSpec[] rowSpecs;
int[][] rowGroups;
int[][] columnGroups;
try {
Method method = layout.getClass().getMethod("getRowCount", ArrayUtil.EMPTY_CLASS_ARRAY);
int rowCount = ((Integer)method.invoke(layout, ArrayUtil.EMPTY_OBJECT_ARRAY)).intValue();
method = layout.getClass().getMethod("getColumnCount", ArrayUtil.EMPTY_CLASS_ARRAY);
int columnCount = ((Integer)method.invoke(layout, ArrayUtil.EMPTY_OBJECT_ARRAY)).intValue();
rowSpecs = new RowSpec[rowCount];
colSpecs = new ColumnSpec[columnCount];
method = layout.getClass().getMethod("getRowSpec", int.class);
for (int i = 0; i < rowCount; i++) {
rowSpecs[i] = (RowSpec)createSerializedCopy(method.invoke(layout, i + 1));
}
method = layout.getClass().getMethod("getColumnSpec", int.class);
for (int i = 0; i < columnCount; i++) {
colSpecs[i] = (ColumnSpec)createSerializedCopy(method.invoke(layout, i + 1));
}
method = layout.getClass().getMethod("getRowGroups", ArrayUtil.EMPTY_CLASS_ARRAY);
rowGroups = (int[][])method.invoke(layout);
method = layout.getClass().getMethod("getColumnGroups", ArrayUtil.EMPTY_CLASS_ARRAY);
columnGroups = (int[][])method.invoke(layout);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
final FormLayout formLayout = new FormLayout(colSpecs, rowSpecs);
formLayout.setRowGroups(rowGroups);
formLayout.setColumnGroups(columnGroups);
container.setLayout(formLayout);
}
private static Object createSerializedCopy(final Object original) {
// FormLayout may have been loaded with a different classloader, so we need to create a copy through serialization
Object copy;
try {
BufferExposingByteArrayOutputStream baos = new BufferExposingByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(baos);
try {
os.writeObject(original);
}
finally {
os.close();
}
InputStream bais = new ByteArrayInputStream(baos.getInternalBuffer(), 0, baos.size());
ObjectInputStream is = new ObjectInputStream(bais);
try {
copy = is.readObject();
}
finally {
is.close();
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
return copy;
}
@Override
public void addSnapshotComponent(final JComponent parent,
final JComponent child,
final RadContainer container,
final RadComponent component) {
CellConstraints cc;
try {
LayoutManager layout = parent.getLayout();
//noinspection HardCodedStringLiteral
Method method = layout.getClass().getMethod("getConstraints", Component.class);
cc = (CellConstraints)createSerializedCopy(method.invoke(layout, child));
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
copyCellToGridConstraints(component.getConstraints(), cc);
component.setCustomLayoutConstraints(cc);
container.addComponent(component);
}
private static class ComponentInsetsProperty extends AbstractInsetsProperty<RadComponent> {
public ComponentInsetsProperty() {
super(null, "Insets");
}
public Insets getValue(final RadComponent component) {
if (component.getCustomLayoutConstraints() instanceof CellConstraints) {
final CellConstraints cellConstraints = (CellConstraints)component.getCustomLayoutConstraints();
return cellConstraints.insets;
}
return new Insets(0, 0, 0, 0);
}
protected void setValueImpl(final RadComponent component, final Insets value) throws Exception {
if (component.getCustomLayoutConstraints() instanceof CellConstraints) {
final CellConstraints cellConstraints = (CellConstraints)component.getCustomLayoutConstraints();
cellConstraints.insets = value;
FormLayout layout = (FormLayout) component.getParent().getLayout();
CellConstraints cc = (CellConstraints)layout.getConstraints(component.getDelegee()).clone();
cc.insets = value;
layout.setConstraints(component.getDelegee(), cc);
}
}
}
}