vbAccelerator - Contents of code file: vbalGrid.ctl

VERSION 5.00
Begin VB.UserControl vbalGrid 
   ClientHeight    =   3600
   ClientLeft      =   0
   ClientTop       =   0
   ClientWidth     =   4800
   OLEDropMode     =   1  'Manual
   ScaleHeight     =   3600
   ScaleWidth      =   4800
   ToolboxBitmap   =   "vbalGrid.ctx":0000
   Begin VB.PictureBox picImage 
      AutoRedraw      =   -1  'True
      AutoSize        =   -1  'True
      BorderStyle     =   0  'None
      Height          =   1920
      Left            =   1980
      ScaleHeight     =   1920
      ScaleWidth      =   1920
      TabIndex        =   0
      TabStop         =   0   'False
      Top             =   900
      Visible         =   0   'False
      Width           =   1920
   End
End
Attribute VB_Name = "vbalGrid"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True
Attribute VB_Description = "vbAccelerator Grid Control"
Option Explicit

'
 ===============================================================================
=======
' Name:     vbAccelerator S-Grid Control
' Author:   Steve McMahon (steve@vbaccelerator.com)
' Date:     22 December 1998
'
' Requires: SSUBTMR.DLL
'           cScrollBars.cls
'           cShellSort.cls
'           mGDI.bas
'           HeaderControl.ctl
'
' Copyright  1998-2003 Steve McMahon for vbAccelerator
'
 -------------------------------------------------------------------------------
-------
' Visit vbAccelerator - advanced free source code for VB programmers
' http://vbaccelerator.com
'
 -------------------------------------------------------------7-----------------
-------
'
' A serious VB grid control.  Can be used to replace the ListView and
 MSFlexGrid, and
' can emulate the Outlook message list view.
'
' Features:
'
'  * Drag-drop columns
'  * Visible or invisible columns
'  * Row height can be set independently for each row
'  * MS Common Controls or vbAccelerator ImageList support
'  * Up to two icons per cell (e.g. a check box and a standard icon)
'  * Indent text within any cell
'  * Many cell text formatting options including multi-line text
'  * Independently set BackColor,ForeColor and Font for each cell
'  * Show/Hide rows to allow filtering options
'  * Show/Hide columns
'  * Scroll bars implemented using true API scroll bars, and support
 flat/encarta style
'  * Up to 2 billion rows and columns (although practically about 20,000 is the
 limit)
'  * Full row sorting by up to three columns at once, allows sorting by icon,
 text,
'    date/time or number.
'  * Tile bitmaps into the grid's background
'  * Autosize columns
'
' Updated 19/10/99
'   * 1) Added hWnd() property (Igor Tur)
'   * 2) Flat Headers (SPM)
'   * 3) Header icons now works when no text set (Igor Tur)
'   * 4) ClearSelection method
'   * 5) EnsureVisible method
'   * 6) Prevented scroll bar edges from being visible in a new grid (see
 UserControl_Show)
'   * 7) Clear RowTextColumn when columns are removed (Rhys Nicholls)
'   * 8) HighlightForeColor and HighlightBackColor Properties (Michael
 Karathanasis, Igor Tur)
'   * 9) Make sure all header items are persisted (Ricardo Taborda dos Reis)
'   * 10) Allow setting of HeaderHeight (Andreas Claesson)
'   * 11) First column didn't resize correctly when dbl click header (Cuong
 Nguyen)
'   * 12) GPF when add column with rows present in grid (Marc Scherwinski)
'   * 13) ColumnWidthChanged event (Brian Beatty)
'   * 14) Ensure cells ungray themselves when enable is set back to true, don't
 draw
'         focus rect when disabled (Ricardo Taborda dos Reis)
'
' Updated 2003-12-08
'   * 1) Added MouseWheel support
'   * 2) CancelEdit now done using a WH_MOUSEHOOK - much more reliable
'   * 3) PreCancelEdit event now offered.
'   * 4) VB6 ImageList support (although not for the header)
'   * 5) Option of separate ImageList for headers
'   * 6) No Horizontal or No Vertical Grid line options
'   * 7) Out of focus highlight colour can be set
'   * 8) Control now draws correctly when it is wider than a single screen
'   * 9) When using left and right keys in row mode, control no longer tries
'        to scroll when the scroll bars are hidden.
'   * 10) Corrected the gap between the borders and selection box, and also
'         the positioning of the focus rectangle.  Borders are more accurately
'         positioned.
'   * 11) Grid fits exactly into the space, rather than having a border
'   * 12) Auto-fill grid lines option
'   * 13) Alternate row back colour option
'   * 14) Outlook-style grouping option with drag-drop grouping of cells
'   * 15) Sorting is now 2 - 10x faster
'   * 16) Inserting and removing rows is 10x faster.
'   * 17) Responds to system colour or display change events
'   * 18) SplitRow property allows a set of rows to be fixed
'         within the grid so they always display
'
'
' Ongoing work:
'
'   * *) Assign auto-edit controls to columns in the grid.  Any control that
'        supports the "Text" property can be used.
'   * *) New virtual mode which works
'   * *) Image processing of background bitmap for selections and highlighting
'   * *) Owner-draw cells
'   * *) Marquee multi-select when not in row-mode
'   * *) Show a checkbox in a cell
'
' FREE SOURCE CODE - ENJOY!
'
 ===============================================================================
=======
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
    lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( _
    ByVal lpLibFileName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" ( _
   ByVal hLibModule As Long) As Long
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()

Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As
 Integer
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long

Private Declare Function ClientToScreen Lib "user32" (ByVal hwnd As Long,
 lpPoint As POINTAPI) As Long
Private Declare Function ScreenToClient Lib "user32" (ByVal hwnd As Long,
 lpPoint As POINTAPI) As Long
Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As
 Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long,
 ByVal yPoint As Long) As Long
Private Declare Function ChildWindowFromPoint Lib "user32" (ByVal hWndParent As
 Long, ByVal xPoint As Long, ByVal yPoint As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As
 Long, ByVal lpString As String) As Long
Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, ByVal
 fEnable As Long) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal
 hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long)
 As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long)
 As Long
Private Const SM_CXVSCROLL = 2
Private Const SM_CYHSCROLL = 3

Private Const WM_ACTIVATEAPP = &H1C
Private Const WM_SETTINGCHANGE = &H1A&
Private Const WM_DISPLAYCHANGE = &H7E&
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_MBUTTONDOWN = &H207
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_NCLBUTTONDOWN = &HA1
Private Const WM_NCMBUTTONDOWN = &HA7
Private Const WM_NCRBUTTONDOWN = &HA4
Private Const WM_LBUTTONUP = &H202

Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA"
 (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"
 (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_EXSTYLE = (-20)
Private Const GWL_STYLE = (-16)
Private Const WS_EX_WINDOWEDGE = &H100
Private Const WS_EX_CLIENTEDGE = &H200
Private Const WS_EX_STATICEDGE = &H20000
Private Const WS_HSCROLL = &H100000
Private Const WS_VSCROLL = &H200000

Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal
 hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long,
 ByVal cy As Long, ByVal wFlags As Long) As Long
Private Enum ESetWindowPosStyles
    SWP_SHOWWINDOW = &H40
    SWP_HIDEWINDOW = &H80
    SWP_FRAMECHANGED = &H20 ' The frame changed: send WM_NCCALCSIZE
    SWP_NOACTIVATE = &H10
    SWP_NOCOPYBITS = &H100
    SWP_NOMOVE = &H2
    SWP_NOOWNERZORDER = &H200 ' Don't do owner Z ordering
    SWP_NOREDRAW = &H8
    SWP_NOREPOSITION = SWP_NOOWNERZORDER
    SWP_NOSIZE = &H1
    SWP_NOZORDER = &H4
    SWP_DRAWFRAME = SWP_FRAMECHANGED
    HWND_NOTOPMOST = -2
End Enum

Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal
 nIndex As Long) As Long
Private Const BITSPIXEL = 12         '  Number of bits per pixel
Private Declare Function ScrollDC Lib "user32" (ByVal hdc As Long, ByVal dx As
 Long, ByVal dy As Long, lprcScroll As RECT, lprcClip As RECT, ByVal hrgnUpdate
 As Long, lprcUpdate As RECT) As Long
Private Declare Function DrawFocusRect Lib "user32" (ByVal hdc As Long, lpRect
 As RECT) As Long
Private Declare Function FrameRect Lib "user32" (ByVal hdc As Long, lpRect As
 RECT, ByVal hBrush As Long) As Long
Private Declare Function DrawTextA Lib "user32" (ByVal hdc As Long, ByVal lpStr
 As String, ByVal nCount As Long, lpRect As RECT, ByVal wFormat As Long) As Long
Private Declare Function DrawTextW Lib "user32" (ByVal hdc As Long, ByVal lpStr
 As Long, ByVal nCount As Long, lpRect As RECT, ByVal wFormat As Long) As Long
Private Const DT_NOFULLWIDTHCHARBREAK = &H80000
Private Const DT_HIDEPREFIX = &H100000
Private Const DT_PREFIXONLY = &H200000
Private Declare Function GetSysColorBrush Lib "user32" (ByVal nIndex As Long)
 As Long
Private Const COLOR_HIGHLIGHT = 13
Private Const COLOR_HIGHLIGHTTEXT = 14
Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long)
 As Long
Private Declare Function FillRect Lib "user32" (ByVal hdc As Long, lpRect As
 RECT, ByVal hBrush As Long) As Long
Private Declare Function SetTextColor Lib "gdi32" (ByVal hdc As Long, ByVal
 crColor As Long) As Long
Private Declare Function SetBkColor Lib "gdi32" (ByVal hdc As Long, ByVal
 crColor As Long) As Long
Private Declare Function SetBkMode Lib "gdi32" (ByVal hdc As Long, ByVal
 nBkMode As Long) As Long
Private Const OPAQUE = 2
Private Const TRANSPARENT = 1
Private Declare Function Rectangle Lib "gdi32" (ByVal hdc As Long, ByVal X1 As
 Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function InflateRect Lib "user32" (lpRect As RECT, ByVal x As
 Long, ByVal y As Long) As Long
Private Declare Function OffsetRect Lib "user32" (lpRect As RECT, ByVal x As
 Long, ByVal y As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect
 As RECT) As Long
Private Declare Function PtInRect Lib "user32" (lpRect As RECT, ByVal ptX As
 Long, ByVal ptY As Long) As Long
Private Declare Function GetClientRect Lib "user32" (ByVal hwnd As Long, lpRect
 As RECT) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal x As
 Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal
 hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long)
 As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal
 hObject As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
 Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long,
 ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As
 Long
Private Declare Function CreateFontIndirect Lib "gdi32" Alias
 "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long

Private Declare Function ImageList_GetIconSize Lib "COMCTL32" (ByVal hImagelist
 As Long, cx As Long, cy As Long) As Long
Private Declare Function ImageList_GetImageCount Lib "COMCTL32" (ByVal
 hImagelist As Long) As Long

Private Type POINTAPI
   x As Long
   y As Long
End Type

Private Declare Sub mouse_event Lib "user32" ( _
   ByVal dwFlags As Long, _
   ByVal dx As Long, ByVal dy As Long, _
   ByVal cButtons As Long, _
   ByVal dwExtraInfo As Long)
Private Const MOUSEEVENTF_ABSOLUTE = &H8000 '  absolute move
Private Const MOUSEEVENTF_LEFTDOWN = &H2 '  left button down
Private Const MOUSEEVENTF_LEFTUP = &H4 '  left button up
Private Const MOUSEEVENTF_MIDDLEDOWN = &H20 '  middle button down
Private Const MOUSEEVENTF_MIDDLEUP = &H40 '  middle button up
Private Const MOUSEEVENTF_MOVE = &H1 '  mouse move
Private Const MOUSEEVENTF_RIGHTDOWN = &H8 '  right button down
Private Const MOUSEEVENTF_RIGHTUP = &H10 '  right button up


Public Enum ECGScrollBarStyles
    ecgSbrRegular = EFSStyleConstants.efsRegular
    ecgSbrEncarta = EFSStyleConstants.efsEncarta
    ecgSbrFlat = EFSStyleConstants.efsFlat
End Enum

Public Enum ECGHdrTextAlignFlags
   ecgHdrTextALignLeft = EHdrTextAlign.HdrTextALignLeft
   ecgHdrTextALignCentre = EHdrTextAlign.HdrTextALignCentre
   ecgHdrTextALignRight = EHdrTextAlign.HdrTextALignRight
End Enum

Public Enum ECGTextAlignFlags
   DT_TOP = &H0&
   DT_LEFT = &H0&
   DT_CENTER = &H1&
   DT_RIGHT = &H2&
   DT_VCENTER = &H4&
   DT_BOTTOM = &H8&
   DT_WORDBREAK = &H10&
   DT_SINGLELINE = &H20&
   DT_EXPANDTABS = &H40&
   DT_TABSTOP = &H80&
   DT_NOCLIP = &H100&
   DT_EXTERNALLEADING = &H200&
   DT_CALCRECT = &H400&
   DT_NOPREFIX = &H800&
   DT_INTERNAL = &H1000&
'#if(WINVER >= =&H0400)
   DT_EDITCONTROL = &H2000&
   DT_PATH_ELLIPSIS = &H4000&
   DT_END_ELLIPSIS = &H8000&
   DT_MODIFYSTRING = &H10000
   DT_RTLREADING = &H20000
   DT_WORD_ELLIPSIS = &H40000
End Enum

Public Enum ECGGridLineMode
   ecgGridStandard = 0
   ecgGridFillControl = 1
End Enum

Public Enum ECGGroupRowState
   ecgCollapsed = 0
   ecgExpanded = 1
End Enum

Public Enum ECGDrawStage
   ecgBeforeAll = 0
   ecgBeforeIconAndText = 10
   ecgAfter = 20
End Enum

' The grid:
Private m_tCells() As tGridCell
Private m_iTotalCellRows As Long
Private m_colGarbageRows As New Collection
Private m_tDefaultCell As tGridCell

' Row and columns and associated info:
Private m_iCols As Long
Private m_iRows As Long
Private m_tRows() As tRowPosition

Private Type tColPosition
   
   lWidth As Long
   lCorrectWidth As Long
   bFixedWidth As Long
   lStartX As Long
   lCellColIndex As Long
   bVisible As Boolean
   bRowTextCol As Boolean
   sKey As String
   sTag As String
   bIncludeInSelect As Boolean
   lHeaderColIndex As Long
   sHeader As String
   iIconIndex As Long
   eTextAlign As ECGHdrTextAlignFlags
   sFmtString As String
   bImageOnRight As Boolean
   eSortType As ECGSortTypeConstants
   eSortOrder As ECGSortOrderConstants
   
   ' 2003-11-26 additions
   bIsGrouped As Boolean
   iGroupOrder As Long
   
   ' 2004-01-10 for future expansion
   bOwnerDraw As Boolean
   
