blob: 97d048108f0460e527c343eb93c69a08a94d4796 [file] [log] [blame]
/*
* 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);
}
}
}