blob: a6530568733a6faddf57f4e2635c71b6677797cf [file] [log] [blame]
/*
* Copyright 2000-2014 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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Couple;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.uiDesigner.core.Util;
import com.intellij.uiDesigner.designSurface.GuiEditor;
import com.intellij.uiDesigner.palette.ComponentItem;
import com.intellij.uiDesigner.palette.Palette;
import com.intellij.uiDesigner.radComponents.*;
import com.intellij.uiDesigner.shared.XYLayoutManager;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
/**
* @author yole
*/
public class GridBuildUtil {
private static final Logger LOG=Logger.getInstance("#com.intellij.uiDesigner.GridBuildUtil");
private final static int HORIZONTAL_GRID = 1;
private final static int VERTICAL_GRID = 2;
private final static int GRID = 3;
/**
* TODO[anton,vova]: most likely should be equal to "xy grid step" when available
*/
private static final int GRID_TREMOR = 5;
private GridBuildUtil() {
}
public static void breakGrid(final GuiEditor editor) {
final ArrayList<RadComponent> selection = FormEditingUtil.getSelectedComponents(editor);
if (selection.size() != 1){
return;
}
if (!(selection.get(0) instanceof RadContainer)) {
return;
}
final RadContainer container = (RadContainer)selection.get(0);
if (
container instanceof RadScrollPane ||
container instanceof RadSplitPane ||
container instanceof RadTabbedPane
){
return;
}
final RadContainer parent = container.getParent();
if (parent instanceof RadRootContainer) {
editor.getRootContainer().setMainComponentBinding(container.getBinding());
}
// XY can be broken only if its parent is also XY.
// In other words, breaking of XY is a deletion of unnecessary intermediate panel
if (container.isXY() && !parent.isXY()) {
return;
}
if (parent != null && parent.isXY()) {
// parent is XY
// put the contents of the container into 'parent' and remove 'container'
final int dx = container.getX();
final int dy = container.getY();
while (container.getComponentCount() > 0) {
final RadComponent component = container.getComponent(0);
component.shift(dx, dy);
parent.addComponent(component);
}
parent.removeComponent(container);
}
else {
// container becomes XY
final XYLayoutManager xyLayout = new XYLayoutManagerImpl();
container.setLayout(xyLayout);
xyLayout.setPreferredSize(container.getSize());
}
editor.refreshAndSave(true);
}
public static void convertToVerticalGrid(final GuiEditor editor){
convertToGridImpl(editor, VERTICAL_GRID);
}
public static void convertToHorizontalGrid(final GuiEditor editor){
convertToGridImpl(editor, HORIZONTAL_GRID);
}
public static void convertToGrid(final GuiEditor editor){
convertToGridImpl(editor, GRID);
}
private static void convertToGridImpl(final GuiEditor editor, final int gridType) {
final boolean createNewContainer;
final RadContainer parent;
final RadComponent[] componentsToConvert;
{
final ArrayList<RadComponent> selection = FormEditingUtil.getSelectedComponents(editor);
if (selection.size() == 0) {
// root container selected
final RadRootContainer rootContainer = editor.getRootContainer();
if (rootContainer.getComponentCount() < 2) {
// nothing to convert
return;
}
componentsToConvert = new RadComponent[rootContainer.getComponentCount()];
for (int i = 0; i < componentsToConvert.length; i++) {
componentsToConvert[i] = rootContainer.getComponent(i);
}
parent = rootContainer;
createNewContainer = true;
}
else if (selection.size() == 1 && selection.get(0) instanceof RadContainer) {
parent = (RadContainer)selection.get(0);
componentsToConvert = new RadComponent[parent.getComponentCount()];
for (int i = 0; i < componentsToConvert.length; i++) {
componentsToConvert[i] = parent.getComponent(i);
}
createNewContainer = false;
}
else {
componentsToConvert = selection.toArray(new RadComponent[selection.size()]);
parent = selection.get(0).getParent();
createNewContainer = true;
}
}
if (!parent.isXY()) {
// only components in XY can be layed out in grid
return;
}
for (int i = 1; i < componentsToConvert.length; i++) {
final RadComponent component = componentsToConvert[i];
if (component.getParent() != parent) {
return;
}
}
final GridLayoutManager gridLayoutManager;
if (componentsToConvert.length == 0) {
// we convert empty XY panel to grid
gridLayoutManager = new GridLayoutManager(1,1);
}
else {
if (gridType == VERTICAL_GRID) {
gridLayoutManager = createOneDimensionGrid(componentsToConvert, true);
}
else if (gridType == HORIZONTAL_GRID) {
gridLayoutManager = createOneDimensionGrid(componentsToConvert, false);
}
else if (gridType == GRID) {
gridLayoutManager = createTwoDimensionGrid(componentsToConvert);
}
else {
throw new IllegalArgumentException("invalid grid type: " + gridType);
}
}
for (final RadComponent component : componentsToConvert) {
if (component instanceof RadContainer) {
final LayoutManager layout = ((RadContainer)component).getLayout();
if (layout instanceof XYLayoutManager) {
((XYLayoutManager)layout).setPreferredSize(component.getSize());
}
}
}
if (createNewContainer) {
// we should create a new panel
final Module module = editor.getModule();
final ComponentItem panelItem = Palette.getInstance(editor.getProject()).getPanelItem();
final RadContainer newContainer = new RadContainer(editor, FormEditingUtil.generateId(editor.getRootContainer()));
newContainer.setLayout(gridLayoutManager);
newContainer.init(editor, panelItem);
for (RadComponent componentToConvert : componentsToConvert) {
newContainer.addComponent(componentToConvert);
}
final Point topLeftPoint = getTopLeftPoint(componentsToConvert);
newContainer.setLocation(topLeftPoint);
final Point bottomRightPoint = getBottomRightPoint(componentsToConvert);
final Dimension size = new Dimension(bottomRightPoint.x - topLeftPoint.x, bottomRightPoint.y - topLeftPoint.y);
Util.adjustSize(newContainer.getDelegee(), newContainer.getConstraints(), size);
newContainer.getDelegee().setSize(size);
parent.addComponent(newContainer);
FormEditingUtil.clearSelection(editor.getRootContainer());
newContainer.setSelected(true);
// restore binding of main component
{
final String mainComponentBinding = editor.getRootContainer().getMainComponentBinding();
if (mainComponentBinding != null && parent instanceof RadRootContainer) {
newContainer.setBinding(mainComponentBinding);
editor.getRootContainer().setMainComponentBinding(null);
}
}
}
else {
// convert entire 'parent' to grid
parent.setLayout(gridLayoutManager);
FormEditingUtil.clearSelection(editor.getRootContainer());
parent.setSelected(true);
}
editor.refreshAndSave(true);
}
private static GridLayoutManager createOneDimensionGrid(final RadComponent[] selection, final boolean isVertical){
Arrays.sort(
selection,
new Comparator<RadComponent>() {
public int compare(final RadComponent o1, final RadComponent o2){
final Rectangle bounds1 = o1.getBounds();
final Rectangle bounds2 = o2.getBounds();
if (isVertical) {
return (bounds1.y + bounds1.height / 2) - (bounds2.y + bounds2.height / 2);
}
else {
return (bounds1.x + bounds1.width / 2) - (bounds2.x + bounds2.width / 2);
}
}
}
);
for (int i = 0; i < selection.length; i++) {
final RadComponent component = selection[i];
final GridConstraints constraints = component.getConstraints();
if (isVertical) {
constraints.setRow(i);
constraints.setColumn(0);
}
else {
constraints.setRow(0);
constraints.setColumn(i);
}
constraints.setRowSpan(1);
constraints.setColSpan(1);
}
final GridLayoutManager gridLayoutManager;
if (isVertical) {
gridLayoutManager = new GridLayoutManager(selection.length, 1);
}
else {
gridLayoutManager = new GridLayoutManager(1, selection.length);
}
return gridLayoutManager;
}
/**
* @param x array of <code>X</code> coordinates of components that should be layed out in a grid.
* This is input/output parameter.
*
* @param y array of <code>Y</code> coordinates of components that should be layed out in a grid.
* This is input/output parameter.
*
* @param rowSpans output parameter.
*
* @param colSpans output parameter.
*
* @return pair that says how many (rows, columns) are in the composed grid.
*/
public static Couple<Integer> layoutInGrid(
final int[] x,
final int[] y,
final int[] rowSpans,
final int[] colSpans
){
LOG.assertTrue(x.length == y.length);
LOG.assertTrue(y.length == colSpans.length);
LOG.assertTrue(colSpans.length == rowSpans.length);
for (int i = 0; i < x.length; i++) {
colSpans[i] = Math.max(colSpans[i], 1);
rowSpans[i] = Math.max(rowSpans[i], 1);
if (colSpans[i] > GRID_TREMOR * 4) {
colSpans[i] -= GRID_TREMOR * 2;
x[i] += GRID_TREMOR;
}
if (rowSpans[i] > GRID_TREMOR * 4) {
rowSpans[i] -= GRID_TREMOR * 2;
y[i] += GRID_TREMOR;
}
}
return Couple.of(
new Integer(Util.eliminate(y, rowSpans, null)),
new Integer(Util.eliminate(x, colSpans, null))
);
}
private static GridLayoutManager createTwoDimensionGrid(final RadComponent[] selection){
final int[] x = new int[selection.length];
final int[] y = new int[selection.length];
final int[] colSpans = new int[selection.length];
final int[] rowSpans = new int[selection.length];
for (int i = selection.length - 1; i >= 0; i--) {
x[i] = selection[i].getX();
y[i] = selection[i].getY();
rowSpans[i] = selection[i].getHeight();
colSpans[i] = selection[i].getWidth();
}
final Couple<Integer> pair = layoutInGrid(x, y, rowSpans, colSpans);
for (int i = 0; i < selection.length; i++) {
final RadComponent component = selection[i];
final GridConstraints constraints = component.getConstraints();
constraints.setRow(y[i]);
constraints.setRowSpan(rowSpans[i]);
constraints.setColumn(x[i]);
constraints.setColSpan(colSpans[i]);
}
return new GridLayoutManager(pair.first.intValue(), pair.second.intValue());
}
/**
* Find top left point of component group, i.e. (minimum X of all components; minimum Y of all components)
* @param components array should contain at least one element
*/
private static Point getTopLeftPoint(final RadComponent[] components){
LOG.assertTrue(components.length > 0);
final Point point = new Point(Integer.MAX_VALUE, Integer.MAX_VALUE);
for (final RadComponent component : components) {
point.x = Math.min(component.getX(), point.x);
point.y = Math.min(component.getY(), point.y);
}
return point;
}
/**
* Find bottom right point of component group, i.e. (maximum (x + width) of all components; maximum (y + height) of all components)
* @param components array should contain at least one element
*/
private static Point getBottomRightPoint(final RadComponent[] components){
LOG.assertTrue(components.length > 0);
final Point point = new Point(Integer.MIN_VALUE, Integer.MIN_VALUE);
for (final RadComponent component : components) {
point.x = Math.max(component.getX() + component.getWidth(), point.x);
point.y = Math.max(component.getY() + component.getHeight(), point.y);
}
return point;
}
}