sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.power/src/com/nokia/carbide/cpp/pi/power/PowerTraceGraph.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.power/src/com/nokia/carbide/cpp/pi/power/PowerTraceGraph.java	Thu Feb 11 15:32:31 2010 +0200
@@ -0,0 +1,1155 @@
+/*
+ * 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: 
+ *
+ */
+
+package com.nokia.carbide.cpp.pi.power;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.DecimalFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.draw2d.FigureCanvas;
+import org.eclipse.draw2d.Graphics;
+import org.eclipse.draw2d.MouseEvent;
+import org.eclipse.draw2d.MouseMotionListener;
+import org.eclipse.draw2d.Panel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MenuItem;
+
+import com.nokia.carbide.cpp.internal.pi.actions.SaveSamples;
+import com.nokia.carbide.cpp.internal.pi.analyser.NpiInstanceRepository;
+import com.nokia.carbide.cpp.internal.pi.interfaces.ISaveSamples;
+import com.nokia.carbide.cpp.internal.pi.model.GenericSampledTrace;
+import com.nokia.carbide.cpp.internal.pi.plugin.model.IContextMenu;
+import com.nokia.carbide.cpp.internal.pi.power.actions.PowerSettingsDialog;
+import com.nokia.carbide.cpp.internal.pi.power.actions.PowerStatisticsDialog;
+import com.nokia.carbide.cpp.internal.pi.visual.GenericTraceGraph;
+import com.nokia.carbide.cpp.internal.pi.visual.GraphComposite;
+import com.nokia.carbide.cpp.internal.pi.visual.PICompositePanel;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEvent;
+import com.nokia.carbide.cpp.internal.pi.visual.PIEventListener;
+import com.nokia.carbide.cpp.pi.address.GppSample;
+import com.nokia.carbide.cpp.pi.address.GppTrace;
+import com.nokia.carbide.cpp.pi.editors.PIPageEditor;
+import com.nokia.carbide.cpp.pi.util.ColorPalette;
+
+
+public class PowerTraceGraph extends GenericTraceGraph implements ActionListener,
+																  PIEventListener,
+																  MouseMotionListener,
+																  IContextMenu
+{
+	
+    private int[] DTrace; // used for synchronizing the power and gpptraces.
+	private int[] DPower;
+	
+	// 3 tabs can share the same trace, but they need different graphs
+    private PwrTrace trace;
+    
+	private FigureCanvas leftFigureCanvas;
+	
+	// used to draw the text for the power line in the graph window.
+	private int mPowerLineY = 0;
+	
+    public int x = 400;
+    public int y = 800;
+
+    private double averageConsumption = (float)0.0;
+	private int averageComsumptionLastOffset = Integer.MAX_VALUE;
+    private double cumulative = (float)0.0;
+    private double energy = 0.0f;
+    private int[] sampleTimes;
+    private int[] ampValues;
+    private int[] voltValues;
+//    private int[] capaValues;
+    private double maxAmps = Integer.MIN_VALUE;
+	private double minAmps = Integer.MAX_VALUE;
+	private double maxPower = 0.0;
+    private boolean mSelecting = false;
+	
+	// used when determining when to draw the text over the power bar.
+	private int leftBorder = 0;
+	
+	private static int xLegendHeight = 20;
+	
+	boolean mShowPowerLine = true;
+	
+	private static DecimalFormat powerFormat   = new DecimalFormat(Messages.getString("powerFormat")); //$NON-NLS-1$
+	private static DecimalFormat voltageFormat = new DecimalFormat(Messages.getString("voltageFormat")); //$NON-NLS-1$
+	
+	private int uid;
+	
+	protected static int SAMPLES_AT_ONE_TIME = 1000;
+	protected int stringTime;
+
+	
+	// class to pass sample data to the save wizard
+    public class SaveSampleString implements ISaveSamples {
+		int startTime;
+		int endTime;
+    	
+    	public SaveSampleString() {
+		}
+
+    	public String getData() {
+    		return getSampleString(SAMPLES_AT_ONE_TIME, this.startTime, this.endTime);
+		}
+
+		public int getIndex() {
+			if (stringTime == (int) (getSelectionStart() + 0.0005))
+				return 0;
+
+			return stringTime;
+		}
+
+		public void clear() {
+			this.startTime = (int) (getSelectionStart() + 0.0005);
+			this.endTime   = (int) (getSelectionEnd() + 0.0005);
+			stringTime = startTime;
+		}
+    }
+
+	/*
+	 * return the power samples selected in the interval 
+	 */
+	protected String getSampleString(int count, int startTime, int endTime)
+	{
+		Vector sampleVector = ((PwrTrace) this.getTrace()).samples;
+		PwrSample lastSample = (PwrSample) sampleVector.get(sampleVector.size() - 1);
+		
+		String returnString = null;
+		
+		if (this.stringTime == startTime) {
+			returnString = Messages.getString("PowerTraceGraph.saveSamplesHeading"); //$NON-NLS-1$
+		}
+
+		this.stringTime++;
+
+		// check if we have returned everything
+		if ((this.stringTime > lastSample.sampleSynchTime) || (this.stringTime > endTime)) {
+			this.stringTime = endTime + 1;
+			return returnString;
+		}
+		
+		double oldCurrent  = -1;
+		double oldVoltage  = -1;
+		double oldCapacity = -1;
+		String string = ""; //$NON-NLS-1$
+
+		if (this.trace.isComplete()) {
+			for ( ;
+				 (this.stringTime < endTime + 1) && (count > 0) && (this.stringTime < sampleVector.size());
+				 this.stringTime++) {
+				PwrSample sample = (PwrSample) sampleVector.get(stringTime);
+				double current  = sample.current;
+				double voltage  = sample.voltage;
+				double capacity = sample.capacity;
+				
+				if ((oldCurrent != current) || (oldVoltage != voltage) || (oldCapacity != capacity)) {
+					string = Messages.getString("PowerTraceGraph.comma") + (int) current + Messages.getString("PowerTraceGraph.comma")  + (int) voltage + Messages.getString("PowerTraceGraph.comma") + (int) capacity + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+					oldCurrent  = current;
+					oldVoltage  = voltage;
+					oldCapacity = capacity;
+				}
+	
+				returnString += sample.sampleSynchTime + string;
+				count--;
+			}
+		} else {
+			int i = 0;
+			
+			//find least sampling time greater than or equal to the start index
+			for ( ; i < sampleVector.size(); i++) {
+				if (((PwrSample) sampleVector.get(i)).sampleSynchTime > this.stringTime)
+					break;
+			}
+
+			if (i != 0)
+				i--;
+
+			for ( ; i < sampleVector.size() && (count > 0); i++) {
+				PwrSample sample = (PwrSample) sampleVector.get(i);
+				
+				this.stringTime = (int) sample.sampleSynchTime;
+				if (sample.sampleSynchTime > endTime)
+					break;
+				
+				double current  = sample.current;
+				double voltage  = sample.voltage;
+				double capacity = sample.capacity;
+				
+				returnString += sample.sampleSynchTime + Messages.getString("PowerTraceGraph.comma") + (int) current + Messages.getString("PowerTraceGraph.comma")  + (int) voltage + Messages.getString("PowerTraceGraph.comma") + (int) capacity + "\n"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+				count--;
+			}
+		}
+
+		// check if we have returned everything
+		if (this.stringTime >= lastSample.sampleSynchTime) {
+			this.stringTime = endTime + 1;
+		}
+
+		return returnString;
+	}
+	
+	protected void actionSaveSamples(ISaveSamples saveSamples)
+	{
+		new SaveSamples(saveSamples);
+	}
+
+	protected MenuItem getSaveSamplesItem(Menu menu, boolean enabled) {
+	    MenuItem saveSamplesItem = new MenuItem(menu, SWT.PUSH);
+
+		saveSamplesItem.setText(Messages.getString("PowerTraceGraph.saveSamplesForInterval")); //$NON-NLS-1$
+		saveSamplesItem.setEnabled(enabled);
+		
+		if (enabled) {
+			saveSamplesItem.addSelectionListener(new SelectionAdapter() { 
+				public void widgetSelected(SelectionEvent e) {
+					saveEventSamples();
+				}
+			});
+		}
+	
+		return saveSamplesItem;
+	}
+	
+	public void saveEventSamples() {
+    	SaveSampleString saveSampleString = new SaveSampleString();
+    	actionSaveSamples(saveSampleString); //$NON-NLS-1$
+	}
+
+    public PowerTraceGraph( int graphIndex, int uid, PwrTrace trace )
+    {
+        super((GenericSampledTrace)trace);
+		this.graphIndex     = graphIndex;
+		this.uid			= uid;
+       
+        if ( trace != null ) 
+		{
+            this.trace = trace;
+
+            if (this.trace.getSampleTimes() == null)
+            	this.trace.initData();
+
+			this.sampleTimes = this.trace.getSampleTimes();
+			this.ampValues   = this.trace.getAmpValues();
+			this.voltValues  = this.trace.getVoltValues();
+//			this.capaValues  = this.trace.getCapaValues();
+			this.maxAmps  = this.trace.getMaxAmps();
+			this.minAmps  = this.trace.getMinAmps();
+			this.maxPower = this.trace.getMaxPower();
+        }
+    }
+    
+	public int getUid() {
+		return this.uid;
+	}
+    
+	// This method is called when widgets in the PowerInfoPanel are
+	// manipulated.
+	//
+	public void action(String actionString)
+	{   
+		if (actionString.equals("show_average")) //$NON-NLS-1$
+		{
+			// show average power as a line
+			mShowPowerLine = true;
+		}
+		else if (actionString.equals("hide_average")) //$NON-NLS-1$
+		{
+			// do not show average power as a line
+			mShowPowerLine = false;
+		}
+		else if (actionString.equals("changeVoltage")) //$NON-NLS-1$
+		{
+			// voltage changed, so compute new maximum power
+			this.trace.scaleMaxPower();
+			this.maxPower = this.trace.getMaxPower();
+        }
+		else if (actionString.equals("changeBatterySize"))  //$NON-NLS-1$
+		{
+			// battery size changed, so repaint
+        }
+		else
+			return;
+
+        this.repaint();
+		if (this.leftFigureCanvas != null)
+			this.leftFigureCanvas.redraw();
+    }
+
+    public void piEventReceived(PIEvent be)
+	{
+	    switch (be.getType())
+	    {
+	    case (PIEvent.SELECTION_AREA_CHANGED):
+
+			// send this message to the 2 other graphs
+			PIEvent be2 = new PIEvent(be.getValueObject(),
+					PIEvent.SELECTION_AREA_CHANGED2);
+
+	    	for (int i = 0; i < 3; i++)
+	    	{
+	    		PowerTraceGraph graph = trace.getPowerGraph(i, getUid());
+	    		
+	    		if (graph != this) {
+					graph.piEventReceived(be2);
+	    		}
+	    	}
+
+		// FALL THROUGH
+		case PIEvent.SELECTION_AREA_CHANGED2:
+			double[] values = (double[])be.getValueObject();
+			this.setSelectionStart(values[0]);
+			this.setSelectionEnd(values[1]);
+			this.calcValues();
+			mSelecting = true;
+            break;
+
+		case (PIEvent.MOUSE_PRESSED):
+			this.calcValues();	
+        	this.parentComponent.getSashForm().redraw();
+			break;
+
+		case PIEvent.SCROLLED:
+			Event event = ((Event)be.getValueObject());
+			this.parentComponent.setScrolledOrigin(event.x, event.y);
+			this.repaint();
+			break;
+
+		default:
+        	break;
+	    }
+	}
+	
+	public void refreshDataFromTrace()
+	{
+	}
+
+	public void enablePowerLine( boolean state )
+	{
+		mShowPowerLine = state;
+	}
+
+	public void actionPerformed ( ActionEvent ae ) 
+	{
+        if( ae.getActionCommand().equals("switch") )  //$NON-NLS-1$
+		{
+            parentComponent.piEventReceived( new PIEvent( "switch_gpp", PIEvent.PLUGIN_STRING_MESSAGE) ); //$NON-NLS-1$
+        } 
+        this.repaint();
+    }	
+    
+    public PICompositePanel getParentComponent() {
+        return this.parentComponent;
+    }
+    
+    public float getVoltage() 
+	{
+        if ( trace != null )
+            return trace.getVoltage();
+            
+        return 0.0f;
+    }
+    
+    public void setVoltage( float newVal ) 
+	{
+        if ( trace != null )
+            trace.setVoltage(newVal);
+    }
+    
+    public double getAverageConsumption() 
+	{	
+        if ( trace != null ) 
+		{
+			if( averageComsumptionLastOffset != trace.getOffset() )
+			{
+				averageComsumptionLastOffset = trace.getOffset();
+				// not calculated yet
+				calcValues();
+			}
+			
+            return this.averageConsumption * trace.getVoltage();
+        } 
+		
+		return 0.0f;
+    }
+    
+    public void setOffset( int newOffset ) 
+	{   
+	    if ( trace != null )
+			trace.setOffset( newOffset );
+	}
+    
+    public void increaseOffset() 
+	{
+        if ( trace != null )
+		{
+            trace.setOffset( trace.getOffset() + 50 );
+		}
+    }
+    
+    public void decreaseOffset() 
+	{
+        if ( trace != null )
+		{
+            trace.setOffset( trace.getOffset() - 50 );
+		}
+    }
+    
+    public void setSize(int x, int y)
+	{
+	    this.x = x;
+	    this.y = y;
+	}
+
+    public Dimension getSize()
+	{
+	    return new Dimension(x, y);
+	}
+
+	public void paint(Panel panel, Graphics graphics)
+	{
+		this.setSize(this.getSize().width, getVisualSize().height);
+	    this.drawDottedLineBackground(graphics, PowerTraceGraph.xLegendHeight);
+	    this.drawPowerData(graphics, PowerTraceGraph.xLegendHeight);
+
+	    // draws the same selection as the Address/Thread trace
+		this.drawSelectionSection(graphics, PowerTraceGraph.xLegendHeight);
+
+		if (mShowPowerLine)
+			this.drawPowerLine(graphics);		
+	}
+
+    public void repaint()
+	{   
+	    this.parentComponent.repaintComponent();
+	}
+    	
+	private void showPowerValueAtX(MouseEvent me)
+	{
+		if (me.y > this.getVisualSizeY() - PowerTraceGraph.xLegendHeight) {
+			this.setToolTipText(null);
+			return;
+		}
+		
+		// scale the value of x
+		int x = (int) (me.x * this.getScale() + 0.5);
+		
+		if (x > (int) (PIPageEditor.currentPageEditor().getMaxEndTime() * 1000 + 0.0005)) {
+			this.setToolTipText(null);
+			return;
+		}
+		
+		if (x > trace.getLastSampleNumber())
+			x = trace.getLastSampleNumber();
+
+		String textToShow = Double.toString(x / 1000.0)
+							+ Messages.getString("tooltip1"); //$NON-NLS-1$
+
+		// We could instead use the measured voltage: voltValues[x +/- movement]/1000.0 rather than the user-specified voltage?
+		double voltage = trace.getVoltage();
+
+		int movement = trace.getOffset();
+		int ampValue = 0;
+
+		// find the amperage for x
+		int index = -1;
+		if (movement == 0)
+		{
+			index = timeIndex(x);
+			if (index != -1)
+				ampValue = ampValues[index];
+		}
+		else if (movement < 0)
+		{
+			// make it positive
+			movement *= -1;
+			
+			// eat the first N=offset values.
+			if (x < sampleTimes.length - movement)
+			{
+				index = timeIndex(x + movement);
+				if (index != -1)
+					ampValue = ampValues[index];
+			}
+		}
+		else // movement > 0
+		{
+			if (x > movement)
+			{
+				index = timeIndex(x - movement);
+				if (index != -1)
+					ampValue = ampValues[index];
+			}
+		}
+
+		// determine the power = amps * voltage
+		textToShow += (int) ((ampValue * voltage) + 0.5);
+
+		this.setToolTipText(textToShow + Messages.getString("tooltip2") //$NON-NLS-1$
+							+ PowerTraceGraph.voltageFormat.format(voltage));
+	}
+	
+	public int timeIndex(int time) {
+		if (time < sampleTimes[0])
+			return -1;
+		
+		if (time >= sampleTimes[sampleTimes.length - 1])
+			return sampleTimes.length - 1;
+
+		int i = 0;
+		for ( ; sampleTimes[i] <= time; i++)
+			;
+		
+		if (sampleTimes[i] == time)
+			return i;
+		else
+			return i - 1;
+	}
+
+	private void drawPowerData(Graphics graphics, int yLegendSpace)
+	{
+		int visY = this.getVisualSize().height - yLegendSpace;
+		if (visY < 0)
+			visY = 0;
+		int sampleCount = this.sampleTimes.length;
+				
+		// arrays of values to draw
+		int points[] = new int[sampleCount * 4];
+		
+		// the offset changes when the user moves the graph or the traces are synched
+		int movement = trace.getOffset();
+		
+		// look for a move to the right ( > 0)
+		// the y value is in thisValue is the milliAmps	
+		// it doesn't matter if we render the milliAmps or mW, the graphs have the same
+		// form, it only matters when you show the values associated with the graph.
+		double maxAmps = this.maxPower / this.trace.getVoltage();
+
+		double cachedScale = this.getScale();
+		// or no movement (==0)
+		if( movement == 0 )
+		{
+			for( int i = 0, k = 0; i < sampleTimes.length; i++ )
+			{
+				points[k++] = (int)(sampleTimes[i] / cachedScale);
+				points[k++] = (int)(visY - (ampValues[i] / maxAmps) * visY);
+				if (i < sampleTimes.length - 1)
+				{
+					points[k++] = (int)(sampleTimes[i + 1] / cachedScale);
+				} else {
+					long lastTime = (int) (PIPageEditor.currentPageEditor().getMaxEndTime() * 1000 + 0.0005);
+					points[k++] = (int) (lastTime / cachedScale);
+				}
+				points[k++] = (int)(visY - (ampValues[i] / maxAmps) * visY);
+			}
+		} 
+		else if( movement > 0 )
+		{
+			// set the first N=offset values to be 0
+			for (int i = 0, k = movement * 2; i < sampleCount - movement; i++) {
+				points[k++] = sampleTimes[i];
+				points[k++] = ampValues[i];
+			}
+
+			for( int i = 0, k = 0; i < sampleCount; i++ )
+			{
+				if( i < movement )
+				{
+					points[k++] = (int)(i / cachedScale);
+					points[k++] = (int)(visY - ((float)1 / maxAmps) * visY);
+				}
+				else
+				{
+					points[k++] = (int)((points[2 * i] + movement)/cachedScale);
+					points[k++] = (int)(visY - ((float)points[1 + 2 * i] / maxAmps) * visY);
+				}
+			}
+		}
+		else
+		{
+			// make it positive
+			movement *= -1;
+			
+			// eat the first N=offset values.
+			int cutoff = sampleTimes.length - movement;
+			
+			for (int i = 0, k = movement * 2; i < sampleCount - movement; i++) {
+				points[k++] = sampleTimes[i];
+				points[k++] = ampValues[i];
+			}
+
+			for( int i = 0, k = 0; i < sampleCount; i++ )
+			{
+				if( i >= cutoff )
+				{
+					points[k++] = (int)(i / cachedScale);
+					points[k++] = (int)(visY - ((float)1 / maxAmps) * visY);
+				}
+				else
+				{
+					points[k++] = (int)((points[2 * i] - movement) / cachedScale);
+					points[k++] = (int)(visY - ((float)points[1 + 2 * i] / maxAmps) * visY);
+				}
+			}
+		}
+
+		graphics.setForegroundColor(ColorConstants.red);
+		// draw all the points at once.
+		graphics.drawPolyline(points);
+		points = null;
+	}
+
+	public double calcValueForY( int y )
+	{
+		double visY = this.getVisualSize().height - PowerTraceGraph.xLegendHeight;
+		if (visY <= 0)
+			return 0.0;
+		double maxAmps = this.maxPower / this.trace.getVoltage();
+		double yScalingFactor = maxAmps/(double)visY;
+		// visY-y to compensate for the transpose, * voltage to compenstate for the mA in the samples, we 
+		// want to show the mW values here.
+		return ( (double)(visY - (double)y) * yScalingFactor * (double)trace.getVoltage());
+	}
+	
+	public int calcYforValue( double value )
+	{
+		double visY = this.getVisualSize().height - PowerTraceGraph.xLegendHeight;
+		if ((visY <= 0) || (this.trace.getVoltage() == 0))
+			return 0;
+		double maxAmps = this.maxPower / this.trace.getVoltage();
+		double yScalingFactor = maxAmps / visY;
+
+		// visY-y to compensate for the transpose, * voltage to compenstate for the mA in the samples, we 
+		// want to show the mW values here.
+		if (yScalingFactor == 0)
+			return 0;
+
+		double tmp = value / (double)trace.getVoltage() / yScalingFactor;
+		
+		return (int)Math.ceil(visY - tmp);
+	}
+
+	private void drawPowerLine( Graphics graphics )
+	{
+		//mPowerLineY == 0  on startup.
+		boolean startup = (mPowerLineY == 0);
+
+		if( mPowerLineY == 0 || mSelecting )
+		{
+			if( mSelecting )
+			{
+				// still selecting?
+				if( ((super.getSelectionStart() == -1)  && (super.getSelectionEnd() == -1)) )
+				{
+					mSelecting = false;
+					// need to turn them back on. mouse drag events for drawing the power line.
+				}
+				else
+				{
+					mPowerLineY = calcYforValue( getAverageConsumption() );
+				}
+			}
+			
+			if( mPowerLineY == 0 )
+			{
+				mPowerLineY = calcYforValue( getAverageConsumption() );
+			}
+		}
+		
+		double powerValue = 0.0;
+		
+		String strValue = null;
+		
+		// requirement, if dragging power bar then print true power value. If
+		// selecting then print average of area not the true value of the y coordinate.
+		//
+		if( !mSelecting && !startup )
+			powerValue = calcValueForY(mPowerLineY);
+		else if( mSelecting || startup )
+			powerValue = getAverageConsumption();
+
+		strValue = PowerTraceGraph.powerFormat.format((int)(powerValue + 0.5));
+
+		// this does cause some overhead and could be removed since it only provides some aesthetics.
+		updateVisibleBorders();
+		leftBorder = this.parentComponent.getScrolledOrigin().x;
+//		leftBorder = this.getVisibleLeftBorder();
+		if( leftBorder < 0 )
+			leftBorder = 0;	
+		
+		// draw the average power line with a width of 3
+		int lineWidth = graphics.getLineWidth();
+
+		graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+		graphics.setLineWidth(2);
+		graphics.drawLine(leftBorder, mPowerLineY, this.getSize().width, mPowerLineY );
+
+		graphics.setLineWidth(lineWidth);
+
+		// figure out how big the box behind the text should be.
+		GC gc = new GC(PIPageEditor.currentPageEditor().getSite().getShell());
+		Point point = gc.stringExtent(strValue);
+		gc.dispose();
+
+		// clear the text rectangle
+		graphics.setBackgroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+		graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
+		graphics.fillRectangle(leftBorder + 20, mPowerLineY - (point.y / 2), point.x + 6, point.y );
+
+		graphics.setForegroundColor(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
+
+		// make the font bold 16, draw the text, and restore the font
+		int oldStyle  = graphics.getFont().getFontData()[0].getStyle();
+		int oldHeight = graphics.getFont().getFontData()[0].getHeight();
+
+		graphics.getFont().getFontData()[0].setStyle((oldStyle & SWT.NORMAL) | (oldStyle & SWT.ITALIC) | SWT.BOLD);
+		graphics.getFont().getFontData()[0].setHeight(16);
+		
+		graphics.drawString(strValue, leftBorder + 23, mPowerLineY - (point.y / 2));
+
+		graphics.getFont().getFontData()[0].setStyle(oldStyle);
+		graphics.getFont().getFontData()[0].setHeight(oldHeight);
+
+		if ( startup )
+			mPowerLineY = 0;
+	}
+
+    public void drawPwrGraphSelection(Graphics g)
+	{	
+	    int selectionStart = (int)super.getSelectionStart();
+	    int selectionEnd = (int)super.getSelectionEnd();
+	    double scale = super.getScale();
+		
+	    if(selectionStart != -1 && selectionEnd != -1)
+	    {
+	    	Color selectColor = new Color(Display.getCurrent(),123,156,178);
+	    	
+	    	g.setForegroundColor(selectColor);
+	    	g.fillRectangle((int)(selectionStart / scale + 0.5),
+	    					0,
+	    					(int)((selectionEnd - selectionStart) / scale + 0.5),
+	    					this.getVisualSize().height - PowerTraceGraph.xLegendHeight);
+	    	selectColor.dispose();
+	    }
+	}
+	
+	// synchronize the power and software traces
+	// logically:
+	// 1) digitize the GPP and power traces
+	// 2) align the traces so that the tail of the software trace is aligned with 
+	//	  the tail of the power trace.
+	// 3) slowly move software trace to the left ADDing the software and power trace
+	// 	  values together and performing a sum until the highest total is found.
+	// 4) using the index of the highest total, either add dummy values to the power trace or
+	//    remove values from the power trace.
+	// 5) finally update the samples and refresh turn off the wait cursor.
+    
+	public void doSynchronize( GppTrace gpp, PwrTrace power )
+	{
+//		System.err.println( "Starting synchronization process" );
+		digitizeGPPFile(gpp);
+		digitizePowerFile(power);
+		
+		int MAX = 0;
+		int MAX_POWER_INDEX = 0;
+		int MAX_TRACE_INDEX = 0;
+		int accum = 0;
+
+		int TOTAL_MOVEMENT = (DPower.length-DTrace.length) + (int)(0.25*DTrace.length) + 1;
+
+		// TI = trace index
+		int TI = 0;
+		// PI = power trace index
+		int PI = DPower.length-DTrace.length;
+
+		for( int j = 0; j < TOTAL_MOVEMENT; j++ )
+		{
+			for( int i = TI; i < DTrace.length; i++ )
+			{
+				if( (DTrace[i] & DPower[PI+i]) == 0 )
+				{
+					accum++;
+				}
+			}
+			if( accum > MAX )
+			{
+				MAX = accum;
+				MAX_POWER_INDEX = PI;
+				MAX_TRACE_INDEX = TI;
+			}
+			accum = 0;
+			PI--;
+			if( PI < 0 )
+			{
+				PI=0;
+				TI++;
+			}
+		}
+		// one more pass through the data to update the graphs.
+		//
+
+		if( MAX_TRACE_INDEX > 0 )
+		{
+			trace.setOffset( trace.getOffset() - MAX_TRACE_INDEX );
+		}
+		else
+		{
+			trace.setOffset( trace.getOffset() + MAX_POWER_INDEX );
+		}
+		parentComponent.getActiveGraph().getCompositePanel().getSashForm()
+								.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT) );
+	}
+	// digitize the power trace
+	// basically:
+	// 1) find the min and max power values in the trace
+	// 2) for each value in the trace if the value is > Half the average range
+	//    then set that value to 1 else 0.
+	//
+	public void digitizePowerFile( PwrTrace power )
+	{
+		double MIN_Value = (double)minAmps/(double)1000;
+		double MAX_Value = (double)maxAmps/(double)1000;
+		
+		// find the min and max in the power
+		//
+		// we already have this data so we can simply divide by 1000 for the scale we need.
+
+		float Half_Avg_Range = (float)((MAX_Value - MIN_Value)/2);		
+		
+		DPower = new int[power.samples.size()];
+		int DPowerIndex = 0;
+
+		// as an aside, this tracks well with the application states.
+		//
+		for( Enumeration e = power.getSamples(); e.hasMoreElements() ; )
+		{
+			PwrSample current = (PwrSample)e.nextElement();
+			if( current.current > Half_Avg_Range )
+			{
+				DPower[DPowerIndex] = 1;
+			}
+			else
+			{
+				DPower[DPowerIndex] = 0;
+			}
+			DPowerIndex++;
+		}
+	}
+	
+	// digitize the gpp file
+	// basically:
+	// 1a) divide the run into windows and for each window:
+	// 1b) count the number of transitions i.e. different functions called in the windows by putting the process
+	//    name into a hashtable, if the add returns a null value then the insert 
+	//    did not collide and we transitioned.
+	// 1c) find the min and max number of transistions for all windows
+	//
+	// 2a) for each window if the number of transitions for that window are greater
+	//	   than the average number of transtitions then set the digitized version to be 1 else 0.
+	//
+	public void digitizeGPPFile( GppTrace thisTrace )
+	{		
+		int WINDOW_SIZE = 20;
+
+		int numSamples = thisTrace.samples.size();
+		int count = 1;
+		Hashtable<String,String> histogram = new Hashtable<String,String>();
+		int windowSizes[] = new int[numSamples/WINDOW_SIZE];
+		int min = Integer.MAX_VALUE;
+		int max = Integer.MIN_VALUE;
+
+		int numTransitions = 1;
+
+		int total = 0;
+//		String lastTrace = null;
+		for( int i = 0; i < numSamples; i++ )
+		{
+			GppSample sample = thisTrace.getGppSample(i);
+			if( count % WINDOW_SIZE == 0 )
+			{
+				windowSizes[i/WINDOW_SIZE] = histogram.size() + numTransitions;
+				if( windowSizes[i/WINDOW_SIZE] < min )
+					min = windowSizes[i/WINDOW_SIZE];
+				else if( windowSizes[i/WINDOW_SIZE] > max )
+					max = windowSizes[i/WINDOW_SIZE];
+
+				total += windowSizes[i/WINDOW_SIZE];
+
+				numTransitions = 1;
+				histogram.clear();
+				count = 0;
+			}
+			count++;
+			if( histogram.put( new String(sample.thread.process.name + Messages.getString("PowerTraceGraph.histogram1") //$NON-NLS-1$
+						                  + (sample.thread.threadName != null ? sample.thread.threadName
+											   : Messages.getString("PowerTraceGraph.11")) + Messages.getString("PowerTraceGraph.12") //$NON-NLS-1$ //$NON-NLS-2$
+					   							   + sample.thread.threadId + Messages.getString("PowerTraceGraph.13") //$NON-NLS-1$
+					   							   + sample.currentFunctionSym.functionName + Messages.getString("PowerTraceGraph.14") //$NON-NLS-1$
+					   							   + Long.toHexString(sample.currentFunctionSym.startAddress.longValue()) ),
+					   			Messages.getString("PowerTraceGraph.15") ) == null ) //$NON-NLS-1$
+			{
+				numTransitions++;
+			}
+		}
+		
+		int average = (max - min) /2 ;
+
+		DTrace = new int[windowSizes.length * WINDOW_SIZE];
+		int DTraceIndex = 0;
+		for( int i = 0; i < windowSizes.length; i++ )
+		{
+			for( int j = 0; j < WINDOW_SIZE; j++ )
+			{
+				// okay, so it seems that when there is more variablity in the trace there is more constant variablity in the power.
+				if( windowSizes[i] <= average /*halfAvgRange */ )	
+				{
+					DTrace[DTraceIndex] = 0;
+				}
+				else
+				{
+					DTrace[DTraceIndex] = 1;
+				}
+				DTraceIndex++;
+			}
+		}
+	}
+
+	// calculates the average power numbers of a selection.
+    private void calcValues() 
+	{
+        int selStart = (int)super.getSelectionStart(); 
+        int selEnd   = (int)super.getSelectionEnd();
+        int sum = 0;
+        int offset = trace.getOffset();
+        this.cumulative = 0;
+        this.averageConsumption = 0;
+        this.energy = 0;
+        
+        if (selStart < offset)
+        	selStart = offset;
+
+        if (selEnd < trace.getFirstSampleNumber() || selStart > selEnd)
+        	return;
+        
+        // find the first sample greater than or equal to selStart
+        int index = timeIndex(selStart);
+
+        // count time before the first sample as a bunch of zeros
+        if (selStart < trace.getFirstSampleNumber()) {
+        	sum = trace.getFirstSampleNumber() - selStart;
+        	index = 0;
+        }
+        
+		for (int j = index; j < sampleTimes.length; j++)
+		{	
+			int time = sampleTimes[j] + 1;
+			if (time < selStart)
+				time = selStart;
+			
+			int nextTime = j == sampleTimes.length - 1 ? Integer.MAX_VALUE : sampleTimes[j + 1];
+			
+			int count = Math.min(nextTime - time + 1, selEnd - time + 1);
+			this.energy += ((double)ampValues[j] * (double)this.voltValues[j])/(float)1000000.0 * count;
+			this.cumulative += ampValues[j] * count;
+			sum += count;
+			time += count;
+			
+			if (time > selEnd)
+				break;
+		}
+		
+		if (sum > 0)
+			this.averageConsumption = (double)this.cumulative / sum ;     		
+    }
+    
+    public double getCumulativeConsumption() 
+	{
+        return this.energy;
+    }
+     
+    public float getBatterySize() {
+        if ( trace != null ) {
+            return trace.getBatterySize();
+        } else
+            return 0.0f;
+    }
+    
+    public void setBatterySize( float newVal ) {
+        if ( trace != null )
+            trace.setBatterySize( newVal );
+    }
+	
+	public void mouseDragged(MouseEvent me)  
+	{
+		int tmpY = me.y;
+		int tmpX = me.x;
+
+		if( (tmpY < (this.getVisualSize().height - PowerTraceGraph.xLegendHeight)) && (tmpY > 0) && (!mSelecting))
+		{
+			mPowerLineY = tmpY;
+		}
+
+		this.repaint();
+	}
+
+	public void mouseMoved(MouseEvent me)  
+	{
+		showPowerValueAtX( me );
+	}
+
+	public void mouseEntered(MouseEvent arg0) {
+	}
+
+	public void mouseExited(MouseEvent arg0) {
+	}
+
+	public void mouseHover(MouseEvent arg0) {
+	}
+
+	public void addContextMenuItems(Menu menu, org.eclipse.swt.events.MouseEvent me) {
+		
+		new MenuItem(menu, SWT.SEPARATOR);
+		
+		Boolean showLine   = Boolean.TRUE;	// by default, show the interval average power as a line
+
+		// if there is a show average power line value associated with the current Analyser tab, then use it
+		Object obj = NpiInstanceRepository.getInstance().getPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine");  //$NON-NLS-1$
+		if ((obj != null) && (obj instanceof Boolean))
+			// retrieve the current value
+			showLine = (Boolean)obj;
+		else
+			// set the initial value
+			NpiInstanceRepository.getInstance().setPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine", showLine);  //$NON-NLS-1$
+
+		final Boolean showLineFinal = showLine;
+
+		MenuItem showLineItem = new MenuItem(menu, SWT.CHECK);
+		showLineItem.setText(Messages.getString("PowerTraceGraph.18")); //$NON-NLS-1$
+		showLineItem.setSelection(showLine);
+		showLineItem.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				String action;
+				NpiInstanceRepository.getInstance().setPersistState(uid, "com.nokia.carbide.cpp.pi.power.showLine", !showLineFinal);  //$NON-NLS-1$
+				if (!showLineFinal)
+				{
+					action = "show_average";  //$NON-NLS-1$
+				} else {
+					action = "hide_average";  //$NON-NLS-1$
+				}
+
+		    	for (int i = 0; i < 3; i++)
+		    	{
+		    		PowerTraceGraph graph = trace.getPowerGraph(i, getUid());
+					graph.action(action);
+		    	}
+			}
+		});
+
+		new MenuItem(menu, SWT.SEPARATOR);
+
+		int startTime = (int) this.getSelectionStart();
+		int endTime   = (int) this.getSelectionEnd();
+
+		// save raw samples
+		getSaveSamplesItem(menu, (startTime != -1) && (endTime != -1) && (startTime != endTime));
+		
+		new MenuItem(menu, SWT.SEPARATOR);
+
+		MenuItem powerSettingsItem = new MenuItem(menu, SWT.PUSH);
+		powerSettingsItem.setText(Messages.getString("PowerTraceGraph.22")); //$NON-NLS-1$
+		powerSettingsItem.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				new PowerSettingsDialog(Display.getCurrent());
+			}
+		});
+		
+		MenuItem powerStatsItem = new MenuItem(menu, SWT.PUSH);
+		powerStatsItem.setText(Messages.getString("PowerTraceGraph.23")); //$NON-NLS-1$
+		powerStatsItem.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				new PowerStatisticsDialog(Display.getCurrent());
+			}
+		});
+	}
+
+	public void paintLeftLegend(FigureCanvas figureCanvas, GC gc)
+	{
+		GC localGC = gc;
+		
+		if (gc == null)
+			gc = new GC(PIPageEditor.currentPageEditor().getSite().getShell());
+		
+		if (this.leftFigureCanvas == null)
+			this.leftFigureCanvas = figureCanvas;
+
+		Rectangle rect = ((GraphComposite) figureCanvas.getParent()).figureCanvas.getClientArea();
+		
+		double visY = rect.height - PowerTraceGraph.xLegendHeight;
+		if (visY < 0)
+			visY = 0;
+		double yIncrement = visY / 10;
+		
+		// this assumes maxPower is evenly divisible by 10
+		int maxPower = (int) this.maxPower;
+		int powerIncrement = maxPower / 10;
+
+		gc.setForeground(ColorPalette.getColor(new RGB(100, 100, 100)));
+		gc.setBackground(ColorPalette.getColor(new RGB(255, 255, 255)));
+
+		int previousBottom = 0;		// bottom of the previous legend drawn
+		String legend;
+
+		// draw 11 value indicators (0..10) to the scale  
+		int i = 0;
+		for (double y = 0; i < 11; i++, y += yIncrement, maxPower -= powerIncrement)
+		{
+			// construct the text for each scale
+			legend = (int)maxPower + Messages.getString("PowerTraceGraph.24"); //$NON-NLS-1$
+			
+			Point extent = gc.stringExtent(legend);
+			
+			gc.drawLine(GenericTraceGraph.yLegendWidth - 3, (int)y + 1, GenericTraceGraph.yLegendWidth, (int)y + 1);
+
+			if (y >= previousBottom)
+			{
+				gc.drawString(legend, GenericTraceGraph.yLegendWidth - extent.x - 4, (int)y);
+				previousBottom = (int)y + extent.y;
+			}
+		}
+
+		if (localGC == null) {
+			gc.dispose();
+			figureCanvas.redraw();
+		}
+	}
+}