End Type

Private m_tCols() As tColPosition

' Grouping of cells:
Private Type tGroupCells
   iGroupNum As Long
   iRow As Long
   iCol As Long
End Type
Private m_tGroupCells() As tGroupCells

' Sorting:
Private m_cSort As New cGridSortObject

' Selection optimisations for not multi-select:
Private m_iSelRow As Long
Private m_iSelCol As Long
Private m_iLastSelRow As Long
Private m_iLastSelCol As Long

' Defaults:
Private m_lDefaultRowHeight As Long
Private m_lDefaultColumnWidth As Long

' Display fonts:
Private m_Fnt() As StdFont
Private m_hFnt() As Long
Private m_iFontCount As Long

' Drawing area:
Private m_lAvailWidth As Long
Private m_lAvailheight As Long
Private m_lGridWidth As Long
Private m_lGridHeight As Long
Private m_lStartX As Long
Private m_lStartY As Long

' Memory DC for flicker-free (1 row only) - also implements clipping
Private m_hDC As Long
Private m_hBmp As Long
Private m_hBmpOld As Long
Private m_lHeight As Long
Private m_lMaxRowHeight As Long
Private m_lMaxMemDCWidth As Long
Private m_hFntDC As Long
Private m_hFntOldDC As Long

' Background:
Private m_bBitmap As Boolean
Private m_hDCSrc As Long
Private m_lBitmapW As Long
Private m_lBitmapH As Long
Private m_bTrueColor As Boolean

' Icons:
Private m_hIml As Long
' VB6 ImageList support 2003-11-10 (5)
Private m_ptrVb6ImageList As Long
Private m_lIconSizeX As Long
Private m_lIconSizeY As Long
' Separate Header ImageList 2003-11-24
Private m_bHeaderImageListSet As Boolean

' Gridlines:
Private m_bGridLines As Boolean
' 2003-11-26: Switch off horizontal or vertical grid lines
Private m_bNoHorizontalGridLines As Boolean
Private m_bNoVerticalGridLines As Boolean
' 2003-11-26: Grid lines extend to fill control
Private m_eGridLineMode As ECGGridLineMode
Private m_oGridLineColor As OLE_COLOR
Private m_oGridFillLineColor As OLE_COLOR

' Active Colour 19/10/1999 (8)
Private m_oHighlightForeColor As OLE_COLOR
Private m_oHighlightBackColor As OLE_COLOR
' 2003-11-24: NoFocus Colours
Private m_oNoFocusHighlightForeColor As OLE_COLOR
Private m_oNoFocusHighlightBackColor As OLE_COLOR
' 2003-11-27: GroupRow Colours
Private m_oGroupRowBackColor As OLE_COLOR
Private m_oGroupRowForeColor As OLE_COLOR
Private m_oGroupAreaBackColor As OLE_COLOR
' 2003-12-07: Alternate Row BackColor
Private m_oAlternateRowBackColor As OLE_COLOR
' 2003-12-13: Gutter Colour for grouped rows
Private m_oGutterBackColor As OLE_COLOR

Private m_bAlphaBlendSelection As Boolean
Private m_bOutlineSelection As Boolean

' Behaviour:
Private m_bMultiSelect As Boolean
Private m_bMarquee As Boolean
Private WithEvents m_tmrMarquee As CTimer
Attribute m_tmrMarquee.VB_VarHelpID = -1
Private m_lMarqueeStartRow As Long
Private m_lMarqueeStartCol As Long
Private m_lMarqueeEndRow As Long
Private m_lMarqueeEndCol As Long
Private m_bRowMode As Boolean
Private m_bRedraw As Boolean
Private m_bHeader As Boolean
Private m_lSplitRow As Long
Private m_lSplitSeparatorSize As Long
Private m_bStretchLastColumnToFit As Boolean
Private m_lStretchedColumn As Long
Private m_lPtrOwnerDraw As Long

Private WithEvents m_tmrHotTrack As CTimer
Attribute m_tmrHotTrack.VB_VarHelpID = -1
Private m_bHotTrack As Boolean
Private m_lHotTrackRow As Long
Private m_lHotTrackCol As Long

' Control flags
Private m_bInFocus As Boolean
Private m_bDirty As Boolean
Private m_bUserMode As Boolean
Private m_bMouseDown As Boolean

' Edit flags
Private m_bInEdit As Boolean
Private m_bInEndEditInterlock As Boolean
' Store edit row and column 2003-11-10
Private m_iEditRow As Long
Private m_iEditCol As Long
' When window is disabled and control is used as an EXE
' we need to repost the cancel edit event
Private m_bRunningInVBIDE As Boolean
Private m_iRepostMsg As Long
Private m_tRepostPos As POINTAPI
Private m_lRepostShiftState As Long

' Check for WM_SETTINGSCHANGE 2003-12-10
Private m_hWnd As Long
' Check for WM_ACTIVATEAPP 2003-11-10
Private m_hWndParentForm As Long
Private m_bEditable As Boolean
Private m_bSingleClickEdit As Boolean
Private m_bSelChange As Boolean
Private m_bEnabled As Boolean
Private m_bDisableIcons As Boolean
Private m_bHighlightSelectedIcons As Boolean
Private m_bDrawFocusRectangle As Boolean
Private m_bNoOptimiseScroll As Boolean
Private m_bTryToFitGroupRows As Boolean

' "Row Text" Column:
Private m_iRowTextCol As Long
Private m_lRowTextStartCol As Long
Private m_bHasRowText As Boolean
' Search Column:
Private m_iSearchCol As Long
Private m_sSearchString As String

' Scroll bars:
Private WithEvents m_cScroll As cScrollBars
Attribute m_cScroll.VB_VarHelpID = -1
Private m_eScrollStyle As EFSStyleConstants
Private m_bAllowVert As Boolean
Private m_bAllowHorz As Boolean

' Header:
Private WithEvents m_cHeader As cHeaderControl
Attribute m_cHeader.VB_VarHelpID = -1
Private m_cFlatHeader As cFlatHeader
Private m_bHeaderFlat As Boolean

' Add rows on demand
Private m_bAddRowsOnDemand As Boolean
Private m_bInAddRowRequest As Boolean

' Hack for XP Crash with VB6 controls:
Private m_hMod As Long

Public Enum ECGBorderStyle
   ecgBorderStyleNone = 0
   ecgBorderStyle3d = 1
   ecgBorderStyle3dThin = 2
End Enum
Private m_eBorderStyle As ECGBorderStyle

Public Enum ECGSerialiseTypes
   ecgSerialiseSGRID = 0
   ecgSerialiseSGRIDLayout = 1
   ecgSerialiseTextTabNewLine = 2
   ecgSerialiseCSV = 3
End Enum

Public Enum ECGScrollBarTypes
   ecgScrollBarHorizontal
   ecgScrollBarVertical
End Enum

' Events

''' <summary>
''' Raised when a column header is clicked
''' </summary>
''' <param name="lCol">Column index</param>
Public Event ColumnClick(ByVal lCol As Long)
Attribute ColumnClick.VB_Description = "Raised when the user clicks a column."

''' <summary>
''' Raised when a column header's width is about to be changed
''' </summary>
''' <param name="lCol">Column index</param>
''' <param name="lWidth">New width (can be modified)</param>
''' <param name="bCancel">Set to <c>true</c> to cancel the size change.</param>
Public Event ColumnWidthStartChange(ByVal lCol As Long, ByRef lWidth As Long,
 ByRef bCancel As Boolean)
Attribute ColumnWidthStartChange.VB_Description = "Raised before a column's
 width is about to be changed."

''' <summary>
''' Raised whilst a column header's width is changing
''' </summary>
''' <param name="lCol">Column index</param>
''' <param name="lWidth">New width (can be modified)</param>
''' <param name="bCancel">Set to <c>true</c> to cancel the size change.</param>
Public Event ColumnWidthChanging(ByVal lCol As Long, ByRef lWidth As Long,
 ByRef bCancel As Boolean)
Attribute ColumnWidthChanging.VB_Description = "Raised as a column's width is
 being changed."

''' <summary>
''' Raised when a column header's width has been changed
''' </summary>
''' <param name="lCol">Column index</param>
''' <param name="lWidth">New width (can be modified)</param>
''' <param name="bCancel">Set to <c>true</c> to cancel the size change.</param>
Public Event ColumnWidthChanged(ByVal lCol As Long, ByRef lWidth As Long, ByRef
 bCancel As Boolean)
Attribute ColumnWidthChanged.VB_Description = "Raised when a column's width has
 been changed."

''' <summary>
''' Raised when a column header's divider is double clicked
''' </summary>
''' <param name="lCol">Column index</param>
''' <param name="bCancel">Set to <c>true</c> to cancel the size change.</param>
Public Event ColumnDividerDblClick(ByVal lCol As Long, ByRef bCancel As Boolean)
Attribute ColumnDividerDblClick.VB_Description = "Raised when the divider
 between two columns is double clicked."

''' <summary>
''' Raised when the order of columns has been changed in the control.
''' </summary>
Public Event ColumnOrderChanged()
Attribute ColumnOrderChanged.VB_Description = "Raised when the order of the
 columns is changed following a drag-drop operation."

''' <summary>
''' Raised when the user right clicks in the header.
''' </summary>
''' <param name="x">x Position of right click</param>
''' <param name="y">y Position of right click</param>
Public Event HeaderRightClick(ByVal x As Single, ByVal y As Single)
Attribute HeaderRightClick.VB_Description = "Raised when the user right clicks
 on the grid's header."

''' <summary>
''' Raised when the selected cell(s) in the grid change.
''' </summary>
''' <param name="lRow">Most recently selected row</param>
''' <param name="lCol">Most recently selected column</param>
Public Event SelectionChange(ByVal lRow As Long, ByVal lCol As Long)
Attribute SelectionChange.VB_Description = "Raised when the user changes the
 selected cell."

''' <summary>
''' Raised when the hot item in the grid changes.
''' </summary>
''' <param name="lRow">Most recently hot row, may be zero</param>
''' <param name="lCol">Most recently hot column, may be zero</param>
Public Event HotItemChange(ByVal lRow As Long, ByVal lCol As Long)
Attribute HotItemChange.VB_Description = "Raised when the hot cell or row
 changes.  Only raised when HotTrack is True."


''' <summary>
''' Raised when the grid identifies that edit mode should
''' be started.  The application should show the edit control
''' over the specified cell if <c>bCancel</c> is not set to
''' <c>true</c>.
''' </summary>
''' <param name="lRow">Row to edit</param>
''' <param name="lCol">Column to edit</param>
''' <param name="iKeyAscii">ASCII code of key that was pressed, or 0 if
''' edit mode was started using the mouse.</param>
''' <param name="bCancel">Set to <c>true</c> to not enter edit mode.</param>
Public Event RequestEdit(ByVal lRow As Long, ByVal lCol As Long, ByVal
 iKeyAscii As Integer, ByRef bCancel As Boolean)
Attribute RequestEdit.VB_Description = "Raised when the grid has the Editable
 property set to True and the user's actions request editing of the current
 cell."


''' <summary>
''' Raised when the grid identifies that edit should be ended. Provides
 opportunity
''' to validate the data in the cell prior to ending edit mode.
''' </summary>
''' <param name="lRow">Row to edit</param>
''' <param name="lCol">Column to edit</param>
''' <param name="newValue">For future expansion.</param>
''' <param name="bStayInEditMode">Set to <c>true</c> to not stay in edit
 mode.</param>
Public Event PreCancelEdit(ByVal lRow As Long, ByVal lCol As Long, ByRef
 newValue As Variant, ByRef bStayInEditMode As Boolean)
Attribute PreCancelEdit.VB_Description = "Raised when the user has taken an
 action that will cancel an edit operation.  Allows the edit contents to be
 validated prior to exiting edit mode."

''' <summary>
''' Raised when the edit mode is exited.  The application should hide any edit
''' control in response to this event.  Note: do not attempt to perform
 validation
''' in response to this event, use <c>PreCancelEdit</c> instead.
''' </summary>
Public Event CancelEdit()

''' <summary>
''' Raised when the user depresses a key in the control.
''' </summary>
''' <param name="KeyCode">Key code that was depressed</param>
''' <param name="Shift">Current shift mask state</param>
''' <param name="bDoDefault">Set to <c>false</c> to prevent the control
 performing
''' default processing on the key</span>
Public Event KeyDown(KeyCode As Integer, Shift As Integer, bDoDefault As
 Boolean)
Attribute KeyDown.VB_Description = "Raised when a key is pressed in the
 control."

''' <summary>
''' Raised when the control converts a KeyDown in the control
''' into a character.
''' </summary>
''' <param name="KeyAscii">Ascii code of the key that was depressed</param>
Public Event KeyPress(KeyAscii As Integer)
Attribute KeyPress.VB_Description = "Raised after the KeyDown event when the
 key press has been converted to an ASCII code."

''' <summary>
''' Raised when the user releases a key in the control.
''' </summary>
''' <param name="KeyCode">Key code that was released</param>
''' <param name="Shift">Current shift mask state</param>
Public Event KeyUp(KeyCode As Integer, Shift As Integer)
Attribute KeyUp.VB_Description = "Raised when a key is released on the grid."

''' <summary>
''' Raised when the user depresses a mouse button in the control.
''' </summary>
''' <param name="Button">Mouse button that was depressed</param>
''' <param name="Shift">Current shift mask state</param>
''' <param name="X">X position of mouse</param>
''' <param name="Y">Y position of mouse</param>
''' <param name="bDoDefault">Set to <c>false</c> to prevent the control
 performing
''' default processing on the key</span>
Public Event MouseDown(Button As Integer, Shift As Integer, x As Single, y As
 Single, bDoDefault As Boolean)
Attribute MouseDown.VB_Description = "Raised when the a mouse button is pressed
 over the control."

''' <summary>
''' Raised when the user moves the mouse button in the control.
''' </summary>
''' <param name="Button">Mouse button that was depressed</param>
''' <param name="Shift">Current shift mask state</param>
''' <param name="X">X position of mouse</param>
''' <param name="Y">Y position of mouse</param>
Public Event MouseMove(Button As Integer, Shift As Integer, x As Single, y As
 Single)
Attribute MouseMove.VB_Description = "Raised when the mouse moves over the
 control, or when the mouse moves anywhere and a mouse button has been pressed
 over the control."

