Copy & Paste

This commit is contained in:
Alexander Lobas
2012-04-01 20:34:13 +04:00
parent 67ea8f281b
commit 0426162e2a
17 changed files with 286 additions and 71 deletions
@@ -42,6 +42,10 @@ public abstract class AbstractEditOperation extends com.intellij.designer.design
for (RadComponent component : myComponents) {
ModelParser.addComponent(myContainer, (RadViewComponent)component, null);
}
if (myContext.isPaste()) {
myContext.getPasteFactory().finish();
}
}
}
}
@@ -426,7 +426,7 @@ public final class AndroidDesignerEditorPanel extends DesignerEditorPanel {
@Override
public ComponentPasteFactory createPasteFactory(String xmlComponents) {
return null; // TODO: Auto-generated method stub
return new AndroidPasteFactory(getProject(), xmlComponents);
}
@Override
@@ -0,0 +1,159 @@
/*
* 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.designSurface;
import com.intellij.android.designer.model.ModelParser;
import com.intellij.android.designer.model.PropertyParser;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.ViewsMetaManager;
import com.intellij.designer.designSurface.tools.ComponentPasteFactory;
import com.intellij.designer.model.MetaManager;
import com.intellij.designer.model.MetaModel;
import com.intellij.designer.model.RadComponent;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.XmlElementFactory;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.hash.HashMap;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Alexander Lobas
*/
public class AndroidPasteFactory implements ComponentPasteFactory {
private final Project myProject;
private final MetaManager myMetaManager;
private final String myXmlComponents;
private final Map<RadViewComponent, Element> myProperties = new HashMap<RadViewComponent, Element>();
private List<RadComponent> myComponents;
public AndroidPasteFactory(Project project, String xmlComponents) {
myProject = project;
myMetaManager = ViewsMetaManager.getInstance(project);
myXmlComponents = xmlComponents;
}
@NotNull
@Override
public List<RadComponent> create() throws Exception {
myComponents = new ArrayList<RadComponent>();
Document document = new SAXBuilder().build(new StringReader(myXmlComponents), "UTF-8");
for (Object element : document.getRootElement().getChildren("component")) {
myComponents.add(createComponent((Element)element));
}
return myComponents;
}
private RadComponent createComponent(Element element) throws Exception {
MetaModel metaModel = myMetaManager.getModelByTag(element.getAttributeValue("tag"));
RadViewComponent component = ModelParser.createComponent(null, metaModel);
List<RadComponent> children = component.getChildren();
myProperties.put(component, element.getChild("properties"));
for (Object childElement : element.getChildren("component")) {
RadComponent childComponent = createComponent((Element)childElement);
childComponent.setParent(component);
children.add(childComponent);
}
return component;
}
@Override
public void finish() throws Exception {
for (RadComponent component : myComponents) {
createTagAndProperties((RadViewComponent)component);
}
}
private void createTagAndProperties(final RadViewComponent component) throws Exception {
final Element properties = myProperties.get(component);
if (properties != null) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
XmlTag xmlTag = component.getTag();
if (xmlTag == null) {
Language language = StdFileTypes.XML.getLanguage();
xmlTag =
XmlElementFactory.getInstance(myProject).createTagFromText("\n" + component.getMetaModel().getCreation(), language);
RadViewComponent parent = (RadViewComponent)component.getParent();
XmlTag parentTag = parent.getTag();
XmlTag nextTag = null;
List<RadComponent> children = parent.getChildren();
int index = children.indexOf(component) + 1;
if (index < children.size()) {
nextTag = ((RadViewComponent)children.get(index)).getTag();
}
if (nextTag == null) {
xmlTag = parentTag.addSubTag(xmlTag, false);
}
else {
xmlTag = (XmlTag)parentTag.addBefore(xmlTag, nextTag);
}
}
for (XmlAttribute attribute : xmlTag.getAttributes()) {
attribute.delete();
}
for (Object object : properties.getAttributes()) {
Attribute attribute = (Attribute)object;
xmlTag.setAttribute(attribute.getName(), attribute.getValue());
}
for (Object object : properties.getChildren()) {
Element element = (Element)object;
String namespace = element.getName();
for (Object child : element.getAttributes()) {
Attribute attribute = (Attribute)child;
xmlTag.setAttribute(namespace + ":" + attribute.getName(), attribute.getValue());
}
}
}
});
}
if (component.getProperties() == null) {
PropertyParser propertyParser = component.getRoot().getClientProperty(PropertyParser.KEY);
propertyParser.load(component);
}
for (RadComponent child : component.getChildren()) {
createTagAndProperties((RadViewComponent)child);
}
}
}
@@ -108,7 +108,7 @@ public class ModelParser extends XmlRecursiveElementVisitor {
}
}
public static RadViewComponent createComponent(XmlTag tag, MetaModel metaModel) throws Exception {
public static RadViewComponent createComponent(@Nullable XmlTag tag, MetaModel metaModel) throws Exception {
RadViewComponent component = (RadViewComponent)metaModel.getModel().newInstance();
component.setMetaModel(metaModel);
component.setTag(tag);
@@ -19,12 +19,16 @@ import com.android.ide.common.rendering.api.ViewInfo;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.propertyTable.Property;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.hash.HashMap;
import org.jdom.Element;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author Alexander Lobas
@@ -105,4 +109,48 @@ public class RadViewComponent extends RadComponent {
public void setProperties(List<Property> properties) {
myProperties = properties;
}
@Override
public void copyTo(Element parent) throws Exception {
// skip root
if (getParent() != null) {
Element component = new Element("component");
component.setAttribute("tag", myTag.getName());
XmlAttribute[] attributes = myTag.getAttributes();
if (attributes.length > 0) {
Element properties = new Element("properties");
component.addContent(properties);
Map<String, Element> namespaces = new HashMap<String, Element>();
for (XmlAttribute attribute : attributes) {
String namespace = attribute.getNamespacePrefix();
if (namespace.length() == 0) {
properties.setAttribute(attribute.getName(), attribute.getValue());
}
else {
Element element = namespaces.get(namespace);
if (element == null) {
element = new Element(namespace);
namespaces.put(namespace, element);
}
element.setAttribute(attribute.getLocalName(), attribute.getValue());
}
}
for (Element element : namespaces.values()) {
properties.addContent(element);
}
}
parent.addContent(component);
parent = component;
}
for (RadComponent child : myChildren) {
child.copyTo(parent);
}
}
}
@@ -73,7 +73,7 @@ public class RadScrollViewComponent extends RadViewComponent {
// TODO: maybe reload properties
}
}, true);
}, getTemplatePresentation().getText(), true);
}
});
}
@@ -16,7 +16,6 @@
package com.intellij.designer.actions;
import com.intellij.designer.DesignerBundle;
import com.intellij.uiDesigner.SerializedComponentData;
import com.intellij.designer.clipboard.SimpleTransferable;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.designSurface.EditableArea;
@@ -29,8 +28,8 @@ import com.intellij.ide.DeleteProvider;
import com.intellij.ide.PasteProvider;
import com.intellij.ide.dnd.FileCopyPasteUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.uiDesigner.SerializedComponentData;
import com.intellij.util.ThrowableRunnable;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;
@@ -75,30 +74,26 @@ public class CommonEditActionsProvider implements DeleteProvider, CopyProvider,
@Override
public void deleteElement(@NotNull DataContext dataContext) {
CommandProcessor.getInstance().executeCommand(myDesigner.getProject(), new Runnable() {
public void run() {
myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
EditableArea area = myDesigner.getActionsArea();
List<RadComponent> selection = area.getSelection();
List<RadComponent> components = RadComponent.getPureSelection(selection);
RadComponent newSelection = getNewSelection(components.get(0), selection);
myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
EditableArea area = myDesigner.getActionsArea();
List<RadComponent> selection = area.getSelection();
List<RadComponent> components = RadComponent.getPureSelection(selection);
RadComponent newSelection = getNewSelection(components.get(0), selection);
for (RadComponent component : components) {
component.delete();
}
for (RadComponent component : components) {
component.delete();
}
if (newSelection == null) {
area.deselectAll();
}
else {
area.select(newSelection);
}
}
}, true);
if (newSelection == null) {
area.deselectAll();
}
else {
area.select(newSelection);
}
}
}, DesignerBundle.message("command.delete.selection"), null);
}, DesignerBundle.message("command.delete.selection"), true);
}
@Nullable
@@ -26,6 +26,7 @@ import com.intellij.designer.palette.Item;
import com.intellij.ide.palette.impl.PaletteManager;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -211,13 +212,23 @@ public abstract class DesignerEditorPanel extends JPanel implements DataProvider
}
@Override
public boolean execute(ThrowableRunnable<Exception> operation, boolean updateProperties) {
return DesignerEditorPanel.this.execute(operation, updateProperties);
public boolean execute(final ThrowableRunnable<Exception> operation, String command, final boolean updateProperties) {
final boolean[] is = {true};
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
public void run() {
is[0] = DesignerEditorPanel.this.execute(operation, updateProperties);
}
}, command, null);
return is[0];
}
@Override
public void execute(List<EditOperation> operations) {
DesignerEditorPanel.this.execute(operations);
public void execute(final List<EditOperation> operations, String command) {
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
public void run() {
DesignerEditorPanel.this.execute(operations);
}
}, command, null);
}
@Override
@@ -15,6 +15,7 @@
*/
package com.intellij.designer.designSurface;
import com.intellij.designer.designSurface.tools.ComponentPasteFactory;
import com.intellij.designer.model.RadComponent;
import org.jetbrains.annotations.Nullable;
@@ -34,6 +35,7 @@ public final class OperationContext {
private Object myType;
private EditableArea myArea;
private List<RadComponent> myComponents;
private ComponentPasteFactory myPasteFactory;
private InputEvent myInputEvent;
private Point myLocation;
private Point myMoveDelta;
@@ -104,6 +106,14 @@ public final class OperationContext {
myComponents = components;
}
public ComponentPasteFactory getPasteFactory() {
return myPasteFactory;
}
public void setPasteFactory(ComponentPasteFactory pasteFactory) {
myPasteFactory = pasteFactory;
}
public boolean isMoveEnabled() {
return myMoveEnabled;
}
@@ -26,4 +26,6 @@ import java.util.List;
public interface ComponentPasteFactory {
@NotNull
List<RadComponent> create() throws Exception;
void finish() throws Exception;
}
@@ -17,8 +17,6 @@ package com.intellij.designer.designSurface.tools;
import com.intellij.designer.designSurface.OperationContext;
import java.util.Collections;
/**
* @author Alexander Lobas
*/
@@ -34,6 +32,7 @@ public class PasteTool extends AbstractCreationTool {
public void activate() {
super.activate();
myContext.setType(OperationContext.PASTE);
myContext.setPasteFactory(myFactory);
try {
myContext.setComponents(myFactory.create());
@@ -15,6 +15,7 @@
*/
package com.intellij.designer.designSurface.tools;
import com.intellij.designer.DesignerBundle;
import com.intellij.designer.designSurface.EditOperation;
import com.intellij.designer.designSurface.OperationContext;
import com.intellij.designer.model.RadComponent;
@@ -122,7 +123,7 @@ public class ResizeTracker extends InputTool {
operations.add(operation);
}
}
myToolProvider.execute(operations);
myToolProvider.execute(operations, DesignerBundle.message("command.tool_operation", myContext.getType()));
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.designer.designSurface.tools;
import com.intellij.designer.DesignerBundle;
import com.intellij.designer.designSurface.ComponentTargetFilter;
import com.intellij.designer.designSurface.EditOperation;
import com.intellij.designer.designSurface.OperationContext;
@@ -78,7 +79,8 @@ public abstract class TargetingTool extends InputTool {
protected void executeCommand() {
if (myExecuteEnabled) {
myToolProvider.execute(Collections.singletonList(myTargetOperation));
myToolProvider
.execute(Collections.singletonList(myTargetOperation), DesignerBundle.message("command.tool_operation", myContext.getType()));
}
}
@@ -130,7 +130,7 @@ public abstract class ToolProvider {
public abstract void loadDefaultTool();
public abstract boolean execute(ThrowableRunnable<Exception> operation, boolean updateProperties);
public abstract boolean execute(ThrowableRunnable<Exception> operation, String command, boolean updateProperties);
public abstract void execute(List<EditOperation> operations);
public abstract void execute(List<EditOperation> operations, String command);
}
@@ -18,7 +18,6 @@ package com.intellij.designer.model;
import com.intellij.designer.palette.Group;
import com.intellij.designer.palette.Item;
import com.intellij.ide.palette.PaletteGroup;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
@@ -21,7 +21,6 @@ import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.designSurface.EditableArea;
import com.intellij.designer.model.RadComponent;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
@@ -176,20 +175,16 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
cellEditor.stopCellEditing();
}
CommandProcessor.getInstance().executeCommand(myDesigner.getProject(), new Runnable() {
public void run() {
myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
for (RadComponent component : myComponents) {
if (!property.isDefaultValue(component)) {
property.setDefaultValue(component);
}
}
myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
for (RadComponent component : myComponents) {
if (!property.isDefaultValue(component)) {
property.setDefaultValue(component);
}
}, false);
}
}
}, DesignerBundle.message("designer.properties.restore_default"), null);
}, DesignerBundle.message("designer.properties.restore_default"), false);
repaint();
}
@@ -578,41 +573,30 @@ public final class PropertyTable extends JBTable implements ComponentSelectionLi
isNewValue = true;
}
final boolean[] isSet = {true};
boolean isSetValue = true;
if (isNewValue) {
CommandProcessor.getInstance().executeCommand(myDesigner.getProject(), new Runnable() {
public void run() {
isSet[0] = myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
for (RadComponent component : myComponents) {
property.setValue(component, newValue);
}
}
}, false);
isSetValue = myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
for (RadComponent component : myComponents) {
property.setValue(component, newValue);
}
}
}, DesignerBundle.message("command.set.property.value"), null);
}, DesignerBundle.message("command.set.property.value"), false);
}
if (property.needRefreshPropertyList() && isSet[0]) {
if (property.needRefreshPropertyList() && isSetValue) {
updateProperties();
}
return isSet[0];
return isSetValue;
}
private static void showInvalidInput(Exception e) {
Throwable cause = e.getCause();
String message = cause == null ? e.getMessage() : cause.getMessage();
if (cause == null) {
e.printStackTrace();
}
else {
cause.printStackTrace();
}
if (message == null || message.length() == 0) {
message = DesignerBundle.message("designer.properties.no_message.error");
}
@@ -1,6 +1,7 @@
designer.toolwindow.name=Designer
designer.toolwindow.title=Component Tree
command.tool_operation=Visual edit operation: {0}
command.delete.selection=Delete Selection
command.set.property.value=Set Property Value