diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java new file mode 100644 index 000000000..97d048108 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SelectionOverlay.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.android.ide.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.api.DrawingStyle; +import com.android.ide.common.api.IGraphics; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.Margins; +import com.android.ide.common.api.Rect; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; + +import org.eclipse.swt.graphics.GC; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The {@link SelectionOverlay} paints the current selection as an overlay. + */ +public class SelectionOverlay extends Overlay { + private final LayoutCanvas mCanvas; + private boolean mHidden; + + /** + * Constructs a new {@link SelectionOverlay} tied to the given canvas. + * + * @param canvas the associated canvas + */ + public SelectionOverlay(LayoutCanvas canvas) { + mCanvas = canvas; + } + + /** + * Set whether the selection overlay should be hidden. This is done during some + * gestures like resize where the new bounds could be confused with the current + * selection bounds. + * + * @param hidden when true, hide the selection bounds, when false, unhide. + */ + public void setHidden(boolean hidden) { + mHidden = hidden; + } + + /** + * Paints the selection. + * + * @param selectionManager The {@link SelectionManager} holding the + * selection. + * @param gcWrapper The graphics context wrapper for the layout rules to use. + * @param gc The SWT graphics object + * @param rulesEngine The {@link RulesEngine} holding the rules. + */ + public void paint(SelectionManager selectionManager, GCWrapper gcWrapper, + GC gc, RulesEngine rulesEngine) { + if (mHidden) { + return; + } + + List<SelectionItem> selections = selectionManager.getSelections(); + int n = selections.size(); + if (n > 0) { + List<NodeProxy> selectedNodes = new ArrayList<NodeProxy>(); + boolean isMultipleSelection = n > 1; + for (SelectionItem s : selections) { + if (s.isRoot()) { + // The root selection is never painted + continue; + } + + NodeProxy node = s.getNode(); + if (node != null) { + paintSelection(gcWrapper, gc, s, isMultipleSelection); + selectedNodes.add(node); + } + } + + if (selectedNodes.size() > 0) { + paintSelectionFeedback(gcWrapper, selectedNodes, rulesEngine); + } else { + CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot(); + if (root != null) { + NodeProxy parent = mCanvas.getNodeFactory().create(root); + rulesEngine.callPaintSelectionFeedback(gcWrapper, + parent, Collections.<INode>emptyList(), root.getViewObject()); + } + } + + if (n == 1) { + NodeProxy node = selections.get(0).getNode(); + if (node != null) { + paintHints(gcWrapper, node, rulesEngine); + } + } + } else { + CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot(); + if (root != null) { + NodeProxy parent = mCanvas.getNodeFactory().create(root); + rulesEngine.callPaintSelectionFeedback(gcWrapper, + parent, Collections.<INode>emptyList(), root.getViewObject()); + } + } + } + + /** Paint hint for current selection */ + private void paintHints(GCWrapper gcWrapper, NodeProxy node, RulesEngine rulesEngine) { + INode parent = node.getParent(); + if (parent instanceof NodeProxy) { + NodeProxy parentNode = (NodeProxy) parent; + List<String> infos = rulesEngine.callGetSelectionHint(parentNode, node); + if (infos != null && infos.size() > 0) { + gcWrapper.useStyle(DrawingStyle.HELP); + + Rect b = mCanvas.getImageOverlay().getImageBounds(); + if (b == null) { + return; + } + + // Compute the location to display the help. This is done in + // layout coordinates, so we need to apply the scale in reverse + // when making pixel margins + // TODO: We could take the Canvas dimensions into account to see + // where there is more room. + // TODO: The scrollbars should take the presence of hint text + // into account. + double scale = mCanvas.getScale(); + int x, y; + if (b.w > b.h) { + x = (int) (b.x + 3 / scale); + y = (int) (b.y + b.h + 6 / scale); + } else { + x = (int) (b.x + b.w + 6 / scale); + y = (int) (b.y + 3 / scale); + } + gcWrapper.drawBoxedStrings(x, y, infos); + } + } + } + + private void paintSelectionFeedback(GCWrapper gcWrapper, List<NodeProxy> nodes, + RulesEngine rulesEngine) { + // Add fastpath for n=1 + + // Group nodes into parent/child groups + Set<INode> parents = new HashSet<INode>(); + for (INode node : nodes) { + INode parent = node.getParent(); + if (/*parent == null || */parent instanceof NodeProxy) { + NodeProxy parentNode = (NodeProxy) parent; + parents.add(parentNode); + } + } + ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy(); + for (INode parent : parents) { + List<INode> children = new ArrayList<INode>(); + for (INode node : nodes) { + INode nodeParent = node.getParent(); + if (nodeParent == parent) { + children.add(node); + } + } + CanvasViewInfo viewInfo = viewHierarchy.findViewInfoFor((NodeProxy) parent); + Object view = viewInfo != null ? viewInfo.getViewObject() : null; + + rulesEngine.callPaintSelectionFeedback(gcWrapper, + (NodeProxy) parent, children, view); + } + } + + /** Called by the canvas when a view is being selected. */ + private void paintSelection(IGraphics gc, GC swtGc, SelectionItem item, + boolean isMultipleSelection) { + CanvasViewInfo view = item.getViewInfo(); + if (view.isHidden()) { + return; + } + + NodeProxy selectedNode = item.getNode(); + Rect r = selectedNode.getBounds(); + if (!r.isValid()) { + return; + } + + gc.useStyle(DrawingStyle.SELECTION); + + Margins insets = mCanvas.getInsets(selectedNode.getFqcn()); + int x1 = r.x; + int y1 = r.y; + int x2 = r.x2() + 1; + int y2 = r.y2() + 1; + + if (insets != null) { + x1 += insets.left; + x2 -= insets.right; + y1 += insets.top; + y2 -= insets.bottom; + } + + gc.drawRect(x1, y1, x2, y2); + + // Paint sibling rectangles, if applicable + List<CanvasViewInfo> siblings = view.getNodeSiblings(); + if (siblings != null) { + for (CanvasViewInfo sibling : siblings) { + if (sibling != view) { + r = SwtUtils.toRect(sibling.getSelectionRect()); + gc.fillRect(r); + gc.drawRect(r); + } + } + } + + // Paint selection handles. These are painted in control coordinates on the + // real SWT GC object rather than in layout coordinates on the GCWrapper, + // since we want them to have a fixed size that is independent of the + // screen zoom. + CanvasTransform horizontalTransform = mCanvas.getHorizontalTransform(); + CanvasTransform verticalTransform = mCanvas.getVerticalTransform(); + int radius = SelectionHandle.PIXEL_RADIUS; + int doubleRadius = 2 * radius; + for (SelectionHandle handle : item.getSelectionHandles()) { + int cx = horizontalTransform.translate(handle.centerX); + int cy = verticalTransform.translate(handle.centerY); + + SwtDrawingStyle style = SwtDrawingStyle.of(DrawingStyle.SELECTION); + gc.setAlpha(style.getStrokeAlpha()); + swtGc.fillRectangle(cx - radius, cy - radius, doubleRadius, doubleRadius); + } + } +} |