sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi/src/com/nokia/carbide/cpp/internal/pi/visual/GenericTraceGraph.java
changeset 2 b9ab3b238396
child 5 844b047e260d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi/src/com/nokia/carbide/cpp/internal/pi/visual/GenericTraceGraph.java	Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of the License "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description: GenericTraceGraph.java 
+ *
+ */
+
+package com.nokia.carbide.cpp.internal.pi.visual;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.Panel;
+import org.eclipse.draw2d.geometry.Point;
+import org.eclipse.draw2d.geometry.PointList;
+import org.eclipse.draw2d.geometry.Rectangle;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Display;
+
+import com.nokia.carbide.cpp.internal.pi.model.GenericTrace;
+import com.nokia.carbide.cpp.internal.pi.model.ProfiledGeneric;
+import com.nokia.carbide.cpp.pi.util.ColorPalette;
+
+
+public abstract class GenericTraceGraph
+{
+	protected int graphIndex = 0;
+	private static final int ALPHA_UNSELECTED = 175;
+	private static final int SELECTION_BAR_WIDTH = 2;
+	
+	// amount of space at left of graph to contain y-axis units
+	public static final int yLegendWidth = 50;
+
+    // fill selection flags
+    private double timeOffset = 0; 
+    protected boolean debug = false;
+    
+    // PI time scale multiplier
+    private float piTimeScale = 1;
+    private boolean timescalingEnabled = false;
+    
+    private int visibleAreaIdentifier = 0; //is used to detect changes in drawing area
+    private boolean updatePolylinesIsNeeded = true;
+    public boolean updateCumulativeThreadTableIsNeeded = true;
+    
+	public boolean fillFlag = false;	 //fill items is selected, all or selected?
+	public boolean fillSelected = false; //fill selected items
+	
+    private int visibleRightBorder; //these are updated when the visible area changes / polylines need to be updated
+    private int visibleLeftBorder;
+    
+	private int sizeX = 0;
+	private int sizeY = 0;
+
+	private int visualSizeX = 0;
+	private int visualSizeY = 0;
+	
+	private int preferredSizeX = 0;
+	private int preferredSizeY = 0;
+
+	private double selectionStart = -1;
+	private double selectionEnd = -1;
+   
+//	private double scaleX = 10;	 //default when creating a new analysis
+	private double scaleY = 100; //default for percentages
+	
+	private double scale = 0;
+    private boolean highResolution = true;
+	
+	public PICompositePanel parentComponent;
+
+	private GenericTrace myTrace;
+	
+	private Vector<GenericTraceGraph> graphSubComponents;
+	
+	private Image graphImage = null;
+	private boolean graphImageChanged = true;
+
+	public GenericTraceGraph(GenericTrace data)
+	{
+		this.graphSubComponents = new Vector<GenericTraceGraph>();
+		this.myTrace = data;
+	}
+	
+	public GenericTrace getTrace()
+	{
+		return this.myTrace;
+	}
+    
+    public void setTimeOffset(double offset)
+    {
+        this.timeOffset = offset;
+    }
+
+    public double getTimeOffset()
+    {
+        return this.timeOffset;
+    }
+	
+	public void addSubGraphComponent(GenericTraceGraph subGraph)
+	{
+		this.graphSubComponents.add(subGraph);
+	}
+	
+	public Enumeration getGraphSubComponents()
+	{
+		return graphSubComponents.elements();
+	}
+	
+	public void importParentComponent(PICompositePanel parent)
+	{
+		this.parentComponent = parent;
+	}
+	
+	public void setCurrentInfoComponent(Component infoComponent)
+	{
+		if (parentComponent instanceof PICompositePanel)
+		{
+			((PICompositePanel)parentComponent).
+				setCurrentInfoComponent(infoComponent);
+		}
+	}
+	
+	public PIVisualSharedData getSharedDataInstance()
+	{
+		if (parentComponent instanceof PICompositePanel)
+		{
+			return ((PICompositePanel)parentComponent).getSharedData();
+		}
+		else
+			return null;
+	}
+	
+	// a rectangle that describes visible area fo the draw 2D graphics so we paint only that
+	public Rectangle getVisibleArea(Graphics graphics) {
+		return graphics.getClip(new org.eclipse.draw2d.geometry.Rectangle());
+	}
+	
+	public abstract void paint(Panel panel, Graphics graphics);
+	
+	public abstract void paintLeftLegend(FigureCanvas figureCanvas, GC gc);
+	
+	// call all subcomponents' repaint methods
+	public abstract void repaint();
+	
+	public abstract void refreshDataFromTrace();
+	
+	public abstract void action(String action);
+	
+	public void setSize(int x, int y)
+	{
+		this.sizeY = y;
+		this.sizeX = x;
+	}
+	
+	public void setPITimeScale(float scale)
+	{
+	    System.out.println(Messages.getString("GenericTraceGraph.setTimeScaleTo") + scale); //$NON-NLS-1$
+	    this.piTimeScale = scale;
+	    
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setPITimeScale(scale);
+	    }
+	}
+	
+	public void setTimescalingEnabled(boolean flag)
+	{
+	    this.timescalingEnabled = flag;
+	    
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setTimescalingEnabled(flag);
+	    }
+	}
+	
+	public float getPITimeScale()
+	{
+	    if (this.timescalingEnabled)
+	        return this.piTimeScale;
+	    else
+	        return 1f;
+	}
+	
+	public Dimension getSize()
+	{
+		return new Dimension(this.sizeX, this.sizeY);
+	}
+	
+	public void setVisualSize(int x, int y)
+	{
+		this.visualSizeX = x;
+		this.visualSizeY = y;
+		
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setVisualSize(x, y);
+	    }
+	}
+	
+	public Dimension getVisualSize()
+	{
+		return new Dimension(this.visualSizeX, this.visualSizeY);
+	}
+	
+	public Dimension getPreferredSize()
+	{
+		return new Dimension(this.preferredSizeX, this.preferredSizeY);
+	}
+	
+	public void setSelectionStart(double start)
+	{
+        if (highResolution)
+            selectionStart = start;
+        else
+            selectionStart = (int) (start / 100 + 0.5) * 100;
+		
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setSelectionStart(selectionStart);
+	    }
+	}
+
+	public void setSelectionEnd(double end)
+	{
+        if (highResolution)
+            this.selectionEnd = end;
+        else
+            selectionEnd = (int) (end / 100 + 0.5) * 100;
+
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setSelectionEnd(selectionEnd);
+	    }
+	}
+    
+    public void setHighResolution(boolean flag)
+    {
+        this.highResolution = flag;
+    }
+    
+	public double getSelectionStart()
+	{
+		return this.selectionStart;
+	}
+
+	public double getSelectionEnd()
+	{
+		return this.selectionEnd;
+	}
+	
+	public void setScale(double scaleX, double scaleY)
+	{
+//	    if (scaleX > 0)
+//	        this.scaleX = scaleX;
+	    if (scaleY > 0)
+	        this.scaleY = scaleY;
+	    
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setScale(scaleX, scaleY);
+	    }
+	}
+	
+	public double getScale()
+	{
+		return this.scale;
+	}
+	
+	public void setScale(double scale)
+	{
+        this.scale = scale;
+		
+	    Enumeration sc = this.getGraphSubComponents();
+	    while (sc.hasMoreElements())
+	    {
+	    	GenericTraceGraph gtc = (GenericTraceGraph)sc.nextElement();
+	    	gtc.setScale(scale);
+	    }
+	}
+	
+	public void setToolTipText(String text)
+	{
+		this.parentComponent.setToolTipTextForGraphComponent(this, text);
+	}
+	
+	public PICompositePanel getCompositePanel()
+	{
+		return this.parentComponent;
+	}
+	
+	//new generic method to draw background
+	public void drawDottedLineBackground(Graphics graphics, int yLegendSpace)
+	{
+		Rectangle visibleArea = getVisibleArea(graphics);
+		int visY = this.getVisualSize().height;
+		float visYfloat = visY - yLegendSpace;
+
+		// draw the dotted lines
+		graphics.setForegroundColor(ColorPalette.getColor(new RGB(200, 200, 200)));
+
+		// vertical lines
+		// float values will be slightly smaller than the actual result
+		// and they will be incremented by one, since rounding to int
+		// discards the remaining decimals
+		int k = 0;
+		for (float y = 0; k <= 10; y += visYfloat * 10000f / 100001f, k++)
+		{
+			for (int x = visibleArea.x; x <= visibleArea.x + visibleArea.width; x += 5)
+			{
+				if ((x / 5) % 2 == 0) graphics.drawLine(x, ((int)y) + 1, x + 5, ((int)y) + 1);
+			}
+		}
+
+		// horizontal lines
+		if (visibleArea.width > 0)
+		{
+			for (int x = visibleArea.x; x <= visibleArea.x + visibleArea.width; x += 50)
+			{
+				if (x % 100 == 0)
+					graphics.setForegroundColor(ColorPalette.getColor(new RGB(100, 100, 100)));
+				else
+					graphics.setForegroundColor(ColorPalette.getColor(new RGB(200, 200, 200)));
+				
+				for (int y = 0; y < visY; y += 5)
+				{
+					if ((y / 5) % 2 == 0)
+						graphics.drawLine(x, y, x, y + 5);
+				}
+			}
+		}
+
+		// draw the line indices
+		graphics.setForegroundColor(ColorPalette.getColor(new RGB(100, 100, 100)));
+		graphics.setBackgroundColor(ColorPalette.getColor(new RGB(255, 255, 255)));
+		for (int x = visibleArea.x; x <= visibleArea.x + visibleArea.width; x += 50)
+		{
+			double time = (double) x;
+			time = time * this.getScale();
+            time += this.timeOffset;
+			time = time / 1000;
+
+			String stringTime = String.valueOf(time);
+			if (stringTime.length() > 4)
+			{
+				int i;
+				for (i = 0; i < stringTime.length(); i++)
+					if (stringTime.charAt(i) == '.')
+						break;
+					
+				if (i + 4 < stringTime.length())
+					stringTime = stringTime.substring(0, i + 4);
+			}
+			graphics.drawString(stringTime + Messages.getString("GenericTraceGraph.seconds"), x + 5, visY - 13); //$NON-NLS-1$
+	    }
+	}
+	
+	public void drawSelectionSection(Graphics graphics, int yLegendSpace)
+	{
+        //draws the new selection
+        this.drawSelection(graphics, selectionStart, selectionEnd, yLegendSpace);
+ 	}
+    
+    protected void drawSelection(Graphics graphics, double start, double end, int yLegendSpace)
+    {
+    	if (start > end) {
+    		double tmp = end;
+    		end = start;
+    		start = tmp;
+    	}
+
+        double scale = getScale();
+
+        int savedAlpha   = graphics.getAlpha();
+		Color savedColor = graphics.getForegroundColor();
+		
+		// shade unselected area with a fixed alpha of darker gray
+		Color selectColor = ColorPalette.getColor(new RGB(170, 170, 170));
+		graphics.setBackgroundColor(selectColor);
+		graphics.setAlpha(ALPHA_UNSELECTED);
+		
+		Point origin = this.parentComponent.getScrolledOrigin();
+        
+		int visX = this.getVisualSizeX();
+		int visY = this.getVisualSizeY()- yLegendSpace;
+
+		if (start != -1 && end != -1)
+        {
+            // mask with alpha adjusted the unselected area before the indicators
+			if (origin.x < (start / scale) - SELECTION_BAR_WIDTH)
+				graphics.fillRectangle(origin.x, 0,
+						(int)(start / scale) - SELECTION_BAR_WIDTH - origin.x, visY);
+
+			// mask with alpha adjusted the unselected area after the indicators
+			if ((int)(end/scale + SELECTION_BAR_WIDTH) < origin.x + visX)
+	           	graphics.fillRectangle((int)(end / scale + SELECTION_BAR_WIDTH), 0,
+	           			origin.x + visX - (int)(end / scale + SELECTION_BAR_WIDTH), visY);
+
+            // draw two indicators		
+            graphics.setForegroundColor(ColorPalette.getColor(new RGB(255, 255, 0)));
+            for (int i = 0; i < SELECTION_BAR_WIDTH; i++) {
+            	graphics.drawLine((int)(start/scale) - i, 0, (int)(start/scale) - i, visY);
+            	graphics.drawLine((int)(end/scale)   + i, 0, (int)(end/scale)   + i, visY);
+            }
+        }
+		else
+		{
+            // mask the entire visible area with alpha adjusted
+			graphics.fillRectangle(origin.x, 0, visX, visY);
+		}
+
+		//restore proper alpha
+		graphics.setBackgroundColor(savedColor);
+		graphics.setAlpha(savedAlpha);
+	}
+	
+	protected void drawThreadMarks(ProfiledGeneric pg, int visY, Graphics graphics)
+	{
+		Rectangle visibleArea = getVisibleArea(graphics);
+		int firstSample = (int)(pg.getFirstSample() / this.getScale());
+		int lastSample  = (int)(pg.getLastSample()  / this.getScale());
+		
+		// Thread start mark
+		if (firstSample >= visibleArea.x && firstSample <= visibleArea.x + visibleArea.width) {
+			graphics.drawLine(firstSample,     (visY - 50), firstSample,     (visY - 40));
+			graphics.drawLine(firstSample + 1, (visY - 50), firstSample + 1, (visY - 40));
+			graphics.drawLine(firstSample - 1, (visY - 50), firstSample - 1, (visY - 40));
+			graphics.drawLine(firstSample,(visY - 40), firstSample + 10, (visY - 40));
+			graphics.drawLine(firstSample,(visY - 41), firstSample + 10, (visY - 41));
+			graphics.drawLine(firstSample,(visY - 39), firstSample + 10, (visY - 39));
+	
+		}
+		
+		// Thread end mark
+		if (lastSample >= visibleArea.x && lastSample <= visibleArea.x + visibleArea.width) {
+			graphics.drawLine(lastSample,      (visY - 50), lastSample,      (visY - 40));
+			graphics.drawLine(lastSample  + 1, (visY - 50), lastSample  + 1, (visY - 40));
+			graphics.drawLine(lastSample  - 1, (visY - 50), lastSample  - 1, (visY - 40));
+			graphics.drawLine(lastSample, (visY - 40), lastSample  - 10, (visY - 40));
+			graphics.drawLine(lastSample, (visY - 41), lastSample  - 10, (visY - 41));
+			graphics.drawLine(lastSample, (visY - 39), lastSample  - 10, (visY - 39));
+		}
+	}
+	
+	public void updateVisibleBorders()
+	{
+	    SashForm rect = this.parentComponent.getSashForm();	    
+	    int scale = (int) this.getScale();
+	    int correctionValue = 10;
+
+	    // calculate display overscan to remove graphical glitches
+        if (scale >= 10)
+            correctionValue = 10;
+        else if (scale < 10 && scale > 1)
+	        correctionValue = 100 - (scale * 10);
+	    else if ( scale <= 1 && scale > 0.3)
+	        correctionValue = 200;
+        else if ( scale <= 0.3)
+            correctionValue = 800;
+
+        Point origin = this.parentComponent.getScrolledOrigin();
+
+		visibleLeftBorder  = origin.x - (int)(correctionValue * 1.2);
+		visibleRightBorder = origin.x + rect.getBounds().width + (int)(correctionValue * 1.1);
+	}
+	
+	public void genericRefreshCumulativeThreadTable()
+	{
+	    updateCumulativeThreadTableIsNeeded = true;
+	}
+	
+	protected void genericRefreshCumulativeThreadTable(Enumeration<ProfiledGeneric> threads)
+	{	
+	  	int[] cumulativePerc = null;
+
+	  	while (threads.hasMoreElements())
+	  	{		
+			ProfiledGeneric pg = threads.nextElement();
+
+			// go through all samples
+	        if (pg.isEnabled(graphIndex))   
+	        {
+	        	// zero this profiled generic's cumulative values
+	            pg.setupCumulativeList(graphIndex);
+
+				// Note: getSampleList() and getActivityList() create the lists
+				int[] samples = pg.getSampleList();
+				int[] values  = pg.getActivityList();
+
+				if (samples == null || values == null || samples.length == 0)
+				    return;
+				
+			  	if (cumulativePerc == null)
+			  	{
+			  		// create a zeroed array of cumulative values
+			  		cumulativePerc = new int[samples.length];
+			  	}
+
+				for (int sampIndx = 0; sampIndx < samples.length; sampIndx++)
+				{
+					// get this profiled generic's sample count in each chunk of samples
+					int thisValue = values[sampIndx];
+				  	
+					int oldCumValue = cumulativePerc[sampIndx];
+							
+					// add the percentage to the current cumulative value
+					cumulativePerc[sampIndx] += thisValue;
+
+					// set the profiled generic's cumulative value for this bucket
+					pg.setCumulativeValue(graphIndex, sampIndx, oldCumValue);
+				}
+	        }
+	  	}
+	  	updatePolylinesIsNeeded = true;
+	  	updateCumulativeThreadTableIsNeeded = false;
+  	}
+	
+    private void updatePolyLinesGeneric(Enumeration enumer)
+    {
+        // updatePolylinesIsNeeded will be true if refreshCumulativeThreadTable has been invoked
+	    /******************************************************************/
+		if (!updatePolylinesIsNeeded)
+		{
+		    int visY = this.getVisualSize().height;
+		    int newVisibleAreaIdentifier;
+		    SashForm rect = this.parentComponent.getSashForm();	    
+		    Point point = this.parentComponent.getScrolledOrigin();
+		    int scale = (int) this.getScale();
+		    
+			int visibleLeft  = point.x;
+			int visibleRight = point.x + rect.getBounds().width;
+		    
+			newVisibleAreaIdentifier = scale * 13 + visibleLeft + visibleRight * 2 + visY * 3;
+	
+			if (!(visibleAreaIdentifier == newVisibleAreaIdentifier))
+			    updatePolylinesIsNeeded = true;
+			
+			visibleAreaIdentifier = newVisibleAreaIdentifier;
+		}
+		/**********************************************************/
+
+		if (updatePolylinesIsNeeded)
+			this.updatePolyLinesGeneric(enumer, (int) this.scaleY);
+    }
+	  
+	private void updatePolyLinesGeneric(Enumeration enumer, int heightDivider)
+	{
+	    if (!updatePolylinesIsNeeded)
+	        return;
+	    
+	    this.updateVisibleBorders();
+		int visY = this.getVisualSize().height;
+	  	
+	  	float xscale;
+	  	if (this.timescalingEnabled)
+	  	    xscale = this.piTimeScale;
+	  	else
+	  	    xscale = 1;
+	  	
+	  	// draw one thread/binary/function at a time
+	  	ProfiledGeneric pg = null;
+	  	while (enumer.hasMoreElements())
+	  	{
+  	        pg = (ProfiledGeneric ) enumer.nextElement();
+	  
+	  		if (pg.isEnabled(graphIndex)) //is visualised
+		  	{
+	  		    if (debug)
+	  		        System.out.println(Messages.getString("GenericTraceGraph.debug")+pg.getNameString()); //$NON-NLS-1$
+	  		    
+			  	// get samples and their corresponding values
+			  	int[] samples = pg.getSampleList();  //time stamps
+			  	int[] values = pg.getActivityList(); //percentage values
+			  	
+			  	// if a ProfiledGeneric has a null cumulative list, give
+			  	// it a list of zeros
+			  	if (pg.getCumulativeList(graphIndex) == null) {
+			  		pg.setupCumulativeList(graphIndex);
+			  	}
+
+			  	int[] cumulatives = pg.getCumulativeList(graphIndex);
+
+		  	    pg.resetPolyline(graphIndex);
+			  	    
+			  	double tmpScale = this.getScale();
+			  	    
+			  	// go through all samples
+        
+	            int thisSample, x, cumValue, thisValue;
+	            int y = visY - 50;
+	            
+			    for (int sampIndx = 0; sampIndx < samples.length; sampIndx++)
+				{
+		            // the x value in thisSample is the timestamp
+		            thisSample = samples[sampIndx];
+		            x = (int)(thisSample / tmpScale / xscale);
+
+		            // the y value is the percentage
+		            thisValue = values[sampIndx];	
+		            cumValue = cumulatives[sampIndx];
+		            y = ((thisValue + cumValue) * (visY - 50)) / heightDivider;
+
+                    pg.addPointToPolyline(graphIndex, x, (visY - 50) - y);
+				} // for
+	  		} //if (pg.isenabled)
+	  	}//while (enum.hasmoreElements)
+	  	updatePolylinesIsNeeded = false;
+	}
+	
+	public void drawGraphsGeneric(Vector<ProfiledGeneric> profiledGenerics, Graphics graphics, Object[] selection)
+	{
+	    if (profiledGenerics.elements() == null)
+	       return;
+	    
+	    // update cumulative thread table
+	    if (this.updateCumulativeThreadTableIsNeeded)
+	       this.genericRefreshCumulativeThreadTable(profiledGenerics.elements());
+	    
+	    // update polylines based on cumulative thread table
+        this.updatePolyLinesGeneric(profiledGenerics.elements());
+	    
+	  	boolean threadIsSelected = false;
+	  	int visY = this.getVisualSize().height;
+	  	ProfiledGeneric pg;
+
+  		GC gc = null;
+  		boolean useImage = false;
+  		int width = sizeX;
+  		int height= sizeY;
+  		// if image < 16KB and image can be allocated, use a pre drawn image instead of
+  		// painting on draw2D graphics, scrolling and repaint from coming into focus
+  		// are faster
+  		if (width * height * 4 <= 16777216){	// 16KB image under 32bit color
+  			if (getGraphImageChanged()) {
+  			  	if (graphImage != null) {
+  	  		  		graphImage.dispose();
+  	  		  	}
+  			  	try {
+  			  		graphImage = new Image(Display.getDefault(), width, height);
+  			  		gc = new GC(this.graphImage);
+  			  		useImage = true;
+  			  	} catch (SWTError e) {
+  			  		// we cannot allocate any more image, stick to slow draw2D
+  			  	}
+  		  	} else {
+  		  		useImage = true;
+  		  	}
+  		} else {
+  			if (graphImage != null) {
+  	  		  	graphImage.dispose();	// don't need this anymore, use draw2D
+  	  		}
+  		}
+	  	
+		if (useImage == false || getGraphImageChanged() == true) { // using graphics for paint or image needs update
+		  	// draw one thread/binary/function at a time
+		  	for (int i = profiledGenerics.size() - 1; i >= 0; i--)
+		  	{
+	  	        pg = profiledGenerics.get(i);
+			  
+		  		if (pg.isEnabled(this.graphIndex)) //is visualised
+			  	{
+				  	// get samples and their corresponding values
+				  	int[] samples = pg.getSampleList();   //time stamps
+				  	int[] values  = pg.getActivityList(); //percentage values
+				  	int[] cumulatives = pg.getCumulativeList(this.graphIndex);
+
+				  	if (this.fillSelected)  //selected items are filled, not all of them
+				  	{
+				  		threadIsSelected = selection.length > 0;
+				  	}
+				  	
+					if (gc != null) {
+						gc.setForeground(pg.getColor());
+						gc.setBackground(pg.getColor());
+					} else {
+						graphics.setForegroundColor(pg.getColor());
+			  			graphics.setBackgroundColor(pg.getColor());
+					}
+		  			drawGraph(pg, threadIsSelected, visY, graphics, gc, cumulatives, samples, values);			
+		  		} //if (pg.isenabled)
+		  	}//while (enum.hasmoreElements)
+		}
+	  	
+	  	if (gc != null) {
+	  		gc.dispose();
+	  	}
+	  	if (useImage) {
+	  		graphics.drawImage(this.graphImage, 0, 0);
+	  		setGraphImageChanged(false);
+	  	}
+	  	
+	  	for (int i = profiledGenerics.size() - 1; i >= 0; i--) {
+	  		pg = profiledGenerics.get(i);
+	  		if (pg.isEnabled(this.graphIndex)) //is visualised
+		  	{
+	  			graphics.setForegroundColor(pg.getColor());
+	  			graphics.setBackgroundColor(pg.getColor());
+		  		drawThreadMarks(pg, visY, graphics);	
+		  	}
+	  	}
+	}
+	
+	public boolean getGraphImageChanged() {
+		return graphImageChanged;
+	}
+	
+	public void setGraphImageChanged(boolean status) {
+		graphImageChanged = status;
+	}
+
+	private int searchSortedPointListForX(PointList list, int xValue, boolean firstLowerIfNoMatch) {
+		// bin search for now
+		if (list.getFirstPoint().x == xValue) {
+			return 0;
+		}
+		if (list.getLastPoint().x == xValue) {
+			return list.size() - 1;
+		}
+		int start = 0;
+		int end = list.size() - 1;
+		int match = 0;
+		while (match == 0) {
+			// no match
+			if (start + 1 == end) {
+				if (firstLowerIfNoMatch) {
+					return start;
+				} else {
+					return end;
+				}
+			}
+			int middle = (start + end) / 2;
+			int middleX = list.getPoint(middle).x;
+			if (middleX == xValue) {
+				match = middle;
+			} else {
+				if (middleX < xValue) {
+					start = middle;
+				} else {
+					end = middle;
+				}
+			}
+		}
+		return match;
+	}
+	
+	/*
+	 *  this method draws a polyline or a polygon in the graph
+	 *  
+	 *  Note: it assumes all polylines have the same number of points 
+	 */
+	private void drawGraph(ProfiledGeneric pg, boolean threadIsSelected, int visY, Graphics graphics, GC gc,
+	        int[] cumulatives, int[] samples, int[] values)
+	{
+		int pointCount = pg.getPointList(this.graphIndex).size();
+		Rectangle drawArea;
+		
+		if (gc != null) {
+			// draw one big picture including non-visible when drawing on image
+			drawArea = new Rectangle(0, 0, sizeX, this.getVisibleArea(graphics).height);
+		} else {
+			drawArea = this.getVisibleArea(graphics);
+		}
+		
+		if (pointCount < 2)	// these lusers really want to see a two pixel wide chart? what's wrong with them
+			return;
+
+		PointList allPointList = pg.getPointList(graphIndex);
+		int xIndex = searchSortedPointListForX(allPointList, drawArea.x, true);
+		PointList visiblePointList = new PointList();
+		
+		Point currentPoint = allPointList.getPoint(xIndex++);
+		visiblePointList.addPoint(currentPoint);
+		
+		currentPoint = allPointList.getPoint(xIndex++);
+		visiblePointList.addPoint(currentPoint);
+		
+		Point peakPointInVertical = null;
+		Point troughPointInVertical = null;
+		Point lastPointInVertical = null;
+		
+		while (xIndex < allPointList.size()) {
+			// only draw visible area
+			if (currentPoint.x >= drawArea.x + drawArea.width) {
+				break;
+			}
+			int lastX = currentPoint.x;
+			currentPoint = allPointList.getPoint(xIndex++);
+			//optimized polyline and draw slighly inaccurate graph for those pixel fine movement, 
+			//only account for peak/valley if x doesn't move(e.g. vertical lines)
+			
+			if (lastX == currentPoint.x) {
+				if (lastPointInVertical == null) {
+					// seen first point in the vertical line 
+					peakPointInVertical = troughPointInVertical = visiblePointList.getLastPoint();
+				}
+				if (troughPointInVertical.y > currentPoint.y) {
+					troughPointInVertical = currentPoint;
+				} else if (peakPointInVertical.y < currentPoint.y) {
+					peakPointInVertical = currentPoint;
+				}
+				lastPointInVertical = currentPoint;
+			}
+			else {
+				// we just write two points of peak and trough of the vertical line if needed and the last
+				// if we seen peak and trough, this way we can render the conceptually inaccurate graph
+				// we less point, but still show the same vertical line on screen
+				if (lastPointInVertical != null) {
+					boolean seenPeakOrTrough = false;
+					if (lastPointInVertical.y != peakPointInVertical.y) {
+						visiblePointList.addPoint(peakPointInVertical);
+						seenPeakOrTrough = true;
+					}
+					if (lastPointInVertical.y != troughPointInVertical.y) {
+						visiblePointList.addPoint(troughPointInVertical);
+						seenPeakOrTrough = true;
+					}
+					if (seenPeakOrTrough) {
+						visiblePointList.addPoint(lastPointInVertical);
+					}
+					peakPointInVertical = null;
+					troughPointInVertical = null;
+					lastPointInVertical = null;
+				}
+				visiblePointList.addPoint(currentPoint);
+			}
+		}
+
+		if (fillFlag)
+		{
+			// close the bottom parameter
+			visiblePointList.addPoint(new Point(visiblePointList.getLastPoint().x, visY - 50));
+			visiblePointList.addPoint(new Point(visiblePointList.getFirstPoint().x, visY - 50));
+
+			if (gc != null) {
+				gc.fillPolygon(visiblePointList.toIntArray());
+			} else {
+				graphics.fillPolygon(visiblePointList.toIntArray());
+			}
+		}
+		else
+		{
+			if (gc != null) {
+				gc.drawPolyline(visiblePointList.toIntArray());
+			} else {
+				graphics.drawPolyline(visiblePointList.toIntArray());
+			}
+		}
+		
+		//creates the first polyListX and polyListY
+		if (fillFlag && (!threadIsSelected && this.fillSelected))
+	  	{
+		    this.resetPolyList(pg, visY, samples, values, cumulatives);
+	  	}
+	}		
+
+	//this method is used to reset the list of points
+	private void resetPolyList(ProfiledGeneric pg, int visY, int[] samples, int[] values, int[] cumulatives)
+	{
+		pg.resetPolyline(graphIndex);
+			
+		int thisValue, cumValue, thisSample, x;
+		int y = (visY - 50);
+		double tmpScale = this.getScale();
+		for (int sampIndx = 0; sampIndx < samples.length; sampIndx++)
+		{
+		  	// the x value in thisSample is the timestamp
+		  	thisSample = samples[sampIndx];
+		  	x = (int)(thisSample / tmpScale);
+												
+            if (x > visibleLeftBorder && x < visibleRightBorder) //draws only visible stuff
+            {
+   			  	// the y value is the percentage
+   			  	thisValue = values[sampIndx];	
+   			  	cumValue = cumulatives[sampIndx];
+   			  	y = ((thisValue + cumValue) * (visY - 50)) / 100;
+	    			  	
+                pg.addPointToPolyline(graphIndex, x, (visY - 50) - y);
+            }
+            else if (x >= visibleRightBorder)  //optimises end drawing
+            {
+                pg.addPointToPolyline(graphIndex, x, visY - 50);
+                sampIndx = samples.length; //breaks the for loop
+            }
+		} // for
+	}
+	
+	public void updateIfNeeded(Vector<ProfiledGeneric> profiledGenerics)
+	{
+	    // updates cumulative thread table
+	    if (this.updateCumulativeThreadTableIsNeeded)
+	       this.genericRefreshCumulativeThreadTable(profiledGenerics.elements());
+	    
+	    // updates polylines based on cumulative thread table
+        this.updatePolyLinesGeneric(profiledGenerics.elements());
+	}
+	
+	public int getVisibleRightBorder()
+	{
+		return this.visibleRightBorder;
+	}
+
+	public int getVisibleLeftBorder()
+	{
+		return this.visibleLeftBorder;
+	}
+
+	public int getVisualSizeX()
+	{
+		return this.visualSizeX;
+	}
+
+	public int getVisualSizeY()
+	{
+		return this.visualSizeY;
+	}
+}