/*******************************************************************************
* Copyright (c) 2009, 2017 Red Hat, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat - initial API and implementation
*******************************************************************************/
package org.eclipse.linuxtools.internal.callgraph;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeSet;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.core.runtime.Status;
import org.eclipse.draw2d.Animation;
import org.eclipse.draw2d.Label;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.Projectionist;
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphKeyListener;
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseListener;
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseWheelListener;
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeContentProvider;
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeDoubleClickListener;
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeLabelProvider;
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.zest.core.widgets.Graph;
import org.eclipse.zest.core.widgets.GraphNode;
import org.eclipse.zest.layouts.LayoutStyles;
public class StapGraph extends Graph {
public static final String CONSTANT_TOP_NODE_NAME = Messages
.getString("StapGraph.StartNode"); //$NON-NLS-1$
public static final int CONSTANT_HORIZONTAL_SPACING = 50;
public static final int CONSTANT_DRAWMODE_LEVEL = 0;
public static final int CONSTANT_DRAWMODE_RADIAL = 1;
public static final int CONSTANT_DRAWMODE_TREE = 2;
public static final int CONSTANT_DRAWMODE_AGGREGATE = 3;
public static final int CONSTANT_ANIMATION_SLOW = 1;
public static final int CONSTANT_ANIMATION_FASTER = 2;
public static final int CONSTANT_ANIMATION_FASTEST = 3;
public static final int CONSTANT_MAX_NUMBER_OF_SIBLINGS = 3;
public static final int CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS = 15;
public static final int CONSTANT_VERTICAL_INCREMENT = 50;
public static final int CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL = 150;
public static final Color CONSTANT_HAS_PARENT = new Color(
Display.getCurrent(), 240, 200, 200);
public static final Color CONSTANT_HAS_CHILDREN = new Color(
Display.getCurrent(), 200, 250, 200);
public static final Color CONSTANT_MARKED = new Color(Display.getCurrent(),
210, 112, 214);
private int ANIMATION_TIME = 500;
// Draw level management
private int topLevelToDraw;
private int bottomLevelToDraw;
public int levelBuffer = 30;
private int maxNodes = 150;
private Projectionist proj;
private int lowestLevelOfNodesAdded;
public Map<Integer, List<Integer>> levels; // Level number, list of node ids
// Node management
private int idOfLastNode;
private int idOfLastCollapsedNode;
public Map<Integer, StapNode> nodeMap; // HashMap of current nodes
public Map<Integer, StapData> nodeDataMap; // HashMap of all data
// The negative side of nodeDataMap is collapsed, the positive side is
// uncollapsed
public List<GraphNode> aggregateNodes;
public Map<String, Long> aggregateTime;
public Map<String, Integer> aggregateCount;
private Map<Integer, Integer> collapsedLevelSize;
public List<Integer> markedNodes;
public List<Integer> markedCollapsedNodes;
// Modes
private boolean collapseMode;
private int draw_mode;
private int animation_mode;
// Time
private long totalTime;
private long endTime;
private long startTime;
// The current center/top of the nodes list
private int rootVisibleNodeNumber;
// Special cases
private boolean killInvalidFunctions; // Toggle hiding of invalid functions
// Tree viewer
private static TreeViewer treeViewer;
private Composite treeComp;
private static IDoubleClickListener treeDoubleListener;
public HashMap<Integer, Integer> currentPositionInLevel;
// (level, next horizontal position to place a node)
// For cycling through marked nodes
private int nextMarkedNode;
// Zooming factor
public double scale;
private List<Integer> callOrderList;
private int lastFunctionCalled;
private int treeLevelFromRoot;
private Canvas thumbCanvas;
private ICProject project;
private boolean threaded;
private int counter; // All purpose counting variable
public StapGraphMouseListener getMouseListener() {
return mListener;
}
private StapGraphMouseListener mListener;
private CallgraphView callgraphView;
public StapGraph(Composite parent, int style, Composite treeComp,
Canvas tCanvas, CallgraphView callgraphView) {
super(parent, style);
// -------------Initialize variables
thumbCanvas = tCanvas;
nodeMap = new HashMap<>();
levels = new HashMap<>();
nodeDataMap = new HashMap<>();
aggregateTime = new HashMap<>();
aggregateCount = new HashMap<>();
currentPositionInLevel = new HashMap<>();
collapsedLevelSize = new HashMap<>();
markedNodes = new ArrayList<>();
markedCollapsedNodes = new ArrayList<>();
animation_mode = 1;
idOfLastNode = 0;
rootVisibleNodeNumber = 0;
totalTime = 0;
collapseMode = false;
killInvalidFunctions = true;
nextMarkedNode = -1;
scale = 1;
treeLevelFromRoot = 0;
idOfLastCollapsedNode = 0;
this.callgraphView = callgraphView;
this.treeComp = treeComp;
if (treeViewer == null || treeViewer.getControl().isDisposed()) {
// Only create once
treeViewer = new TreeViewer(this.treeComp);
StapTreeListener stl = new StapTreeListener(treeViewer.getTree()
.getHorizontalBar());
treeViewer.addTreeListener(stl);
}
// -------------Add listeners
mListener = new StapGraphMouseListener(this);
this.addMouseListener(mListener);
this.addKeyListener(new StapGraphKeyListener(this));
this.addMouseWheelListener(new StapGraphMouseWheelListener(this));
}
/**
* Initialize the treeviewer with data from the graph. If the treeviewer has
* already been initialized (i.e. if it already has a content provider set),
* we merely call treeViewer.refresh();
*/
public void initializeTree() {
if (treeViewer.getContentProvider() == null) {
StapTreeContentProvider scp = new StapTreeContentProvider();
treeViewer.setContentProvider(scp);
} else {
((StapTreeContentProvider) treeViewer.getContentProvider())
.setGraph(this);
treeViewer.refresh();
return;
}
((StapTreeContentProvider) treeViewer.getContentProvider())
.setGraph(this);
if (treeViewer.getLabelProvider() != null) {
treeViewer.getLabelProvider().dispose();
}
StapTreeLabelProvider prov = new StapTreeLabelProvider();
treeViewer.setLabelProvider(prov);
if (treeDoubleListener != null) {
treeViewer.removeDoubleClickListener(treeDoubleListener);
}
treeDoubleListener = new StapTreeDoubleClickListener(treeViewer, this);
treeViewer.addDoubleClickListener(treeDoubleListener);
treeViewer.setInput(getNodeData(getTopNode()));
treeViewer.refresh();
}
/**
* Convenience method to loadData with a message preset.
*
* @param style
* @param id
* @param txt
* @param time
* @param called
* @param caller
* @return
*/
public int loadData(int style, int id, String txt, long time, int called,
int caller, boolean isMarked, String message) {
// -------------Invalid function catching
// Catches some random C/C++ directive functions
if (id < 10 && killInvalidFunctions) {
if (txt.contains(")")) { //$NON-NLS-1$
return -1;
} else if (txt.contains(".")) { //$NON-NLS-1$
return -1;
} else if (txt.contains("\"")) { //$NON-NLS-1$
return -1;
}
}
// -------------Add node to appropriate map/list
StapData n = new StapData(this, style, txt, time, called, id, caller,
isMarked);
if (isMarked) {
n.setMessage(message);
markedNodes.add(id);
}
nodeDataMap.put(id, n);
// Make no assumptions about the order that data is input
if (id > idOfLastNode) {
idOfLastNode = id;
}
return id;
}
public void insertMessage(int id, String message) {
StapData temp = nodeDataMap.get(id);
if (temp == null) {
return;
}
temp.insertMessage(message);
nodeDataMap.put(id, temp);
}
/*
* Fully functional draw functions
*
* -Radial -Tree
*/
/**
* Draws a 2-node-layer circle Draws all nodes in place.
*
* @param centerNode
*/
private void drawRadial(int centerNode) {
int radius = Math.max(CONSTANT_VERTICAL_INCREMENT,
Math.min(this.getBounds().width, this.getBounds().height) / 2
- 2 * CONSTANT_VERTICAL_INCREMENT);
rootVisibleNodeNumber = centerNode;
StapData nodeData = getNodeData(centerNode);
int collapsed = nodeData.getPartOfCollapsedNode();
if (!nodeData.isCollapsed
&& collapsed != StapData.NOT_PART_OF_COLLAPSED_NODE) {
nodeData = getNodeData(collapsed);
}
treeViewer.expandToLevel(nodeData, 0);
treeViewer.setSelection(new StructuredSelection(nodeData));
if (nodeMap.get(centerNode) == null) {
nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
}
// Draw node in center
StapNode n = nodeMap.get(centerNode);
int x = this.getBounds().width / 2 - n.getSize().width / 2;
int y = this.getBounds().height / 2;
n.setLocation(x, y);
if (getNodeData(centerNode).isMarked()) {
nodeMap.get(centerNode).setBackgroundColor(CONSTANT_MARKED);
}
radialHelper(centerNode, x, y, radius);
}
/**
* Helps animation of radial draw. Can be replaced by a draw and moveAll.
*
* @param centerNode
*/
private void preDrawRadial(int centerNode) {
rootVisibleNodeNumber = centerNode;
if (nodeMap.get(centerNode) == null) {
nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
StapNode n = nodeMap.get(centerNode);
n.setLocation(this.getBounds().width / 2,
this.getShell().getSize().y / 2);
}
// Pass coordinates of the node to radialHelper
StapNode n = nodeMap.get(centerNode);
int x = n.getLocation().x;
int y = n.getLocation().y;
radialHelper(centerNode, x, y, 0);
}
/**
* Completes radial-mode draws
*
* @param id
* @param x
* @param y
* @param radius
*/
private void radialHelper(int id, int x, int y, int radius) {
// -------------Draw parent node
// Draw caller node right beside this one, in a different color
int callerID = nodeDataMap.get(id).parent;
if (callerID != -1) {
if (getNode(callerID) == null) {
nodeMap.put(callerID, getNodeData(callerID).makeNode(this));
}
getNode(callerID).setBackgroundColor(CONSTANT_HAS_PARENT);
getNode(callerID).setLocation(x + radius / 5, y - radius / 5);
if (getNode(id).connection == null) {
getNode(id).makeConnection(SWT.NONE, getNode(callerID),
getNodeData(id).timesCalled);
}
if (getNodeData(callerID).isMarked()) {
nodeMap.get(callerID).setBackgroundColor(CONSTANT_MARKED);
}
}
// -------------Draw children nodes
List<Integer> nodeList;
if (!collapseMode) {
nodeList = nodeDataMap.get(id).children;
} else {
nodeList = nodeDataMap.get(id).collapsedChildren;
}
int numberOfNodes;
if (nodeList.size() >= CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS) {
numberOfNodes = CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
} else {
numberOfNodes = nodeList.size();
}
double angle;
if (numberOfNodes > 5) {
angle = 2 * Math.PI / numberOfNodes;
} else {
angle = 2 * Math.PI / CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
}
int i = 0;
for (i = 0; i < numberOfNodes; i++) {
int subID = nodeList.get(i);
int yOffset = 0;
int xOffset = 0;
if (nodeMap.get(subID) == null) {
nodeMap.put(subID, getNodeData(subID).makeNode(this));
}
StapNode subN = nodeMap.get(subID);
if (radius != 0) {
yOffset = (int) (radius * Math.cos((float) angle * i));
xOffset = (int) (radius * Math.sin((float) angle * i))
- subN.getSize().width / 2
+ getNode(id).getSize().width / 2;
}
if (hasChildren(subID)) {
subN.setBackgroundColor(CONSTANT_HAS_CHILDREN);
}
subN.setLocation(x + xOffset, y + yOffset);
if (subN.connection == null) {
subN.makeConnection(SWT.NONE, nodeMap.get(id),
nodeDataMap.get(subID).timesCalled);
}
StapData d = getNodeData(subID);
if (d.isMarked()) {
subN.setBackgroundColor(CONSTANT_MARKED);
}
}
}
/**
* Draws nodes according to the name of the function (not accounting for
* call heirarchies). Uses colour to indicate the number of calls and size
* to indicate the percentage time spent.
*/
private void drawAggregateView() {
if (aggregateNodes == null) {
aggregateNodes = new ArrayList<>();
} else {
aggregateNodes.clear();
}
// -------------Format numbers
float percentage_time;
float percentage_count;
int maxTimesCalled = 0;
final int colorLevels = 15;
final int colorLevelDifference = 12;
int primary;
int secondary;
NumberFormat num = NumberFormat.getInstance(Locale.CANADA);
num.setMinimumFractionDigits(2);
num.setMaximumFractionDigits(2);
// FIND THE MOST TIMES A FUNCTION IS CALLED
for (int val : aggregateCount.values()) {
if (val > maxTimesCalled) {
maxTimesCalled = val;
}
}
// TEMPORARY STORAGE OF THE ENTRIES
// IMPLEMENTS A COMPARATOR TO STORE BY ORDER OF THE VALUE
TreeSet<Entry<String, Long>> sortedValues = new TreeSet<>(
StapGraph.VALUE_ORDER);
HashMap<String, Long> tempMap = new HashMap<>();
tempMap.putAll(aggregateTime);
for (String key : tempMap.keySet()) {
long time = aggregateTime.get(key);
// This is a stupid way to get the times right, but it is almost
// always guaranteed to work.
while (time < 0) {
time += endTime;
}
tempMap.put(key, time);
}
sortedValues.addAll(tempMap.entrySet());
// -------------Draw nodes
for (Entry<String, Long> ent : sortedValues) {
String key = ent.getKey();
GraphNode n = new GraphNode(this.getGraphModel(), SWT.NONE);
aggregateNodes.add(n);
percentage_count = (float) aggregateCount.get(key)
/ (float) maxTimesCalled;
percentage_time = ((float) ent.getValue() / this.getTotalTime() * 100);
n.setText(key + "\n" //$NON-NLS-1$
+ num.format(percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ aggregateCount.get(key) + "\n"); //$NON-NLS-1$
n.setData("AGGREGATE_NAME", key); //$NON-NLS-1$
primary = (int) (percentage_count * colorLevels * colorLevelDifference);
secondary = (colorLevels * colorLevelDifference)
- (int) (percentage_count * colorLevels * colorLevelDifference);
primary = Math.max(0, primary);
secondary = Math.max(0, secondary);
primary = Math.min(primary, 255);
secondary = Math.min(secondary, 255);
Color c = new Color(this.getDisplay(), primary, 0, secondary);
n.setBackgroundColor(c);
n.setHighlightColor(c);
n.setForegroundColor(new Color(this.getDisplay(), 255, 255, 255));
n.setTooltip(new Label(
Messages.getString("StapGraph.Func") + key + "\n" //$NON-NLS-1$ //$NON-NLS-2$
+ Messages.getString("StapGraph.Time") + num.format(percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ Messages.getString("StapGraph.NumOfCalls") + aggregateCount.get(key) //$NON-NLS-1$
));
n.setBorderWidth(2);
}
// Set layout to gridlayout
this.setLayoutAlgorithm(new AggregateLayoutAlgorithm(LayoutStyles.NONE,
sortedValues, this.getTotalTime(), this.getBounds().width),
true);
}
/**
* Draws a tree starting with node id, putting node id at location x,y
*
* @param id
* @param x
* @param y
*/
private void drawTree(int id, int x, int y) {
// -------------Create node id
// Create and set
if (nodeMap.get(id) == null) {
nodeMap.put(id, getNodeData(id).makeNode(this));
}
StapNode n = getNode(id);
n.setLocation(x, y);
n.setSize(n.getSize().width / scale, n.getSize().height / scale);
// This is the lowest level of nodes to draw, and it still has kids
if (getLevelOfNode(id) == bottomLevelToDraw
&& getNodeData(id).children.size() > 0) {
n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
}
if (getNodeData(id).isMarked()) {
n.setBackgroundColor(CONSTANT_MARKED);
}
// -------------Get appropriate list of children
List<Integer> callees = null;
int usefulSize = 0;
// Determine which list of callees to use
if (!collapseMode) {
callees = getNodeData(id).children;
} else {
callees = getNodeData(id).collapsedChildren;
}
if (callees == null) {
return;
}
int cLevel = getLevelOfNode(id) + 1;
if (!collapseMode) {
if (levels.get(cLevel) != null) {
usefulSize = levels.get(cLevel).size()
- collapsedLevelSize.get(cLevel);
}
} else {
if (collapsedLevelSize.get(cLevel) != null) {
usefulSize = collapsedLevelSize.get(cLevel);
}
}
// -------------Draw all children
for (int i = 0; i < callees.size(); i++) {
// Find the number of nodes on this level for spacing purposes
int childID = callees.get(i);
int childLevel = getLevelOfNode(childID);
// Initialise the offset to roughly centre the nodes
if (currentPositionInLevel.get(getLevelOfNode(childID)) == null) {
int tmp = (int) (CONSTANT_HORIZONTAL_SPACING * (usefulSize - 1)
* -1 / scale);
currentPositionInLevel.put(childLevel,
getNode(rootVisibleNodeNumber).getLocation().x + tmp);
}
// Recursive iteration
if (childLevel <= bottomLevelToDraw
&& childLevel <= lowestLevelOfNodesAdded) {
drawTree(callees.get(i),
currentPositionInLevel.get(childLevel), y
+ (int) (CONSTANT_VERTICAL_INCREMENT / scale));
// Do not scale newSize or nodes will no longer be adjacent
int newSize = currentPositionInLevel
.get(getLevelOfNode(childID))
+ getNode(childID).getSize().width;
// Leave a small blank space between nodes for aesthetic
// purposes
if (i == callees.size() - 1) {
newSize += CONSTANT_HORIZONTAL_SPACING / 3;
}
currentPositionInLevel.put(getLevelOfNode(childID), newSize);
}
}
}
/**
* Extend the tree downwards
*/
public void extendTree() {
if (bottomLevelToDraw >= lowestLevelOfNodesAdded) {
return;
}
StapData data = getNodeData(rootVisibleNodeNumber);
if (data.children != null && data.children.size() < 1) {
return;
}
List<Integer> list = data.children;
if (isCollapseMode()) {
list = data.collapsedChildren;
}
if (list.size() == 1) {
// Special case - only one child of the root node
// Therefore change root node to this new root node
int aMode = animation_mode;
draw(CONSTANT_DRAWMODE_TREE, CONSTANT_ANIMATION_FASTEST,
list.get(0));
setAnimationMode(aMode);
return;
}
List<Integer> bottomList = levels.get(bottomLevelToDraw);
bottomLevelToDraw++;
for (int i : bottomList) {
if (getNode(i) != null) {
getNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
getParentNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
drawTree(i, getNode(i).getLocation().x, getNode(i)
.getLocation().y);
}
}
treeLevelFromRoot++;
}
/**
* Removes nodes from the bottom of the tree
*/
public void shrinkTree() {
if (treeLevelFromRoot < 1) {
return;
}
bottomLevelToDraw--;
deleteAll(rootVisibleNodeNumber);
int i = rootVisibleNodeNumber;
currentPositionInLevel.clear();
drawTree(i, getNode(i).getLocation().x, getNode(i).getLocation().y);
treeLevelFromRoot--;
}
/**
* Draws the next node, unless the next node does not exist.
*/
public void drawNextNode() {
if (isCollapseMode()) {
setCollapseMode(false);
}
int toDraw = getNextCalledNode(getRootVisibleNodeNumber());
if (toDraw != -1) {
draw(toDraw);
} else {
proj.pause();
}
}
/**
* Moves all nodes to the point x,y
*
* @param x
* @param y
*/
private void moveAllNodesTo(int x, int y) {
for (StapNode node : nodeMap.values()) {
node.setLocation(x, y);
}
}
/**
* Draws a tree roughly starting from node id
*/
private void drawBox(int id, int y) {
setLevelLimits(id);
int MaxLevelPixelWidth = 1;
int currPixelWidth = 1;
// FIND THE LEVEL THAT WILL BE THE WIDEST
// WILL BE A USEFUL VALUE LATER ON
int count;
for (int i = topLevelToDraw; i <= bottomLevelToDraw; i++) {
count = 0;
levels.get(i).add(0, count);
int size = levels.get(i).size();
for (int j = 1; j < size; j++) {
int val = levels.get(i).get(j);
StapData data = nodeDataMap.get(val);
if (!data.isOnlyChildWithThisName()) {
if (collapseMode && data.isPartOfCollapsedNode()) {
continue;
}
if (!collapseMode && data.isCollapsed) {
continue;
}
}
currPixelWidth += data.name.length() * 10
+ StapGraph.CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL;
if (MaxLevelPixelWidth < currPixelWidth) {
MaxLevelPixelWidth = currPixelWidth;
}
count++;
levels.get(i).remove(0);
levels.get(i).add(0, count);
}
currPixelWidth = 1;
}
MaxLevelPixelWidth = (int) (MaxLevelPixelWidth / scale);
counter = 0;
if (id == getFirstUsefulNode()) {
nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth / 2), y);
}
drawFromBottomToTop(
bottomLevelToDraw,
y
+ ((bottomLevelToDraw - topLevelToDraw) * 3 * (int) (CONSTANT_VERTICAL_INCREMENT / scale)),
MaxLevelPixelWidth);
if (id == getFirstUsefulNode()) {
nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth / 2), y);
}
}
private void drawFromBottomToTop(int level, int height,
int maxLevelPixelWidth) {
// FINISHED DRAWING THE ROOT IN THE LAST RECURSIVE CALL
if (level == 0 || level < topLevelToDraw) {
return;
}
// FIND ALL THE CHILDREN AT LEVEL 'level'
int total = levels.get(level).remove(0);
int count = 1;
// CREATE THE NODES
for (int i = 0; i < levels.get(level).size(); i++) {
int id = levels.get(level).get(i);
StapData data = nodeDataMap.get(id);
if (!data.isOnlyChildWithThisName()) {
if (collapseMode && data.isPartOfCollapsedNode()) {
continue;
}
if (!collapseMode && nodeDataMap.get(id).isCollapsed) {
continue;
}
}
if (nodeMap.get(id) == null) {
nodeMap.put(id, getNodeData(id).makeNode(this));
}
StapNode n = nodeMap.get(id);
n.setVisible(true);
n.setSize(n.getSize().width / scale, n.getSize().height / scale);
// Placement algorithm
if (getAnimationMode() == CONSTANT_ANIMATION_SLOW) {
if (counter <= ANIMATION_TIME) {
Animation.markBegin();
}
n.setLocation(150 + (nodeMap.get(getRootVisibleNodeNumber())
.getLocation().x),
nodeMap.get(getRootVisibleNodeNumber()).getLocation().y);
n.setLocation(150 + (maxLevelPixelWidth / (total + 1) * count),
height);
if (counter <= ANIMATION_TIME) {
Animation.run(ANIMATION_TIME / nodeMap.size() / 3);
counter += ANIMATION_TIME / nodeMap.size();
}
} else {
n.setLocation(150 + (maxLevelPixelWidth / (total + 1) * count),
height);
}
// IF WE CANNOT DISPLAY ALL NODES COLOUR NODES ON BOTTOM THAT STILL
// HAVE CHILDREN
if (level == bottomLevelToDraw
&& nodeDataMap.get(id).children.size() != 0) {
n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
}
if (getNodeData(n.id).isMarked()) {
n.setBackgroundColor(CONSTANT_MARKED);
}
// FIND ALL THE NODES THAT THIS NODE CALLS AND MAKE CONNECTIONS
List<Integer> setOfCallees = null;
if (collapseMode) {
setOfCallees = nodeDataMap.get(id).collapsedChildren;
} else {
setOfCallees = nodeDataMap.get(id).children;
}
for (int val : setOfCallees) {
if (nodeMap.get(val) != null) {
nodeMap.get(val).makeConnection(SWT.NONE, n,
nodeDataMap.get(val).timesCalled);
}
}
count++;
}
// DRAW THE NEXT LEVEL UP
drawFromBottomToTop(level - 1, height
- (3 * (int) (CONSTANT_VERTICAL_INCREMENT / scale)),
maxLevelPixelWidth);
}
/*
* Level/node management
*/
/**
* Delete all nodes except for the node with the specified nodeID
*
* @param exception
* - id of node NOT to delete (use -1 for 'no exceptions')
*/
private void deleteAll(int exception) {
// -------------Delete aggregate nodes
if (aggregateNodes != null) {
for (GraphNode n : aggregateNodes) {
n.dispose();
}
aggregateNodes.clear();
}
// -------------Save exception node's location
int x = -1;
int y = -1;
if (exception != -1 && nodeMap.get(exception) != null) {
x = nodeMap.get(exception).getLocation().x;
y = nodeMap.get(exception).getLocation().y;
}
// -------------Delete all nodes
for (StapNode node : nodeMap.values()) {
if (node == null) {
continue;
}
node.unhighlight();
node.dispose();
}
nodeMap.clear();
// -------------Recreate exception
if (x != -1 && y != -1) {
StapNode n = getNodeData(exception).makeNode(this);
n.setLocation(x, y);
n.highlight();
nodeMap.put(exception, n);
}
}
/**
* Delete a number of levels from the top of the graph
*
* @param numberOfLevelsToDelete
*/
private void deleteLevelsFromTop(int numberOfLevelsToDelete) {
if (numberOfLevelsToDelete <= 0) {
return;
}
for (int i = 0; i < numberOfLevelsToDelete; i++) {
List<Integer> level = levels.get(topLevelToDraw);
for (int j = 0; j < level.size(); j++) {
if (nodeMap.get(level.get(j)) != null) {
nodeMap.remove(level.get(j)).dispose();
}
}
topLevelToDraw++;
}
}
/**
* Delete a number of levels from the bottom of the graph
*
* @param numberOfLevelsToDelete
*/
private void deleteLevelsFromBottom(int numberOfLevelsToDelete) {
if (numberOfLevelsToDelete <= 0) {
return;
}
for (int i = 0; i < numberOfLevelsToDelete; i++) {
List<Integer> level = levels.get(bottomLevelToDraw);
for (int j = 0; j < level.size(); j++) {
if (nodeMap.get(level.get(j)) != null) {
nodeMap.remove(level.get(j)).dispose();
}
}
bottomLevelToDraw--;
}
}
/**
* Sets top level limit to the level of id, bottom level limit to top level
* limit + CONSTANT_LEVEL_BUFFER. Deletes extraneous levels, changes
* topLevelToDraw, bottomLevelToDraw
*
* Convenience method: Calls setLevelLimitsToLevel(levelOfNode(id))
*
* @param id
* - node to recenter with
*/
private void setLevelLimits(int id) {
setTopLevelTo(getLevelOfNode(id));
}
/**
* Sets top level limit to the given level, bottom level limit to top level
* limit + CONSTANT_LEVEL_BUFFER. Deletes extraneous levels, changes
* topLevelToDraw, bottomLevelToDraw
*
* @param id
* - node to recenter with
*/
public void setTopLevelTo(int newTopLevelToDraw) {
changeLevelLimits(newTopLevelToDraw);
int new_bottomLevelToDraw = newTopLevelToDraw + levelBuffer;
if (new_bottomLevelToDraw > lowestLevelOfNodesAdded) {
new_bottomLevelToDraw = lowestLevelOfNodesAdded;
}
deleteLevelsFromTop(newTopLevelToDraw - topLevelToDraw);
deleteLevelsFromBottom(bottomLevelToDraw - new_bottomLevelToDraw);
topLevelToDraw = newTopLevelToDraw;
bottomLevelToDraw = new_bottomLevelToDraw;
}
public boolean changeLevelLimits(int lvl) {
int numberOfNodes = 0;
List<Integer> list;
int maxLevel = Math.min(lvl + levelBuffer, lowestLevelOfNodesAdded);
for (int level = lvl; level < maxLevel; level++) {
for (int id : levels.get(level)) {
if (isCollapseMode()) {
list = getNodeData(id).collapsedChildren;
} else {
list = getNodeData(id).children;
}
numberOfNodes += list.size();
if (numberOfNodes > maxNodes) {
levelBuffer = Math.max(0, level - 1);
return true;
}
}
}
return false;
}
/**
* Convenience method to redraw everything.
*/
public void draw() {
draw(getRootVisibleNodeNumber());
}
/**
* Convenience method to draw with current draw parameters. Equivalent to
* draw(graph.draw_mode, graph.animation_mode, id)
*
* @param id
*/
public void draw(int id) {
draw(draw_mode, animation_mode, id);
}
/**
* Draws with the given modes.
*
* @param drawMode
* @param animationMode
* @param id
*/
public void draw(int drawMode, int animationMode, int id) {
this.draw_mode = drawMode;
setAnimationMode(animationMode);
if (nodeDataMap.get(id) == null) {
return;
}
this.clearSelection();
treeLevelFromRoot = 0;
currentPositionInLevel.clear();
this.setRedraw(false);
if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
// Remove thumbnail
GridData gd = (GridData) thumbCanvas.getLayoutData();
gd.exclude = true;
thumbCanvas.setLayoutData(gd);
thumbCanvas.setVisible(false);
callgraphView.layout();
// Add treeComp
gd = (GridData) treeComp.getLayoutData();
gd.exclude = false;
treeComp.setLayoutData(gd);
treeComp.setVisible(true);
treeViewer.collapseToLevel(getNodeData(id), 1);
treeViewer.expandToLevel(getNodeData(id), 1);
} else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE) {
// Remove treeComp
GridData gd = (GridData) treeComp.getLayoutData();
gd.exclude = true;
treeComp.setLayoutData(gd);
treeComp.setVisible(false);
callgraphView.layout();
// Remove thumbnail
gd = (GridData) thumbCanvas.getLayoutData();
gd.exclude = true;
thumbCanvas.setLayoutData(gd);
thumbCanvas.setVisible(false);
} else {
// Remove treeComp
GridData gd = (GridData) treeComp.getLayoutData();
gd.exclude = true;
treeComp.setLayoutData(gd);
treeComp.setVisible(false);
callgraphView.layout();
// Add thumbnail
gd = (GridData) thumbCanvas.getLayoutData();
gd.exclude = true;
thumbCanvas.setLayoutData(gd);
thumbCanvas.setVisible(true);
thumbCanvas.setBackground(this.getBackground());
}
callgraphView.layout();
this.setRedraw(true);
// -------------Draw tree
if (draw_mode == CONSTANT_DRAWMODE_TREE) {
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
if (nodeMap.get(id) == null) {
nodeMap.put(id, getNodeData(id).makeNode(this));
}
int tempX = nodeMap.get(id).getLocation().x;
int tempY = nodeMap.get(id).getLocation().y;
Animation.markBegin();
moveAllNodesTo(tempX, tempY);
Animation.run(ANIMATION_TIME);
deleteAll(id);
setLevelLimits(id);
rootVisibleNodeNumber = id;
drawTree(id, this.getBounds().width / 2, 20);
currentPositionInLevel.clear();
this.update();
Animation.markBegin();
drawTree(id, this.getBounds().width / 2, 20);
Animation.run(ANIMATION_TIME);
getNode(id).unhighlight();
} else {
deleteAll(id);
setLevelLimits(id);
rootVisibleNodeNumber = id;
drawTree(id, this.getBounds().width / 2, 20);
getNode(id).unhighlight();
}
}
// -------------Draw radial
else if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
rootVisibleNodeNumber = id;
deleteAll(id);
preDrawRadial(id);
this.redraw();
this.getLightweightSystem().getUpdateManager().performUpdate();
Animation.markBegin();
nodeMap.get(id).setLocation(this.getBounds().width / 2,
this.getBounds().height / 2);
drawRadial(id);
Animation.run(ANIMATION_TIME);
callgraphView.layout();
} else {
deleteAll(id);
drawRadial(id);
}
}
// -------------Draw level
else if (draw_mode == CONSTANT_DRAWMODE_LEVEL) {
rootVisibleNodeNumber = id;
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
if (nodeMap.get(id) == null) {
nodeMap.put(id, getNodeData(id).makeNode(this));
}
Animation.markBegin();
moveAllNodesTo(nodeMap.get(id).getLocation().x, nodeMap.get(id)
.getLocation().y);
Animation.run(ANIMATION_TIME);
deleteAll(id);
drawBox(id, 0);
} else {
if (nodeMap.get(id) == null) {
nodeMap.put(id, getNodeData(id).makeNode(this));
}
deleteAll(id);
drawBox(id, 0);
}
}
// -------------Draw aggregate
else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE) {
rootVisibleNodeNumber = getFirstUsefulNode();
deleteAll(-1);
drawAggregateView();
}
if (getNode(id) != null) {
getNode(id).unhighlight();
}
clearSelection();
// AFTER FIRST LOADING LET THE GRAPH EXPAND TO FILL THE VIEW
this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
}
/**
* Unhighlights all selected nodes and sets selection to null
*/
private void clearSelection() {
List<GraphNode> list = this.getSelection();
for (GraphNode n : list) {
if (n != null) {
n.unhighlight();
}
}
this.setSelection(null);
}
/**
* Recursively collapses all children of node id, and puts them in the
* collapsedCallees list of id.
*
* At the end of this run, each collapsed node will have a list of other
* collapsed nodes AND a list of non-collapsed nodes. So will node #id.
*
* Uncollapsed nodes, however, will not have a list of collapsed nodes
* attached.
*
* @param ID
* of node to start from (use getFirstUsefulNode() to collapse
* everything
* @return True if successful
*/
public boolean recursivelyCollapseAllChildrenOfNode(int id) {
// -------------Initialize
// If all nodes have been collapsed, don't do anything
setCollapseMode(true);
if (nodeDataMap.get(id).children.size() == 0) {
return true;
}
// Name, id
HashMap<String, Integer> newNodeMap = new HashMap<>();
for (int collapsedID : nodeDataMap.get(id).collapsedChildren) {
newNodeMap.put(getNodeData(collapsedID).name, collapsedID);
}
// id of 'collapsed' node, id of its uncollapsed twin
HashMap<Integer, Integer> collapsedNodesWithOnlyOneNodeInThem = new HashMap<>();
int size = nodeDataMap.get(id).children.size();
// -------------Iterate
for (int i = 0; i < size; i++) {
int childID = nodeDataMap.get(id).children.get(i);
if (getNodeData(childID).isPartOfCollapsedNode()) {
continue;
}
int childLevel = getLevelOfNode(childID);
if (collapsedLevelSize.get(childLevel) == null) {
collapsedLevelSize.put(childLevel, 0);
}
String nodeName = nodeDataMap.get(childID).name;
/*
* Aggregate data for the given node
*/
if (newNodeMap.get(nodeName) != null) {
int aggregateID = newNodeMap.get(nodeName);
if (collapsedNodesWithOnlyOneNodeInThem.get(aggregateID) != null) {
// -------------Aggregate nodes - second node to be found
// We still think this is an only child, but now we know
// better.
// Create a new data node and aggregate
this.loadData(SWT.NONE, aggregateID, nodeName, nodeDataMap
.get(childID).getTime(),
nodeDataMap.get(childID).timesCalled, id,
nodeDataMap.get(childID).isMarked(), ""); //$NON-NLS-1$
if (getNodeData(aggregateID).isMarked()) {
markedCollapsedNodes.add(aggregateID);
markedNodes.remove((Integer) aggregateID);
}
nodeDataMap.get(id).children.remove((Integer) aggregateID);
nodeDataMap.get(id).collapsedChildren.add(aggregateID);
nodeDataMap.get(childID)
.setPartOfCollapsedNode(aggregateID);
nodeDataMap.get(aggregateID).collapsedParent = id;
// Aggregate the first node that we found, and set it
// as the uncollapsed piece of the aggregate node
int otherChildID = collapsedNodesWithOnlyOneNodeInThem
.get(aggregateID);
aggregateData(nodeDataMap.get(aggregateID),
nodeDataMap.get(otherChildID));
collapsedNodesWithOnlyOneNodeInThem.remove(aggregateID);
nodeDataMap.get(aggregateID).children.addAll(nodeDataMap
.get(otherChildID).children);
nodeDataMap.get(aggregateID).setPartOfCollapsedNode(
StapData.NOT_PART_OF_COLLAPSED_NODE);
nodeDataMap.get(otherChildID).setPartOfCollapsedNode(
aggregateID);
nodeDataMap.get(aggregateID).uncollapsedPiece = otherChildID;
} else {
// -------------Aggregate - third and additional nodes
aggregateData(nodeDataMap.get(aggregateID),
nodeDataMap.get(childID));
}
// -------------Complete aggregation
nodeDataMap.get(aggregateID).children.addAll(nodeDataMap
.get(childID).children);
nodeDataMap.get(aggregateID).isCollapsed = true;
if (nodeMap.get(childID) != null) {
nodeMap.get(childID).setLocation(
nodeMap.get(id).getLocation().x
- nodeMap.get(id).getSize().width,
nodeMap.get(id).getLocation().y);
}
nodeDataMap.get(childID).setPartOfCollapsedNode(aggregateID);
} else {
// -------------First child with this name
idOfLastCollapsedNode--;
newNodeMap.put(nodeName, idOfLastCollapsedNode);
collapsedNodesWithOnlyOneNodeInThem.put(idOfLastCollapsedNode,
childID);
if (nodeMap.get(childID) != null) {
nodeMap.get(childID).setLocation(
nodeMap.get(id).getLocation().x,
nodeMap.get(id).getLocation().y);
}
int tmp = collapsedLevelSize.get(childLevel) + 1;
collapsedLevelSize.put(childLevel, tmp);
}
}
// -------------Handle nodes that only appeared once
for (int childID : collapsedNodesWithOnlyOneNodeInThem.values()) {
nodeDataMap.get(childID).onlyChildWithThisName = true;
nodeDataMap.get(id).collapsedChildren.add(childID);
newNodeMap.remove(nodeDataMap.get(childID).name);
nodeDataMap.get(childID).collapsedParent = id;
// This node is technically a part of itself
nodeDataMap.get(childID).setPartOfCollapsedNode(childID);
if (getNodeData(childID).isMarked()) {
markedCollapsedNodes.add(childID);
}
}
// -------------Finish iterations
for (int i : nodeDataMap.get(id).collapsedChildren) {
recursivelyCollapseAllChildrenOfNode(i);
}
collapsedNodesWithOnlyOneNodeInThem.clear();
newNodeMap.clear();
return true;
}
/**
* Add time, called values for the two given nodes, storing them inside
* victim. Also adds marked collapsed nodes to markedCollapsedNodes list
*
* @param target
* @param victim
*/
private void aggregateData(StapData target, StapData victim) {
target.setTime(target.getTime() + victim.getTime());
target.timesCalled += victim.timesCalled;
if (victim.isMarked() || target.isMarked()) {
target.setMarked();
markedCollapsedNodes.add(target.id);
}
}
/**
*
* @param id
* of node
* @return StapNode
*/
public StapNode getNode(int id) {
return nodeMap.get(id);
}
/**
*
* @param id
* of node
* @return StapData
*/
public StapData getNodeData(int id) {
return nodeDataMap.get(id);
}
/**
* Recommend using getFirstUsefulNode instead.
*
* @return First node in level 0
*/
public int getTopNode() {
return levels.get(topLevelToDraw).get(0);
}
/**
* Recommend use of this function instead of getTopNode()
*
* @return First node that is not the dummy first node
*/
public int getFirstUsefulNode() {
if (threaded) {
return 0;
}
int id = 0;
if (nodeDataMap.get(id).name == CONSTANT_TOP_NODE_NAME) {
id++;
}
// Get first node that is not TOP_NODE_NAME
while (nodeDataMap.get(id) == null && id < idOfLastNode) {
id++;
}
return id;
}
/**
*
* @return Top level to draw - the level should be defined by draw
* algorithms
*/
public int getTopLevel() {
return topLevelToDraw;
}
/**
* Returns the level of recursion associated with the given node.
*
* @param nodeID
* @return
*/
public int getLevelOfNode(int nodeID) {
return nodeDataMap.get(nodeID).levelOfRecursion;
}
/**
* Returns true if the given node has any children.
*
* @param nodeID
* @return
*/
private boolean hasChildren(int nodeID) {
if (nodeDataMap.get(nodeID).children.size() > 0) {
return true;
}
return false;
}
/**
* Sets animation mode - all available modes are named
* StapGraph.CONSTANT_ANIMATION_*
*
* @param mode
*/
public void setAnimationMode(int mode) {
animation_mode = mode;
if (mode == CONSTANT_ANIMATION_SLOW) {
callgraphView.getAnimationSlow().setChecked(true);
callgraphView.getAnimationFast().setChecked(false);
} else if (mode == CONSTANT_ANIMATION_FASTEST) {
callgraphView.getAnimationSlow().setChecked(false);
callgraphView.getAnimationFast().setChecked(true);
}
}
public void setCollapseMode(boolean value) {
if (collapseMode == value
|| draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE) {
return;
}
if (draw_mode != StapGraph.CONSTANT_DRAWMODE_LEVEL) {
if (collapseMode) {
// Collapsed to noncollapsed
if (!getRootData().isOnlyChildWithThisName()) {
// A collapsed node that isn't an only child must have an
// uncollapsed piece
rootVisibleNodeNumber = getRootData().uncollapsedPiece;
}
} else {
// Uncollapsed to collapsed -- set center node to collapsed node
if (!getRootData().isOnlyChildWithThisName()) {
int temp = getRootData().getPartOfCollapsedNode();
if (temp != StapData.NOT_PART_OF_COLLAPSED_NODE) {
rootVisibleNodeNumber = temp;
}
}
}
}
collapseMode = value;
callgraphView.getModeCollapsednodes().setChecked(value);
nextMarkedNode = -1;
}
/**
*
* @return getNodeData(getRootVisibleNodeNumber())
*/
private StapData getRootData() {
return getNodeData(rootVisibleNodeNumber);
}
/**
* Gets id of root visible node
*
* @return rootVisibleNode - ID of centre node
*/
public int getRootVisibleNodeNumber() {
return rootVisibleNodeNumber;
}
/**
* Gets to the total time spent running tapped program
*
* @return Time in milliseconds
*/
public long getTotalTime() {
if (totalTime == 0 || totalTime > 1200000000000000000l) {
return endTime - startTime;
}
return totalTime;
}
/**
* Sets total time spent running tapped program
*
* @param totalTime
* - Time in milliseconds
*/
public void setTotalTime(long val) {
this.totalTime = val;
}
/**
*
* @return Number of the lowest level in which nodes exist
*/
public int getLowestLevelOfNodesAdded() {
return lowestLevelOfNodesAdded;
}
/**
* Set lowest level to which nodes have been added
*
* WARNING: Do not set this without adding nodes to that level first, or
* there may be null pointer exceptions.
*
* @param lowestLevelOfNodesAdded
*/
public void setLowestLevelOfNodesAdded(int lowestLevelOfNodesAdded) {
this.lowestLevelOfNodesAdded = lowestLevelOfNodesAdded;
}
public int getDrawMode() {
return draw_mode;
}
/**
* Resets the tree and graph to center on the first useful node. Does NOT
* change draw mode, animation mode or collapse mode.
*/
public void reset() {
setSelection(null);
draw(draw_mode, animation_mode, getFirstUsefulNode());
if (!(draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)) {
getNode(getFirstUsefulNode()).unhighlight();
}
if (treeViewer != null) {
treeViewer.collapseAll();
treeViewer.expandToLevel(2);
}
scale = 1;
nextMarkedNode = -1;
}
public boolean isCollapseMode() {
return this.collapseMode;
}
public TreeViewer getTreeViewer() {
return treeViewer;
}
/**
* Returns the StapNode object for the parent of node id. May be null.
*
* @param id
* @return
*/
public StapNode getParentNode(int id) {
return nodeMap.get(nodeDataMap.get(id).parent);
}
/**
* Returns the id of the next node that was called. This is necessarily
* either a child of the current root node or of one of its ancestors'
* children.
*
* @param id
* @return Id of next node that was called.
*/
private int getNextCalledNode(int id) {
int returnID = -1;
if (isCollapseMode()) {
setCollapseMode(false);
// Redraw the current graph in uncollapsed mode if currently
// collapsed
draw();
}
for (int count = callOrderList.indexOf(id) + 1; count < callOrderList
.size(); count++) {
int next = callOrderList.get(count);
if (getNodeData(id) == null) {
continue;
}
if (!getNodeData(next).isCollapsed
|| getNodeData(next).isOnlyChildWithThisName()) {
return next;
}
}
return returnID;
}
/**
* Returns the id of the previous node that was called.
*
* @param id
* @return Id of previous node that was called.
*/
public int getPreviousCalledNode(int id) {
int returnID = -1;
for (int count = callOrderList.indexOf(id) - 1; count > -1; count--) {
if (getNodeData(id) == null) {
continue;
}
if (!getNodeData(id).isCollapsed
|| getNodeData(id).isOnlyChildWithThisName()) {
returnID = callOrderList.get(count);
return returnID;
}
}
return returnID;
}
/**
* Returns the id of the next marked node in current collapse mode. Wraps
* back to the first marked node.
*
* @return Node id of next marked node.
*/
public int getNextMarkedNode() {
List<Integer> list = markedNodes;
if (collapseMode) {
list = markedCollapsedNodes;
}
if (list.size() == 0) {
return -1;
}
nextMarkedNode++;
if (nextMarkedNode >= list.size()) {
nextMarkedNode = 0;
}
return list.get(nextMarkedNode);
}
/**
* Returns the id of the next marked node in current collapse mode. Wraps
* back to the first marked node.
*
* @return Node id of next marked node.
*/
public int getPreviousMarkedNode() {
List<Integer> list = markedNodes;
if (collapseMode) {
list = markedCollapsedNodes;
}
if (list.size() == 0) {
return -1;
}
nextMarkedNode--;
if (nextMarkedNode < 0) {
nextMarkedNode = list.size() - 1;
}
return list.get(nextMarkedNode);
}
public void play() {
if (proj == null || proj.getResult() == Status.OK_STATUS) {
proj = new Projectionist("Projectionist", this, 2000); //$NON-NLS-1$
proj.schedule();
} else {
proj.pause();
}
}
private static final Comparator<Entry<String, Long>> VALUE_ORDER = (a, b) -> a.getValue().compareTo((b.getValue()));
/**
* Smoothly increments the scrollbars by x, y
*
* @param x
* @param y
*/
public void scrollSmoothBy(int x, int y) {
this.scrollSmoothTo(this.getHorizontalBar().getSelection() + x, this
.getVerticalBar().getSelection() + y);
}
public int getAnimationMode() {
return animation_mode;
}
public int getLevelBuffer() {
return levelBuffer;
}
public void setLevelBuffer(int val) {
levelBuffer = val;
}
public int getMaxNodes() {
return maxNodes;
}
public void setMaxNodes(int val) {
maxNodes = val;
}
public void setCallOrderList(List<Integer> callOrderList) {
this.callOrderList = callOrderList;
}
public int getLastFunctionCalled() {
return lastFunctionCalled;
}
public void setLastFunctionCalled(int lastFunctionCalled) {
this.lastFunctionCalled = lastFunctionCalled;
}
public ICProject getProject() {
return project;
}
public Projectionist getProjectionist() {
return proj;
}
public void setProject(ICProject myProject) {
this.project = myProject;
}
public CallgraphView getCallgraphView() {
return callgraphView;
}
public void setEndTime(long val) {
endTime = val;
}
public long getEndTime() {
return endTime;
}
public void setStartTime(long val) {
startTime = val;
}
public void setThreaded() {
threaded = true;
}
public boolean getCollapseMode() {
return collapseMode;
}
public void addCalled(int idChild) {
getNodeData(idChild).timesCalled++;
}
}