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.eclipse.adt.internal.editors.IconFactory;
20 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
21 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
22 import com.google.common.collect.Lists;
23 
24 import org.eclipse.core.resources.IMarker;
25 import org.eclipse.swt.graphics.GC;
26 import org.eclipse.swt.graphics.Image;
27 import org.eclipse.swt.graphics.ImageData;
28 import org.eclipse.swt.graphics.Rectangle;
29 import org.w3c.dom.Node;
30 
31 import java.util.Collection;
32 
33 /**
34  * The {@link LintOverlay} paints an icon over each view that contains at least one
35  * lint error (unless the view is smaller than the icon)
36  */
37 public class LintOverlay extends Overlay {
38     /** Approximate size of lint overlay icons */
39     static final int ICON_SIZE = 8;
40     /** Alpha to draw lint overlay icons with */
41     private static final int ALPHA = 192;
42 
43     private final LayoutCanvas mCanvas;
44     private Image mWarningImage;
45     private Image mErrorImage;
46 
47     /**
48      * Constructs a new {@link LintOverlay}
49      *
50      * @param canvas the associated canvas
51      */
LintOverlay(LayoutCanvas canvas)52     public LintOverlay(LayoutCanvas canvas) {
53         mCanvas = canvas;
54     }
55 
56     @Override
isHiding()57     public boolean isHiding() {
58         return super.isHiding() || !AdtPrefs.getPrefs().isLintOnSave();
59     }
60 
61     @Override
paint(GC gc)62     public void paint(GC gc) {
63         LayoutEditorDelegate editor = mCanvas.getEditorDelegate();
64         Collection<Node> nodes = editor.getLintNodes();
65         if (nodes != null && !nodes.isEmpty()) {
66             // Copy list before iterating through it to avoid a concurrent list modification
67             // in case lint runs in the background while painting and updates this list
68             nodes = Lists.newArrayList(nodes);
69             ViewHierarchy hierarchy = mCanvas.getViewHierarchy();
70             Image icon = getWarningIcon();
71             ImageData imageData = icon.getImageData();
72             int iconWidth = imageData.width;
73             int iconHeight = imageData.height;
74             CanvasTransform mHScale = mCanvas.getHorizontalTransform();
75             CanvasTransform mVScale = mCanvas.getVerticalTransform();
76 
77             // Right/bottom edges of the canvas image; don't paint overlays outside of
78             // that. (With for example RelativeLayouts with margins rendered on smaller
79             // screens than they are intended for this can happen.)
80             int maxX = mHScale.translate(0) + mHScale.getScaledImgSize();
81             int maxY = mVScale.translate(0) + mVScale.getScaledImgSize();
82 
83             int oldAlpha = gc.getAlpha();
84             try {
85                 gc.setAlpha(ALPHA);
86                 for (Node node : nodes) {
87                     CanvasViewInfo vi = hierarchy.findViewInfoFor(node);
88                     if (vi != null) {
89                         Rectangle bounds = vi.getAbsRect();
90                         int x = mHScale.translate(bounds.x);
91                         int y = mVScale.translate(bounds.y);
92                         int w = mHScale.scale(bounds.width);
93                         int h = mVScale.scale(bounds.height);
94                         if (w < iconWidth || h < iconHeight) {
95                             // Don't draw badges on tiny widgets (including those
96                             // that aren't tiny but are zoomed out too far)
97                             continue;
98                         }
99 
100                         x += w - iconWidth;
101                         y += h - iconHeight;
102 
103                         if (x > maxX || y > maxY) {
104                             continue;
105                         }
106 
107                         boolean isError = false;
108                         IMarker marker = editor.getIssueForNode(vi.getUiViewNode());
109                         if (marker != null) {
110                             int severity = marker.getAttribute(IMarker.SEVERITY, 0);
111                             isError = severity == IMarker.SEVERITY_ERROR;
112                         }
113 
114                         icon = isError ? getErrorIcon() : getWarningIcon();
115 
116                         gc.drawImage(icon, x, y);
117                     }
118                 }
119             } finally {
120                 gc.setAlpha(oldAlpha);
121             }
122         }
123     }
124 
getWarningIcon()125     private Image getWarningIcon() {
126         if (mWarningImage == null) {
127             mWarningImage = IconFactory.getInstance().getIcon("warning-badge"); //$NON-NLS-1$
128         }
129 
130         return mWarningImage;
131     }
132 
getErrorIcon()133     private Image getErrorIcon() {
134         if (mErrorImage == null) {
135             mErrorImage = IconFactory.getInstance().getIcon("error-badge");     //$NON-NLS-1$
136         }
137 
138         return mErrorImage;
139     }
140 }
141