| /* |
| * 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); |
| } |
| } |
| } |
| } |