''' <summary>
''' Raised when the user releases a mouse button in the control.
''' </summary>
''' <param name="Button">Mouse button that was released</param>
''' <param name="Shift">Current shift mask state</param>
''' <param name="X">X position of mouse</param>
''' <param name="Y">Y position of mouse</param>
Public Event MouseUp(Button As Integer, Shift As Integer, x As Single, y As
 Single)
Attribute MouseUp.VB_Description = "Raised when a mouse button is released
 after having been pressed over the control."

''' <summary>
''' Raised when the user double clicks in the control.
''' </summary>
''' <param name="lRow">Row user double clicked in, or 0 if no row.</param>
''' <param name="lCol">Column user double clicked in, or 0 if no row.</param>
Public Event DblClick(ByVal lRow As Long, ByVal lCol As Long)
Attribute DblClick.VB_Description = "Raised when the user double clicks on the
 grid."

''' <summary>
''' Raised when one of the scroll bars is changed
''' </summary>
''' <param name="eBar">The scroll bar that has been changed</param>
Public Event ScrollChange(ByVal eBar As ECGScrollBarTypes)
Attribute ScrollChange.VB_Description = "Raised when the grid is scrolled."

''' <summary>
''' Raised when a row's grouping state is about to be changed.
''' </summary>
''' <param name="lRow">The group row who's state will be changed.</param>
''' <param name="eNewState">The new state for the row.</param>
''' <param name="bCancel">Set to <c>true</c> to cancel the event.</param>
Public Event RowGroupingStateChange(ByVal lRow As Long, ByVal eNewState As
 ECGGroupRowState, ByRef bCancel As Boolean)
Attribute RowGroupingStateChange.VB_Description = "Raised when the state of a
 grouping row changes (between collapsed and expanded)."

Public Event RequestRow(ByVal lRow As Long, ByRef lItemData As Long, ByRef
 bVisible As Boolean, ByRef lHeight As Long, ByRef bNoMoreRows As Boolean)
Attribute RequestRow.VB_Description = "Raised when a new row is needed and the
 AddRowsOnDemand property is set to True."
Public Event RequestRowData(ByVal lRow As Long)
Attribute RequestRowData.VB_Description = "Raised after a new row has been
 added in response to RequestRow when AddRowsOnDemand is set True. Respond by
 filling in the cells for that row."

Implements ISubclass

Public Sub SaveGridData(ByVal sFile As String)
Attribute SaveGridData.VB_Description = "Saves the grid's data using an
 internal format.  The data can be reloaded into a grid with the same columns
 using LoadGridData."
Dim lLenPos As Long
Dim lNowPos As Long
Dim lLen As Long
Dim iFile As Integer

On Error GoTo ErrorHandler

   iFile = FreeFile
   Open sFile For Binary Access Write Lock Read As #iFile

   ' Remove any group rows
   AllowGrouping = False

   ' Write out info that allows us to detect the data
   ' version for future updates
   Put #iFile, , "SGrid002.000.000" ' 16 chars

   ' Space for the length of the data
   lLenPos = Seek(iFile)
   Put #iFile, , lLen

   ' Write the number of rows
   Put #iFile, , m_iRows
   ' And the internal number of allocated cells
   Put #iFile, , m_iTotalCellRows
   ' Garbage row count
   Put #iFile, , m_colGarbageRows.Count

   ' Write the data
   Put #iFile, , m_tRows
   Put #iFile, , m_tCells
   ' Write out the garbage row list
   If (m_colGarbageRows.Count > 0) Then
      Dim v As Variant
      Dim i As Long
      Dim lGarbage() As Long
      ReDim lGarbage(1 To m_colGarbageRows.Count) As Long
      For Each v In m_colGarbageRows
         i = i + 1
         lGarbage(i) = v
      Next
      Put #iFile, , lGarbage
   End If

   ' Write the length
   lNowPos = Seek(iFile)
   lLen = lNowPos - lLenPos - 4
   Seek #iFile, lLenPos
   Put #iFile, , lLen
   Seek #iFile, lNowPos

   Close #iFile
   Exit Sub

ErrorHandler:
Dim sErr As String
Dim lErr As Long
   sErr = Err.Description
   lErr = Err.Number
   On Error Resume Next
   Close #iFile
   Kill sFile
   On Error GoTo 0
   gErr lErr, sErr
   Exit Sub
End Sub

Public Sub LoadGridData(ByVal sFile As String)
Attribute LoadGridData.VB_Description = "Loads data from a grid that was
 previously saved using SaveGridData."
Dim sSaveVer As String
Dim iFile As Integer

On Error GoTo ErrorHandler

   iFile = FreeFile
   Open sFile For Binary Access Read Lock Write As #iFile

   sSaveVer = Space(16)
   Get #iFile, , sSaveVer

   Clear

   Select Case sSaveVer
   Case "SGrid002.000.000"
      loadGridData_2_0_0 iFile
   Case Else
      Close #iFile
      On Error GoTo 0
      gErr 502, "Unsupported data version"
   End Select

   Close #iFile

   UserControl_Resize

   Exit Sub

ErrorHandler:
Dim sErr As String
Dim lErr As Long
   sErr = Err.Description
   lErr = Err.Number
   On Error Resume Next
   Close #iFile
   On Error GoTo 0
   gErr lErr, sErr
   Exit Sub
End Sub

Private Sub loadGridData_2_0_0(ByVal iFile As Integer)
Dim lLen As Long
Dim iGarbageRows As Long
Dim i As Long

   Get #iFile, , lLen ' Not used by this routine, but could be used for a
    sanity check

   Get #iFile, , m_iRows ' Number of rows
   If (m_iRows > 0) Then
      Get #iFile, , m_iTotalCellRows ' Number of cells allocated
      Get #iFile, , iGarbageRows ' Number of garbage rows
      ' Prepare the arrays
      ReDim m_tRows(0 To m_iRows) As tRowPosition
      ReDim m_tCells(1 To m_iCols, 1 To m_iTotalCellRows) As tGridCell
      If (iGarbageRows > 0) Then
         ReDim lGarbage(1 To iGarbageRows) As Long
      End If

      ' Read the data
      Get #iFile, , m_tRows
      Get #iFile, , m_tCells

      ' Read the garbage rows
      If (iGarbageRows > 0) Then
         Get #iFile, , lGarbage
         For i = 1 To iGarbageRows
            m_colGarbageRows.Add lGarbage(i)
         Next i
      End If
      
      ' Set max row height:
      m_lMaxRowHeight = m_lDefaultRowHeight
      For i = 1 To m_iRows
         If (m_tRows(i).lHeight > m_lMaxRowHeight) Then
            m_lMaxRowHeight = m_tRows(i).lHeight
         End If
      Next i
      BuildMemDC m_lMaxRowHeight
      
   End If

End Sub

Public Property Get OwnerDrawImpl() As IGridCellOwnerDraw
Attribute OwnerDrawImpl.VB_Description = "Gets/sets the object which implements
 the IGridCellOwnerDraw interface for this grid, or Nothing if there is no
 owner draw implementation."
   If Not (m_lPtrOwnerDraw = 0) Then
      Set OwnerDrawImpl = ObjectFromPtr(m_lPtrOwnerDraw)
   End If
End Property
Public Property Let OwnerDrawImpl(impl As IGridCellOwnerDraw)
   pSetOwnerDrawImpl impl
End Property
Public Property Set OwnerDrawImpl(impl As IGridCellOwnerDraw)
   pSetOwnerDrawImpl impl
End Property

Private Sub pSetOwnerDrawImpl(impl As IGridCellOwnerDraw)
   If (impl Is Nothing) Then
      m_lPtrOwnerDraw = 0
   Else
      m_lPtrOwnerDraw = ObjPtr(impl)
   End If
End Sub


' SPM 2003-11-10: use Mouse Hook to identify edit cancellation
Friend Function MouseEvent( _
      ByVal iMsg As Long, _
      ByVal hwnd As Long, _
      ByVal x As Long, _
      ByVal y As Long, _
      ByVal hitTest As Long _
   ) As Boolean
   
   If (iMsg = WM_NCLBUTTONDOWN Or iMsg = WM_NCRBUTTONDOWN Or iMsg =
    WM_NCMBUTTONDOWN) Then
            
      EndEdit
      If (m_bInEdit) Then
         ' We have requested to cancel the edit.
         MouseEvent = True
      End If
      
   ElseIf (iMsg = WM_RBUTTONDOWN Or iMsg = WM_LBUTTONDOWN Or iMsg =
    WM_MBUTTONDOWN) Then
      
      ' Check which type of class we are over:
      Dim className As String
      Dim hWndOver As Long
      Dim hWndParent As Long
      Dim hWndDesktop As Long
      Dim hWndChild As Long
      
      hWndDesktop = GetDesktopWindow()
      
      hWndOver = WindowFromPoint(x, y)
      hWndParent = GetParent(hWndOver)
      
      ' The owner of a combo is the desktop
      If Not (hWndOver = hWndDesktop) Then
            
         If (GetProp(hWndOver, MAGIC_END_EDIT_IGNORE_WINDOW_PROP) = 0) Then
            className = WindowClassName(hWndOver)
            
            ' Extra check for ComboLBox probably isn't needed, but menus have a
             parent 0
            If (InStr(className, "ComboLBox") = 0) And (InStr(className,
             "#32768") = 0) Then ' second check!
            
               ' Check if the mouse event is within the boundaries of
               ' the cell that is being edited:
               
               Dim pt As POINTAPI
               Dim cursorPos As POINTAPI
               GetCursorPos cursorPos
               LSet pt = cursorPos
               ScreenToClient UserControl.hwnd, pt
               
               Dim tR As RECT
               Dim lWidth As Long
               Dim lHeight As Long
               Dim clickedInCell As Boolean
               Dim lOffsetX As Long
               
               CellBoundary m_iEditRow, m_iEditCol, tR.left, tR.top, lWidth,
                lHeight
               lOffsetX = m_tCells(m_iEditCol,
                m_tRows(m_iEditRow).lGridCellArrayRow).lIndent + _
                  (Abs(m_tCells(m_iEditCol,
                   m_tRows(m_iEditRow).lGridCellArrayRow).iIconIndex <> -1) *
                   m_lIconSizeX) + _
                  (Abs(m_tCells(m_iEditCol,
                   m_tRows(m_iEditRow).lGridCellArrayRow).lExtraIconIndex <>
                   -1) * m_lIconSizeX)
               tR.left = tR.left \ Screen.TwipsPerPixelX - lOffsetX
               tR.top = tR.top \ Screen.TwipsPerPixelY
               tR.right = tR.left + lWidth \ Screen.TwipsPerPixelX + lOffsetX
               tR.bottom = tR.top + lHeight \ Screen.TwipsPerPixelY
               If (pt.x >= tR.left And pt.x <= tR.right) Then
                  If (pt.y >= tR.top And pt.y <= tR.bottom) Then
                     clickedInCell = True
                  End If
               End If
               
               If Not (clickedInCell) Then
                  EndEdit
                  If (m_bInEdit) Then
                     ' We have requested to cancel cancelling the edit.
                     MouseEvent = True
                  Else
                     GetWindowRect m_hWnd, tR
                     If Not (PtInRect(tR, cursorPos.x, cursorPos.y) = 0) Then
                        
                        m_iRepostMsg = iMsg
                        LSet m_tRepostPos = cursorPos
                        
                        Dim bShift As Boolean
                        Dim bAlt As Boolean
                        Dim bCtrl As Boolean
                        
                        bShift = (GetAsyncKeyState(vbKeyShift) <> 0)
                        bAlt = (GetAsyncKeyState(vbKeyMenu) <> 0)
                        bCtrl = (GetAsyncKeyState(vbKeyControl) <> 0)
                        m_lRepostShiftState = Abs(bShift * vbShiftMask) Or
                         Abs(bCtrl * vbCtrlMask) Or Abs(bAlt * vbAltMask)
                        
                     End If
                  End If
               End If
            
            End If
         End If
         
      End If
      
   End If
   
End Function

Public Property Get HideGroupingBox() As Boolean
Attribute HideGroupingBox.VB_Description = "When AllowGrouping is True,
 Gets/sets whether the drag-drop area for grouping rows is hidden."
   HideGroupingBox = m_cHeader.HideGroupingBox
End Property
Public Property Let HideGroupingBox(ByVal Value As Boolean)
   If Not (m_cHeader.HideGroupingBox = Value) Then
      m_cHeader.HideGroupingBox = Value
      UserControl_Resize
      PropertyChanged "HideGroupingBox"
   End If
End Property

Public Property Get AllowGrouping() As Boolean
Attribute AllowGrouping.VB_Description = "Gets/sets whether the header shows a
 grouping box to drag header items into."
   AllowGrouping = m_cHeader.AllowGrouping
End Property
Public Property Let AllowGrouping(ByVal Value As Boolean)
Dim bRedraw As Boolean
   If Not (m_cHeader.AllowGrouping = Value) Then
      m_cHeader.AllowGrouping = Value
      If Not (Value) Then
         If (m_bRedraw) Then
            bRedraw = True
            Redraw = False
         End If
         pRemoveGroupingRows
         pSyncHeaderOrder
         pRowVisibility m_lSplitRow + 1
         If (bRedraw) Then
            Redraw = True
         End If
      End If
      UserControl_Resize
      PropertyChanged "AllowGrouping"
   End If
End Property

Public Property Get GroupBoxHintText() As String
Attribute GroupBoxHintText.VB_Description = "Gets/sets the text shown in the
 column header grouping box when no column headers are grouped."
   GroupBoxHintText = m_cHeader.GroupBoxHintText
End Property
Public Property Let GroupBoxHintText(ByVal sText As String)
   m_cHeader.GroupBoxHintText = sText
   If (m_cHeader.AllowGrouping) Then
      UserControl.Refresh
   End If
   PropertyChanged "GroupBoxHintText"
End Property

Private Function GetParentFormhWNd() As Long
Dim lHWnd As Long
Dim lhWndParent As Long
   lHWnd = UserControl.hwnd
   lhWndParent = GetParent(lHWnd)
   Do While Not (lhWndParent = 0) And Not (IsWindowVisible(lhWndParent) = 0)
      lHWnd = lhWndParent
      lhWndParent = GetParent(lHWnd)
   Loop
   GetParentFormhWNd = lHWnd
   
   ' Detect if we're running in the VB IDE - the Message Loop
   ' works in a different way in the IDE compared to as an EXE.
   ' In an EXE, we need to repost end edit mouse events over the
   ' control once it is re-enabled.  In the EXE, we don't.
   ' Bitch!
Dim sClass As String
   sClass = WindowClassName(lHWnd)
   ' In the IDE, the form's name starts with 'ThunderForm' or 'ThunderMDIForm'.
   ' In EXE, it starts with 'ThunderRT'.  We assume that this message loop
   ' hacking does not occur in other apps, but it may be that it also occurs
   ' in MS Office...
   If InStr(sClass, "ThunderForm") = 1 Or InStr(sClass, "ThunderMDIForm") = 1
    Then
      m_bRunningInVBIDE = True
   End If
   
