diff options
Diffstat (limited to 'hierarchyviewer2')
35 files changed, 1771 insertions, 714 deletions
diff --git a/hierarchyviewer2/.gitignore b/hierarchyviewer2/.gitignore index 89a196a..7c59e86 100644 --- a/hierarchyviewer2/.gitignore +++ b/hierarchyviewer2/.gitignore @@ -1,4 +1,3 @@ app/bin libs/hierarchyviewerlib/bin -libs/hierarchyvieweruilib/bin diff --git a/hierarchyviewer2/app/.classpath b/hierarchyviewer2/app/.classpath index f7a8bdf..d75889a 100644 --- a/hierarchyviewer2/app/.classpath +++ b/hierarchyviewer2/app/.classpath @@ -4,7 +4,6 @@ <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="/hierarchyvieweruilib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmlib"/> <classpathentry combineaccessrules="false" kind="src" path="/ddmuilib"/> <classpathentry combineaccessrules="false" kind="src" path="/SdkLib"/> diff --git a/hierarchyviewer2/app/etc/manifest.txt b/hierarchyviewer2/app/etc/manifest.txt index ef45052..fa7e491 100644 --- a/hierarchyviewer2/app/etc/manifest.txt +++ b/hierarchyviewer2/app/etc/manifest.txt @@ -1,2 +1,2 @@ Main-Class: com.android.hierarchyviewer.HierarchyViewerApplication -Class-Path: ddmlib.jar hierarchyviewerlib.jar hierarchyvieweruilib.jar +Class-Path: ddmlib.jar hierarchyviewerlib.jar diff --git a/hierarchyviewer2/app/src/Android.mk b/hierarchyviewer2/app/src/Android.mk index d8d8fc4..0a25ae7 100644 --- a/hierarchyviewer2/app/src/Android.mk +++ b/hierarchyviewer2/app/src/Android.mk @@ -22,7 +22,6 @@ LOCAL_JAVA_LIBRARIES := \ ddmlib \ ddmuilib \ hierarchyviewerlib \ - hierarchyvieweruilib \ swt \ sdklib diff --git a/hierarchyviewer2/app/src/com/android/hierarchyviewer/UIThread.java b/hierarchyviewer2/app/src/com/android/hierarchyviewer/UIThread.java index 63881a2..3da9468 100644 --- a/hierarchyviewer2/app/src/com/android/hierarchyviewer/UIThread.java +++ b/hierarchyviewer2/app/src/com/android/hierarchyviewer/UIThread.java @@ -18,21 +18,25 @@ package com.android.hierarchyviewer; import com.android.ddmuilib.ImageLoader; import com.android.hierarchyviewerlib.ComponentRegistry; -import com.android.hierarchyvieweruilib.DeviceSelector; -import com.android.hierarchyvieweruilib.PixelPerfect; -import com.android.hierarchyvieweruilib.PixelPerfectLoupe; -import com.android.hierarchyvieweruilib.PixelPerfectTree; -import com.android.hierarchyvieweruilib.ProfileViewer; -import com.android.hierarchyvieweruilib.PropertyViewer; -import com.android.hierarchyvieweruilib.TreeView; -import com.android.hierarchyvieweruilib.TreeViewOverview; +import com.android.hierarchyviewerlib.ui.DeviceSelector; +import com.android.hierarchyviewerlib.ui.LayoutViewer; +import com.android.hierarchyviewerlib.ui.PixelPerfect; +import com.android.hierarchyviewerlib.ui.PixelPerfectLoupe; +import com.android.hierarchyviewerlib.ui.PixelPerfectTree; +import com.android.hierarchyviewerlib.ui.ProfileViewer; +import com.android.hierarchyviewerlib.ui.PropertyViewer; +import com.android.hierarchyviewerlib.ui.TreeView; +import com.android.hierarchyviewerlib.ui.TreeViewOverview; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.events.ShellListener; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -47,67 +51,12 @@ public class UIThread { shell.setLayout(new FillLayout()); DeviceSelector deviceSelector = new DeviceSelector(shell); shell.open(); - Shell shell2 = new Shell(display); - shell2.setLayout(new FillLayout()); - /* - - - PixelPerfectTree pixelPerfectTree = new PixelPerfectTree(shell2); - Composite overview = new Composite(shell2, SWT.NONE); - overview.setLayout(new GridLayout()); - PixelPerfect pixelPerfect = new PixelPerfect(overview); - pixelPerfect.setLayoutData(new GridData(GridData.FILL_BOTH)); - final Slider slider = new Slider(overview, SWT.HORIZONTAL); - slider.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - slider.setMinimum(2); - slider.setMaximum(25); - slider.setSelection(8); - slider.setThumb(1); - final PixelPerfectLoupe pixelPerfectLoupe = new PixelPerfectLoupe(shell2); - slider.addSelectionListener(new SelectionListener() { - private int oldZoom = 8; - - public void widgetDefaultSelected(SelectionEvent arg0) { - // pass - } - - public void widgetSelected(SelectionEvent arg0) { - int newZoom = slider.getSelection(); - if (newZoom != oldZoom) { - ComponentRegistry.getPixelPerfectModel().setZoom(newZoom); - oldZoom = newZoom; - } - } - - }); - shell2.open(); - */ - TreeView treeView = new TreeView(shell2); - shell2.open(); - Shell shell3 = new Shell(display); - shell3.setLayout(new FillLayout()); - PropertyViewer propertyViewer = new PropertyViewer(shell3); - shell3.open(); - Shell shell4 = new Shell(display); - shell4.setLayout(new FillLayout()); - ProfileViewer profileViewer = new ProfileViewer(shell4); - shell4.open(); - // ComponentRegistry.getDirector().loadViewTreeData(null); - while (!shell.isDisposed() && !shell2.isDisposed() && !shell3.isDisposed()) { + while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } - if (!shell.isDisposed()) { - shell.dispose(); - } - if (!shell2.isDisposed()) { - shell2.dispose(); - } - if(!shell3.isDisposed()) { - shell3.dispose(); - } // NO LONGER TESTING STUFF. 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 Binary files differnew file mode 100644 index 0000000..b52a342 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png Binary files differnew file mode 100644 index 0000000..338c2d9 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png Binary files differnew file mode 100644 index 0000000..b6fadac --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png 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(); - } -} |