From 077e34008d3360b93351ee92ad66a0690644fba3 Mon Sep 17 00:00:00 2001 From: Alexander Lobas Date: Wed, 6 Jun 2012 20:01:14 +0400 Subject: [PATCH] Morphing --- .../designer/model/ComponentMorphingTool.java | 13 +- .../model/morphing/AbsoluteLayout.java | 53 ++++++ .../designer/model/morphing/FrameLayout.java | 169 ++++++++++++++++++ .../designer/model/morphing/GridLayout.java | 75 ++++++++ .../model/morphing/RelativeLayout.java | 49 +++++ .../designer/model/morphing/TableLayout.java | 97 ++++++++++ .../designer/model/views-meta-model.xml | 14 +- 7 files changed, 460 insertions(+), 10 deletions(-) create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/morphing/AbsoluteLayout.java create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/morphing/FrameLayout.java create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/morphing/GridLayout.java create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/morphing/RelativeLayout.java create mode 100644 plugins/android-designer/src/com/intellij/android/designer/model/morphing/TableLayout.java diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/ComponentMorphingTool.java b/plugins/android-designer/src/com/intellij/android/designer/model/ComponentMorphingTool.java index a66c2b5a2c34..c8c6792f386d 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/ComponentMorphingTool.java +++ b/plugins/android-designer/src/com/intellij/android/designer/model/ComponentMorphingTool.java @@ -15,6 +15,7 @@ */ package com.intellij.android.designer.model; +import com.intellij.android.designer.model.morphing.RelativeLayout; import com.intellij.designer.model.MetaModel; import com.intellij.designer.model.RadComponent; import com.intellij.designer.model.RadLayout; @@ -97,7 +98,15 @@ public class ComponentMorphingTool { "com.intellij.android.designer.model.morphing." + component.getMetaModel().getTag()); Object sourceConverter = sourceConverterClass.newInstance(); - Method method = sourceConverterClass.getMethod(target.getTag(), RadViewComponent.class, MetaModel.class); - return (RadViewComponent)method.invoke(sourceConverter, component, target); + try { + Method method = sourceConverterClass.getMethod(target.getTag(), RadViewComponent.class, MetaModel.class); + return (RadViewComponent)method.invoke(sourceConverter, component, target); + } + catch (NoSuchMethodException e) { + if ("RelativeLayout".equals(target.getTag())) { + return RelativeLayout.RelativeLayout(component, target); + } + throw e; + } } } \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/morphing/AbsoluteLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/AbsoluteLayout.java new file mode 100644 index 000000000000..67fc45a1e267 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/AbsoluteLayout.java @@ -0,0 +1,53 @@ +/* + * 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.android.designer.model.morphing; + +import com.intellij.android.designer.model.ComponentMorphingTool; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.android.designer.model.layout.relative.RadRelativeLayout; +import com.intellij.android.designer.model.layout.relative.RadRelativeLayoutComponent; +import com.intellij.designer.model.MetaModel; +import com.intellij.designer.model.RadComponent; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; + +/** + * @author Alexander Lobas + */ +public class AbsoluteLayout { + public static RadViewComponent RelativeLayout(RadViewComponent component, MetaModel target) throws Exception { + return new ComponentMorphingTool(component, new RadRelativeLayoutComponent(), target, new RadRelativeLayout()) { + @Override + protected void convertTag() { + for (RadComponent childComponent : myNewComponent.getChildren()) { + XmlTag tag = ((RadViewComponent)childComponent).getTag(); + + XmlAttribute xAttribute = tag.getAttribute("android:layout_x"); + if (xAttribute != null) { + tag.setAttribute("android:layout_alignParentLeft", "true"); + xAttribute.setName("android:layout_marginLeft"); + } + + XmlAttribute yAttribute = tag.getAttribute("android:layout_y"); + if (yAttribute != null) { + tag.setAttribute("android:layout_alignParentTop", "true"); + yAttribute.setName("android:layout_marginTop"); + } + } + } + }.result(); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/morphing/FrameLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/FrameLayout.java new file mode 100644 index 000000000000..74cda55d4d34 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/FrameLayout.java @@ -0,0 +1,169 @@ +/* + * 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.android.designer.model.morphing; + +import com.intellij.android.designer.model.*; +import com.intellij.android.designer.model.layout.Gravity; +import com.intellij.android.designer.model.layout.grid.RadGridLayout; +import com.intellij.android.designer.model.layout.grid.RadGridLayoutComponent; +import com.intellij.android.designer.model.layout.table.RadTableLayout; +import com.intellij.android.designer.model.layout.table.RadTableLayoutComponent; +import com.intellij.designer.model.MetaModel; +import com.intellij.designer.model.RadComponent; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.containers.hash.HashMap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Alexander Lobas + */ +public class FrameLayout { + private static final String COLUMN_KEY = "Column"; + + public static RadViewComponent TableLayout(RadViewComponent component, MetaModel target) throws Exception { + final MetaModel tableRowModel = ViewsMetaManager.getInstance(component.getTag().getProject()).getModelByTag("TableRow"); + + return new ComponentMorphingTool(component, new RadTableLayoutComponent(), target, new RadTableLayout()) { + @Override + protected void convertChildren() throws Exception { + RadViewComponent[] rowComponents = new RadViewComponent[3]; + Map>> components = + new HashMap>>(); + + for (int i = 0; i < rowComponents.length; i++) { + RadViewComponent newRowComponent = ModelParser.createComponent(null, tableRowModel); + ModelParser.addComponent(myNewComponent, newRowComponent, null); + rowComponents[i] = newRowComponent; + } + + for (RadComponent childComponent : new ArrayList(myOldComponent.getChildren())) { + Pair sides = Gravity.getSides(childComponent); + + RadViewComponent rowComponent = rowComponents[getRowIndex(sides.second)]; + Map> rowMap = components.get(rowComponent); + if (rowMap == null) { + rowMap = new HashMap>(); + components.put(rowComponent, rowMap); + } + + List rowChildren = rowMap.get(sides.first); + if (rowChildren == null) { + rowChildren = new ArrayList(); + rowMap.put(sides.first, rowChildren); + } + rowChildren.add((RadViewComponent)childComponent); + } + + for (RadViewComponent rowComponent : rowComponents) { + Map> rowMap = components.get(rowComponent); + boolean column = moveComponents(rowComponent, rowMap.remove(Gravity.left), -1); + column = moveComponents(rowComponent, rowMap.remove(Gravity.center), column ? 1 : -1); + moveComponents(rowComponent, rowMap.remove(Gravity.right), column ? 2 : -1); + + for (List rowChildren : rowMap.values()) { + moveComponents(rowComponent, rowChildren, -1); + } + } + } + + @Override + protected void convertTag() { + for (RadComponent rowComponent : myNewComponent.getChildren()) { + for (RadComponent cellComponent : rowComponent.getChildren()) { + XmlTag childTag = ((RadViewComponent)cellComponent).getTag(); + ModelParser.deleteAttribute(childTag, "android:layout_gravity"); + + Integer column = cellComponent.extractClientProperty(COLUMN_KEY); + if (column != null) { + childTag.setAttribute("android:layout_column", column.toString()); + } + } + } + } + + @Override + protected void loadChildProperties(PropertyParser propertyParser) throws Exception { + } + }.result(); + } + + private static boolean moveComponents(RadViewComponent container, Collection children, int column) throws Exception { + if (children != null) { + for (RadViewComponent childComponent : children) { + if (column != -1) { + childComponent.setClientProperty(COLUMN_KEY, column); + } + ModelParser.moveComponent(container, childComponent, null); + } + return false; + } + return true; + } + + private static int getRowIndex(Gravity side) { + if (side == Gravity.center) { + return 1; + } + if (side == Gravity.bottom) { + return 2; + } + return 0; + } + + public static RadViewComponent GridLayout(RadViewComponent component, MetaModel target) throws Exception { + return new ComponentMorphingTool(component, new RadGridLayoutComponent(), target, new RadGridLayout()) { + @Override + protected void convertTag() { + XmlTag tag = myNewComponent.getTag(); + tag.setAttribute("android:rowCount", "3"); + tag.setAttribute("android:columnCount", "3"); + + for (RadComponent childComponent : myNewComponent.getChildren()) { + XmlTag childTag = ((RadViewComponent)childComponent).getTag(); + Pair sides = Gravity.getSides(childComponent); + ModelParser.deleteAttribute(childTag, "android:layout_gravity"); + childTag.setAttribute("android:layout_row", getRowIndexValue(sides.second)); + childTag.setAttribute("android:layout_column", getColumnIndexValue(sides.first)); + } + } + }.result(); + } + + private static String getRowIndexValue(Gravity side) { + if (side == Gravity.center) { + return "1"; + } + if (side == Gravity.bottom) { + return "2"; + } + return "0"; + } + + private static String getColumnIndexValue(Gravity side) { + if (side == Gravity.center) { + return "1"; + } + if (side == Gravity.right) { + return "2"; + } + return "0"; + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/morphing/GridLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/GridLayout.java new file mode 100644 index 000000000000..2d106bad3b54 --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/GridLayout.java @@ -0,0 +1,75 @@ +/* + * 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.android.designer.model.morphing; + +import com.intellij.android.designer.model.*; +import com.intellij.android.designer.model.layout.grid.RadGridLayoutComponent; +import com.intellij.android.designer.model.layout.table.RadTableLayout; +import com.intellij.android.designer.model.layout.table.RadTableLayoutComponent; +import com.intellij.designer.model.MetaModel; +import com.intellij.designer.model.RadComponent; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; + +/** + * @author Alexander Lobas + */ +public class GridLayout { + public static RadViewComponent TableLayout(RadViewComponent component, MetaModel target) throws Exception { + final RadComponent[][] components = ((RadGridLayoutComponent)component).getGridComponents(false); + final MetaModel tableRowModel = ViewsMetaManager.getInstance(component.getTag().getProject()).getModelByTag("TableRow"); + + return new ComponentMorphingTool(component, new RadTableLayoutComponent(), target, new RadTableLayout()) { + @Override + protected void convertChildren() throws Exception { + for (RadComponent[] rowComponents : components) { + RadViewComponent newRowComponent = ModelParser.createComponent(null, tableRowModel); + ModelParser.addComponent(myNewComponent, newRowComponent, null); + + for (RadComponent cellComponent : rowComponents) { + if (cellComponent != null) { + ModelParser.moveComponent(newRowComponent, (RadViewComponent)cellComponent, null); + } + } + } + } + + @Override + protected void convertTag() { + XmlTag tag = myNewComponent.getTag(); + ModelParser.deleteAttribute(tag, "android:rowCount"); + ModelParser.deleteAttribute(tag, "android:columnCount"); + + for (RadComponent rowComponent : myNewComponent.getChildren()) { + for (RadComponent cellComponent : rowComponent.getChildren()) { + XmlTag cellTag = ((RadViewComponent)cellComponent).getTag(); + ModelParser.deleteAttribute(cellTag, "android:layout_row"); + ModelParser.deleteAttribute(cellTag, "android:layout_rowSpan"); + + XmlAttribute attribute = cellTag.getAttribute("android:layout_columnSpan"); + if (attribute != null) { + attribute.setName("android:layout_span"); + } + } + } + } + + @Override + protected void loadChildProperties(PropertyParser propertyParser) throws Exception { + } + }.result(); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/morphing/RelativeLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/RelativeLayout.java new file mode 100644 index 000000000000..c3fcb9d3726c --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/RelativeLayout.java @@ -0,0 +1,49 @@ +/* + * 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.android.designer.model.morphing; + +import com.intellij.android.designer.model.ComponentMorphingTool; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.android.designer.model.layout.relative.RadRelativeLayout; +import com.intellij.android.designer.model.layout.relative.RadRelativeLayoutComponent; +import com.intellij.designer.model.MetaModel; +import com.intellij.designer.model.RadComponent; +import com.intellij.psi.xml.XmlTag; + +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public class RelativeLayout { + public static RadViewComponent RelativeLayout(RadViewComponent component, MetaModel target) throws Exception { + return new ComponentMorphingTool(component, new RadRelativeLayoutComponent(), target, new RadRelativeLayout()) { + @Override + protected void convertTag() { + Rectangle parentBounds = myOldComponent.getBounds(); + + for (RadComponent childComponent : myNewComponent.getChildren()) { + Rectangle bounds = childComponent.getBounds(); + XmlTag tag = ((RadViewComponent)childComponent).getTag(); + tag.setAttribute("android:layout_alignParentLeft", "true"); + tag.setAttribute("android:layout_marginLeft", Integer.toString(bounds.x - parentBounds.x) + "dp"); + tag.setAttribute("android:layout_alignParentTop", "true"); + tag.setAttribute("android:layout_marginTop", Integer.toString(bounds.y - parentBounds.y) + "dp"); + } + } + }.result(); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/morphing/TableLayout.java b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/TableLayout.java new file mode 100644 index 000000000000..2f537fe01eca --- /dev/null +++ b/plugins/android-designer/src/com/intellij/android/designer/model/morphing/TableLayout.java @@ -0,0 +1,97 @@ +/* + * 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.android.designer.model.morphing; + +import com.intellij.android.designer.model.ComponentMorphingTool; +import com.intellij.android.designer.model.ModelParser; +import com.intellij.android.designer.model.RadViewComponent; +import com.intellij.android.designer.model.layout.grid.RadGridLayout; +import com.intellij.android.designer.model.layout.grid.RadGridLayoutComponent; +import com.intellij.android.designer.model.layout.table.RadTableLayoutComponent; +import com.intellij.android.designer.model.layout.table.RadTableRowLayout; +import com.intellij.designer.model.MetaModel; +import com.intellij.designer.model.RadComponent; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlTag; + +import java.util.ArrayList; + +/** + * @author Alexander Lobas + */ +public class TableLayout { + private static final String TABLE_ROW_KEY = "TableRow"; + + public static RadViewComponent GridLayout(RadViewComponent component, MetaModel target) throws Exception { + final RadComponent[][] components = ((RadTableLayoutComponent)component).getGridComponents(false); + + return new ComponentMorphingTool(component, new RadGridLayoutComponent(), target, new RadGridLayout()) { + @Override + protected void convertChildren() throws Exception { + for (final RadComponent rowComponent : new ArrayList(myOldComponent.getChildren())) { + if (RadTableRowLayout.is(rowComponent)) { + for (RadComponent cellComponent : new ArrayList(rowComponent.getChildren())) { + ModelParser.moveComponent(myOldComponent, (RadViewComponent)cellComponent, (RadViewComponent)rowComponent); + } + rowComponent.delete(); + } + else { + rowComponent.setClientProperty(TABLE_ROW_KEY, Boolean.TRUE); + } + } + super.convertChildren(); + } + + @Override + protected void convertTag() { + XmlTag tag = myNewComponent.getTag(); + if (components.length > 0) { + String columnCount = Integer.toString(components[0].length); + tag.setAttribute("android:rowCount", Integer.toString(components.length)); + tag.setAttribute("android:columnCount", columnCount); + + for (int i = 0; i < components.length; i++) { + RadComponent[] rowComponents = components[i]; + + RadComponent firstCellComponent = rowComponents[0]; + if (firstCellComponent != null && firstCellComponent.extractClientProperty(TABLE_ROW_KEY) == Boolean.TRUE) { + XmlTag cellTag = ((RadViewComponent)firstCellComponent).getTag(); + ModelParser.deleteAttribute(cellTag, "android:layout_span"); + cellTag.setAttribute("android:layout_column", "0"); + cellTag.setAttribute("android:layout_columnSpan", columnCount); + cellTag.setAttribute("android:layout_gravity", "fill_horizontal"); + } + + for (RadComponent cellComponent : rowComponents) { + if (cellComponent != null) { + XmlTag cellTag = ((RadViewComponent)cellComponent).getTag(); + cellTag.setAttribute("android:layout_row", Integer.toString(i)); + break; + } + } + } + + for (RadComponent childComponent : myNewComponent.getChildren()) { + XmlAttribute attribute = ((RadViewComponent)childComponent).getTag().getAttribute("android:layout_span"); + if (attribute != null) { + attribute.setName("android:layout_columnSpan"); + } + } + } + } + }.result(); + } +} \ No newline at end of file diff --git a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml index 240977dd60e2..3ed8a4dda653 100644 --- a/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml +++ b/plugins/android-designer/src/com/intellij/android/designer/model/views-meta-model.xml @@ -1113,7 +1113,7 @@ - + - + - + - + - + - + @@ -1419,8 +1419,6 @@ - -