| /* |
| * 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.designer.actions; |
| |
| import com.intellij.designer.DesignerBundle; |
| import com.intellij.designer.clipboard.SimpleTransferable; |
| import com.intellij.designer.designSurface.DesignerEditorPanel; |
| import com.intellij.designer.designSurface.EditableArea; |
| import com.intellij.designer.designSurface.tools.ComponentPasteFactory; |
| import com.intellij.designer.designSurface.tools.PasteTool; |
| import com.intellij.designer.model.IComponentCopyProvider; |
| import com.intellij.designer.model.IComponentDeletionParticipant; |
| import com.intellij.designer.model.IGroupDeleteComponent; |
| import com.intellij.designer.model.RadComponent; |
| import com.intellij.ide.CopyProvider; |
| import com.intellij.ide.CutProvider; |
| 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.ide.CopyPasteManager; |
| import com.intellij.uiDesigner.SerializedComponentData; |
| import com.intellij.util.ThrowableRunnable; |
| import org.jdom.Element; |
| import org.jdom.output.XMLOutputter; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.awt.datatransfer.DataFlavor; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author Alexander Lobas |
| */ |
| public class CommonEditActionsProvider implements DeleteProvider, CopyProvider, PasteProvider, CutProvider { |
| private static final DataFlavor DATA_FLAVOR = FileCopyPasteUtil.createJvmDataFlavor(SerializedComponentData.class); |
| |
| public static boolean isDeleting; |
| |
| private final DesignerEditorPanel myDesigner; |
| |
| public CommonEditActionsProvider(DesignerEditorPanel designer) { |
| myDesigner = designer; |
| } |
| |
| protected EditableArea getArea(DataContext dataContext) { |
| EditableArea area = EditableArea.DATA_KEY.getData(dataContext); |
| return area == null ? myDesigner.getSurfaceArea() : area; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Delete |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public boolean canDeleteElement(@NotNull DataContext dataContext) { |
| if (myDesigner.getInplaceEditingLayer().isEditing()) { |
| return false; |
| } |
| List<RadComponent> selection = getArea(dataContext).getSelection(); |
| if (selection.isEmpty()) { |
| return false; |
| } |
| for (RadComponent component : selection) { |
| if (!component.canDelete()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public void deleteElement(final @NotNull DataContext dataContext) { |
| myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() { |
| @Override |
| public void run() throws Exception { |
| EditableArea area = getArea(dataContext); |
| List<RadComponent> selection = area.getSelection(); |
| |
| if (selection.isEmpty()) { |
| return; |
| } |
| |
| myDesigner.getToolProvider().loadDefaultTool(); |
| List<RadComponent> components = RadComponent.getPureSelection(selection); |
| updateSelectionBeforeDelete(area, components.get(0), selection); |
| handleDeletion(components); |
| } |
| }, DesignerBundle.message("command.delete.selection"), true); |
| } |
| |
| private static void handleDeletion(@NotNull List<RadComponent> components) throws Exception { |
| // Segment the deleted components into lists of siblings |
| Map<RadComponent, List<RadComponent>> siblingLists = RadComponent.groupSiblings(components); |
| |
| // Notify parent components about children getting deleted |
| for (Map.Entry<RadComponent, List<RadComponent>> entry : siblingLists.entrySet()) { |
| RadComponent parent = entry.getKey(); |
| List<RadComponent> children = entry.getValue(); |
| boolean finished = false; |
| if (parent instanceof IComponentDeletionParticipant) { |
| IComponentDeletionParticipant handler = (IComponentDeletionParticipant)parent; |
| finished = handler.deleteChildren(parent, children); |
| } |
| else if (parent != null && /*check root*/ |
| parent.getLayout() instanceof IComponentDeletionParticipant) { |
| IComponentDeletionParticipant handler = (IComponentDeletionParticipant)parent.getLayout(); |
| finished = handler.deleteChildren(parent, children); |
| } |
| |
| if (!finished) { |
| deleteComponents(children); |
| } |
| } |
| } |
| |
| private static void deleteComponents(List<RadComponent> components) throws Exception { |
| if (components.get(0) instanceof IGroupDeleteComponent) { |
| ((IGroupDeleteComponent)components.get(0)).delete(components); |
| } |
| else { |
| for (RadComponent component : components) { |
| component.delete(); |
| } |
| } |
| } |
| |
| public static void updateSelectionBeforeDelete(EditableArea area, RadComponent component, List<RadComponent> excludes) { |
| try { |
| isDeleting = true; |
| |
| RadComponent newSelection = getNewSelection(component, excludes); |
| if (newSelection == null) { |
| area.deselectAll(); |
| } |
| else { |
| area.select(newSelection); |
| } |
| } |
| finally { |
| isDeleting = false; |
| } |
| } |
| |
| @Nullable |
| private static RadComponent getNewSelection(RadComponent component, List<RadComponent> excludes) { |
| RadComponent parent = component.getParent(); |
| if (parent == null) { |
| return null; |
| } |
| |
| List<RadComponent> children = parent.getChildren(); |
| int size = children.size(); |
| for (int i = children.indexOf(component) + 1; i < size; i++) { |
| RadComponent next = children.get(i); |
| if (!excludes.contains(next)) { |
| return next; |
| } |
| } |
| |
| return parent; |
| } |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Copy |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public boolean isCopyVisible(@NotNull DataContext dataContext) { |
| return true; |
| } |
| |
| @Override |
| public boolean isCopyEnabled(@NotNull DataContext dataContext) { |
| if (myDesigner.getInplaceEditingLayer().isEditing()) { |
| return false; |
| } |
| |
| List<RadComponent> selection = getArea(dataContext).getSelection(); |
| if (selection.isEmpty()) { |
| return false; |
| } |
| |
| RadComponent rootComponent = myDesigner.getRootComponent(); |
| if (rootComponent instanceof IComponentCopyProvider) { |
| IComponentCopyProvider copyProvider = (IComponentCopyProvider)rootComponent; |
| return copyProvider.isCopyEnabled(selection); |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public void performCopy(@NotNull DataContext dataContext) { |
| doCopy(dataContext); |
| } |
| |
| private boolean doCopy(DataContext dataContext) { |
| try { |
| Element root = new Element("designer"); |
| root.setAttribute("target", myDesigner.getPlatformTarget()); |
| |
| List<RadComponent> components = RadComponent.getPureSelection(getArea(dataContext).getSelection()); |
| RadComponent rootComponent = myDesigner.getRootComponent(); |
| |
| if (rootComponent instanceof IComponentCopyProvider) { |
| IComponentCopyProvider copyProvider = (IComponentCopyProvider)rootComponent; |
| copyProvider.copyTo(root, components); |
| } |
| else { |
| for (RadComponent component : components) { |
| component.copyTo(root); |
| } |
| } |
| |
| SerializedComponentData data = new SerializedComponentData(new XMLOutputter().outputString(root)); |
| CopyPasteManager.getInstance().setContents(new SimpleTransferable(data, DATA_FLAVOR)); |
| |
| return true; |
| } |
| catch (Throwable e) { |
| myDesigner.showError("Copy error", e); |
| return false; |
| } |
| } |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Paste |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public boolean isPastePossible(@NotNull DataContext dataContext) { |
| return isPasteEnabled(dataContext); |
| } |
| |
| @Override |
| public boolean isPasteEnabled(@NotNull DataContext dataContext) { |
| return !myDesigner.getInplaceEditingLayer().isEditing() && getSerializedComponentData() != null; |
| } |
| |
| @Nullable |
| private String getSerializedComponentData() { |
| try { |
| Object transferData = CopyPasteManager.getInstance().getContents(DATA_FLAVOR); |
| if (transferData instanceof SerializedComponentData) { |
| SerializedComponentData data = (SerializedComponentData)transferData; |
| String xmlComponents = data.getSerializedComponents(); |
| if (xmlComponents.startsWith("<designer target=\"" + myDesigner.getPlatformTarget() + "\">")) { |
| return xmlComponents; |
| } |
| } |
| } |
| catch (Throwable ignored) { |
| // ignored |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public void performPaste(@NotNull DataContext dataContext) { |
| ComponentPasteFactory factory = myDesigner.createPasteFactory(getSerializedComponentData()); |
| if (factory != null) { |
| myDesigner.getToolProvider().setActiveTool(new PasteTool(true, factory)); |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| // |
| // Cut |
| // |
| ////////////////////////////////////////////////////////////////////////////////////////// |
| |
| @Override |
| public boolean isCutVisible(@NotNull DataContext dataContext) { |
| return true; |
| } |
| |
| @Override |
| public boolean isCutEnabled(@NotNull DataContext dataContext) { |
| return isCopyEnabled(dataContext) && canDeleteElement(dataContext); |
| } |
| |
| @Override |
| public void performCut(@NotNull DataContext dataContext) { |
| if (doCopy(dataContext)) { |
| deleteElement(dataContext); |
| } |
| } |
| } |