From 70a830d8ab396328ab984c75f923400af6a52e55 Mon Sep 17 00:00:00 2001 From: Konstantin Lopyrev Date: Tue, 17 Aug 2010 18:00:33 -0700 Subject: Tweaking and moving. Change-Id: I9906268a91c53c7b9e938b9c969cedeae2a4303a --- hierarchyviewer2/libs/Android.mk | 1 - .../libs/hierarchyviewerlib/.classpath | 2 + .../libs/hierarchyviewerlib/src/Android.mk | 7 +- .../HierarchyViewerDirector.java | 114 +++- .../hierarchyviewerlib/device/DeviceBridge.java | 29 +- .../device/DeviceConnection.java | 5 + .../hierarchyviewerlib/device/ViewNode.java | 101 +++- .../models/PixelPerfectModel.java | 131 +++-- .../hierarchyviewerlib/models/TreeViewModel.java | 9 +- .../hierarchyviewerlib/scene/DrawableViewNode.java | 249 --------- .../hierarchyviewerlib/ui/CaptureDisplay.java | 213 +++++++ .../hierarchyviewerlib/ui/DeviceSelector.java | 266 +++++++++ .../hierarchyviewerlib/ui/LayoutViewer.java | 319 +++++++++++ .../hierarchyviewerlib/ui/PixelPerfect.java | 330 +++++++++++ .../hierarchyviewerlib/ui/PixelPerfectLoupe.java | 322 +++++++++++ .../hierarchyviewerlib/ui/PixelPerfectTree.java | 222 ++++++++ .../hierarchyviewerlib/ui/ProfileViewer.java | 188 +++++++ .../hierarchyviewerlib/ui/PropertyViewer.java | 230 ++++++++ .../android/hierarchyviewerlib/ui/TreeView.java | 617 +++++++++++++++++++++ .../hierarchyviewerlib/ui/TreeViewOverview.java | 316 +++++++++++ .../ui/util/DrawableViewNode.java | 266 +++++++++ .../ui/util/TreeColumnResizer.java | 109 ++++ .../src/resources/images/green.png | Bin 0 -> 219 bytes .../src/resources/images/red.png | Bin 0 -> 220 bytes .../src/resources/images/yellow.png | Bin 0 -> 221 bytes .../libs/hierarchyvieweruilib/.classpath | 10 - .../libs/hierarchyvieweruilib/.gitignore | 1 - .../libs/hierarchyvieweruilib/.project | 17 - .../libs/hierarchyvieweruilib/Android.mk | 16 - .../libs/hierarchyvieweruilib/src/Android.mk | 29 - .../hierarchyvieweruilib/DeviceSelector.java | 266 --------- .../android/hierarchyvieweruilib/PixelPerfect.java | 307 ---------- .../hierarchyvieweruilib/PixelPerfectLoupe.java | 288 ---------- .../hierarchyvieweruilib/PixelPerfectTree.java | 216 -------- .../hierarchyvieweruilib/ProfileViewer.java | 179 ------ .../hierarchyvieweruilib/PropertyViewer.java | 232 -------- .../com/android/hierarchyvieweruilib/TreeView.java | 361 ------------ .../hierarchyvieweruilib/TreeViewOverview.java | 289 ---------- .../util/TreeColumnResizer.java | 92 --- 39 files changed, 3730 insertions(+), 2619 deletions(-) delete mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/CaptureDisplay.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/LayoutViewer.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeView.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png create mode 100644 hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/.classpath delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/.gitignore delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/.project delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/Android.mk delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/Android.mk delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeView.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java delete mode 100644 hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java (limited to 'hierarchyviewer2/libs') 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 @@ + + 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 categories = new TreeSet(); - 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 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 imageChangeListeners = new ArrayList(); - 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/scene/DrawableViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java deleted file mode 100644 index aff3d6d..0000000 --- a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/scene/DrawableViewNode.java +++ /dev/null @@ -1,249 +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.hierarchyviewerlib.scene; - -import com.android.hierarchyviewerlib.device.ViewNode; - -import java.util.ArrayList; - -public class DrawableViewNode { - public ViewNode viewNode; - - public final ArrayList children = new ArrayList(); - - public final static int NODE_HEIGHT = 70; - - public final static int NODE_WIDTH = 100; - - public final static int LEAF_NODE_SPACING = 5; - - public final static int NON_LEAF_NODE_SPACING = 10; - - public final static int PARENT_CHILD_SPACING = 40; - - public final static int PADDING = 30; - - public int treeHeight; - - public int treeWidth; - - public boolean leaf; - - public DrawableViewNode parent; - - public int left; - - public double top; - - public int topSpacing; - - public int bottomSpacing; - - public static class Rectangle { - public double x, y, width, height; - - public Rectangle() { - - } - - public Rectangle(Rectangle other) { - this.x = other.x; - this.y = other.y; - this.width = other.width; - this.height = other.height; - } - - public Rectangle(double x, double y, double width, double height) { - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - @Override - public String toString() { - return "{" + x + ", " + y + ", " + width + ", " + height + "}"; - } - - } - - public static class Point { - public double x, y; - - public Point() { - } - - public Point(double x, double y) { - this.x = x; - this.y = y; - } - } - - public Rectangle bounds = new Rectangle(); - - public DrawableViewNode(ViewNode viewNode) { - this.viewNode = viewNode; - if (viewNode.children.size() == 0) { - treeHeight = NODE_HEIGHT; - treeWidth = NODE_WIDTH; - leaf = true; - } else { - leaf = false; - int N = viewNode.children.size(); - treeHeight = 0; - treeWidth = 0; - for (int i = 0; i < N; i++) { - DrawableViewNode child = new DrawableViewNode(viewNode.children.get(i)); - children.add(child); - child.parent = this; - treeHeight += child.treeHeight; - treeWidth = Math.max(treeWidth, child.treeWidth); - if (i != 0) { - DrawableViewNode prevChild = children.get(i - 1); - if (prevChild.leaf && child.leaf) { - treeHeight += LEAF_NODE_SPACING; - prevChild.bottomSpacing = LEAF_NODE_SPACING; - child.topSpacing = LEAF_NODE_SPACING; - } else { - treeHeight += NON_LEAF_NODE_SPACING; - prevChild.bottomSpacing = NON_LEAF_NODE_SPACING; - child.topSpacing = NON_LEAF_NODE_SPACING; - } - } - } - treeWidth += NODE_WIDTH + PARENT_CHILD_SPACING; - } - } - - public void setLeft() { - if (parent == null) { - left = PADDING; - bounds.x = 0; - bounds.width = treeWidth + 2 * PADDING; - } else { - left = parent.left + NODE_WIDTH + PARENT_CHILD_SPACING; - } - int N = children.size(); - for (int i = 0; i < N; i++) { - children.get(i).setLeft(); - } - } - - public void placeRoot() { - top = PADDING + (treeHeight - NODE_HEIGHT) / 2.0; - double currentTop = PADDING; - int N = children.size(); - for (int i = 0; i < N; i++) { - DrawableViewNode child = children.get(i); - child.place(currentTop, top - currentTop); - currentTop += child.treeHeight + child.bottomSpacing; - } - bounds.y = 0; - bounds.height = treeHeight + 2 * PADDING; - } - - private void place(double treeTop, double rootDistance) { - if (treeHeight <= rootDistance) { - top = treeTop + treeHeight - NODE_HEIGHT; - } else if (rootDistance <= -NODE_HEIGHT) { - top = treeTop; - } else { - if (children.size() == 0) { - top = treeTop; - } else { - top = - rootDistance + treeTop - NODE_HEIGHT + (2.0 * NODE_HEIGHT) - / (treeHeight + NODE_HEIGHT) * (treeHeight - rootDistance); - } - } - int N = children.size(); - double currentTop = treeTop; - for (int i = 0; i < N; i++) { - DrawableViewNode child = children.get(i); - child.place(currentTop, rootDistance); - currentTop += child.treeHeight + child.bottomSpacing; - rootDistance -= child.treeHeight + child.bottomSpacing; - } - } - - public DrawableViewNode getSelected(double x, double y) { - if (x >= left && x < left + NODE_WIDTH && y >= top && y <= top + NODE_HEIGHT) { - return this; - } - int N = children.size(); - for (int i = 0; i < N; i++) { - DrawableViewNode selected = children.get(i).getSelected(x, y); - if (selected != null) { - return selected; - } - } - return null; - } - - /* - * Moves the node the specified distance up. - */ - public void move(double distance) { - top -= distance; - - // Get the root - DrawableViewNode root = this; - while (root.parent != null) { - root = root.parent; - } - - // Figure out the new tree top. - double treeTop; - if (top + NODE_HEIGHT <= root.top) { - treeTop = top + NODE_HEIGHT - treeHeight; - } else if (top >= root.top + NODE_HEIGHT) { - treeTop = top; - } else { - if (leaf) { - treeTop = top; - } else { - double distanceRatio = 1 - (root.top + NODE_HEIGHT - top) / (2.0 * NODE_HEIGHT); - treeTop = root.top - treeHeight + distanceRatio * (treeHeight + NODE_HEIGHT); - } - } - // Go up the tree and figure out the tree top. - DrawableViewNode node = this; - while (node.parent != null) { - int index = node.viewNode.index; - for (int i = 0; i < index; i++) { - DrawableViewNode sibling = node.parent.children.get(i); - treeTop -= sibling.treeHeight + sibling.bottomSpacing; - } - node = node.parent; - } - - // Update the bounds. - root.bounds.y = Math.min(root.top - PADDING, treeTop - PADDING); - root.bounds.height = - Math.max(treeTop + root.treeHeight + PADDING, root.top + NODE_HEIGHT + PADDING) - - root.bounds.y; - // Place all the children of the root - double currentTop = treeTop; - int N = root.children.size(); - for (int i = 0; i < N; i++) { - DrawableViewNode child = root.children.get(i); - child.place(currentTop, root.top - currentTop); - currentTop += child.treeHeight + child.bottomSpacing; - } - - } -} 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/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java new file mode 100644 index 0000000..5e7c606 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/DeviceSelector.java @@ -0,0 +1,266 @@ +/* + * 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.ddmlib.IDevice; +import com.android.ddmuilib.ImageLoader; +import com.android.hierarchyviewerlib.ComponentRegistry; +import com.android.hierarchyviewerlib.device.Window; +import com.android.hierarchyviewerlib.models.DeviceSelectionModel; +import com.android.hierarchyviewerlib.models.DeviceSelectionModel.WindowChangeListener; + +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +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.Tree; +import org.eclipse.swt.widgets.TreeItem; + +public class DeviceSelector extends Composite implements WindowChangeListener, SelectionListener { + private TreeViewer treeViewer; + + private Tree tree; + + private DeviceSelectionModel model; + + private Font boldFont; + + private Image deviceImage; + + private Image emulatorImage; + + private final static int ICON_WIDTH = 16; + + private class ContentProvider implements ITreeContentProvider, ILabelProvider, IFontProvider { + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof IDevice) { + Window[] list = model.getWindows((IDevice) parentElement); + if (list != null) { + return list; + } + } + return new Object[0]; + } + + public Object getParent(Object element) { + if (element instanceof Window) { + return ((Window) element).getDevice(); + } + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof IDevice) { + Window[] list = model.getWindows((IDevice) element); + if (list != null) { + return list.length != 0; + } + } + return false; + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof DeviceSelectionModel) { + return model.getDevices(); + } + return new Object[0]; + } + + public void dispose() { + // pass + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // pass + } + + public Image getImage(Object element) { + if (element instanceof IDevice) { + if (((IDevice) element).isEmulator()) { + return emulatorImage; + } + return deviceImage; + } + return null; + } + + public String getText(Object element) { + if (element instanceof IDevice) { + return ((IDevice) element).toString(); + } else if (element instanceof Window) { + return ((Window) element).getTitle(); + } + return null; + } + + public Font getFont(Object element) { + if (element instanceof Window) { + int focusedWindow = model.getFocusedWindow(((Window) element).getDevice()); + if (focusedWindow == ((Window) element).getHashCode()) { + return boldFont; + } + } + return null; + } + + public void addListener(ILabelProviderListener listener) { + // pass + } + + public boolean isLabelProperty(Object element, String property) { + // pass + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // pass + } + } + + public DeviceSelector(Composite parent) { + super(parent, SWT.NONE); + setLayout(new FillLayout()); + treeViewer = new TreeViewer(this, SWT.SINGLE); + treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); + + tree = treeViewer.getTree(); + tree.setLinesVisible(true); + tree.addSelectionListener(this); + + addDisposeListener(disposeListener); + + loadResources(); + + model = ComponentRegistry.getDeviceSelectionModel(); + ContentProvider contentProvider = new ContentProvider(); + treeViewer.setContentProvider(contentProvider); + treeViewer.setLabelProvider(contentProvider); + model.addWindowChangeListener(this); + treeViewer.setInput(model); + } + + public void loadResources() { + Display display = Display.getDefault(); + Font systemFont = display.getSystemFont(); + FontData[] fontData = systemFont.getFontData(); + FontData[] newFontData = new FontData[fontData.length]; + for (int i = 0; i < fontData.length; i++) { + newFontData[i] = + new FontData(fontData[i].getName(), fontData[i].getHeight(), fontData[i] + .getStyle() + | SWT.BOLD); + } + boldFont = new Font(Display.getDefault(), newFontData); + + ImageLoader loader = ImageLoader.getDdmUiLibLoader(); + deviceImage = + loader.loadImage(display, "device.png", ICON_WIDTH, ICON_WIDTH, display + .getSystemColor(SWT.COLOR_RED)); + + emulatorImage = + loader.loadImage(display, "emulator.png", ICON_WIDTH, ICON_WIDTH, display + .getSystemColor(SWT.COLOR_BLUE)); + } + + private DisposeListener disposeListener = new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + model.removeWindowChangeListener(DeviceSelector.this); + boldFont.dispose(); + } + }; + + @Override + public boolean setFocus() { + return tree.setFocus(); + } + + public void deviceConnected(final IDevice device) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + treeViewer.refresh(); + treeViewer.setExpandedState(device, true); + } + }); + } + + public void deviceChanged(final IDevice device) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + TreeSelection selection = (TreeSelection) treeViewer.getSelection(); + treeViewer.refresh(device); + if (selection.getFirstElement() instanceof Window + && ((Window) selection.getFirstElement()).getDevice() == device) { + treeViewer.setSelection(selection, true); + } + } + }); + } + + public void deviceDisconnected(final IDevice device) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + treeViewer.refresh(); + } + }); + } + + public void focusChanged(final IDevice device) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + TreeSelection selection = (TreeSelection) treeViewer.getSelection(); + treeViewer.refresh(device); + if (selection.getFirstElement() instanceof Window + && ((Window) selection.getFirstElement()).getDevice() == device) { + treeViewer.setSelection(selection, true); + } + } + }); + } + + public void widgetDefaultSelected(SelectionEvent e) { + // TODO: Double click to open view hierarchy + Object selection = ((TreeItem) e.item).getData(); + if (selection instanceof IDevice) { + ComponentRegistry.getDirector().loadPixelPerfectData((IDevice) selection); + } else if (selection instanceof Window) { + ComponentRegistry.getDirector().loadViewTreeData((Window) selection); + } + } + + public void widgetSelected(SelectionEvent e) { + Object selection = ((TreeItem) e.item).getData(); + if (selection instanceof IDevice) { + model.setSelection((IDevice) selection, null); + } else if (selection instanceof Window) { + model.setSelection(((Window) selection).getDevice(), (Window) selection); + } + } +} 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 rightLeftDistances = new ArrayList(); + 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/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java new file mode 100644 index 0000000..1a2876b --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfect.java @@ -0,0 +1,330 @@ +/* + * 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.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 org.eclipse.swt.SWT; +import org.eclipse.swt.custom.ScrolledComposite; +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.PaintEvent; +import org.eclipse.swt.events.PaintListener; +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; +import org.eclipse.swt.widgets.Display; + +public class PixelPerfect extends ScrolledComposite implements ImageChangeListener { + private Canvas canvas; + + private PixelPerfectModel model; + + private Image image; + + private Color crosshairColor; + + private Color marginColor; + + private Color borderColor; + + private Color paddingColor; + + private int width; + + private int height; + + private Point crosshairLocation; + + 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); + setContent(canvas); + setExpandHorizontal(true); + setExpandVertical(true); + model = ComponentRegistry.getPixelPerfectModel(); + model.addImageChangeListener(this); + + canvas.addPaintListener(paintListener); + canvas.addMouseListener(mouseListener); + canvas.addMouseMoveListener(mouseMoveListener); + + addDisposeListener(disposeListener); + + crosshairColor = new Color(Display.getDefault(), new RGB(0, 255, 255)); + borderColor = new Color(Display.getDefault(), new RGB(255, 0, 0)); + marginColor = new Color(Display.getDefault(), new RGB(0, 255, 0)); + paddingColor = new Color(Display.getDefault(), new RGB(0, 0, 255)); + } + + private DisposeListener disposeListener = new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + model.removeImageChangeListener(PixelPerfect.this); + if (image != null) { + image.dispose(); + } + crosshairColor.dispose(); + borderColor.dispose(); + paddingColor.dispose(); + } + }; + + @Override + public boolean setFocus() { + return canvas.setFocus(); + } + + private MouseListener mouseListener = new MouseListener() { + + public void mouseDoubleClick(MouseEvent e) { + // pass + } + + public void mouseDown(MouseEvent e) { + handleMouseEvent(e); + } + + public void mouseUp(MouseEvent e) { + handleMouseEvent(e); + } + + }; + + private MouseMoveListener mouseMoveListener = new MouseMoveListener() { + public void mouseMove(MouseEvent e) { + if (e.stateMask != 0) { + handleMouseEvent(e); + } + } + }; + + private void handleMouseEvent(MouseEvent e) { + synchronized (PixelPerfect.this) { + if (image == null) { + return; + } + int leftOffset = canvas.getSize().x / 2 - width / 2; + int topOffset = canvas.getSize().y / 2 - height / 2; + e.x -= leftOffset; + e.y -= topOffset; + e.x = Math.max(e.x, 0); + e.x = Math.min(e.x, width - 1); + e.y = Math.max(e.y, 0); + e.y = Math.min(e.y, height - 1); + } + model.setCrosshairLocation(e.x, e.y); + } + + private PaintListener paintListener = new PaintListener() { + public void paintControl(PaintEvent e) { + 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) { + // Let's be cool and put it in the center... + 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, + // they are always treated as positive. + // Secondly, if the screen is in landscape mode, the + // coordinates are backwards. + int leftShift = 0; + int topShift = 0; + int nodeLeft = selectedNode.left; + int nodeTop = selectedNode.top; + int nodeWidth = selectedNode.width; + int nodeHeight = selectedNode.height; + int nodeMarginLeft = selectedNode.marginLeft; + int nodeMarginTop = selectedNode.marginTop; + int nodeMarginRight = selectedNode.marginRight; + int nodeMarginBottom = selectedNode.marginBottom; + int nodePadLeft = selectedNode.paddingLeft; + int nodePadTop = selectedNode.paddingTop; + int nodePadRight = selectedNode.paddingRight; + int nodePadBottom = selectedNode.paddingBottom; + ViewNode cur = selectedNode; + while (cur.parent != null) { + leftShift += cur.parent.left - cur.parent.scrollX; + topShift += cur.parent.top - cur.parent.scrollY; + cur = cur.parent; + } + + // Everything is sideways. + if (cur.width > cur.height) { + e.gc.setForeground(paddingColor); + e.gc.drawRectangle(leftOffset + width - nodeTop - topShift - nodeHeight + + nodePadBottom, + topOffset + leftShift + nodeLeft + nodePadLeft, nodeHeight + - nodePadBottom - nodePadTop, nodeWidth - nodePadRight + - nodePadLeft); + e.gc.setForeground(marginColor); + e.gc.drawRectangle(leftOffset + width - nodeTop - topShift - nodeHeight + - nodeMarginBottom, topOffset + leftShift + nodeLeft + - nodeMarginLeft, + nodeHeight + nodeMarginBottom + nodeMarginTop, nodeWidth + + nodeMarginRight + nodeMarginLeft); + e.gc.setForeground(borderColor); + e.gc.drawRectangle( + leftOffset + width - nodeTop - topShift - nodeHeight, topOffset + + leftShift + nodeLeft, nodeHeight, nodeWidth); + } else { + e.gc.setForeground(paddingColor); + e.gc.drawRectangle(leftOffset + leftShift + nodeLeft + nodePadLeft, + topOffset + topShift + nodeTop + nodePadTop, nodeWidth + - nodePadRight - nodePadLeft, nodeHeight + - nodePadBottom - nodePadTop); + e.gc.setForeground(marginColor); + e.gc.drawRectangle(leftOffset + leftShift + nodeLeft - nodeMarginLeft, + topOffset + topShift + nodeTop - nodeMarginTop, nodeWidth + + nodeMarginRight + nodeMarginLeft, nodeHeight + + nodeMarginBottom + nodeMarginTop); + e.gc.setForeground(borderColor); + e.gc.drawRectangle(leftOffset + leftShift + nodeLeft, topOffset + + topShift + nodeTop, nodeWidth, nodeHeight); + } + } + if (crosshairLocation != null) { + e.gc.setForeground(crosshairColor); + e.gc.drawLine(leftOffset, topOffset + crosshairLocation.y, leftOffset + + width - 1, topOffset + crosshairLocation.y); + e.gc.drawLine(leftOffset + crosshairLocation.x, topOffset, leftOffset + + crosshairLocation.x, topOffset + height - 1); + } + } + } + } + }; + + private void doRedraw() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + canvas.redraw(); + } + }); + } + + private void loadImage() { + image = model.getImage(); + if (image != null) { + width = image.getBounds().width; + height = image.getBounds().height; + } else { + width = 0; + height = 0; + } + setMinSize(width, height); + } + + public void imageLoaded() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + loadImage(); + crosshairLocation = model.getCrosshairLocation(); + selectedNode = model.getSelected(); + } + } + }); + doRedraw(); + } + + public void imageChanged() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + loadImage(); + } + } + }); + doRedraw(); + } + + public void crosshairMoved() { + synchronized (this) { + crosshairLocation = model.getCrosshairLocation(); + } + doRedraw(); + } + + public void selectionChanged() { + synchronized (this) { + selectedNode = model.getSelected(); + } + doRedraw(); + } + + public void focusChanged() { + 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/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java new file mode 100644 index 0000000..84ce08f --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectLoupe.java @@ -0,0 +1,322 @@ +/* + * 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.ddmlib.RawImage; +import com.android.hierarchyviewerlib.ComponentRegistry; +import com.android.hierarchyviewerlib.models.PixelPerfectModel; +import com.android.hierarchyviewerlib.models.PixelPerfectModel.ImageChangeListener; + +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.MouseWheelListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.graphics.Color; +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; +import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; + +public class PixelPerfectLoupe extends Canvas implements ImageChangeListener { + private PixelPerfectModel model; + + private Image image; + + private Image grid; + + private Color crosshairColor; + + private int width; + + private int height; + + private Point crosshairLocation; + + private int zoom; + + private Transform transform; + + private int canvasWidth; + + private int canvasHeight; + + private Image overlayImage; + + private double overlayTransparency; + + private boolean showOverlay = false; + + public PixelPerfectLoupe(Composite parent) { + super(parent, SWT.NONE); + model = ComponentRegistry.getPixelPerfectModel(); + model.addImageChangeListener(this); + + addPaintListener(paintListener); + addMouseListener(mouseListener); + addMouseWheelListener(mouseWheelListener); + addDisposeListener(disposeListener); + + crosshairColor = new Color(Display.getDefault(), new RGB(255, 94, 254)); + + 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); + if (image != null) { + image.dispose(); + } + crosshairColor.dispose(); + transform.dispose(); + if (grid != null) { + grid.dispose(); + } + } + }; + + private MouseListener mouseListener = new MouseListener() { + + public void mouseDoubleClick(MouseEvent e) { + // pass + } + + public void mouseDown(MouseEvent e) { + handleMouseEvent(e); + } + + public void mouseUp(MouseEvent e) { + // + } + + }; + + private MouseWheelListener mouseWheelListener = new MouseWheelListener() { + public void mouseScrolled(MouseEvent e) { + int newZoom = -1; + synchronized (PixelPerfectLoupe.this) { + if (image != null && crosshairLocation != null) { + if (e.count > 0) { + newZoom = zoom + 1; + } else { + newZoom = zoom - 1; + } + } + } + if (newZoom != -1) { + model.setZoom(newZoom); + } + } + }; + + private void handleMouseEvent(MouseEvent e) { + int newX = -1; + int newY = -1; + synchronized (PixelPerfectLoupe.this) { + if (image == null) { + return; + } + int zoomedX = -crosshairLocation.x * zoom - zoom / 2 + getBounds().width / 2; + int zoomedY = -crosshairLocation.y * zoom - zoom / 2 + getBounds().height / 2; + int x = (e.x - zoomedX) / zoom; + int y = (e.y - zoomedY) / zoom; + if (x >= 0 && x < width && y >= 0 && y < height) { + newX = x; + newY = y; + } + } + if (newX != -1) { + model.setCrosshairLocation(newX, newY); + } + } + + private PaintListener paintListener = new PaintListener() { + public void paintControl(PaintEvent e) { + 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) { + int zoomedX = -crosshairLocation.x * zoom - zoom / 2 + getBounds().width / 2; + int zoomedY = -crosshairLocation.y * zoom - zoom / 2 + getBounds().height / 2; + transform.translate(zoomedX, zoomedY); + transform.scale(zoom, zoom); + 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); + + // If the size of the canvas has changed, we need to make + // another grid. + if (grid != null + && (canvasWidth != getBounds().width || canvasHeight != getBounds().height)) { + grid.dispose(); + grid = null; + } + canvasWidth = getBounds().width; + canvasHeight = getBounds().height; + if (grid == null) { + // Make a transparent image; + ImageData imageData = + new ImageData(canvasWidth + zoom + 1, canvasHeight + zoom + 1, 1, + new PaletteData(new RGB[] { + new RGB(0, 0, 0) + })); + imageData.transparentPixel = 0; + + // Draw the grid. + grid = new Image(Display.getDefault(), imageData); + GC gc = new GC(grid); + gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE)); + for (int x = 0; x <= canvasWidth + zoom; x += zoom) { + gc.drawLine(x, 0, x, canvasHeight + zoom); + } + for (int y = 0; y <= canvasHeight + zoom; y += zoom) { + gc.drawLine(0, y, canvasWidth + zoom, y); + } + gc.dispose(); + } + + e.gc.setClipping(new Rectangle(zoomedX, zoomedY, width * zoom + 1, height + * zoom + 1)); + e.gc.setAlpha(76); + e.gc.drawImage(grid, (canvasWidth / 2 - zoom / 2) % zoom - zoom, + (canvasHeight / 2 - zoom / 2) % zoom - zoom); + e.gc.setAlpha(255); + + e.gc.setForeground(crosshairColor); + e.gc.drawLine(0, canvasHeight / 2, canvasWidth - 1, canvasHeight / 2); + e.gc.drawLine(canvasWidth / 2, 0, canvasWidth / 2, canvasHeight - 1); + } + } + } + }; + + private void doRedraw() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + redraw(); + } + }); + } + + private void loadImage() { + image = model.getImage(); + if (image != null) { + width = image.getBounds().width; + height = image.getBounds().height; + } else { + width = 0; + height = 0; + } + } + + public void imageLoaded() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + loadImage(); + crosshairLocation = model.getCrosshairLocation(); + zoom = model.getZoom(); + } + } + }); + doRedraw(); + } + + public void imageChanged() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + loadImage(); + } + } + }); + doRedraw(); + } + + public void crosshairMoved() { + synchronized (this) { + crosshairLocation = model.getCrosshairLocation(); + } + doRedraw(); + } + + public void selectionChanged() { + // pass + } + + public void focusChanged() { + imageChanged(); + } + + public void zoomChanged() { + 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/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java new file mode 100644 index 0000000..7df4d9d --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PixelPerfectTree.java @@ -0,0 +1,222 @@ +/* + * 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; +import com.android.hierarchyviewerlib.models.PixelPerfectModel; +import com.android.hierarchyviewerlib.models.PixelPerfectModel.ImageChangeListener; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +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.Tree; + +import java.util.List; + +public class PixelPerfectTree extends Composite implements ImageChangeListener, SelectionListener { + + private TreeViewer treeViewer; + + private Tree tree; + + private PixelPerfectModel model; + + private Image folderImage; + + private Image fileImage; + + private class ContentProvider implements ITreeContentProvider, ILabelProvider { + public Object[] getChildren(Object element) { + if (element instanceof ViewNode) { + List children = ((ViewNode) element).children; + return children.toArray(new ViewNode[children.size()]); + } + return null; + } + + public Object getParent(Object element) { + if (element instanceof ViewNode) { + return ((ViewNode) element).parent; + } + return null; + } + + public boolean hasChildren(Object element) { + if (element instanceof ViewNode) { + return ((ViewNode) element).children.size() != 0; + } + return false; + } + + public Object[] getElements(Object element) { + if (element instanceof PixelPerfectModel) { + ViewNode viewNode = ((PixelPerfectModel) element).getViewNode(); + if (viewNode == null) { + return new Object[0]; + } + return new Object[] { + viewNode + }; + } + return new Object[0]; + } + + public void dispose() { + // pass + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // pass + } + + public Image getImage(Object element) { + if (element instanceof ViewNode) { + if (hasChildren(element)) { + return folderImage; + } + return fileImage; + } + return null; + } + + public String getText(Object element) { + if (element instanceof ViewNode) { + return ((ViewNode) element).name; + } + return null; + } + + public void addListener(ILabelProviderListener listener) { + // pass + } + + public boolean isLabelProperty(Object element, String property) { + // pass + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // pass + } + } + + public PixelPerfectTree(Composite parent) { + super(parent, SWT.NONE); + setLayout(new FillLayout()); + treeViewer = new TreeViewer(this, SWT.SINGLE); + treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); + + tree = treeViewer.getTree(); + tree.addSelectionListener(this); + + loadResources(); + + addDisposeListener(disposeListener); + + model = ComponentRegistry.getPixelPerfectModel(); + ContentProvider contentProvider = new ContentProvider(); + treeViewer.setContentProvider(contentProvider); + treeViewer.setLabelProvider(contentProvider); + treeViewer.setInput(model); + model.addImageChangeListener(this); + + } + + public void loadResources() { + ImageLoader loader = ImageLoader.getDdmUiLibLoader(); + fileImage = loader.loadImage("file.png", Display.getDefault()); + + folderImage = loader.loadImage("folder.png", Display.getDefault()); + } + + private DisposeListener disposeListener = new DisposeListener() { + public void widgetDisposed(DisposeEvent e) { + model.removeImageChangeListener(PixelPerfectTree.this); + fileImage.dispose(); + folderImage.dispose(); + } + }; + + @Override + public boolean setFocus() { + return tree.setFocus(); + } + + public void imageLoaded() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + treeViewer.refresh(); + treeViewer.expandAll(); + } + }); + } + + public void imageChanged() { + // pass + } + + public void crosshairMoved() { + // pass + } + + public void selectionChanged() { + // pass + } + + public void focusChanged() { + imageLoaded(); + } + + public void widgetDefaultSelected(SelectionEvent e) { + // pass + } + + public void widgetSelected(SelectionEvent e) { + // To combat phantom selection... + if (((TreeSelection) treeViewer.getSelection()).isEmpty()) { + model.setSelected(null); + } else { + model.setSelected((ViewNode) e.item.getData()); + } + } + + public void zoomChanged() { + // pass + } + + public void overlayChanged() { + // pass + } + + public void overlayTransparencyChanged() { + // pass + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java new file mode 100644 index 0000000..f83ba3d --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/ProfileViewer.java @@ -0,0 +1,188 @@ +/* + * 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.TreeColumnResizer; + +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +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.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +import java.text.DecimalFormat; + +public class ProfileViewer extends Composite implements TreeChangeListener { + private TreeViewModel model; + + private TreeViewer treeViewer; + + private Tree tree; + + private DrawableViewNode selectedNode; + + private class ContentProvider implements ITreeContentProvider, ITableLabelProvider { + + public Object[] getChildren(Object parentElement) { + synchronized (ProfileViewer.this) { + return new Object[0]; + } + } + + public Object getParent(Object element) { + synchronized (ProfileViewer.this) { + return new Object[0]; + } + } + + public boolean hasChildren(Object element) { + synchronized (ProfileViewer.this) { + return false; + } + } + + public Object[] getElements(Object inputElement) { + synchronized (ProfileViewer.this) { + if (selectedNode != null && selectedNode.viewNode.measureTime != -1) { + return new String[] { + "measure", "layout", "draw" + }; + } + return new Object[0]; + } + } + + public void dispose() { + // pass + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // pass + } + + public Image getColumnImage(Object element, int column) { + return null; + } + + public String getColumnText(Object element, int column) { + synchronized (ProfileViewer.this) { + if (selectedNode != null) { + if (column == 0) { + return (String) element; + } 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); + } + } + } + return ""; + } + } + + public void addListener(ILabelProviderListener listener) { + // pass + } + + public boolean isLabelProperty(Object element, String property) { + // pass + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // pass + } + } + + public ProfileViewer(Composite parent) { + super(parent, SWT.NONE); + setLayout(new FillLayout()); + treeViewer = new TreeViewer(this, SWT.NONE); + + tree = treeViewer.getTree(); + tree.setLinesVisible(true); + tree.setHeaderVisible(true); + + TreeColumn operationColumn = new TreeColumn(tree, SWT.NONE); + operationColumn.setText("Operation"); + TreeColumn durationColumn = new TreeColumn(tree, SWT.NONE); + durationColumn.setText("Duration (ms)"); + + model = ComponentRegistry.getTreeViewModel(); + ContentProvider contentProvider = new ContentProvider(); + treeViewer.setContentProvider(contentProvider); + treeViewer.setLabelProvider(contentProvider); + treeViewer.setInput(model); + model.addTreeChangeListener(this); + + new TreeColumnResizer(this, operationColumn, durationColumn); + } + + public void selectionChanged() { + synchronized (this) { + selectedNode = model.getSelection(); + } + doRefresh(); + } + + public void treeChanged() { + synchronized (this) { + selectedNode = model.getSelection(); + } + doRefresh(); + } + + public void viewportChanged() { + // pass + } + + public void zoomChanged() { + // pass + } + + private void doRefresh() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + treeViewer.refresh(); + } + }); + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java new file mode 100644 index 0000000..d262d16 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/PropertyViewer.java @@ -0,0 +1,230 @@ +/* + * 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.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.ui.util.DrawableViewNode; +import com.android.hierarchyviewerlib.ui.util.TreeColumnResizer; + +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +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.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +import java.util.ArrayList; + +public class PropertyViewer extends Composite implements TreeChangeListener { + private TreeViewModel model; + + private TreeViewer treeViewer; + + private Tree tree; + + private DrawableViewNode selectedNode; + + private class ContentProvider implements ITreeContentProvider, ITableLabelProvider { + + public Object[] getChildren(Object parentElement) { + synchronized (PropertyViewer.this) { + if (selectedNode != null && parentElement instanceof String) { + String category = (String) parentElement; + ArrayList returnValue = new ArrayList(); + for (Property property : selectedNode.viewNode.properties) { + if (category.equals(ViewNode.MISCELLANIOUS)) { + if (property.name.indexOf(':') == -1) { + returnValue.add(property); + } + } else { + if (property.name.startsWith(((String) parentElement) + ":")) { + returnValue.add(property); + } + } + } + return returnValue.toArray(new Property[returnValue.size()]); + } + return new Object[0]; + } + } + + public Object getParent(Object element) { + synchronized (PropertyViewer.this) { + if (selectedNode != null && element instanceof Property) { + if (selectedNode.viewNode.categories.size() == 0) { + return null; + } + String name = ((Property) element).name; + int index = name.indexOf(':'); + if (index == -1) { + return ViewNode.MISCELLANIOUS; + } + return name.substring(0, index); + } + return null; + } + } + + public boolean hasChildren(Object element) { + synchronized (PropertyViewer.this) { + if (selectedNode != null && element instanceof String) { + String category = (String) element; + for (String name : selectedNode.viewNode.namedProperties.keySet()) { + if (category.equals(ViewNode.MISCELLANIOUS)) { + if (name.indexOf(':') == -1) { + return true; + } + } else { + if (name.startsWith(((String) element) + ":")) { + return true; + } + } + } + } + return false; + } + } + + public Object[] getElements(Object inputElement) { + synchronized (PropertyViewer.this) { + if (selectedNode != null && inputElement instanceof TreeViewModel) { + if (selectedNode.viewNode.categories.size() == 0) { + return selectedNode.viewNode.properties + .toArray(new Property[selectedNode.viewNode.properties.size()]); + } else { + return selectedNode.viewNode.categories + .toArray(new String[selectedNode.viewNode.categories.size()]); + } + } + return new Object[0]; + } + } + + public void dispose() { + // pass + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // pass + } + + public Image getColumnImage(Object element, int column) { + return null; + } + + public String getColumnText(Object element, int column) { + synchronized (PropertyViewer.this) { + if (selectedNode != null) { + if (element instanceof String && column == 0) { + String category = (String) element; + return Character.toUpperCase(category.charAt(0)) + category.substring(1); + } else if (element instanceof Property) { + if (column == 0) { + String returnValue = ((Property) element).name; + int index = returnValue.indexOf(':'); + if (index != -1) { + return returnValue.substring(index + 1); + } + return returnValue; + } else if (column == 1) { + return ((Property) element).value; + } + } + } + return ""; + } + } + + public void addListener(ILabelProviderListener listener) { + // pass + } + + public boolean isLabelProperty(Object element, String property) { + // pass + return false; + } + + public void removeListener(ILabelProviderListener listener) { + // pass + } + } + + public PropertyViewer(Composite parent) { + super(parent, SWT.NONE); + setLayout(new FillLayout()); + treeViewer = new TreeViewer(this, SWT.NONE); + + tree = treeViewer.getTree(); + tree.setLinesVisible(true); + tree.setHeaderVisible(true); + + TreeColumn propertyColumn = new TreeColumn(tree, SWT.NONE); + propertyColumn.setText("Property"); + TreeColumn valueColumn = new TreeColumn(tree, SWT.NONE); + valueColumn.setText("Value"); + + model = ComponentRegistry.getTreeViewModel(); + ContentProvider contentProvider = new ContentProvider(); + treeViewer.setContentProvider(contentProvider); + treeViewer.setLabelProvider(contentProvider); + treeViewer.setInput(model); + model.addTreeChangeListener(this); + + new TreeColumnResizer(this, propertyColumn, valueColumn); + } + + + public void selectionChanged() { + synchronized (this) { + selectedNode = model.getSelection(); + } + doRefresh(); + } + + public void treeChanged() { + synchronized (this) { + selectedNode = model.getSelection(); + } + doRefresh(); + } + + public void viewportChanged() { + // pass + } + + public void zoomChanged() { + // pass + } + + private void doRefresh() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + treeViewer.refresh(); + } + }); + } +} 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 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/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java new file mode 100644 index 0000000..83a2b0d --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/TreeViewOverview.java @@ -0,0 +1,316 @@ +/* + * 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 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.MouseEvent; +import org.eclipse.swt.events.MouseListener; +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; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +public class TreeViewOverview extends Canvas implements TreeChangeListener { + + private TreeViewModel model; + + private DrawableViewNode tree; + + private Rectangle viewport; + + private Transform transform; + + private Transform inverse; + + private Rectangle bounds = new Rectangle(); + + private double scale; + + private boolean dragging = false; + + public TreeViewOverview(Composite parent) { + super(parent, SWT.NONE); + + model = ComponentRegistry.getTreeViewModel(); + model.addTreeChangeListener(this); + + addPaintListener(paintListener); + addMouseListener(mouseListener); + addMouseMoveListener(mouseMoveListener); + 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(TreeViewOverview.this); + transform.dispose(); + inverse.dispose(); + } + }; + + private MouseListener mouseListener = new MouseListener() { + + public void mouseDoubleClick(MouseEvent e) { + // pass + } + + public void mouseDown(MouseEvent e) { + boolean redraw = false; + synchronized (TreeViewOverview.this) { + if (tree != null && viewport != null) { + dragging = true; + redraw = true; + handleMouseEvent(transformPoint(e.x, e.y)); + } + } + if (redraw) { + model.removeTreeChangeListener(TreeViewOverview.this); + model.setViewport(viewport); + model.addTreeChangeListener(TreeViewOverview.this); + doRedraw(); + } + } + + public void mouseUp(MouseEvent e) { + boolean redraw = false; + synchronized (TreeViewOverview.this) { + if (tree != null && viewport != null) { + dragging = false; + redraw = true; + handleMouseEvent(transformPoint(e.x, e.y)); + setBounds(); + setTransform(); + } + } + if (redraw) { + model.removeTreeChangeListener(TreeViewOverview.this); + model.setViewport(viewport); + model.addTreeChangeListener(TreeViewOverview.this); + doRedraw(); + } + } + + }; + + private MouseMoveListener mouseMoveListener = new MouseMoveListener() { + public void mouseMove(MouseEvent e) { + boolean moved = false; + synchronized (TreeViewOverview.this) { + if (dragging) { + moved = true; + handleMouseEvent(transformPoint(e.x, e.y)); + } + } + if (moved) { + model.removeTreeChangeListener(TreeViewOverview.this); + model.setViewport(viewport); + model.addTreeChangeListener(TreeViewOverview.this); + doRedraw(); + } + } + }; + + private void handleMouseEvent(Point pt) { + viewport.x = pt.x - viewport.width / 2; + viewport.y = pt.y - viewport.height / 2; + if (viewport.x < bounds.x) { + viewport.x = bounds.x; + } + if (viewport.y < bounds.y) { + viewport.y = bounds.y; + } + if (viewport.x + viewport.width > bounds.x + bounds.width) { + viewport.x = bounds.x + bounds.width - viewport.width; + } + if (viewport.y + viewport.height > bounds.y + bounds.height) { + viewport.y = bounds.y + bounds.height - viewport.height; + } + } + + private Point transformPoint(double x, double y) { + float[] pt = { + (float) x, (float) y + }; + inverse.transform(pt); + return new Point(pt[0], pt[1]); + } + + private Listener resizeListener = new Listener() { + public void handleEvent(Event arg0) { + synchronized (TreeViewOverview.this) { + setTransform(); + } + doRedraw(); + } + }; + + private PaintListener paintListener = new PaintListener() { + public void paintControl(PaintEvent e) { + 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, 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, 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().asyncExec(new Runnable() { + public void run() { + redraw(); + } + }); + } + + public void treeChanged() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + tree = model.getTree(); + setBounds(); + setTransform(); + } + } + }); + doRedraw(); + } + + private void setBounds() { + if (viewport != null && tree != null) { + bounds.x = Math.min(viewport.x, tree.bounds.x); + bounds.y = Math.min(viewport.y, tree.bounds.y); + bounds.width = + Math.max(viewport.x + viewport.width, tree.bounds.x + tree.bounds.width) + - bounds.x; + bounds.height = + Math.max(viewport.y + viewport.height, tree.bounds.y + tree.bounds.height) + - bounds.y; + } + } + + private void setTransform() { + if (viewport != null && tree != null) { + + transform.identity(); + inverse.identity(); + final Point size = new Point(); + 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); + inverse.translate((float) -bounds.x, (float) -bounds.y); + if (size.x / bounds.width < size.y / bounds.height) { + transform.translate(0, (float) (size.y / scale - bounds.height) / 2); + inverse.translate(0, (float) (size.y / scale - bounds.height) / 2); + } else { + transform.translate((float) (size.x / scale - bounds.width) / 2, 0); + inverse.translate((float) (size.x / scale - bounds.width) / 2, 0); + } + inverse.invert(); + } + } + + public void viewportChanged() { + Display.getDefault().syncExec(new Runnable() { + public void run() { + synchronized (this) { + viewport = model.getViewport(); + setBounds(); + setTransform(); + } + } + }); + doRedraw(); + } + + public void zoomChanged() { + viewportChanged(); + } + + public void selectionChanged() { + // pass + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java new file mode 100644 index 0000000..fccc3ba --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/DrawableViewNode.java @@ -0,0 +1,266 @@ +/* + * 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.util; + +import com.android.hierarchyviewerlib.device.ViewNode; + +import java.util.ArrayList; + +public class DrawableViewNode { + public ViewNode viewNode; + + public final ArrayList children = new ArrayList(); + + public final static int NODE_HEIGHT = 110; + + public final static int NODE_WIDTH = 170; + + public final static int CONTENT_LEFT_RIGHT_PADDING = 3; + + public final static int CONTENT_TOP_BOTTOM_PADDING = 7; + + 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; + + public int treeHeight; + + public int treeWidth; + + public boolean leaf; + + public DrawableViewNode parent; + + public int left; + + public double top; + + public int topSpacing; + + public int bottomSpacing; + + public boolean treeDrawn; + + public static class Rectangle { + public double x, y, width, height; + + public Rectangle() { + + } + + public Rectangle(Rectangle other) { + this.x = other.x; + this.y = other.y; + this.width = other.width; + this.height = other.height; + } + + public Rectangle(double x, double y, double width, double height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + @Override + public String toString() { + return "{" + x + ", " + y + ", " + width + ", " + height + "}"; + } + + } + + public static class Point { + public double x, y; + + public Point() { + } + + public Point(double x, double y) { + 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; + leaf = true; + } else { + leaf = false; + int N = viewNode.children.size(); + treeHeight = 0; + treeWidth = 0; + for (int i = 0; i < N; i++) { + DrawableViewNode child = new DrawableViewNode(viewNode.children.get(i)); + children.add(child); + child.parent = this; + treeHeight += child.treeHeight; + treeWidth = Math.max(treeWidth, child.treeWidth); + if (i != 0) { + DrawableViewNode prevChild = children.get(i - 1); + if (prevChild.leaf && child.leaf) { + treeHeight += LEAF_NODE_SPACING; + prevChild.bottomSpacing = LEAF_NODE_SPACING; + child.topSpacing = LEAF_NODE_SPACING; + } else { + treeHeight += NON_LEAF_NODE_SPACING; + prevChild.bottomSpacing = NON_LEAF_NODE_SPACING; + child.topSpacing = NON_LEAF_NODE_SPACING; + } + } + treeDrawn |= child.treeDrawn; + } + treeWidth += NODE_WIDTH + PARENT_CHILD_SPACING; + } + } + + public void setLeft() { + if (parent == null) { + left = PADDING; + bounds.x = 0; + bounds.width = treeWidth + 2 * PADDING; + } else { + left = parent.left + NODE_WIDTH + PARENT_CHILD_SPACING; + } + int N = children.size(); + for (int i = 0; i < N; i++) { + children.get(i).setLeft(); + } + } + + public void placeRoot() { + top = PADDING + (treeHeight - NODE_HEIGHT) / 2.0; + double currentTop = PADDING; + int N = children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode child = children.get(i); + child.place(currentTop, top - currentTop); + currentTop += child.treeHeight + child.bottomSpacing; + } + bounds.y = 0; + bounds.height = treeHeight + 2 * PADDING; + } + + private void place(double treeTop, double rootDistance) { + if (treeHeight <= rootDistance) { + top = treeTop + treeHeight - NODE_HEIGHT; + } else if (rootDistance <= -NODE_HEIGHT) { + top = treeTop; + } else { + if (children.size() == 0) { + top = treeTop; + } else { + top = + rootDistance + treeTop - NODE_HEIGHT + (2.0 * NODE_HEIGHT) + / (treeHeight + NODE_HEIGHT) * (treeHeight - rootDistance); + } + } + int N = children.size(); + double currentTop = treeTop; + for (int i = 0; i < N; i++) { + DrawableViewNode child = children.get(i); + child.place(currentTop, rootDistance); + currentTop += child.treeHeight + child.bottomSpacing; + rootDistance -= child.treeHeight + child.bottomSpacing; + } + } + + public DrawableViewNode getSelected(double x, double y) { + if (x >= left && x < left + NODE_WIDTH && y >= top && y <= top + NODE_HEIGHT) { + return this; + } + int N = children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode selected = children.get(i).getSelected(x, y); + if (selected != null) { + return selected; + } + } + return null; + } + + /* + * Moves the node the specified distance up. + */ + public void move(double distance) { + top -= distance; + + // Get the root + DrawableViewNode root = this; + while (root.parent != null) { + root = root.parent; + } + + // Figure out the new tree top. + double treeTop; + if (top + NODE_HEIGHT <= root.top) { + treeTop = top + NODE_HEIGHT - treeHeight; + } else if (top >= root.top + NODE_HEIGHT) { + treeTop = top; + } else { + if (leaf) { + treeTop = top; + } else { + double distanceRatio = 1 - (root.top + NODE_HEIGHT - top) / (2.0 * NODE_HEIGHT); + treeTop = root.top - treeHeight + distanceRatio * (treeHeight + NODE_HEIGHT); + } + } + // Go up the tree and figure out the tree top. + DrawableViewNode node = this; + while (node.parent != null) { + int index = node.viewNode.index; + for (int i = 0; i < index; i++) { + DrawableViewNode sibling = node.parent.children.get(i); + treeTop -= sibling.treeHeight + sibling.bottomSpacing; + } + node = node.parent; + } + + // Update the bounds. + root.bounds.y = Math.min(root.top - PADDING, treeTop - PADDING); + root.bounds.height = + Math.max(treeTop + root.treeHeight + PADDING, root.top + NODE_HEIGHT + PADDING) + - root.bounds.y; + // Place all the children of the root + double currentTop = treeTop; + int N = root.children.size(); + for (int i = 0; i < N; i++) { + DrawableViewNode child = root.children.get(i); + child.place(currentTop, root.top - currentTop); + currentTop += child.treeHeight + child.bottomSpacing; + } + + } +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java new file mode 100644 index 0000000..ad18540 --- /dev/null +++ b/hierarchyviewer2/libs/hierarchyviewerlib/src/com/android/hierarchyviewerlib/ui/util/TreeColumnResizer.java @@ -0,0 +1,109 @@ +/* + * 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.util; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.TreeColumn; + +public class TreeColumnResizer { + + private TreeColumn column1; + private TreeColumn column2; + + 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; + this.column2 = column2; + control.addListener(SWT.Resize, resizeListener); + column1.addListener(SWT.Resize, column1ResizeListener); + column2.setResizable(false); + } + + private Listener resizeListener = new Listener() { + public void handleEvent(Event e) { + if (column1Width == 0 && column2Width == 0) { + column1Width = (control.getBounds().width - 18) / 2; + column2Width = (control.getBounds().width - 18) / 2; + } else { + int dif = control.getBounds().width - 18 - (column1Width + column2Width); + int columnDif = Math.abs(column1Width - column2Width); + int mainColumnChange = Math.min(Math.abs(dif), columnDif); + int left = Math.max(0, Math.abs(dif) - columnDif); + if (dif < 0) { + if (column1Width > column2Width) { + column1Width -= mainColumnChange; + } else { + column2Width -= mainColumnChange; + } + column1Width -= left / 2; + column2Width -= left - left / 2; + } else { + if (column1Width > column2Width) { + column2Width += mainColumnChange; + } else { + column1Width += mainColumnChange; + } + column1Width += left / 2; + column2Width += left - left / 2; + } + } + column1.removeListener(SWT.Resize, column1ResizeListener); + column1.setWidth(column1Width); + column2.setWidth(column2Width); + column1.addListener(SWT.Resize, column1ResizeListener); + } + }; + + private Listener column1ResizeListener = new Listener() { + public void handleEvent(Event e) { + int widthDif = column1Width - column1.getWidth(); + column1Width -= widthDif; + column2Width += widthDif; + 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); + } + column2.setWidth(column2Width); + } + }; +} diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png new file mode 100644 index 0000000..b52a342 Binary files /dev/null and b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/green.png differ diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png new file mode 100644 index 0000000..338c2d9 Binary files /dev/null and b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/red.png differ diff --git a/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png new file mode 100644 index 0000000..b6fadac Binary files /dev/null and b/hierarchyviewer2/libs/hierarchyviewerlib/src/resources/images/yellow.png differ diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath b/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath deleted file mode 100644 index 4bddd6c..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - 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 @@ - - - hierarchyvieweruilib - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - 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/DeviceSelector.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java deleted file mode 100644 index 49eb418..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/DeviceSelector.java +++ /dev/null @@ -1,266 +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.ddmlib.IDevice; -import com.android.ddmuilib.ImageLoader; -import com.android.hierarchyviewerlib.ComponentRegistry; -import com.android.hierarchyviewerlib.device.Window; -import com.android.hierarchyviewerlib.models.DeviceSelectionModel; -import com.android.hierarchyviewerlib.models.DeviceSelectionModel.WindowChangeListener; - -import org.eclipse.jface.viewers.IFontProvider; -import org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.FontData; -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.Tree; -import org.eclipse.swt.widgets.TreeItem; - -public class DeviceSelector extends Composite implements WindowChangeListener, SelectionListener { - private TreeViewer treeViewer; - - private Tree tree; - - private DeviceSelectionModel model; - - private Font boldFont; - - private Image deviceImage; - - private Image emulatorImage; - - private final static int ICON_WIDTH = 16; - - private class ContentProvider implements ITreeContentProvider, ILabelProvider, IFontProvider { - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof IDevice) { - Window[] list = model.getWindows((IDevice) parentElement); - if (list != null) { - return list; - } - } - return new Object[0]; - } - - public Object getParent(Object element) { - if (element instanceof Window) { - return ((Window) element).getDevice(); - } - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof IDevice) { - Window[] list = model.getWindows((IDevice) element); - if (list != null) { - return list.length != 0; - } - } - return false; - } - - public Object[] getElements(Object inputElement) { - if (inputElement instanceof DeviceSelectionModel) { - return model.getDevices(); - } - return new Object[0]; - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - - public Image getImage(Object element) { - if (element instanceof IDevice) { - if (((IDevice) element).isEmulator()) { - return emulatorImage; - } - return deviceImage; - } - return null; - } - - public String getText(Object element) { - if (element instanceof IDevice) { - return ((IDevice) element).toString(); - } else if (element instanceof Window) { - return ((Window) element).getTitle(); - } - return null; - } - - public Font getFont(Object element) { - if (element instanceof Window) { - int focusedWindow = model.getFocusedWindow(((Window) element).getDevice()); - if (focusedWindow == ((Window) element).getHashCode()) { - return boldFont; - } - } - return null; - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - public DeviceSelector(Composite parent) { - super(parent, SWT.NONE); - setLayout(new FillLayout()); - treeViewer = new TreeViewer(this, SWT.SINGLE); - treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); - - tree = treeViewer.getTree(); - tree.setLinesVisible(true); - tree.addSelectionListener(this); - - addDisposeListener(disposeListener); - - loadResources(); - - model = ComponentRegistry.getDeviceSelectionModel(); - ContentProvider contentProvider = new ContentProvider(); - treeViewer.setContentProvider(contentProvider); - treeViewer.setLabelProvider(contentProvider); - treeViewer.setInput(model); - model.addWindowChangeListener(this); - } - - public void loadResources() { - Display display = Display.getDefault(); - Font systemFont = display.getSystemFont(); - FontData[] fontData = systemFont.getFontData(); - FontData[] newFontData = new FontData[fontData.length]; - for (int i = 0; i < fontData.length; i++) { - newFontData[i] = - new FontData(fontData[i].getName(), fontData[i].getHeight(), fontData[i] - .getStyle() - | SWT.BOLD); - } - boldFont = new Font(Display.getDefault(), newFontData); - - ImageLoader loader = ImageLoader.getDdmUiLibLoader(); - deviceImage = - loader.loadImage(display, "device.png", ICON_WIDTH, ICON_WIDTH, display - .getSystemColor(SWT.COLOR_RED)); - - emulatorImage = - loader.loadImage(display, "emulator.png", ICON_WIDTH, ICON_WIDTH, display - .getSystemColor(SWT.COLOR_BLUE)); - } - - private DisposeListener disposeListener = new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - model.removeWindowChangeListener(DeviceSelector.this); - boldFont.dispose(); - } - }; - - @Override - public boolean setFocus() { - return tree.setFocus(); - } - - public void deviceConnected(final IDevice device) { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - treeViewer.refresh(); - treeViewer.setExpandedState(device, true); - } - }); - } - - public void deviceChanged(final IDevice device) { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - TreeSelection selection = (TreeSelection) treeViewer.getSelection(); - treeViewer.refresh(device); - if (selection.getFirstElement() instanceof Window - && ((Window) selection.getFirstElement()).getDevice() == device) { - treeViewer.setSelection(selection, true); - } - } - }); - } - - public void deviceDisconnected(final IDevice device) { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - treeViewer.refresh(); - } - }); - } - - public void focusChanged(final IDevice device) { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - TreeSelection selection = (TreeSelection) treeViewer.getSelection(); - treeViewer.refresh(device); - if (selection.getFirstElement() instanceof Window - && ((Window) selection.getFirstElement()).getDevice() == device) { - treeViewer.setSelection(selection, true); - } - } - }); - } - - public void widgetDefaultSelected(SelectionEvent e) { - // TODO: Double click to open view hierarchy - Object selection = ((TreeItem) e.item).getData(); - if (selection instanceof IDevice) { - ComponentRegistry.getDirector().loadPixelPerfectData((IDevice) selection); - } else if (selection instanceof Window) { - ComponentRegistry.getDirector().loadViewTreeData((Window) selection); - } - } - - public void widgetSelected(SelectionEvent e) { - Object selection = ((TreeItem) e.item).getData(); - if (selection instanceof IDevice) { - model.setSelection((IDevice) selection, null); - } else if (selection instanceof Window) { - model.setSelection(((Window) selection).getDevice(), (Window) selection); - } - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java deleted file mode 100644 index 8165c0b..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfect.java +++ /dev/null @@ -1,307 +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.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; -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.PaintEvent; -import org.eclipse.swt.events.PaintListener; -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.RGB; -import org.eclipse.swt.widgets.Canvas; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; - -public class PixelPerfect extends ScrolledComposite implements ImageChangeListener { - private Canvas canvas; - - private PixelPerfectModel model; - - private Image image; - - private Color crosshairColor; - - private Color marginColor; - - private Color borderColor; - - private Color paddingColor; - - private int width; - - private int height; - - private Point crosshairLocation; - - private ViewNode selectedNode; - - public PixelPerfect(Composite parent) { - super(parent, SWT.H_SCROLL | SWT.V_SCROLL); - canvas = new Canvas(this, SWT.NONE); - setContent(canvas); - setExpandHorizontal(true); - setExpandVertical(true); - model = ComponentRegistry.getPixelPerfectModel(); - model.addImageChangeListener(this); - - canvas.addPaintListener(paintListener); - canvas.addMouseListener(mouseListener); - canvas.addMouseMoveListener(mouseMoveListener); - - addDisposeListener(disposeListener); - - crosshairColor = new Color(Display.getDefault(), new RGB(0, 255, 255)); - borderColor = new Color(Display.getDefault(), new RGB(255, 0, 0)); - marginColor = new Color(Display.getDefault(), new RGB(0, 255, 0)); - paddingColor = new Color(Display.getDefault(), new RGB(0, 0, 255)); - } - - private DisposeListener disposeListener = new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - model.removeImageChangeListener(PixelPerfect.this); - if (image != null) { - image.dispose(); - } - crosshairColor.dispose(); - borderColor.dispose(); - paddingColor.dispose(); - } - }; - - @Override - public boolean setFocus() { - return canvas.setFocus(); - } - - private MouseListener mouseListener = new MouseListener() { - - public void mouseDoubleClick(MouseEvent e) { - // pass - } - - public void mouseDown(MouseEvent e) { - handleMouseEvent(e); - } - - public void mouseUp(MouseEvent e) { - handleMouseEvent(e); - } - - }; - - private MouseMoveListener mouseMoveListener = new MouseMoveListener() { - public void mouseMove(MouseEvent e) { - if (e.stateMask != 0) { - handleMouseEvent(e); - } - } - }; - - private void handleMouseEvent(MouseEvent e) { - synchronized (this) { - if (image == null) { - return; - } - int leftOffset = canvas.getSize().x / 2 - width / 2; - int topOffset = canvas.getSize().y / 2 - height / 2; - e.x -= leftOffset; - e.y -= topOffset; - e.x = Math.max(e.x, 0); - e.x = Math.min(e.x, width - 1); - e.y = Math.max(e.y, 0); - e.y = Math.min(e.y, height - 1); - } - model.setCrosshairLocation(e.x, e.y); - } - - private PaintListener paintListener = new PaintListener() { - public void paintControl(PaintEvent e) { - synchronized (this) { - e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - e.gc.fillRectangle(0, 0, canvas.getSize().x, canvas.getSize().y); - if (image != null) { - // Let's be cool and put it in the center... - int leftOffset = canvas.getSize().x / 2 - width / 2; - int topOffset = canvas.getSize().y / 2 - height / 2; - e.gc.drawImage(image, leftOffset, topOffset); - if (selectedNode != null) { - // There are a few quirks here. First of all, margins - // are sometimes negative or positive numbers... Yet, - // they are always treated as positive. - // Secondly, if the screen is in landscape mode, the - // coordinates are backwards. - int leftShift = 0; - int topShift = 0; - int nodeLeft = selectedNode.left; - int nodeTop = selectedNode.top; - int nodeWidth = selectedNode.width; - int nodeHeight = selectedNode.height; - int nodeMarginLeft = selectedNode.marginLeft; - int nodeMarginTop = selectedNode.marginTop; - int nodeMarginRight = selectedNode.marginRight; - int nodeMarginBottom = selectedNode.marginBottom; - int nodePadLeft = selectedNode.paddingLeft; - int nodePadTop = selectedNode.paddingTop; - int nodePadRight = selectedNode.paddingRight; - int nodePadBottom = selectedNode.paddingBottom; - ViewNode cur = selectedNode; - while (cur.parent != null) { - leftShift += cur.parent.left; - topShift += cur.parent.top; - cur = cur.parent; - } - - // Everything is sideways. - if (cur.width > cur.height) { - e.gc.setForeground(paddingColor); - e.gc.drawRectangle(leftOffset + width - nodeTop - topShift - nodeHeight - + nodePadBottom, - topOffset + leftShift + nodeLeft + nodePadLeft, nodeHeight - - nodePadBottom - nodePadTop, nodeWidth - nodePadRight - - nodePadLeft); - e.gc.setForeground(marginColor); - e.gc.drawRectangle(leftOffset + width - nodeTop - topShift - nodeHeight - - nodeMarginBottom, topOffset + leftShift + nodeLeft - - nodeMarginLeft, - nodeHeight + nodeMarginBottom + nodeMarginTop, nodeWidth - + nodeMarginRight + nodeMarginLeft); - e.gc.setForeground(borderColor); - e.gc.drawRectangle( - leftOffset + width - nodeTop - topShift - nodeHeight, topOffset - + leftShift + nodeLeft, nodeHeight, nodeWidth); - } else { - e.gc.setForeground(paddingColor); - e.gc.drawRectangle(leftOffset + leftShift + nodeLeft + nodePadLeft, - topOffset + topShift + nodeTop + nodePadTop, nodeWidth - - nodePadRight - nodePadLeft, nodeHeight - - nodePadBottom - nodePadTop); - e.gc.setForeground(marginColor); - e.gc.drawRectangle(leftOffset + leftShift + nodeLeft - nodeMarginLeft, - topOffset + topShift + nodeTop - nodeMarginTop, nodeWidth - + nodeMarginRight + nodeMarginLeft, nodeHeight - + nodeMarginBottom + nodeMarginTop); - e.gc.setForeground(borderColor); - e.gc.drawRectangle(leftOffset + leftShift + nodeLeft, topOffset - + topShift + nodeTop, nodeWidth, nodeHeight); - } - } - if (crosshairLocation != null) { - e.gc.setForeground(crosshairColor); - e.gc.drawLine(leftOffset, topOffset + crosshairLocation.y, leftOffset - + width - 1, topOffset + crosshairLocation.y); - e.gc.drawLine(leftOffset + crosshairLocation.x, topOffset, leftOffset - + crosshairLocation.x, topOffset + height - 1); - } - } - } - } - }; - - private void doRedraw() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - canvas.redraw(); - } - }); - } - - 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; - - } else { - if (image != null) { - image.dispose(); - image = null; - } - width = 0; - height = 0; - } - Display.getDefault().asyncExec(new Runnable() { - public void run() { - setMinSize(width, height); - } - }); - } - - public void imageLoaded() { - synchronized (this) { - loadImage(); - crosshairLocation = model.getCrosshairLocation(); - selectedNode = model.getSelected(); - } - doRedraw(); - } - - public void imageChanged() { - synchronized (this) { - loadImage(); - } - doRedraw(); - } - - public void crosshairMoved() { - synchronized (this) { - crosshairLocation = model.getCrosshairLocation(); - } - doRedraw(); - } - - public void selectionChanged() { - synchronized (this) { - selectedNode = model.getSelected(); - } - doRedraw(); - } - - public void focusChanged() { - synchronized (this) { - loadImage(); - selectedNode = model.getSelected(); - } - doRedraw(); - } - - public void zoomChanged() { - // pass - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java deleted file mode 100644 index f0402f3..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectLoupe.java +++ /dev/null @@ -1,288 +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.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; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.MouseWheelListener; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.graphics.Color; -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.RGB; -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; - -public class PixelPerfectLoupe extends Canvas implements ImageChangeListener { - private PixelPerfectModel model; - - private Image image; - - private Image grid; - - private Color crosshairColor; - - private int width; - - private int height; - - private Point crosshairLocation; - - private int zoom; - - private Transform transform; - - private int canvasWidth; - - private int canvasHeight; - - public PixelPerfectLoupe(Composite parent) { - super(parent, SWT.NONE); - model = ComponentRegistry.getPixelPerfectModel(); - model.addImageChangeListener(this); - - addPaintListener(paintListener); - addMouseListener(mouseListener); - addMouseWheelListener(mouseWheelListener); - addDisposeListener(disposeListener); - - crosshairColor = new Color(Display.getDefault(), new RGB(255, 94, 254)); - - transform = new Transform(Display.getDefault()); - } - - private DisposeListener disposeListener = new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - model.removeImageChangeListener(PixelPerfectLoupe.this); - if (image != null) { - image.dispose(); - } - crosshairColor.dispose(); - transform.dispose(); - if (grid != null) { - grid.dispose(); - } - } - }; - - private MouseListener mouseListener = new MouseListener() { - - public void mouseDoubleClick(MouseEvent e) { - // pass - } - - public void mouseDown(MouseEvent e) { - handleMouseEvent(e); - } - - public void mouseUp(MouseEvent e) { - // - } - - }; - - private MouseWheelListener mouseWheelListener = new MouseWheelListener() { - public void mouseScrolled(MouseEvent e) { - int newZoom = -1; - synchronized (this) { - if (image != null && crosshairLocation != null) { - if (e.count > 0) { - newZoom = zoom + 1; - } else { - newZoom = zoom - 1; - } - } - } - if (newZoom != -1) { - model.setZoom(newZoom); - } - } - }; - - private void handleMouseEvent(MouseEvent e) { - int newX = -1; - int newY = -1; - synchronized (this) { - if (image == null) { - return; - } - int zoomedX = -crosshairLocation.x * zoom - zoom / 2 + getBounds().width / 2; - int zoomedY = -crosshairLocation.y * zoom - zoom / 2 + getBounds().height / 2; - int x = (e.x - zoomedX) / zoom; - int y = (e.y - zoomedY) / zoom; - if (x >= 0 && x < width && y >= 0 && y < height) { - newX = x; - newY = y; - } - } - if (newX != -1) { - model.setCrosshairLocation(newX, newY); - } - } - - private PaintListener paintListener = new PaintListener() { - public void paintControl(PaintEvent e) { - synchronized (this) { - e.gc.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); - e.gc.fillRectangle(0, 0, getSize().x, getSize().y); - if (image != null && crosshairLocation != null) { - int zoomedX = -crosshairLocation.x * zoom - zoom / 2 + getBounds().width / 2; - int zoomedY = -crosshairLocation.y * zoom - zoom / 2 + getBounds().height / 2; - transform.translate(zoomedX, zoomedY); - transform.scale(zoom, zoom); - e.gc.setInterpolation(SWT.NONE); - e.gc.setTransform(transform); - e.gc.drawImage(image, 0, 0); - transform.identity(); - e.gc.setTransform(transform); - - // If the size of the canvas has changed, we need to make - // another grid. - if (grid != null - && (canvasWidth != getBounds().width || canvasHeight != getBounds().height)) { - grid.dispose(); - grid = null; - } - canvasWidth = getBounds().width; - canvasHeight = getBounds().height; - if (grid == null) { - // Make a transparent image; - ImageData imageData = - new ImageData(canvasWidth + zoom + 1, canvasHeight + zoom + 1, 1, - new PaletteData(new RGB[] { - new RGB(0, 0, 0) - })); - imageData.transparentPixel = 0; - - // Draw the grid. - grid = new Image(Display.getDefault(), imageData); - GC gc = new GC(grid); - gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE)); - for (int x = 0; x <= canvasWidth + zoom; x += zoom) { - gc.drawLine(x, 0, x, canvasHeight + zoom); - } - for (int y = 0; y <= canvasHeight + zoom; y += zoom) { - gc.drawLine(0, y, canvasWidth + zoom, y); - } - gc.dispose(); - } - - e.gc.setClipping(new Rectangle(zoomedX, zoomedY, width * zoom + 1, height - * zoom + 1)); - e.gc.setAlpha(76); - e.gc.drawImage(grid, (canvasWidth / 2 - zoom / 2) % zoom - zoom, - (canvasHeight / 2 - zoom / 2) % zoom - zoom); - e.gc.setAlpha(255); - - e.gc.setForeground(crosshairColor); - e.gc.drawLine(0, canvasHeight / 2, canvasWidth - 1, canvasHeight / 2); - e.gc.drawLine(canvasWidth / 2, 0, canvasWidth / 2, canvasHeight - 1); - } - } - } - }; - - private void doRedraw() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - redraw(); - } - }); - } - - 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; - } else { - if (image != null) { - image.dispose(); - image = null; - } - width = 0; - height = 0; - } - } - - public void imageLoaded() { - synchronized (this) { - loadImage(); - crosshairLocation = model.getCrosshairLocation(); - zoom = model.getZoom(); - } - doRedraw(); - } - - public void imageChanged() { - synchronized (this) { - loadImage(); - } - doRedraw(); - } - - public void crosshairMoved() { - synchronized (this) { - crosshairLocation = model.getCrosshairLocation(); - } - doRedraw(); - } - - public void selectionChanged() { - // pass - } - - public void focusChanged() { - imageChanged(); - } - - 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(); - } - } - doRedraw(); - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java deleted file mode 100644 index 2c30857..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PixelPerfectTree.java +++ /dev/null @@ -1,216 +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.ddmuilib.ImageLoader; -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 org.eclipse.jface.viewers.ILabelProvider; -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeSelection; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -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.Tree; -import org.eclipse.swt.widgets.TreeColumn; - -import java.util.List; - -public class PixelPerfectTree extends Composite implements ImageChangeListener, SelectionListener { - - private TreeViewer treeViewer; - - private Tree tree; - - private PixelPerfectModel model; - - private Image folderImage; - - private Image fileImage; - - private class ContentProvider implements ITreeContentProvider, ILabelProvider { - public Object[] getChildren(Object element) { - if (element instanceof ViewNode) { - List children = ((ViewNode) element).children; - return children.toArray(new ViewNode[children.size()]); - } - return null; - } - - public Object getParent(Object element) { - if (element instanceof ViewNode) { - return ((ViewNode) element).parent; - } - return null; - } - - public boolean hasChildren(Object element) { - if (element instanceof ViewNode) { - return ((ViewNode) element).children.size() != 0; - } - return false; - } - - public Object[] getElements(Object element) { - if (element instanceof PixelPerfectModel) { - ViewNode viewNode = ((PixelPerfectModel) element).getViewNode(); - if (viewNode == null) { - return new Object[0]; - } - return new Object[] { - viewNode - }; - } - return new Object[0]; - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - - public Image getImage(Object element) { - if (element instanceof ViewNode) { - if (hasChildren(element)) { - return folderImage; - } - return fileImage; - } - return null; - } - - public String getText(Object element) { - if (element instanceof ViewNode) { - return ((ViewNode) element).name; - } - return null; - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - public PixelPerfectTree(Composite parent) { - super(parent, SWT.NONE); - setLayout(new FillLayout()); - treeViewer = new TreeViewer(this, SWT.SINGLE); - treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); - - tree = treeViewer.getTree(); - tree.addSelectionListener(this); - - loadResources(); - - addDisposeListener(disposeListener); - - model = ComponentRegistry.getPixelPerfectModel(); - ContentProvider contentProvider = new ContentProvider(); - treeViewer.setContentProvider(contentProvider); - treeViewer.setLabelProvider(contentProvider); - treeViewer.setInput(model); - model.addImageChangeListener(this); - - } - - public void loadResources() { - ImageLoader loader = ImageLoader.getDdmUiLibLoader(); - fileImage = loader.loadImage("file.png", Display.getDefault()); - - folderImage = loader.loadImage("folder.png", Display.getDefault()); - } - - private DisposeListener disposeListener = new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - model.removeImageChangeListener(PixelPerfectTree.this); - fileImage.dispose(); - folderImage.dispose(); - } - }; - - @Override - public boolean setFocus() { - return tree.setFocus(); - } - - public void imageLoaded() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - treeViewer.refresh(); - treeViewer.expandAll(); - } - }); - } - - public void imageChanged() { - // pass - } - - public void crosshairMoved() { - // pass - } - - public void selectionChanged() { - // pass - } - - public void focusChanged() { - imageLoaded(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - // pass - } - - public void widgetSelected(SelectionEvent e) { - // To combat phantom selection... - if (((TreeSelection) treeViewer.getSelection()).isEmpty()) { - model.setSelected(null); - } else { - model.setSelected((ViewNode) e.item.getData()); - } - } - - public void zoomChanged() { - // pass - } - -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java deleted file mode 100644 index 400318c..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/ProfileViewer.java +++ /dev/null @@ -1,179 +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.hierarchyvieweruilib.util.TreeColumnResizer; - -import org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -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.Tree; -import org.eclipse.swt.widgets.TreeColumn; - -import java.text.DecimalFormat; - -public class ProfileViewer extends Composite implements TreeChangeListener { - private TreeViewModel model; - - private TreeViewer treeViewer; - - private Tree tree; - - private DrawableViewNode selectedNode; - - private class ContentProvider implements ITreeContentProvider, ITableLabelProvider { - - public Object[] getChildren(Object parentElement) { - synchronized (ProfileViewer.this) { - return new Object[0]; - } - } - - public Object getParent(Object element) { - synchronized (ProfileViewer.this) { - return new Object[0]; - } - } - - public boolean hasChildren(Object element) { - synchronized (ProfileViewer.this) { - return false; - } - } - - public Object[] getElements(Object inputElement) { - synchronized (ProfileViewer.this) { - if (selectedNode != null && selectedNode.viewNode.measureTime != -1) { - return new String[] { - "measure", "layout", "draw" - }; - } - return new Object[0]; - } - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - - public Image getColumnImage(Object element, int column) { - return null; - } - - public String getColumnText(Object element, int column) { - synchronized (ProfileViewer.this) { - if (selectedNode != null) { - if (column == 0) { - return (String) element; - } else if (column == 1) { - DecimalFormat formatter = new DecimalFormat("0.000"); - if(((String)element).equals("measure")) { - return formatter.format(selectedNode.viewNode.measureTime); - } else if (((String) element).equals("layout")) { - return formatter.format(selectedNode.viewNode.layoutTime); - } else { - return formatter.format(selectedNode.viewNode.drawTime); - } - } - } - return ""; - } - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - public ProfileViewer(Composite parent) { - super(parent, SWT.NONE); - setLayout(new FillLayout()); - treeViewer = new TreeViewer(this, SWT.NONE); - - tree = treeViewer.getTree(); - tree.setLinesVisible(true); - tree.setHeaderVisible(true); - - TreeColumn operationColumn = new TreeColumn(tree, SWT.NONE); - operationColumn.setText("Operation"); - TreeColumn durationColumn = new TreeColumn(tree, SWT.NONE); - durationColumn.setText("Duration (ms)"); - - model = ComponentRegistry.getTreeViewModel(); - ContentProvider contentProvider = new ContentProvider(); - treeViewer.setContentProvider(contentProvider); - treeViewer.setLabelProvider(contentProvider); - treeViewer.setInput(model); - model.addTreeChangeListener(this); - - new TreeColumnResizer(this, operationColumn, durationColumn); - } - - public void selectionChanged() { - synchronized (this) { - selectedNode = model.getSelection(); - } - doRefresh(); - } - - public void treeChanged() { - synchronized (this) { - selectedNode = model.getSelection(); - } - doRefresh(); - } - - public void viewportChanged() { - // pass - } - - public void zoomChanged() { - // pass - } - - private void doRefresh() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - treeViewer.refresh(); - } - }); - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java deleted file mode 100644 index da4997b..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/PropertyViewer.java +++ /dev/null @@ -1,232 +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.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 org.eclipse.jface.viewers.ILabelProviderListener; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.swt.SWT; -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; - -import java.util.ArrayList; - -public class PropertyViewer extends Composite implements TreeChangeListener { - private TreeViewModel model; - - private TreeViewer treeViewer; - - private Tree tree; - - private DrawableViewNode selectedNode; - - private class ContentProvider implements ITreeContentProvider, ITableLabelProvider { - - public Object[] getChildren(Object parentElement) { - synchronized (PropertyViewer.this) { - if (selectedNode != null && parentElement instanceof String) { - String category = (String) parentElement; - ArrayList returnValue = new ArrayList(); - for (Property property : selectedNode.viewNode.properties) { - if (category.equals(ViewNode.MISCELLANIOUS)) { - if (property.name.indexOf(':') == -1) { - returnValue.add(property); - } - } else { - if (property.name.startsWith(((String) parentElement) + ":")) { - returnValue.add(property); - } - } - } - return returnValue.toArray(new Property[returnValue.size()]); - } - return new Object[0]; - } - } - - public Object getParent(Object element) { - synchronized (PropertyViewer.this) { - if (selectedNode != null && element instanceof Property) { - if (selectedNode.viewNode.categories.size() == 0) { - return null; - } - String name = ((Property) element).name; - int index = name.indexOf(':'); - if (index == -1) { - return ViewNode.MISCELLANIOUS; - } - return name.substring(0, index); - } - return null; - } - } - - public boolean hasChildren(Object element) { - synchronized (PropertyViewer.this) { - if (selectedNode != null && element instanceof String) { - String category = (String) element; - for (String name : selectedNode.viewNode.namedProperties.keySet()) { - if (category.equals(ViewNode.MISCELLANIOUS)) { - if (name.indexOf(':') == -1) { - return true; - } - } else { - if (name.startsWith(((String) element) + ":")) { - return true; - } - } - } - } - return false; - } - } - - public Object[] getElements(Object inputElement) { - synchronized (PropertyViewer.this) { - if (selectedNode != null && inputElement instanceof TreeViewModel) { - if (selectedNode.viewNode.categories.size() == 0) { - return selectedNode.viewNode.properties - .toArray(new Property[selectedNode.viewNode.properties.size()]); - } else { - return selectedNode.viewNode.categories - .toArray(new String[selectedNode.viewNode.categories.size()]); - } - } - return new Object[0]; - } - } - - public void dispose() { - // pass - } - - public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { - // pass - } - - public Image getColumnImage(Object element, int column) { - return null; - } - - public String getColumnText(Object element, int column) { - synchronized (PropertyViewer.this) { - if (selectedNode != null) { - if (element instanceof String && column == 0) { - String category = (String) element; - return Character.toUpperCase(category.charAt(0)) + category.substring(1); - } else if (element instanceof Property) { - if (column == 0) { - String returnValue = ((Property) element).name; - int index = returnValue.indexOf(':'); - if (index != -1) { - return returnValue.substring(index + 1); - } - return returnValue; - } else if (column == 1) { - return ((Property) element).value; - } - } - } - return ""; - } - } - - public void addListener(ILabelProviderListener listener) { - // pass - } - - public boolean isLabelProperty(Object element, String property) { - // pass - return false; - } - - public void removeListener(ILabelProviderListener listener) { - // pass - } - } - - public PropertyViewer(Composite parent) { - super(parent, SWT.NONE); - setLayout(new FillLayout()); - treeViewer = new TreeViewer(this, SWT.NONE); - - tree = treeViewer.getTree(); - tree.setLinesVisible(true); - tree.setHeaderVisible(true); - - TreeColumn propertyColumn = new TreeColumn(tree, SWT.NONE); - propertyColumn.setText("Property"); - TreeColumn valueColumn = new TreeColumn(tree, SWT.NONE); - valueColumn.setText("Value"); - - model = ComponentRegistry.getTreeViewModel(); - ContentProvider contentProvider = new ContentProvider(); - treeViewer.setContentProvider(contentProvider); - treeViewer.setLabelProvider(contentProvider); - treeViewer.setInput(model); - model.addTreeChangeListener(this); - - new TreeColumnResizer(this, propertyColumn, valueColumn); - } - - - public void selectionChanged() { - synchronized (this) { - selectedNode = model.getSelection(); - } - doRefresh(); - } - - public void treeChanged() { - synchronized (this) { - selectedNode = model.getSelection(); - } - doRefresh(); - } - - public void viewportChanged() { - // pass - } - - public void zoomChanged() { - // pass - } - - private void doRefresh() { - Display.getDefault().asyncExec(new Runnable() { - public void run() { - treeViewer.refresh(); - } - }); - } -} 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(); - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java deleted file mode 100644 index feed1af..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/TreeViewOverview.java +++ /dev/null @@ -1,289 +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.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 TreeViewOverview extends Canvas implements TreeChangeListener { - - private TreeViewModel model; - - private DrawableViewNode tree; - - private Rectangle viewport; - - private Transform transform; - - private Transform inverse; - - private Rectangle bounds = new Rectangle(); - - private double scale; - - private boolean dragging = false; - - public TreeViewOverview(Composite parent) { - super(parent, SWT.NONE); - - model = ComponentRegistry.getTreeViewModel(); - model.addTreeChangeListener(this); - - addPaintListener(paintListener); - addMouseListener(mouseListener); - addMouseMoveListener(mouseMoveListener); - 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(TreeViewOverview.this); - transform.dispose(); - inverse.dispose(); - } - }; - - private MouseListener mouseListener = new MouseListener() { - - public void mouseDoubleClick(MouseEvent e) { - // pass - } - - public void mouseDown(MouseEvent e) { - boolean redraw = false; - synchronized (this) { - if (tree != null && viewport != null) { - dragging = true; - redraw = true; - handleMouseEvent(transformPoint(e.x, e.y)); - } - } - if (redraw) { - model.removeTreeChangeListener(TreeViewOverview.this); - model.setViewport(viewport); - model.addTreeChangeListener(TreeViewOverview.this); - doRedraw(); - } - } - - public void mouseUp(MouseEvent e) { - boolean redraw = false; - synchronized (this) { - if (tree != null && viewport != null) { - dragging = false; - redraw = true; - handleMouseEvent(transformPoint(e.x, e.y)); - setBounds(); - setTransform(); - } - } - if (redraw) { - model.removeTreeChangeListener(TreeViewOverview.this); - model.setViewport(viewport); - model.addTreeChangeListener(TreeViewOverview.this); - doRedraw(); - } - } - - }; - - private MouseMoveListener mouseMoveListener = new MouseMoveListener() { - public void mouseMove(MouseEvent e) { - boolean moved = false; - synchronized (this) { - if (dragging) { - moved = true; - handleMouseEvent(transformPoint(e.x, e.y)); - } - } - if (moved) { - model.removeTreeChangeListener(TreeViewOverview.this); - model.setViewport(viewport); - model.addTreeChangeListener(TreeViewOverview.this); - doRedraw(); - } - } - }; - - private void handleMouseEvent(Point pt) { - viewport.x = pt.x - viewport.width / 2; - viewport.y = pt.y - viewport.height / 2; - if (viewport.x < bounds.x) { - viewport.x = bounds.x; - } - if (viewport.y < bounds.y) { - viewport.y = bounds.y; - } - if (viewport.x + viewport.width > bounds.x + bounds.width) { - viewport.x = bounds.x + bounds.width - viewport.width; - } - if (viewport.y + viewport.height > bounds.y + bounds.height) { - viewport.y = bounds.y + bounds.height - viewport.height; - } - } - - private Point transformPoint(double x, double y) { - float[] pt = { - (float) x, (float) y - }; - inverse.transform(pt); - return new Point(pt[0], pt[1]); - } - - private Listener resizeListener = new Listener() { - public void handleEvent(Event arg0) { - synchronized (this) { - setTransform(); - } - doRedraw(); - } - }; - - 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)); - } - } - }; - - private void paintRecursive(GC gc, DrawableViewNode node) { - 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(); - setBounds(); - setTransform(); - } - doRedraw(); - } - - private void setBounds() { - if (viewport != null && tree != null) { - bounds.x = Math.min(viewport.x, tree.bounds.x); - bounds.y = Math.min(viewport.y, tree.bounds.y); - bounds.width = - Math.max(viewport.x + viewport.width, tree.bounds.x + tree.bounds.width) - - bounds.x; - bounds.height = - Math.max(viewport.y + viewport.height, tree.bounds.y + tree.bounds.height) - - bounds.y; - } - } - - private void setTransform() { - if (viewport != null && tree != null) { - - 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); - transform.scale((float) scale, (float) scale); - inverse.scale((float) scale, (float) scale); - transform.translate((float) -bounds.x, (float) -bounds.y); - inverse.translate((float) -bounds.x, (float) -bounds.y); - if (size.x / bounds.width < size.y / bounds.height) { - transform.translate(0, (float) (size.y / scale - bounds.height) / 2); - inverse.translate(0, (float) (size.y / scale - bounds.height) / 2); - } else { - transform.translate((float) (size.x / scale - bounds.width) / 2, 0); - inverse.translate((float) (size.x / scale - bounds.width) / 2, 0); - } - inverse.invert(); - } - } - - public void viewportChanged() { - synchronized (this) { - viewport = model.getViewport(); - setBounds(); - setTransform(); - } - doRedraw(); - } - - public void zoomChanged() { - viewportChanged(); - } - - public void selectionChanged() { - // pass - } -} diff --git a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java b/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java deleted file mode 100644 index 09851c3..0000000 --- a/hierarchyviewer2/libs/hierarchyvieweruilib/src/com/android/hierarchyvieweruilib/util/TreeColumnResizer.java +++ /dev/null @@ -1,92 +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.util; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Event; -import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.TreeColumn; - -public class TreeColumnResizer { - - private TreeColumn column1; - private TreeColumn column2; - - private Composite control; - private int column1Width; - private int column2Width; - public TreeColumnResizer(Composite control, TreeColumn column1, TreeColumn column2) { - this.control = control; - this.column1 = column1; - this.column2 = column2; - control.addListener(SWT.Resize, resizeListener); - column1.addListener(SWT.Resize, column1ResizeListener); - column2.setResizable(false); - } - - private Listener resizeListener = new Listener() { - public void handleEvent(Event e) { - if (column1Width == 0 && column2Width == 0) { - column1Width = (control.getBounds().width - 18) / 2; - column2Width = (control.getBounds().width - 18) / 2; - } else { - int dif = control.getBounds().width - 18 - (column1Width + column2Width); - int columnDif = Math.abs(column1Width - column2Width); - int mainColumnChange = Math.min(Math.abs(dif), columnDif); - int left = Math.max(0, Math.abs(dif) - columnDif); - if (dif < 0) { - if (column1Width > column2Width) { - column1Width -= mainColumnChange; - } else { - column2Width -= mainColumnChange; - } - column1Width -= left / 2; - column2Width -= left - left / 2; - } else { - if (column1Width > column2Width) { - column2Width += mainColumnChange; - } else { - column1Width += mainColumnChange; - } - column1Width += left / 2; - column2Width += left - left / 2; - } - } - column1.removeListener(SWT.Resize, column1ResizeListener); - column1.setWidth(column1Width); - column2.setWidth(column2Width); - column1.addListener(SWT.Resize, column1ResizeListener); - } - }; - - private Listener column1ResizeListener = new Listener() { - public void handleEvent(Event e) { - int widthDif = column1Width - column1.getWidth(); - column1Width -= widthDif; - column2Width += widthDif; - if (column2Width < 0) { - column1Width += column2Width; - column2Width = 0; - column1.removeListener(SWT.Resize, this); - column1.setWidth(column1Width); - column1.addListener(SWT.Resize, this); - } - column2.setWidth(column2Width); - } - }; -} -- cgit v1.1