/*
* 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<DrawableViewNode> children = new ArrayList<DrawableViewNode>();
public final static int NODE_HEIGHT = 100;
public final static int NODE_WIDTH = 180;
public final static int CONTENT_LEFT_RIGHT_PADDING = 9;
public final static int CONTENT_TOP_BOTTOM_PADDING = 8;
public final static int CONTENT_INTER_PADDING = 3;
public final static int INDEX_PADDING = 7;
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 + "}"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
}
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 + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
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;
}
}
}