End Function

Public Property Get HighlightSelectedIcons() As Boolean
Attribute HighlightSelectedIcons.VB_Description = "Gets/sets whether icons in
 selected cells will be highlighted using the selection colour."
   HighlightSelectedIcons = m_bHighlightSelectedIcons
End Property
Public Property Let HighlightSelectedIcons(ByVal bHighlight As Boolean)
   m_bHighlightSelectedIcons = bHighlight
   PropertyChanged "HighlightSelectedIcons"
End Property
Public Property Get DrawFocusRectangle() As Boolean
Attribute DrawFocusRectangle.VB_Description = "Gets/sets whether a focus
 rectangle (dotted line around the selection) will be shown."
   DrawFocusRectangle = m_bDrawFocusRectangle
End Property
Public Property Let DrawFocusRectangle(ByVal bDraw As Boolean)
   m_bDrawFocusRectangle = bDraw
   PropertyChanged "DrawFocusRectangle"
End Property

Public Property Get Enabled() As Boolean
Attribute Enabled.VB_Description = "Gets/sets whether the grid is enabled or
 not.  Note the grid can still be read when it is disabled, but cannot be
 selected or edited."
   Enabled = m_bEnabled
End Property

Public Property Let Enabled(ByVal bState As Boolean)
Dim iRow As Long, iCol As Long
   m_bEnabled = bState
   m_cHeader.Enabled = bState
   If UserControl.Ambient.UserMode Then
      m_bDirty = True
      For iRow = 1 To m_iRows
         For iCol = 1 To m_iCols
            m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag = True
         Next iCol
      Next iRow
      ' 19/10/1999 (14):
      Draw
      UserControl_Paint
   End If
   PropertyChanged "Enabled"
End Property

Public Property Get DisableIcons() As Boolean
Attribute DisableIcons.VB_Description = "Gets/sets whether icons are drawn
 disabled when the control is disabled."
   DisableIcons = m_bDisableIcons
End Property
Public Property Let DisableIcons(ByVal bState As Boolean)
   m_bDisableIcons = bState
   If Not (m_bEnabled) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "DisableIcons"
End Property

Public Property Get Editable() As Boolean
Attribute Editable.VB_Description = "Gets/sets whether the grid will be
 editable (i.e. raise RequestEdit events)."
   Editable = m_bEditable
End Property
Public Property Let Editable(ByVal bState As Boolean)
   If Not (m_bEditable = bState) Then
      m_bEditable = bState
      PropertyChanged "Editable"
   End If
End Property
Public Property Get SingleClickEdit() As Boolean
Attribute SingleClickEdit.VB_Description = "Gets/sets whether the control
 immediately goes into edit mode on selection of a cell when in EditMode.  The
 default is two-click edit."
   SingleClickEdit = m_bSingleClickEdit
End Property
Public Property Let SingleClickEdit(ByVal bState As Boolean)
   If Not (m_bSingleClickEdit = bState) Then
      m_bSingleClickEdit = bState
      PropertyChanged "SingleClickEdit"
   End If
End Property

Public Property Get SortObject() As cGridSortObject
Attribute SortObject.VB_Description = "Returns a reference to the sort object
 where grid sorting options can be specified."
   Set SortObject = m_cSort
End Property

Public Sub Sort()
Attribute Sort.VB_Description = "Sorts the grid data according to the options
 set up in the SortObject."
Dim sKey As String
Dim i As Long
Dim j As Long
Dim bS As Boolean
Dim bResetRedraw As Boolean
Dim iSortRowBefore As Long
   
   If m_iRows > 0 And m_iCols > 0 Then
   
      bResetRedraw = m_bRedraw
      If (bResetRedraw) Then
         m_bRedraw = False
      End If
      
      If (m_iSelRow > 0) And (m_iSelRow <= m_iRows) Then
         If (m_iSelCol > 0) And (m_iSelCol <= m_iCols) Then
            iSortRowBefore = m_tRows(m_iSelRow).lGridCellArrayRow
         End If
      End If
      
      If Not (m_cSort.GridMatch) Then
         '
         pSyncHeaderWithSort
         
         If (m_cHeader.ColumnGroupCount > 0) Then
            pRemoveGroupingRows
         End If
         '
      End If
                  
      m_cSort.SortItems m_tCells(), m_tRows(), m_lSplitRow + 1, m_iRows
      
      m_tRows(m_lSplitRow + 1).lStartY = 0
                  
      If Not (m_cSort.GridMatch) Then
         If (m_cHeader.ColumnGroupCount > 0) Then
            ' put the grouping rows back in
            pAddGroupingRows
         End If
         m_cSort.SetGridMatch
      End If
      
      pRowVisibility m_lSplitRow + 1
      bS = m_bNoOptimiseScroll
      m_bNoOptimiseScroll = True
      m_bDirty = True
            
      If (iSortRowBefore > 0) Then
         m_iSelRow = 0
         For i = 1 To m_iRows
            If (m_tRows(i).lGridCellArrayRow = iSortRowBefore) Then
               m_iSelRow = i
               Exit For
            End If
         Next i
         If (m_iSelRow > 0) Then
            If Not (pbEnsureVisible(m_iSelRow, m_iSelCol)) Then
            End If
         End If
      End If
      
      If (bResetRedraw) Then
         m_bRedraw = True
         Draw
      End If
      
      m_bNoOptimiseScroll = bS
   
   Else
      ' That makes the sort somewhat quicker :)
      
   End If
   
End Sub

Private Sub pSyncHeaderWithSort()
Dim iCol As Long
Dim lHeaderCol As Long
Dim iSortCol As Long
Dim iSortOrder As Long

   ' First make sure that there are no orphan group
   ' items, if there are then shuffle
   m_cSort.RemoveGroupBubbles

   ' Now check if the header's grouped items match
   ' up with what's in the sort:
   For iCol = 1 To m_iCols
      If (m_tCols(iCol).lHeaderColIndex > 0) Then
         
         iSortOrder = -1
         lHeaderCol = m_tCols(iCol).lHeaderColIndex - 1
         For iSortCol = 1 To m_cSort.Count
            If (m_cSort.GridColumnArrayIndex(iSortCol) = iCol) Then
               If (m_cSort.GroupBy(iSortCol)) Then
                  iSortOrder = iSortCol - 1
                  Exit For
               End If
            End If
         Next iSortCol
         
         If (m_cHeader.ColumnIsGrouped(lHeaderCol)) Then
            If (m_cHeader.ColumnGroupOrder(lHeaderCol) = iSortOrder) Then
               ' No change for this column
            Else
               If (iSortOrder = -1) Then
                  ' has been removed
                  m_cHeader.ColumnIsGrouped(lHeaderCol) = False
               Else
                  ' has had its order changed
                  m_cHeader.ColumnGroupOrder(lHeaderCol) = iSortOrder
               End If
            End If
         Else
            If (iSortOrder = -1) Then
               ' No change for this column
            Else
               ' has been added as a new group at this order:
               m_cHeader.ColumnIsGrouped(lHeaderCol) = True
               m_cHeader.ColumnGroupOrder(lHeaderCol) = iSortOrder
            End If
         End If
         
      End If
   Next iCol
   
   
End Sub

Public Property Get EvaluateTextHeight( _
      ByVal lRow As Long, _
      ByVal lCol As Long _
   ) As Long
Attribute EvaluateTextHeight.VB_Description = "Determines the ideal height
 required to display all the cell's text in a cell.  This property is only of
 any use if the Cell's CellTextAlign property allows multiple lines."
Dim hFntOld As Long
Dim tR As RECT
Dim sCopy As String
Dim iCol As Long, lCCol As Long

   ' Ensure correct font:
   If (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iFntIndex <> 0) Then
      hFntOld = SelectObject(m_hDC, m_hFnt(m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).iFntIndex))
   End If
   
   ' Draw the text, calculating rect:
   If Not IsMissing(m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText) Then
      sCopy = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText
      For iCol = 1 To m_iCols
         If (m_tCols(iCol).lCellColIndex = lCol) Then
            lCCol = iCol
            Exit For
         End If
      Next iCol
      If Len(m_tCols(lCCol).sFmtString) > 0 Then
         sCopy = Format$(sCopy, m_tCols(lCCol).sFmtString)
      End If
      tR.right = m_tCols(lCCol).lWidth - 4 - 2 * Abs(m_bGridLines And Not
       (m_bNoVerticalGridLines))
      tR.right = tR.right - m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).lIndent
      If (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iIconIndex >= 0) Then
         tR.right = tR.right - m_lIconSizeX - 2
      End If
      If (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lExtraIconIndex >= 0)
       Then
         tR.right = tR.right - m_lIconSizeX - 2
      End If
      DrawText m_hDC, sCopy & vbNullChar, -1, tR, m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).eTextFlags Or DT_CALCRECT
      EvaluateTextHeight = tR.bottom - tR.top
   Else
      ' don't need to do anything:
   End If
   
   If (hFntOld <> 0) Then
      SelectObject m_hDC, hFntOld
      hFntOld = 0
   End If
      
End Property
Public Property Get EvaluateTextWidth( _
      ByVal lRow As Long, _
      ByVal lCol As Long, _
      Optional ByVal bForceNoModify As Boolean = True _
   ) As Long
Attribute EvaluateTextWidth.VB_Description = "Determines the ideal width
 required to fully display text in a cell."
   EvaluateTextWidth = plEvaluateTextWidth(lRow, lCol, bForceNoModify, 0)
End Property
Private Property Get plEvaluateTextWidth( _
      ByVal lRow As Long, _
      ByVal lCol As Long, _
      ByVal bForceNoModify As Boolean, _
      ByVal lMaxWidth As Long _
   ) As Long
Dim hFntOld As Long
Dim tR As RECT
Dim sCopy As String
Dim sOrig As String
Dim iCol As Long
Dim lCCol As Long
Dim eFlags As ECGTextAlignFlags
Dim lLastRight As Long

   ' Ensure correct font:
   If (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iFntIndex <> 0) Then
      hFntOld = SelectObject(m_hDC, m_hFnt(m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).iFntIndex))
   End If
   
   ' Find the index of lCol in the columns array:
   For iCol = 1 To m_iCols
      If (m_tCols(iCol).lCellColIndex = lCol) Then
         lCCol = iCol
         Exit For
      End If
   Next iCol
   
   ' Evaluate the text in the cell:
   If Not (IsMissing(m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText))
    Then
      sCopy = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText
   End If
   If Len(m_tCols(lCCol).sFmtString) > 0 Then
      sCopy = Format$(sCopy, m_tCols(lCCol).sFmtString)
   End If
   eFlags = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).eTextFlags Or
    DT_CALCRECT
   
   ' For multi line we specify the right so we get a height:
   If (eFlags And DT_WORDBREAK) = DT_WORDBREAK Then
      tR.right = m_tCols(lCCol).lWidth
      If (lMaxWidth > tR.right) Then
         tR.right = lMaxWidth
      End If
   End If
   If (bForceNoModify) Then
      eFlags = eFlags And Not (DT_WORD_ELLIPSIS Or DT_PATH_ELLIPSIS Or
       DT_MODIFYSTRING Or DT_END_ELLIPSIS)
   End If
   
   sOrig = sCopy
   DrawText m_hDC, sCopy & vbNullChar, -1, tR, eFlags
   If (eFlags And DT_WORDBREAK) = DT_WORDBREAK Then
      Do While (tR.bottom > m_tRows(lRow).lHeight)
         sCopy = sOrig
         ' Extend in blocks of 16 until we fit...
         tR.right = tR.right + 16
         lLastRight = tR.right
         DrawText m_hDC, sCopy & vbNullChar, -1, tR, eFlags
         tR.right = lLastRight
      Loop
   End If
   
   plEvaluateTextWidth = tR.right - tR.left
   
   If (hFntOld <> 0) Then
      SelectObject m_hDC, hFntOld
      hFntOld = 0
   End If
   
End Property

Public Property Get RowTextStartColumn() As Long
Attribute RowTextStartColumn.VB_Description = "Gets/sets the column that text
 in the RowText column will start drawing at."
Attribute RowTextStartColumn.VB_MemberFlags = "400"
   RowTextStartColumn = m_lRowTextStartCol
End Property
Public Property Let RowTextStartColumn(ByVal lColumn As Long)
   m_lRowTextStartCol = lColumn
End Property
Public Property Let DefaultRowHeight(ByVal lHeight As Long)
Attribute DefaultRowHeight.VB_Description = "Gets/sets the height which will be
 used as a default for rows in the grid."
   m_lDefaultRowHeight = lHeight
   PropertyChanged "DefaultRowHeight"
End Property
Public Property Get DefaultRowHeight() As Long
   DefaultRowHeight = m_lDefaultRowHeight
End Property
Public Property Get Redraw() As Boolean
Attribute Redraw.VB_Description = "Gets/sets whether the grid is redrawn in
 response to changes.  Set to False whilst setting many properties to increase
 speed.  Setting to True after it has been False forces a re-draw of the
 control."
Attribute Redraw.VB_ProcData.VB_Invoke_Property = ";Behavior"
   Redraw = m_bRedraw
End Property
Public Property Let Redraw(ByVal bState As Boolean)
   m_bRedraw = bState
   If (UserControl.Ambient.UserMode) And (bState) Then
      m_bDirty = True
      If (m_cHeader.AllowGrouping And Not (m_cHeader.HideGroupingBox)) Then
         UserControl_Paint
      Else
         Draw
         pResizeHeader
      End If
   End If
   PropertyChanged "Redraw"
End Property
Public Property Get EditRow() As Long
Attribute EditRow.VB_Description = "Returns the index of the row currently
 being edited, if any."
   EditRow = m_iEditRow
End Property
Public Property Get EditCol() As Long
Attribute EditCol.VB_Description = "Returns the index of the column currently
 being edited, if any."
   EditCol = m_iEditCol
End Property
Public Property Get SelectionCount() As Long
Attribute SelectionCount.VB_Description = "In row mode; gets the number of
 selected rows in the grid, otherwise gets the number of selected cells in the
 grid."
Dim iRow As Long
Dim iCol As Long
Dim iSelCount As Long
   If (m_bMultiSelect) Then
      For iRow = 1 To m_iRows
         For iCol = 1 To m_iCols
            If (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected) Then
               iSelCount = iSelCount + 1
               If (m_bRowMode) Then
                  Exit For
               End If
            End If
         Next iCol
      Next iRow
      SelectionCount = iSelCount
   Else
      SelectionCount = Abs(m_iSelRow > 0 And m_iSelCol > 0)
   End If
