aboutsummaryrefslogtreecommitdiffstats
path: root/hierarchyviewer2/libs
diff options
context:
space:
mode:
Diffstat (limited to 'hierarchyviewer2/libs')
-rw-r--r--hierarchyviewer2/libs/Android.mk1
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/.classpath2
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk7
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java114
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java29
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceConnection.java5
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java101
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java131
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java9
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/CaptureDisplay.java213
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java)4
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/LayoutViewer.java319
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java)103
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java)106
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java)10
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java)15
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java)8
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeView.java617
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java)121
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java (renamed from hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java)29
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java (renamed from hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java)25
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.pngbin0 -> 219 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.pngbin0 -> 220 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.pngbin0 -> 221 bytes
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/.classpath10
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/.gitignore1
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/.project17
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/Android.mk16
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/src/Android.mk29
-rw-r--r--hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeView.java361
30 files changed, 1757 insertions, 646 deletions
diff --git a/hierarchyviewer2/libs/Android.mk b/hierarchyviewer2/libs/Android.mk
index 7d5c631..f4b34cd 100644
--- a/hierarchyviewer2/libs/Android.mk
+++ b/hierarchyviewer2/libs/Android.mk
@@ -14,4 +14,3 @@
HIERARCHYVIEWERLIBS_LOCAL_DIR := $(call my-dir)
include $(HIERARCHYVIEWERLIBS_LOCAL_DIR)/hierarchyviewerlib/Android.mk
-include $(HIERARCHYVIEWERLIBS_LOCAL_DIR)/hierarchyvieweruilib/Android.mk
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
index b0326c8..ef57724 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/.classpath
@@ -3,5 +3,7 @@
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
index 1be1a29..922e337 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/Android.mk
@@ -16,8 +16,13 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAVA_RESOURCE_DIRS := resources
-LOCAL_JAVA_LIBRARIES := ddmlib
+LOCAL_JAVA_LIBRARIES := ddmlib \
+ ddmuilib \
+ hierarchyviewerlib \
+ swt \
+ org.eclipse.jface_3.4.2.M20090107-0800
LOCAL_MODULE := hierarchyviewerlib
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
index 65cb24f..48a45dd 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/HierarchyViewerDirector.java
@@ -28,6 +28,16 @@ import com.android.hierarchyviewerlib.device.Window;
import com.android.hierarchyviewerlib.device.WindowUpdater;
import com.android.hierarchyviewerlib.device.DeviceBridge.ViewServerInfo;
import com.android.hierarchyviewerlib.device.WindowUpdater.IWindowChangeListener;
+import com.android.hierarchyviewerlib.ui.CaptureDisplay;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
import java.io.IOException;
@@ -175,19 +185,12 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener,
public void run() {
if (ComponentRegistry.getDeviceSelectionModel().getFocusedWindow(device) != -1
&& device == ComponentRegistry.getPixelPerfectModel().getDevice()) {
- try {
- ViewNode viewNode =
- DeviceBridge.loadWindowData(Window.getFocusedWindow(device));
- RawImage screenshot = device.getScreenshot();
- ComponentRegistry.getPixelPerfectModel().setFocusData(screenshot,
+ ViewNode viewNode =
+ DeviceBridge.loadWindowData(Window.getFocusedWindow(device));
+ Image screenshotImage = getScreenshotImage(device);
+ if (screenshotImage != null) {
+ ComponentRegistry.getPixelPerfectModel().setFocusData(screenshotImage,
viewNode);
- } catch (IOException e) {
- Log.e(TAG, "Unable to load screenshot from device " + device);
- } catch (TimeoutException e) {
- Log.e(TAG, "Timeout loading screenshot from device " + device);
- } catch (AdbCommandRejectedException e) {
- Log.e(TAG, "Adb rejected command to load screenshot from device "
- + device);
}
}
synchronized (HierarchyViewerDirector.this) {
@@ -202,22 +205,48 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener,
public void loadPixelPerfectData(final IDevice device) {
executeInBackground(new Runnable() {
public void run() {
- try {
- RawImage screenshot = device.getScreenshot();
+ Image screenshotImage = getScreenshotImage(device);
+ if (screenshotImage != null) {
ViewNode viewNode =
DeviceBridge.loadWindowData(Window.getFocusedWindow(device));
- ComponentRegistry.getPixelPerfectModel().setData(device, screenshot, viewNode);
- } catch (IOException e) {
- Log.e(TAG, "Unable to load screenshot from device " + device);
- } catch (TimeoutException e) {
- Log.e(TAG, "Timeout loading screenshot from device " + device);
- } catch (AdbCommandRejectedException e) {
- Log.e(TAG, "Adb rejected command to load screenshot from device " + device);
+ ComponentRegistry.getPixelPerfectModel().setData(device, screenshotImage,
+ viewNode);
}
}
});
}
+ private Image getScreenshotImage(IDevice device) {
+ try {
+ final RawImage screenshot = device.getScreenshot();
+ if (screenshot == null) {
+ return null;
+ }
+ class ImageContainer {
+ public Image image;
+ }
+ final ImageContainer imageContainer = new ImageContainer();
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ ImageData imageData =
+ new ImageData(screenshot.width, screenshot.height, screenshot.bpp,
+ new PaletteData(screenshot.getRedMask(), screenshot
+ .getGreenMask(), screenshot.getBlueMask()), 1,
+ screenshot.data);
+ imageContainer.image = new Image(Display.getDefault(), imageData);
+ }
+ });
+ return imageContainer.image;
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to load screenshot from device " + device);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Timeout loading screenshot from device " + device);
+ } catch (AdbCommandRejectedException e) {
+ Log.e(TAG, "Adb rejected command to load screenshot from device " + device);
+ }
+ return null;
+ }
+
public void loadViewTreeData(final Window window) {
executeInBackground(new Runnable() {
public void run() {
@@ -230,4 +259,47 @@ public abstract class HierarchyViewerDirector implements IDeviceChangeListener,
}
});
}
+
+ public void loadOverlay(final Shell shell) {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
+ fileDialog.setFilterExtensions(new String[] {
+ "*.jpg;*.jpeg;*.png;*.gif;*.bmp"
+ });
+ fileDialog.setFilterNames(new String[] {
+ "Image (*.jpg, *.jpeg, *.png, *.gif, *.bmp)"
+ });
+ String fileName = fileDialog.open();
+ if (fileName != null) {
+ try {
+ Image image = new Image(Display.getDefault(), fileName);
+ ComponentRegistry.getPixelPerfectModel().setOverlayImage(image);
+ } catch (SWTException e) {
+ Log.e(TAG, "Unable to load image from " + fileName);
+ }
+ }
+ }
+ });
+ }
+
+ public void showCapture(final Shell shell, final ViewNode viewNode) {
+ executeInBackground(new Runnable() {
+ public void run() {
+ final Image image = DeviceBridge.loadCapture(viewNode.window, viewNode);
+ if (image != null) {
+ viewNode.image = image;
+
+ // Force the layout viewer to redraw.
+ ComponentRegistry.getTreeViewModel().notifySelectionChanged();
+
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ CaptureDisplay.show(shell, viewNode, image);
+ }
+ });
+ }
+ }
+ });
+ }
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
index af3f9f1..23c6f07 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceBridge.java
@@ -24,6 +24,9 @@ import com.android.ddmlib.MultiLineReceiver;
import com.android.ddmlib.ShellCommandUnresponsiveException;
import com.android.ddmlib.TimeoutException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Display;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
@@ -413,7 +416,7 @@ public class DeviceBridge {
currentNode = currentNode.parent;
currentDepth--;
}
- currentNode = new ViewNode(currentNode, line.substring(depth));
+ currentNode = new ViewNode(window, currentNode, line.substring(depth));
currentDepth = depth;
}
if (currentNode == null) {
@@ -447,7 +450,11 @@ public class DeviceBridge {
if (protocol < 3) {
return loadProfileData(viewNode, in);
} else {
- return loadProfileDataRecursive(viewNode, in);
+ boolean ret = loadProfileDataRecursive(viewNode, in);
+ if (ret) {
+ viewNode.setProfileRatings();
+ }
+ return ret;
}
} catch (IOException e) {
Log.e(TAG, "Unable to load profiling data for window " + window.getTitle()
@@ -485,4 +492,22 @@ public class DeviceBridge {
}
return true;
}
+
+ public static Image loadCapture(Window window, ViewNode viewNode) {
+ DeviceConnection connection = null;
+ try {
+ connection = new DeviceConnection(window.getDevice());
+ connection.sendCommand("CAPTURE " + window.encode() + " " + viewNode.toString());
+ return new Image(Display.getDefault(), connection.getSocket().getInputStream());
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to capture data for node " + viewNode + " in window "
+ + window.getTitle() + " on device " + window.getDevice());
+ } finally {
+ if (connection != null) {
+ connection.close();
+ }
+ }
+ return null;
+ }
+
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceConnection.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceConnection.java
index 581f76b..18b9619 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceConnection.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/DeviceConnection.java
@@ -24,6 +24,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
+import java.net.Socket;
import java.nio.channels.SocketChannel;
/**
@@ -61,6 +62,10 @@ public class DeviceConnection {
return out;
}
+ public Socket getSocket() {
+ return socketChannel.socket();
+ }
+
public void sendCommand(String command) throws IOException {
BufferedWriter out = getOutputStream();
out.write(command);
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java
index 2dd9b61..2872952 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/device/ViewNode.java
@@ -16,6 +16,8 @@
package com.android.hierarchyviewerlib.device;
+import org.eclipse.swt.graphics.Image;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -26,6 +28,15 @@ import java.util.Set;
import java.util.TreeSet;
public class ViewNode {
+
+ public static enum ProfileRating {
+ RED, YELLOW, GREEN, NONE
+ };
+
+ private static final double RED_THRESHOLD = 0.8;
+
+ private static final double YELLOW_THRESHOLD = 0.5;
+
public static final String MISCELLANIOUS = "miscellaneous";
public String id;
@@ -86,9 +97,22 @@ public class ViewNode {
public double drawTime;
+ public ProfileRating measureRating = ProfileRating.NONE;
+
+ public ProfileRating layoutRating = ProfileRating.NONE;
+
+ public ProfileRating drawRating = ProfileRating.NONE;
+
public Set<String> categories = new TreeSet<String>();
- public ViewNode(ViewNode parent, String data) {
+ public Window window;
+
+ public Image image;
+
+ public int imageReferences = 1;
+
+ public ViewNode(Window window, ViewNode parent, String data) {
+ this.window = window;
this.parent = parent;
index = this.parent == null ? 0 : this.parent.children.size();
if (this.parent != null) {
@@ -106,6 +130,25 @@ public class ViewNode {
drawTime = -1;
}
+ public void dispose() {
+ final int N = children.size();
+ for(int i = 0; i<N; i++) {
+ children.get(i).dispose();
+ }
+ dereferenceImage();
+ }
+
+ public void referenceImage() {
+ imageReferences++;
+ }
+
+ public void dereferenceImage() {
+ imageReferences--;
+ if (image != null && imageReferences == 0) {
+ image.dispose();
+ }
+ }
+
private void loadProperties(String data) {
int start = 0;
boolean stop;
@@ -141,9 +184,9 @@ public class ViewNode {
top = namedProperties.containsKey("mTop") ?
getInt("mTop", 0) : getInt("layout:mTop", 0);
width = namedProperties.containsKey("getWidth()") ?
- getInt("getWidth()", 0) : getInt("measurement:getWidth()", 0);
+ getInt("getWidth()", 0) : getInt("layout:getWidth()", 0);
height = namedProperties.containsKey("getHeight()") ?
- getInt("getHeight()", 0) : getInt("measurement:getHeight()", 0);
+ getInt("getHeight()", 0) : getInt("layout:getHeight()", 0);
scrollX = namedProperties.containsKey("mScrollX") ?
getInt("mScrollX", 0) : getInt("scrolling:mScrollX", 0);
scrollY = namedProperties.containsKey("mScrollY") ?
@@ -158,19 +201,19 @@ public class ViewNode {
getInt("mPaddingBottom", 0) : getInt("padding:mPaddingBottom", 0);
marginLeft = namedProperties.containsKey("layout_leftMargin") ?
getInt("layout_leftMargin", Integer.MIN_VALUE) :
- getInt("layout:leftMargin", Integer.MIN_VALUE);
+ getInt("layout:layout_leftMargin", Integer.MIN_VALUE);
marginRight = namedProperties.containsKey("layout_rightMargin") ?
getInt("layout_rightMargin", Integer.MIN_VALUE) :
- getInt("layout:rightMargin", Integer.MIN_VALUE);
+ getInt("layout:layout_rightMargin", Integer.MIN_VALUE);
marginTop = namedProperties.containsKey("layout_topMargin") ?
getInt("layout_topMargin", Integer.MIN_VALUE) :
- getInt("layout:topMargin", Integer.MIN_VALUE);
+ getInt("layout:layout_topMargin", Integer.MIN_VALUE);
marginBottom = namedProperties.containsKey("layout_bottomMargin") ?
getInt("layout_bottomMargin", Integer.MIN_VALUE) :
- getInt("layout:bottomMargin", Integer.MIN_VALUE);
+ getInt("layout:layout_bottomMargin", Integer.MIN_VALUE);
baseline = namedProperties.containsKey("getBaseline()") ?
getInt("getBaseline()", 0) :
- getInt("measurement:getBaseline()", 0);
+ getInt("layout:getBaseline()", 0);
willNotDraw = namedProperties.containsKey("willNotDraw()") ?
getBoolean("willNotDraw()", false) :
getBoolean("drawing:willNotDraw()", false);
@@ -193,6 +236,48 @@ public class ViewNode {
}
}
+ public void setProfileRatings() {
+ final int N = children.size();
+ if (N > 1) {
+ double totalMeasure = 0;
+ double totalLayout = 0;
+ double totalDraw = 0;
+ for (int i = 0; i < N; i++) {
+ ViewNode child = children.get(i);
+ totalMeasure += child.measureTime;
+ totalLayout += child.layoutTime;
+ totalDraw += child.drawTime;
+ }
+ for (int i = 0; i < N; i++) {
+ ViewNode child = children.get(i);
+ if (child.measureTime / totalMeasure >= RED_THRESHOLD) {
+ child.measureRating = ProfileRating.RED;
+ } else if (child.measureTime / totalMeasure >= YELLOW_THRESHOLD) {
+ child.measureRating = ProfileRating.YELLOW;
+ } else {
+ child.measureRating = ProfileRating.GREEN;
+ }
+ if (child.layoutTime / totalLayout >= RED_THRESHOLD) {
+ child.layoutRating = ProfileRating.RED;
+ } else if (child.layoutTime / totalLayout >= YELLOW_THRESHOLD) {
+ child.layoutRating = ProfileRating.YELLOW;
+ } else {
+ child.layoutRating = ProfileRating.GREEN;
+ }
+ if (child.drawTime / totalDraw >= RED_THRESHOLD) {
+ child.drawRating = ProfileRating.RED;
+ } else if (child.drawTime / totalDraw >= YELLOW_THRESHOLD) {
+ child.drawRating = ProfileRating.YELLOW;
+ } else {
+ child.drawRating = ProfileRating.GREEN;
+ }
+ }
+ }
+ for (int i = 0; i < N; i++) {
+ children.get(i).setProfileRatings();
+ }
+ }
+
private boolean getBoolean(String name, boolean defaultValue) {
Property p = namedProperties.get(name);
if (p != null) {
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java
index 4f19368..e8f8240 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/PixelPerfectModel.java
@@ -20,6 +20,10 @@ import com.android.ddmlib.IDevice;
import com.android.ddmlib.RawImage;
import com.android.hierarchyviewerlib.device.ViewNode;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Display;
+
import java.util.ArrayList;
public class PixelPerfectModel {
@@ -30,20 +34,9 @@ public class PixelPerfectModel {
private static final int DEFAULT_ZOOM = 8;
- public static class Point {
- public int x;
-
- public int y;
-
- Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- }
-
private IDevice device;
- private RawImage image;
+ private Image image;
private Point crosshairLocation;
@@ -56,19 +49,31 @@ public class PixelPerfectModel {
private final ArrayList<ImageChangeListener> imageChangeListeners =
new ArrayList<ImageChangeListener>();
- public void setData(IDevice device, RawImage image, ViewNode viewNode) {
- synchronized (this) {
- this.device = device;
- this.image = image;
- this.viewNode = viewNode;
- if (image != null) {
- this.crosshairLocation = new Point(image.width / 2, image.height / 2);
- } else {
- this.crosshairLocation = null;
+ private Image overlayImage;
+
+ private double overlayTransparency = 0.5;
+
+ public void setData(final IDevice device, final Image image, final ViewNode viewNode) {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (PixelPerfectModel.this) {
+ PixelPerfectModel.this.device = device;
+ if (PixelPerfectModel.this.image != null) {
+ PixelPerfectModel.this.image.dispose();
+ }
+ PixelPerfectModel.this.image = image;
+ PixelPerfectModel.this.viewNode = viewNode;
+ if (image != null) {
+ PixelPerfectModel.this.crosshairLocation =
+ new Point(image.getBounds().width / 2, image.getBounds().height / 2);
+ } else {
+ PixelPerfectModel.this.crosshairLocation = null;
+ }
+ PixelPerfectModel.this.selected = null;
+ zoom = DEFAULT_ZOOM;
+ }
}
- this.selected = null;
- zoom = DEFAULT_ZOOM;
- }
+ });
notifyImageLoaded();
}
@@ -86,12 +91,19 @@ public class PixelPerfectModel {
notifySelectionChanged();
}
- public void setFocusData(RawImage image, ViewNode viewNode) {
- synchronized (this) {
- this.image = image;
- this.viewNode = viewNode;
- this.selected = null;
- }
+ public void setFocusData(final Image image, final ViewNode viewNode) {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (PixelPerfectModel.this) {
+ if (PixelPerfectModel.this.image != null) {
+ PixelPerfectModel.this.image.dispose();
+ }
+ PixelPerfectModel.this.image = image;
+ PixelPerfectModel.this.viewNode = viewNode;
+ PixelPerfectModel.this.selected = null;
+ }
+ }
+ });
notifyFocusChanged();
}
@@ -108,6 +120,29 @@ public class PixelPerfectModel {
notifyZoomChanged();
}
+ public void setOverlayImage(final Image overlayImage) {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (PixelPerfectModel.this) {
+ if (PixelPerfectModel.this.overlayImage != null) {
+ PixelPerfectModel.this.overlayImage.dispose();
+ }
+ PixelPerfectModel.this.overlayImage = overlayImage;
+ }
+ }
+ });
+ notifyOverlayChanged();
+ }
+
+ public void setOverlayTransparency(double value) {
+ synchronized (this) {
+ value = Math.max(value, 0);
+ value = Math.min(value, 1);
+ overlayTransparency = value;
+ }
+ notifyOverlayTransparencyChanged();
+ }
+
public ViewNode getViewNode() {
synchronized (this) {
return viewNode;
@@ -120,7 +155,7 @@ public class PixelPerfectModel {
}
}
- public RawImage getImage() {
+ public Image getImage() {
synchronized (this) {
return image;
}
@@ -144,6 +179,18 @@ public class PixelPerfectModel {
}
}
+ public Image getOverlayImage() {
+ synchronized (this) {
+ return overlayImage;
+ }
+ }
+
+ public double getOverlayTransparency() {
+ synchronized (this) {
+ return overlayTransparency;
+ }
+ }
+
public static interface ImageChangeListener {
public void imageLoaded();
@@ -156,6 +203,10 @@ public class PixelPerfectModel {
public void focusChanged();
public void zoomChanged();
+
+ public void overlayChanged();
+
+ public void overlayTransparencyChanged();
}
private ImageChangeListener[] getImageChangeListenerList() {
@@ -225,6 +276,24 @@ public class PixelPerfectModel {
}
}
+ public void notifyOverlayChanged() {
+ ImageChangeListener[] listeners = getImageChangeListenerList();
+ if (listeners != null) {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].overlayChanged();
+ }
+ }
+ }
+
+ public void notifyOverlayTransparencyChanged() {
+ ImageChangeListener[] listeners = getImageChangeListenerList();
+ if (listeners != null) {
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].overlayTransparencyChanged();
+ }
+ }
+ }
+
public void addImageChangeListener(ImageChangeListener listener) {
synchronized (imageChangeListeners) {
imageChangeListeners.add(listener);
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java
index f6279df..c49ce95 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/models/TreeViewModel.java
@@ -18,9 +18,9 @@ package com.android.hierarchyviewerlib.models;
import com.android.hierarchyviewerlib.device.ViewNode;
import com.android.hierarchyviewerlib.device.Window;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Point;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Rectangle;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Rectangle;
import java.util.ArrayList;
@@ -44,6 +44,9 @@ public class TreeViewModel {
public void setData(Window window, ViewNode viewNode) {
synchronized (this) {
+ if (tree != null) {
+ tree.viewNode.dispose();
+ }
this.window = window;
tree = new DrawableViewNode(viewNode);
tree.setLeft();
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/CaptureDisplay.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/CaptureDisplay.java
new file mode 100644
index 0000000..54a94fd
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/CaptureDisplay.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.hierarchyviewerlib.ui;
+
+import com.android.hierarchyviewerlib.device.ViewNode;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.ShellAdapter;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+
+public class CaptureDisplay {
+ private static Shell shell;
+
+ private static Canvas canvas;
+
+ private static Image image;
+
+ private static ViewNode viewNode;
+
+ private static Composite buttonBar;
+
+ private static Button onWhite;
+
+ private static Button onBlack;
+
+ private static Button showExtras;
+
+ public static void show(Shell parentShell, ViewNode viewNode, Image image) {
+ if (shell == null) {
+ createShell();
+ }
+ if (shell.isVisible() && CaptureDisplay.viewNode != null) {
+ CaptureDisplay.viewNode.dereferenceImage();
+ }
+ CaptureDisplay.image = image;
+ CaptureDisplay.viewNode = viewNode;
+ viewNode.referenceImage();
+ int dotIndex = viewNode.name.lastIndexOf('.');
+ if (dotIndex != -1) {
+ shell.setText(viewNode.name.substring(dotIndex + 1));
+ } else {
+ shell.setText(viewNode.name);
+ }
+
+ boolean shellVisible = shell.isVisible();
+ if (!shellVisible) {
+ shell.setSize(0, 0);
+ }
+ shell.open();
+ Rectangle bounds =
+ shell.computeTrim(0, 0, Math.max(buttonBar.getBounds().width,
+ image.getBounds().width), buttonBar.getBounds().height
+ + image.getBounds().height + 5);
+ System.out.println(bounds);
+ shell.setSize(bounds.width, bounds.height);
+ if (!shellVisible) {
+ shell.setLocation(parentShell.getBounds().x
+ + (parentShell.getBounds().width - bounds.width) / 2, parentShell.getBounds().y
+ + (parentShell.getBounds().height - bounds.height) / 2);
+ }
+ if (shellVisible) {
+ canvas.redraw();
+ }
+ }
+
+ private static void createShell() {
+ shell = new Shell(Display.getDefault(), SWT.CLOSE | SWT.TITLE);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ shell.setLayout(gridLayout);
+
+ buttonBar = new Composite(shell, SWT.NONE);
+ RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);
+ rowLayout.pack = true;
+ rowLayout.center = true;
+ buttonBar.setLayout(rowLayout);
+ Composite buttons = new Composite(buttonBar, SWT.NONE);
+ buttons.setLayout(new FillLayout());
+
+ onWhite = new Button(buttons, SWT.TOGGLE);
+ onWhite.setText("On White");
+ onBlack = new Button(buttons, SWT.TOGGLE);
+ onBlack.setText("On Black");
+ onBlack.setSelection(true);
+ onWhite.addSelectionListener(whiteSelectionListener);
+ onBlack.addSelectionListener(blackSelectionListener);
+
+ showExtras = new Button(buttonBar, SWT.CHECK);
+ showExtras.setText("Show Extras");
+ showExtras.addSelectionListener(extrasSelectionListener);
+
+ canvas = new Canvas(shell, SWT.NONE);
+ canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ canvas.addPaintListener(paintListener);
+
+ shell.addShellListener(shellListener);
+ }
+
+ private static PaintListener paintListener = new PaintListener() {
+
+ public void paintControl(PaintEvent e) {
+ if (onWhite.getSelection()) {
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ } else {
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+ }
+ e.gc.fillRectangle(0, 0, canvas.getBounds().width, canvas.getBounds().height);
+ if (image != null) {
+ int width = image.getBounds().width;
+ int height = image.getBounds().height;
+ int x = (canvas.getBounds().width - width) / 2;
+ int y = (canvas.getBounds().height - height) / 2;
+ e.gc.drawImage(image, x, y);
+ if (showExtras.getSelection()) {
+ if ((viewNode.paddingLeft | viewNode.paddingRight | viewNode.paddingTop | viewNode.paddingBottom) != 0) {
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLUE));
+ e.gc.drawRectangle(x + viewNode.paddingLeft, y + viewNode.paddingTop, width
+ - viewNode.paddingLeft - viewNode.paddingRight - 1, height
+ - viewNode.paddingTop - viewNode.paddingBottom - 1);
+ }
+ if (viewNode.hasMargins) {
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GREEN));
+ e.gc.drawRectangle(x - viewNode.marginLeft, y - viewNode.marginTop, width
+ + viewNode.marginLeft + viewNode.marginRight - 1, height
+ + viewNode.marginTop + viewNode.marginBottom - 1);
+ }
+ if (viewNode.baseline != -1) {
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED));
+ e.gc.drawLine(x, y + viewNode.baseline, x + width - 1, viewNode.baseline);
+ }
+ }
+ }
+ }
+ };
+
+ private static ShellAdapter shellListener = new ShellAdapter() {
+ @Override
+ public void shellClosed(ShellEvent e) {
+ e.doit = false;
+ shell.setVisible(false);
+ if (viewNode != null) {
+ viewNode.dereferenceImage();
+ }
+ }
+
+ };
+
+ private static SelectionListener whiteSelectionListener = new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // pass
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ onWhite.setSelection(true);
+ onBlack.setSelection(false);
+ canvas.redraw();
+ }
+ };
+
+ private static SelectionListener blackSelectionListener = new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // pass
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ onBlack.setSelection(true);
+ onWhite.setSelection(false);
+ canvas.redraw();
+ }
+ };
+
+ private static SelectionListener extrasSelectionListener = new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent e) {
+ // pass
+ }
+
+ public void widgetSelected(SelectionEvent e) {
+ canvas.redraw();
+ }
+ };
+}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java
index 49eb418..5e7c606 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.ddmlib.IDevice;
import com.android.ddmuilib.ImageLoader;
@@ -163,8 +163,8 @@ public class DeviceSelector extends Composite implements WindowChangeListener, S
ContentProvider contentProvider = new ContentProvider();
treeViewer.setContentProvider(contentProvider);
treeViewer.setLabelProvider(contentProvider);
- treeViewer.setInput(model);
model.addWindowChangeListener(this);
+ treeViewer.setInput(model);
}
public void loadResources() {
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/LayoutViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/LayoutViewer.java
new file mode 100644
index 0000000..917e96e
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/LayoutViewer.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.hierarchyviewerlib.ui;
+
+import com.android.hierarchyviewerlib.ComponentRegistry;
+import com.android.hierarchyviewerlib.models.TreeViewModel;
+import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+import java.util.ArrayList;
+
+public class LayoutViewer extends Canvas implements TreeChangeListener {
+
+ private TreeViewModel model;
+
+ private DrawableViewNode tree;
+
+ private DrawableViewNode selectedNode;
+
+ private Transform transform;
+
+ private Transform inverse;
+
+ private double scale;
+
+ private boolean showExtras = true;
+
+ public LayoutViewer(Composite parent) {
+ super(parent, SWT.NONE);
+ model = ComponentRegistry.getTreeViewModel();
+ model.addTreeChangeListener(this);
+
+ addDisposeListener(disposeListener);
+ addPaintListener(paintListener);
+ addListener(SWT.Resize, resizeListener);
+ addMouseListener(mouseListener);
+
+ transform = new Transform(Display.getDefault());
+ inverse = new Transform(Display.getDefault());
+ }
+
+ public void setShowExtras(boolean show) {
+ showExtras = show;
+ }
+
+ private DisposeListener disposeListener = new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ transform.dispose();
+ inverse.dispose();
+ }
+ };
+
+ private Listener resizeListener = new Listener() {
+ public void handleEvent(Event e) {
+ synchronized (this) {
+ setTransform();
+ }
+ }
+ };
+
+ private MouseListener mouseListener = new MouseListener() {
+
+ public void mouseDoubleClick(MouseEvent e) {
+ if (selectedNode != null) {
+ ComponentRegistry.getDirector().showCapture(getShell(), selectedNode.viewNode);
+ }
+ }
+
+ public void mouseDown(MouseEvent e) {
+ System.out.println("CLICK");
+ boolean selectionChanged = false;
+ DrawableViewNode newSelection = null;
+ synchronized (LayoutViewer.this) {
+ if (tree != null) {
+ float[] pt = {
+ e.x, e.y
+ };
+ inverse.transform(pt);
+ newSelection =
+ updateSelection(tree, pt[0], pt[1], 0, 0, 0, 0, tree.viewNode.width,
+ tree.viewNode.height);
+ if (selectedNode != newSelection) {
+ selectionChanged = true;
+ }
+ }
+ }
+ if (selectionChanged) {
+ model.setSelection(newSelection);
+ }
+ }
+
+ public void mouseUp(MouseEvent e) {
+ // pass
+ }
+ };
+
+ private DrawableViewNode updateSelection(DrawableViewNode node, float x, float y, int left, int top,
+ int clipX, int clipY, int clipWidth, int clipHeight) {
+ if (!node.treeDrawn) {
+ return null;
+ }
+ // Update the clip
+ int x1 = Math.max(left, clipX);
+ int x2 = Math.min(left + node.viewNode.width, clipX + clipWidth);
+ int y1 = Math.max(top, clipY);
+ int y2 = Math.min(top + node.viewNode.height, clipY + clipHeight);
+ clipX = x1;
+ clipY = y1;
+ clipWidth = x2 - x1;
+ clipHeight = y2 - y1;
+ if (x < clipX || x > clipX + clipWidth || y < clipY || y > clipY + clipHeight) {
+ return null;
+ }
+ final int N = node.children.size();
+ for (int i = N - 1; i >= 0; i--) {
+ DrawableViewNode child = node.children.get(i);
+ DrawableViewNode ret = updateSelection(child, x, y, left + child.viewNode.left - node.viewNode.scrollX,
+ top + child.viewNode.top - node.viewNode.scrollY, clipX, clipY, clipWidth,
+ clipHeight);
+ if(ret != null) {
+ return ret;
+ }
+ }
+ return node;
+ }
+
+ private PaintListener paintListener = new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ synchronized (LayoutViewer.this) {
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+ e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
+ if (tree != null) {
+ e.gc.setLineWidth((int) Math.ceil(0.2 / scale));
+ e.gc.setTransform(transform);
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ Rectangle parentClipping = e.gc.getClipping();
+ e.gc.setClipping(0, 0, tree.viewNode.width, tree.viewNode.height);
+ paintRecursive(e.gc, tree, 0, 0, true);
+
+ if (selectedNode != null) {
+ e.gc.setClipping(parentClipping);
+
+ // w00t, let's be nice and display the whole path in
+ // light red and the selected node in dark red.
+ ArrayList<Point> rightLeftDistances = new ArrayList<Point>();
+ int left = 0;
+ int top = 0;
+ DrawableViewNode currentNode = selectedNode;
+ while (currentNode != tree) {
+ left += currentNode.viewNode.left;
+ top += currentNode.viewNode.top;
+ currentNode = currentNode.parent;
+ left -= currentNode.viewNode.scrollX;
+ top -= currentNode.viewNode.scrollY;
+ rightLeftDistances.add(new Point(left, top));
+ }
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
+ currentNode = selectedNode.parent;
+ final int N = rightLeftDistances.size();
+ for (int i = 0; i < N; i++) {
+ e.gc.drawRectangle((int) (left - rightLeftDistances.get(i).x),
+ (int) (top - rightLeftDistances.get(i).y),
+ currentNode.viewNode.width - (int) Math.ceil(0.2 / scale),
+ currentNode.viewNode.height - (int) Math.ceil(0.2 / scale));
+ currentNode = currentNode.parent;
+ }
+
+ if (showExtras && selectedNode.viewNode.image != null) {
+ e.gc.drawImage(selectedNode.viewNode.image, left, top);
+ e.gc
+ .setForeground(Display.getDefault().getSystemColor(
+ SWT.COLOR_WHITE));
+ paintRecursive(e.gc, selectedNode, left, top, true);
+
+ }
+
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED));
+ e.gc.setLineWidth((int) Math.ceil(2 / scale));
+ e.gc.drawRectangle(left, top, selectedNode.viewNode.width
+ - (int) Math.ceil(2 / scale) + 1, selectedNode.viewNode.height
+ - (int) Math.ceil(2 / scale) + 1);
+ }
+ }
+ }
+ }
+ };
+
+ private void paintRecursive(GC gc, DrawableViewNode node, int left, int top, boolean root) {
+ if (!node.treeDrawn) {
+ return;
+ }
+ // Don't shift the root
+ if (!root) {
+ left += node.viewNode.left;
+ top += node.viewNode.top;
+ }
+ Rectangle parentClipping = gc.getClipping();
+ int x1 = Math.max(parentClipping.x, left);
+ int x2 = Math.min(parentClipping.x + parentClipping.width, left + node.viewNode.width);
+ int y1 = Math.max(parentClipping.y, top);
+ int y2 = Math.min(parentClipping.y + parentClipping.height, top + node.viewNode.height);
+ gc.setClipping(x1, y1, x2 - x1, y2 - y1);
+ final int N = node.children.size();
+ for (int i = 0; i < N; i++) {
+ paintRecursive(gc, node.children.get(i), left - node.viewNode.scrollX, top
+ - node.viewNode.scrollY, false);
+ }
+ gc.setClipping(parentClipping);
+ if (!node.viewNode.willNotDraw) {
+ gc.drawRectangle(left, top, node.viewNode.width - (int) Math.ceil(0.2 / scale),
+ node.viewNode.height - (int) Math.ceil(0.2 / scale));
+ }
+
+ }
+
+ private void doRedraw() {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ redraw();
+ }
+ });
+ }
+
+ private void setTransform() {
+ if (tree != null) {
+ Rectangle bounds = getBounds();
+ int leftRightPadding = bounds.width <= 30 ? 0 : 5;
+ int topBottomPadding = bounds.height <= 30 ? 0 : 5;
+ scale =
+ Math.min(1.0 * (bounds.width - leftRightPadding * 2) / tree.viewNode.width, 1.0
+ * (bounds.height - topBottomPadding * 2) / tree.viewNode.height);
+ int scaledWidth = (int) Math.ceil(tree.viewNode.width * scale);
+ int scaledHeight = (int) Math.ceil(tree.viewNode.height * scale);
+
+ transform.identity();
+ inverse.identity();
+ transform.translate((bounds.width - scaledWidth) / 2.0f,
+ (bounds.height - scaledHeight) / 2.0f);
+ inverse.translate((bounds.width - scaledWidth) / 2.0f,
+ (bounds.height - scaledHeight) / 2.0f);
+ transform.scale((float) scale, (float) scale);
+ inverse.scale((float) scale, (float) scale);
+ if (bounds.width != 0 && bounds.height != 0) {
+ inverse.invert();
+ }
+ }
+ }
+
+ public void selectionChanged() {
+ synchronized (this) {
+ if (selectedNode != null) {
+ selectedNode.viewNode.dereferenceImage();
+ }
+ selectedNode = model.getSelection();
+ if (selectedNode != null) {
+ selectedNode.viewNode.referenceImage();
+ }
+ }
+ doRedraw();
+ }
+
+ public void treeChanged() {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ if (selectedNode != null) {
+ selectedNode.viewNode.dereferenceImage();
+ }
+ tree = model.getTree();
+ selectedNode = model.getSelection();
+ if (selectedNode != null) {
+ selectedNode.viewNode.referenceImage();
+ }
+ setTransform();
+ }
+ }
+ });
+ doRedraw();
+ }
+
+ public void viewportChanged() {
+ // pass
+ }
+
+ public void zoomChanged() {
+ // pass
+ }
+}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java
index 8165c0b..1a2876b 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.ddmlib.RawImage;
import com.android.hierarchyviewerlib.ComponentRegistry;
import com.android.hierarchyviewerlib.device.ViewNode;
import com.android.hierarchyviewerlib.models.PixelPerfectModel;
import com.android.hierarchyviewerlib.models.PixelPerfectModel.ImageChangeListener;
-import com.android.hierarchyviewerlib.models.PixelPerfectModel.Point;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
@@ -36,6 +35,7 @@ import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
@@ -64,6 +64,10 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
private ViewNode selectedNode;
+ private Image overlayImage;
+
+ private double overlayTransparency;
+
public PixelPerfect(Composite parent) {
super(parent, SWT.H_SCROLL | SWT.V_SCROLL);
canvas = new Canvas(this, SWT.NONE);
@@ -127,7 +131,7 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
};
private void handleMouseEvent(MouseEvent e) {
- synchronized (this) {
+ synchronized (PixelPerfect.this) {
if (image == null) {
return;
}
@@ -145,7 +149,7 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
private PaintListener paintListener = new PaintListener() {
public void paintControl(PaintEvent e) {
- synchronized (this) {
+ synchronized (PixelPerfect.this) {
e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
e.gc.fillRectangle(0, 0, canvas.getSize().x, canvas.getSize().y);
if (image != null) {
@@ -153,6 +157,15 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
int leftOffset = canvas.getSize().x / 2 - width / 2;
int topOffset = canvas.getSize().y / 2 - height / 2;
e.gc.drawImage(image, leftOffset, topOffset);
+ if (overlayImage != null) {
+ e.gc.setAlpha((int) (overlayTransparency * 255));
+ int overlayTopOffset =
+ canvas.getSize().y / 2 + height / 2
+ - overlayImage.getBounds().height;
+ e.gc.drawImage(overlayImage, leftOffset, overlayTopOffset);
+ e.gc.setAlpha(255);
+ }
+
if (selectedNode != null) {
// There are a few quirks here. First of all, margins
// are sometimes negative or positive numbers... Yet,
@@ -175,8 +188,8 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
int nodePadBottom = selectedNode.paddingBottom;
ViewNode cur = selectedNode;
while (cur.parent != null) {
- leftShift += cur.parent.left;
- topShift += cur.parent.top;
+ leftShift += cur.parent.left - cur.parent.scrollX;
+ topShift += cur.parent.top - cur.parent.scrollY;
cur = cur.parent;
}
@@ -235,47 +248,38 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
}
private void loadImage() {
- final RawImage rawImage = model.getImage();
- if (rawImage != null) {
- ImageData imageData =
- new ImageData(rawImage.width, rawImage.height, rawImage.bpp,
- new PaletteData(rawImage.getRedMask(), rawImage.getGreenMask(),
- rawImage.getBlueMask()), 1, rawImage.data);
- if (image != null) {
- image.dispose();
- }
- image = new Image(Display.getDefault(), imageData);
- width = rawImage.width;
- height = rawImage.height;
-
+ image = model.getImage();
+ if (image != null) {
+ width = image.getBounds().width;
+ height = image.getBounds().height;
} else {
- if (image != null) {
- image.dispose();
- image = null;
- }
width = 0;
height = 0;
}
- Display.getDefault().asyncExec(new Runnable() {
- public void run() {
- setMinSize(width, height);
- }
- });
+ setMinSize(width, height);
}
public void imageLoaded() {
- synchronized (this) {
- loadImage();
- crosshairLocation = model.getCrosshairLocation();
- selectedNode = model.getSelected();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ loadImage();
+ crosshairLocation = model.getCrosshairLocation();
+ selectedNode = model.getSelected();
+ }
+ }
+ });
doRedraw();
}
public void imageChanged() {
- synchronized (this) {
- loadImage();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ loadImage();
+ }
+ }
+ });
doRedraw();
}
@@ -294,14 +298,33 @@ public class PixelPerfect extends ScrolledComposite implements ImageChangeListen
}
public void focusChanged() {
- synchronized (this) {
- loadImage();
- selectedNode = model.getSelected();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ loadImage();
+ selectedNode = model.getSelected();
+ }
+ }
+ });
doRedraw();
}
public void zoomChanged() {
// pass
}
+
+ public void overlayChanged() {
+ synchronized (this) {
+ overlayImage = model.getOverlayImage();
+ overlayTransparency = model.getOverlayTransparency();
+ }
+ doRedraw();
+ }
+
+ public void overlayTransparencyChanged() {
+ synchronized (this) {
+ overlayTransparency = model.getOverlayTransparency();
+ }
+ doRedraw();
+ }
}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java
index f0402f3..84ce08f 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.ddmlib.RawImage;
import com.android.hierarchyviewerlib.ComponentRegistry;
import com.android.hierarchyviewerlib.models.PixelPerfectModel;
import com.android.hierarchyviewerlib.models.PixelPerfectModel.ImageChangeListener;
-import com.android.hierarchyviewerlib.models.PixelPerfectModel.Point;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
@@ -35,6 +34,7 @@ import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
@@ -65,6 +65,12 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
private int canvasHeight;
+ private Image overlayImage;
+
+ private double overlayTransparency;
+
+ private boolean showOverlay = false;
+
public PixelPerfectLoupe(Composite parent) {
super(parent, SWT.NONE);
model = ComponentRegistry.getPixelPerfectModel();
@@ -80,6 +86,12 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
transform = new Transform(Display.getDefault());
}
+ public void setShowOverlay(boolean value) {
+ synchronized (this) {
+ showOverlay = value;
+ }
+ }
+
private DisposeListener disposeListener = new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
model.removeImageChangeListener(PixelPerfectLoupe.this);
@@ -113,7 +125,7 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
private MouseWheelListener mouseWheelListener = new MouseWheelListener() {
public void mouseScrolled(MouseEvent e) {
int newZoom = -1;
- synchronized (this) {
+ synchronized (PixelPerfectLoupe.this) {
if (image != null && crosshairLocation != null) {
if (e.count > 0) {
newZoom = zoom + 1;
@@ -131,7 +143,7 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
private void handleMouseEvent(MouseEvent e) {
int newX = -1;
int newY = -1;
- synchronized (this) {
+ synchronized (PixelPerfectLoupe.this) {
if (image == null) {
return;
}
@@ -151,7 +163,7 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
private PaintListener paintListener = new PaintListener() {
public void paintControl(PaintEvent e) {
- synchronized (this) {
+ synchronized (PixelPerfectLoupe.this) {
e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
e.gc.fillRectangle(0, 0, getSize().x, getSize().y);
if (image != null && crosshairLocation != null) {
@@ -162,6 +174,12 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
e.gc.setInterpolation(SWT.NONE);
e.gc.setTransform(transform);
e.gc.drawImage(image, 0, 0);
+ if (showOverlay && overlayImage != null) {
+ e.gc.setAlpha((int) (overlayTransparency * 255));
+ e.gc.drawImage(overlayImage, 0, height - overlayImage.getBounds().height);
+ e.gc.setAlpha(255);
+ }
+
transform.identity();
e.gc.setTransform(transform);
@@ -220,41 +238,37 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
}
private void loadImage() {
- final RawImage rawImage = model.getImage();
- if (rawImage != null) {
- ImageData imageData =
- new ImageData(rawImage.width, rawImage.height, rawImage.bpp,
- new PaletteData(rawImage.getRedMask(), rawImage.getGreenMask(),
- rawImage.getBlueMask()), 1, rawImage.data);
- if (image != null) {
- image.dispose();
- }
- image = new Image(Display.getDefault(), imageData);
- width = rawImage.width;
- height = rawImage.height;
+ image = model.getImage();
+ if (image != null) {
+ width = image.getBounds().width;
+ height = image.getBounds().height;
} else {
- if (image != null) {
- image.dispose();
- image = null;
- }
width = 0;
height = 0;
}
}
public void imageLoaded() {
- synchronized (this) {
- loadImage();
- crosshairLocation = model.getCrosshairLocation();
- zoom = model.getZoom();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ loadImage();
+ crosshairLocation = model.getCrosshairLocation();
+ zoom = model.getZoom();
+ }
+ }
+ });
doRedraw();
}
public void imageChanged() {
- synchronized (this) {
- loadImage();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ loadImage();
+ }
+ }
+ });
doRedraw();
}
@@ -274,14 +288,34 @@ public class PixelPerfectLoupe extends Canvas implements ImageChangeListener {
}
public void zoomChanged() {
- synchronized (this) {
- if (grid != null) {
- // To notify that the zoom level has changed, we get rid of the
- // grid.
- grid.dispose();
- grid = null;
- zoom = model.getZoom();
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ if (grid != null) {
+ // To notify that the zoom level has changed, we get rid
+ // of the
+ // grid.
+ grid.dispose();
+ grid = null;
+ zoom = model.getZoom();
+ }
+ }
}
+ });
+ doRedraw();
+ }
+
+ public void overlayChanged() {
+ synchronized (this) {
+ overlayImage = model.getOverlayImage();
+ overlayTransparency = model.getOverlayTransparency();
+ }
+ doRedraw();
+ }
+
+ public void overlayTransparencyChanged() {
+ synchronized (this) {
+ overlayTransparency = model.getOverlayTransparency();
}
doRedraw();
}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java
index 2c30857..7df4d9d 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.ddmuilib.ImageLoader;
import com.android.hierarchyviewerlib.ComponentRegistry;
@@ -38,7 +38,6 @@ import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeColumn;
import java.util.List;
@@ -213,4 +212,11 @@ public class PixelPerfectTree extends Composite implements ImageChangeListener,
// pass
}
+ public void overlayChanged() {
+ // pass
+ }
+
+ public void overlayTransparencyChanged() {
+ // pass
+ }
}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java
index 400318c..f83ba3d 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.hierarchyviewerlib.ComponentRegistry;
import com.android.hierarchyviewerlib.models.TreeViewModel;
import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode;
-import com.android.hierarchyvieweruilib.util.TreeColumnResizer;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.TreeColumnResizer;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -97,10 +97,19 @@ public class ProfileViewer extends Composite implements TreeChangeListener {
} else if (column == 1) {
DecimalFormat formatter = new DecimalFormat("0.000");
if(((String)element).equals("measure")) {
+ if (selectedNode.viewNode.measureTime == -1) {
+ return "unknown";
+ }
return formatter.format(selectedNode.viewNode.measureTime);
} else if (((String) element).equals("layout")) {
+ if (selectedNode.viewNode.layoutTime == -1) {
+ return "unknown";
+ }
return formatter.format(selectedNode.viewNode.layoutTime);
} else {
+ if (selectedNode.viewNode.drawTime == -1) {
+ return "unknown";
+ }
return formatter.format(selectedNode.viewNode.drawTime);
}
}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java
index da4997b..d262d16 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.hierarchyviewerlib.ComponentRegistry;
import com.android.hierarchyviewerlib.device.ViewNode;
import com.android.hierarchyviewerlib.device.ViewNode.Property;
import com.android.hierarchyviewerlib.models.TreeViewModel;
import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode;
-import com.android.hierarchyvieweruilib.util.TreeColumnResizer;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.TreeColumnResizer;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ITableLabelProvider;
@@ -34,8 +34,6 @@ import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeView.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeView.java
new file mode 100644
index 0000000..abf5690
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeView.java
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.hierarchyviewerlib.ui;
+
+import com.android.ddmuilib.ImageLoader;
+import com.android.hierarchyviewerlib.ComponentRegistry;
+import com.android.hierarchyviewerlib.device.ViewNode.ProfileRating;
+import com.android.hierarchyviewerlib.models.TreeViewModel;
+import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Rectangle;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseWheelListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Path;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+public class TreeView extends Canvas implements TreeChangeListener {
+
+ private TreeViewModel model;
+
+ private DrawableViewNode tree;
+
+ private DrawableViewNode selectedNode;
+
+ private Rectangle viewport;
+
+ private Transform transform;
+
+ private Transform inverse;
+
+ private double zoom;
+
+ private Point lastPoint;
+
+ private DrawableViewNode draggedNode;
+
+ public static final int LINE_PADDING = 10;
+
+ public static final float BEZIER_FRACTION = 0.35f;
+
+ private Image redImage;
+
+ private Image yellowImage;
+
+ private Image greenImage;
+
+ public TreeView(Composite parent) {
+ super(parent, SWT.NONE);
+
+ model = ComponentRegistry.getTreeViewModel();
+ model.addTreeChangeListener(this);
+
+ addPaintListener(paintListener);
+ addMouseListener(mouseListener);
+ addMouseMoveListener(mouseMoveListener);
+ addMouseWheelListener(mouseWheelListener);
+ addListener(SWT.Resize, resizeListener);
+ addDisposeListener(disposeListener);
+ addKeyListener(keyListener);
+
+ transform = new Transform(Display.getDefault());
+ inverse = new Transform(Display.getDefault());
+
+ ImageLoader loader = ImageLoader.getLoader(this.getClass());
+ redImage = loader.loadImage("red.png", Display.getDefault());
+ yellowImage = loader.loadImage("yellow.png", Display.getDefault());
+ greenImage = loader.loadImage("green.png", Display.getDefault());
+ }
+
+ private DisposeListener disposeListener = new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ model.removeTreeChangeListener(TreeView.this);
+ transform.dispose();
+ inverse.dispose();
+ }
+ };
+
+ private Listener resizeListener = new Listener() {
+ public void handleEvent(Event e) {
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null) {
+
+ // I don't know what the best behaviour is... This seems
+ // like a good idea.
+ Point viewCenter =
+ new Point(viewport.x + viewport.width / 2, viewport.y + viewport.height
+ / 2);
+ viewport.width = getBounds().width / zoom;
+ viewport.height = getBounds().height / zoom;
+ viewport.x = viewCenter.x - viewport.width / 2;
+ viewport.y = viewCenter.y - viewport.height / 2;
+ }
+ }
+ if (viewport != null) {
+ model.setViewport(viewport);
+ }
+ }
+ };
+
+ private KeyListener keyListener = new KeyListener() {
+
+ public void keyPressed(KeyEvent e) {
+ boolean selectionChanged = false;
+ DrawableViewNode clickedNode = null;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null && selectedNode != null) {
+ switch (e.keyCode) {
+ case SWT.ARROW_LEFT:
+ if(selectedNode.parent != null) {
+ selectedNode = selectedNode.parent;
+ selectionChanged = true;
+ }
+ break;
+ case SWT.ARROW_UP:
+ int levelsOut = 0;
+ DrawableViewNode currentNode = selectedNode;
+ while (currentNode.parent != null && currentNode.viewNode.index == 0) {
+ levelsOut++;
+ currentNode = currentNode.parent;
+ }
+ if (currentNode.parent != null) {
+ selectionChanged = true;
+ currentNode =
+ currentNode.parent.children
+ .get(currentNode.viewNode.index - 1);
+ while (currentNode.children.size() != 0) {
+ currentNode =
+ currentNode.children
+ .get(currentNode.children.size() - 1);
+ levelsOut--;
+ }
+ }
+ if (selectionChanged) {
+ selectedNode = currentNode;
+ }
+ break;
+ case SWT.ARROW_DOWN:
+ levelsOut = 0;
+ currentNode = selectedNode;
+ while (currentNode.parent != null
+ && currentNode.viewNode.index + 1 == currentNode.parent.children
+ .size()) {
+ levelsOut++;
+ currentNode = currentNode.parent;
+ }
+ if (currentNode.parent != null) {
+ selectionChanged = true;
+ currentNode =
+ currentNode.parent.children
+ .get(currentNode.viewNode.index + 1);
+ while (currentNode.children.size() != 0) {
+ currentNode = currentNode.children.get(0);
+ levelsOut--;
+ }
+ }
+ if (selectionChanged) {
+ selectedNode = currentNode;
+ }
+ break;
+ case SWT.ARROW_RIGHT:
+ DrawableViewNode rightNode = null;
+ double mostOverlap = 0;
+ final int N = selectedNode.children.size();
+ for(int i = 0; i<N; i++) {
+ DrawableViewNode child = selectedNode.children.get(i);
+ DrawableViewNode topMostChild = child;
+ while (topMostChild.children.size() != 0) {
+ topMostChild = topMostChild.children.get(0);
+ }
+ double overlap =
+ Math.min(DrawableViewNode.NODE_HEIGHT, Math.min(
+ selectedNode.top + DrawableViewNode.NODE_HEIGHT
+ - topMostChild.top, topMostChild.top
+ + child.treeHeight - selectedNode.top));
+ if (overlap > mostOverlap) {
+ mostOverlap = overlap;
+ rightNode = child;
+ }
+ }
+ if (rightNode != null) {
+ selectedNode = rightNode;
+ selectionChanged = true;
+ }
+ break;
+ case SWT.CR:
+ clickedNode = selectedNode;
+ break;
+ }
+ }
+ }
+ if (selectionChanged) {
+ model.setSelection(selectedNode);
+ }
+ if (clickedNode != null) {
+ ComponentRegistry.getDirector().showCapture(getShell(), clickedNode.viewNode);
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {
+ }
+ };
+
+ private MouseListener mouseListener = new MouseListener() {
+
+ public void mouseDoubleClick(MouseEvent e) {
+ DrawableViewNode clickedNode = null;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null) {
+ Point pt = transformPoint(e.x, e.y);
+ clickedNode = tree.getSelected(pt.x, pt.y);
+ }
+ }
+ if (clickedNode != null) {
+ ComponentRegistry.getDirector().showCapture(getShell(), clickedNode.viewNode);
+ }
+ }
+
+ public void mouseDown(MouseEvent e) {
+ boolean selectionChanged = false;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null) {
+ Point pt = transformPoint(e.x, e.y);
+ draggedNode = tree.getSelected(pt.x, pt.y);
+ if (draggedNode != null && draggedNode != selectedNode) {
+ selectedNode = draggedNode;
+ selectionChanged = true;
+ }
+ if (draggedNode == tree) {
+ draggedNode = null;
+ }
+ if (draggedNode != null) {
+ lastPoint = pt;
+ } else {
+ lastPoint = new Point(e.x, e.y);
+ }
+ }
+ }
+ if (selectionChanged) {
+ model.setSelection(selectedNode);
+ }
+ }
+
+ public void mouseUp(MouseEvent e) {
+ boolean redraw = false;
+ boolean viewportChanged = false;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null && lastPoint != null) {
+ if (draggedNode == null) {
+ handleMouseDrag(new Point(e.x, e.y));
+ viewportChanged = true;
+ } else {
+ handleMouseDrag(transformPoint(e.x, e.y));
+ }
+ lastPoint = null;
+ draggedNode = null;
+ redraw = true;
+ }
+ }
+ if (viewportChanged) {
+ model.setViewport(viewport);
+ } else if (redraw) {
+ model.removeTreeChangeListener(TreeView.this);
+ model.notifyViewportChanged();
+ model.addTreeChangeListener(TreeView.this);
+ doRedraw();
+ }
+ }
+
+ };
+
+ private MouseMoveListener mouseMoveListener = new MouseMoveListener() {
+ public void mouseMove(MouseEvent e) {
+ boolean redraw = false;
+ boolean viewportChanged = false;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null && lastPoint != null) {
+ if (draggedNode == null) {
+ handleMouseDrag(new Point(e.x, e.y));
+ viewportChanged = true;
+ } else {
+ handleMouseDrag(transformPoint(e.x, e.y));
+ }
+ redraw = true;
+ }
+ }
+ if (viewportChanged) {
+ model.setViewport(viewport);
+ } else if (redraw) {
+ model.removeTreeChangeListener(TreeView.this);
+ model.notifyViewportChanged();
+ model.addTreeChangeListener(TreeView.this);
+ doRedraw();
+ }
+ }
+ };
+
+ private void handleMouseDrag(Point pt) {
+ if (draggedNode != null) {
+ draggedNode.move(lastPoint.y - pt.y);
+ lastPoint = pt;
+ return;
+ }
+ double xDif = (lastPoint.x - pt.x) / zoom;
+ double yDif = (lastPoint.y - pt.y) / zoom;
+
+ if (viewport.width > tree.bounds.width) {
+ if (xDif < 0 && viewport.x + viewport.width > tree.bounds.x + tree.bounds.width) {
+ viewport.x =
+ Math.max(viewport.x + xDif, tree.bounds.x + tree.bounds.width
+ - viewport.width);
+ } else if (xDif > 0 && viewport.x < tree.bounds.x) {
+ viewport.x = Math.min(viewport.x + xDif, tree.bounds.x);
+ }
+ } else {
+ if (xDif < 0 && viewport.x > tree.bounds.x) {
+ viewport.x = Math.max(viewport.x + xDif, tree.bounds.x);
+ } else if (xDif > 0 && viewport.x + viewport.width < tree.bounds.x + tree.bounds.width) {
+ viewport.x =
+ Math.min(viewport.x + xDif, tree.bounds.x + tree.bounds.width
+ - viewport.width);
+ }
+ }
+ if (viewport.height > tree.bounds.height) {
+ if (yDif < 0 && viewport.y + viewport.height > tree.bounds.y + tree.bounds.height) {
+ viewport.y =
+ Math.max(viewport.y + yDif, tree.bounds.y + tree.bounds.height
+ - viewport.height);
+ } else if (yDif > 0 && viewport.y < tree.bounds.y) {
+ viewport.y = Math.min(viewport.y + yDif, tree.bounds.y);
+ }
+ } else {
+ if (yDif < 0 && viewport.y > tree.bounds.y) {
+ viewport.y = Math.max(viewport.y + yDif, tree.bounds.y);
+ } else if (yDif > 0
+ && viewport.y + viewport.height < tree.bounds.y + tree.bounds.height) {
+ viewport.y =
+ Math.min(viewport.y + yDif, tree.bounds.y + tree.bounds.height
+ - viewport.height);
+ }
+ }
+ lastPoint = pt;
+ }
+
+ private Point transformPoint(double x, double y) {
+ float[] pt = {
+ (float) x, (float) y
+ };
+ inverse.transform(pt);
+ return new Point(pt[0], pt[1]);
+ }
+
+ private MouseWheelListener mouseWheelListener = new MouseWheelListener() {
+ public void mouseScrolled(MouseEvent e) {
+ Point zoomPoint = null;
+ synchronized (TreeView.this) {
+ if (tree != null && viewport != null) {
+ zoom += Math.ceil(e.count / 3.0) * 0.1;
+ zoomPoint = transformPoint(e.x, e.y);
+ }
+ }
+ if (zoomPoint != null) {
+ model.zoomOnPoint(zoom, zoomPoint);
+ }
+ }
+ };
+
+ private PaintListener paintListener = new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ synchronized (TreeView.this) {
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
+ if (tree != null && viewport != null) {
+ e.gc.setTransform(transform);
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
+ Path connectionPath = new Path(Display.getDefault());
+ paintRecursive(e.gc, tree, connectionPath);
+ e.gc.drawPath(connectionPath);
+ connectionPath.dispose();
+ }
+ }
+ }
+ };
+
+ private void paintRecursive(GC gc, DrawableViewNode node, Path connectionPath) {
+ if (selectedNode == node) {
+ gc.fillRectangle(node.left, (int) Math.round(node.top), DrawableViewNode.NODE_WIDTH,
+ DrawableViewNode.NODE_HEIGHT);
+ } else {
+ gc.drawRectangle(node.left, (int) Math.round(node.top), DrawableViewNode.NODE_WIDTH,
+ DrawableViewNode.NODE_HEIGHT);
+ }
+
+ int fontHeight = gc.getFontMetrics().getHeight();
+
+ // Draw the text...
+ int contentWidth =
+ DrawableViewNode.NODE_WIDTH - 2 * DrawableViewNode.CONTENT_LEFT_RIGHT_PADDING;
+ String name = node.viewNode.name;
+ int dotIndex = name.lastIndexOf('.');
+ if (dotIndex != -1) {
+ name = name.substring(dotIndex + 1);
+ }
+ double x = node.left + DrawableViewNode.CONTENT_LEFT_RIGHT_PADDING;
+ double y = node.top + DrawableViewNode.CONTENT_TOP_BOTTOM_PADDING;
+ drawTextInArea(gc, name, x, y, contentWidth, fontHeight);
+
+ y += fontHeight + DrawableViewNode.CONTENT_INTER_PADDING;
+
+ gc.drawText("@" + node.viewNode.hashCode, (int) x, (int) y, SWT.DRAW_TRANSPARENT);
+
+ y += fontHeight + DrawableViewNode.CONTENT_INTER_PADDING;
+ if (!node.viewNode.id.equals("NO_ID")) {
+ drawTextInArea(gc, node.viewNode.id, x, y, contentWidth, fontHeight);
+ }
+
+ if (node.viewNode.measureRating != ProfileRating.NONE) {
+ y =
+ node.top + DrawableViewNode.NODE_HEIGHT
+ - DrawableViewNode.CONTENT_TOP_BOTTOM_PADDING
+ - redImage.getBounds().height;
+ x +=
+ (contentWidth - (redImage.getBounds().width * 3 + 2 * DrawableViewNode.CONTENT_INTER_PADDING)) / 2;
+ switch (node.viewNode.measureRating) {
+ case GREEN:
+ gc.drawImage(greenImage, (int) x, (int) y);
+ break;
+ case YELLOW:
+ gc.drawImage(yellowImage, (int) x, (int) y);
+ break;
+ case RED:
+ gc.drawImage(redImage, (int) x, (int) y);
+ break;
+ }
+
+ x += redImage.getBounds().width + DrawableViewNode.CONTENT_INTER_PADDING;
+ switch (node.viewNode.layoutRating) {
+ case GREEN:
+ gc.drawImage(greenImage, (int) x, (int) y);
+ break;
+ case YELLOW:
+ gc.drawImage(yellowImage, (int) x, (int) y);
+ break;
+ case RED:
+ gc.drawImage(redImage, (int) x, (int) y);
+ break;
+ }
+
+ x += redImage.getBounds().width + DrawableViewNode.CONTENT_INTER_PADDING;
+ switch (node.viewNode.drawRating) {
+ case GREEN:
+ gc.drawImage(greenImage, (int) x, (int) y);
+ break;
+ case YELLOW:
+ gc.drawImage(yellowImage, (int) x, (int) y);
+ break;
+ case RED:
+ gc.drawImage(redImage, (int) x, (int) y);
+ break;
+ }
+ }
+
+
+ org.eclipse.swt.graphics.Point indexExtent =
+ gc.stringExtent(Integer.toString(node.viewNode.index));
+ x = node.left+DrawableViewNode.NODE_WIDTH-DrawableViewNode.INDEX_PADDING-indexExtent.x;
+ y = node.top+DrawableViewNode.NODE_HEIGHT-DrawableViewNode.INDEX_PADDING-indexExtent.y;
+ gc.drawText(Integer.toString(node.viewNode.index), (int) x, (int) y, SWT.DRAW_TRANSPARENT);
+
+
+
+ int N = node.children.size();
+ if (N == 0) {
+ return;
+ }
+ float childSpacing = (1.0f * (DrawableViewNode.NODE_HEIGHT - 2 * LINE_PADDING)) / N;
+ for (int i = 0; i < N; i++) {
+ DrawableViewNode child = node.children.get(i);
+ paintRecursive(gc, child, connectionPath);
+ float x1 = node.left + DrawableViewNode.NODE_WIDTH;
+ float y1 = (float) node.top + LINE_PADDING + childSpacing * i + childSpacing / 2;
+ float x2 = child.left;
+ float y2 = (float) child.top + DrawableViewNode.NODE_HEIGHT / 2.0f;
+ float cx1 = x1 + BEZIER_FRACTION * DrawableViewNode.PARENT_CHILD_SPACING;
+ float cy1 = y1;
+ float cx2 = x2 - BEZIER_FRACTION * DrawableViewNode.PARENT_CHILD_SPACING;
+ float cy2 = y2;
+ connectionPath.moveTo(x1, y1);
+ connectionPath.cubicTo(cx1, cy1, cx2, cy2, x2, y2);
+ }
+ }
+
+ private void drawTextInArea(GC gc, String text, double x, double y, double width, double height) {
+ org.eclipse.swt.graphics.Point extent = gc.stringExtent(text);
+
+ if (extent.x > width) {
+ // Oh no... we need to scale it.
+ double scale = width / extent.x;
+ float[] transformElements = new float[6];
+ transform.getElements(transformElements);
+ transform.scale((float) scale, (float) scale);
+ gc.setTransform(transform);
+
+ x/=scale;
+ y/=scale;
+ y += (extent.y / scale - extent.y) / 2;
+
+ gc.drawText(text, (int) x, (int) y, SWT.DRAW_TRANSPARENT);
+
+ transform.setElements(transformElements[0], transformElements[1], transformElements[2],
+ transformElements[3], transformElements[4], transformElements[5]);
+ gc.setTransform(transform);
+ } else {
+ gc.drawText(text, (int) x, (int) y, SWT.DRAW_TRANSPARENT);
+ }
+
+ }
+
+ private void doRedraw() {
+ Display.getDefault().asyncExec(new Runnable() {
+ public void run() {
+ redraw();
+ }
+ });
+ }
+
+ public void treeChanged() {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ tree = model.getTree();
+ selectedNode = model.getSelection();
+ if (tree == null) {
+ viewport = null;
+ } else {
+ viewport =
+ new Rectangle((tree.bounds.width - getBounds().width) / 2,
+ (tree.bounds.height - getBounds().height) / 2,
+ getBounds().width, getBounds().height);
+ }
+ }
+ }
+ });
+ if (viewport != null) {
+ model.setViewport(viewport);
+ }
+ }
+
+ private void setTransform() {
+ if (viewport != null && tree != null) {
+ // Set the transform.
+ transform.identity();
+ inverse.identity();
+
+ transform.scale((float) zoom, (float) zoom);
+ inverse.scale((float) zoom, (float) zoom);
+ transform.translate((float) -viewport.x, (float) -viewport.y);
+ inverse.translate((float) -viewport.x, (float) -viewport.y);
+ inverse.invert();
+ }
+ }
+
+ public void viewportChanged() {
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ viewport = model.getViewport();
+ zoom = model.getZoom();
+ setTransform();
+ }
+ }
+ });
+ doRedraw();
+ }
+
+ public void zoomChanged() {
+ viewportChanged();
+ }
+
+ public void selectionChanged() {
+ synchronized (this) {
+ selectedNode = model.getSelection();
+ }
+ doRedraw();
+ }
+}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java
index feed1af..83a2b0d 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib;
+package com.android.hierarchyviewerlib.ui;
import com.android.hierarchyviewerlib.ComponentRegistry;
import com.android.hierarchyviewerlib.models.TreeViewModel;
import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Point;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Rectangle;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Point;
+import com.android.hierarchyviewerlib.ui.util.DrawableViewNode.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
@@ -32,6 +32,7 @@ import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
@@ -89,7 +90,7 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
public void mouseDown(MouseEvent e) {
boolean redraw = false;
- synchronized (this) {
+ synchronized (TreeViewOverview.this) {
if (tree != null && viewport != null) {
dragging = true;
redraw = true;
@@ -106,7 +107,7 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
public void mouseUp(MouseEvent e) {
boolean redraw = false;
- synchronized (this) {
+ synchronized (TreeViewOverview.this) {
if (tree != null && viewport != null) {
dragging = false;
redraw = true;
@@ -128,7 +129,7 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
private MouseMoveListener mouseMoveListener = new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
boolean moved = false;
- synchronized (this) {
+ synchronized (TreeViewOverview.this) {
if (dragging) {
moved = true;
handleMouseEvent(transformPoint(e.x, e.y));
@@ -170,7 +171,7 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
private Listener resizeListener = new Listener() {
public void handleEvent(Event arg0) {
- synchronized (this) {
+ synchronized (TreeViewOverview.this) {
setTransform();
}
doRedraw();
@@ -179,41 +180,59 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
private PaintListener paintListener = new PaintListener() {
public void paintControl(PaintEvent e) {
- if (tree != null && viewport != null) {
- e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
- e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
- e.gc.setTransform(transform);
- paintRecursive(e.gc, tree);
-
- e.gc.setAlpha(80);
- e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
- e.gc.fillRectangle((int) viewport.x, (int) viewport.y, (int) Math
- .ceil(viewport.width), (int) Math.ceil(viewport.height));
-
- e.gc.setAlpha(255);
- e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
- e.gc.setLineWidth((int) Math.ceil(2 / scale));
- e.gc.drawRectangle((int) viewport.x, (int) viewport.y, (int) Math
- .ceil(viewport.width), (int) Math.ceil(viewport.height));
+ synchronized (TreeViewOverview.this) {
+ if (tree != null && viewport != null) {
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
+ e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
+ e.gc.setTransform(transform);
+ Path connectionPath = new Path(Display.getDefault());
+ paintRecursive(e.gc, tree, connectionPath);
+ e.gc.drawPath(connectionPath);
+ connectionPath.dispose();
+
+ e.gc.setAlpha(80);
+ e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GRAY));
+ e.gc.fillRectangle((int) viewport.x, (int) viewport.y, (int) Math
+ .ceil(viewport.width), (int) Math.ceil(viewport.height));
+
+ e.gc.setAlpha(255);
+ e.gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
+ e.gc.setLineWidth((int) Math.ceil(2 / scale));
+ e.gc.drawRectangle((int) viewport.x, (int) viewport.y, (int) Math
+ .ceil(viewport.width), (int) Math.ceil(viewport.height));
+ }
}
}
};
- private void paintRecursive(GC gc, DrawableViewNode node) {
+ private void paintRecursive(GC gc, DrawableViewNode node, Path connectionPath) {
gc.drawRectangle(node.left, (int) Math.round(node.top), DrawableViewNode.NODE_WIDTH,
DrawableViewNode.NODE_HEIGHT);
int N = node.children.size();
+ if (N == 0) {
+ return;
+ }
+ float childSpacing =
+ (1.0f * (DrawableViewNode.NODE_HEIGHT - 2 * TreeView.LINE_PADDING)) / N;
for (int i = 0; i < N; i++) {
DrawableViewNode child = node.children.get(i);
- paintRecursive(gc, child);
- gc.drawLine(node.left + DrawableViewNode.NODE_WIDTH, (int) Math.round(node.top)
- + DrawableViewNode.NODE_HEIGHT / 2, child.left, (int) Math.round(child.top)
- + DrawableViewNode.NODE_HEIGHT / 2);
+ paintRecursive(gc, child, connectionPath);
+ float x1 = node.left + DrawableViewNode.NODE_WIDTH;
+ float y1 =
+ (float) node.top + TreeView.LINE_PADDING + childSpacing * i + childSpacing / 2;
+ float x2 = child.left;
+ float y2 = (float) child.top + DrawableViewNode.NODE_HEIGHT / 2.0f;
+ float cx1 = x1 + TreeView.BEZIER_FRACTION * DrawableViewNode.PARENT_CHILD_SPACING;
+ float cy1 = y1;
+ float cx2 = x2 - TreeView.BEZIER_FRACTION * DrawableViewNode.PARENT_CHILD_SPACING;
+ float cy2 = y2;
+ connectionPath.moveTo(x1, y1);
+ connectionPath.cubicTo(cx1, cy1, cx2, cy2, x2, y2);
}
}
private void doRedraw() {
- Display.getDefault().syncExec(new Runnable() {
+ Display.getDefault().asyncExec(new Runnable() {
public void run() {
redraw();
}
@@ -221,11 +240,15 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
}
public void treeChanged() {
- synchronized (this) {
- tree = model.getTree();
- setBounds();
- setTransform();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ tree = model.getTree();
+ setBounds();
+ setTransform();
+ }
+ }
+ });
doRedraw();
}
@@ -248,13 +271,13 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
transform.identity();
inverse.identity();
final Point size = new Point();
- Display.getDefault().syncExec(new Runnable() {
- public void run() {
- size.x = getBounds().width;
- size.y = getBounds().height;
- }
- });
- scale = Math.min(size.x / bounds.width, size.y / bounds.height);
+ size.x = getBounds().width;
+ size.y = getBounds().height;
+ if (bounds.width == 0 || bounds.height == 0 || size.x == 0 || size.y == 0) {
+ scale = 1;
+ } else {
+ scale = Math.min(size.x / bounds.width, size.y / bounds.height);
+ }
transform.scale((float) scale, (float) scale);
inverse.scale((float) scale, (float) scale);
transform.translate((float) -bounds.x, (float) -bounds.y);
@@ -271,11 +294,15 @@ public class TreeViewOverview extends Canvas implements TreeChangeListener {
}
public void viewportChanged() {
- synchronized (this) {
- viewport = model.getViewport();
- setBounds();
- setTransform();
- }
+ Display.getDefault().syncExec(new Runnable() {
+ public void run() {
+ synchronized (this) {
+ viewport = model.getViewport();
+ setBounds();
+ setTransform();
+ }
+ }
+ });
doRedraw();
}
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java
index aff3d6d..fccc3ba 100644
--- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hierarchyviewerlib.scene;
+package com.android.hierarchyviewerlib.ui.util;
import com.android.hierarchyviewerlib.device.ViewNode;
@@ -25,15 +25,23 @@ public class DrawableViewNode {
public final ArrayList<DrawableViewNode> children = new ArrayList<DrawableViewNode>();
- public final static int NODE_HEIGHT = 70;
+ public final static int NODE_HEIGHT = 110;
- public final static int NODE_WIDTH = 100;
+ public final static int NODE_WIDTH = 170;
- public final static int LEAF_NODE_SPACING = 5;
+ public final static int CONTENT_LEFT_RIGHT_PADDING = 3;
- public final static int NON_LEAF_NODE_SPACING = 10;
+ public final static int CONTENT_TOP_BOTTOM_PADDING = 7;
- public final static int PARENT_CHILD_SPACING = 40;
+ public final static int CONTENT_INTER_PADDING = 3;
+
+ public final static int INDEX_PADDING = 5;
+
+ public final static int LEAF_NODE_SPACING = 9;
+
+ public final static int NON_LEAF_NODE_SPACING = 15;
+
+ public final static int PARENT_CHILD_SPACING = 50;
public final static int PADDING = 30;
@@ -53,6 +61,8 @@ public class DrawableViewNode {
public int bottomSpacing;
+ public boolean treeDrawn;
+
public static class Rectangle {
public double x, y, width, height;
@@ -91,12 +101,18 @@ public class DrawableViewNode {
this.x = x;
this.y = y;
}
+
+ @Override
+ public String toString() {
+ return "(" + x + ", " + y + ")";
+ }
}
public Rectangle bounds = new Rectangle();
public DrawableViewNode(ViewNode viewNode) {
this.viewNode = viewNode;
+ treeDrawn = !viewNode.willNotDraw;
if (viewNode.children.size() == 0) {
treeHeight = NODE_HEIGHT;
treeWidth = NODE_WIDTH;
@@ -124,6 +140,7 @@ public class DrawableViewNode {
child.topSpacing = NON_LEAF_NODE_SPACING;
}
}
+ treeDrawn |= child.treeDrawn;
}
treeWidth += NODE_WIDTH + PARENT_CHILD_SPACING;
}
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java
index 09851c3..ad18540 100644
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.hierarchyvieweruilib.util;
+package com.android.hierarchyviewerlib.ui.util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
@@ -30,6 +30,11 @@ public class TreeColumnResizer {
private Composite control;
private int column1Width;
private int column2Width;
+
+ private final static int MIN_COLUMN1_WIDTH = 18;
+
+ private final static int MIN_COLUMN2_WIDTH = 3;
+
public TreeColumnResizer(Composite control, TreeColumn column1, TreeColumn column2) {
this.control = control;
this.column1 = column1;
@@ -79,9 +84,21 @@ public class TreeColumnResizer {
int widthDif = column1Width - column1.getWidth();
column1Width -= widthDif;
column2Width += widthDif;
- if (column2Width < 0) {
- column1Width += column2Width;
- column2Width = 0;
+ boolean column1Changed = false;
+
+ // Strange, but these constants make the columns look the same.
+
+ if (column1Width < MIN_COLUMN1_WIDTH) {
+ column2Width -= MIN_COLUMN1_WIDTH - column1Width;
+ column1Width += MIN_COLUMN1_WIDTH - column1Width;
+ column1Changed = true;
+ }
+ if (column2Width < MIN_COLUMN2_WIDTH) {
+ column1Width += column2Width - MIN_COLUMN2_WIDTH;
+ column2Width = MIN_COLUMN2_WIDTH;
+ column1Changed = true;
+ }
+ if (column1Changed) {
column1.removeListener(SWT.Resize, this);
column1.setWidth(column1Width);
column1.addListener(SWT.Resize, this);
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png
new file mode 100644
index 0000000..b52a342
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png
new file mode 100644
index 0000000..338c2d9
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png
new file mode 100644
index 0000000..b6fadac
--- /dev/null
+++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png
Binary files differ
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath b/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath
deleted file mode 100644
index 4bddd6c..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/ANDROID_SWT"/>
- <classpathentry combineaccessrules="false" kind="src" path="/hierarchyviewerlib"/>
- <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/>
- <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/.gitignore b/hierarchyviewer2/libs/hierarchyvieweruilib/.gitignore
deleted file mode 100644
index e660fd9..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-bin/
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/.project b/hierarchyviewer2/libs/hierarchyvieweruilib/.project
deleted file mode 100644
index e9b1298..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>hierarchyvieweruilib</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/Android.mk b/hierarchyviewer2/libs/hierarchyvieweruilib/Android.mk
deleted file mode 100644
index 341880b..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
-#
-# 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.
-
-HIERARCHYVIEWERUILIB_LOCAL_DIR := $(call my-dir)
-include $(HIERARCHYVIEWERUILIB_LOCAL_DIR)/src/Android.mk
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/Android.mk b/hierarchyviewer2/libs/hierarchyvieweruilib/src/Android.mk
deleted file mode 100644
index 44d6b51..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (C) 2010 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
-#
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_JAVA_LIBRARIES := \
- ddmlib \
- ddmuilib \
- hierarchyviewerlib \
- swt \
- org.eclipse.jface_3.4.2.M20090107-0800
-LOCAL_MODULE := hierarchyvieweruilib
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeView.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeView.java
deleted file mode 100644
index f5d7ae0..0000000
--- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeView.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.hierarchyvieweruilib;
-
-import com.android.hierarchyviewerlib.ComponentRegistry;
-import com.android.hierarchyviewerlib.models.TreeViewModel;
-import com.android.hierarchyviewerlib.models.TreeViewModel.TreeChangeListener;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Point;
-import com.android.hierarchyviewerlib.scene.DrawableViewNode.Rectangle;
-
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.DisposeEvent;
-import org.eclipse.swt.events.DisposeListener;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.events.MouseListener;
-import org.eclipse.swt.events.MouseMoveListener;
-import org.eclipse.swt.events.MouseWheelListener;
-import org.eclipse.swt.events.PaintEvent;
-import org.eclipse.swt.events.PaintListener;
-import org.eclipse.swt.graphics.GC;
-import org.eclipse.swt.graphics.Transform;
-import org.eclipse.swt.widgets.Canvas;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Event;
-import org.eclipse.swt.widgets.Listener;
-
-public class TreeView extends Canvas implements TreeChangeListener {
-
- private TreeViewModel model;
-
- private DrawableViewNode tree;
-
- private DrawableViewNode selectedNode;
-
- private Rectangle viewport;
-
- private Transform transform;
-
- private Transform inverse;
-
- private double zoom;
-
- private Point lastPoint;
-
- private DrawableViewNode draggedNode;
-
- public TreeView(Composite parent) {
- super(parent, SWT.NONE);
-
- model = ComponentRegistry.getTreeViewModel();
- model.addTreeChangeListener(this);
-
- addPaintListener(paintListener);
- addMouseListener(mouseListener);
- addMouseMoveListener(mouseMoveListener);
- addMouseWheelListener(mouseWheelListener);
- addListener(SWT.Resize, resizeListener);
- addDisposeListener(disposeListener);
-
- transform = new Transform(Display.getDefault());
- inverse = new Transform(Display.getDefault());
- }
-
- private DisposeListener disposeListener = new DisposeListener() {
- public void widgetDisposed(DisposeEvent e) {
- model.removeTreeChangeListener(TreeView.this);
- transform.dispose();
- inverse.dispose();
- }
- };
-
- private Listener resizeListener = new Listener() {
- public void handleEvent(Event e) {
- synchronized (this) {
- if (tree != null && viewport != null) {
-
- // I don't know what the best behaviour is... This seems
- // like a good idea.
- Point viewCenter =
- new Point(viewport.x + viewport.width / 2, viewport.y + viewport.height
- / 2);
- viewport.width = getBounds().width / zoom;
- viewport.height = getBounds().height / zoom;
- viewport.x = viewCenter.x - viewport.width / 2;
- viewport.y = viewCenter.y - viewport.height / 2;
- }
- }
- if (viewport != null) {
- model.setViewport(viewport);
- }
- }
- };
-
- private MouseListener mouseListener = new MouseListener() {
-
- public void mouseDoubleClick(MouseEvent e) {
- // pass
- }
-
- public void mouseDown(MouseEvent e) {
- boolean selectionChanged = false;
- synchronized (this) {
- if (tree != null && viewport != null) {
- Point pt = transformPoint(e.x, e.y);
- draggedNode = tree.getSelected(pt.x, pt.y);
- if (draggedNode != null && draggedNode != selectedNode) {
- selectedNode = draggedNode;
- selectionChanged = true;
- }
- if (draggedNode == tree) {
- draggedNode = null;
- }
- if (draggedNode != null) {
- lastPoint = pt;
- } else {
- lastPoint = new Point(e.x, e.y);
- }
- }
- }
- if (selectionChanged) {
- model.setSelection(selectedNode);
- }
- }
-
- public void mouseUp(MouseEvent e) {
- boolean redraw = false;
- boolean viewportChanged = false;
- synchronized (this) {
- if (tree != null && viewport != null && lastPoint != null) {
- if (draggedNode == null) {
- handleMouseDrag(new Point(e.x, e.y));
- viewportChanged = true;
- } else {
- handleMouseDrag(transformPoint(e.x, e.y));
- }
- lastPoint = null;
- draggedNode = null;
- redraw = true;
- }
- }
- if (viewportChanged) {
- model.setViewport(viewport);
- } else if (redraw) {
- model.removeTreeChangeListener(TreeView.this);
- model.notifyTreeChanged();
- model.addTreeChangeListener(TreeView.this);
- doRedraw();
- }
- }
-
- };
-
- private MouseMoveListener mouseMoveListener = new MouseMoveListener() {
- public void mouseMove(MouseEvent e) {
- boolean redraw = false;
- boolean viewportChanged = false;
- synchronized (this) {
- if (tree != null && viewport != null && lastPoint != null) {
- if (draggedNode == null) {
- handleMouseDrag(new Point(e.x, e.y));
- viewportChanged = true;
- } else {
- handleMouseDrag(transformPoint(e.x, e.y));
- }
- redraw = true;
- }
- }
- if (viewportChanged) {
- model.setViewport(viewport);
- } else if (redraw) {
- model.removeTreeChangeListener(TreeView.this);
- model.notifyTreeChanged();
- model.addTreeChangeListener(TreeView.this);
- doRedraw();
- }
- }
- };
-
- private void handleMouseDrag(Point pt) {
- if (draggedNode != null) {
- draggedNode.move(lastPoint.y - pt.y);
- lastPoint = pt;
- return;
- }
- double xDif = (lastPoint.x - pt.x) / zoom;
- double yDif = (lastPoint.y - pt.y) / zoom;
-
- if (viewport.width > tree.bounds.width) {
- if (xDif < 0 && viewport.x + viewport.width > tree.bounds.x + tree.bounds.width) {
- viewport.x =
- Math.max(viewport.x + xDif, tree.bounds.x + tree.bounds.width
- - viewport.width);
- } else if (xDif > 0 && viewport.x < tree.bounds.x) {
- viewport.x = Math.min(viewport.x + xDif, tree.bounds.x);
- }
- } else {
- if (xDif < 0 && viewport.x > tree.bounds.x) {
- viewport.x = Math.max(viewport.x + xDif, tree.bounds.x);
- } else if (xDif > 0 && viewport.x + viewport.width < tree.bounds.x + tree.bounds.width) {
- viewport.x =
- Math.min(viewport.x + xDif, tree.bounds.x + tree.bounds.width
- - viewport.width);
- }
- }
- if (viewport.height > tree.bounds.height) {
- if (yDif < 0 && viewport.y + viewport.height > tree.bounds.y + tree.bounds.height) {
- viewport.y =
- Math.max(viewport.y + yDif, tree.bounds.y + tree.bounds.height
- - viewport.height);
- } else if (yDif > 0 && viewport.y < tree.bounds.y) {
- viewport.y = Math.min(viewport.y + yDif, tree.bounds.y);
- }
- } else {
- if (yDif < 0 && viewport.y > tree.bounds.y) {
- viewport.y = Math.max(viewport.y + yDif, tree.bounds.y);
- } else if (yDif > 0
- && viewport.y + viewport.height < tree.bounds.y + tree.bounds.height) {
- viewport.y =
- Math.min(viewport.y + yDif, tree.bounds.y + tree.bounds.height
- - viewport.height);
- }
- }
- lastPoint = pt;
- }
-
- private Point transformPoint(double x, double y) {
- float[] pt = {
- (float) x, (float) y
- };
- inverse.transform(pt);
- return new Point(pt[0], pt[1]);
- }
-
- private MouseWheelListener mouseWheelListener = new MouseWheelListener() {
-
- public void mouseScrolled(MouseEvent e) {
- Point zoomPoint = null;
- synchronized (this) {
- if (tree != null && viewport != null) {
- zoom += Math.ceil(e.count / 3.0) * 0.1;
- zoomPoint = transformPoint(e.x, e.y);
- }
- }
- if (zoomPoint != null) {
- model.zoomOnPoint(zoom, zoomPoint);
- }
- }
- };
-
- private PaintListener paintListener = new PaintListener() {
- public void paintControl(PaintEvent e) {
- synchronized (this) {
- e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
- e.gc.fillRectangle(0, 0, getBounds().width, getBounds().height);
- if (tree != null && viewport != null) {
- e.gc.setTransform(transform);
- e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
- paintRecursive(e.gc, tree);
- }
- }
- }
- };
-
- private void paintRecursive(GC gc, DrawableViewNode node) {
- if (selectedNode == node) {
- gc.fillRectangle(node.left, (int) Math.round(node.top), DrawableViewNode.NODE_WIDTH,
- DrawableViewNode.NODE_HEIGHT);
- } else {
- gc.drawRectangle(node.left, (int) Math.round(node.top), DrawableViewNode.NODE_WIDTH,
- DrawableViewNode.NODE_HEIGHT);
- }
- int N = node.children.size();
- for (int i = 0; i < N; i++) {
- DrawableViewNode child = node.children.get(i);
- paintRecursive(gc, child);
- gc.drawLine(node.left + DrawableViewNode.NODE_WIDTH, (int) Math.round(node.top)
- + DrawableViewNode.NODE_HEIGHT / 2, child.left, (int) Math.round(child.top)
- + DrawableViewNode.NODE_HEIGHT / 2);
- }
- }
-
- private void doRedraw() {
- Display.getDefault().syncExec(new Runnable() {
- public void run() {
- redraw();
- }
- });
- }
-
- public void treeChanged() {
- synchronized (this) {
- tree = model.getTree();
- selectedNode = model.getSelection();
- if (tree == null) {
- viewport = null;
- } else {
- Display.getDefault().syncExec(new Runnable() {
- public void run() {
- viewport =
- new Rectangle((tree.bounds.width - getBounds().width) / 2,
- (tree.bounds.height - getBounds().height) / 2,
- getBounds().width, getBounds().height);
- }
- });
- }
- }
- if (viewport != null) {
- model.setViewport(viewport);
- }
- }
-
- private void setTransform() {
- if (viewport != null && tree != null) {
- // Set the transform.
- transform.identity();
- inverse.identity();
-
- transform.scale((float) zoom, (float) zoom);
- inverse.scale((float) zoom, (float) zoom);
- transform.translate((float) -viewport.x, (float) -viewport.y);
- inverse.translate((float) -viewport.x, (float) -viewport.y);
- inverse.invert();
- }
- }
-
- public void viewportChanged() {
- synchronized (this) {
- viewport = model.getViewport();
- zoom = model.getZoom();
- setTransform();
- }
- doRedraw();
- }
-
- public void zoomChanged() {
- viewportChanged();
- }
-
- public void selectionChanged() {
- synchronized (this) {
- selectedNode = model.getSelection();
- }
- doRedraw();
- }
-}