1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.adt.internal.editors.layout.gle2; 17 18 import com.android.ide.common.api.INode; 19 import com.android.ide.common.api.InsertType; 20 import com.android.ide.common.layout.BaseLayoutRule; 21 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; 22 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; 23 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; 24 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; 25 26 import org.eclipse.jface.viewers.TreeViewer; 27 import org.eclipse.jface.viewers.ViewerDropAdapter; 28 import org.eclipse.swt.dnd.DND; 29 import org.eclipse.swt.dnd.DropTargetEvent; 30 import org.eclipse.swt.dnd.TransferData; 31 32 import java.util.ArrayList; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Set; 36 37 /** Drop listener for the outline page */ 38 /*package*/ class OutlineDropListener extends ViewerDropAdapter { 39 private final OutlinePage mOutlinePage; 40 41 public OutlineDropListener(OutlinePage outlinePage, TreeViewer treeViewer) { 42 super(treeViewer); 43 mOutlinePage = outlinePage; 44 } 45 46 @Override 47 public void dragEnter(DropTargetEvent event) { 48 if (event.detail == DND.DROP_NONE && GlobalCanvasDragInfo.getInstance().isDragging()) { 49 // For some inexplicable reason, we get DND.DROP_NONE from the palette 50 // even though in its drag start we set DND.DROP_COPY, so correct that here... 51 int operation = DND.DROP_COPY; 52 event.detail = operation; 53 } 54 super.dragEnter(event); 55 } 56 57 @Override 58 public boolean performDrop(Object data) { 59 final DropTargetEvent event = getCurrentEvent(); 60 if (event == null) { 61 return false; 62 } 63 int location = determineLocation(event); 64 if (location == LOCATION_NONE) { 65 return false; 66 } 67 68 final SimpleElement[] elements; 69 SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance(); 70 if (sxt.isSupportedType(event.currentDataType)) { 71 if (data instanceof SimpleElement[]) { 72 elements = (SimpleElement[]) data; 73 } else { 74 return false; 75 } 76 } else { 77 return false; 78 } 79 if (elements.length == 0) { 80 return false; 81 } 82 83 // Determine target: 84 CanvasViewInfo parent = OutlinePage.getViewInfo(event.item.getData()); 85 if (parent == null) { 86 return false; 87 } 88 89 int index = -1; 90 UiViewElementNode parentNode = parent.getUiViewNode(); 91 if (location == LOCATION_BEFORE || location == LOCATION_AFTER) { 92 UiViewElementNode node = parentNode; 93 parent = parent.getParent(); 94 if (parent == null) { 95 return false; 96 } 97 parentNode = parent.getUiViewNode(); 98 99 // Determine index 100 index = 0; 101 for (UiElementNode child : parentNode.getUiChildren()) { 102 if (child == node) { 103 break; 104 } 105 index++; 106 } 107 if (location == LOCATION_AFTER) { 108 index++; 109 } 110 } 111 112 // Copy into new position. 113 final LayoutCanvas canvas = mOutlinePage.getEditor().getCanvasControl(); 114 final NodeProxy targetNode = canvas.getNodeFactory().create(parentNode); 115 116 // Record children of the target right before the drop (such that we can 117 // find out after the drop which exact children were inserted) 118 Set<INode> children = new HashSet<INode>(); 119 for (INode node : targetNode.getChildren()) { 120 children.add(node); 121 } 122 123 String label = MoveGesture.computeUndoLabel(targetNode, elements, event.detail); 124 final int indexFinal = index; 125 canvas.getEditorDelegate().getEditor().wrapUndoEditXmlModel(label, new Runnable() { 126 @Override 127 public void run() { 128 InsertType insertType = MoveGesture.getInsertType(event, targetNode); 129 canvas.getRulesEngine().setInsertType(insertType); 130 131 Object sourceCanvas = GlobalCanvasDragInfo.getInstance().getSourceCanvas(); 132 boolean createNew = event.detail == DND.DROP_COPY || sourceCanvas != canvas; 133 BaseLayoutRule.insertAt(targetNode, elements, createNew, indexFinal); 134 targetNode.applyPendingChanges(); 135 136 // Clean up drag if applicable 137 if (event.detail == DND.DROP_MOVE) { 138 GlobalCanvasDragInfo.getInstance().removeSource(); 139 } 140 } 141 }); 142 143 // Now find out which nodes were added, and look up their corresponding 144 // CanvasViewInfos 145 final List<INode> added = new ArrayList<INode>(); 146 for (INode node : targetNode.getChildren()) { 147 if (!children.contains(node)) { 148 added.add(node); 149 } 150 } 151 // Select the newly dropped nodes 152 final SelectionManager selectionManager = canvas.getSelectionManager(); 153 selectionManager.setOutlineSelection(added); 154 155 canvas.redraw(); 156 157 return true; 158 } 159 160 @Override 161 public boolean validateDrop(Object target, int operation, 162 TransferData transferType) { 163 DropTargetEvent event = getCurrentEvent(); 164 if (event == null) { 165 return false; 166 } 167 int location = determineLocation(event); 168 if (location == LOCATION_NONE) { 169 return false; 170 } 171 172 SimpleXmlTransfer sxt = SimpleXmlTransfer.getInstance(); 173 if (!sxt.isSupportedType(transferType)) { 174 return false; 175 } 176 177 CanvasViewInfo parent = OutlinePage.getViewInfo(event.item.getData()); 178 if (parent == null) { 179 return false; 180 } 181 182 UiViewElementNode parentNode = parent.getUiViewNode(); 183 184 if (location == LOCATION_ON) { 185 // Targeting the middle of an item means to add it as a new child 186 // of the given element. This is only allowed on some types of nodes. 187 if (!DescriptorsUtils.canInsertChildren(parentNode.getDescriptor(), 188 parent.getViewObject())) { 189 return false; 190 } 191 } 192 193 // Check that the drop target position is not a child or identical to 194 // one of the dragged items 195 SelectionItem[] sel = GlobalCanvasDragInfo.getInstance().getCurrentSelection(); 196 if (sel != null) { 197 for (SelectionItem item : sel) { 198 if (isAncestor(item.getViewInfo().getUiViewNode(), parentNode)) { 199 return false; 200 } 201 } 202 } 203 204 return true; 205 } 206 207 /** Returns true if the given parent node is an ancestor of the given child node */ 208 private boolean isAncestor(UiElementNode parent, UiElementNode child) { 209 while (child != null) { 210 if (child == parent) { 211 return true; 212 } 213 child = child.getUiParent(); 214 } 215 return false; 216 } 217 } 218