End Property
Public Property Get SelectedRowByIndex(ByVal lIndex As Long) As Long
Attribute SelectedRowByIndex.VB_Description = "Gets the row of the selected
 cell or row with the specified 1-based index.  See also SelectionCount."
Dim iRow As Long
Dim iCol As Long
Dim lSelIndex As Long

   For iRow = 1 To m_iRows
      For iCol = 1 To m_iCols
         If (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected) Then
            lSelIndex = lSelIndex + 1
            If (lIndex = lSelIndex) Then
               SelectedRowByIndex = iRow
               Exit Property
            End If
            If (m_bRowMode) Then
               Exit For
            End If
         End If
      Next iCol
   Next iRow
   
End Property
Public Property Get SelectedColByIndex(ByVal lIndex As Long)
Attribute SelectedColByIndex.VB_Description = "Gets the column of the selected
 cell with the specified 1-based index.  See also SelectionCount."
Dim iRow As Long
Dim iCol As Long
Dim lSelIndex As Long

   For iRow = 1 To m_iRows
      For iCol = 1 To m_iCols
         If (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected) Then
            lSelIndex = lSelIndex + 1
            If (lIndex = lSelIndex) Then
               SelectedColByIndex = iCol
               Exit Property
            End If
            If (m_bRowMode) Then
               Exit For
            End If
         End If
      Next iCol
   Next iRow

End Property

Public Property Get SelectedRow() As Long
Attribute SelectedRow.VB_Description = "Gets the selected row.  In multi-select
 mode, this is the most recently selected row."
Attribute SelectedRow.VB_MemberFlags = "400"
   SelectedRow = m_iSelRow
End Property
Public Property Let SelectedRow(ByVal lRow As Long)
Dim iCol As Long
Dim iRow As Long
   If (m_iSelCol = 0) Then
      'm_iSelCol = plGetFirstVisibleColumn()
   End If
   If (lRow > 0) And (lRow <= m_iRows) Then
      m_iSelRow = lRow
      If (m_bMultiSelect) Then
         For iRow = 1 To m_iRows
            For iCol = 1 To m_iCols
               If (m_bRowMode) Then
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected
                   <> (iRow = m_iSelRow))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   (iRow = m_iSelRow)
               Else
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected)
                   <> ((iCol = m_iSelCol) And (iRow = m_iSelRow))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   ((iCol = m_iSelCol) And (iRow = m_iSelRow))
               End If
            Next iCol
         Next iRow
      Else
         pSingleModeSelect
      End If
      If Not (pbEnsureVisible(m_iSelRow, m_iSelCol)) Then
         Draw
      End If
   Else
      gErr 9, "Row subscript out of range"
   End If
End Property
Public Property Get HotCol() As Long
Attribute HotCol.VB_Description = "Gets the current Hot column (or 0 if no hot
 column) when HotTrack is True."
   If (m_bHotTrack) Then
      HotCol = m_lHotTrackCol
   End If
End Property
Public Property Get HotRow() As Long
Attribute HotRow.VB_Description = "Gets the current hot row (or 0 if no hot
 row) when HotTrack is True."
   If (m_bHotTrack) Then
      HotRow = m_lHotTrackRow
   End If
End Property

Public Property Get SelectedCol() As Long
Attribute SelectedCol.VB_Description = "Gets the selected column.  In
 multi-select mode, this is the most recently selected column."
Attribute SelectedCol.VB_MemberFlags = "400"
   SelectedCol = m_iSelCol
End Property
Public Property Let SelectedCol(ByVal lCol As Long)
Dim iRow As Long
Dim iCol As Long

   If (lCol > 0) And (lCol <= m_iCols) Then
      m_iSelCol = lCol
      If (m_bMultiSelect) Then
         For iRow = 1 To m_iRows
            For iCol = 1 To m_iCols
               If (m_bRowMode) Then
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected
                   <> (iRow = m_iSelRow))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   (iRow = m_iSelRow)
               Else
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected)
                   <> ((iCol = m_iSelCol) And (iRow = m_iSelRow))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   ((iCol = m_iSelCol) And (iRow = m_iSelRow))
               End If
            Next iCol
         Next iRow
      Else
         pSingleModeSelect
      End If
      If Not (pbEnsureVisible(m_iSelRow, m_iSelCol)) Then
         Draw
      End If
   Else
      gErr 9, "Column subscript out of range"
   End If
End Property
Public Property Let ScrollBarStyle(ByVal eStyle As ECGScrollBarStyles)
Attribute ScrollBarStyle.VB_Description = "Gets/sets the style in which scroll
 bars are drawn.  Flat or Encarta style scroll bars are only supported in
 systems with COMCTL32.DLL version 4.72 or higher."
   m_eScrollStyle = eStyle
   If Not (m_cScroll Is Nothing) Then
      m_cScroll.Style = eStyle
   End If
   PropertyChanged "ScrollBarStyle"
End Property
Public Property Get ScrollBarStyle() As ECGScrollBarStyles
   ScrollBarStyle = m_eScrollStyle
End Property
Public Property Get CellFormattedText(ByVal lRow As Long, ByVal lCol As Long)
 As String
Attribute CellFormattedText.VB_Description = "Gets the text of a cell with any
 formatting string applicable to the cell's column applied."
Dim iCCol As Long
Dim iCol As Long
   For iCol = 1 To m_iCols
      If (m_tCols(iCol).lCellColIndex = lCol) Then
         iCCol = iCol
         Exit For
      End If
   Next iCol
   If Len(m_tCols(iCCol).sFmtString) > 0 Then
      CellFormattedText = Format$(m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).sText, m_tCols(iCCol).sFmtString)
   Else
      CellFormattedText = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText
   End If
End Property
Public Property Get CellText(ByVal lRow As Long, ByVal lCol As Long) As Variant
Attribute CellText.VB_Description = "Gets/sets the text associated with a cell.
  This property is a variant allowing you to store Numbers and Dates as well. 
 In columns which are not visible, it could also be used to store objects. "
   If pbValid(lRow, lCol) Then
      CellText = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText
   End If
End Property
Public Property Let CellText(ByVal lRow As Long, ByVal lCol As Long, ByVal
 sText As Variant)
Dim bMissing As Boolean
Dim bMissingNew As Boolean
Dim bChanged As Boolean
   If pbValid(lRow, lCol) Then
      bMissing = IsMissing(m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).sText)
      bMissingNew = IsMissing(sText)
      If (bMissing Or bMissingNew) Then
         If Not (bMissing = bMissingNew) Then
            bChanged = True
         End If
      Else
         If Not (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText = sText)
          Then
            bChanged = True
         End If
      End If
      If (bChanged) Then
         m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).sText = sText
         m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
         Draw
      End If
   End If
End Property
Public Property Get CellTextAlign(ByVal lRow As Long, ByVal lCol As Long) As
 ECGTextAlignFlags
Attribute CellTextAlign.VB_Description = "Gets/sets the alignment and
 formatting properties used to draw cell text."
   If pbValid(lRow, lCol) Then
      CellTextAlign = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).eTextFlags
   End If
End Property
Public Property Let CellTextAlign(ByVal lRow As Long, ByVal lCol As Long, ByVal
 eAlign As ECGTextAlignFlags)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).eTextFlags = eAlign Or
       DT_NOPREFIX And Not DT_CALCRECT
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property

Public Property Get CellIndent(ByVal lRow As Long, ByVal lCol As Long) As Long
Attribute CellIndent.VB_Description = "Gets/sets the horizontal indentation of
 a cell from the cell's border."
   If pbValid(lRow, lCol) Then
      CellIndent = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lIndent
   End If
End Property
Public Property Let CellIndent(ByVal lRow As Long, ByVal lCol As Long, ByVal
 lIndent As Long)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lIndent = lIndent
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property
Public Property Get CellExtraIcon(ByVal lRow As Long, ByVal lCol As Long) As
 Long
Attribute CellExtraIcon.VB_Description = "Gets/sets the extra icon for a cell. 
 This icon will always appear in the leftmost position for the cell.  Set
 CellExtraIcon to -1 to remove an icon.  CellExtraIcons represent ImageList
 icon indexes and run from 0 to Count-1."
   If pbValid(lRow, lCol) Then
      CellExtraIcon = m_tCells(lCol,
       m_tRows(lRow).lGridCellArrayRow).lExtraIconIndex
   End If
End Property
Public Property Let CellExtraIcon(ByVal lRow As Long, ByVal lCol As Long, ByVal
 lIconIndex As Long)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lExtraIconIndex =
       lIconIndex
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property
Public Property Get CellItemData(ByVal lRow As Long, ByVal lCol As Long) As Long
Attribute CellItemData.VB_Description = "Gets/sets a long value associated with
 the cell."
   If pbValid(lRow, lCol) Then
      CellItemData = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lItemData
   End If
End Property
Public Property Let CellItemData(ByVal lRow As Long, ByVal lCol As Long, ByVal
 lItemData As Long)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).lItemData = lItemData
   End If
End Property
Public Property Get CellSelected(ByVal lRow As Long, ByVal lCol As Long) As
 Boolean
Attribute CellSelected.VB_Description = "Gets/sets whether a cell is selected
 or not."
   If pbValid(lRow, lCol) Then
      CellSelected = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bSelected
   End If
End Property
Public Property Let CellSelected(ByVal lRow As Long, ByVal lCol As Long, ByVal
 bState As Boolean)
Dim iInitSelCol As Long
Dim iInitSelRow As Long
Dim iCol As Long
   If pbValid(lRow, lCol) Then
      ' for single select mode, bstate is ignored.
      If (m_bMultiSelect) Then
         iInitSelCol = m_iSelCol
         iInitSelRow = m_iSelRow
         m_iSelRow = lRow
         m_iSelCol = lCol
         If (m_bRowMode) Then
            For iCol = 1 To m_iCols
               m_tCells(iCol, m_tRows(m_iSelRow).lGridCellArrayRow).bDirtyFlag
                = (m_tCells(iCol,
                m_tRows(m_iSelRow).lGridCellArrayRow).bSelected <> bState)
               m_tCells(iCol, m_tRows(m_iSelRow).lGridCellArrayRow).bSelected =
                bState
            Next iCol
         Else
            m_tCells(m_iSelCol,
             m_tRows(m_iSelRow).lGridCellArrayRow).bDirtyFlag =
             (m_tCells(m_iSelCol,
             m_tRows(m_iSelRow).lGridCellArrayRow).bSelected <> bState)
            m_tCells(m_iSelCol, m_tRows(m_iSelRow).lGridCellArrayRow).bSelected
             = bState
         End If
         Draw
      Else
         iInitSelCol = m_iSelCol
         iInitSelRow = m_iSelRow
         m_iSelRow = lRow
         m_iSelCol = lCol
         pSingleModeSelect
         If Not (pbEnsureVisible(m_iSelRow, m_iSelCol)) Then
            Draw
         End If
         If (iInitSelCol <> m_iSelCol) Or (iInitSelRow <> m_iSelRow) Then
            RaiseEvent SelectionChange(m_iSelRow, m_iSelCol)
         End If
      End If
   End If
End Property

Public Property Get CellIcon(ByVal lRow As Long, ByVal lCol As Long) As Long
Attribute CellIcon.VB_Description = "Gets/sets the icon for a cell.  If the
 cell has an icon set via the CellExtraIcon property, this icon will appear
 after it.  Set CellIcon to -1 to remove an icon.  CellIcons represent
 ImageList icon indexes and run from 0 to Count-1."
   If pbValid(lRow, lCol) Then
      CellIcon = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iIconIndex
   End If
End Property
Public Property Let CellIcon(ByVal lRow As Long, ByVal lCol As Long, ByVal
 lIconIndex As Long)
   If pbValid(lRow, lCol) Then
      If Not (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iIconIndex =
       lIconIndex) Then
         m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iIconIndex = lIconIndex
         m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
         Draw
      End If
   End If
End Property
Public Property Get CellBackColor(ByVal lRow As Long, ByVal lCol As Long) As
 OLE_COLOR
Attribute CellBackColor.VB_Description = "Gets/sets the background colour for a
 cell.  Set to -1 to make the cell transparent."
   If pbValid(lRow, lCol) Then
      CellBackColor = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oBackColor
   End If
End Property
Public Property Let CellBackColor(ByVal lRow As Long, ByVal lCol As Long, ByVal
 oColor As OLE_COLOR)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oBackColor = oColor
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property
Public Property Get CellForeColor(ByVal lRow As Long, ByVal lCol As Long) As
 OLE_COLOR
Attribute CellForeColor.VB_Description = "Gets/sets the foreground colour to
 draw a cell in.  Set to -1 to use the default foreground colour."
   If pbValid(lRow, lCol) Then
      CellForeColor = m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oForeColor
   End If
End Property
Public Property Let CellForeColor(ByVal lRow As Long, ByVal lCol As Long, ByVal
 oColor As OLE_COLOR)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oForeColor = oColor
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property
Public Sub CellDefaultForeColor(ByVal lRow As Long, ByVal lCol As Long)
Attribute CellDefaultForeColor.VB_Description = "Sets a cell to use the default
 foreground colour (the fore colour of the control)."
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oForeColor = CLR_NONE
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Sub
Public Sub CellDefaultBackColor(ByVal lRow As Long, ByVal lCol As Long)
Attribute CellDefaultBackColor.VB_Description = "Sets a cell to use the default
 background colour (transparent)."
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).oBackColor = CLR_NONE
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Sub

Public Property Get CellFont(ByVal lRow As Long, ByVal lCol As Long) As StdFont
Attribute CellFont.VB_Description = "Gets/sets the font to use to draw a cell."
   If pbValid(lRow, lCol) Then
      If (m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iFntIndex = 0) Then
         Set CellFont = UserControl.Font
      Else
         Set CellFont = m_Fnt(m_tCells(lCol,
          m_tRows(lRow).lGridCellArrayRow).iFntIndex)
      End If
   End If
