1
/*******************************************************************************
2
* Copyright (c) 2009 Red Hat, Inc.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Red Hat - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.linuxtools.internal.callgraph;
13
import java.text.NumberFormat;
14
import java.util.ArrayList;
15
import java.util.Comparator;
16
import java.util.HashMap;
17
import java.util.List;
18
import java.util.Locale;
19
import java.util.TreeSet;
20
import java.util.Map.Entry;
22
import org.eclipse.cdt.core.model.ICProject;
23
import org.eclipse.core.runtime.Status;
24
import org.eclipse.draw2d.Animation;
25
import org.eclipse.draw2d.Label;
26
import org.eclipse.jface.viewers.IDoubleClickListener;
27
import org.eclipse.jface.viewers.StructuredSelection;
28
import org.eclipse.jface.viewers.TreeViewer;
29
import org.eclipse.linuxtools.internal.callgraph.core.MP;
30
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.Projectionist;
31
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphKeyListener;
32
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseListener;
33
import org.eclipse.linuxtools.internal.callgraph.graphlisteners.StapGraphMouseWheelListener;
34
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeContentProvider;
35
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeDoubleClickListener;
36
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeLabelProvider;
37
import org.eclipse.linuxtools.internal.callgraph.treeviewer.StapTreeListener;
38
import org.eclipse.swt.SWT;
39
import org.eclipse.swt.graphics.Color;
40
import org.eclipse.swt.layout.GridData;
41
import org.eclipse.swt.widgets.Canvas;
42
import org.eclipse.swt.widgets.Composite;
43
import org.eclipse.swt.widgets.Display;
44
import org.eclipse.zest.core.widgets.Graph;
45
import org.eclipse.zest.core.widgets.GraphNode;
46
import org.eclipse.zest.layouts.LayoutStyles;
49
public class StapGraph extends Graph {
51
public static final String CONSTANT_TOP_NODE_NAME = Messages.getString("StapGraph.StartNode"); //$NON-NLS-1$
52
public static final int CONSTANT_HORIZONTAL_SPACING = 50;
53
public static final int CONSTANT_DRAWMODE_LEVEL = 0;
54
public static final int CONSTANT_DRAWMODE_RADIAL = 1;
55
public static final int CONSTANT_DRAWMODE_TREE = 2;
56
public static final int CONSTANT_DRAWMODE_AGGREGATE = 3;
57
public static final int CONSTANT_ANIMATION_SLOW = 1;
58
public static final int CONSTANT_ANIMATION_FASTER = 2;
59
public static final int CONSTANT_ANIMATION_FASTEST = 3;
60
public static final int CONSTANT_MAX_NUMBER_OF_SIBLINGS = 3;
61
public static final int CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS = 15;
62
public static final int CONSTANT_VERTICAL_INCREMENT = 50;
63
public static final int CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL = 150;
64
public static final Color CONSTANT_HAS_PARENT = new Color(Display.getCurrent(), 240, 200,
66
public static final Color CONSTANT_HAS_CHILDREN = new Color(Display.getCurrent(), 200,
68
public static final Color CONSTANT_MARKED = new Color(Display.getCurrent(), 210, 112, 214);
69
private int ANIMATION_TIME = 500;
70
//Draw level management
71
private int topLevelToDraw;
72
private int bottomLevelToDraw;
73
private int topLevelOnScreen;
74
public int levelBuffer = 30;
75
private int maxNodes = 150;
76
private Projectionist proj;
80
private int lowestLevelOfNodesAdded;
81
public HashMap<Integer, List<Integer>> levels; //Level number, list of node ids
85
private int idOfLastNode;
86
private int idOfLastCollapsedNode;
87
public HashMap<Integer, StapNode> nodeMap; // HashMap of current nodes
88
public HashMap<Integer, StapData> nodeDataMap; // HashMap of all data
89
//The negative side of nodeDataMap is collapsed, the positive side is uncollapsed
91
public List<GraphNode> aggregateNodes;
92
public HashMap<String, Long> aggregateTime;
93
public HashMap<String, Integer> aggregateCount;
94
private HashMap <Integer, Integer> collapsedLevelSize;
95
public List<Integer> markedNodes;
96
public List<Integer> markedCollapsedNodes;
99
private boolean collapse_mode;
100
private int draw_mode;
101
private int animation_mode;
104
private long totalTime;
105
private long endTime;
106
private long startTime;
108
//The current center/top of the nodes list
109
private int rootVisibleNodeNumber;
112
private boolean killInvalidFunctions; //Toggle hiding of invalid functions
115
private static TreeViewer treeViewer;
116
private Composite treeComp;
117
private static IDoubleClickListener treeDoubleListener;
118
public HashMap<Integer, Integer> currentPositionInLevel;
119
//(level, next horizontal position to place a node)
121
//For cycling through marked nodes
122
private int nextMarkedNode;
129
private ArrayList<Integer> callOrderList;
130
private int lastFunctionCalled;
131
private int treeLevelFromRoot;
132
private Canvas thumbCanvas;
133
private ICProject project;
134
private boolean threaded;
135
private int counter; //All purpose counting variable
139
public StapGraphMouseListener getMouseListener() {
145
public StapGraphMouseWheelListener getMouseWheelListener() {
151
public StapGraphKeyListener getKeyListener() {
157
private StapGraphMouseListener mListener;
158
private StapGraphMouseWheelListener mwListener;
159
private StapGraphKeyListener kListener;
161
private CallgraphView callgraphView;
163
public StapGraph(Composite parent, int style, Composite treeComp, Canvas tCanvas,
164
CallgraphView callgraphView) {
165
super(parent, style);
167
//-------------Initialize variables
168
thumbCanvas = tCanvas;
169
nodeMap = new HashMap<Integer, StapNode>();
170
levels = new HashMap<Integer, List<Integer>>();
171
nodeDataMap = new HashMap<Integer, StapData>();
172
aggregateTime = new HashMap<String, Long>();
173
aggregateCount = new HashMap<String, Integer>();
174
currentPositionInLevel = new HashMap<Integer, Integer>();
175
collapsedLevelSize = new HashMap<Integer, Integer>();
176
markedNodes = new ArrayList<Integer>();
177
markedCollapsedNodes = new ArrayList<Integer>();
180
rootVisibleNodeNumber=0;
182
collapse_mode = false;
183
killInvalidFunctions = true;
186
treeLevelFromRoot = 0;
187
idOfLastCollapsedNode = 0;
188
this.callgraphView = callgraphView;
190
this.treeComp = treeComp;
191
if (treeViewer == null || treeViewer.getControl().isDisposed()) {
193
treeViewer = new TreeViewer(this.treeComp);
194
StapTreeListener stl = new StapTreeListener(treeViewer.getTree().getHorizontalBar());
195
treeViewer.addTreeListener(stl);
199
//-------------Add listeners
200
mListener = new StapGraphMouseListener(this);
201
kListener = new StapGraphKeyListener(this);
202
mwListener = new StapGraphMouseWheelListener(this);
203
this.addMouseListener(mListener);
204
this.addKeyListener(kListener);
205
this.addMouseWheelListener(mwListener);
213
* Initialize the treeviewer with data from the graph. If the treeviewer
214
* has already been initialized (i.e. if it already has a content provider
215
* set), we merely call treeViewer.refresh();
217
public void initializeTree() {
218
if (treeViewer.getContentProvider() == null) {
219
StapTreeContentProvider scp = new StapTreeContentProvider();
220
treeViewer.setContentProvider(scp);
222
((StapTreeContentProvider) treeViewer.getContentProvider())
224
treeViewer.refresh();
228
((StapTreeContentProvider) treeViewer.getContentProvider()).setGraph(this);
230
if (treeViewer.getLabelProvider() != null)
231
treeViewer.getLabelProvider().dispose();
232
StapTreeLabelProvider prov = new StapTreeLabelProvider();
233
treeViewer.setLabelProvider(prov);
235
if (treeDoubleListener != null) {
236
treeViewer.removeDoubleClickListener(treeDoubleListener);
238
treeDoubleListener = new StapTreeDoubleClickListener(treeViewer, this);
239
treeViewer.addDoubleClickListener(treeDoubleListener);
241
treeViewer.setInput(getNodeData(getTopNode()));
242
treeViewer.refresh();
249
* Convenience method to loadData with a message preset.
259
public int loadData(int style, int id, String txt, long time, int called,
260
int caller, boolean isMarked, String message) {
261
//-------------Invalid function catching
262
// Catches some random C/C++ directive functions
263
if (id < 10 && killInvalidFunctions) {
264
if (txt.contains(")")) { //$NON-NLS-1$
266
} else if (txt.contains(".")) { //$NON-NLS-1$
268
} else if (txt.contains("\"")) { //$NON-NLS-1$
273
//-------------Add node to appropriate map/list
274
StapData n = new StapData(this, style, txt, time, called,
275
id, caller, isMarked);
277
n.setMessage(message);
280
nodeDataMap.put(id, n);
282
// Make no assumptions about the order that data is input
283
if (id > idOfLastNode)
288
public void insertMessage(int id, String message) {
289
StapData temp = nodeDataMap.get(id);
290
if (temp == null) return;
291
temp.insertMessage(message);
292
nodeDataMap.put(id, temp);
296
* Fully functional draw functions
303
* Draws a 2-node-layer circle
304
* Draws all nodes in place.
307
public void drawRadial(int centerNode) {
308
int radius = Math.max(CONSTANT_VERTICAL_INCREMENT,
309
Math.min(this.getBounds().width,
310
this.getBounds().height)
311
/ 2 - 2*CONSTANT_VERTICAL_INCREMENT);
313
rootVisibleNodeNumber = centerNode;
314
StapData nodeData = getNodeData(centerNode);
315
int collapsed = nodeData.getPartOfCollapsedNode();
316
if (!nodeData.isCollapsed && collapsed != StapData.NOT_PART_OF_COLLAPSED_NODE) {
317
nodeData = getNodeData(collapsed);
319
treeViewer.expandToLevel(nodeData, 0);
320
treeViewer.setSelection(new StructuredSelection(nodeData));
322
if (nodeMap.get(centerNode) == null) {
323
nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
327
// Draw node in center
328
StapNode n = nodeMap.get(centerNode);
329
int x = this.getBounds().width / 2 - n.getSize().width/2;
330
int y = this.getBounds().height / 2;
333
if (getNodeData(centerNode).isMarked())
334
nodeMap.get(centerNode).setBackgroundColor(CONSTANT_MARKED);
335
radialHelper(centerNode, x, y, radius, 0);
339
* Helps animation of radial draw. Can be replaced by a draw and moveAll.
343
public void preDrawRadial(int centerNode) {
344
rootVisibleNodeNumber = centerNode;
346
if (nodeMap.get(centerNode) == null) {
347
nodeMap.put(centerNode, getNodeData(centerNode).makeNode(this));
348
StapNode n = nodeMap.get(centerNode);
349
n.setLocation(this.getBounds().width / 2, this.getShell()
353
//Pass coordinates of the node to radialHelper
354
StapNode n = nodeMap.get(centerNode);
355
int x = n.getLocation().x;
356
int y = n.getLocation().y;
357
radialHelper(centerNode, x, y, 0, 0);
361
* Completes radial-mode draws
367
* @param startFromChild
369
public void radialHelper(int id, int x, int y, int radius, int startFromChild) {
370
//-------------Draw parent node
371
// Draw caller node right beside this one, in a different color
372
int callerID = nodeDataMap.get(id).parent;
373
if (callerID != -1) {
374
if (getNode(callerID) == null) {
375
nodeMap.put(callerID, getNodeData(callerID).makeNode(this));
377
getNode(callerID).setBackgroundColor(CONSTANT_HAS_PARENT);
378
getNode(callerID).setLocation(x + radius / 5, y - radius / 5);
379
if (getNode(id).connection == null) {
380
getNode(id).makeConnection(SWT.NONE, getNode(callerID), getNodeData(id).timesCalled);
383
if (getNodeData(callerID).isMarked())
384
nodeMap.get(callerID).setBackgroundColor(CONSTANT_MARKED);
388
//-------------Draw children nodes
389
List<Integer> nodeList;
390
if (!collapse_mode) {
391
nodeList = nodeDataMap.get(id).children;
394
nodeList = nodeDataMap.get(id).collapsedChildren;
399
if (nodeList.size() >= CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS ) {
400
numberOfNodes = CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
403
numberOfNodes = nodeList.size();
407
if (numberOfNodes > 5)
408
angle = 2 * Math.PI / numberOfNodes;
410
angle = 2 * Math.PI / CONSTANT_MAX_NUMBER_OF_RADIAL_SIBLINGS;
414
for (i = 0; i < numberOfNodes; i++) {
417
int subID = nodeList.get(i);
420
if (nodeMap.get(subID) == null) {
421
nodeMap.put(subID, getNodeData(subID).makeNode(this));
424
StapNode subN = nodeMap.get(subID);
427
yOffset = (int) (radius * Math.cos((float) angle * i));
428
xOffset = (int) (radius * Math.sin((float) angle * i)) - subN.getSize().width/2 + getNode(id).getSize().width/2;
431
if (hasChildren(subID))
432
subN.setBackgroundColor(CONSTANT_HAS_CHILDREN);
433
subN.setLocation(x + xOffset, y + yOffset);
434
if (subN.connection == null) {
435
subN.makeConnection(SWT.NONE, nodeMap.get(id), nodeDataMap
436
.get(subID).timesCalled);
439
StapData d = getNodeData(subID);
441
subN.setBackgroundColor(CONSTANT_MARKED);
447
* Draws nodes according to the name of the function (not accounting for call
448
* heirarchies). Uses colour to indicate the number of calls and size to indicate
449
* the percentage time spent.
451
private void drawAggregateView(){
453
if (aggregateNodes == null){
454
aggregateNodes = new ArrayList<GraphNode>();
456
aggregateNodes.clear();
459
//-------------Format numbers
460
float percentage_time;
461
float percentage_count;
462
int maxTimesCalled = 0;
463
final int colorLevels = 15;
464
final int colorLevelDifference = 12;
468
NumberFormat num = NumberFormat.getInstance(Locale.CANADA);
469
num.setMinimumFractionDigits(2);
470
num.setMaximumFractionDigits(2);
472
//FIND THE MOST TIMES A FUNCTION IS CALLED
473
for (int val : aggregateCount.values()){
474
if ( val > maxTimesCalled){
475
maxTimesCalled = val;
480
//TEMPORARY STORAGE OF THE ENTRIES
481
//IMPLEMENTS A COMPARATOR TO STORE BY ORDER OF THE VALUE
482
TreeSet<Entry<String, Long>> sortedValues = new TreeSet<Entry<String, Long>>(StapGraph.VALUE_ORDER);
483
HashMap<String, Long> tempMap = new HashMap<String, Long>();
484
tempMap.putAll(aggregateTime);
486
for (String key : tempMap.keySet()) {
487
long time = aggregateTime.get(key);
488
//This is a stupid way to get the times right, but it is almost always guaranteed to work.
492
tempMap.put(key, time);
495
sortedValues.addAll(tempMap.entrySet());
497
//-------------Draw nodes
498
for (Entry<String, Long> ent: sortedValues) {
499
String key = ent.getKey();
500
GraphNode n = new GraphNode(this.getGraphModel(),SWT.NONE);
501
aggregateNodes.add(n);
503
percentage_count = (float)aggregateCount.get(key) / (float)maxTimesCalled;
504
percentage_time = ((float) ent.getValue()/ this
505
.getTotalTime() * 100);
507
n.setText(key + "\n" //$NON-NLS-1$
508
+ num.format((float)percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$
509
+ aggregateCount.get(key) + "\n") ; //$NON-NLS-1$
510
n.setData("AGGREGATE_NAME", key); //$NON-NLS-1$
513
primary = (int)(percentage_count * colorLevels * colorLevelDifference);
514
secondary = (colorLevels * colorLevelDifference) - (int)(percentage_count * colorLevels * colorLevelDifference);
516
primary = Math.max(0, primary);
517
secondary = Math.max(0, secondary);
519
primary = Math.min(primary, 255);
520
secondary = Math.min(secondary, 255);
523
Color c = new Color(this.getDisplay(),primary,0,secondary);
524
n.setBackgroundColor(c);
525
n.setHighlightColor(c);
526
n.setForegroundColor(new Color(this.getDisplay(),255,255,255));
527
n.setTooltip(new Label(
528
Messages.getString("StapGraph.Func")+ key + "\n" //$NON-NLS-1$ //$NON-NLS-2$
529
+ Messages.getString("StapGraph.Time") + num.format((float)percentage_time) + "%" + "\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
530
+ Messages.getString("StapGraph.NumOfCalls") + aggregateCount.get(key) //$NON-NLS-1$
536
//Set layout to gridlayout
537
this.setLayoutAlgorithm(new AggregateLayoutAlgorithm(LayoutStyles.NONE, sortedValues, this.getTotalTime(), this.getBounds().width), true);
543
* Draws a tree starting with node id, putting node id at location x,y
548
private void drawTree(int id, int x, int y) {
550
//-------------Create node id
552
if (nodeMap.get(id) == null) {
553
nodeMap.put(id, getNodeData(id).makeNode(this));
555
StapNode n = getNode(id);
557
n.setSize(n.getSize().width/scale, n.getSize().height/scale);
559
//This is the lowest level of nodes to draw, and it still has kids
560
if (getLevelOfNode(id) == bottomLevelToDraw &&
561
getNodeData(id).children.size() > 0)
562
n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
564
if (getNodeData(id).isMarked())
565
n.setBackgroundColor(CONSTANT_MARKED);
568
//-------------Get appropriate list of children
569
List<Integer> callees = null;
572
// Determine which list of callees to use
574
callees = getNodeData(id).children;
576
callees = getNodeData(id).collapsedChildren;
580
int cLevel = getLevelOfNode(id) + 1;
582
if (!collapse_mode) {
583
if (levels.get(cLevel) != null) {
584
usefulSize = levels.get(cLevel).size() - collapsedLevelSize.get(cLevel);
588
if (collapsedLevelSize.get(cLevel) != null)
589
usefulSize = collapsedLevelSize.get(cLevel);
591
//-------------Draw all children
592
for (int i = 0; i < callees.size(); i++) {
593
//Find the number of nodes on this level for spacing purposes
594
int childID = callees.get(i);
595
int childLevel = getLevelOfNode(childID);
598
//Initialise the offset to roughly centre the nodes
599
if (currentPositionInLevel.get(getLevelOfNode(childID)) == null) {
600
int tmp = (int) (CONSTANT_HORIZONTAL_SPACING*(usefulSize-1) * -1/scale);
601
currentPositionInLevel.put(childLevel, getNode(rootVisibleNodeNumber)
602
.getLocation().x + tmp);
605
//Recursive iteration
606
if (childLevel <= bottomLevelToDraw &&
607
childLevel <= lowestLevelOfNodesAdded) {
608
drawTree(callees.get(i), currentPositionInLevel.get(childLevel),
609
y + (int)(CONSTANT_VERTICAL_INCREMENT/scale));
611
//Do not scale newSize or nodes will no longer be adjacent
612
int newSize = currentPositionInLevel.get(getLevelOfNode(childID))
613
+ getNode(childID).getSize().width;
615
//Leave a small blank space between nodes for aesthetic purposes
616
if (i == callees.size() - 1)
617
newSize += CONSTANT_HORIZONTAL_SPACING/3;
618
currentPositionInLevel.put(getLevelOfNode(childID), newSize);
625
* Extend the tree downwards
627
public void extendTree() {
628
if (bottomLevelToDraw >= lowestLevelOfNodesAdded)
632
StapData data = getNodeData(rootVisibleNodeNumber);
633
if (data.children != null) {
634
if (data.children.size() < 1) {
639
List<Integer> list = data.children;
640
if (isCollapseMode())
641
list = data.collapsedChildren;
643
if (list.size() == 1) {
644
//Special case - only one child of the root node
645
//Therefore change root node to this new root node
646
int aMode = animation_mode;
647
draw(CONSTANT_DRAWMODE_TREE, CONSTANT_ANIMATION_FASTEST, list.get(0));
648
setAnimationMode(aMode);
653
List<Integer> bottomList = levels.get(bottomLevelToDraw);
656
for (int i : bottomList) {
657
if (getNode(i) != null) {
658
getNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
659
getParentNode(i).setBackgroundColor(DEFAULT_NODE_COLOR);
660
drawTree(i, getNode(i).getLocation().x, getNode(i).getLocation().y);
668
* Removes nodes from the bottom of the tree
670
public void shrinkTree() {
671
if (treeLevelFromRoot < 1)
676
deleteAll(rootVisibleNodeNumber);
678
int i = rootVisibleNodeNumber;
679
currentPositionInLevel.clear();
680
drawTree(i, getNode(i).getLocation().x, getNode(i).getLocation().y);
687
* Draws the next node, unless the next node does not exist.
689
public void drawNextNode() {
690
if (isCollapseMode()) {
691
setCollapseMode(false);
693
int toDraw = getNextCalledNode(getRootVisibleNodeNumber());
702
* Moves all nodes to the point x,y
706
public void moveAllNodesTo(int x, int y) {
707
for (int i: nodeMap.keySet()) {
708
nodeMap.get(i).setLocation(x,y);
714
* Draws a tree roughly starting from node id
716
public void drawBox(int id, int x, int y) {
718
int MaxLevelPixelWidth = 1;
719
int currPixelWidth = 1;
721
// FIND THE LEVEL THAT WILL BE THE WIDEST
722
// WILL BE A USEFUL VALUE LATER ON
726
for (int i = topLevelToDraw; i <= bottomLevelToDraw; i++) {
728
levels.get(i).add(0, count);
729
int size = levels.get(i).size();
730
for (int j = 1; j < size; j++){
731
int val = levels.get(i).get(j);
732
StapData data = nodeDataMap.get(val);
733
if (!data.isOnlyChildWithThisName()) {
734
if (collapse_mode && data.isPartOfCollapsedNode()) {
737
if (!collapse_mode && data.isCollapsed)
741
currPixelWidth += data.name.length() * 10 + StapGraph.CONSTANT_HORIZONTAL_SPACING_FOR_LEVEL;
742
if (MaxLevelPixelWidth < currPixelWidth) {
743
MaxLevelPixelWidth = currPixelWidth;
746
levels.get(i).remove(0);
747
levels.get(i).add(0, count);
752
MaxLevelPixelWidth = (int)(MaxLevelPixelWidth/scale);
754
if (id == getFirstUsefulNode())
755
nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth/2),y);
757
drawFromBottomToTop(bottomLevelToDraw, y
758
+ ((bottomLevelToDraw - topLevelToDraw ) * 3 * (int)(CONSTANT_VERTICAL_INCREMENT/scale)),
761
if (id == getFirstUsefulNode())
762
nodeMap.get(id).setLocation(150 + (MaxLevelPixelWidth/2),y);
766
public void drawFromBottomToTop(int level, int height,
767
int MaxLevelPixelWidth) {
769
// FINISHED DRAWING THE ROOT IN THE LAST RECURSIVE CALL
770
if (level == 0 || level < topLevelToDraw ) {
774
// FIND ALL THE CHILDREN AT LEVEL 'level'
775
int total = levels.get(level).remove(0);
779
for (int i = 0; i < levels.get(level).size(); i ++) {
780
int id = levels.get(level).get(i);
782
StapData data = nodeDataMap.get(id);
783
if (!data.isOnlyChildWithThisName()) {
784
if (collapse_mode && data.isPartOfCollapsedNode() ) {
787
if (!collapse_mode && nodeDataMap.get(id).isCollapsed)
791
if (nodeMap.get(id) == null) {
792
nodeMap.put(id, getNodeData(id).makeNode(this));
795
StapNode n = nodeMap.get(id);
798
n.setSize(n.getSize().width/scale, n.getSize().height/scale);
799
//Placement algorithm
800
if (getAnimationMode() == CONSTANT_ANIMATION_SLOW){
802
if (counter <= ANIMATION_TIME)
803
Animation.markBegin();
804
n.setLocation(150 + (nodeMap.get(getRootVisibleNodeNumber()).getLocation().x),nodeMap.get(getRootVisibleNodeNumber()).getLocation().y);
805
n.setLocation(150 + (MaxLevelPixelWidth / (total + 1) * count),height);
807
if (counter <= ANIMATION_TIME) {
808
Animation.run(ANIMATION_TIME/nodeMap.size()/3);
809
counter+=ANIMATION_TIME/nodeMap.size();
813
n.setLocation(150 + (MaxLevelPixelWidth / (total + 1) * count),height);
816
//IF WE CANNOT DISPLAY ALL NODES COLOUR NODES ON BOTTOM THAT STILL HAVE CHILDREN
817
if (level == bottomLevelToDraw && nodeDataMap.get(id).children.size() != 0){
818
n.setBackgroundColor(CONSTANT_HAS_CHILDREN);
823
if (getNodeData(n.id).isMarked())
824
n.setBackgroundColor(CONSTANT_MARKED);
827
// FIND ALL THE NODES THAT THIS NODE CALLS AND MAKE CONNECTIONS
828
List<Integer> setOfCallees = null;
830
setOfCallees = nodeDataMap.get(id).collapsedChildren;
832
setOfCallees = nodeDataMap.get(id).children;
834
for (int val : setOfCallees) {
835
if (nodeMap.get(val) != null)
836
nodeMap.get(val).makeConnection(SWT.NONE, n,
837
nodeDataMap.get(val).timesCalled);
842
// DRAW THE NEXT LEVEL UP
843
drawFromBottomToTop(level - 1, height - (3 * (int)(CONSTANT_VERTICAL_INCREMENT/scale)),
850
* Level/node management
854
* Delete all nodes except for the node with the specified nodeID
857
* - id of node NOT to delete (use -1 for 'no exceptions')
859
public void deleteAll(int exception) {
860
//-------------Delete aggregate nodes
861
if (aggregateNodes != null){
862
for (GraphNode n : aggregateNodes){
865
aggregateNodes.clear();
868
//-------------Save exception node's location
871
if (exception != -1 && nodeMap.get(exception) != null) {
872
x = nodeMap.get(exception).getLocation().x;
873
y = nodeMap.get(exception).getLocation().y;
876
//-------------Delete all nodes
877
for (int i : nodeMap.keySet()) {
878
StapNode node = nodeMap.get(i);
887
//-------------Recreate exception
888
if (x != -1 && y != -1) {
889
StapNode n =getNodeData(exception).makeNode(this);
892
nodeMap.put(exception, n);
897
* Delete a number of levels from the top of the graph
899
* @param numberOfLevelsToDelete
901
private void deleteLevelsFromTop(int numberOfLevelsToDelete) {
903
if (numberOfLevelsToDelete <= 0)
906
for (int i = 0; i < numberOfLevelsToDelete; i++) {
907
List<Integer> level = levels.get(topLevelToDraw);
908
for (int j = 0; j < level.size(); j++) {
909
if (nodeMap.get(level.get(j)) != null)
910
nodeMap.remove(level.get(j)).dispose();
917
* Delete a number of levels from the bottom of the graph
919
* @param numberOfLevelsToDelete
921
private void deleteLevelsFromBottom(int numberOfLevelsToDelete) {
923
if (numberOfLevelsToDelete <= 0)
926
for (int i = 0; i < numberOfLevelsToDelete; i++) {
927
List<Integer> level = levels.get(getBottomLevelToDraw());
929
for (int j = 0; j < level.size(); j++) {
930
if (nodeMap.get(level.get(j)) != null)
931
nodeMap.remove(level.get(j)).dispose();
938
* Sets top level limit to the level of id, bottom level limit to top level
939
* limit + CONSTANT_LEVEL_BUFFER.
940
* Deletes extraneous levels, changes topLevelToDraw, bottomLevelToDraw
942
* Convenience method: Calls setLevelLimitsToLevel(levelOfNode(id))
944
* @param id - node to recenter with
946
public void setLevelLimits(int id) {
947
setTopLevelTo(getLevelOfNode(id));
951
* Sets top level limit to the given level, bottom level limit to top level
952
* limit + CONSTANT_LEVEL_BUFFER.
953
* Deletes extraneous levels, changes topLevelToDraw, bottomLevelToDraw
955
* @param id - node to recenter with
958
public void setTopLevelTo(int new_topLevelToDraw) {
959
changeLevelLimits(new_topLevelToDraw);
961
int new_bottomLevelToDraw = new_topLevelToDraw + levelBuffer;
962
if (new_bottomLevelToDraw > lowestLevelOfNodesAdded)
963
new_bottomLevelToDraw = lowestLevelOfNodesAdded;
965
deleteLevelsFromTop(new_topLevelToDraw - topLevelToDraw);
966
deleteLevelsFromBottom(getBottomLevelToDraw() - new_bottomLevelToDraw);
968
topLevelToDraw = new_topLevelToDraw;
969
bottomLevelToDraw = new_bottomLevelToDraw;
973
public boolean changeLevelLimits(int lvl) {
974
int numberOfNodes = 0;
977
int maxLevel = min(lvl + levelBuffer, lowestLevelOfNodesAdded);
979
for (int level = lvl; level < maxLevel; level++) {
980
for (int id : levels.get(level)) {
981
if (isCollapseMode())
982
list = getNodeData(id).collapsedChildren;
984
list = getNodeData(id).children;
986
numberOfNodes += list.size();
988
if (numberOfNodes > maxNodes) {
989
levelBuffer = max(0,level-1);
1001
* Convenience method to redraw everything.
1003
public void draw() {
1004
draw(getRootVisibleNodeNumber());
1008
* Convenience method to draw with current draw parameters. Equivalent to
1009
* draw(graph.draw_mode, graph.animation_mode, id)
1012
public void draw(int id) {
1013
draw(draw_mode, animation_mode, id);
1017
* Convenience method to draw with current draw parameters. Equivalent to
1018
* draw(graph.draw_mode, animation, id)
1021
public void draw(int animation, int id) {
1022
draw(draw_mode, animation, id);
1027
* Draws with the given modes.
1029
* @param animationMode
1032
public void draw(int drawMode, int animationMode, int id) {
1033
setDrawMode(drawMode);
1034
setAnimationMode(animationMode);
1035
if (nodeDataMap.get(id) == null)
1037
this.clearSelection();
1038
treeLevelFromRoot = 0;
1039
currentPositionInLevel.clear();
1043
this.setRedraw(false);
1044
if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
1046
GridData gd = (GridData) thumbCanvas.getLayoutData();
1048
thumbCanvas.setLayoutData(gd);
1049
thumbCanvas.setVisible(false);
1050
callgraphView.layout();
1054
gd = (GridData) treeComp.getLayoutData();
1056
treeComp.setLayoutData(gd);
1057
treeComp.setVisible(true);
1058
treeViewer.collapseToLevel(getNodeData(id), 1);
1059
treeViewer.expandToLevel(getNodeData(id), 1);
1062
} else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE){
1064
GridData gd = (GridData) treeComp.getLayoutData();
1066
treeComp.setLayoutData(gd);
1067
treeComp.setVisible(false);
1069
callgraphView.layout();
1071
gd = (GridData) thumbCanvas.getLayoutData();
1073
thumbCanvas.setLayoutData(gd);
1074
thumbCanvas.setVisible(false);
1078
GridData gd = (GridData) treeComp.getLayoutData();
1080
treeComp.setLayoutData(gd);
1081
treeComp.setVisible(false);
1083
callgraphView.layout();
1086
gd = (GridData) thumbCanvas.getLayoutData();
1088
thumbCanvas.setLayoutData(gd);
1089
thumbCanvas.setVisible(true);
1090
thumbCanvas.setBackground(this.getBackground());
1094
callgraphView.layout();
1095
this.setRedraw(true);
1098
//-------------Draw tree
1099
if (draw_mode == CONSTANT_DRAWMODE_TREE) {
1100
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
1101
if (nodeMap.get(id) == null)
1102
nodeMap.put(id, getNodeData(id).makeNode(this));
1103
int tempX = nodeMap.get(id).getLocation().x;
1104
int tempY = nodeMap.get(id).getLocation().y;
1105
Animation.markBegin();
1106
moveAllNodesTo(tempX, tempY);
1107
Animation.run(ANIMATION_TIME);
1111
rootVisibleNodeNumber = id;
1112
drawTree(id, this.getBounds().width / 2, 20);
1113
currentPositionInLevel.clear();
1116
Animation.markBegin();
1117
drawTree(id, this.getBounds().width / 2, 20);
1119
Animation.run(ANIMATION_TIME);
1120
getNode(id).unhighlight();
1124
rootVisibleNodeNumber = id;
1125
drawTree(id, this.getBounds().width / 2, 20);
1126
getNode(id).unhighlight();
1131
//-------------Draw radial
1132
else if (draw_mode == CONSTANT_DRAWMODE_RADIAL) {
1134
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
1135
rootVisibleNodeNumber = id;
1140
this.getLightweightSystem().getUpdateManager()
1143
Animation.markBegin();
1144
nodeMap.get(id).setLocation(this.getBounds().width / 2,
1145
this.getBounds().height / 2);
1147
Animation.run(ANIMATION_TIME);
1148
callgraphView.maximizeOrRefresh(false);
1157
//-------------Draw level
1158
else if (draw_mode == CONSTANT_DRAWMODE_LEVEL) {
1159
rootVisibleNodeNumber = id;
1160
if (animation_mode == CONSTANT_ANIMATION_SLOW) {
1161
if (nodeMap.get(id) == null)
1162
nodeMap.put(id, getNodeData(id).makeNode(this));
1164
Animation.markBegin();
1165
moveAllNodesTo(nodeMap.get(id).getLocation().x, nodeMap.get(id).getLocation().y);
1166
Animation.run(ANIMATION_TIME);
1173
if (nodeMap.get(id) == null)
1174
nodeMap.put(id, getNodeData(id).makeNode(this));
1182
//-------------Draw aggregate
1183
else if (draw_mode == CONSTANT_DRAWMODE_AGGREGATE) {
1184
rootVisibleNodeNumber = getFirstUsefulNode();
1186
drawAggregateView();
1189
if (getNode(id) != null)
1190
getNode(id).unhighlight();
1193
//AFTER FIRST LOADING LET THE GRAPH EXPAND TO FILL THE VIEW
1194
this.setLayoutData(new GridData(SWT.FILL,SWT.FILL,true,true));
1199
@SuppressWarnings("unchecked")
1201
* Unhighlights all selected nodes and sets selection to null
1203
public void clearSelection() {
1204
List<GraphNode> list = this.getSelection();
1206
for (GraphNode n : list) {
1207
if (n != null) n.unhighlight();
1209
this.setSelection(null);
1214
* THE FOLLOWING METHODS ARE NOT WELL TESTED
1216
* Some are not thoroughly tested, and some just plain don't work. Use at your peril!
1220
* Shift the given node to the given location, moving all children nodes
1228
public void moveRecursive(int id, int xTarget, int yTarget) {
1229
if (nodeMap.get(id) != null) {
1230
int x = nodeMap.get(id).getLocation().x;
1231
int y = nodeMap.get(id).getLocation().y;
1232
nodeMap.get(id).setLocation(x + xTarget, y + yTarget);
1234
//If a node is null, then its children must be null
1238
List<Integer> list = null;
1241
list = nodeDataMap.get(id).collapsedChildren;
1243
list = nodeDataMap.get(id).children;
1244
for (int i = 0; i < list.size(); i++) {
1245
moveRecursive(list.get(i), xTarget, yTarget);
1251
* Moves all nodes by the given amount. Adds xDiff, yDiff to the current x,y coordinates.
1255
public void moveAllNodesBy(int xDiff, int yDiff) {
1256
for (int id : nodeMap.keySet()) {
1257
if (nodeMap.get(id) == null) continue;
1259
int x = nodeMap.get(id).getLocation().x;
1260
int y = nodeMap.get(id).getLocation().y;
1261
getNode(id).setLocation(x + xDiff, y + yDiff);
1268
* Recursively collapses all children of node id, and puts them in the
1269
* collapsedCallees list of id.
1271
* At the end of this run, each collapsed node will have a list of other
1272
* collapsed nodes AND a list of non-collapsed nodes. So will node #id.
1274
* Uncollapsed nodes, however, will not have a list of collapsed nodes attached.
1276
* @param ID of node to start from (use getFirstUsefulNode() to collapse everything
1277
* @return True if successful
1279
public boolean recursivelyCollapseAllChildrenOfNode(int id) {
1280
//-------------Initialize
1281
//If all nodes have been collapsed, don't do anything
1282
setCollapseMode(true);
1284
if (nodeDataMap.get(id).children.size() == 0)
1286
nodeDataMap.get(id).hasCollapsedChildren = true;
1291
HashMap<String, Integer> newNodeMap = new HashMap<String, Integer>();
1293
for (int collapsedID : nodeDataMap.get(id).collapsedChildren) {
1294
newNodeMap.put(getNodeData(collapsedID).name, collapsedID);
1296
// id of 'collapsed' node, id of its uncollapsed twin
1297
HashMap<Integer, Integer> collapsedNodesWithOnlyOneNodeInThem = new HashMap<Integer, Integer>();
1298
int size = nodeDataMap.get(id).children.size();
1302
//-------------Iterate
1303
for (int i = 0; i < size; i++) {
1305
int childID = nodeDataMap.get(id).children.get(i);
1306
if (getNodeData(childID).isPartOfCollapsedNode())
1308
int childLevel = getLevelOfNode(childID);
1309
if (collapsedLevelSize.get(childLevel) == null)
1310
collapsedLevelSize.put(childLevel, 0);
1311
String nodeName = nodeDataMap.get(childID).name;
1314
* Aggregate data for the given node
1316
if (newNodeMap.get(nodeName) != null) {
1317
int aggregateID = newNodeMap.get(nodeName);
1319
if (collapsedNodesWithOnlyOneNodeInThem.get(aggregateID) != null) {
1321
//-------------Aggregate nodes - second node to be found
1322
// We still think this is an only child, but now we know better.
1323
// Create a new data node and aggregate
1324
this.loadData(SWT.NONE, aggregateID, nodeName, nodeDataMap
1325
.get(childID).getTime(), nodeDataMap.get(childID).timesCalled,
1326
id, nodeDataMap.get(childID).isMarked(), ""); //$NON-NLS-1$
1328
if (getNodeData(aggregateID).isMarked()) {
1329
markedCollapsedNodes.add(aggregateID);
1330
markedNodes.remove((Integer) aggregateID);
1333
nodeDataMap.get(id).children.remove((Integer) aggregateID);
1334
nodeDataMap.get(id).collapsedChildren.add(aggregateID);
1335
nodeDataMap.get(childID).setPartOfCollapsedNode(aggregateID);
1337
nodeDataMap.get(aggregateID).collapsedParent = id;
1339
// Aggregate the first node that we found, and set it
1340
// as the uncollapsed piece of the aggregate node
1341
int otherChildID = collapsedNodesWithOnlyOneNodeInThem
1343
aggregateData(nodeDataMap.get(aggregateID), nodeDataMap
1344
.get(otherChildID));
1345
collapsedNodesWithOnlyOneNodeInThem.remove(aggregateID);
1346
nodeDataMap.get(aggregateID).children.addAll(nodeDataMap
1347
.get(otherChildID).children);
1348
nodeDataMap.get(aggregateID).setPartOfCollapsedNode(StapData.NOT_PART_OF_COLLAPSED_NODE);
1350
nodeDataMap.get(otherChildID).setPartOfCollapsedNode(aggregateID);
1351
nodeDataMap.get(aggregateID).uncollapsedPiece = otherChildID;
1354
//-------------Aggregate - third and additional nodes
1355
aggregateData(nodeDataMap.get(aggregateID), nodeDataMap
1359
//-------------Complete aggregation
1360
nodeDataMap.get(aggregateID).children
1361
.addAll(nodeDataMap.get(childID).children);
1362
nodeDataMap.get(aggregateID).isCollapsed = true;
1364
if (nodeMap.get(childID) != null) {
1365
nodeMap.get(childID).setLocation(
1366
nodeMap.get(id).getLocation().x
1367
- nodeMap.get(id).getSize().width,
1368
nodeMap.get(id).getLocation().y);
1371
nodeDataMap.get(childID).setPartOfCollapsedNode(aggregateID);
1373
//-------------First child with this name
1375
idOfLastCollapsedNode--;
1376
newNodeMap.put(nodeName, idOfLastCollapsedNode);
1377
collapsedNodesWithOnlyOneNodeInThem.put(idOfLastCollapsedNode, childID);
1378
if (nodeMap.get(childID) != null) {
1379
nodeMap.get(childID).setLocation(
1380
nodeMap.get(id).getLocation().x,
1381
nodeMap.get(id).getLocation().y);
1384
int tmp = collapsedLevelSize.get(childLevel) + 1;
1385
collapsedLevelSize.put(childLevel, tmp);
1389
//-------------Handle nodes that only appeared once
1390
for (int i : collapsedNodesWithOnlyOneNodeInThem.keySet()) {
1391
int childID =collapsedNodesWithOnlyOneNodeInThem.get(i);
1392
nodeDataMap.get(childID).onlyChildWithThisName = true;
1393
nodeDataMap.get(id).collapsedChildren.add(childID);
1394
newNodeMap.remove(nodeDataMap.get(childID).name);
1395
nodeDataMap.get(childID).collapsedParent = id;
1396
//This node is technically a part of itself
1397
nodeDataMap.get(childID).setPartOfCollapsedNode(childID);
1399
if (getNodeData(childID).isMarked())
1400
markedCollapsedNodes.add(childID);
1405
//-------------Finish iterations
1406
for (int i : nodeDataMap.get(id).collapsedChildren) {
1407
recursivelyCollapseAllChildrenOfNode(i);
1410
collapsedNodesWithOnlyOneNodeInThem.clear();
1413
nodeDataMap.get(id).hasCollapsedChildren = true;
1418
* Add time, called values for the two given nodes, storing them inside
1419
* victim. Also adds marked collapsed nodes to markedCollapsedNodes list
1423
public void aggregateData(StapData target, StapData victim) {
1424
target.setTime(target.getTime()+ victim.getTime());
1425
target.timesCalled += victim.timesCalled;
1426
if (victim.isMarked() || target.isMarked()) {
1428
markedCollapsedNodes.add(target.id);
1433
* Convenience methods
1437
* Prints the name of every node on the given level
1440
public void printContents(int level) {
1441
if (levels.get(level) != null)
1443
MP.println("Contents of level " + level + ":\n"); //$NON-NLS-1$ //$NON-NLS-2$
1444
for (int i = 0; i < levels.get(level).size(); i++) {
1445
MP.println(nodeDataMap.get(levels.get(level).get(i)).name);
1447
MP.println("---------------------------"); //$NON-NLS-1$
1455
public StapNode getNode(int id) {
1456
return nodeMap.get(id);
1464
public StapData getNodeData(int id) {
1465
return nodeDataMap.get(id);
1469
* Recommend using getFirstUsefulNode instead.
1470
* @return First node in level 0
1472
public int getTopNode() {
1473
return levels.get(topLevelToDraw).get(0);
1477
* Recommend use of this function instead of getTopNode()
1478
* @return First node that is not the dummy first node
1480
public int getFirstUsefulNode() {
1485
if (nodeDataMap.get(id).name == CONSTANT_TOP_NODE_NAME) {
1489
// Get first node that is not TOP_NODE_NAME
1490
while (nodeDataMap.get(id) == null && id < idOfLastNode) {
1499
* @return Top level to draw - the level should be defined by draw algorithms
1501
public int getTopLevel() {
1502
return topLevelToDraw;
1506
* Returns the level of recursion associated with the given node.
1510
public int getLevelOfNode(int nodeID) {
1511
return nodeDataMap.get(nodeID).levelOfRecursion;
1515
* Returns true if the given node has any children.
1519
public boolean hasChildren(int nodeID) {
1520
if (nodeDataMap.get(nodeID).children.size() > 0)
1526
* Attempts to set dimensions (not used)
1530
public void setDimensions(int width, int height) {
1531
this.getBounds().width = width;
1532
this.getBounds().height = height;
1536
* Sets animation mode - all available modes are named
1537
* StapGraph.CONSTANT_ANIMATION_*
1540
public void setAnimationMode(int mode) {
1541
animation_mode = mode;
1542
if (mode == CONSTANT_ANIMATION_SLOW){
1543
callgraphView.getAnimation_slow().setChecked(true);
1544
callgraphView.getAnimation_fast().setChecked(false);
1545
}else if (mode == CONSTANT_ANIMATION_FASTEST){
1546
callgraphView.getAnimation_slow().setChecked(false);
1547
callgraphView.getAnimation_fast().setChecked(true);
1551
public void setCollapseMode(boolean value) {
1552
if (collapse_mode == value ||
1553
draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)
1556
if (draw_mode != StapGraph.CONSTANT_DRAWMODE_LEVEL) {
1557
if (collapse_mode) {
1558
//Collapsed to noncollapsed
1559
if (!getRootData().isOnlyChildWithThisName()) {
1560
//A collapsed node that isn't an only child must have an
1562
rootVisibleNodeNumber = getRootData().uncollapsedPiece;
1566
//Uncollapsed to collapsed -- set center node to collapsed node
1567
if (!getRootData().isOnlyChildWithThisName()) {
1568
int temp = getRootData().getPartOfCollapsedNode();
1569
if (temp != StapData.NOT_PART_OF_COLLAPSED_NODE) {
1570
rootVisibleNodeNumber = temp;
1575
collapse_mode = value;
1576
callgraphView.getMode_collapsednodes().setChecked(value);
1577
nextMarkedNode = -1;
1582
* @return getNodeData(getRootVisibleNodeNumber())
1584
public StapData getRootData() {
1585
return getNodeData(getRootVisibleNodeNumber());
1589
* Gets id of root visible node
1590
* @return rootVisibleNode - ID of centre node
1592
public int getRootVisibleNodeNumber() {
1593
return rootVisibleNodeNumber;
1597
* Sets id of root visible node
1598
* @param id - ID of centre node
1600
public void setRootVisibleNodeNumber(int id) {
1601
this.rootVisibleNodeNumber = id;
1605
* Gets to the total time spent running tapped program
1606
* @return Time in milliseconds
1608
public long getTotalTime() {
1609
if (totalTime == 0 || totalTime > 1200000000000000000l)
1610
return endTime - startTime;
1615
* Sets total time spent running tapped program
1616
* @param totalTime - Time in milliseconds
1618
public void setTotalTime(long val) {
1619
this.totalTime = val;
1624
* @return Number of the lowest level in which nodes exist
1626
public int getLowestLevelOfNodesAdded() {
1627
return lowestLevelOfNodesAdded;
1631
* Set lowest level to which nodes have been added
1633
* WARNING: Do not set this without adding nodes to that level first, or there
1634
* may be null pointer exceptions.
1635
* @param lowestLevelOfNodesAdded
1637
public void setLowestLevelOfNodesAdded(int lowestLevelOfNodesAdded) {
1638
this.lowestLevelOfNodesAdded = lowestLevelOfNodesAdded;
1642
public int getDrawMode() {
1646
public void setDrawMode(int draw_mode) {
1647
this.draw_mode = draw_mode;
1651
* Resets the tree and graph to center on the first useful node.
1652
* Does NOT change draw mode, animation mode or collapse mode.
1654
public void reset() {
1657
draw(draw_mode, animation_mode, getFirstUsefulNode());
1658
if (! (draw_mode == StapGraph.CONSTANT_DRAWMODE_AGGREGATE)){
1659
getNode(getFirstUsefulNode()).unhighlight();
1661
if (treeViewer!=null) {
1662
treeViewer.collapseAll();
1663
treeViewer.expandToLevel(2);
1666
nextMarkedNode = -1;
1670
public boolean isCollapseMode() {
1671
return this.collapse_mode;
1675
public void setBottomLevelToDraw(int bottomLevelToDraw) {
1676
this.bottomLevelToDraw = bottomLevelToDraw;
1680
public int getBottomLevelToDraw() {
1681
return bottomLevelToDraw;
1685
public void setTopLevelOnScreen(int topLevelOnScreen) {
1686
this.topLevelOnScreen = topLevelOnScreen;
1690
public int getTopLevelOnScreen() {
1691
return topLevelOnScreen;
1696
* Returns a list of all nodes at the given level.
1699
* @return List of ID's of all nodes at the given level.
1701
public List<Integer> getLevel(int level) {
1702
if (level < 0 || level > lowestLevelOfNodesAdded) {
1706
return levels.get(level);
1710
public TreeViewer getTreeViewer() {
1716
* Returns the StapNode object for the parent of node id. May be null.
1720
public StapNode getParentNode(int id) {
1721
return nodeMap.get(nodeDataMap.get(id).parent);
1726
* Returns a StapData object for the parent of node id. May be null.
1730
public StapData getParentData(int id) {
1731
return nodeDataMap.get(nodeDataMap.get(id).parent);
1735
* Returns the id of the next node that was called. This is necessarily either a
1736
* child of the current root node or of one of its ancestors' children.
1739
* @return Id of next node that was called.
1741
public int getNextCalledNode(int id) {
1744
if (isCollapseMode()) {
1745
setCollapseMode(false);
1746
//Redraw the current graph in uncollapsed mode if currently collapsed
1750
for (int count = callOrderList.indexOf((Integer)id) + 1;
1751
count < callOrderList.size(); count++) {
1752
int next = callOrderList.get(count);
1753
if (getNodeData(id) == null)
1755
if (!getNodeData(next).isCollapsed || getNodeData(next).isOnlyChildWithThisName()) {
1765
* Returns the id of the previous node that was called.
1768
* @return Id of previous node that was called.
1770
public int getPreviousCalledNode(int id) {
1773
for (int count = callOrderList.indexOf((Integer)id) - 1;
1774
count > -1; count--) {
1775
if (getNodeData(id) == null)
1777
if (!getNodeData(id).isCollapsed || getNodeData(id).isOnlyChildWithThisName()) {
1778
returnID = callOrderList.get(count);
1790
* Returns the id of the next marked node in current collapse mode.
1791
* Wraps back to the first marked node.
1793
* @return Node id of next marked node.
1795
public int getNextMarkedNode() {
1796
List<Integer> list = markedNodes;
1798
list = markedCollapsedNodes;
1800
if (list.size() == 0)
1805
if (nextMarkedNode >= list.size())
1809
return list.get(nextMarkedNode);
1814
* Returns the id of the next marked node in current collapse mode.
1815
* Wraps back to the first marked node.
1817
* @return Node id of next marked node.
1819
public int getPreviousMarkedNode() {
1820
List<Integer> list = markedNodes;
1822
list = markedCollapsedNodes;
1824
if (list.size() == 0)
1828
if (nextMarkedNode < 0)
1829
nextMarkedNode = list.size() - 1;
1832
return list.get(nextMarkedNode);
1836
public void play() {
1837
if (proj == null || proj.getResult() == Status.OK_STATUS) {
1838
proj = new Projectionist("Projectionist", this, 2000); //$NON-NLS-1$
1847
public static final Comparator<Entry<String, Long>> VALUE_ORDER = new Comparator<Entry<String, Long>>()
1849
public int compare(Entry<String, Long> a, Entry<String, Long> b){
1850
return ((Long)a.getValue()).compareTo(((Long)b.getValue()));
1856
* Increments the scrollbars by x, y
1861
public void scrollBy(int x, int y) {
1862
this.scrollTo(this.getHorizontalBar().getSelection() + x,
1863
this.getVerticalBar().getSelection() + y);
1868
* Smoothly increments the scrollbars by x, y
1873
public void scrollSmoothBy(int x, int y) {
1874
this.scrollSmoothTo(this.getHorizontalBar().getSelection() + x,
1875
this.getVerticalBar().getSelection() + y);
1879
* Retruns the number of StapData objects placed in the nodeDataMap.
1882
public int getNodeDataMapSize() {
1883
return nodeDataMap.size();
1887
public int getAnimationMode() {
1888
return animation_mode;
1892
public int getLevelBuffer() {
1897
public void setLevelBuffer(int val) {
1901
public int min(int a, int b) {
1902
if (a < b) return a;
1906
public int max(int a, int b) {
1907
if (a > b) return a;
1911
public int getMaxNodes() {
1915
public void setMaxNodes(int val) {
1919
public ArrayList<Integer> getCallOrderList() {
1920
return callOrderList;
1923
public void setCallOrderList(ArrayList<Integer> callOrderList) {
1924
this.callOrderList = callOrderList;
1927
public int getLastFunctionCalled() {
1928
return lastFunctionCalled;
1931
public void setLastFunctionCalled(int lastFunctionCalled) {
1932
this.lastFunctionCalled = lastFunctionCalled;
1935
public ICProject getProject() {
1939
public Projectionist getProjectionist() {
1943
public void setProject(ICProject myProject) {
1944
this.project = myProject;
1947
public CallgraphView getCallgraphView() {
1948
return callgraphView;
1951
public void setEndTime(long val) {
1955
public long getEndTime() {
1959
public void setStartTime(long val) {
1963
public void setThreaded() {
1967
public boolean getCollapseMode() {
1968
return collapse_mode;
1973
public void addCalled(int idChild) {
1974
getNodeData(idChild).timesCalled++;