/* * Copyright (C) 2014 The Android Open Source Project * * 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.designSurface; import com.google.common.base.Objects; import com.intellij.android.designer.AndroidDesignerEditor; import com.intellij.android.designer.componentTree.AndroidTreeDecorator; import com.intellij.android.designer.model.RadComponentOperations; import com.intellij.android.designer.model.RadModelBuilder; import com.intellij.android.designer.model.RadViewComponent; import com.intellij.android.designer.model.ViewsMetaManager; import com.intellij.designer.componentTree.AttributeWrapper; import com.intellij.designer.designSurface.EditOperation; import com.intellij.designer.designSurface.OperationContext; import com.intellij.designer.designSurface.tools.ComponentCreationFactory; import com.intellij.designer.designSurface.tools.CreationTool; import com.intellij.designer.model.MetaModel; import com.intellij.designer.model.Property; import com.intellij.designer.palette.DefaultPaletteItem; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.XmlElementFactory; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.ui.SimpleColoredRenderer; import org.jetbrains.android.AndroidTestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.io.File; import java.util.Collections; public abstract class LayoutEditorTestBase extends AndroidTestCase { @Override public void setUp() throws Exception { super.setUp(); } @Override protected boolean requireRecentSdk() { return true; } protected File getTestDir() { return new File(FileUtil.toSystemDependentName(getTestDataPath()), "designer"); } @NotNull protected VirtualFile getTestFile(String filename) { File sourceFile = new File(getTestDir(), filename); return myFixture.copyFileToProject(sourceFile.getPath(), "res/layout/" + filename); } protected AndroidDesignerEditorPanel createLayoutEditor(VirtualFile xmlFile) { AndroidDesignerEditor editor = new AndroidDesignerEditor(getProject(), xmlFile); AndroidDesignerEditorPanel panel = (AndroidDesignerEditorPanel)editor.getDesignerPanel(); panel.requestImmediateRender(); return panel; } public static String printTree(RadViewComponent root, boolean internal) { StringBuilder sb = new StringBuilder(200); if (internal) { describe(sb, root, 0); } else { decorate(sb, root, 0); } return sb.toString().trim(); } private static void decorate(StringBuilder sb, RadViewComponent component, int depth) { for (int i = 0; i < depth; i++) { sb.append(" "); } SimpleColoredRenderer renderer = new SimpleColoredRenderer(); AndroidTreeDecorator decorator = new AndroidTreeDecorator(RadModelBuilder.getProject(component)); decorator.decorate(component, renderer, AttributeWrapper.DEFAULT, true); sb.append(renderer); sb.append('\n'); for (RadViewComponent child : RadViewComponent.getViewComponents(component.getChildren())) { decorate(sb, child, depth + 1); } } private static void describe(StringBuilder sb, RadViewComponent component, int depth) { for (int i = 0; i < depth; i++) { sb.append(" "); } sb.append(describe(component)); sb.append('\n'); for (RadViewComponent child : RadViewComponent.getViewComponents(component.getChildren())) { describe(sb, child, depth + 1); } } private static String describe(RadViewComponent root) { return Objects.toStringHelper(root).omitNullValues() .add("tag", describe(root.getTag())) .add("id", root.getId()) .add("bounds", describe(root.getBounds())) .toString(); } private static String describe(@Nullable XmlTag tag) { if (tag == null) { return ""; } else { return '<' + tag.getName() + '>'; } } private static String describe(Rectangle rectangle) { // More brief description than toString default: java.awt.Rectangle[x=0,y=100,width=768,height=1084] return "[" + rectangle.x + "," + rectangle.y + ":" + rectangle.width + "x" + rectangle.height; } @NotNull protected Property getProperty(@NotNull RadViewComponent component, @NotNull String name) { for (Property property : component.getProperties()) { if (name.equals(property.getName())) { return property; } } fail("Did not find property as expected"); throw new RuntimeException("not reached"); } protected RadViewComponent addComponent(@NotNull AndroidDesignerEditorPanel editor, @NotNull final RadViewComponent parent, @Nullable final RadViewComponent before, @NotNull String tagName) throws Exception { XmlFile psiFile = editor.getXmlFile(); ViewsMetaManager metaManager = ViewsMetaManager.getInstance(getProject()); MetaModel metaModel = metaManager.getModelByTag(tagName); assertNotNull(metaModel); final boolean ADD_AS_DROP_OPERATION = true; //noinspection ConstantConditions if (ADD_AS_DROP_OPERATION) { DefaultPaletteItem paletteItem = new DefaultPaletteItem(tagName, "", "", "", "", ""); paletteItem.setMetaModel(metaModel); ComponentCreationFactory factory = editor.createCreationFactory(paletteItem); final OperationContext context = new OperationContext(); context.setType(OperationContext.CREATE); CreationTool tool = new CreationTool(true, factory); context.setComponents(Collections.singletonList(tool.getFactory().create())); EditOperation operation = new AbstractEditOperation(parent, context) { @Override public void showFeedback() { } @Override public void eraseFeedback() { } @Override public void execute() throws Exception { AbstractEditOperation.execute(myContext, (RadViewComponent)myContainer, myComponents, before); } }; operation.setComponents(context.getComponents()); editor.getToolProvider().execute(Collections.singletonList(operation), context.getMessage()); final RadViewComponent newComponent = (RadViewComponent)context.getComponents().get(0); assertNotNull(newComponent); return newComponent; } else { // Add as XML tag edit XmlTag tag = XmlElementFactory.getInstance(getProject()).createTagFromText(metaModel.getCreation()); assertNotNull(tag); final RadViewComponent newComponent = RadComponentOperations.createComponent(tag, metaModel); assertNotNull(newComponent.getTag()); assertEquals(tagName, newComponent.getTag().getName()); WriteCommandAction<Void> action = new WriteCommandAction<Void>(getProject(), "Add Tag " + tagName, psiFile) { @Override protected void run(@NotNull Result<Void> result) throws Throwable { RadComponentOperations.addComponent(parent, newComponent, before); } }; action.execute(); return newComponent; } } protected void setProperty(@NotNull AndroidDesignerEditorPanel editor, @NotNull final RadViewComponent component, @NotNull final String name, @Nullable final Object value) { XmlFile psiFile = editor.getXmlFile(); WriteCommandAction<Void> action = new WriteCommandAction<Void>(getProject(), "Set Attribute " + name + " to " + value, psiFile) { @Override protected void run(@NotNull Result<Void> result) throws Throwable { //noinspection unchecked getProperty(component, name).setValue(component, value); } }; action.execute(); } }