End Property
Public Property Let CellFont(ByVal lRow As Long, ByVal lCol As Long, ByVal sFnt
 As StdFont)
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iFntIndex =
       plAddFontIfRequired(sFnt)
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Property
Public Sub CellDefaultFont(ByVal lRow As Long, ByVal lCol As Long)
Attribute CellDefaultFont.VB_Description = "Sets a cell to use the default
 font."
   If pbValid(lRow, lCol) Then
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).iFntIndex = 0
      m_tCells(lCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
      Draw
   End If
End Sub
Public Property Get MultiSelect() As Boolean
Attribute MultiSelect.VB_Description = "Gets/sets whether multiple grid cells
 or rows can be selected or not."
Attribute MultiSelect.VB_ProcData.VB_Invoke_Property = ";Behavior"
   MultiSelect = m_bMultiSelect
End Property
Public Property Let MultiSelect(ByVal bState As Boolean)
Dim iCol As Long
Dim iRow As Long
   If (bState <> m_bMultiSelect) Then
      If Not (bState) Then
         For iRow = 1 To m_iRows
            For iCol = 1 To m_iCols
               If (m_bRowMode) Then
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected
                   <> (iRow = m_iSelRow))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   (iRow = m_iSelRow)
               Else
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected
                   <> ((iRow = m_iSelRow) And (iCol = m_iSelCol)))
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   ((iRow = m_iSelRow) And (iCol = m_iSelCol))
               End If
            Next iCol
         Next iRow
      End If
   End If
   m_bMultiSelect = bState
   m_bMarquee = bState
   Draw
   PropertyChanged "MultiSelect"
End Property

Public Property Get SplitRow() As Long
Attribute SplitRow.VB_Description = "Gets/sets the index of the row to split
 the grid at.  Set to 0 for no split."
   SplitRow = m_lSplitRow
End Property
Public Property Let SplitRow(ByVal lRow As Long)
   If Not (m_lSplitRow = lRow) Then
      m_lSplitRow = lRow
      pResizeHeader
      If (m_iRows > m_lSplitRow) Then
         m_tRows(m_lSplitRow + 1).lStartY = 0
         pRowVisibility m_lSplitRow + 1
      End If
      UserControl_Paint
   End If
End Property
Public Property Get SplitSeparatorSize() As Long
Attribute SplitSeparatorSize.VB_Description = "Gets/sets the height of the
 separator between rows before SplitRow and rows after."
   SplitSeparatorSize = m_lSplitSeparatorSize
End Property
Public Property Let SplitSeparatorSize(ByVal lSize As Long)
   If Not (m_lSplitSeparatorSize = lSize) Then
      m_lSplitSeparatorSize = lSize
      pResizeHeader
      Draw
      PropertyChanged "SplitSeparatorSize"
   End If
End Property

Private Function plSplitSize() As Long
   If (m_lSplitRow > 0) Then
      Dim lSplitSize As Long
      Dim iRow As Long
      For iRow = 1 To m_lSplitRow
         If (iRow < m_iRows) Then
            lSplitSize = lSplitSize + m_tRows(iRow).lHeight
         Else
            lSplitSize = lSplitSize + m_lDefaultRowHeight
         End If
      Next iRow
      plSplitSize = lSplitSize + m_lSplitSeparatorSize
   End If
End Function

Public Property Get RowMode() As Boolean
Attribute RowMode.VB_Description = "Gets/sets whether cells can be selected in
 the grid (False) or rows (True)."
Attribute RowMode.VB_ProcData.VB_Invoke_Property = ";Behavior"
   RowMode = m_bRowMode
End Property
Public Property Let RowMode(ByVal bState As Boolean)
Dim iCol As Long
Dim iRow As Long
Dim bSelRow As Boolean
   m_bRowMode = bState
   If Not (m_bMultiSelect) Then
      If (m_iSelRow > 0) And (m_iSelCol > 0) Then
         For iCol = 1 To m_iCols
            m_tCells(iCol, m_tRows(m_iSelRow).lGridCellArrayRow).bDirtyFlag =
             True
            If (bState) Then
               m_tCells(iCol, m_tRows(m_iSelRow).lGridCellArrayRow).bSelected =
                True
            Else
               m_tCells(iCol, m_tRows(m_iSelRow).lGridCellArrayRow).bSelected =
                (iCol = m_iSelCol)
            End If
         Next iCol
      End If
   Else
      If (bState) Then
         For iRow = 1 To m_iRows
            For iCol = 1 To m_iCols
               If (m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected)
                Then
                  bSelRow = True
                  Exit For
               End If
            Next iCol
            If (bSelRow) Then
               For iCol = 1 To m_iCols
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bSelected =
                   True
                  m_tCells(iCol, m_tRows(iRow).lGridCellArrayRow).bDirtyFlag =
                   True
               Next iCol
            End If
         Next iRow
      End If
   End If
   m_bDirty = True
   Draw
   PropertyChanged "RowMode"
End Property
Public Property Get RowIsGroup(ByVal lRow As Long) As Boolean
Attribute RowIsGroup.VB_Description = "Gets/sets whether a row should be
 regarded as a group row."
   If (lRow > 0) And (lRow <= m_iRows) Then
      RowIsGroup = m_tRows(lRow).bGroupRow
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Get RowGroupingState(ByVal lRow As Long) As ECGGroupRowState
Attribute RowGroupingState.VB_Description = "Gets/sets the state (expanded or
 collapsed) for a grouping row."
   If (lRow > 0) And (lRow <= m_iRows) Then
      If (m_tRows(lRow).bGroupRow) Then
         If (lRow < m_iRows) Then
            If (m_tRows(lRow + 1).bCollapsed) Then
               RowGroupingState = ecgCollapsed
            Else
               RowGroupingState = ecgExpanded
            End If
         End If
      Else
         gErr 501, "The selected row is not a grouping row."
      End If
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Let RowGroupingState(ByVal lRow As Long, ByVal eState As
 ECGGroupRowState)
Dim eCurrentState As ECGGroupRowState
   If (lRow > 0) And (lRow <= m_iRows) Then
      If (m_tRows(lRow).bGroupRow) Then
         If (lRow < m_iRows) Then
            If (m_tRows(lRow + 1).bCollapsed) Then
               eCurrentState = ecgCollapsed
            Else
               eCurrentState = ecgExpanded
            End If
            If Not (eCurrentState = eState) Then
               pExpandCollapseGroupingRow lRow
            End If
         End If
      Else
         gErr 501, "The selected row is not a grouping row."
      End If
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Get RowGroupingLevel(ByVal lRow As Long) As Long
Attribute RowGroupingLevel.VB_Description = "Gets the grouping level of the
 specified row if the row is a group row.  The grouping level specifies how far
 this group row is indented, with level 1 being the first group, 2 being the
 second and so on."
Dim lMax As Long
   If (lRow > 0) And (lRow < m_iRows) Then
      lMax = m_cHeader.ColumnGroupCount
      If (lMax = 0) Then
         RowGroupingLevel = 0
      Else
         If (m_tRows(lRow).lGroupIndentLevel > 0) Then
            RowGroupingLevel = m_tRows(lRow).lGroupIndentLevel
         Else
            RowGroupingLevel = lMax
         End If
      End If
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Get RowGroupStartColumn(ByVal lRow As Long) As Long
Attribute RowGroupStartColumn.VB_Description = "Gets/sets the row at which the
 grouped column's cells start drawing."
   If (lRow > 0) And (lRow <= m_iRows) Then
      RowGroupStartColumn = m_tRows(lRow).lGroupStartColIndex
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Let RowGroupStartColumn(ByVal lRow As Long, ByVal lColumn As
 Long)
Dim iCol As Long
   If (lRow > 0) And (lRow <= m_iRows) Then
      If m_tRows(lRow).lGroupStartColIndex <> lColumn Then
         m_tRows(lRow).lGroupStartColIndex = lColumn
         For iCol = 1 To m_iCols
            m_tCells(iCol, m_tRows(lRow).lGridCellArrayRow).bDirtyFlag = True
         Next iCol
         Draw
      End If
   Else
      gErr 9, "Invalid Row Subscript"
   End If
End Property
Public Property Get GridLines() As Boolean
Attribute GridLines.VB_Description = "Gets/sets whether grid-lines are drawn or
 not."
Attribute GridLines.VB_ProcData.VB_Invoke_Property = ";Appearance"
   GridLines = m_bGridLines
End Property
Public Property Let GridLines(ByVal bState As Boolean)
   m_bDirty = Not (bState = m_bGridLines)
   m_bGridLines = bState
   If (m_bDirty) Then
      Draw
   End If
   PropertyChanged "GridLines"
End Property
Public Property Get NoVerticalGridLines() As Boolean
Attribute NoVerticalGridLines.VB_Description = "Gets/sets whether vertical grid
 lines should be supressed when grid lines are on."
   ' 2003-11-26
   NoVerticalGridLines = m_bNoVerticalGridLines
End Property
Public Property Let NoVerticalGridLines(ByVal bState As Boolean)
   ' 2003-11-26
   m_bDirty = (m_bGridLines And (bState <> m_bNoVerticalGridLines))
   m_bNoVerticalGridLines = bState
   If (m_bDirty) Then
      Draw
   End If
   PropertyChanged "NoVerticalGridLines"
End Property
Public Property Get NoHorizontalGridLines() As Boolean
Attribute NoHorizontalGridLines.VB_Description = "Gets/sets whether horizontal
 grid lines should be supressed when grid lines are on."
   ' 2003-11-26
   NoHorizontalGridLines = m_bNoHorizontalGridLines
End Property
Public Property Let NoHorizontalGridLines(ByVal bState As Boolean)
   ' 2003-11-26
   m_bDirty = (m_bGridLines And (bState <> m_bNoHorizontalGridLines))
   m_bNoHorizontalGridLines = bState
   If (m_bDirty) Then
      Draw
   End If
   PropertyChanged "NoHorizontalGridLines"
End Property
Public Property Get GridLineMode() As ECGGridLineMode
Attribute GridLineMode.VB_Description = "Gets/sets the grid line mode.  The
 mode can either be standard, in which grid lines are only drawn around cells,
 or fill, in which case the grid lines fill the control."
   ' 2003-11-26
   GridLineMode = m_eGridLineMode
End Property
Public Property Let GridLineMode(ByVal eMode As ECGGridLineMode)
   ' 2003-11-26
   If Not (m_eGridLineMode = eMode) Then
      m_eGridLineMode = eMode
      If (m_bGridLines) Then
         m_bDirty = True
         Draw
      End If
      PropertyChanged "GridLineMode"
   End If
End Property

Public Property Let HeaderImageList(vThis As Variant)
Attribute HeaderImageList.VB_Description = "Gets/sets an ImageList to associate
 with the Header control.  By default, the ImageList associated with the
 control is used."
Dim hImlHeader As Long

   ' Set the ImageList handle property either from a VB
   ' image list or directly:
   If VarType(vThis) = vbObject Then
       ' Assume VB ImageList control.  Note that unless
       ' some call has been made to an object within a
       ' VB ImageList the image list itself is not
       ' created.  Therefore hImageList returns error. So
       ' ensure that the ImageList has been initialised by
       ' drawing into nowhere:
       On Error Resume Next
       ' Get the image list initialised..
       vThis.ListImages(1).Draw 0, 0, 0, 1
       hImlHeader = vThis.hImagelist
       If (Err.Number <> 0) Then
           hImlHeader = 0
       Else
            ' Check for VB6 image list:
            If (TypeName(vThis) = "ImageList") Then
                If (vThis.ListImages.Count <>
                 ImageList_GetImageCount(hImlHeader)) Then
                  ' VB6 Image List is no good
                  gErr 1049, _
                     "Cannot use MSCOMCTL.OCX ImageList for the
                      HeaderImageList."
                  hImlHeader = 0
                End If
            End If
       End If
       On Error GoTo 0
   ElseIf IsNumeric(vThis) Then
      On Error Resume Next
       ' Assume ImageList handle:
       hImlHeader = CLng(vThis)
       If Not (Err.Number = 0) Then
          gErr 1049, "ImageList property expects ImageList object or long
           hImageList handle."
       End If
   Else
       gErr 1049, "ImageList property expects ImageList object or long
        hImageList handle."
   End If
    
   ' Set or remove the header image list:
   m_cHeader.SetImageList UserControl.hdc, hImlHeader
   m_bHeaderImageListSet = True

End Property

Public Property Let ImageList(vThis As Variant)
Attribute ImageList.VB_Description = "Sets an ImageList as the source of icons
 for the control.  The ImageList can be either a VB ImageList, a vbAccelerator
 ImageList or an API hIml handle.  If it is a VB Image List, the Image List
 must have had at least one icon in it before using this prop"
Attribute ImageList.VB_ProcData.VB_Invoke_PropertyPut = ";Behavior"
Dim hIml As Long
   
   m_hIml = 0
   m_ptrVb6ImageList = 0

   ' Set the ImageList handle property either from a VB
   ' image list or directly:
   If VarType(vThis) = vbObject Then
       ' Assume VB ImageList control.  Note that unless
       ' some call has been made to an object within a
       ' VB ImageList the image list itself is not
       ' created.  Therefore hImageList returns error. So
       ' ensure that the ImageList has been initialised by
       ' drawing into nowhere:
       On Error Resume Next
       ' Get the image list initialised..
       vThis.ListImages(1).Draw 0, 0, 0, 1
       hIml = vThis.hImagelist
       If (Err.Number <> 0) Then
           hIml = 0
       Else
            ' Check for VB6 image list:
            If (TypeName(vThis) = "ImageList") Then
                If (vThis.ListImages.Count <> ImageList_GetImageCount(hIml))
                 Then
                    Dim o As Object
                    Set o = vThis
                    m_ptrVb6ImageList = ObjPtr(o)
                End If
            End If
       End If
       On Error GoTo 0
   ElseIf IsNumeric(vThis) Then
      On Error Resume Next
       ' Assume ImageList handle:
       hIml = CLng(vThis)
       If Not (Err.Number = 0) Then
          gErr 1049, "ImageList property expects ImageList object or long
           hImageList handle."
       End If
   Else
       gErr 1049, "ImageList property expects ImageList object or long
        hImageList handle."
   End If
    
   ' If we have a valid image list, then associate it with the control:
   If Not (hIml = 0) Or Not (m_ptrVb6ImageList = 0) Then
      m_hIml = hIml
      If Not (m_hIml = 0) Then
         If Not (m_bHeaderImageListSet) Then
            m_cHeader.SetImageList UserControl.hdc, hIml
         End If
      End If
      If (m_ptrVb6ImageList = 0) Then
         ImageList_GetIconSize m_hIml, m_lIconSizeX, m_lIconSizeY
      Else
         m_lIconSizeX = vThis.ImageWidth
         m_lIconSizeY = vThis.ImageHeight
      End If
   End If
   
End Property

Public Property Set BackgroundPicture(sPic As StdPicture)
Attribute BackgroundPicture.VB_Description = "Gets/sets a picture to be used as
 the grid's background."
