Home | History | Annotate | Download | only in gle2
      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 
     17 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
     18 
     19 import com.android.ide.common.api.DrawingStyle;
     20 import com.android.ide.common.api.IGraphics;
     21 import com.android.ide.common.api.INode;
     22 import com.android.ide.common.api.Margins;
     23 import com.android.ide.common.api.Rect;
     24 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
     25 import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
     26 
     27 import org.eclipse.swt.graphics.GC;
     28 
     29 import java.util.ArrayList;
     30 import java.util.Collections;
     31 import java.util.HashSet;
     32 import java.util.List;
     33 import java.util.Set;
     34 
     35 /**
     36  * The {@link SelectionOverlay} paints the current selection as an overlay.
     37  */
     38 public class SelectionOverlay extends Overlay {
     39     private final LayoutCanvas mCanvas;
     40     private boolean mHidden;
     41 
     42     /**
     43      * Constructs a new {@link SelectionOverlay} tied to the given canvas.
     44      *
     45      * @param canvas the associated canvas
     46      */
     47     public SelectionOverlay(LayoutCanvas canvas) {
     48         mCanvas = canvas;
     49     }
     50 
     51     /**
     52      * Set whether the selection overlay should be hidden. This is done during some
     53      * gestures like resize where the new bounds could be confused with the current
     54      * selection bounds.
     55      *
     56      * @param hidden when true, hide the selection bounds, when false, unhide.
     57      */
     58     public void setHidden(boolean hidden) {
     59         mHidden = hidden;
     60     }
     61 
     62     /**
     63      * Paints the selection.
     64      *
     65      * @param selectionManager The {@link SelectionManager} holding the
     66      *            selection.
     67      * @param gcWrapper The graphics context wrapper for the layout rules to use.
     68      * @param gc The SWT graphics object
     69      * @param rulesEngine The {@link RulesEngine} holding the rules.
     70      */
     71     public void paint(SelectionManager selectionManager, GCWrapper gcWrapper,
     72             GC gc, RulesEngine rulesEngine) {
     73         if (mHidden) {
     74             return;
     75         }
     76 
     77         List<SelectionItem> selections = selectionManager.getSelections();
     78         int n = selections.size();
     79         if (n > 0) {
     80             List<NodeProxy> selectedNodes = new ArrayList<NodeProxy>();
     81             boolean isMultipleSelection = n > 1;
     82             for (SelectionItem s : selections) {
     83                 if (s.isRoot()) {
     84                     // The root selection is never painted
     85                     continue;
     86                 }
     87 
     88                 NodeProxy node = s.getNode();
     89                 if (node != null) {
     90                     paintSelection(gcWrapper, gc, s, isMultipleSelection);
     91                     selectedNodes.add(node);
     92                 }
     93             }
     94 
     95             if (selectedNodes.size() > 0) {
     96                 paintSelectionFeedback(gcWrapper, selectedNodes, rulesEngine);
     97             } else {
     98                 CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot();
     99                 if (root != null) {
    100                     NodeProxy parent = mCanvas.getNodeFactory().create(root);
    101                     rulesEngine.callPaintSelectionFeedback(gcWrapper,
    102                             parent, Collections.<INode>emptyList(), root.getViewObject());
    103                 }
    104             }
    105 
    106             if (n == 1) {
    107                 NodeProxy node = selections.get(0).getNode();
    108                 if (node != null) {
    109                     paintHints(gcWrapper, node, rulesEngine);
    110                 }
    111             }
    112         } else {
    113             CanvasViewInfo root = mCanvas.getViewHierarchy().getRoot();
    114             if (root != null) {
    115                 NodeProxy parent = mCanvas.getNodeFactory().create(root);
    116                 rulesEngine.callPaintSelectionFeedback(gcWrapper,
    117                         parent, Collections.<INode>emptyList(), root.getViewObject());
    118             }
    119         }
    120     }
    121 
    122     /** Paint hint for current selection */
    123     private void paintHints(GCWrapper gcWrapper, NodeProxy node, RulesEngine rulesEngine) {
    124         INode parent = node.getParent();
    125         if (parent instanceof NodeProxy) {
    126             NodeProxy parentNode = (NodeProxy) parent;
    127             List<String> infos = rulesEngine.callGetSelectionHint(parentNode, node);
    128             if (infos != null && infos.size() > 0) {
    129                 gcWrapper.useStyle(DrawingStyle.HELP);
    130 
    131                 Rect b = mCanvas.getImageOverlay().getImageBounds();
    132                 if (b == null) {
    133                     return;
    134                 }
    135 
    136                 // Compute the location to display the help. This is done in
    137                 // layout coordinates, so we need to apply the scale in reverse
    138                 // when making pixel margins
    139                 // TODO: We could take the Canvas dimensions into account to see
    140                 // where there is more room.
    141                 // TODO: The scrollbars should take the presence of hint text
    142                 // into account.
    143                 double scale = mCanvas.getScale();
    144                 int x, y;
    145                 if (b.w > b.h) {
    146                     x = (int) (b.x + 3 / scale);
    147                     y = (int) (b.y + b.h + 6 / scale);
    148                 } else {
    149                     x = (int) (b.x + b.w + 6 / scale);
    150                     y = (int) (b.y + 3 / scale);
    151                 }
    152                 gcWrapper.drawBoxedStrings(x, y, infos);
    153             }
    154         }
    155     }
    156 
    157     private void paintSelectionFeedback(GCWrapper gcWrapper, List<NodeProxy> nodes,
    158             RulesEngine rulesEngine) {
    159         // Add fastpath for n=1
    160 
    161         // Group nodes into parent/child groups
    162         Set<INode> parents = new HashSet<INode>();
    163         for (INode node : nodes) {
    164             INode parent = node.getParent();
    165             if (/*parent == null || */parent instanceof NodeProxy) {
    166                 NodeProxy parentNode = (NodeProxy) parent;
    167                 parents.add(parentNode);
    168             }
    169         }
    170         ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy();
    171         for (INode parent : parents) {
    172             List<INode> children = new ArrayList<INode>();
    173             for (INode node : nodes) {
    174                 INode nodeParent = node.getParent();
    175                 if (nodeParent == parent) {
    176                     children.add(node);
    177                 }
    178             }
    179             CanvasViewInfo viewInfo = viewHierarchy.findViewInfoFor((NodeProxy) parent);
    180             Object view = viewInfo != null ? viewInfo.getViewObject() : null;
    181 
    182             rulesEngine.callPaintSelectionFeedback(gcWrapper,
    183                     (NodeProxy) parent, children, view);
    184         }
    185     }
    186 
    187     /** Called by the canvas when a view is being selected. */
    188     private void paintSelection(IGraphics gc, GC swtGc, SelectionItem item,
    189             boolean isMultipleSelection) {
    190         CanvasViewInfo view = item.getViewInfo();
    191         if (view.isHidden()) {
    192             return;
    193         }
    194 
    195         NodeProxy selectedNode = item.getNode();
    196         Rect r = selectedNode.getBounds();
    197         if (!r.isValid()) {
    198             return;
    199         }
    200 
    201         gc.useStyle(DrawingStyle.SELECTION);
    202 
    203         Margins insets = mCanvas.getInsets(selectedNode.getFqcn());
    204         int x1 = r.x;
    205         int y1 = r.y;
    206         int x2 = r.x2() + 1;
    207         int y2 = r.y2() + 1;
    208 
    209         if (insets != null) {
    210             x1 += insets.left;
    211             x2 -= insets.right;
    212             y1 += insets.top;
    213             y2 -= insets.bottom;
    214         }
    215 
    216         gc.drawRect(x1, y1, x2, y2);
    217 
    218         // Paint sibling rectangles, if applicable
    219         List<CanvasViewInfo> siblings = view.getNodeSiblings();
    220         if (siblings != null) {
    221             for (CanvasViewInfo sibling : siblings) {
    222                 if (sibling != view) {
    223                     r = SwtUtils.toRect(sibling.getSelectionRect());
    224                     gc.fillRect(r);
    225                     gc.drawRect(r);
    226                 }
    227             }
    228         }
    229 
    230         // Paint selection handles. These are painted in control coordinates on the
    231         // real SWT GC object rather than in layout coordinates on the GCWrapper,
    232         // since we want them to have a fixed size that is independent of the
    233         // screen zoom.
    234         CanvasTransform horizontalTransform = mCanvas.getHorizontalTransform();
    235         CanvasTransform verticalTransform = mCanvas.getVerticalTransform();
    236         int radius = SelectionHandle.PIXEL_RADIUS;
    237         int doubleRadius = 2 * radius;
    238         for (SelectionHandle handle : item.getSelectionHandles()) {
    239             int cx = horizontalTransform.translate(handle.centerX);
    240             int cy = verticalTransform.translate(handle.centerY);
    241 
    242             SwtDrawingStyle style = SwtDrawingStyle.of(DrawingStyle.SELECTION);
    243             gc.setAlpha(style.getStrokeAlpha());
    244             swtGc.fillRectangle(cx - radius, cy - radius, doubleRadius, doubleRadius);
    245         }
    246     }
    247 }
    248