--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sysperfana/heapanalyser/Libraries/UI/HeapCtrlLib/Renderers/HeapDataRenderer.cs Tue Jun 15 12:47:20 2010 +0300
@@ -0,0 +1,1231 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*
+* - Redistributions of source code must retain the above copyright notice,
+* this list of conditions and the following disclaimer.
+* - Redistributions in binary form must reproduce the above copyright notice,
+* this list of conditions and the following disclaimer in the documentation
+* and/or other materials provided with the distribution.
+* - Neither the name of Nokia Corporation nor the names of its contributors
+* may be used to endorse or promote products derived from this software
+* without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+* POSSIBILITY OF SUCH DAMAGE.
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+using HeapLib;
+using HeapLib.Cells;
+using HeapLib.Array;
+using HeapLib.Relationships;
+using HeapLib.Reconstructor;
+using HeapCtrlLib.Utilities;
+using HeapCtrlLib.Interfaces;
+using HeapCtrlLib.Factories;
+using HeapCtrlLib.Types;
+using SymbianUtils.Range;
+using SymbianUtils.RawItems;
+
+namespace HeapCtrlLib.Renderers
+{
+ internal class HeapDataRenderer : UserControl
+ {
+ #region Delegates & events
+ public delegate void AddressChangeHandler( uint aAddressOld, uint aAddressNew, HeapCell aFirstCell, int aFirstCellIndex );
+ public event AddressChangeHandler AddressChanged;
+ public delegate void CellSelectionHandler( HeapCell aCell );
+ public event CellSelectionHandler CellSelected;
+ public delegate void CellDoubleClickHandler( HeapCell aCell );
+ public event CellDoubleClickHandler CellDoubleClicked;
+ public delegate void CellRightClickedHandler( HeapCell aCell, RawItem aItem, Point aViewerPos );
+ public event CellRightClickedHandler CellRightClicked;
+ #endregion
+
+ #region Constructors & destructor
+ internal HeapDataRenderer()
+ {
+ InitializeComponent();
+ //
+ LoadTypeSet( THeapCtrlRenderingType.EHeapCtrlRenderingTypeByCell );
+ //
+ this.SetStyle( ControlStyles.UserPaint, true );
+ this.SetStyle( ControlStyles.DoubleBuffer, true );
+ this.SetStyle( ControlStyles.AllPaintingInWmPaint, true );
+ this.SetStyle( ControlStyles.ResizeRedraw, true );
+ this.SetStyle( ControlStyles.Selectable, true );
+ //
+ this.MouseWheel += new MouseEventHandler( HeapDataRenderer_MouseWheel );
+ }
+
+ protected override void Dispose( bool disposing )
+ {
+ if ( disposing )
+ {
+ }
+ base.Dispose( disposing );
+ }
+ #endregion
+
+ #region Component Designer generated code
+ private void InitializeComponent()
+ {
+ this.iLbl_NoContent = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // iLbl_NoContent
+ //
+ this.iLbl_NoContent.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.iLbl_NoContent.Location = new System.Drawing.Point( 0, 0 );
+ this.iLbl_NoContent.Name = "iLbl_NoContent";
+ this.iLbl_NoContent.Size = new System.Drawing.Size( 600, 408 );
+ this.iLbl_NoContent.TabIndex = 0;
+ this.iLbl_NoContent.Text = "No Content";
+ this.iLbl_NoContent.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // HeapDataRenderer
+ //
+ this.Controls.Add( this.iLbl_NoContent );
+ this.DoubleBuffered = true;
+ this.Name = "HeapDataRenderer";
+ this.Size = new System.Drawing.Size( 600, 408 );
+ this.MouseDown += new System.Windows.Forms.MouseEventHandler( this.HeapDataRenderer_MouseDown );
+ this.MouseMove += new System.Windows.Forms.MouseEventHandler( this.HeapDataRenderer_MouseMove );
+ this.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler( this.HeapDataRenderer_MouseDoubleClick );
+ this.MouseLeave += new System.EventHandler( this.HeapDataRenderer_MouseLeave );
+ this.MouseUp += new System.Windows.Forms.MouseEventHandler( this.HeapDataRenderer_MouseUp );
+ this.KeyDown += new System.Windows.Forms.KeyEventHandler( this.HeapDataRenderer_KeyDown );
+ this.ResumeLayout( false );
+
+ }
+ #endregion
+
+ #region API
+ public void SetupFilters()
+ {
+ if ( !SupportsFiltering )
+ {
+ throw new NotSupportedException();
+ }
+
+ iFactory.Renderers.Content.SetupFilters();
+ }
+
+ public void LoadTypeSet( THeapCtrlRenderingType aType )
+ {
+ iFactory = HeapCtrlLib.Factories.Factory.CreateByType( aType );
+
+ // Set sizes
+ CellBoxSize = iFactory.CellBoxSize( Zoom );
+ CellPadding = iFactory.CellPadding( Zoom );
+
+ // Do init.
+ InitialiseRenderers();
+ }
+
+ public int RowForAddress( uint aAddress )
+ {
+ uint addressOffset = aAddress - Reconstructor.Statistics.HeapAddressStart;
+ uint bytesPerRow = BytesPerRow;
+ int row = (int) ( addressOffset / bytesPerRow );
+ //System.Diagnostics.Debug.WriteLine( "base: 0x" + Reconstructor.Statistics.HeapAddressStart.ToString("x8") + ", offset: 0x" + addressOffset.ToString("x8") + ", addr: 0x" + aAddress.ToString("x8") + ", row: " + row + ", remainder: " + ( addressOffset % bytesPerRow ) + ", bytesPerRow: " + bytesPerRow );
+ //
+ return row;
+ }
+ #endregion
+
+ #region Properties
+ public HeapReconstructor Reconstructor
+ {
+ get
+ {
+ return iReconstructor;
+ }
+ set
+ {
+ iReconstructor = value;
+ if ( iReconstructor != null )
+ {
+ iCells = iReconstructor.Data;
+ InitialiseRenderers();
+ if ( iCells.Count > 0 )
+ {
+ HeapCell cell = Cells[ 0 ];
+
+ // Must first set the address, or else this messes up
+ // VisibleAddressRange...
+ Address = cell.Address;
+
+ // ... which is needed by FocusedCell when changing
+ // the focus.
+ FocusedCell = cell;
+ }
+ }
+ }
+ }
+
+ public HeapCellArray Cells
+ {
+ get
+ {
+ return iCells;
+ }
+ }
+
+ public HeapCell FocusedCell
+ {
+ get
+ {
+ return iFocusedCellKeyboard;
+ }
+ set
+ {
+ iFocusedCellKeyboard = value;
+ if ( iFocusedCellKeyboard != null )
+ {
+ uint visRangeMin = (uint) VisibleAddressRange.Min;
+ uint visRangeMax = (uint) VisibleAddressRange.Max - BytesPerRow;
+
+ // Do we need to move the view?
+ int currentMinRow = RowForAddress( visRangeMin );
+ int row = RowForAddress( value.Address );
+ int currentMaxRow = RowForAddress ( visRangeMax );
+
+ //
+ //System.Diagnostics.Debug.WriteLine( "row: " + row + ", minR: " + currentMinRow + "(" + visRangeMin.ToString("x8") + "), maxR: " + currentMaxRow + "(" + visRangeMax.ToString("x8") + ")" );
+ //System.Diagnostics.Debug.WriteLine( " " );
+
+ if ( row < currentMinRow || row > currentMaxRow )
+ {
+ Address = (uint) ( Reconstructor.Statistics.HeapAddressStart + ( row * BytesPerRow ) );
+ }
+ else
+ {
+ Invalidate();
+ }
+ }
+
+ if ( CellSelected != null )
+ {
+ CellSelected( iFocusedCellKeyboard );
+ }
+ }
+ }
+
+ public uint Address
+ {
+ get { return iCellAddress; }
+ set
+ {
+ this.Enabled = ( value > 0 );
+ iLbl_NoContent.Visible = ( value == 0 );
+
+ // Validate
+ int index = -1;
+ HeapCell cell = Cells.CellByAddress( value, out index );
+ if ( cell != null )
+ {
+ iFactory.PopupManager.PopupHide();
+ //
+ iCellIndex = index;
+ uint oldAddress = iCellAddress;
+ iCellAddress = value;
+ iFocusedCellMouse = null;
+ //
+ if ( AddressChanged != null )
+ {
+ AddressChanged( oldAddress, iCellAddress, cell, iCellIndex );
+ }
+ //
+ Invalidate();
+ }
+ else if ( value == 0 )
+ {
+ // Do nothing, we're disabled
+ }
+ else
+ {
+ // Bad address
+ throw new ArgumentException( "Invalid cell address", "value" );
+ }
+ }
+ }
+
+ public uint BytesPerRow
+ {
+ get
+ {
+ uint bytesPerRow = (uint) RowsAndColumns.Width * 4;
+ return bytesPerRow;
+ }
+ }
+
+ public uint BytesPerScreen
+ {
+ get
+ {
+ uint bytesPerScreen = BytesPerRow * (uint) RowsAndColumns.Height;
+ return bytesPerScreen;
+ }
+ }
+
+ public THeapCtrlZoom Zoom
+ {
+ get { return iZoom; }
+ set
+ {
+ if ( value != iZoom )
+ {
+ // Get base size from factory
+ CellBoxSize = iFactory.CellBoxSize( value );
+ CellPadding = iFactory.CellPadding( value );
+ }
+ //
+ iZoom = value;
+ }
+ }
+
+ public AddressRange VisibleAddressRange
+ {
+ get
+ {
+ AddressRange range = new AddressRange();
+ //
+ range.UpdateMin( Address );
+ range.UpdateMax( Address + BytesPerScreen );
+ //
+ return range;
+ }
+ }
+
+ public bool SupportsFiltering
+ {
+ get { return iFactory.Renderers.Content.SupportsFiltering; }
+ }
+
+ public HeapCellArrayBase BreadcrumbCellsOutgoing
+ {
+ get { return iBreadcrumbCellsOutgoing; }
+ set { iBreadcrumbCellsOutgoing = new HeapCellArrayUnsorted( value ); }
+ }
+
+ public HeapCellArrayBase BreadcrumbCellsIncoming
+ {
+ get { return iBreadcrumbCellsIncoming; }
+ set { iBreadcrumbCellsIncoming = new HeapCellArrayUnsorted( value ); }
+ }
+
+ internal Factory Factory
+ {
+ get { return iFactory; }
+ }
+ #endregion
+
+ #region Co-ordinate mapping
+ public Point AddressToPixelCoordinate( uint aAddress )
+ {
+ Point ret = new Point( 0, 0 );
+ //
+ Size overallPaddingSize = RowsAndColumnsRemainingPixels;
+ Size rowAndColumnDimensions = RowsAndColumns;
+
+ // First, work out how many boxes would be required to draw
+ // the specific cell address
+ long delta = ((long) aAddress - (long) iCellAddress);
+
+ // This can be a negative number
+ int boxesForDelta = (int) ( delta / KDWordSize );
+
+ // Then work out how many rows and columns would be needed to reach
+ // that value.
+ Point numberOfRowsAndColumns = new Point( 0, 0 );
+
+ if ( delta > 0 )
+ {
+ // Going forwards from current top left of view port.
+ numberOfRowsAndColumns.Y = ( boxesForDelta / rowAndColumnDimensions.Width );
+ numberOfRowsAndColumns.X = ( boxesForDelta - ( numberOfRowsAndColumns.Y * rowAndColumnDimensions.Width ) );
+ }
+ else
+ {
+ numberOfRowsAndColumns.Y = ( boxesForDelta / rowAndColumnDimensions.Width ) - 1;
+ numberOfRowsAndColumns.X = Math.Abs( ( numberOfRowsAndColumns.Y * rowAndColumnDimensions.Width ) - boxesForDelta );
+ }
+
+ // Work our pixel pos
+ ret.X = numberOfRowsAndColumns.X * CellBoxSizeIncludingPadding.Width;
+ ret.Y = numberOfRowsAndColumns.Y * CellBoxSizeIncludingPadding.Height;
+
+ // And take into account padding and header size
+ ret.X += CellBoxRect.X;
+ ret.Y += CellBoxRect.Y;
+
+ //
+ return ret;
+ }
+
+ public Point CoordinateToBox( Point aPixelPos )
+ {
+ Point ret = new Point( -1, -1 );
+ //
+ Size rowsAndColumns = RowsAndColumns;
+ if ( CellBoxRect.Contains( aPixelPos ) )
+ {
+ Size overallPaddingSize = RowsAndColumnsRemainingPixels;
+
+ // Work out which row this event occurs within.
+ int row = ( aPixelPos.Y - ( overallPaddingSize.Height / 2 ) ) / CellBoxSizeIncludingPadding.Height;
+ if ( row >= 0 && row < rowsAndColumns.Height )
+ {
+ // Work out the column index for that cell.
+ int xpos = aPixelPos.X - ( overallPaddingSize.Width / 2 ) - CellAddressHeaderSize.Width;
+
+ // How many boxes could we have rendered for that xpos
+ int boxCount = ( xpos / CellBoxSizeIncludingPadding.Width );
+
+ ret.X = boxCount;
+ ret.Y = row;
+ }
+ }
+ //System.Diagnostics.Debug.WriteLine( "aPixelPos[ " + aPixelPos.X + ", " + aPixelPos.Y + " ], boxPos: [ " + ret.X + ", " + ret.Y + " ]" );
+ //
+ return ret;
+ }
+
+ public Point BoxCoordinateByAddress( uint aAddress )
+ {
+ Point ret = new Point( -1, -1 );
+
+ // First, work out how many boxes would be required to draw
+ // the specific cell address
+ uint delta = aAddress - iCellAddress;
+ int boxesForDelta = (int) ( delta / KDWordSize );
+
+ // Then work out how many rows & columns would need to be
+ // drawn to reach that value.
+ Size rowAndColumnDimensions = RowsAndColumns;
+ ret.Y = boxesForDelta / rowAndColumnDimensions.Width;
+ ret.X = boxesForDelta - ( ret.Y * rowAndColumnDimensions.Width );
+ //
+ return ret;
+ }
+
+ public uint AddressByBoxCoordinates( Point aBoxCoordinates )
+ {
+ uint ret = iCellAddress;
+ //
+ if ( Cells.Count > 0 && aBoxCoordinates.X >= 0 && aBoxCoordinates.Y >= 0 )
+ {
+ Size rowsAndColumns = RowsAndColumns;
+ //
+ ret += (uint) ( aBoxCoordinates.Y * KDWordSize * rowsAndColumns.Width );
+ ret += (uint) ( aBoxCoordinates.X * KDWordSize );
+ }
+ //
+ return ret;
+ }
+
+ public uint AddressByCoordinate( Point aPixelPos )
+ {
+ Point boxPos = CoordinateToBox( aPixelPos );
+ uint ret = AddressByBoxCoordinates( boxPos );
+ return ret;
+ }
+
+ public RawItem RawItemByPixelPos( Point aPixelPos )
+ {
+ RawItem ret = null;
+ //
+ Point boxPos = CoordinateToBox( aPixelPos );
+ HeapCell cell = CellByBoxCoordinates( boxPos );
+ //
+ if ( cell != null )
+ {
+ uint rawItemAddress = AddressByBoxCoordinates( boxPos );
+ HeapCell.TRegion region = cell.RegionForAddress( rawItemAddress );
+
+ // We only provide raw items for payload sections
+ if ( region == HeapCell.TRegion.EPayload )
+ {
+ ret = cell[ rawItemAddress ];
+ }
+ }
+ //
+ return ret;
+ }
+
+ public HeapCell CellByPosition( Point aPixelPos )
+ {
+ HeapCell ret = null;
+ //
+ Point boxPos = CoordinateToBox( aPixelPos );
+ if ( Cells.Count > 0 && boxPos.X >= 0 && boxPos.Y >= 0 )
+ {
+ ret = CellByBoxCoordinates( boxPos );
+ }
+ //
+ return ret;
+ }
+
+ public HeapCell CellByBoxCoordinates( Point aBoxCoordinates )
+ {
+ uint address = AddressByBoxCoordinates( aBoxCoordinates );
+ HeapCell ret = Cells.CellByAddress( address );
+ return ret;
+ }
+
+ public int CellIndex( HeapCell aCell )
+ {
+ int index = -1;
+ Cells.CellByExactAddress( aCell.Address, out index );
+ return index;
+ }
+ #endregion
+
+ #region Sizing
+ internal Rectangle CellBoxRect
+ {
+ get
+ {
+ Size overallPaddingSize = RowsAndColumnsRemainingPixels;
+ Size bodySize = RowsAndColumnsInPixels;
+ //
+ Rectangle ret = new Rectangle();
+ ret.X = ( overallPaddingSize.Width / 2 ) + CellAddressHeaderSize.Width;
+ ret.Y = overallPaddingSize.Height / 2;
+ ret.Width = bodySize.Width;
+ ret.Height = bodySize.Height;
+ //
+ return ret;
+ }
+ }
+
+ [Browsable(false)]
+ internal Size CellBoxSize
+ {
+ get { return iCellBoxSize; }
+ set
+ {
+ iCellBoxSize = value;
+ Invalidate();
+ }
+ }
+
+ internal Size CellBoxSizeIncludingPadding
+ {
+ get
+ {
+ Size size = CellBoxSize;
+ //
+ size.Width += CellPadding.Width;
+ size.Height += CellPadding.Height;
+ //
+ return size;
+ }
+ }
+
+ internal Size CellBoxSizeIncludingPaddingHalved
+ {
+ get
+ {
+ Size size = CellBoxSize;
+ //
+ size.Width += CellPadding.Width;
+ size.Height += CellPadding.Height;
+ //
+ return new Size( size.Width / 2, size.Height / 2 );
+ }
+ }
+
+ [Browsable(false)]
+ internal Size CellPadding
+ {
+ get { return iCellPadding; }
+ set
+ {
+ iCellPadding = value;
+ Invalidate();
+ }
+ }
+
+ internal Size CellPaddingHalved
+ {
+ get { return new Size( iCellPadding.Width / 2, iCellPadding.Height / 2 ); }
+ }
+
+ internal Size RowsAndColumns
+ {
+ get
+ {
+ Size s = this.Size;
+
+ // First, strip off the address header text width
+ s.Width -= iHeaderTextWidth;
+
+ // Next, calculate how wide each box will be, including padding
+ int boxWidthPerCell = CellBoxSizeIncludingPadding.Width;
+ int cols = Math.Max( 0, ( s.Width - 1 ) / boxWidthPerCell );
+
+ // Now, do the same for rows
+ int boxHeightPerCell = CellBoxSizeIncludingPadding.Height;
+ int rows = Math.Max( 0, ( s.Height - 1 ) / boxHeightPerCell );
+ //
+ return new Size( cols, rows );
+ }
+ }
+
+ internal Size RowsAndColumnsInPixels
+ {
+ get
+ {
+ Size rowsAndCols = RowsAndColumns;
+ Size rowsAndColsInPixels = new Size( rowsAndCols.Width * CellBoxSizeIncludingPadding.Width, rowsAndCols.Height * CellBoxSizeIncludingPadding.Height );
+ //
+ return rowsAndColsInPixels;
+ }
+ }
+
+ internal Size RowsAndColumnsRemainingPixels
+ {
+ get
+ {
+ Size s = this.Size;
+ Size rowsAndColsInPixels = RowsAndColumnsInPixels;
+ //
+ int colsRemainder = s.Width - rowsAndColsInPixels.Width - CellAddressHeaderSize.Width;
+ int rowsRemainder = s.Height - rowsAndColsInPixels.Height;
+ //
+ return new Size( colsRemainder, rowsRemainder );
+ }
+ }
+
+ internal Size RowsAndColumnsRemainingPixelsDistributedEvenly
+ {
+ get
+ {
+ Size overallPaddingSize = RowsAndColumnsRemainingPixels;
+ overallPaddingSize.Width /= 2;
+ overallPaddingSize.Height /= 2;
+ return overallPaddingSize;
+ }
+ }
+
+ internal Size CellAddressHeaderSize
+ {
+ get
+ {
+ return new Size( iHeaderTextWidth, CellBoxSize.Height );
+ }
+ }
+ #endregion
+
+ #region Drawing
+ protected override void OnPaint( PaintEventArgs aArgs )
+ {
+ try
+ {
+ // First must measure the size of the header text as we need
+ // this in order to layout the boxes. We cannot measure the text width
+ // without a graphics object, hence we have to do that here. There must
+ // be a better way?
+ iHeaderTextWidth = iFactory.Renderers.Header.MeasureCellHeaderText( aArgs.Graphics );
+
+ // Store our graphics object for later use
+ iGraphics = aArgs.Graphics;
+
+ // Make the navigator object that will help us lay out the cells
+ HeapRenderingNavigator navigator = new HeapRenderingNavigator( Cells );
+
+ // Queue events we want to receive
+ navigator.iNavBegin += new HeapCtrlLib.Utilities.HeapRenderingNavigator.NavBegin( Navigator_NavBegin );
+ navigator.iNavEnd += new HeapCtrlLib.Utilities.HeapRenderingNavigator.NavEnd( Navigator_NavEnd );
+ navigator.iNavNewRowHeader += new HeapCtrlLib.Utilities.HeapRenderingNavigator.NavNewRowHeader( Navigator_NavNewRowHeader );
+ navigator.iNavNewColumn += new HeapCtrlLib.Utilities.HeapRenderingNavigator.NavNewColumn( Navigator_NavNewColumn );
+ navigator.iNavHeapCellEnd += new HeapCtrlLib.Utilities.HeapRenderingNavigator.NavHeapCellEnd( Navigator_NavHeapCellEnd );
+
+ // The padding amount we apply around the entire control area in order to
+ // center the data.
+ Size centerAlignmentPadding = RowsAndColumnsRemainingPixelsDistributedEvenly;
+
+ // The co-ordinates at which we will render a box (or header).
+ Point startPos = new Point( centerAlignmentPadding.Width, centerAlignmentPadding.Height );
+
+ // Tell the renderers we're about to start
+ iFactory.Renderers.PrepareToNavigate( navigator );
+
+ // Do it
+ navigator.Navigate( iCellIndex, iCellAddress, iHeaderTextWidth, startPos, RowsAndColumns, CellBoxSize, CellPadding );
+
+ // Draw link lines
+ aArgs.Graphics.Clip = new Region( CellBoxRect );
+
+ foreach ( HeapCell cell in BreadcrumbCellsOutgoing )
+ {
+ RelationshipManager relManager = cell.RelationshipManager;
+
+ // Draw embedded (outgoing) lines
+ foreach ( RelationshipInfo outgoingRelationship in relManager.EmbeddedReferencesTo )
+ {
+ // Get coordinates of top left corner of the box for the specified
+ // address. If we have a clean link, we point right at the cell header.
+ // If not, then we point at the box in question within the target cell.
+ uint targetAddress = outgoingRelationship.FromCellRawItem.Data;
+ if ( outgoingRelationship.IsCleanLink )
+ {
+ targetAddress = outgoingRelationship.ToCell.Address;
+ }
+ Point pixelPosEnd = AddressToPixelCoordinate( targetAddress );
+ pixelPosEnd += CellBoxSizeIncludingPaddingHalved;
+ Point pixelPosStart = AddressToPixelCoordinate( outgoingRelationship.FromCellRawItem.Address );
+ pixelPosStart += CellBoxSizeIncludingPaddingHalved;
+ //
+ Color outgoingPenColour = Color.FromArgb( 120, 255, 0, 0 );
+ using ( Pen pen = new Pen( outgoingPenColour, 4.0f ) )
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
+ //
+ aArgs.Graphics.DrawLine( pen, pixelPosStart, pixelPosEnd );
+ }
+ }
+ }
+
+ foreach( HeapCell cell in BreadcrumbCellsIncoming )
+ {
+ RelationshipManager relManager = cell.RelationshipManager;
+
+ // Draw incoming links
+ foreach ( HeapCell fromCell in relManager.ReferencedBy )
+ {
+ Point pixelPosStart = AddressToPixelCoordinate( fromCell.Address );
+ pixelPosStart += CellBoxSizeIncludingPaddingHalved;
+ Point pixelPosEnd = AddressToPixelCoordinate( cell.Address );
+ pixelPosEnd += CellBoxSizeIncludingPaddingHalved;
+ //
+ Color outgoingPenColour = Color.FromArgb( 100, 0, 0, 255 );
+ using ( Pen pen = new Pen( outgoingPenColour, 4.0f ) )
+ {
+ pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
+ pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor;
+ //
+ aArgs.Graphics.DrawLine( pen, pixelPosStart, pixelPosEnd );
+ }
+ }
+ }
+ }
+ finally
+ {
+ iGraphics = null;
+ base.OnPaint( aArgs );
+ }
+ }
+ #endregion
+
+ #region Navigation call backs
+ private void Navigator_NavBegin()
+ {
+ iRenderStartTime = DateTime.Now;
+ //System.Diagnostics.Debug.WriteLine( "DRAW START" );
+
+ // Clear any existing data
+ iGraphics.Clear( Color.White );
+ }
+
+ private void Navigator_NavEnd()
+ {
+ iFactory.Renderers.RenderingComplete( iGraphics );
+
+ System.DateTime renderTimeEnd = DateTime.Now;
+ //System.Diagnostics.Debug.WriteLine( "DRAW END - " + ( renderTimeEnd.Ticks - iRenderStartTime.Ticks ) / 100 );
+ }
+
+ private void Navigator_NavNewRowHeader( uint aAddress, Point aPosition, Size aDimensions, Size aBoxSize, Size aPadding )
+ {
+ // Draw the address at the start of each row
+ iFactory.Renderers.Header.PaintRowHeader( iGraphics, aPosition, CellAddressHeaderSize, aAddress );
+ }
+
+ private void Navigator_NavNewColumn( HeapCell aCell, HeapCellMetaData aMetaData, uint aAddress, Point aPixelPos, Point aBoxPos, Size aDimensions, Size aBoxSize, Size aPadding )
+ {
+ // Draw content
+ iFactory.Renderers.Content.PaintContent( iGraphics, aMetaData, aCell, aAddress, aPixelPos, aBoxSize, aPadding );
+
+ // Draw cell border
+ iFactory.Renderers.ContentBorder.PaintContentBorder( iGraphics, aMetaData, aCell, aAddress, aPixelPos, aBoxSize, aPadding );
+
+ // If we are handling a click & drag operation, then check whether the cell
+ // needs a border.
+ if ( iMouseSelectionCell != null )
+ {
+ // Get the cell index within the cell array
+ int index = CellIndex( aCell );
+ int mouseBoundLower = (int) iMouseSelectionCellBoundaryLower.Tag;
+ int mouseBoundUpper = (int) iMouseSelectionCellBoundaryUpper.Tag;
+
+ if ( index >= mouseBoundLower && index <= mouseBoundUpper )
+ {
+ iFactory.Renderers.SelectionBorder.PaintSelectionBorder( iGraphics, aMetaData, aCell, aAddress, aPixelPos, aBoxSize, aPadding, THeapSelectionBorderType.ESelectionMouse );
+ }
+ }
+ else if ( iFocusedCellKeyboard == aCell )
+ {
+ // Draw border around currently mouse over'd cell
+ iFactory.Renderers.SelectionBorder.PaintSelectionBorder( iGraphics, aMetaData, aCell, aAddress, aPixelPos, aBoxSize, aPadding, THeapSelectionBorderType.ESelectionKeyboard );
+ }
+ else if ( iFocusedCellMouse == aCell )
+ {
+ // Draw border around currently mouse over'd cell
+ iFactory.Renderers.SelectionBorder.PaintSelectionBorder( iGraphics, aMetaData, aCell, aAddress, aPixelPos, aBoxSize, aPadding, THeapSelectionBorderType.ESelectionMouse );
+ }
+ }
+
+ private void Navigator_NavHeapCellEnd( HeapCell aCell, HeapCellMetaData aMetaData, uint aAddress, Point aPosition, Size aDimensions, Size aBoxSize, Size aPadding )
+ {
+ iFactory.Renderers.HeapCellRenderingComplete( iGraphics, aCell, aMetaData );
+ }
+ #endregion
+
+ #region Internal constants
+ private const uint KDWordSize = SymbianUtils.RawItems.RawItem.KSizeOfOneRawItemInBytes;
+ private Label iLbl_NoContent;
+ #endregion
+
+ #region Internal methods
+ private void InitialiseRenderers()
+ {
+ if ( Reconstructor != null )
+ {
+ iFactory.Renderers.Initialise( Cells, Reconstructor, this );
+ }
+ }
+
+ private void AsyncShowPopup( HeapCellArrayWithStatistics aCells, Point aPos, RawItem aRawItem )
+ {
+ if ( iFactory.PopupManager.SupportsRawItemInfo && aRawItem != null && aRawItem.Tag != null && aRawItem.Tag is RelationshipInfo && aCells.Count == 1 )
+ {
+ HeapCell cell = aCells[ 0 ];
+ iFactory.PopupManager.PopupShowAsync( cell, aRawItem, Reconstructor.Statistics, aPos, PointToScreen( aPos ), CellBoxSizeIncludingPadding, new KeyEventHandler( HeapDataRenderer_KeyDown ) );
+ }
+ else
+ {
+ iFactory.PopupManager.PopupShowAsync( aCells, Reconstructor.Statistics, aPos, PointToScreen( aPos ), CellBoxSizeIncludingPadding, new KeyEventHandler( HeapDataRenderer_KeyDown ) );
+ }
+
+ iMouseHoverPosition = aPos;
+ }
+ #endregion
+
+ #region Key handling
+ protected override bool ProcessCmdKey(ref Message aMsg, Keys aKeyData)
+ {
+ const int WM_KEYFIRST = 0x100;
+ bool handled = false;
+ //
+ if ( aMsg.Msg == WM_KEYFIRST )
+ {
+ //SymbianUtilsUi.Utilities.WindowMessages.PrintMessage( "RCMD [" + aKeyData + "] ", aMsg.Msg );
+ KeyEventArgs keyArgs = new KeyEventArgs( aKeyData );
+ HandleKey( this, keyArgs );
+ handled = keyArgs.Handled;
+ }
+ //
+ if ( !handled )
+ {
+ handled = base.ProcessCmdKey( ref aMsg, aKeyData );
+ }
+ //
+ return handled;
+ }
+
+ private void HeapDataRenderer_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
+ {
+ //System.Diagnostics.Debug.WriteLine( "Key Down: " + e.KeyCode );
+ HandleKey( sender, e );
+ }
+
+ private void HandleKey( object sender, System.Windows.Forms.KeyEventArgs e )
+ {
+ switch( e.KeyCode )
+ {
+ // Move up & down one LINE at a time...
+ case Keys.Down:
+ ScrollByLineDelta( 1 );
+ e.Handled = true;
+ break;
+ case Keys.Up:
+ ScrollByLineDelta( -1 );
+ e.Handled = true;
+ break;
+
+ // Move up & down one PAGE at a time...
+ case Keys.Next:
+ ScrollByLineDelta( RowsAndColumns.Height );
+ e.Handled = true;
+ break;
+ case Keys.Prior:
+ ScrollByLineDelta( -RowsAndColumns.Height );
+ e.Handled = true;
+ break;
+
+ // Move to beginning or end of heap
+ case Keys.Home:
+ ScrollToAddress( Reconstructor.Statistics.HeapAddressStart );
+ e.Handled = true;
+ break;
+ case Keys.End:
+ {
+ uint rowsForHeap = iReconstructor.Statistics.HeapSize / BytesPerRow;
+ uint rowsPerPage = (uint) RowsAndColumns.Height;
+ uint targetRow = rowsForHeap - rowsPerPage + 1;
+ uint address = Reconstructor.Statistics.HeapAddressStart + ( targetRow * BytesPerRow );
+ ScrollToAddress( address );
+ e.Handled = true;
+ break;
+ }
+
+ // Move one cell at a time
+ case Keys.Right:
+ {
+ if ( iFocusedCellKeyboard != null )
+ {
+ int index = Cells.CellIndex( iFocusedCellKeyboard );
+ if ( index + 1 < Cells.Count )
+ {
+ iFactory.PopupManager.PopupHide();
+ FocusedCell = Cells[ index + 1 ];
+ e.Handled = true;
+ }
+ }
+ break;
+ }
+ case Keys.Left:
+ {
+ if ( iFocusedCellKeyboard != null )
+ {
+ int index = Cells.CellIndex( iFocusedCellKeyboard );
+ if ( index - 1 >= 0 )
+ {
+ iFactory.PopupManager.PopupHide();
+ FocusedCell = Cells[ index - 1 ];
+ e.Handled = true;
+ }
+ }
+ break;
+ }
+
+ // Unhandled
+ default:
+ e.Handled = false;
+ break;
+ }
+ }
+
+ private void ScrollByLineDelta( int aLines )
+ {
+ // Update delta to now finally take into account how many
+ // bytes we are going to offset the current address by
+ uint delta = (uint) ( aLines * BytesPerRow );
+
+ // Get current address from renderer
+ uint address = Address;
+
+ // We are going to attempt to offset the current renderer address
+ // by the delta, but we don't want to fall out of bounds (before or
+ // after the min/max address range for the heap).
+ address += delta;
+
+ // Set address
+ ScrollToAddress( address );
+ }
+
+ private void ScrollToAddress( uint aAddress )
+ {
+ // Work out the maximum address. We never prevent the user to scroll so far
+ // that they go past the last line.
+ uint rowsForHeap = Reconstructor.Statistics.HeapSize / BytesPerRow;
+ uint lastRowStartingAddress = Reconstructor.Statistics.HeapAddressStart + ( rowsForHeap * BytesPerRow );
+ uint address = Math.Min( lastRowStartingAddress, Math.Max( Reconstructor.Statistics.HeapAddressStart, aAddress ) );
+
+ if ( address >= Reconstructor.Statistics.HeapAddressStart )
+ {
+ Address = address;
+ }
+ }
+ #endregion
+
+ #region Mouse handling
+ /*protected override void WndProc(ref Message m)
+ {
+ SymbianUtilsUi.Utilities.WindowMessages.PrintMessage( "RNDR ", m.Msg );
+ base.WndProc (ref m);
+ }*/
+
+ private void HeapDataRenderer_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
+ {
+ if ( e.Button == MouseButtons.Right )
+ {
+ // Convert the click pos to a cell
+ HeapCell cell = CellByPosition( new Point( e.X, e.Y ) );
+ if ( cell != null && CellRightClicked != null )
+ {
+ // Convert the click pos to a box
+ Point boxPos = CoordinateToBox( e.Location );
+
+ // and then convert the box coordinate to an address
+ uint address = AddressByBoxCoordinates( boxPos );
+
+ // And the try to map that address to a raw item
+ RawItem rawItem = cell[ address ];
+
+ // Then notify observer
+ CellRightClicked( cell, rawItem, e.Location );
+ }
+ }
+ else
+ {
+ bool fc = Focus();
+ Select();
+ //System.Diagnostics.Debug.WriteLine( "RNDR Focused: " + fc );
+
+ if ( Cells.Count > 0 && iMouseSelectionCell == null )
+ {
+ iMouseSelectionCell = CellByPosition( new Point( e.X, e.Y ) );
+ //
+ if ( iMouseSelectionCell != null )
+ {
+ iFactory.PopupManager.PopupHide();
+ //
+ iMouseSelectionCell.Tag = CellIndex( iMouseSelectionCell );
+ //
+ iMouseSelectionCellBoundaryLower = iMouseSelectionCell;
+ iMouseSelectionCellBoundaryUpper = iMouseSelectionCell;
+ //
+ //System.Diagnostics.Debug.WriteLine( "MouseDOWN: [ " + e.X + ", " + e.Y + " ] " + iMouseSelectionCell.ToString() );
+ //
+ }
+ }
+ }
+ }
+
+ private void HeapDataRenderer_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
+ {
+ //System.Diagnostics.Debug.WriteLine( "MouseUP: [ " + e.X + ", " + e.Y + " ]" );
+ if ( Cells.Count > 0 && iMouseSelectionCell != null )
+ {
+ HeapCell mouseUpHeapCell = CellByPosition( new Point( e.X, e.Y ) );
+ //
+ if ( mouseUpHeapCell != null )
+ {
+ //System.Diagnostics.Debug.WriteLine( "MouseUP: [ " + e.X + ", " + e.Y + " ] " + mouseUpHeapCell.ToString() );
+ }
+
+ FocusedCell = mouseUpHeapCell;
+
+ iMouseSelectionCell = null;
+ Invalidate();
+ }
+ }
+
+ private void HeapDataRenderer_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
+ {
+ if ( Cells.Count > 0 )
+ {
+ Point mousePos = new Point( e.X, e.Y );
+ HeapCell cell = CellByPosition( mousePos );
+ //
+ if ( cell != null )
+ {
+ // Also try to get the raw item associated with the hover position. This can
+ // also return null.
+ RawItem rawItem = RawItemByPixelPos( mousePos );
+
+ // Now process the mouse movement
+ if ( iMouseSelectionCell != null )
+ {
+ int mouseBoundStart = (int) iMouseSelectionCell.Tag;
+ int mouseBoundLower = (int) iMouseSelectionCellBoundaryLower.Tag;
+ int mouseBoundUpper = (int) iMouseSelectionCellBoundaryUpper.Tag;
+ int originalSelectionRangeCount = ( mouseBoundUpper - mouseBoundLower );
+
+ int index = CellIndex( cell );
+ if ( index < mouseBoundStart )
+ {
+ // Reset upper boundary
+ iMouseSelectionCellBoundaryUpper = iMouseSelectionCell;
+ iMouseSelectionCellBoundaryLower = cell;
+ iMouseSelectionCellBoundaryLower.Tag = index;
+ }
+ else if ( index > mouseBoundStart )
+ {
+ // Reset lower boundary
+ iMouseSelectionCellBoundaryLower = iMouseSelectionCell;
+ iMouseSelectionCellBoundaryUpper = cell;
+ iMouseSelectionCellBoundaryUpper.Tag = index;
+ }
+ else if ( index == mouseBoundStart )
+ {
+ iMouseSelectionCellBoundaryUpper = iMouseSelectionCell;
+ iMouseSelectionCellBoundaryLower = iMouseSelectionCell;
+ }
+
+ // Update boundary
+ mouseBoundLower = (int) iMouseSelectionCellBoundaryLower.Tag;
+ mouseBoundUpper = (int) iMouseSelectionCellBoundaryUpper.Tag;
+
+ // Work out if this is a multi-select scenario
+ int selectionRangeCount = mouseBoundUpper - mouseBoundLower;
+ bool selectionHasChanged = ( selectionRangeCount != originalSelectionRangeCount );
+ if ( selectionHasChanged && Math.Abs( selectionRangeCount ) + 1 > 1 )
+ {
+ // Build array of selected cells
+ HeapCellArrayWithStatistics cells = new HeapCellArrayWithStatistics();
+ for( index = mouseBoundLower; index <= mouseBoundUpper; index++ )
+ {
+ cell = Cells[ index ];
+ cells.Add( cell );
+ }
+
+ // Show popup
+ bool popupVisible = iFactory.PopupManager.Visible;
+ Point pos = new Point( e.X, e.Y );
+
+ if ( !popupVisible || !( e.X == iMouseHoverPosition.X && e.Y == iMouseHoverPosition.Y ) )
+ {
+ if ( !popupVisible )
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE - MS [popup not vis], Differing Coords: [ " + e.X + ", " + e.Y + " ] -> Popup Show" );
+ AsyncShowPopup( cells, pos, rawItem );
+ }
+ else
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE - MS [popup visible], Differing Coords: [ " + e.X + ", " + e.Y + " ] -> Popup Already Shown" );
+ iMouseHoverPosition = pos;
+ iFactory.PopupManager.PopupRelocate( cells, Reconstructor.Statistics, pos, PointToScreen( pos ), CellBoxSizeIncludingPadding );
+ }
+ }
+ else if ( !popupVisible )
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE - MS [popup not vis], Differing Coords: [ " + e.X + ", " + e.Y + " ] -> Popup Show" );
+ AsyncShowPopup( cells, pos, rawItem );
+ }
+ }
+ else if ( selectionHasChanged )
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE - Have Existing Selection: [ " + e.X + ", " + e.Y + " ] -> Single Item Hover" );
+ iFactory.PopupManager.PopupHide();
+ }
+ }
+ else
+ {
+ if ( iFactory.PopupManager.Visible )
+ {
+ if ( ! ( e.X == iMouseHoverPosition.X && e.Y == iMouseHoverPosition.Y ) )
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE: [ " + e.X + ", " + e.Y + " ] -> Popup Hidden" );
+ iFactory.PopupManager.PopupHide();
+ }
+ }
+ else if ( ! ( e.X == iMouseHoverPosition.X && e.Y == iMouseHoverPosition.Y ) )
+ {
+ //System.Diagnostics.Debug.WriteLine( "Mouse MOVE: [ " + e.X + ", " + e.Y + " ] -> Popup Show Async" );
+ Point pos = new Point( e.X, e.Y );
+ HeapCellArrayWithStatistics cells = new HeapCellArrayWithStatistics();
+ cells.Add( cell );
+ AsyncShowPopup( cells, pos, rawItem );
+ }
+ }
+ //
+ Invalidate();
+ }
+ //
+ iFocusedCellMouse = cell;
+ }
+ }
+
+ private void HeapDataRenderer_MouseLeave(object sender, System.EventArgs e)
+ {
+ Point pos = System.Windows.Forms.Cursor.Position;
+ //System.Diagnostics.Debug.WriteLine( "Mouse LEAVE: Popup hidden - pos: " + pos + ", locY: " + PointToScreen( Location ).Y );
+
+ iFactory.PopupManager.PopupHide();
+ //
+ iFocusedCellMouse = null;
+ iMouseSelectionCell = null;
+ //
+ Invalidate();
+ }
+
+ private void HeapDataRenderer_MouseWheel(object sender, MouseEventArgs e)
+ {
+ // For each scroll of the mouse wheel
+ int mouseScrollLines = SystemInformation.MouseWheelScrollLines;
+
+ // Odd(?), but scrolling down results in a negative delta (-120), and
+ // scrolling up results in positive
+ int delta = ( e.Delta < 0 ) ? 1 : -1;
+ delta *= mouseScrollLines;
+
+ ScrollByLineDelta( delta );
+ }
+
+ private void HeapDataRenderer_MouseDoubleClick( object sender, MouseEventArgs e )
+ {
+ HeapCell cell = CellByPosition( new Point( e.X, e.Y ) );
+ //
+ if ( cell != null && CellDoubleClicked != null )
+ {
+ CellDoubleClicked( cell );
+ }
+ }
+ #endregion
+
+ #region Data members
+ private HeapCell iFocusedCellKeyboard = null;
+ private HeapCell iFocusedCellMouse = null;
+ private Point iMouseHoverPosition;
+ private HeapCell iMouseSelectionCell = null;
+ private HeapCell iMouseSelectionCellBoundaryLower = null;
+ private HeapCell iMouseSelectionCellBoundaryUpper = null;
+ private HeapReconstructor iReconstructor = null;
+ private HeapCellArray iCells = new HeapCellArray();
+ private int iHeaderTextWidth = 30;
+ private Size iCellBoxSize = new Size( 100, 100 );
+ private Size iCellPadding = new Size( 20, 20 );
+ private int iCellIndex = 0;
+ private uint iCellAddress = 0;
+ private Factory iFactory = null;
+ private Graphics iGraphics = null;
+ private THeapCtrlZoom iZoom = THeapCtrlZoom.EHeapCtrlZoomMedium;
+ private HeapCellArrayWithStatistics iSelectedCells = new HeapCellArrayWithStatistics();
+ private System.DateTime iRenderStartTime = new DateTime();
+ private HeapCellArrayUnsorted iBreadcrumbCellsOutgoing = new HeapCellArrayUnsorted();
+ private HeapCellArrayUnsorted iBreadcrumbCellsIncoming = new HeapCellArrayUnsorted();
+ #endregion
+ }
+}