Attribute BackgroundPicture.VB_ProcData.VB_Invoke_PropertyPutRef = ";Appearance"
On Error Resume Next
   
   Set picImage.Picture = sPic
   picImage.Refresh
   If (Err.Number <> 0) Or (picImage.ScaleWidth = 0) Or (sPic Is Nothing) Then
      m_hDCSrc = 0
      m_bBitmap = False
   Else
      m_bBitmap = True
      m_hDCSrc = picImage.hdc
      m_lBitmapW = picImage.ScaleWidth \ Screen.TwipsPerPixelX
      m_lBitmapH = picImage.ScaleHeight \ Screen.TwipsPerPixelY
   End If
   m_bDirty = True
   Draw
   
   PropertyChanged "BackgroundPicture"
   
End Property
Public Property Get BackgroundPictureHeight() As Long
Attribute BackgroundPictureHeight.VB_Description = "Gets/sets the height of the
 background picture."
Attribute BackgroundPictureHeight.VB_ProcData.VB_Invoke_Property = ";Appearance"
Attribute BackgroundPictureHeight.VB_MemberFlags = "400"
   BackgroundPictureHeight = m_lBitmapH
End Property
Public Property Let BackgroundPictureHeight(ByVal lHeight As Long)
   m_lBitmapH = lHeight
   PropertyChanged "BackgroundPictureHeight"
End Property
Public Property Get BackgroundPictureWidth() As Long
Attribute BackgroundPictureWidth.VB_Description = "Gets/sets the width of the
 background picture."
Attribute BackgroundPictureWidth.VB_ProcData.VB_Invoke_Property = ";Appearance"
Attribute BackgroundPictureWidth.VB_MemberFlags = "400"
   BackgroundPictureWidth = m_lBitmapW
End Property
Public Property Let BackgroundPictureWidth(ByVal lWidth As Long)
   m_lBitmapW = lWidth
   PropertyChanged "BackgroundPictureWidth"
End Property

Public Property Get BackgroundPicture() As StdPicture
   Set BackgroundPicture = picImage.Picture
End Property

Public Property Get AlternateRowBackColor() As OLE_COLOR
Attribute AlternateRowBackColor.VB_Description = "Gets/sets the background
 colour to use when rendering alternate rows.  Set to -1 to use the standard
 back colour."
   AlternateRowBackColor = m_oAlternateRowBackColor
End Property
Public Property Let AlternateRowBackColor(ByVal oColor As OLE_COLOR)
   If Not (m_oAlternateRowBackColor = oColor) Then
      m_oAlternateRowBackColor = oColor
      If (m_bRedraw) Then
         m_bDirty = True
         Draw
      End If
      PropertyChanged "AlternateRowBackColor"
   End If
End Property

Public Property Get BackColor() As OLE_COLOR
Attribute BackColor.VB_Description = "Gets/sets the background color of the
 grid."
Attribute BackColor.VB_ProcData.VB_Invoke_Property = ";Appearance"
Attribute BackColor.VB_UserMemId = -501
   BackColor = UserControl.BackColor
End Property
Public Property Let BackColor(ByVal oColor As OLE_COLOR)
   UserControl.BackColor = oColor
   If (m_hDC <> 0) Then
      SetBkColor m_hDC, TranslateColor(UserControl.BackColor)
   End If
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "BackColor"
End Property
Public Property Get HighlightBackColor() As OLE_COLOR
Attribute HighlightBackColor.VB_Description = "Gets/sets the background colour
 of highlighted cells.  Set to -1 to use the default."
' 19/10/1999 (8)
   HighlightBackColor = m_oHighlightBackColor
End Property
Public Property Let HighlightBackColor(oColor As OLE_COLOR)
' 19/10/1999 (8)
   m_oHighlightBackColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "HighlightBackColor"
End Property
Public Property Get HighlightForeColor() As OLE_COLOR
Attribute HighlightForeColor.VB_Description = "Gets/sets the foreground colour
 of highlighted cells.  Set to -1 to use the default."
' 19/10/1999 (8)
   HighlightForeColor = m_oHighlightForeColor
End Property
Public Property Let HighlightForeColor(oColor As OLE_COLOR)
' 19/10/1999 (8)
   m_oHighlightForeColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "HighlightForeColor"
End Property

Public Property Get NoFocusHighlightBackColor() As OLE_COLOR
Attribute NoFocusHighlightBackColor.VB_Description = "Gets/sets the highlight
 background colour for cells when the grid is out of focus.  Use -1 for the
 default colour."
' 2003-11-26
   NoFocusHighlightBackColor = m_oNoFocusHighlightBackColor
End Property
Public Property Let NoFocusHighlightBackColor(ByVal oColor As OLE_COLOR)
' 2003-11-26
   m_oNoFocusHighlightBackColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "NoFocusHighlightBackColor"
End Property
Public Property Get NoFocusHighlightForeColor() As OLE_COLOR
Attribute NoFocusHighlightForeColor.VB_Description = "Gets/sets the highlight
 foreground colour for cells when the grid is out of focus.  Use -1 for the
 default colour."
' 2003-11-26
   NoFocusHighlightForeColor = m_oNoFocusHighlightForeColor
End Property
Public Property Let NoFocusHighlightForeColor(ByVal oColor As OLE_COLOR)
' 2003-11-26
   m_oNoFocusHighlightForeColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "NoFocusHighlightForeColor"
End Property

Public Property Get GroupingAreaBackColor() As OLE_COLOR
Attribute GroupingAreaBackColor.VB_Description = "Gets/sets the colour of the
 column grouping area."
   GroupingAreaBackColor = m_oGroupAreaBackColor
End Property
Public Property Let GroupingAreaBackColor(ByVal oColor As OLE_COLOR)
   m_oGroupAreaBackColor = oColor
   If (m_bRedraw And m_cHeader.AllowGrouping And Not
    (m_cHeader.HideGroupingBox)) Then
      m_bDirty = True
      UserControl_Paint
   End If
   PropertyChanged "GroupingAreaBackColor"
End Property

Public Property Get GroupingGutterBackColor() As OLE_COLOR
Attribute GroupingGutterBackColor.VB_Description = "Gets/sets the colour used
 to fill the gutter to the near side of a row which is indented in a group."
   GroupingGutterBackColor = m_oGutterBackColor
End Property
Public Property Let GroupingGutterBackColor(ByVal oColor As OLE_COLOR)
   m_oGutterBackColor = oColor
   If (m_bRedraw And m_cHeader.AllowGrouping) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "GutterBackColor"
End Property

Public Property Get GroupRowBackColor() As OLE_COLOR
Attribute GroupRowBackColor.VB_Description = "Gets/sets the background colour
 for grouping rows."
   GroupRowBackColor = m_oGroupRowBackColor
End Property
Public Property Let GroupRowBackColor(ByVal oColor As OLE_COLOR)
   m_oGroupRowBackColor = oColor
   If (m_bRedraw And m_cHeader.AllowGrouping And m_cHeader.ColumnGroupCount >
    0) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "GroupRowBackColor"
End Property
Public Property Get GroupRowForeColor() As OLE_COLOR
Attribute GroupRowForeColor.VB_Description = "Gets/sets the foreground colour
 of the text in grouping rows."
   GroupRowForeColor = m_oGroupRowForeColor
End Property
Public Property Let GroupRowForeColor(ByVal oColor As OLE_COLOR)
   m_oGroupRowForeColor = oColor
   If (m_bRedraw And m_cHeader.AllowGrouping And m_cHeader.ColumnGroupCount >
    0) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "GroupRowForeColor"
End Property

Public Property Get ForeColor() As OLE_COLOR
Attribute ForeColor.VB_Description = "Gets/sets the foreground color used to
 draw the control."
Attribute ForeColor.VB_ProcData.VB_Invoke_Property = ";Appearance"
Attribute ForeColor.VB_UserMemId = -513
   ForeColor = UserControl.ForeColor
End Property
Public Property Let ForeColor(ByVal oColor As OLE_COLOR)
   UserControl.ForeColor = oColor
   If (m_hDC <> 0) Then
      SetTextColor m_hDC, TranslateColor(oColor)
   End If
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "ForeColor"
End Property
Public Property Get GridLineColor() As OLE_COLOR
Attribute GridLineColor.VB_Description = "Gets/sets the colour used to draw
 grid lines."
Attribute GridLineColor.VB_ProcData.VB_Invoke_Property = ";Appearance"
   GridLineColor = m_oGridLineColor
End Property
Public Property Let GridLineColor(ByVal oColor As OLE_COLOR)
   m_oGridLineColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "GridLineColor"
End Property

Public Property Get GridFillLineColor() As OLE_COLOR
Attribute GridFillLineColor.VB_Description = "Gets the colour used to draw fill
 grid lines.  Set to -1 to use the default grid line colour."
   GridFillLineColor = m_oGridFillLineColor
End Property
Public Property Let GridFillLineColor(ByVal oColor As OLE_COLOR)
   m_oGridFillLineColor = oColor
   If (m_bRedraw) Then
      m_bDirty = True
      Draw
   End If
   PropertyChanged "GridFillLineColor"
End Property

Public Property Get Font() As StdFont
Attribute Font.VB_Description = "Gets/sets the font used by the control."
Attribute Font.VB_ProcData.VB_Invoke_Property = ";Appearance"
Attribute Font.VB_UserMemId = -512
Dim tLF As LOGFONT
   Set Font = UserControl.Font
End Property
Public Property Set Font(ByVal sFont As StdFont)
Dim tLF As LOGFONT
   
   Set UserControl.Font = sFont
   m_cHeader.SetFont UserControl.hdc, sFont
   If (m_hFntDC <> 0) Then
      If (m_hDC <> 0) Then
         If (m_hFntOldDC <> 0) Then
            SelectObject m_hDC, m_hFntOldDC
         End If
         DeleteObject m_hFntDC
      End If
   End If
   pOLEFontToLogFont sFont, UserControl.hdc, tLF
   m_hFntDC = CreateFontIndirect(tLF)
   If (m_hDC <> 0) Then
      m_hFntOldDC = SelectObject(m_hDC, m_hFntDC)
   End If
   PropertyChanged "Font"
   
End Property

Public Property Get AddRowsOnDemand() As Boolean
Attribute AddRowsOnDemand.VB_Description = "Gets/sets whether the grid is in
 Virtual Mode (i.e. rows are added as required via the RequestRow and
 RequestRowData events)."
   AddRowsOnDemand = m_bAddRowsOnDemand
End Property
Public Property Let AddRowsOnDemand(ByVal bAddRowsOnDemand As Boolean)
   m_bAddRowsOnDemand = bAddRowsOnDemand
   If Not m_bAddRowsOnDemand Then
      m_bInAddRowRequest = False
   Else
      m_bInAddRowRequest = True
   End If
   PropertyChanged "AddRowsOnDemand"
End Property

''' <summary>
''' Draws all the dirty cells in the grid.
''' </summary>
Public Sub Draw()
Attribute Draw.VB_Description = "Draws the control."
Dim bDirty As Boolean

   If (m_bRedraw) Then
      ' Draw split row cells:
      If (m_lSplitRow > 0) Then
         bDirty = m_bDirty
         pDraw True
         m_bDirty = bDirty
      End If
      
      ' Draw main grid:
      pDraw False
   End If
   
End Sub

''' <summary>
''' Draws all the dirty cells in the grid.
''' </summary>
''' <param name="bSplitArea">Whether drawing the split area or not</param>
Private Sub pDraw(ByVal bSplitArea As Boolean)

''' TODO this function is hopelessly procedural, it needs to be split
''' up so each cell draws itself.  However, there are issues with
''' making sure the selection is drawn correctly in that case.
''' Not just that, but variables are being reused for other purposes
''' which is pretty rubbish. Bad Boy!  In your bed!

