• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.hierarchyviewerlib.ui;
18 
19 import com.android.hierarchyviewerlib.HierarchyViewerDirector;
20 import com.android.hierarchyviewerlib.models.TreeViewModel;
21 import com.android.hierarchyviewerlib.models.TreeViewModel.ITreeChangeListener;
22 import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
23 import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
24 
25 import org.eclipse.swt.SWT;
26 import org.eclipse.swt.events.DisposeEvent;
27 import org.eclipse.swt.events.DisposeListener;
28 import org.eclipse.swt.events.MouseEvent;
29 import org.eclipse.swt.events.MouseListener;
30 import org.eclipse.swt.events.PaintEvent;
31 import org.eclipse.swt.events.PaintListener;
32 import org.eclipse.swt.graphics.GC;
33 import org.eclipse.swt.graphics.Rectangle;
34 import org.eclipse.swt.graphics.Transform;
35 import org.eclipse.swt.widgets.Canvas;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.Event;
39 import org.eclipse.swt.widgets.Listener;
40 
41 import java.util.ArrayList;
42 
43 public class LayoutViewer extends Canvas implements ITreeChangeListener {
44 
45     private TreeViewModel mModel;
46 
47     private DrawableViewNode mTree;
48 
49     private DrawableViewNode mSelectedNode;
50 
51     private Transform mTransform;
52 
53     private Transform mInverse;
54 
55     private double mScale;
56 
57     private boolean mShowExtras = false;
58 
59     private boolean mOnBlack = true;
60 
LayoutViewer(Composite parent)61     public LayoutViewer(Composite parent) {
62         super(parent, SWT.NONE);
63         mModel = TreeViewModel.getModel();
64         mModel.addTreeChangeListener(this);
65 
66         addDisposeListener(mDisposeListener);
67         addPaintListener(mPaintListener);
68         addListener(SWT.Resize, mResizeListener);
69         addMouseListener(mMouseListener);
70 
71         mTransform = new Transform(Display.getDefault());
72         mInverse = new Transform(Display.getDefault());
73 
74         treeChanged();
75     }
76 
setShowExtras(boolean show)77     public void setShowExtras(boolean show) {
78         mShowExtras = show;
79         doRedraw();
80     }
81 
setOnBlack(boolean value)82     public void setOnBlack(boolean value) {
83         mOnBlack = value;
84         doRedraw();
85     }
86 
getOnBlack()87     public boolean getOnBlack() {
88         return mOnBlack;
89     }
90 
91     private DisposeListener mDisposeListener = new DisposeListener() {
92         public void widgetDisposed(DisposeEvent e) {
93             mModel.removeTreeChangeListener(LayoutViewer.this);
94             mTransform.dispose();
95             mInverse.dispose();
96             if (mSelectedNode != null) {
97                 mSelectedNode.viewNode.dereferenceImage();
98             }
99         }
100     };
101 
102     private Listener mResizeListener = new Listener() {
103         public void handleEvent(Event e) {
104             synchronized (this) {
105                 setTransform();
106             }
107         }
108     };
109 
110     private MouseListener mMouseListener = new MouseListener() {
111 
112         public void mouseDoubleClick(MouseEvent e) {
113             if (mSelectedNode != null) {
114                 HierarchyViewerDirector.getDirector()
115                         .showCapture(getShell(), mSelectedNode.viewNode);
116             }
117         }
118 
119         public void mouseDown(MouseEvent e) {
120             boolean selectionChanged = false;
121             DrawableViewNode newSelection = null;
122             synchronized (LayoutViewer.this) {
123                 if (mTree != null) {
124                     float[] pt = {
125                             e.x, e.y
126                     };
127                     mInverse.transform(pt);
128                     newSelection =
129                             updateSelection(mTree, pt[0], pt[1], 0, 0, 0, 0, mTree.viewNode.width,
130                                     mTree.viewNode.height);
131                     if (mSelectedNode != newSelection) {
132                         selectionChanged = true;
133                     }
134                 }
135             }
136             if (selectionChanged) {
137                 mModel.setSelection(newSelection);
138             }
139         }
140 
141         public void mouseUp(MouseEvent e) {
142             // pass
143         }
144     };
145 
updateSelection(DrawableViewNode node, float x, float y, int left, int top, int clipX, int clipY, int clipWidth, int clipHeight)146     private DrawableViewNode updateSelection(DrawableViewNode node, float x, float y, int left,
147             int top, int clipX, int clipY, int clipWidth, int clipHeight) {
148         if (!node.treeDrawn) {
149             return null;
150         }
151         // Update the clip
152         int x1 = Math.max(left, clipX);
153         int x2 = Math.min(left + node.viewNode.width, clipX + clipWidth);
154         int y1 = Math.max(top, clipY);
155         int y2 = Math.min(top + node.viewNode.height, clipY + clipHeight);
156         clipX = x1;
157         clipY = y1;
158         clipWidth = x2 - x1;
159         clipHeight = y2 - y1;
160         if (x < clipX || x > clipX + clipWidth || y < clipY || y > clipY + clipHeight) {
161             return null;
162         }
163         final int N = node.children.size();
164         for (int i = N - 1; i >= 0; i--) {
165             DrawableViewNode child = node.children.get(i);
166             DrawableViewNode ret =
167                     updateSelection(child, x, y,
168                             left + child.viewNode.left - node.viewNode.scrollX, top
169                                     + child.viewNode.top - node.viewNode.scrollY, clipX, clipY,
170                             clipWidth, clipHeight);
171             if (ret != null) {
172                 return ret;
173             }
174         }
175         return node;
176     }
177 
178     private PaintListener mPaintListener = new PaintListener() {
179         public void paintControl(PaintEvent e) {
180             synchronized (LayoutViewer.this) {
181                 if (mOnBlack) {
182                     e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
183                 } else {
184                     e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
185                 }
186                 e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
187                 if (mTree != null) {
188                     e.gc.setLineWidth((int) Math.ceil(0.3 / mScale));
189                     e.gc.setTransform(mTransform);
190                     if (mOnBlack) {
191                         e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
192                     } else {
193                         e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
194                     }
195                     Rectangle parentClipping = e.gc.getClipping();
196                     e.gc.setClipping(0, 0, mTree.viewNode.width + (int) Math.ceil(0.3 / mScale),
197                             mTree.viewNode.height + (int) Math.ceil(0.3 / mScale));
198                     paintRecursive(e.gc, mTree, 0, 0, true);
199 
200                     if (mSelectedNode != null) {
201                         e.gc.setClipping(parentClipping);
202 
203                         // w00t, let's be nice and display the whole path in
204                         // light red and the selected node in dark red.
205                         ArrayList<Point> rightLeftDistances = new ArrayList<Point>();
206                         int left = 0;
207                         int top = 0;
208                         DrawableViewNode currentNode = mSelectedNode;
209                         while (currentNode != mTree) {
210                             left += currentNode.viewNode.left;
211                             top += currentNode.viewNode.top;
212                             currentNode = currentNode.parent;
213                             left -= currentNode.viewNode.scrollX;
214                             top -= currentNode.viewNode.scrollY;
215                             rightLeftDistances.add(new Point(left, top));
216                         }
217                         e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
218                         currentNode = mSelectedNode.parent;
219                         final int N = rightLeftDistances.size();
220                         for (int i = 0; i < N; i++) {
221                             e.gc.drawRectangle((int) (left - rightLeftDistances.get(i).x),
222                                     (int) (top - rightLeftDistances.get(i).y),
223                                     currentNode.viewNode.width, currentNode.viewNode.height);
224                             currentNode = currentNode.parent;
225                         }
226 
227                         if (mShowExtras && mSelectedNode.viewNode.image != null) {
228                             e.gc.drawImage(mSelectedNode.viewNode.image, left, top);
229                             if (mOnBlack) {
230                                 e.gc.setForeground(Display.getDefault().getSystemColor(
231                                         SWT.COLOR_WHITE));
232                             } else {
233                                 e.gc.setForeground(Display.getDefault().getSystemColor(
234                                         SWT.COLOR_BLACK));
235                             }
236                             paintRecursive(e.gc, mSelectedNode, left, top, true);
237 
238                         }
239 
240                         e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED));
241                         e.gc.setLineWidth((int) Math.ceil(2 / mScale));
242                         e.gc.drawRectangle(left, top, mSelectedNode.viewNode.width,
243                                 mSelectedNode.viewNode.height);
244                     }
245                 }
246             }
247         }
248     };
249 
paintRecursive(GC gc, DrawableViewNode node, int left, int top, boolean root)250     private void paintRecursive(GC gc, DrawableViewNode node, int left, int top, boolean root) {
251         if (!node.treeDrawn) {
252             return;
253         }
254         // Don't shift the root
255         if (!root) {
256             left += node.viewNode.left;
257             top += node.viewNode.top;
258         }
259         Rectangle parentClipping = gc.getClipping();
260         int x1 = Math.max(parentClipping.x, left);
261         int x2 =
262                 Math.min(parentClipping.x + parentClipping.width, left + node.viewNode.width
263                         + (int) Math.ceil(0.3 / mScale));
264         int y1 = Math.max(parentClipping.y, top);
265         int y2 =
266                 Math.min(parentClipping.y + parentClipping.height, top + node.viewNode.height
267                         + (int) Math.ceil(0.3 / mScale));
268 
269         // Clipping is weird... You set it to -5 and it comes out 17 or
270         // something.
271         if (x2 <= x1 || y2 <= y1) {
272             return;
273         }
274         gc.setClipping(x1, y1, x2 - x1, y2 - y1);
275         final int N = node.children.size();
276         for (int i = 0; i < N; i++) {
277             paintRecursive(gc, node.children.get(i), left - node.viewNode.scrollX, top
278                     - node.viewNode.scrollY, false);
279         }
280         gc.setClipping(parentClipping);
281         if (!node.viewNode.willNotDraw) {
282             gc.drawRectangle(left, top, node.viewNode.width, node.viewNode.height);
283         }
284 
285     }
286 
doRedraw()287     private void doRedraw() {
288         Display.getDefault().syncExec(new Runnable() {
289             public void run() {
290                 redraw();
291             }
292         });
293     }
294 
setTransform()295     private void setTransform() {
296         if (mTree != null) {
297             Rectangle bounds = getBounds();
298             int leftRightPadding = bounds.width <= 30 ? 0 : 5;
299             int topBottomPadding = bounds.height <= 30 ? 0 : 5;
300             mScale =
301                     Math.min(1.0 * (bounds.width - leftRightPadding * 2) / mTree.viewNode.width, 1.0
302                             * (bounds.height - topBottomPadding * 2) / mTree.viewNode.height);
303             int scaledWidth = (int) Math.ceil(mTree.viewNode.width * mScale);
304             int scaledHeight = (int) Math.ceil(mTree.viewNode.height * mScale);
305 
306             mTransform.identity();
307             mInverse.identity();
308             mTransform.translate((bounds.width - scaledWidth) / 2.0f,
309                     (bounds.height - scaledHeight) / 2.0f);
310             mInverse.translate((bounds.width - scaledWidth) / 2.0f,
311                     (bounds.height - scaledHeight) / 2.0f);
312             mTransform.scale((float) mScale, (float) mScale);
313             mInverse.scale((float) mScale, (float) mScale);
314             if (bounds.width != 0 && bounds.height != 0) {
315                 mInverse.invert();
316             }
317         }
318     }
319 
selectionChanged()320     public void selectionChanged() {
321         synchronized (this) {
322             if (mSelectedNode != null) {
323                 mSelectedNode.viewNode.dereferenceImage();
324             }
325             mSelectedNode = mModel.getSelection();
326             if (mSelectedNode != null) {
327                 mSelectedNode.viewNode.referenceImage();
328             }
329         }
330         doRedraw();
331     }
332 
333     // Note the syncExec and then synchronized... It avoids deadlock
treeChanged()334     public void treeChanged() {
335         Display.getDefault().syncExec(new Runnable() {
336             public void run() {
337                 synchronized (this) {
338                     if (mSelectedNode != null) {
339                         mSelectedNode.viewNode.dereferenceImage();
340                     }
341                     mTree = mModel.getTree();
342                     mSelectedNode = mModel.getSelection();
343                     if (mSelectedNode != null) {
344                         mSelectedNode.viewNode.referenceImage();
345                     }
346                     setTransform();
347                 }
348             }
349         });
350         doRedraw();
351     }
352 
viewportChanged()353     public void viewportChanged() {
354         // pass
355     }
356 
zoomChanged()357     public void zoomChanged() {
358         // pass
359     }
360 }
361