Dim iStartRow As Long
Dim iStartCol As Long
Dim iStartX As Long
Dim iStartY As Long
Dim lRowStartX As Long
Dim lThisRowStartX As Long
Dim lRowEndX As Long
Dim iEndRow As Long
Dim iEndCol As Long
Dim lStartX As Long
Dim lStartY As Long
Dim iEndX As Long
Dim iEndY As Long
Dim iY As Long
Dim iRow As Long
Dim iCol As Long
Dim iCellCol As Long
Dim iCRowTextCol As Long
Dim iFirstColInSelect As Long
Dim iLastColInSelect As Long
Dim tR As RECT
Dim tTR As RECT
Dim tBR As RECT
Dim tFR As RECT
Dim tLR As RECT
Dim tGR As RECT
Dim sText As String
Dim sCopy As String
Dim sHeaderTitle As String
Dim lBltOffset As Long
Dim lBltStart As Long
Dim lBltHeight As Long
Dim hBr As Long
Dim hBrGrid As Long
Dim hBrGridFill As Long
Dim hBrGutter As Long
Dim hFntOld As Long
Dim lLastPos As Long
Dim lOffsetX As Long
Dim lOffsetY As Long
Dim bSel As Boolean
Dim bDoIt As Boolean
Dim bCellSelected As Boolean
Dim bGroupLastDrawn As Boolean
Dim lStartColIndex As Long
Dim lItemData As Long
Dim bVisible As Boolean
Dim bGroupRow As Boolean
Dim bNoMoreRows As Boolean
Dim lHeight As Long
Dim bRecall As Boolean
Dim bDefaultStartCol As Boolean
Dim bHeaderTruncateDraw As Boolean
Dim bAlternateRow As Boolean
Dim lHighlightColor  As Long
Dim bLastSelCol() As Boolean
Dim impl As IGridCellOwnerDraw
Dim cCell As cGridCell
Dim bSkipDefault As Boolean

   ' No redrawing is done unless we are in run mode and
   ' the redraw flag is set:
   If m_bRedraw And m_bUserMode Then
      
      If Not (m_lPtrOwnerDraw = 0) Then
         Set impl = ObjectFromPtr(m_lPtrOwnerDraw)
      End If
      
      
      lStartY = m_lStartY
      
      ' Get the initial offset for the top row of the grid:
      If (m_cHeader.Visible) Then
         lOffsetX = m_cHeader.ColumnGroupCount * m_lDefaultRowHeight
         lOffsetY = m_cHeader.Height + m_cHeader.top
      End If
      If (bSplitArea) Then
         lStartY = 0
      Else
         lOffsetY = lOffsetY + plSplitSize()
      End If
      
      
      
      ' Get the size of the control and prepare to draw:
      GetClientRect UserControl.hwnd, tR
      tBR.right = m_lAvailWidth + 24 + Abs(m_tCols(iStartCol).lStartX -
       m_lStartX)
      tBR.bottom = m_lMaxRowHeight
      
      
      ' Ensure the scroll bars are set correctly:
      If pbScrollVisible() Then
         UserControl_Resize
         ' Resize redraws entire control;
         ' no need to do it again
         Exit Sub
      End If
      
      
      
      
      ' Find the start and end of drawing:
      GetStartEndCell _
         bSplitArea, _
         iStartRow, iStartCol, iStartX, iStartY, _
         iEndRow, iEndCol, iEndX, iEndY, bAlternateRow
      If (iEndCol >= iStartCol) Then
         ReDim bLastSelCol(iStartCol To iEndCol) As Boolean
      End If
         
      ' If in add rows on demand mode then we prepare for more rows:
      If (m_bAddRowsOnDemand And m_bInAddRowRequest) Then
         If (iEndY < m_lAvailheight) Then
            iY = iEndY
            Do
               iEndRow = iEndRow + 1
               iY = iY + m_lDefaultRowHeight
            Loop While iY < m_lAvailheight
         End If
      End If
               
      ' Evaluate the default group column start & end:
      lStartColIndex = m_lRowTextStartCol
      bDefaultStartCol = (lStartColIndex = 0)
      For iCol = 1 To m_iCols
         If iFirstColInSelect = 0 Then
            If (m_tCols(iCol).bIncludeInSelect And m_tCols(iCol).bVisible) Then
               iFirstColInSelect = iCol
               iLastColInSelect = iCol
               iCRowTextCol = iCol
               lRowStartX = m_tCols(iCol).lStartX - m_lStartX
               If (m_lRowTextStartCol = 0) Then
                  lStartColIndex = iCol
               End If
            End If
         ElseIf (m_tCols(iCol).bVisible And Not (m_tCols(iCol).bRowTextCol))
          Then
            iLastColInSelect = iCol
         End If
         If (m_tCols(iCol).lCellColIndex = lStartColIndex) And Not
          (bDefaultStartCol) Then
            lRowStartX = m_tCols(iCol).lStartX - m_lStartX
         ElseIf (m_tCols(iCol).lCellColIndex = m_iRowTextCol) Then
            iCRowTextCol = iCol
         ElseIf (m_tCols(iCol).bVisible And m_tCols(iCol).iGroupOrder = -1) Then
            If (m_tCols(iCol).lStartX + m_tCols(iCol).lWidth - m_lStartX) >
             lRowEndX Then
               lRowEndX = m_tCols(iCol).lStartX + m_tCols(iCol).lWidth -
                m_lStartX
            End If
         End If
      Next iCol
           
      
      'Set up for grid lines:
      If (m_bGridLines) Then
         If (m_bEnabled) Then
            hBrGrid = CreateSolidBrush(TranslateColor(m_oGridLineColor))
            hBrGridFill = CreateSolidBrush(TranslateColor(m_oGridFillLineColor))
         Else
            hBrGrid = GetSysColorBrush(vbGrayText And &H1F&)
            hBrGridFill = GetSysColorBrush(vbGrayText And &H1F&)
         End If
      End If
      
      ' Text colour for disabled grid:
      If Not (m_bEnabled) Then
         SetTextColor m_hDC, TranslateColor(vbGrayText)
      End If
      
      
      ' Draw the dirty cells:
      For iRow = iStartRow To iEndRow
         
         
         ' Request new row if in add rows on demand mode:
         If (iRow > m_iRows) Then
            If m_iCols > 0 Then
               If (m_bAddRowsOnDemand) Then
                  lHeight = m_lDefaultRowHeight
                  bVisible = True
                  RaiseEvent RequestRow(iRow, lItemData, bVisible, lHeight,
                   bNoMoreRows)
                  If bNoMoreRows Then
                     ' that's it
                     m_bInAddRowRequest = False
                     pbScrollVisible
                     bRecall = True
                     Exit For
                  Else
                     AddRow , lItemData, bVisible, lHeight
                     pbScrollVisible
                     RaiseEvent RequestRowData(iRow)
                  End If
               Else
                  ' This does not occur:
                  Debug.Assert iRow <= m_iRows
                  Exit For
               End If
            Else
               ' Can't do it until cols are set up
               ' 2004-01-14: Removed exit sub here, it caused GDI leak - erk..
            End If
         End If
         
         
         ' If the row should be drawn:
         If (m_tRows(iRow).bVisible) Then
      
            bAlternateRow = Not (bAlternateRow)
            
            tR.top = 0
            tR.bottom = tR.top + m_tRows(iRow).lHeight
               
            pFillBackground m_hDC, tBR, 0, m_tRows(iRow).lStartY - lStartY,
             bAlternateRow
            If Not (m_oGutterBackColor = CLR_NONE) Then
               ' *** not working yet, doh... ***
               'hBrGutter = CreateSolidBrush(TranslateColor(m_oGutterBackColor))
               'LSet tGR = tBR
               'tGR.right = tGR.left + (m_cHeader.ColumnGroupCount -
                m_tRows(iRow).lGroupIndentLevel + 1) * 16
               'FillRect m_HDC, tGR, hBrGutter
               'DeleteObject hBrGutter
            End If
            
            
            bDoIt = m_bDirty
            If Not (bDoIt) Then
               ' Any dirty cells on this row?
               If m_tRows(iRow).bGroupRow Then
                  If m_tCells(m_iRowTextCol,
                   m_tRows(iRow).lGridCellArrayRow).bDirtyFlag Then
                     bDoIt = True
                     m_tCells(m_iRowTextCol,
                      m_tRows(iRow).lGridCellArrayRow).bDirtyFlag = False
                  End If
               Else
                  For iCol = iStartCol To iEndCol
                     iCellCol = m_tCols(iCol).lCellColIndex
                     If m_tCells(iCellCol,
                      m_tRows(iRow).lGridCellArrayRow).bDirtyFlag Then
                        bDoIt = True
                        m_tCells(iCellCol,
                         m_tRows(iRow).lGridCellArrayRow).bDirtyFlag = False
                     End If
                  Next iCol
               End If
            End If
            
            If (bDoIt) Then
               
               ' Draw individual columns unless this row has the group row
                style, in
               ' which case we draw only the RowTextColumn.
               If Not (m_tRows(iRow).bGroupRow) Then
                  
                  For iCol = iStartCol To iEndCol
                     
                     If (m_tCols(iCol).bVisible And m_tCols(iCol).iGroupOrder =
                      -1) And Not (iCol = m_iRowTextCol) Then
                        
                        bSkipDefault = False
                        bCellSelected = False
                        iCellCol = m_tCols(iCol).lCellColIndex
                        tR.left = m_tCols(iCol).lStartX - m_lStartX +
                         m_tCells(iCellCol,
                         m_tRows(iRow).lGridCellArrayRow).lIndent
                        tR.right = tR.left + m_tCols(iCol).lWidth -
                         m_tCells(iCellCol,
                         m_tRows(iRow).lGridCellArrayRow).lIndent
                        OffsetRect tR, -1, 0
                        
                        ' 2004-01-15 Owner Draw pre
                        If Not (m_lPtrOwnerDraw = 0) Then
                           Set cCell = cell(iRow, iCellCol)
                           impl.Draw cCell, m_hDC, ecgBeforeAll, tR.left,
                            tR.top, tR.right, tR.bottom, bSkipDefault
                        End If
                        
                        If Not (bSkipDefault) Then
                        
                           ' 2004-01-10
                           ' Moving this here ensures that if there is an
                            indent with a background colour
                           ' then the background is correctly filled before the
                            selection starts
                           If (m_bEnabled) Then
                              If Not (m_tCells(iCellCol,
                               m_tRows(iRow).lGridCellArrayRow).oBackColor =
                               CLR_NONE) Then
                                 hBr =
                                  CreateSolidBrush(TranslateColor(m_tCells(iCell
                                 Col,
                                  m_tRows(iRow).lGridCellArrayRow).oBackColor))
                                 LSet tTR = tR
                                 If Not (m_tCells(iCellCol,
                                  m_tRows(iRow).lGridCellArrayRow).lIndent = 0)
                                  Then
                                    tTR.left = tTR.left - m_tCells(iCellCol,
                                     m_tRows(iRow).lGridCellArrayRow).lIndent
                                 End If
                                 FillRect m_hDC, tTR, hBr
                                 DeleteObject hBr
                              End If
                              If Not (m_tCells(iCellCol,
                               m_tRows(iRow).lGridCellArrayRow).oForeColor =
                               CLR_NONE) Then
                                 SetTextColor m_hDC,
                                  TranslateColor(m_tCells(iCellCol,
                                  m_tRows(iRow).lGridCellArrayRow).oForeColor)
                                 bSel = True
                              Else
                                 If (bSel) Then
                                    SetTextColor m_hDC,
                                     TranslateColor(UserControl.ForeColor)
                                    bSel = False
                                 End If
                              End If
                           End If
                           
                           ' Draw selection for this cell if that is appropriate
                           If (m_tCells(iCellCol,
                            m_tRows(iRow).lGridCellArrayRow).bSelected) And
                            (m_bEnabled) Then
                              If (m_tCols(iCol).bIncludeInSelect) Or (iCol >=
                               iFirstColInSelect) Then
                              
                                 lHighlightColor = m_oHighlightBackColor
                                 If (m_bInFocus) Or (m_bInEdit) Then
                                    bCellSelected = True
                                 Else
                                    lHighlightColor =
                                     m_oNoFocusHighlightBackColor
                                 End If
                                 If (m_bAlphaBlendSelection And m_bTrueColor)
                                  Then
                                    If (m_tCells(iCellCol,
                                     m_tRows(iRow).lGridCellArrayRow).oBackColor
                                     = CLR_NONE) Then
                                       hBr =
                                        CreateSolidBrush(BlendColor(lHighlightCo
                                       lor, UserControl.BackColor, 92))
                                    Else
                                       hBr =
                                        CreateSolidBrush(BlendColor(lHighlightCo
                                       lor, m_tCells(iCellCol,
                                        m_tRows(iRow).lGridCellArrayRow).oBackCo
                                       lor))
                                    End If
                                 Else
                                    hBr =
                                     CreateSolidBrush(TranslateColor(lHighlightC
                                    olor))
                                 End If
                                 
                                 
                                 LSet tTR = tR
                                 If (m_bGridLines) Then
                                    InflateRect tTR, Not
                                     (m_bNoVerticalGridLines), Not
                                     (m_bNoHorizontalGridLines)
                                 End If
                                 If (m_bRowMode) Then
                                    If (iCol > iFirstColInSelect) Then
                                       tTR.left = tTR.left - m_tCells(iCellCol,
                                        m_tRows(iRow).lGridCellArrayRow).lIndent
                                    End If
                                 End If
                                 LSet tFR = tTR
                                 
                                 If Not (m_bRowMode) Then
                                    If m_bGridLines And (m_bEnabled) Then
                                       If (iCellCol = m_iSelCol) And (iRow =
                                        m_iSelRow) Then
                                          If m_bDrawFocusRectangle Then
                                             LSet tFR = tTR
                                             InflateRect tFR, Not
                                         (m_bNoVerticalGridLines), Not
                                         (m_bNoHorizontalGridLines)
                                             If Not (m_bNoVerticalGridLines)
                                         Then
                                                tFR.left = tFR.left - 1
                                             Else
                                                tFR.top = tFR.top - 1
                                                tFR.bottom = tFR.bottom - 1
                                             End If
                                             If Not (m_bNoHorizontalGridLines)
                                         Then
                                                tFR.bottom = tFR.bottom + 1
                                             End If
                                          End If
                                       End If
                                    End If
                                 Else
                                    If Not (m_bGridLines) Then
                                       tFR.top = tFR.top - 1
                                    Else
                                       If Not (m_bNoVerticalGridLines) Then
                                          tFR.top = tFR.top + 1
                                          If (iCol > iFirstColInSelect) Then
                                             tFR.left = tFR.left - 1
                                          Else
                                             tFR.left = tFR.left + 1
                                          End If
                                          If (iCol >= iLastColInSelect) Then
                                             tFR.right = tFR.right - 2
                                          Else
                                             tFR.right = tFR.right + 1
                                          End If
                                       End If
                                       If Not (m_bNoHorizontalGridLines) Then
                                          If (m_bNoVerticalGridLines) Then
                                             tFR.bottom = tFR.bottom - 2
                                          Else
                                             If (iRow = m_iSelRow) Then
                                                tFR.bottom = tFR.bottom - 1
                                             Else
                                                tFR.bottom = tFR.bottom + 1
                                             End If
                                          End If
                                       End If
                                    End If
                                 End If
                                 If (bLastSelCol(iCol)) Then
                                    tFR.top = tFR.top - 4
                                 End If
                                 FillRect m_hDC, tFR, hBr
                                 bLastSelCol(iCol) = True
                                 DeleteObject hBr
                              Else
                                 bLastSelCol(iCol) = False
                              End If
                              
                              bSel = True
                           Else
                              bLastSelCol(iCol) = False
                           End If
                           
                           
                           
                           If (m_bGridLines) Then
                              LSet tTR = tR
                              tTR.left = tTR.left - m_tCells(iCellCol,
                               m_tRows(iRow).lGridCellArrayRow).lIndent
                              tTR.right = tR.right + 1
                              tTR.bottom = tR.bottom + 1
                              If Not (m_bNoHorizontalGridLines Or
                               m_bNoVerticalGridLines) Then
                                 LSet tLR = tTR
                                 OffsetRect tLR, -1, 0
                                 If (iRow < m_iRows) Then
                                    If (m_tRows(iRow + 1).bGroupRow) Then
                                       tLR.bottom = tLR.bottom - 1
                                    End If
                                 End If
                                 FrameRect m_hDC, tLR, hBrGrid
                              Else
                                 If (m_bNoHorizontalGridLines) And Not
                                  (m_bNoVerticalGridLines) Then
                                    LSet tLR = tTR
                                    tLR.left = tLR.right - 2
                                    tLR.right = tLR.right - 1
                                    tLR.top = tLR.top - 1
                                    tLR.bottom = tLR.bottom + 1
                                    FillRect m_hDC, tLR, hBrGrid
                                 ElseIf (m_bNoVerticalGridLines) And Not
                                  (m_bNoHorizontalGridLines) Then
                                    LSet tLR = tTR
                                    tLR.top = tLR.bottom - 2
                                    tLR.bottom = tLR.top + 1
                                    FillRect m_hDC, tLR, hBrGrid
                                 End If
                              End If
                              LSet tTR = tR
                              InflateRect tTR, -1 + Not
                               (m_bNoVerticalGridLines), -1 + Not
                               (m_bNoVerticalGridLines)
                           Else
                              LSet tTR = tR
                              InflateRect tTR, -1, -1
                           End If
                           
                           
                           
                           If Not (m_bRowMode) Then
                              If (m_bEnabled) Then
                                 If (iCellCol = m_iSelCol) And (iRow =
                                  m_iSelRow) Then
                                    pDrawFocusRectangle m_hDC, tFR, iCellCol,
                                     iRow, False
                                 ElseIf (m_tCols(iCol).lCellColIndex =
                                  m_lHotTrackCol)