VERSION 5.00
Object = "{5E9E78A0-531B-11CF-91F6-C2863C385E30}#1.0#0"; "MSFLXGRD.OCX"
Begin VB.UserControl ArmGrid 
   BackColor       =   &H00E0E0E0&
   ClientHeight    =   2940
   ClientLeft      =   0
   ClientTop       =   0
   ClientWidth     =   3540
   KeyPreview      =   -1  'True
   ScaleHeight     =   196
   ScaleMode       =   3  'Pixel
   ScaleWidth      =   236
   ToolboxBitmap   =   "A_Grid.ctx":0000
   Begin VB.PictureBox pct_Icon 
      Appearance      =   0  'Flat
      AutoSize        =   -1  'True
      BackColor       =   &H80000005&
      BorderStyle     =   0  'None
      ForeColor       =   &H80000008&
      Height          =   240
      Index           =   1
      Left            =   405
      Picture         =   "A_Grid.ctx":0312
      ScaleHeight     =   240
      ScaleWidth      =   240
      TabIndex        =   16
      TabStop         =   0   'False
      Top             =   1080
      Visible         =   0   'False
      Width           =   240
   End
   Begin VB.PictureBox reload_grid 
      Appearance      =   0  'Flat
      BackColor       =   &H80000000&
      BorderStyle     =   0  'None
      ForeColor       =   &H80000008&
      Height          =   540
      Left            =   1920
      Picture         =   "A_Grid.ctx":0414
      ScaleHeight     =   405
      ScaleMode       =   0  'User
      ScaleWidth      =   570
      TabIndex        =   15
      TabStop         =   0   'False
      Top             =   600
      Visible         =   0   'False
      Width           =   570
   End
   Begin VB.Frame frm_Like 
      BackColor       =   &H00E0E0E0&
      Height          =   495
      Left            =   0
      TabIndex        =   5
      Top             =   0
      Visible         =   0   'False
      Width           =   3375
      Begin VB.OptionButton opt_Like 
         BackColor       =   &H00E0E0E0&
         Height          =   255
         Index           =   0
         Left            =   120
         TabIndex        =   6
         TabStop         =   0   'False
         Top             =   120
         Width           =   255
      End
      Begin VB.OptionButton opt_Like 
         BackColor       =   &H00E0E0E0&
         Height          =   255
         Index           =   1
         Left            =   1200
         TabIndex        =   7
         TabStop         =   0   'False
         Top             =   120
         Width           =   255
      End
      Begin VB.OptionButton opt_Like 
         BackColor       =   &H00E0E0E0&
         Height          =   255
         Index           =   2
         Left            =   2280
         TabIndex        =   8
         TabStop         =   0   'False
         Top             =   120
         Width           =   255
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxx"
         ForeColor       =   &H000000FF&
         Height          =   195
         Index           =   3
         Left            =   360
         TabIndex        =   14
         Top             =   120
         Width           =   225
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxxxxx"
         Height          =   195
         Index           =   2
         Left            =   600
         TabIndex        =   11
         Top             =   120
         Width           =   450
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxx"
         ForeColor       =   &H000000FF&
         Height          =   195
         Index           =   5
         Left            =   3000
         TabIndex        =   13
         Top             =   120
         Width           =   225
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxxxxx"
         Height          =   195
         Index           =   4
         Left            =   2520
         TabIndex        =   12
         Top             =   120
         Width           =   450
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxx"
         ForeColor       =   &H000000FF&
         Height          =   195
         Index           =   1
         Left            =   1680
         TabIndex        =   10
         Top             =   120
         Width           =   225
      End
      Begin VB.Label lbl_Like 
         AutoSize        =   -1  'True
         BackColor       =   &H00E0E0E0&
         Caption         =   "xxx      xxx"
         Height          =   195
         Index           =   0
         Left            =   1440
         TabIndex        =   9
         Top             =   120
         Width           =   720
      End
   End
   Begin VB.TextBox txt_Filter 
      Height          =   285
      Index           =   0
      Left            =   120
      TabIndex        =   4
      TabStop         =   0   'False
      Top             =   720
      Visible         =   0   'False
      Width           =   975
   End
   Begin VB.PictureBox pct_Icon 
      Appearance      =   0  'Flat
      AutoSize        =   -1  'True
      BackColor       =   &H80000005&
      BorderStyle     =   0  'None
      ForeColor       =   &H80000008&
      Height          =   240
      Index           =   0
      Left            =   120
      Picture         =   "A_Grid.ctx":071E
      ScaleHeight     =   240
      ScaleWidth      =   240
      TabIndex        =   3
      TabStop         =   0   'False
      Top             =   1080
      Visible         =   0   'False
      Width           =   240
   End
   Begin MSFlexGridLib.MSFlexGrid Grid 
      Height          =   1575
      Left            =   120
      TabIndex        =   2
      TabStop         =   0   'False
      Top             =   1320
      Width           =   3255
      _ExtentX        =   5741
      _ExtentY        =   2778
      _Version        =   393216
      Cols            =   3
      BackColorFixed  =   12632256
      ScrollTrack     =   -1  'True
   End
   Begin VB.Label lbl_Title 
      BackStyle       =   0  'Transparent
      Caption         =   "Title"
      BeginProperty Font 
         Name            =   "Tahoma"
         Size            =   9.75
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   240
      Left            =   855
      TabIndex        =   1
      Top             =   1110
      Width           =   1500
   End
   Begin VB.Label lbl_Curs 
      Alignment       =   1  'Right Justify
      AutoSize        =   -1  'True
      BackStyle       =   0  'Transparent
      Caption         =   "0/0.."
      BeginProperty Font 
         Name            =   "Tahoma"
         Size            =   9.75
         Charset         =   0
         Weight          =   700
         Underline       =   0   'False
         Italic          =   0   'False
         Strikethrough   =   0   'False
      EndProperty
      Height          =   240
      Left            =   2880
      TabIndex        =   0
      Top             =   1080
      Width           =   480
   End
End
Attribute VB_Name = "ArmGrid"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit

'version 1.0
'Version 1.1 - fixed col property to represent real logical column indexes
'Version 1.2 - integrated printing
'version 1.3 - fixed "Subscript out of range" error when print, better error check on ShowPrintDlg

Private Const FW_NORMAL = 400
Private Const DEFAULT_CHARSET = 1
Private Const OUT_DEFAULT_PRECIS = 0
Private Const CLIP_DEFAULT_PRECIS = 0
Private Const DEFAULT_QUALITY = 0
Private Const DEFAULT_PITCH = 0
Private Const FF_ROMAN = 16
Private Const CF_PRINTERFONTS = &H2
Private Const CF_SCREENFONTS = &H1
Private Const CF_BOTH = (CF_SCREENFONTS Or CF_PRINTERFONTS)
Private Const CF_EFFECTS = &H100&
Private Const CF_FORCEFONTEXIST = &H10000
Private Const CF_INITTOLOGFONTSTRUCT = &H40&
Private Const CF_LIMITSIZE = &H2000&
Private Const REGULAR_FONTTYPE = &H400
Private Const LF_FACESIZE = 32
Private Const CCHDEVICENAME = 32
Private Const CCHFORMNAME = 32
Private Const GMEM_MOVEABLE = &H2
Private Const GMEM_ZEROINIT = &H40
Private Const DM_DUPLEX = &H1000&
Private Const DM_ORIENTATION = &H1&
Private Const PD_PRINTSETUP = &H40
Private Const PD_DISABLEPRINTTOFILE = &H80000
Private Const PD_SELECTION As Long = &H1
Private Const PD_NOSELECTION As Long = &H4


      ' Constants for nIndex argument of GetDeviceCaps
Private Const HORZRES = 8
Private Const VERTRES = 10
Private Const LOGPIXELSX = 88
Private Const LOGPIXELSY = 90
Private Const PHYSICALWIDTH = 110
Private Const PHYSICALHEIGHT = 111
Private Const PHYSICALOFFSETX = 112
Private Const PHYSICALOFFSETY = 113

Private Const BORDER_NONE = 0
Private Const BORDER_LEFT = 1
Private Const BORDER_TOP = 2
Private Const BORDER_RIGHT = 4
Private Const BORDER_BOTTOM = 8
Private Const BORDER_OUTLINE = BORDER_LEFT Or BORDER_TOP Or BORDER_RIGHT Or BORDER_BOTTOM

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type RECT
    Left As Long
    Top As Long
    right As Long
    bottom As Long
End Type

Private Type PAGESETUPDLG
    lStructSize As Long
    hwndOwner As Long
    hDevMode As Long
    hDevNames As Long
    flags As Long
    ptPaperSize As POINTAPI
    rtMinMargin As RECT
    rtMargin As RECT
    hInstance As Long
    lCustData As Long
    lpfnPageSetupHook As Long
    lpfnPagePaintHook As Long
    lpPageSetupTemplateName As String
    hPageSetupTemplate As Long
End Type

Private Type PRINTDLG_TYPE
    lStructSize As Long
    hwndOwner As Long
    hDevMode As Long
    hDevNames As Long
    hdc As Long
    flags As Long
    nFromPage As Integer
    nToPage As Integer
    nMinPage As Integer
    nMaxPage As Integer
    nCopies As Integer
    hInstance As Long
    lCustData As Long
    lpfnPrintHook As Long
    lpfnSetupHook As Long
    lpPrintTemplateName As String
    lpSetupTemplateName As String
    hPrintTemplate As Long
    hSetupTemplate As Long
End Type

Private Type DEVNAMES_TYPE
    wDriverOffset As Integer
    wDeviceOffset As Integer
    wOutputOffset As Integer
    wDefault As Integer
    extra As String * 256
End Type

Private Type DEVMODE_TYPE
    dmDeviceName As String * CCHDEVICENAME
    dmSpecVersion As Integer
    dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * CCHFORMNAME
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
End Type

Private Type PRINTER_INFO
    TotalPrtAreaVert As Long
    TotalHeight  As Long
    MarginTop As Long
    MarginBottom As Long
    TotalPrtAreaHorz As Long
    TotalWidth As Long
    MarginLeft As Long
    MarginRight As Long
    CellMarginX As Long
    CellMarginY As Long
    CellHeight As Long
End Type

Private Declare Function PrintDialog Lib "comdlg32.dll" Alias "PrintDlgA" (pPrintdlg As PRINTDLG_TYPE) As Long
Private Declare Function PAGESETUPDLG Lib "comdlg32.dll" Alias "PageSetupDlgA" (pPagesetupdlg As PAGESETUPDLG) As Long
Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long)
Private Declare Function GlobalLock Lib "Kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalUnlock Lib "Kernel32" (ByVal hMem As Long) As Long
Private Declare Function GlobalAlloc Lib "Kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "Kernel32" (ByVal hMem As Long) As Long
Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Long) As Long
Private Declare Sub Sleep Lib "Kernel32" (ByVal dwMiliseconds As Long)
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
'Private Declare Function ExtTextOut Lib "gdi32" Alias "ExtTextOutA" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal wOptions As Long, ByVal lpRect As Any, ByVal lpString As String, ByVal nCount As Long, lpDx As Long) As Long
Private Declare Function GetTextExtentPoint32 Lib "gdi32" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As POINTAPI) As Long
'Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long
'Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
'Private Declare Function SetMapMode Lib "gdi32" (ByVal hdc As Long, ByVal nMapMode As Long) As Long
Private Declare Function DrawTextA Lib "user32.dll" (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.dll" ( _
     ByVal hdc As Long, _
     lpStr As Any, _
     ByVal nCount As Long, _
     ByRef lpRect As RECT, _
     ByVal wFormat As Long) As Long

Private Const DT_BOTTOM = &H8
Private Const DT_CALCRECT = &H400
Private Const DT_CENTER = &H1
Private Const DT_CHARSTREAM = 4 ' Character-stream, PLP
Private Const DT_DISPFILE = 6 ' Display-file
Private Const DT_EXPANDTABS = &H40
Private Const DT_EXTERNALLEADING = &H200
Private Const DT_INTERNAL = &H1000
Private Const DT_LEFT = &H0
Private Const DT_METAFILE = 5 ' Metafile, VDM
Private Const DT_NOCLIP = &H100
Private Const DT_NOPREFIX = &H800
Private Const DT_PLOTTER = 0 ' Vector plotter
Private Const DT_RASCAMERA = 3 ' Raster camera
Private Const DT_RASDISPLAY = 1 ' Raster display
Private Const DT_RASPRINTER = 2 ' Raster printer
Private Const DT_RIGHT = &H2
Private Const DT_SINGLELINE = &H20
Private Const DT_TABSTOP = &H80
Private Const DT_TOP = &H0
Private Const DT_VCENTER = &H4
Private Const DT_WORDBREAK = &H10


Private Const GRID_MARGIN = 5             'margin of grid
Private Const MAX_ROWCOUNT_LIMIT = 10000  'limit of grid (its about
Private Const PROP_TWIP_PIX = 14.95       'Conversion
Private Const SEP1 As String = "®"
Private Const SEP2 As String = "©"
Private Const SEP As String = SEP1 + SEP2
Private Const ICON_EXCEL = 0
Private Const ICON_PRINT = 1
'Events available for armgrid
Public Event Click()
Public Event DblClick()
Public Event KeyDown(KeyCode As Integer, Shift As Integer)
Public Event KeyPress(KeyAscii As Integer)
Public Event ItemSelected()
Public Event SelChange()
Public Event RowColChange()
Public Event Scroll()
Public Event CustomFormatValue(ByRef av_Value As Variant, ao_Column As ArmColumn, ByRef as_Result As String)
Public Event RowLoaded(ByVal al_row As Long)
Public Event AfterExcelExport(ByVal ao_ExcelApp As Object, ByVal ao_ExcelWorkbook As Object, ByVal ao_ExcelSheet As Object)
Public Event MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single, ByVal al_row As Long, ByVal al_Col As Long)
' Raised after the Grid fetch the cursor
Public Event DataLoaded(ByVal ab_FullDataLoaded As Boolean)
Public Event OLEDragDrop(Data As MSFlexGridLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
'armsyscom instance
Private mo_Db As ARMSYSCOMLib.ArmDb
'grid is connected through call of OpenConnection or through setting ArmDb property
Private mo_Trace As Object
'flag if component connect internally ArmDB to SQL database
Private mb_InternalConnection As Boolean

'array of requests for retrieving records from SQL server
Private mv_Requests As Variant
'request which is active opened in array or which will be opened when none is active
Private mi_ActiveRequestIndex As Integer
'at a time only one cusor is active
Private ml_ActiveCursor As Long

'collection of all columns in grid (plus system columns for date)
Private mo_Columns As Collection
'array of indexes into columns collection, only non system columns set by framework
Private mv_ColumnIndexes As Variant
'number of key columns in columns collection
Private ml_KeyCount As Long
'maximal number of rows which can be loaded into grid
Private ml_MaxRows As Long
'flag which detect if all records are loaded into grid or not
Private mb_FullLoaded As Boolean
'flag if we have to load all records in one time after call of Execute method
Private mb_FetchAll As Boolean
'flag if grid is in data bound or unbound mode
Private mb_UnBound As Boolean
'number of records which will be fetched in one block when we don't load all records in one time
Private ml_BlockRowCount As Long
'block row count was set by calling method load if this flag is true
Private mb_BlockRowCountSetByFrameWork As Boolean
'number of records visible in grid
Private ml_VisibleRowCount As Long
'there is internal change of current row or col, don't select whole row
Private mb_InternalRowColChange As Boolean
'user can select more than one line
Private mb_MultiSelect As Boolean
Private mb_FreeSelect As Boolean
'allow export titles to excel
Private mb_ExportTitles As Boolean
'allow to export all grid columns or only visible columns to Excel
Private mb_ExportOnlyVisibleColumns As Boolean
'current row to export to excel
Private ml_CurrentRow As Long
'column will be create automatically or not
Private mb_AutoColumns As Boolean
'rows can be sorted when user click title
Private mb_AllowSort As Boolean
'rows can be sorted by multiple columns when user press CTRL + click title
Private mb_AllowMultiSort As Boolean
'default auto column width
Private ml_AutoColumnWidth As Long
'excel sheet object
Private mo_Sheet As Object
'excel application object
Private mo_ExcelApp As Object
'local codepage ID for excel export
Private ml_LocalID As Long
Private ml_CodePage As Long
'font template for print output
Private mo_PrintFont As StdFont
' number of fixed columns in grid
Private ml_FixedCols As Long
'label for subtotal row
Private ms_TextSubTotal As String
'label for grandtotal row
Private ms_TextGrandTotal As String
Private mb_ShowGrandTotal As Boolean
Private mb_RemoveRepeatValue As Boolean

Private ms_MasterDataCol As String
Private ms_MasterIconCol As String
Private ms_DetailCountCol As String
Private ms_MasterIconExpanded As String
Private ms_MasterIconCollapsed As String
Private ms_MasterIconNoDetail As String
Private ml_MasterRowColor As Long
Private ml_DetailRowColor As Long
Private ml_TotalRowColor As Long
Private ms_MasterData As String
Private ms_DetailData As String
Private ms_MasterDetailRequest As String
Private mb_RequestLoadDetails As Boolean

Private mb_FiltersEnabled As Boolean
Private mb_AllowExcelExport As Boolean
Private mb_AllowPrint As Boolean
'how to print column width on printer page
Private mt_ColumnWidthPrintMode As ColumnWidthPrintMode
'connect parameters
Private ms_Server As String
Private ms_Db As String
Private ms_User As String
Private ms_Pwd As String
Private ms_App As String
Private mv_TagVariant As Variant

'array of messages translated send from framework or from SQL
Private mv_Messages As Variant

Public Enum ColumnWidthPrintMode
    cwAutoFitToPage
    cwAutoFitToColumn
    cwManualSettings
End Enum

'constants for grid system messages
Public Enum MsgType
  mtGRID_LIMIT_REACHED = 0
  mtLOADALL_SORT = 1
  mtLOADALL_SEARCH = 2
  mtCONNECT_FAILED = 3
  mtNOPRINTER_INSTALLED = 4
  mtEXCELL_EXPORT_REQUEST = 5
End Enum

'constant enumeration for parameter type in LoadConstants method
Public Enum ParamType
  ptSQL
  ptStatic
End Enum

'constant enumeration for constant type in LoadConstants method
Public Enum ConstType
  ctTopGrid
  ctColumns
  ctMessages
  ctColumnPrintWidth
End Enum

Public Enum RowMoveDirection
  rmUp
  rmDown
End Enum
'--------------------------------------------------------------------------------

'main ArmSysCom object, if you pass this property, object should be connected to db
'when calling Execute or Load
Public Property Set ArmDb(ByVal lo_Db As ARMSYSCOMLib.ArmDb)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ArmDB_Set")
#End If
  
  'do not accept external instance of ArmDb when already internal instance exists
  If Not mb_InternalConnection Then
    Set mo_Db = lo_Db
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ArmDB_Set")
#End If
End Property

Public Property Get ArmDb() As ARMSYSCOMLib.ArmDb
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ArmDB_Get")
#End If
  
  Set ArmDb = mo_Db

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ArmDB_Get")
#End If
End Property

Public Property Set ArmTrace(ByVal lo_Trace As Object)
  
  Set mo_Trace = lo_Trace
End Property

Public Property Get ArmTrace() As Object
  
  Set ArmTrace = mo_Trace
End Property

Property Let LocalID(al_Value As Long)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:LocalID_Let")
#End If
  
  ml_LocalID = al_Value

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:LocalID_Let")
#End If
End Property

Property Get LocalID() As Long
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:LocalID_Get")
#End If
  
  LocalID = ml_LocalID

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:LocalID_Get")
#End If
End Property

Property Let Codepage(al_Value As Long)
  
  ml_CodePage = al_Value
End Property

Property Get Codepage() As Long
  
  Codepage = ml_CodePage
End Property

Public Property Let Visible(ByVal aVisible As Boolean)
On Error GoTo errhandler
    
  UserControl.Extender.Visible = aVisible
  Exit Property
errhandler:
  Call ErrorHandler("Visible.Let")
End Property

Public Property Get Visible() As Boolean
On Error GoTo errhandler
    
  Visible = UserControl.Extender.Visible
  Exit Property
errhandler:
  Call ErrorHandler("Visible.Get")
End Property

Public Property Let Top(ByVal aTop As Single)
On Error GoTo errhandler
    
  UserControl.Extender.Top = aTop
  Exit Property
errhandler:
  Call ErrorHandler("Top.Let")
End Property

Public Property Get Top() As Single
On Error GoTo errhandler
    
  Top = UserControl.Extender.Top
  Exit Property
errhandler:
  Call ErrorHandler("Top.Get")
End Property

Public Property Let Height(ByVal aHeight As Single)
On Error GoTo errhandler
    
  UserControl.Extender.Height = aHeight
  Exit Property
errhandler:
  Call ErrorHandler("Height.Let")
End Property

Public Property Get Height() As Single
On Error GoTo errhandler
    
  Height = UserControl.Extender.Height
  Exit Property
errhandler:
  Call ErrorHandler("Height.Get")
End Property

Public Property Let Left(ByVal aLeft As Single)
On Error GoTo errhandler
    
  UserControl.Extender.Left = aLeft
  Exit Property
errhandler:
  Call ErrorHandler("Left.Let")
End Property

Public Property Get Left() As Single
On Error GoTo errhandler
    
  Left = UserControl.Extender.Left
  Exit Property
errhandler:
  Call ErrorHandler("Left.Get")
End Property

Public Property Let Width(ByVal aWidth As Single)
On Error GoTo errhandler
    
  UserControl.Extender.Width = aWidth
  Exit Property
errhandler:
  Call ErrorHandler(".Let")
End Property

Public Property Get Width() As Single
On Error GoTo errhandler
    
  Width = UserControl.Extender.Width
  Exit Property
errhandler:
  Call ErrorHandler("Width.Get")
End Property

Public Sub Move(ByVal aLeft As Single, ByVal aTop As Single, ByVal aWidth As Single, ByVal aHeight As Single)
On Error GoTo errhandler
  
  Call UserControl.Extender.Move(aLeft, aTop, aWidth, aHeight)
  Exit Sub
errhandler:
  Call ErrorHandler("Move")
End Sub

Public Sub SetFocus()

    Grid.SetFocus
End Sub
'physical limit of row which can be loaded in grid. Recomended max 10000 because of
'grid limitation
Property Let MaxRows(aValue As Long)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MaxRows_Let")
#End If
  
  If aValue >= 0 Then
      ml_MaxRows = aValue
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MaxRows_Let")
#End If
End Property

Property Get MaxRows() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MaxRows_Get")
#End If
  
  MaxRows = ml_MaxRows

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MaxRows_Get")
#End If
End Property

Property Let BlockRowCount(aValue As Long)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:BlockRowCount_Let")
#End If
  
  If aValue > 1 Then
      ml_BlockRowCount = aValue
      mb_BlockRowCountSetByFrameWork = True
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:BlockRowCount_Let")
#End If
End Property

Property Get BlockRowCount() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:BlockRowCount_Get")
#End If
  
  BlockRowCount = ml_BlockRowCount

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:BlockRowCount_Get")
#End If
End Property

'default column width for automatically created columns
Property Let AutoColumnWidth(aValue As Long)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AutoColumnWidth_Let")
#End If
  
  If (aValue > 0) Then
      ml_AutoColumnWidth = aValue
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AutoColumnWidth_Let")
#End If
End Property

Property Get AutoColumnWidth() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AutoColumnWidth_Get")
#End If
  
  AutoColumnWidth = ml_AutoColumnWidth

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AutoColumnWidth_Get")
#End If
End Property

'set title which will be displayed on top of the grid
Property Let Title(ATitle As String)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Title_Let")
#End If
  
  lbl_Title.Caption = ATitle
  Call MoveTitle

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Title_Let")
#End If
End Property

Property Get Title() As String
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Title_Get")
#End If

  Title = lbl_Title.Caption

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Title_Get")
#End If
End Property

Property Let ExportTitles(lb_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportTitles_Let")
#End If
  
  mb_ExportTitles = lb_Value

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportTitles_Let")
#End If
End Property

Property Get ExportTitles() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportTitles_Get")
#End If

  ExportTitles = mb_ExportTitles

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportTitles_Get")
#End If
End Property

Property Let ExportOnlyVisibleColumns(ByVal lb_Value As Boolean)
  
  mb_ExportOnlyVisibleColumns = lb_Value

End Property

Property Get ExportOnlyVisibleColumns() As Boolean

  ExportOnlyVisibleColumns = mb_ExportOnlyVisibleColumns
End Property

Property Let AllowSort(lb_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AllowSort_Let")
#End If
  
  mb_AllowSort = lb_Value

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AllowSort_Let")
#End If
End Property

Property Get AllowSort() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AllowSort_Get")
#End If

  AllowSort = mb_AllowSort

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AllowSort_Get")
#End If
End Property

Property Let AllowMultiSort(lb_Value As Boolean)
  
  mb_AllowMultiSort = lb_Value
End Property

Property Get AllowMultiSort() As Boolean

  AllowMultiSort = mb_AllowMultiSort
End Property

Property Let AutoColumns(lb_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AutoColumns_Let")
#End If
  
  mb_AutoColumns = lb_Value

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AutoColumns_Let")
#End If
End Property

Property Get AutoColumns() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AutoColumns_Get")
#End If

  AutoColumns = mb_AutoColumns

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AutoColumns_Get")
#End If
End Property

Property Let AllowExcelExport(lb_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AllowExcelExport_Let")
#End If

  pct_Icon(ICON_EXCEL).Visible = lb_Value
  mb_AllowExcelExport = lb_Value
  Call MoveTitle

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AllowExcelExport_Let")
#End If
End Property

Property Get AllowExcelExport() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Allow_ExcelExport_Get")
#End If

  AllowExcelExport = mb_AllowExcelExport

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Allow_ExcelExport_Get")
#End If
End Property

Property Let AllowPrint(lb_Value As Boolean)
  
  pct_Icon(ICON_PRINT).Visible = lb_Value
  mb_AllowPrint = lb_Value
  Call MoveTitle
End Property

Property Get AllowPrint() As Boolean

  AllowPrint = mb_AllowPrint
End Property

Property Get Columns(av_Column As Variant) As ArmColumn
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Allow_ExcelExport_Get")
#End If

  Set Columns = GetColumn(av_Column)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Allow_ExcelExport_Get")
#End If
End Property

Property Let FixedCols(ByVal av_Column As Variant)
  
  Dim lo_Column As ArmColumn
  
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) Then
    ml_FixedCols = lo_Column.ColumnIndex
  End If
End Property

Property Get FixedCols() As Variant
  FixedCols = ml_FixedCols
End Property

Property Let Redraw(lb_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Redraw_Let")
#End If

  Call SetRedraw(lb_Value)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Redraw_Let")
#End If
End Property

Property Get Redraw() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Redraw_Get")
#End If

  Redraw = Grid.Redraw

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Redraw_Get")
#End If
End Property

'set or get current grid row (entire row is selected)
Property Let Row(al_row As Long)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Row_Let")
#End If
    
  If IsValidDataRow(al_row) Then
    Grid.Row = al_row + 1
    Call SelectWholeRow
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Row_Let")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Row_Let", "al_Row=" & al_row)
#End If
End Property

Property Get Row() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Row_Get")
#End If

  Row = Grid.Row - 1

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Row_Get")
#End If
End Property

Property Let Col(al_Col As Long)

  If IsArray(mv_ColumnIndexes) Then
    If (al_Col >= 0) And (al_Col <= UBound(mv_ColumnIndexes)) Then Grid.Col = mv_ColumnIndexes(al_Col) - 1
  End If

End Property

Property Get Col() As Long
Dim ll_Index As Long

  Col = -1
  If IsArray(mv_ColumnIndexes) Then
    For ll_Index = 0 To UBound(mv_ColumnIndexes)
        If Grid.Col + 1 = mv_ColumnIndexes(ll_Index) Then
            Col = ll_Index
            Exit Property
        End If
    Next
  End If

End Property

'get number of columns in grid as size of mv_ColumnIndexes array - we don't want
'to show user system (date) columns which are hidden in grid
Property Get Cols() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Cols_Get")
#End If

  If IsArray(mv_ColumnIndexes) Then
    Cols = UBound(mv_ColumnIndexes) + 1
  Else
    Cols = 0
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Cols_Get")
#End If
End Property

Property Let Rows(al_Value As Long)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Rows_Let")
#End If
    
  If al_Value > ml_MaxRows Then
    al_Value = ml_MaxRows
    Call ShowMessage(mtGRID_LIMIT_REACHED, "#Limit of maximum row count in grid reached.")
  End If
    
    If Cols > 0 Then
      Call InitGrid(al_Value, Grid.Cols)
      Call DisplayCursorInfo
    End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Rows_Let")
#End If
End Property

'get number of rows in grid (zero based index)
Property Get Rows() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Rows_Get")
#End If

     Rows = Grid.Rows - 1

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Rows_Get")
#End If
End Property

Property Let RowData(al_Index As Long, al_Value As Long)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:RowData_Let")
#End If
    
    If (al_Index >= 0) And (al_Index < Grid.Rows - 1) Then
      Grid.RowData(al_Index + 1) = al_Value
    End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:RowData_Let")
#End If
End Property

'get number of rows in grid (zero based index)
Property Get RowData(al_Index As Long) As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:RowData_Get")
#End If

    RowData = 0
    If (al_Index >= 0) And (al_Index < Grid.Rows - 1) Then
      RowData = Grid.RowData(al_Index + 1)
    End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:RowData_Get")
#End If
End Property

'get number of selected rows in grid
Property Get SelectedCount() As Long
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SelectedCount_Get")
#End If
  
  If Grid.Row > 0 Then
    SelectedCount = Abs(Grid.Row - Grid.RowSel) + 1
  Else
    SelectedCount = 0
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SelectedCount_Get")
#End If
End Property

'get one key as variant from all selected key as index (zero based to first selected line)
Property Get SelectedKey(al_SelectedIndex As Long) As Variant
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SelectedKey_Get")
#End If

  SelectedKey = GetRowKey(GetSelectedRowIndex(al_SelectedIndex))

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SelectedKey_Get")
#End If
End Property

'get array of keys of selected rows
Property Get MultiSelectedKey() As Variant
Dim lv_MultiKey As Variant
Dim ll_SelectedCount As Long
Dim ll_Index As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MultiSelectedKey_Get")
#End If
  lv_MultiKey = Empty
  ll_SelectedCount = SelectedCount
  If ll_SelectedCount > 0 Then
    ReDim lv_MultiKey(ll_SelectedCount - 1)
    For ll_Index = 0 To ll_SelectedCount - 1
      lv_MultiKey(ll_Index) = GetRowKey(GetSelectedRowIndex(ll_Index))
    Next
  End If
  MultiSelectedKey = lv_MultiKey

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MultiSelectedKey_Get")
#End If
  Exit Property
ErrorHandler:
  MultiSelectedKey = Empty
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:MultiSelectedKey_Get", "ll_Index=" & ll_Index)
#End If
End Property

'get array of keys of all rows
Property Get MultiKey() As Variant
Dim lv_MultiKey As Variant
Dim ll_Rows As Long
Dim ll_Index As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MultiKey_Get")
#End If
  lv_MultiKey = Empty
  ll_Rows = Rows
  If ll_Rows > 0 Then
    ReDim lv_MultiKey(ll_Rows - 1)
    For ll_Index = 0 To ll_Rows - 1
      lv_MultiKey(ll_Index) = GetRowKey(ll_Index)
    Next
  End If
  MultiKey = lv_MultiKey

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MultiKey_Get")
#End If
  Exit Property
ErrorHandler:
  MultiKey = Empty
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:MultiKey_Get", "ll_Index=" & ll_Index)
#End If
End Property

'get or set any value from selected line according index of selected line (zero based relativ
'to first selected line and column name (index)
Property Let SelectedLine(al_SelectedIndex As Long, av_Column As Variant, av_Value As Variant)
Dim lo_Column As ArmColumn
Dim ll_Row As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SelectedLine_Let")
#End If

  ll_Row = GetSelectedRowIndex(al_SelectedIndex)
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) And IsValidDataRow(ll_Row) Then
    Call SetInternalRowColChange(True)
    Call lo_Column.SetData(ll_Row, av_Value)
    Call SetInternalRowColChange(False)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SelectedLine_Let")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SelectedLine_Let", "al_SelectedIndex=" & al_SelectedIndex)
#End If
End Property

Property Get SelectedLine(al_SelectedIndex As Long, av_Column As Variant) As Variant
Dim lo_Column As ArmColumn
Dim ll_Row As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SelectedLine_Get")
#End If

  ll_Row = GetSelectedRowIndex(al_SelectedIndex)
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) And IsValidDataRow(ll_Row) Then
    SelectedLine = lo_Column.GetData(ll_Row)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SelectedLine_Get")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SelectedLine_Get", "al_SelectedIndex=" & al_SelectedIndex)
#End If
End Property

'user can select more than one line if true (default is true)
Property Let MultiSelect(ab_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MultiSelect_Let")
#End If

  mb_MultiSelect = ab_Value
  Call SelectWholeRow

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MultiSelect_Let")
#End If
End Property

Property Get MultiSelect() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MultiSelect_Get")
#End If
  
  MultiSelect = mb_MultiSelect

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MultiSelect_Get")
#End If
End Property

Property Let WordWrap(ab_Value As Boolean)
  Grid.WordWrap = ab_Value
End Property

Property Get WordWrap() As Boolean
  WordWrap = Grid.WordWrap
End Property


Property Let OleDropMode(aValue As MSFlexGridLib.OLEDragConstants)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:OleDropMode_Let")
#End If
    Grid.OleDropMode = aValue
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:OleDropMode_Let")
#End If
End Property

Property Get OleDropMode() As MSFlexGridLib.OLEDragConstants
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:OleDropMode_Get")
#End If
    OleDropMode = Grid.OleDropMode
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:OleDropMode_Get")
#End If
End Property


Public Function SetAutoRowHeight(av_Column As Variant) As Boolean
Dim ls_Text As String
Dim lo_Column As ArmColumn
Dim ll_Count As Long, ll_Row As Long, ll_Index As Long
Dim ll_Length As Long

  On Error GoTo ErrorHandler
  
  SetAutoRowHeight = False
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) Then
    Font.Name = Grid.Font.Name
    Font.Size = Grid.Font.Size
    For ll_Row = 0 To Rows - 1
      ls_Text = lo_Column.GetData(ll_Row)
      ll_Count = 0
      ll_Index = 0
      Do
        ll_Index = InStr(ll_Index + 1, ls_Text, vbCrLf)
        ll_Count = ll_Count + 1
      Loop Until ll_Index = 0
      
      If Grid.ColWidth(lo_Column.ColumnIndex) > 0 Then
        ' text width + line width + text left margin size
        ll_Length = ScaleX(TextWidth(ls_Text), vbPixels, vbTwips) + ScaleX(Grid.GridLineWidth, vbPixels, vbTwips) + ScaleX(3, vbPixels, vbTwips)
        ll_Count = ll_Count + ll_Length \ Grid.ColWidth(lo_Column.ColumnIndex)
      End If
      
      If ll_Count > 0 Then
        Grid.RowHeight(ll_Row + 1) = ll_Count * (Grid.RowHeight(0) + ScaleY(Grid.GridLineWidth, vbPixels, vbTwips))
      End If
    Next
    SetAutoRowHeight = True
  End If
  Exit Function
ErrorHandler:
  SetAutoRowHeight = False
End Function

'user can select more than one line if true (default is true)
Property Let FreeSelect(ab_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FreeSelect_Let")
#End If

  mb_FreeSelect = ab_Value
  Call ResetGrid

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FreeSelect_Let")
#End If
End Property

Property Get FreeSelect() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FreeSelect_Get")
#End If
  
  FreeSelect = mb_FreeSelect

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FreeSelect_Get")
#End If
End Property

Property Let ShowGrandTotal(ab_Value As Boolean)
  mb_ShowGrandTotal = ab_Value
End Property

Property Get ShowGrandTotal() As Boolean
  ShowGrandTotal = mb_ShowGrandTotal
End Property

Property Let TotalRowColor(al_Value As Long)
  ml_TotalRowColor = al_Value
End Property

Property Get TotalRowColor() As Long
  TotalRowColor = ml_TotalRowColor
End Property

Property Let MasterRowColor(al_Value As Long)
  ml_MasterRowColor = al_Value
End Property

Property Get MasterRowColor() As Long
  MasterRowColor = ml_MasterRowColor
End Property

Property Let DetailRowColor(al_Value As Long)
  ml_DetailRowColor = al_Value
End Property

Property Get DetailRowColor() As Long
  DetailRowColor = ml_DetailRowColor
End Property

Property Let RemoveRepeatValue(ab_Value As Boolean)
  mb_RemoveRepeatValue = ab_Value
End Property

Property Get RemoveRepeatValue() As Boolean
  RemoveRepeatValue = mb_RemoveRepeatValue
End Property

'get key as variant of current selected line
Property Get CurrentKey() As Variant
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:CurrentKey_Get")
#End If
  
  CurrentKey = GetRowKey(Row)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:CurrentKey_Get")
#End If
End Property

'get or set any value from current line according column name (index)
Property Let CurrentLine(av_Column As Variant, av_Value As Variant)
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:CurrentLine_Let")
#End If
  
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) And IsValidDataRow(Row) Then
    Call SetInternalRowColChange(True)
    Call lo_Column.SetData(Row, av_Value)
    Call SetInternalRowColChange(False)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:CurrentLine_Let")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:CurrentLine_Let")
#End If
End Property

Property Get CurrentLine(av_Column As Variant) As Variant
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:CurrentLine_Get")
#End If
  
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) And IsValidDataRow(Row) Then
    CurrentLine = lo_Column.GetData(Row)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:CurrentLine_Get")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:CurrentLine_Get")
#End If
End Property

Public Property Let MasterDetailRequest(as_Request As String)

  ms_MasterDetailRequest = as_Request
End Property

Public Property Get MasterDetailRequest() As String

  MasterDetailRequest = ms_MasterDetailRequest
End Property

Public Property Let RequestLoadDetails(ab_Value As Boolean)

  mb_RequestLoadDetails = ab_Value
End Property

Public Property Get RequestLoadDetails() As Boolean

  RequestLoadDetails = mb_RequestLoadDetails
End Property

Public Property Let MasterDetailSetting(as_value As String)
Dim lv_Data As Variant

  If as_value <> "" Then
    lv_Data = Split(as_value, SEP)
    If UBound(lv_Data) > 8 Then
      ms_MasterDataCol = lv_Data(0)
      ms_MasterIconCol = lv_Data(1)
      ms_DetailCountCol = lv_Data(2)
      ms_MasterData = lv_Data(3)
      ms_DetailData = lv_Data(4)
      ml_MasterRowColor = Val(lv_Data(5))
      ml_DetailRowColor = Val(lv_Data(6))
      ms_MasterIconExpanded = lv_Data(7)
      ms_MasterIconCollapsed = lv_Data(8)
      ms_MasterIconNoDetail = lv_Data(9)
    End If
  End If
End Property

'set array of requests (or one request) which will load data into grid
Property Let Requests(av_Requests As Variant)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Requests_Let")
#End If

  Call ClearRequests
  If IsArray(av_Requests) Then
    mv_Requests = av_Requests
  Else
    mv_Requests = Array(av_Requests)
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Requests_Let")
#End If
End Property

Property Get Requests() As Variant
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Requests_Get")
#End If
  
  Requests = mv_Requests

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Requests_Get")
#End If
End Property

'flag which detects if we have load al records in one time or one block in time
Property Let FetchAll(ab_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FetchAll_Let")
#End If
  
  mb_FetchAll = ab_Value

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FetchAll_Let")
#End If
End Property

Property Get FetchAll() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FetchAll_Get")
#End If
  
  FetchAll = mb_FetchAll

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FetchAll_Get")
#End If
End Property

Property Let Enabled(ab_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Enabled_Let")
#End If
  
  Grid.Enabled = ab_Value
  pct_Icon(ICON_EXCEL).Enabled = ab_Value
  pct_Icon(ICON_PRINT).Enabled = ab_Value
  reload_grid.Enabled = ab_Value
  frm_Like.Enabled = ab_Value
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Enabled_Let")
#End If
End Property

Property Get Enabled() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Enabled_Get")
#End If
  
  Enabled = Grid.Enabled

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Enabled_Get")
#End If
End Property

Property Let FiltersEnabled(ab_Value As Boolean)

  mb_FiltersEnabled = ab_Value
  Call CreateFilters
  Call MoveGrid
End Property

Property Get FiltersEnabled() As Boolean

  FiltersEnabled = mb_FiltersEnabled
End Property

Property Get AllDataLoaded() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AllDataLoaded_Get")
#End If
  
  AllDataLoaded = mb_FullLoaded

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AllDataLoaded_Get")
#End If
End Property

Property Let UnBound(ab_Value As Boolean)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:UnBound_Let")
#End If
  
  mb_UnBound = ab_Value
  If mb_UnBound Then
    mb_FullLoaded = True
    Call DisplayCursorInfo
    Call ResetActiveRequest
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:UnBound_Let")
#End If
End Property

Property Get UnBound() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:UnBound_Get")
#End If
  
  UnBound = mb_UnBound

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:UnBound_Get")
#End If
End Property

'armstrong load and unload calls
Public Sub Load_A_Com()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(True, "ArmGrid:Load_A_COM")
#End If

  Set mo_Columns = New Collection
  mb_MultiSelect = True
  mb_FreeSelect = False
  ml_AutoColumnWidth = 1000
  mb_UnBound = False
  mb_AllowSort = True
  mb_AllowMultiSort = True
  Call ResetGrid
  ml_MaxRows = MAX_ROWCOUNT_LIMIT
  mv_Messages = Empty
  mb_AllowExcelExport = False
  mb_AllowPrint = False
  
  Set mo_PrintFont = New StdFont
  mo_PrintFont.Name = "Arial"
  mo_PrintFont.Size = 9
  mt_ColumnWidthPrintMode = cwAutoFitToColumn
  ml_FixedCols = 0
  
#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(False, "ArmGrid:Load_A_COM")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceError("ArmGrid:Load_A_Com")
#End If
End Sub

Public Sub Unload_A_Com()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Unload_A_COM")
#End If
  
  Call ResetGrid
  If IsConnected And mb_InternalConnection Then
    mo_Db.Disconnect
  End If
  Set mo_Db = Nothing
  Set mo_Columns = Nothing
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Unload_A_COM")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Unload_A_COM")
#End If
End Sub

'reset grid to unitialized state, remove data, columns and request, reset properties
Public Function ResetGrid() As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ResetGrid")
#End If
  
  Call ClearGrid
  Call ClearColumns
  Call ClearRequests
  Title = ""
  mb_InternalRowColChange = False
  If mb_FreeSelect Then
    Grid.SelectionMode = flexSelectionFree
  Else
    Grid.SelectionMode = flexSelectionByRow
  End If
  Grid.AllowBigSelection = False
  Grid.AllowUserResizing = flexResizeColumns
  Grid.FocusRect = flexFocusNone
  Grid.ScrollBars = flexScrollBarBoth
  Grid.ScrollTrack = False
  Grid.BackColorFixed = &HC0C0C0
  Grid.Highlight = flexHighlightNever
  Call InitGrid(0, 0)
  Call InitBlockRowCount
  Call CalculateMaxRows
  ResetGrid = True
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ResetGrid")
#End If
  Exit Function
ErrorHandler:
  ResetGrid = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ResetGrid")
#End If
End Function

'clear data only, don't touch columns or requests
Public Sub ClearGrid()

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ClearGrid")
#End If
  
  Call InitGrid(0, Grid.Cols)
  Call ResetColumnSort
  Call ResetActiveRequest
  Call DisplayCursorInfo

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ClearGrid")
#End If
End Sub

'check if component is connected to SQL server or not
Property Get IsConnected() As Boolean

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:IsConnected")
#End If
  
  IsConnected = False
  If Not (mo_Db Is Nothing) Then
    IsConnected = mo_Db.IsConnected
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:IsConnected")
#End If
End Property

Property Let ConnectString(as_value As String)
Dim la_Params() As String

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ConnectString_Let")
#End If
  
  If (as_value <> "") Then
    la_Params = Split(as_value, SEP, 5)
    ms_Server = la_Params(0)
    ms_Db = la_Params(1)
    ms_User = la_Params(2)
    ms_Pwd = la_Params(3)
    If UBound(la_Params) >= 4 Then
      ms_App = la_Params(4)
    Else
      ms_App = "ArmGrid"
    End If
  Else
    ms_Server = ""
    ms_Db = ""
    ms_User = ""
    ms_Pwd = ""
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ConnectString_Let")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ConnectString_Let", "as_Value=" & as_value)
#End If
End Property

Property Get HasMeasure() As Boolean
Dim lo_Column As ArmColumn

  HasMeasure = False
  For Each lo_Column In mo_Columns
    If lo_Column.Measure <> MEASURE_NONE Then
      HasMeasure = True
      Exit For
    End If
  Next
End Property

'open conection and create own instance of ArmDB if it was not passed through property
Private Function OpenConnection(as_Server As String, as_Db As String, as_User As String, _
    as_Pwd As String, as_App As String) As Boolean
Dim lb_Result As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:OpenConnection")
#End If
  
  If IsConnected Then
    lb_Result = False
  Else
    mb_InternalConnection = True
    If mo_Db Is Nothing Then
      Set mo_Db = New ARMSYSCOMLib.ArmDb
    End If

    If (as_Server <> "") And (as_Db <> "") And (as_User <> "") Then
        lb_Result = mo_Db.Connect(as_Server, as_Db, as_User, as_Pwd, as_App)
    End If
  End If
  OpenConnection = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:OpenConnection")
#End If
  Exit Function
ErrorHandler:
  OpenConnection = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:OpenConnection", "as_Server=" & as_Server, _
  "as_Db=" & as_Db, "as_User=" & as_User)
#End If
End Function

'close connection if connection was created with OpenConnection method and
Private Sub CloseConnection()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:CloseConnection")
#End If
  
  If IsConnected Then
    Call ResetActiveRequest
    Call mo_Db.Disconnect
    mb_InternalConnection = False
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:CloseConnection")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:CloseConnection")
#End If
End Sub

Private Sub CalculateMaxRows()

  If Cols > 0 Then
    ml_MaxRows = (350000 / Grid.Cols) - 1
  Else
    ml_MaxRows = MAX_ROWCOUNT_LIMIT
  End If
End Sub

'initialize and create columns in grid
Public Function SetColumns(av_columns As Variant) As Boolean
Dim lb_Result As Boolean
Dim la_ColAttr() As String, ls_ColFormatString As String
Dim li_Index As Integer
Dim lu_FieldType As ArmSysType
Dim lu_Alignment As AlignmentSettings
Dim lb_Group As Boolean, lb_Subtotal As Boolean
Dim lu_Measure As ArmMeasure

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetColumns")
#End If
  
  Call SetRedraw(False)
  Call ClearGrid
  Call ClearColumns
  For li_Index = 0 To UBound(av_columns)
    la_ColAttr = Split(av_columns(li_Index), SEP)
    If UBound(la_ColAttr) >= 5 Then
      Select Case UCase(la_ColAttr(5))
      Case "INT"
        lu_FieldType = DBTYPE_I4
      Case "FLOAT"
        lu_FieldType = DBTYPE_R4
      Case "DATE"
        lu_FieldType = DBTYPE_DATE
      Case "STRING"
        lu_FieldType = DBTYPE_STR
      Case "BOOL"
        lu_FieldType = DBTYPE_BOOL
      Case "BITMAP"
        lu_FieldType = DBTYPE_BMP
      Case Else
        lu_FieldType = DBTYPE_EMPTY
      End Select
    Else
        lu_FieldType = DBTYPE_EMPTY
    End If
    
    If UBound(la_ColAttr) >= 6 Then
      ls_ColFormatString = la_ColAttr(6)
    Else
      ls_ColFormatString = ""
    End If
    
    If UBound(la_ColAttr) >= 7 Then
      Select Case UCase(la_ColAttr(7))
      Case "LEFT"
        lu_Alignment = flexAlignLeftCenter
      Case "RIGHT"
        lu_Alignment = flexAlignRightCenter
      Case "CENTER"
        lu_Alignment = flexAlignCenterCenter
      Case Else
        lu_Alignment = flexAlignGeneral
      End Select
    Else
      lu_Alignment = GetTypeAlignment(lu_FieldType)
    End If
    
    lb_Group = False
    If UBound(la_ColAttr) >= 8 Then
      lb_Group = StrComp(la_ColAttr(8), "TRUE", vbTextCompare) = 0
    End If
    lb_Subtotal = False
    If UBound(la_ColAttr) >= 9 Then
      lb_Subtotal = StrComp(la_ColAttr(9), "TRUE", vbTextCompare) = 0
    End If
        
    If (Not lb_Group) And (UBound(la_ColAttr) >= 10) Then
      Select Case Trim(UCase(la_ColAttr(10)))
      Case ""
        lu_Measure = MEASURE_NONE
      Case "SUM"
        lu_Measure = MEASURE_SUM
      Case "AVG"
        lu_Measure = MEASURE_AVG
      Case "COUNT"
        lu_Measure = MEASURE_COUNT
      Case Else
        lu_Measure = MEASURE_UNKNOWN
      End Select
    Else
      lu_Measure = MEASURE_NONE
    End If

    If Not AddNewColumn(la_ColAttr(0), CLng(la_ColAttr(1)), Not (la_ColAttr(2) = "0"), _
    la_ColAttr(3), la_ColAttr(4), lu_FieldType, ls_ColFormatString, lu_Alignment, lb_Group, lb_Subtotal, lu_Measure) Then GoTo ErrorHandler
  Next
  Call CreateFilters
  Call UpdateColumnTitleAlignment
  Call SetRedraw(True)
  SetColumns = True

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetColumns")
#End If
  Exit Function
ErrorHandler:
  Call ClearColumns
  Call SetRedraw(True)
  SetColumns = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SetColumns", "li_Index=" & li_Index, "av_Columns(li_Index)=" & av_columns(li_Index))
#End If
End Function

'access to all data cells in grid - value is variant of type like in DB
Public Property Let Data(al_row As Long, av_Column As Variant, av_Value As Variant)
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Data_Let")
#End If
  
  If IsValidDataRow(al_row) Then
    Set lo_Column = GetColumn(av_Column)
    Call SetInternalRowColChange(True)
    If Not (lo_Column Is Nothing) Then Call lo_Column.SetData(al_row, av_Value)
    Call SetInternalRowColChange(False)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Data_Let")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Data_Let", "al_Row=" & al_row)
#End If
End Property

Public Function MergeCol(av_Column As Variant) As Boolean
Dim lo_Column As ArmColumn

  MergeCol = False
  Set lo_Column = GetColumn(av_Column)
  If Not (lo_Column Is Nothing) Then
    Call lo_Column.Merge
    MergeCol = True
  End If
End Function

Public Function BoldLine(al_row As Long) As Boolean
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  BoldLine = False
  If IsValidDataRow(al_row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
    ll_Row = Grid.Row
    ll_col = Grid.Col
    ll_ColSel = Grid.ColSel
    Grid.FillStyle = flexFillRepeat
    Grid.Row = al_row + 1
    Grid.Col = 0
    Grid.ColSel = Grid.Cols - 1
    Grid.CellFontBold = True
    Grid.FillStyle = flexFillSingle
    Grid.Row = ll_Row
    Grid.Col = ll_col
    Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
    BoldLine = True
  End If
End Function

Public Property Let TextBold(al_Row As Long, ab_IsBold As Boolean)
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  If IsValidDataRow(al_Row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      ll_ColSel = Grid.ColSel
      Grid.Row = al_Row + 1
      Grid.Col = 0
      Grid.ColSel = Grid.Cols - 1
      Grid.CellFontBold = ab_IsBold
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Public Property Get TextBold(al_Row As Long) As Boolean

Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  If IsValidDataRow(al_Row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      ll_ColSel = Grid.ColSel
      Grid.Row = al_Row + 1
      Grid.Col = 0
      Grid.ColSel = 0
      TextBold = Grid.CellFontBold
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Public Property Let RowHeight(al_row As Long, al_height As Long)

  If IsValidDataRow(al_row) Then
    Grid.RowHeight(al_row + 1) = al_height
  End If
End Property

Public Property Get RowHeight(al_row As Long) As Long

  RowHeight = -1
  If IsValidDataRow(al_row) Then
    RowHeight = Grid.RowHeight(al_row + 1)
  End If
End Property

Public Property Let LineColor(al_row As Long, al_Color As Long)
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  If IsValidDataRow(al_row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
    ll_Row = Grid.Row
    ll_col = Grid.Col
    ll_ColSel = Grid.ColSel
    Grid.FillStyle = flexFillRepeat
    Grid.Row = al_row + 1
    Grid.Col = 0
    Grid.ColSel = Grid.Cols - 1
    Grid.CellBackColor = al_Color
    Grid.FillStyle = flexFillSingle
    Grid.Row = ll_Row
    Grid.Col = ll_col
    Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Public Property Get LineColor(al_row As Long) As Long
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  LineColor = 0
  If IsValidDataRow(al_row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      ll_ColSel = Grid.ColSel
      Grid.Row = al_row + 1
      Grid.Col = 0
      Grid.ColSel = 0
      LineColor = Grid.CellBackColor
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Public Property Let CellColor(al_row As Long, av_Column As Variant, al_Color As Long)
Dim ll_Row As Long
Dim ll_col As Long
Dim lo_Column As ArmColumn

  If IsValidDataRow(al_row) Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      Call SetRedraw(False)
      Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      Grid.FillStyle = flexFillSingle
      Grid.Row = al_row + 1
      Grid.Col = lo_Column.ColumnIndex
      Grid.CellBackColor = al_Color
      Grid.FillStyle = flexFillSingle
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Call SetInternalRowColChange(False)
      Call SetRedraw(True)
    End If
  ElseIf (al_Row = -1) Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      Call SetRedraw(False)
      Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      Grid.FillStyle = flexFillSingle
      Grid.Row = 0
      Grid.Col = lo_Column.ColumnIndex
      Grid.CellBackColor = al_Color
      Grid.FillStyle = flexFillSingle
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Call SetInternalRowColChange(False)
      Call SetRedraw(True)
    End If
  End If
End Property

Public Property Get CellColor(al_row As Long, av_Column As Variant) As Long
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long, ll_RowSel As Long
Dim lo_Column As ArmColumn

  CellColor = 0
  If IsValidDataRow(al_row) Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      Call SetRedraw(False)
      Call SetInternalRowColChange(True)
        ll_Row = Grid.Row
        ll_col = Grid.Col
        ll_RowSel = Grid.RowSel
        ll_ColSel = Grid.ColSel
        Grid.Row = al_row + 1
        Grid.Col = lo_Column.ColumnIndex
        CellColor = Grid.CellBackColor
        Grid.Row = ll_Row
        Grid.Col = ll_col
        Grid.RowSel = ll_RowSel
        Grid.ColSel = ll_ColSel
      Call SetInternalRowColChange(False)
      Call SetRedraw(True)
    End If
  End If
End Property

Public Property Let TextColor(al_row As Long, al_Color As Long)
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  If IsValidDataRow(al_row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
    ll_Row = Grid.Row
    ll_col = Grid.Col
    ll_ColSel = Grid.ColSel
    Grid.FillStyle = flexFillRepeat
    Grid.Row = al_row + 1
    Grid.Col = 0
    Grid.ColSel = Grid.Cols - 1
    Grid.CellForeColor = al_Color
    Grid.FillStyle = flexFillSingle
    Grid.Row = ll_Row
    Grid.Col = ll_col
    Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Public Property Get TextColor(al_row As Long) As Long
Dim ll_Row As Long
Dim ll_col As Long, ll_ColSel As Long

  TextColor = 0
  If IsValidDataRow(al_row) Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
      ll_Row = Grid.Row
      ll_col = Grid.Col
      ll_ColSel = Grid.ColSel
      Grid.Row = al_row + 1
      Grid.Col = 0
      Grid.ColSel = 0
      TextColor = Grid.CellForeColor
      Grid.Row = ll_Row
      Grid.Col = ll_col
      Grid.ColSel = ll_ColSel
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
End Property

Private Function SetRowAttributes(al_row As Long, ab_Master As Boolean) As Boolean

On Error GoTo ErrorHandler
  SetRowAttributes = False
  Call SetRedraw(False)
  Call SetInternalRowColChange(True)
  If IsValidDataRow(al_row) Then
    If ab_Master Then
      TextBold(al_Row) = True
      LineColor(al_row) = ml_MasterRowColor
    Else
      TextBold(al_Row) = False
      LineColor(al_row) = ml_DetailRowColor
    End If
    SetRowAttributes = True
  End If
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  Exit Function
ErrorHandler:
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  SetRowAttributes = False
End Function

Private Function SetMasterIcon(al_row As Long, as_Icon As String)
Dim lo_MasterData As ArmColumn
Dim lo_MasterIcon As ArmColumn

  Set lo_MasterData = GetColumn(ms_MasterDataCol)
  Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
  If (Not (lo_MasterData Is Nothing)) And (Not (lo_MasterIcon Is Nothing)) Then
    Call lo_MasterIcon.SetData(al_row, as_Icon)
  End If
End Function

Public Function CollapseMasterAll()
Dim lo_MasterData As ArmColumn
Dim lo_MasterIcon As ArmColumn
Dim lo_DetailCount As ArmColumn
Dim ll_Row As Long

  Set lo_MasterData = GetColumn(ms_MasterDataCol)
  Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
  Set lo_DetailCount = GetColumn(ms_DetailCountCol)
  If (Not (lo_MasterData Is Nothing)) And (Not (lo_MasterIcon Is Nothing)) Then
    For ll_Row = Rows - 1 To 0 Step -1
      If StrComp(lo_MasterData.GetData(ll_Row), ms_DetailData, vbTextCompare) = 0 Then
        Call lo_MasterData.DeleteRow(ll_Row)
      ElseIf StrComp(lo_MasterData.GetData(ll_Row), ms_MasterData, vbTextCompare) = 0 Then
        If lo_DetailCount Is Nothing Then
          Call SetMasterIcon(ll_Row, ms_MasterIconCollapsed)
        Else
          If lo_DetailCount.GetData(ll_Row) > 0 Then
            Call SetMasterIcon(ll_Row, ms_MasterIconCollapsed)
          Else
            Call SetMasterIcon(ll_Row, ms_MasterIconNoDetail)
          End If
        End If
      End If
    Next
  End If
End Function

Public Function ExpandMasterAll()
Dim lo_MasterData As ArmColumn
Dim ll_Row As Long

  Set lo_MasterData = GetColumn(ms_MasterDataCol)
  If Not (lo_MasterData Is Nothing) Then
    For ll_Row = Rows - 1 To 0 Step -1
      If StrComp(lo_MasterData.GetData(ll_Row), ms_MasterData, vbTextCompare) = 0 Then
        Call ExpandMaster(ll_Row)
      End If
    Next
  End If
End Function

Private Function CollapseMaster(al_row As Long) As Boolean
Dim lo_MasterData As ArmColumn
Dim lo_MasterIcon As ArmColumn

  CollapseMaster = False
  If IsValidDataRow(al_row) Then
    Set lo_MasterData = GetColumn(ms_MasterDataCol)
    Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
    If (Not (lo_MasterData Is Nothing)) And (Not (lo_MasterIcon Is Nothing)) Then
        If StrComp(lo_MasterData.GetData(al_row), ms_MasterData, vbTextCompare) = 0 Then
          If lo_MasterIcon.GetData(al_row) = ms_MasterIconExpanded Then
            Do While al_row + 1 < Rows
              If StrComp(lo_MasterData.GetData(al_row + 1), ms_DetailData, vbTextCompare) = 0 Then
                Call lo_MasterData.DeleteRow(al_row + 1)
              Else
                Exit Do
              End If
            Loop
            Call SetMasterIcon(al_row, ms_MasterIconCollapsed)
            CollapseMaster = True
          End If
        End If
    End If
  End If
End Function

Private Function ReplacePlaceholders(ll_Row As Long, ByVal as_Request As String) As String
Dim lo_Column As ArmColumn
Dim ll_Index As Long


  If IsArray(mv_ColumnIndexes) Then
    For ll_Index = 0 To UBound(mv_ColumnIndexes)
      Set lo_Column = mo_Columns(mv_ColumnIndexes(ll_Index))
      as_Request = Replace(as_Request, "$" & lo_Column.FieldName & "$", lo_Column.GetData(ll_Row), , , vbTextCompare)
    Next
  End If
  ReplacePlaceholders = as_Request
End Function

Private Function ExpandMaster(al_row As Long) As Boolean
Dim lo_MasterData As ArmColumn
Dim lo_MasterIcon As ArmColumn
Dim ll_cursor As Long
Dim ll_col As Long
Dim ll_Index As Long

On Error GoTo ErrorHandler
  ll_cursor = 0
  Call SetMousePointer(False)
  Redraw = False
  If IsValidDataRow(al_row) Then
    Set lo_MasterData = GetColumn(ms_MasterDataCol)
    Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
    If (Not (lo_MasterData Is Nothing)) And _
       (Not (lo_MasterIcon Is Nothing)) And _
       (ms_MasterDetailRequest <> "") Then
       
      If StrComp(lo_MasterData.GetData(al_row), ms_MasterData, vbTextCompare) = 0 Then
        If lo_MasterIcon.GetData(al_row) = ms_MasterIconCollapsed Then
          ll_cursor = mo_Db.OpenSQL(ReplacePlaceholders(al_row, ms_MasterDetailRequest))
          If ll_cursor <> 0 Then
            ll_Index = al_row + 1
            Do While Not mo_Db.EOF(ll_cursor)
              Call InsertLine(ll_Index)
              For ll_col = 0 To mo_Db.FieldCount(ll_cursor) - 1
                Data(ll_Index, mo_Db.GetFieldName(ll_cursor, ll_col)) = mo_Db.GetFields(ll_cursor, ll_col)
              Next
              Call SetRowAttributes(ll_Index, False)
              Call mo_Db.Next(ll_cursor)
              RaiseEvent RowLoaded(ll_Index)
              ll_Index = ll_Index + 1
            Loop
            Call mo_Db.Close(ll_cursor)
          End If
          If ll_Index - al_row - 1 > 0 Then
            Call SetMasterIcon(al_row, ms_MasterIconExpanded)
          Else
            Call SetMasterIcon(al_row, ms_MasterIconNoDetail)
          End If
        End If
        ExpandMaster = True
      End If
    End If
  End If
  Redraw = True
  Call SetMousePointer(True)
  Exit Function
ErrorHandler:
  If ll_cursor <> 0 Then Call mo_Db.Close(ll_cursor)
  Redraw = True
  Call SetMousePointer(True)
  ExpandMaster = False
End Function

Public Property Get Data(al_row As Long, av_Column As Variant) As Variant
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Data_Get")
#End If
  
  Data = Empty
  If IsValidDataRow(al_row) Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then Data = lo_Column.GetData(al_row)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Data_Get")
#End If
  Exit Property
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Data_Get", "al_Row=" & al_row)
#End If
End Property

Property Get FilterValue(av_Column As Variant) As Variant
Dim lo_Column As ArmColumn

  FilterValue = Empty
  If mb_FiltersEnabled Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      FilterValue = txt_Filter(lo_Column.ColumnIndex).Text
    End If
  End If
End Property

Property Let FilterValue(av_Column As Variant, av_Value As Variant)
Dim lo_Column As ArmColumn

  If mb_FiltersEnabled Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      txt_Filter(lo_Column.ColumnIndex).Text = CStr(av_Value)
    End If
  End If
End Property

'make first line current line
Public Function FirstLine() As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FirstLine")
#End If
  
  FirstLine = False
  If Rows > 0 Then
    Row = 0
    Grid.TopRow = 1
    FirstLine = True
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FirstLine")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:FirstLine", "Row=" & Row)
#End If
End Function

'make last line current line
Public Function LastLine() As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:LastLine")
#End If
  
  LastLine = False
  If Not mb_FullLoaded Then
    Call FetchNext(True)
  End If
  If Rows > 0 Then
    'this makes sure the row is visible
    Row = Rows - 1
    LastLine = True
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:LastLine")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:NextLine", "Row=" & Row)
#End If
End Function

'make next line current line
Public Function NextLine() As Boolean
Dim ll_Row As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:NextLine")
#End If
  
  NextLine = False
  ll_Row = Row + 1
  If (Not mb_FullLoaded) And (Row = Rows - 1) Then
    Call FetchNext(mb_FetchAll)
  End If
  If IsValidDataRow(ll_Row) Then
    Row = ll_Row
    NextLine = True
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:NextLine")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:NextLine", "ll_Row=" & ll_Row)
#End If
End Function

'make previous line current line
Public Function PreviousLine() As Boolean
Dim ll_Row As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:PreviousLine")
#End If
  
  PreviousLine = False
  ll_Row = Row - 1
    
  If IsValidDataRow(ll_Row) Then
    Row = ll_Row
    PreviousLine = True
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:PreviousLine")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:PreviousLine", "ll_Row=" & ll_Row)
#End If
End Function

'make previous line current line and fire itemselected event
Public Function PreviousItem() As Boolean
Dim lb_Result As Boolean

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:PreviousItem")
#End If
  
  lb_Result = PreviousLine
  If lb_Result Then RaiseEvent ItemSelected
  PreviousItem = lb_Result

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:PreviousItem")
#End If
End Function

'make next line current line and fire itemselected event
Public Function NextItem() As Boolean
Dim lb_Result As Boolean

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:NextItem")
#End If
  
  lb_Result = NextLine
  If lb_Result Then RaiseEvent ItemSelected
  NextItem = lb_Result

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:NextItem")
#End If
End Function

Public Function MoveSelection(au_Direction As RowMoveDirection) As Boolean
Dim ll_Row As Long
Dim ll_RowFrom As Long, ll_RowTo As Long
Dim ll_col As Long, ll_SaveCol As Long
Dim lo_Column As ArmColumn
  
On Error GoTo ErrorHandler
  SetInternalRowColChange (True)
  ll_SaveCol = Col
  If IsValidDataRow(Row) Then
    If au_Direction = rmDown Then
      ll_Row = Row + 1
      ll_RowFrom = ll_Row
      ll_RowTo = ll_Row + 2
    Else
      ll_Row = Row - 1
      ll_RowFrom = ll_Row + 3
      ll_RowTo = ll_Row + 1
    End If
    If IsValidDataRow(ll_Row) Then
      Call Grid.AddItem("", ll_RowTo)
      For ll_col = 0 To Grid.Cols - 1
        Grid.TextMatrix(ll_RowTo, ll_col) = Grid.TextMatrix(ll_RowFrom, ll_col)
      Next
      Call Grid.RemoveItem(ll_RowFrom)
      Set lo_Column = GetColumn(ms_MasterDataCol)
      If Not (lo_Column Is Nothing) Then
        Call SetRowAttributes(ll_RowTo, StrComp(lo_Column.GetData(ll_RowTo), ms_MasterData, vbTextCompare) = 0)
        Call SetRowAttributes(ll_Row, StrComp(lo_Column.GetData(ll_Row), ms_MasterData, vbTextCompare) = 0)
      End If
      Col = ll_SaveCol
      Row = ll_Row
      MoveSelection = True
    End If
  End If
  SetInternalRowColChange (False)
  Exit Function
ErrorHandler:
  SetInternalRowColChange (False)
  MoveSelection = False
End Function



'add line at end of grid and make it current line
Public Function AddLine(Optional ByVal lv_Data As Variant = Empty) As Boolean
Dim li_Index As Integer
Dim lo_Column As ArmColumn
Dim ll_Row As Long
Dim lb_Result As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AddLine")
#End If

  lb_Result = False
  If Rows >= ml_MaxRows Then
    Call ShowMessage(mtGRID_LIMIT_REACHED, "#Limit of maximum row count in grid reached.")
    Exit Function
  End If
  If Not IsArray(lv_Data) Then lv_Data = Array(lv_Data)
  If IsArray(lv_Data) And IsArray(mv_ColumnIndexes) Then
    If UBound(lv_Data) <= Cols - 1 Then
      Call InitGrid(Rows + 1, Grid.Cols)
      lb_Result = True
      For li_Index = 0 To UBound(lv_Data)
        Set lo_Column = mo_Columns(mv_ColumnIndexes(li_Index))
        If Not (lo_Column Is Nothing) Then
          Call SetInternalRowColChange(True)
          If Not lo_Column.SetData(Rows - 1, lv_Data(li_Index)) Then
            lb_Result = False
          End If
          Call SetInternalRowColChange(False)
        End If
      Next
    End If
  End If
  If lb_Result Then Row = Rows - 1
  Call DisplayCursorInfo
  Call SelectWholeRow
  AddLine = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AddLine")
#End If
  Exit Function
ErrorHandler:
  AddLine = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:AddLine", "Rows=" & Rows, _
  "Cols=" & Cols)
#End If
End Function

'add line at end of grid and make it current line
Public Function InsertLine(al_Index As Long, Optional ByVal lv_Data As Variant = Empty) As Boolean
Dim li_Index As Integer
Dim lo_Column As ArmColumn
Dim ll_Row As Long
Dim lb_Result As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:InsertLine")
#End If

  lb_Result = False
  If Rows >= ml_MaxRows Then
    Call ShowMessage(mtGRID_LIMIT_REACHED, "#Limit of maximum row count in grid reached.")
    Exit Function
  End If
  If Not IsArray(lv_Data) Then lv_Data = Array(lv_Data)
  If IsArray(lv_Data) And IsArray(mv_ColumnIndexes) Then
    If UBound(lv_Data) <= Cols - 1 Then
      Call Grid.AddItem("", al_Index + 1)
      lb_Result = True
      For li_Index = 0 To UBound(lv_Data)
        Set lo_Column = mo_Columns(mv_ColumnIndexes(li_Index))
        If Not (lo_Column Is Nothing) Then
          Call SetInternalRowColChange(True)
          If Not lo_Column.SetData(al_Index, lv_Data(li_Index)) Then
            lb_Result = False
          End If
          Call SetInternalRowColChange(False)
        End If
      Next
    End If
  End If
  If lb_Result Then Row = al_Index + 1
  Call DisplayCursorInfo
  Call SelectWholeRow
  InsertLine = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:InsertLine")
#End If
  Exit Function
ErrorHandler:
  InsertLine = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:InsertLine", "Index=" & al_Index, "Rows=" & Rows, _
  "Cols=" & Cols)
#End If
End Function

Public Function DeleteSelectedLines() As Boolean
Dim ll_Row1 As Long, ll_Row2 As Long
Dim lb_Result As Boolean

  lb_Result = True
  If SelectedCount > 0 Then
    ll_Row1 = GetSelectedRowIndex(0)
    ll_Row2 = GetSelectedRowIndex(SelectedCount - 1)
    Do While ll_Row1 <= ll_Row2
      lb_Result = DeleteDataRow(ll_Row1)
      ll_Row2 = ll_Row2 - 1
      If Not lb_Result Then Exit Do
    Loop
    Call DeselectRow
  End If
  DeleteSelectedLines = lb_Result
End Function

'delete line from grid according its key, if key is not given delete current line
Public Function DeleteLine(Optional av_key As Variant = Empty) As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:DeleteLine")
#End If

  If IsMissing(av_key) Or IsEmpty(av_key) Then
    DeleteLine = DeleteDataRow(Row)
  Else
    DeleteLine = DeleteDataRow(FindDataRow(av_key))
  End If
  Call DisplayCursorInfo

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:DeleteLine")
#End If
End Function

'search for line with specific key in grid and make it current one
Public Function SearchKey(ab_FromBegin As Boolean, av_key As Variant) As Boolean
Dim ll_FoundRow As Long, ll_StartSearchRow As Long
Dim lb_Result As Boolean, lb_SearchAgain As Boolean


On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SearchKey")
#End If
  
  lb_Result = False
  If Cols > 0 Then
    ll_StartSearchRow = IIf(ab_FromBegin, 0, Row)
    Do
      ll_FoundRow = FindDataRow(av_key, ll_StartSearchRow)
      lb_SearchAgain = False
      
      If IsValidDataRow(ll_FoundRow) Then
        'set selection to found row, and return true
        Row = ll_FoundRow
        'select whole row, this makes sure line is visible
        Call SelectWholeRow
        lb_Result = True
      Else
        'if grid is not full loaded load next block and try to find key in loaded block
        If Not mb_FullLoaded Then
          'next time search from current last record
          ll_StartSearchRow = Rows
          'search again if we successfuly load next block only
          lb_SearchAgain = FetchNext(False)
        End If
      End If
    Loop While lb_SearchAgain
  End If
  SearchKey = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SearchKey")
#End If
  Exit Function
ErrorHandler:
  SearchKey = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SearchKey", "ab_FromBegin=" & ab_FromBegin, _
  "ll_StartSearchRow=" & ll_StartSearchRow, "ll_FoundRow=" & ll_FoundRow)
#End If
End Function

'fill grid with data according properties which have been set in previous calls
Public Function Execute() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Execute")
#End If
  
  Call ClearGrid
  Call SetIconMode(False)
  If AutoColumns Then
    Call ClearColumns
  End If
  Execute = FetchNext(mb_FetchAll)
  Call DeselectRow

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Execute")
#End If
End Function

'sort grid according one column in one direction
Public Function Sort(av_columns As Variant, Optional au_Direction As ArmColSort) As Boolean
Dim lo_Column As ArmColumn
Dim ll_Index As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Sort")
#End If
  
  Sort = False
  Call ResetColumnSort
  If IsArray(av_columns) Then
    For ll_Index = 0 To UBound(av_columns)
      Set lo_Column = GetColumn(av_columns)
      If lo_Column Is Nothing Then
        Sort = False
        Exit Function
      Else
        Call SetColumnSort(lo_Column.ColumnIndex, au_Direction, True)
      End If
    Next
  Else
    Set lo_Column = GetColumn(av_columns)
    If lo_Column Is Nothing Then
      Sort = False
      Exit Function
    Else
      Call SetColumnSort(lo_Column.ColumnIndex, au_Direction, False)
    End If
  End If
  Sort = SortInternal
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Sort")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Sort", "au_Direction=" & au_Direction)
#End If
End Function

Private Sub SetIconMode(ab_Enable As Boolean)
  
  Grid.Visible = Not ab_Enable
  pct_Icon(ICON_EXCEL).Visible = (Not ab_Enable) And AllowExcelExport
  pct_Icon(ICON_PRINT).Visible = (Not ab_Enable) And AllowPrint
  reload_grid.Visible = ab_Enable
End Sub

'load data into grid after setting of columns
Public Function Load(av_Requests As Variant, ab_FetchAll As Boolean, Optional al_BlockRowCount As Variant, Optional ab_AutoColumns As Boolean = False, Optional ab_UseReloadIcon As Boolean = False) As Boolean
  
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Load")
#End If
  
  Requests = av_Requests
  FetchAll = ab_FetchAll
  AutoColumns = ab_AutoColumns
  If (Not IsMissing(al_BlockRowCount)) Then
    If (al_BlockRowCount > 1) Then
      ml_BlockRowCount = al_BlockRowCount
      mb_BlockRowCountSetByFrameWork = True
    End If
  End If
  If ab_UseReloadIcon Then
    Call ClearGrid
    Call SetIconMode(True)
    Load = True
  Else
    Load = Execute
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Load")
#End If
  Exit Function
ErrorHandler:
  Load = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Load", "ab_FetchAll=" & ab_FetchAll, _
  "al_BlockRowCount=" & al_BlockRowCount)
#End If
End Function
'execute special (mathematical) function on column and get back result
Public Function GridFunction(as_FunctionName As String, av_Column As Variant, Optional ByRef al_RowIndex As Long = -1) As Variant
Dim ll_ColIndex As Long
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GridFunction")
#End If
  
  GridFunction = Empty
  al_RowIndex = -1
  If as_FunctionName <> "" Then
    Set lo_Column = GetColumn(av_Column)
    If Not (lo_Column Is Nothing) Then
      Select Case UCase(as_FunctionName)
      Case "MAX"
        GridFunction = lo_Column.GetMax(al_RowIndex)
      Case "MIN"
        GridFunction = lo_Column.GetMin(al_RowIndex)
      Case "SUM"
        GridFunction = lo_Column.GetSum
      End Select
    End If
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GridFunction")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GridFunction", "as_FunctionName=" & as_FunctionName)
#End If
End Function

'export content of a grid's request into MS Excel application
Public Function ExportToExcel2(Optional ab_ExportTitles As Boolean = True) As Boolean
Dim lv_Columns As Variant
Dim ll_RowIndex As Long
Dim lb_Result As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportToExcel2")
#End If

  lb_Result = False
  Call SetMousePointer(False)
  If Cols > 0 Then
    mb_ExportTitles = ab_ExportTitles
    lv_Columns = GetColumnIndexes(mb_ExportOnlyVisibleColumns)
        
    If ExportOpen(lv_Columns) Then      ' columns should be loaded into grid
        lb_Result = True
        Dim ll_i As Long
        Dim ll_Cursor As Long
        For ll_i = LBound(mv_Requests) To UBound(mv_Requests)
            ll_Cursor = OpenRequestCursor(ll_i)
            
            If ll_Cursor = 0 Then
                GoTo nextrequest
            End If
            
            While Not mo_Db.EOF(ll_Cursor)
            
                If Not ExportRow2(lv_Columns, ll_Cursor) Then
                    lb_Result = False
                End If
            
                Call mo_Db.Next(ll_Cursor)
            Wend
nextrequest:
        Next

    Else
      lb_Result = False
    End If
    RaiseEvent AfterExcelExport(mo_ExcelApp, mo_ExcelApp.ActiveWorkbook, mo_ExcelApp.ActiveSheet)
    Call ExportClose
  End If
  
  ExportToExcel2 = lb_Result
  Call SetMousePointer(True)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportToExcel2")
#End If
  Exit Function
ErrorHandler:
  Call ExportClose
  Call SetMousePointer(True)
  ExportToExcel2 = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ExportToExcel2")
#End If
End Function

Private Function OpenRequestCursor(ByVal al_Index As Long) As Long
On Error GoTo ErrorHandler
    OpenRequestCursor = 0
    
    If Not IsArray(mv_Requests) Then
        Exit Function
    End If
    
    If al_Index > UBound(mv_Requests) Then
        Exit Function
    End If
    
    Dim ls_Request As String
    ls_Request = mv_Requests(al_Index)
    
    If ls_Request = "" Then
        Exit Function
    End If
              
    ls_Request = UpdateWhereFilterSQL(ls_Request)
                
    Dim ll_retVal As Long
    ll_retVal = mo_Db.OpenSQL(ls_Request)
                
    If ll_retVal = 0 Then
        Exit Function
    End If
                
    OpenRequestCursor = ll_retVal

  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:OpenRequestCursor")
#End If
End Function

'export grid content into MS Excel application
Public Function ExportToExcel(Optional ab_ExportTitles As Boolean = True)
Dim lv_Columns As Variant
Dim ll_RowIndex As Long
Dim lb_Result As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportToExcel")
#End If

    ' 12/1/2017 JN ... to detect uncomplete grid load
    If mb_FullLoaded And ml_ActiveCursor > 0 And mo_Db.Position(ml_ActiveCursor) < mo_Db.RowCount(ml_ActiveCursor) - 1 Then
        Call ShowMessage(mtEXCELL_EXPORT_REQUEST, "#The grid is not completelly loaded!. We will use export from database request.")
        ExportToExcel = ExportToExcel2(ab_ExportTitles)
        Exit Function
    End If

  lb_Result = False
  Call SetMousePointer(False)
  If Cols > 0 Then
    mb_ExportTitles = ab_ExportTitles
    lv_Columns = GetColumnIndexes(mb_ExportOnlyVisibleColumns)
        
    If ExportOpen(lv_Columns) Then
     
      lb_Result = True
      For ll_RowIndex = 0 To Rows - 1
        If Not ExportRow(lv_Columns, ll_RowIndex) Then
          lb_Result = False
          Exit For
        End If
      Next
    Else
      lb_Result = False
    End If
    RaiseEvent AfterExcelExport(mo_ExcelApp, mo_ExcelApp.ActiveWorkbook, mo_ExcelApp.ActiveSheet)
    Call ExportClose
  End If
  ExportToExcel = lb_Result
  Call SetMousePointer(True)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportToExcel")
#End If
  Exit Function
ErrorHandler:
  Call ExportClose
  Call SetMousePointer(True)
  ExportToExcel = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ExportToExcel")
#End If
End Function

Private Function ExportRow2(ByVal av_columns As Variant, ByVal al_Cursor As Long) As Boolean
Dim ll_col As Long
Dim lv_Data As Variant, lv_Value As Variant
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
  If Not IsArray(av_columns) Then Exit Function
  
  If UBound(av_columns) > 0 Then
    
    ReDim lv_Data(UBound(av_columns))
    
    For ll_col = LBound(av_columns) To UBound(av_columns)
      
      Set lo_Column = Columns(av_columns(ll_col))
      
      lv_Value = mo_Db.GetFields(al_Cursor, lo_Column.FieldName)
      
      Select Case lo_Column.FieldType
      Case DBTYPE_STR, DBTYPE_BSTR
          If ml_LocalID <> 0 Then
            lv_Data(ll_col) = "'" & StrConv(StrConv(Left(lv_Value, 910), vbFromUnicode, 1033), vbUnicode, ml_LocalID)
          Else
            lv_Data(ll_col) = "'" & Left(lv_Value, 910)
          End If
      Case DBTYPE_DATE
          If lv_Value = 0 Then lv_Value = Empty
          lv_Data(ll_col) = lv_Value
      Case Else
          lv_Data(ll_col) = lv_Value
      End Select
    Next
    ExportRow2 = ExportArray(lv_Data)
  End If
  Exit Function
ErrorHandler:
  ExportRow2 = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmExcel:ExportRow2")
#End If
End Function

Private Function ExportRow(ByVal av_columns As Variant, ByVal al_RowIndex As Long) As Boolean
Dim ll_col As Long, ll_ColCount As Long
Dim lv_Data As Variant, lv_Value As Variant
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
  If Not IsArray(av_columns) Then Exit Function
  
  If UBound(av_columns) > 0 Then
    ll_ColCount = UBound(av_columns) + 1
    
    ReDim lv_Data(ll_ColCount - 1)
    
    For ll_col = 0 To ll_ColCount - 1
      Set lo_Column = Columns(av_columns(ll_col))
      
      lv_Value = lo_Column.GetData(al_RowIndex)
      Select Case lo_Column.FieldType
      Case DBTYPE_STR, DBTYPE_BSTR
          If ml_LocalID <> 0 Then
            lv_Data(ll_col) = "'" & StrConv(StrConv(Left(lv_Value, 910), vbFromUnicode, 1033), vbUnicode, ml_LocalID)
          Else
            lv_Data(ll_col) = "'" & Left(lv_Value, 910)
          End If
      Case DBTYPE_DATE
          If lv_Value = 0 Then lv_Value = Empty
          lv_Data(ll_col) = lv_Value
      Case Else
          lv_Data(ll_col) = lv_Value
      End Select
    Next
    ExportRow = ExportArray(lv_Data)
  End If
  Exit Function
ErrorHandler:
  ExportRow = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmExcel:ExportRow")
#End If
End Function

Private Function ExportArray(av_Data As Variant) As Boolean
Dim ll_col As Long

On Error GoTo ErrorHandler
  mo_Sheet.Range(mo_Sheet.cells(ml_CurrentRow + 1, 1), _
                 mo_Sheet.cells(ml_CurrentRow + 1, UBound(av_Data) + 1)) = av_Data
  
  ml_CurrentRow = ml_CurrentRow + 1
  ExportArray = True
  Exit Function
ErrorHandler:
  ExportArray = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ExportArray")
#End If
End Function

Private Function ExportOpen(av_columns As Variant) As Boolean
Dim lb_Result As Boolean
Dim ll_idx As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportOpen")
#End If
  
  lb_Result = False
  
  If NewExcelDocument Then
    mo_ExcelApp.ScreenUpdating = False
    mo_ExcelApp.Cursor = 2
    Set mo_Sheet = mo_ExcelApp.ActiveSheet
    If Not (mo_Sheet Is Nothing) Then
      If mb_ExportTitles Then
        If IsArray(av_columns) Then
            For ll_idx = 0 To UBound(av_columns)
              mo_Sheet.cells(1, ll_idx + 1) = "'" & Columns(av_columns(ll_idx)).Title
            Next
          ml_CurrentRow = 1
          lb_Result = True
        End If
      Else
        ml_CurrentRow = 0
        lb_Result = True
      End If
    End If
  End If
  
  ExportOpen = lb_Result

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportOpen")
#End If
  Exit Function
ErrorHandler:
  ExportOpen = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ExportOpen")
#End If
End Function

Private Function ExportClose() As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ExportClose")
#End If
  
  ExportClose = False
  If Not (mo_ExcelApp Is Nothing) Then
    mo_ExcelApp.ScreenUpdating = True
    mo_ExcelApp.Cursor = -4143
    Set mo_Sheet = Nothing
    Set mo_ExcelApp = Nothing
    ExportClose = True
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ExportClose")
#End If
  Exit Function
ErrorHandler:
  ExportClose = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ExportClose")
#End If
End Function

Private Function NewExcelDocument() As Boolean
Dim lo_WorkBook As Object
Dim lo_WorkSheet As Object

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:NewExcelDocument")
#End If
    
    NewExcelDocument = False
    
    On Error GoTo Err_NotLoaded
    
    Set mo_ExcelApp = GetObject(, "Excel.Application")
    
    If mo_ExcelApp Is Nothing Then
        Set mo_ExcelApp = CreateObject("Excel.Application")
    End If
    
    If mo_ExcelApp Is Nothing Then GoTo ErrorHandler
    
    On Error GoTo ErrorHandler
    
    Set lo_WorkBook = mo_ExcelApp.Workbooks.Add
    Set lo_WorkSheet = lo_WorkBook.Worksheets(1)
    mo_ExcelApp.Application.Visible = True
    lo_WorkSheet.Application.Visible = True

    NewExcelDocument = True

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:NewExcelDocument")
#End If
    Exit Function
    
Err_NotLoaded:
    If Err.Number = 429 Then
        Resume Next
    End If
    
ErrorHandler:
    Set mo_ExcelApp = Nothing
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:NewExcelDocument")
#End If
End Function

Private Function GetColumnIndexes(ByVal ab_VisibleOnly As Boolean) As Variant
Dim lv_Columns As Variant
Dim ll_Index As Long
Dim ll_Pos As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetColumnIndexes")
#End If
  
  If Cols > 0 Then
    ReDim lv_Columns(Cols - 1)
    ll_Pos = 0
    For ll_Index = 0 To UBound(lv_Columns)
        If ab_VisibleOnly Then
            If mo_Columns(mv_ColumnIndexes(ll_Index)).Width > 0 Then
                lv_Columns(ll_Pos) = ll_Index
                ll_Pos = ll_Pos + 1
            End If
        Else
            lv_Columns(ll_Pos) = ll_Index
            ll_Pos = ll_Pos + 1
        End If
    Next
    If ll_Pos > 0 Then
        ReDim Preserve lv_Columns(ll_Pos - 1)
        GetColumnIndexes = lv_Columns
        Exit Function
    End If
  End If
  GetColumnIndexes = Empty
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetColumnIndexes")
#End If
  Exit Function
ErrorHandler:
  GetColumnIndexes = Empty
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetColumnIndexes")
#End If
End Function
'open request and return single value from first row and first field of result set
Private Function GetSQLValue(as_Request As String) As Variant
Dim ll_cursor As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetSQLValue")
#End If
  
  GetSQLValue = Empty
  
  If Not IsConnected Then
    If Not OpenConnection(ms_Server, ms_Db, ms_User, ms_Pwd, ms_App) Then
      Call ShowMessage(mtCONNECT_FAILED, "#Open connection failed.")
    End If
  End If
  
  If IsConnected And (as_Request <> "") Then
    ll_cursor = mo_Db.OpenSQL(as_Request)
    If ll_cursor = 0 Then
#If CompDebug Then
      Call mo_Trace.WriteTraceError("ArmGrid:GetSQLValue", "as_Request=" & as_Request, _
      "ll_Cursor=" & ll_cursor, "ms_Server=" & ms_Server, "ms_Db=" & ms_Db, _
      "ms_User=" & ms_User)
#End If
    Else
      If (mo_Db.RowCount(ll_cursor) > 0) And (mo_Db.FieldCount(ll_cursor) > 0) Then
        GetSQLValue = mo_Db.GetFields(ll_cursor, 0)
      End If
      Call mo_Db.Close(ll_cursor)
    End If
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetSQLValue")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetSQLValue", "as_Request=" & as_Request, _
  "IsConnected=" & IsConnected, "ll_Cursor=" & ll_cursor)
#End If
End Function

Private Sub SetLabel(lo_Label As Label, as_Text As String, as_Alignment As String)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetLabel")
#End If

  lo_Label.Caption = as_Text
  Select Case UCase(as_Alignment)
  Case "RIGHT"
    lo_Label.Alignment = vbRightJustify
  Case "CENTER"
    lo_Label.Alignment = vbCenter
  Case Else
    lo_Label.Alignment = vbLeftJustify
  End Select

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetLabel")
#End If
End Sub
'load constants and error messages
Public Function LoadConstants(au_ParamType As ParamType, ByVal as_Param As String, au_ConstType As ConstType) As Boolean
Dim lb_Result As Boolean
Dim ll_Index As Long
Dim la_Const() As String

  lb_Result = False
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:LoadConstants")
#End If
  
  'load constants from SQL and save it to as_Param string
  If (au_ParamType = ParamType.ptSQL) Then
    'convert request to static parameter
    as_Param = GetSQLValue(as_Param)
  End If
  
  la_Const = Split(as_Param, SEP)
  If IsArray(la_Const) Then
  
    Select Case au_ConstType
      
      Case ctTopGrid
        If UBound(la_Const) >= 1 Then
          Call SetLabel(lbl_Title, la_Const(0), la_Const(1))
          lb_Result = True
        End If
        
      Case ctColumns
        If UBound(la_Const) + 1 >= Cols Then
          For ll_Index = 0 To Cols - 1
            mo_Columns(mv_ColumnIndexes(ll_Index)).Title = la_Const(ll_Index)
          Next
          lb_Result = True
        End If
        
      Case ctMessages
        mv_Messages = la_Const
        lb_Result = True
        
      Case ctColumnPrintWidth
        If UBound(la_Const) + 1 >= Cols Then
          For ll_Index = 0 To Cols - 1
            mo_Columns(mv_ColumnIndexes(ll_Index)).PrintWidth = la_Const(ll_Index)
          Next
          lb_Result = True
        End If
    End Select
  End If
  LoadConstants = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:LoadConstants")
#End If
  Exit Function
ErrorHandler:
  LoadConstants = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:LoadConstants", "au_ParamType=" & au_ParamType, _
  "as_Param=" & as_Param, "au_ConstType=" & au_ConstType)
#End If
End Function

'load constants and error messages
Public Function GetConstants(au_ConstType As ConstType) As String
Dim ll_Index As Long
Dim ls_Const As String

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetConstants")
#End If
  GetConstants = ""
  ls_Const = ""
  Select Case au_ConstType
    
    Case ctTopGrid
      ls_Const = "LEFT" & SEP & Title
      
    Case ctColumns
      For ll_Index = 0 To Cols - 1
        If ls_Const <> "" Then ls_Const = ls_Const & SEP
        ls_Const = ls_Const & mo_Columns(mv_ColumnIndexes(ll_Index)).Title
      Next
      
    Case ctMessages
      ls_Const = Join(mv_Messages, SEP)
  End Select
  GetConstants = ls_Const
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetConstants")
#End If
  Exit Function
ErrorHandler:
  GetConstants = ""
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetConstants", "au_ParamType=" & au_ParamType, _
  "as_Param=" & as_Param, "au_ConstType=" & au_ConstType)
#End If
End Function

Public Property Get Font() As StdFont
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Font")
#End If
  
  Set Font = Grid.Font

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Font")
#End If
End Property

'add new column to the end of columns and set all its parameters
Private Function AddNewColumn(as_Name As String, al_width As Long, ab_Key As Boolean, as_FieldName As String, _
  as_Title As String, Optional au_FieldType As ArmSysType = DBTYPE_EMPTY, Optional as_ColFormatString As String = "", _
  Optional au_Alignment As AlignmentSettings = flexAlignGeneral, Optional ab_Group As Boolean = False, Optional ab_Subtotal As Boolean = False, _
  Optional au_Measure As ArmMeasure = MEASURE_NONE, Optional ao_ParentColumn As ArmColumn = Nothing) As Boolean
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AddNewColumn")
#End If
  
  Call InitGrid(Rows, Grid.Cols + 1)
  
  Set lo_Column = New ArmColumn
  Set lo_Column.FlexGrid = Grid
  Set lo_Column.Grid = Me
  Set lo_Column.mo_Trace = mo_Trace
  'add this column as last column
  lo_Column.ColumnIndex = Grid.Cols - 1
  Grid.ColData(lo_Column.ColumnIndex) = lo_Column.ColumnIndex
  Grid.ColAlignment(lo_Column.ColumnIndex) = au_Alignment

  If ao_ParentColumn Is Nothing Then
    lo_Column.IsSortSystemColumn = False
  Else
    Set ao_ParentColumn.SortSystemColumn = lo_Column
    ao_ParentColumn.IsSortSystemColumn = False
    lo_Column.IsSortSystemColumn = True
  End If
  Call SetInternalRowColChange(True)
  lo_Column.SortDirection = csNone
  lo_Column.SortOrder = -1
  Call SetInternalRowColChange(False)
  'add column to internal collection
  Call mo_Columns.Add(lo_Column)
  'column name - can be used when referencing columns instead of index
  lo_Column.Name = as_Name
  'column width in twips
  lo_Column.Width = al_width
  'this column is key if this value is non zero
  lo_Column.Key = ab_Key
    'field name reference in request
  lo_Column.FieldName = as_FieldName
  'title which will be show in column header
  lo_Column.Title = as_Title
  'system columns are only for dates
  lo_Column.FieldType = au_FieldType
  'column format string
  lo_Column.FormatString = as_ColFormatString
  'column alignment
  lo_Column.Alignment = au_Alignment
  'column group
  lo_Column.Group = ab_Group
  'column subtotal
  lo_Column.Subtotal = ab_Subtotal
  'column measure
  lo_Column.Measure = au_Measure
  'if this is not sort system columnadd it to index array
  If Not lo_Column.IsSortSystemColumn Then
    If IsArray(mv_ColumnIndexes) Then
      ReDim Preserve mv_ColumnIndexes(UBound(mv_ColumnIndexes) + 1)
    Else
      ReDim mv_ColumnIndexes(0)
    End If
    mv_ColumnIndexes(UBound(mv_ColumnIndexes)) = mo_Columns.Count
    'try to add system sort column if needed
    Call AddSortSystemColumn(lo_Column)
  End If
  Call SelectWholeRow(False)
  Call CalculateMaxRows
  AddNewColumn = True
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AddNewColumn")
#End If
  Exit Function
ErrorHandler:
  AddNewColumn = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:AddNewColumn", "as_Name=" & as_Name, _
  "Cols=" & Cols, "al_Width=" & al_width, "as_FieldName=" & as_FieldName)
#End If
End Function

'fire event when user click grid
Private Sub Grid_Click()
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_Click")
#End If
  
  If (Cols > 0) And (Rows > 0) And (Grid.MouseRow > 0) Then
    RaiseEvent Click
  End If
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_Click")
#End If
End Sub

'fire item selected when enter is pressed on current line
Private Sub Grid_KeyDown(KeyCode As Integer, Shift As Integer)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_KeyDown")
#End If
  
  If KeyCode = vbKeyReturn Then
    If Row >= 0 Then RaiseEvent ItemSelected
  End If
  RaiseEvent KeyDown(KeyCode, Shift)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_KeyDown")
#End If
End Sub

Private Sub grid_KeyPress(KeyAscii As Integer)
  RaiseEvent KeyPress(KeyAscii)
End Sub

Private Sub Grid_LostFocus()
  Call ResizeFilters
End Sub

'do sorting if user click on column title
Private Sub Grid_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim li_IndexX As Integer, li_IndexY As Integer
Dim ll_Row As Long, ll_col As Long, ll_idx As Long
Dim lb_MultiSort As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_MouseDown")
#End If
    
  If mb_AllowSort Then
  
    lb_MultiSort = mb_AllowMultiSort And (Shift And vbCtrlMask)
    If (Rows > 0) And (y < Grid.RowHeight(0)) Then
      For li_IndexX = 0 To Grid.Cols - 1
        If x < Grid.ColPos(li_IndexX) + Grid.ColWidth(li_IndexX) Then
          If SetColumnSort(Grid.ColData(li_IndexX), , lb_MultiSort) Then
            Call SortInternal
          End If
          Exit For
        End If
      Next
    End If
  End If
  
  ll_Row = -1
  For li_IndexY = 0 To Grid.Rows - 1
    If y < Grid.RowPos(li_IndexY) + Grid.RowHeight(li_IndexY) Then
      ll_Row = li_IndexY - 1
      Exit For
    End If
  Next
  
  ll_col = -1
  If IsArray(mv_ColumnIndexes) Then
    For li_IndexX = 0 To Grid.Cols - 1
      If x < Grid.ColPos(li_IndexX) + Grid.ColWidth(li_IndexX) Then
        'll_Col = li_IndexX
        For ll_idx = 0 To UBound(mv_ColumnIndexes)
          If li_IndexX + 1 = mv_ColumnIndexes(ll_idx) Then
              ll_col = ll_idx
              Exit For
          End If
        Next
        Exit For
      End If
    Next
  End If
  RaiseEvent MouseDown(Button, Shift, x, y, ll_Row, ll_col)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_MouseDown")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:Grid_MouseDown", "Button=" & Button, _
  "y=" & y, "Rows=" & Rows, "Cols=" & Cols)
#End If
End Sub

Private Sub Grid_OLEDragDrop(Data As MSFlexGridLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)

  If Not mb_InternalRowColChange Then
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_OLEDragDrop")
#End If
    RaiseEvent OLEDragDrop(Data, Effect, Button, Shift, x, y)
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_OLEDragDrop")
#End If
End Sub

'row col change event - select entire line if this is not already happened
Private Sub Grid_RowColChange()

  If Not mb_InternalRowColChange Then
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_RowColChange")
#End If
    
    Call DisplayCursorInfo
    Call SelectWholeRow
    RaiseEvent RowColChange
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_RowColChange")
#End If
End Sub

'selection was changed - update cursor info and select entire row
Private Sub Grid_SelChange()

  If Not mb_InternalRowColChange Then
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_SelChange")
#End If
    
    Call ResizeFilters
    Call DisplayCursorInfo
    Call SelectWholeRow
    RaiseEvent SelChange

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_SelChange")
#End If
  End If
End Sub

Public Sub EditCell(ByVal ao_Control As Control)
  If Not (ao_Control Is Nothing) Then
    'use correct font
    If (Grid.CellWidth > 0) And (Grid.CellHeight > 0) Then
    'position the edit box
      ao_Control.Left = Grid.CellLeft + Me.Left + ScaleX(Grid.Left, vbPixels, vbTwips)
      ao_Control.Top = Grid.CellTop + Me.Top + ScaleY(Grid.Top, vbPixels, vbTwips)
      ao_Control.Width = Grid.CellWidth
      If StrComp(TypeName(ao_Control), "TextBox", vbTextCompare) = 0 Then
        ao_Control.Height = Grid.CellHeight
        ao_Control.FontName = Grid.FontName
        ao_Control.FontSize = Grid.FontSize
      ElseIf StrComp(TypeName(ao_Control), "ArmCombobox", vbTextCompare) = 0 Then
        ao_Control.Font.Name = Grid.FontName
        ao_Control.Font.Size = Grid.FontSize
      End If
      DoEvents
      ao_Control.Visible = True
      ao_Control.SetFocus
    End If
  End If
End Sub

Private Sub pct_Icon_Click(Index As Integer)
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:pct_Icon_Click")
#End If
  Select Case Index
  Case ICON_EXCEL
    Call ExportToExcel(mb_ExportTitles)
  Case ICON_PRINT
    Call PrintGrid(Nothing)
  End Select
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:pct_Icon_Click")
#End If

End Sub


Private Sub reload_grid_Click()
  Call Execute
End Sub

Private Sub txt_Filter_KeyPress(Index As Integer, KeyAscii As Integer)
  If KeyAscii = Asc(vbCr) Then
    Call Execute
  End If
End Sub

'initialize grid just for visibility purposes
Private Sub UserControl_Initialize()
#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(True, "ArmGrid:UserControl_Initialize")
#End If
    
    Call InitGrid(0, 0)

#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(False, "ArmGrid:UserControl_Initialize")
#End If
End Sub

'init number of cols and rows in grid
Private Sub InitGrid(la_Rows As Long, la_Cols As Long)
Dim ll_SaveRow As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:InitGrid")
#End If
  
  ll_SaveRow = Grid.Row
  If la_Rows > 0 Then
      Grid.Rows = la_Rows + 1
      Grid.FixedRows = 1
      Grid.BackColor = vbWindowBackground
      Grid.Gridlines = flexGridFlat
  Else
      Grid.Rows = 1
      Grid.FixedRows = 0
      Grid.BackColor = Grid.BackColorFixed
      Grid.Gridlines = flexGridInset
      Grid.Highlight = flexHighlightNever
  End If
  Grid.Cols = la_Cols
  If Grid.Cols > ml_FixedCols Then Grid.FixedCols = ml_FixedCols
  'hack - when you assign fixed rows, position of current row can change
  'without firing event rowcolchange
  If ll_SaveRow = 0 Then
    Call SetInternalRowColChange(True)
    Grid.Row = 0
    Call SetInternalRowColChange(False)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:InitGrid")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:InitGrid", "la_Rows=" & la_Rows, _
  "la_Cols=" & la_Cols)
#End If
End Sub

Private Function GetTypeAlignment(au_FieldType As ArmSysType) As AlignmentSettings
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetTypeAlignment")
#End If
  
  Select Case au_FieldType
  Case DBTYPE_EMPTY
    GetTypeAlignment = flexAlignGeneral
  Case DBTYPE_I4, DBTYPE_R4, DBTYPE_DATE
    GetTypeAlignment = flexAlignRightCenter
  Case Else
    GetTypeAlignment = flexAlignLeftCenter
  End Select

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetTypeAlignment")
#End If
End Function

'update real column types after we get cursor for column which don't have type set
Private Sub UpdateColumnTypesFromCursor()
Dim ll_Index As Integer
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:UpdateColumnTypesFromCursor")
#End If
  
  If IsArray(mv_ColumnIndexes) And (ml_ActiveCursor <> 0) Then
    For ll_Index = 0 To UBound(mv_ColumnIndexes)
      Set lo_Column = mo_Columns(mv_ColumnIndexes(ll_Index))
      If (lo_Column.FieldName <> "") And (lo_Column.FieldType = DBTYPE_EMPTY) Then
        lo_Column.FieldType = mo_Db.GetFieldType(ml_ActiveCursor, lo_Column.FieldName)
        If lo_Column.Alignment = flexAlignGeneral Then
          lo_Column.Alignment = GetTypeAlignment(lo_Column.FieldType)
          Grid.ColAlignment(lo_Column.ColumnIndex) = lo_Column.Alignment
        End If
      End If
      Call AddSortSystemColumn(lo_Column)
    Next
    Call UpdateColumnTitleAlignment
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:UpdateColumnTypesFromCursor")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:UpdateColumnTypesFromCursor", "ll_Index=" & ll_Index, _
  "ml_ActiveCursor=" & ml_ActiveCursor)
#End If
End Sub

'add system sort column for column ao_column if this column doesn't has one and its
'type is date
Private Sub AddSortSystemColumn(ao_Column As ArmColumn)
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:AddSortSystemColumn")
#End If
  
  If (ao_Column.SortSystemColumn Is Nothing) And _
     ((ao_Column.FieldType = DBTYPE_DATE) Or (ao_Column.FormatString <> "")) Then
     
    If ao_Column.FieldType = DBTYPE_DATE Then
      Call AddNewColumn("", 0, False, "", "", DBTYPE_DATE, "yyyymmdd Hh:Nn:Ss", , , , , ao_Column)
    Else
    Call AddNewColumn("", 0, False, "", "", DBTYPE_R4, , , , , , ao_Column)
  End If
  End If
  
  If Not (ao_Column.SortSystemColumn Is Nothing) Then
    If ao_Column.FieldType = DBTYPE_DATE Then
      ao_Column.SortSystemColumn.FieldType = DBTYPE_STR
    Else
      ao_Column.SortSystemColumn.FieldType = ao_Column.FieldType
    End If
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:AddSortSystemColumn")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:AddSortSystemColumn")
#End If
End Sub

Private Function GetSortColumnCount() As Long
Dim ll_Index As Long
Dim lo_Column As ArmColumn
Dim ll_SortCount As Long

  ll_SortCount = 0
  For ll_Index = 0 To UBound(mv_ColumnIndexes)
    Set lo_Column = mo_Columns(mv_ColumnIndexes(ll_Index))
    If lo_Column.SortDirection <> csNone Then
      ll_SortCount = ll_SortCount + 1
    End If
  Next
  GetSortColumnCount = ll_SortCount
End Function

Private Function GetSortColumnIndexes() As Variant
Dim lo_Column As ArmColumn
Dim ll_SortCount As Long
Dim lv_SortColumnIndexes As Variant
Dim ll_Index As Long

  lv_SortColumnIndexes = Empty
  ll_SortCount = GetSortColumnCount
  If ll_SortCount > 0 Then
    ReDim lv_SortColumnIndexes(ll_SortCount - 1)
    For ll_Index = 0 To UBound(mv_ColumnIndexes)
      Set lo_Column = mo_Columns(mv_ColumnIndexes(ll_Index))
      If lo_Column.SortDirection <> csNone And lo_Column.SortOrder >= 0 Then
        lv_SortColumnIndexes(lo_Column.SortOrder) = mv_ColumnIndexes(ll_Index)
      End If
    Next
  End If
  GetSortColumnIndexes = lv_SortColumnIndexes
End Function

Private Function GetCurrentPosition(al_OriginalPosition As Long) As Long
Dim ll_Index As Long

  GetCurrentPosition = -1
  For ll_Index = 0 To Grid.Cols - 1
    If Grid.ColData(ll_Index) = al_OriginalPosition Then
      GetCurrentPosition = ll_Index
      Exit Function
    End If
  Next
End Function

Private Function RestoreOriginalColumnPosition() As Boolean
Dim ll_Index As Long
Dim ll_CurrentPosition As Long

  RestoreOriginalColumnPosition = True
  For ll_Index = Grid.Cols - 1 To 0 Step -1
    ll_CurrentPosition = GetCurrentPosition(ll_Index)
    If ll_CurrentPosition >= 0 Then
      If ll_CurrentPosition <> ll_Index Then
        Grid.ColPosition(ll_CurrentPosition) = ll_Index
      End If
    Else
      RestoreOriginalColumnPosition = False
      Exit For
    End If
  Next
End Function

Private Function SetSortColumnPosition(av_SortColumnIndexes As Variant, al_SortCount As Long) As Boolean
Dim ll_Index As Long
Dim lo_Column As ArmColumn
Dim ll_Position As Long

  SetSortColumnPosition = True
  For ll_Index = 0 To al_SortCount - 1
    Set lo_Column = mo_Columns(av_SortColumnIndexes(ll_Index))
    If lo_Column.SortSystemColumn Is Nothing Then
      ll_Position = GetCurrentPosition(lo_Column.ColumnIndex)
    Else
      ll_Position = GetCurrentPosition(lo_Column.SortSystemColumn.ColumnIndex)
    End If
    If ll_Position >= 0 Then
      Grid.ColPosition(ll_Position) = ll_Index
    Else
      SetSortColumnPosition = False
      Exit For
    End If
  Next
End Function

Private Function SortInternal() As Boolean
Dim lo_Column As ArmColumn
Dim ll_SortCount As Long
Dim lu_Sort As ArmColSort
Dim lv_SortColumnIndexes As Variant
Dim lb_ColPositionChanged As Boolean

On Error GoTo ErrorHandler
  SortInternal = False
  lb_ColPositionChanged = False
  Call SetRedraw(False)
  Call SetInternalRowColChange(True)

  lv_SortColumnIndexes = GetSortColumnIndexes
  If IsArray(lv_SortColumnIndexes) Then
    ll_SortCount = UBound(lv_SortColumnIndexes) + 1
    lu_Sort = mo_Columns(lv_SortColumnIndexes(0)).SortDirection
    
    If ll_SortCount = 1 Then
      SortInternal = mo_Columns(lv_SortColumnIndexes(0)).Sort
    Else
      If SetSortColumnPosition(lv_SortColumnIndexes, ll_SortCount) Then
        lb_ColPositionChanged = True
        Grid.RowSel = Grid.Row
        Grid.Col = 0
        Grid.ColSel = ll_SortCount - 1
        If lu_Sort = csAscending Then
          Grid.Sort = flexSortGenericAscending
        ElseIf lu_Sort = csDescending Then
          Grid.Sort = flexSortGenericDescending
        End If
        SortInternal = RestoreOriginalColumnPosition
        lb_ColPositionChanged = False
      End If
    End If
  End If
    
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  Call DeselectRow
  Exit Function
ErrorHandler:
  SortInternal = False
  If lb_ColPositionChanged Then RestoreOriginalColumnPosition
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
End Function

'do sorting on specific column reset all other columns, force to some direction
Private Function SetColumnSort(ai_ColumnIndex As Integer, Optional ByVal au_SortForce As ArmColSort = csNone, Optional ab_MultiSort As Boolean) As Boolean
Dim lo_Column As ArmColumn
Dim lu_MsgResult As VbMsgBoxResult
Dim ll_NextSortOrder As Long
Dim lv_SortColumnIndexes As Variant

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetColumnSort")
#End If

  SetColumnSort = False
  lu_MsgResult = vbOK
  If (ai_ColumnIndex >= 0) And (Not mb_FullLoaded) Then
    lu_MsgResult = ShowMessage(mtLOADALL_SORT, "#Not all records are loaded in grid. Load all records from server and sort?", vbYesNoCancel)
    If lu_MsgResult = vbYes Then
      Call FetchNext(True)
    End If
  End If
  
  If lu_MsgResult <> vbCancel Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
    Call CollapseMasterAll
    
    If ab_MultiSort Then
      ll_NextSortOrder = GetSortColumnCount
      'in case of multisort, when there is already some column sorted and parameter for sortforce is none
      'get sort direction of first sorted column
      If ll_NextSortOrder > 0 And (au_SortForce = csNone) Then
        lv_SortColumnIndexes = GetSortColumnIndexes
        au_SortForce = mo_Columns(lv_SortColumnIndexes(0)).SortDirection
      End If
    End If
    
    For Each lo_Column In mo_Columns
      If lo_Column.ColumnIndex = ai_ColumnIndex Then
        If Not lo_Column.IsSortSystemColumn Then
          If au_SortForce = csNone Then
            If ab_MultiSort Then
              'if there is no force to set direction,leave current direction
              If lo_Column.SortDirection = csNone Then
                lo_Column.SortDirection = csAscending
                lo_Column.SortOrder = ll_NextSortOrder
                SetColumnSort = True
              End If
            Else
              'if there is no force to set direction, simply reverse current direction
              lo_Column.SortDirection = IIf(lo_Column.SortDirection = csAscending, csDescending, csAscending)
              lo_Column.SortOrder = 0
              SetColumnSort = True
            End If
          Else
            If ab_MultiSort Then
              If lo_Column.SortDirection = csNone Then
                lo_Column.SortDirection = au_SortForce
                lo_Column.SortOrder = ll_NextSortOrder
                SetColumnSort = True
              End If
            Else
              'force to new direction
              lo_Column.SortDirection = au_SortForce
              lo_Column.SortOrder = 0
              SetColumnSort = True
            End If
          End If
        End If
      Else
        If Not ab_MultiSort Then
          'reset all other directions in case it is not multisort
          lo_Column.SortDirection = csNone
          lo_Column.SortOrder = -1
        End If
      End If
    Next
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
    Call DeselectRow
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetColumnSort")
#End If
  Exit Function
ErrorHandler:
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  Call DeselectRow
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SetColumnSort", "ai_ColumnIndex=" & ai_ColumnIndex, _
  "au_SortForce=" & au_SortForce, "mo_Columns.Count=" & mo_Columns.Count)
#End If
End Function

'reset sorting flag on all columns to none
Private Sub ResetColumnSort()
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler:
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ResetColumnSort")
#End If
  
  Call SetInternalRowColChange(True)
  For Each lo_Column In mo_Columns
    lo_Column.SortDirection = csNone
    lo_Column.SortOrder = -1
  Next
  Call SetInternalRowColChange(False)
  Call SelectWholeRow(False)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ResetColumnSort")
#End If
  Exit Sub
ErrorHandler:
  Call SetInternalRowColChange(False)
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ResetColumnSort", "mo_Columns.Count=" & mo_Columns.Count)
#End If
End Sub

Public Function SwapLines(al_Row1 As Long, al_Row2 As Long) As Boolean
Dim lb_Result As Boolean
Dim ll_Index As Long
Dim lv_Value As Variant

  lb_Result = False
On Error GoTo ErrorHandler
  If IsValidDataRow(al_Row1) And IsValidDataRow(al_Row2) And (al_Row1 <> al_Row2) Then
    For ll_Index = 0 To Cols - 1
      lv_Value = Data(al_Row1, ll_Index)
      Data(al_Row1, ll_Index) = Data(al_Row2, ll_Index)
      Data(al_Row2, ll_Index) = lv_Value
    Next
    lb_Result = True
  End If
  SwapLines = lb_Result
  Exit Function
ErrorHandler:
  SwapLines = False
End Function

'get column object according its index or column name
Private Function GetColumn(av_Column As Variant) As ArmColumn
Dim li_Index As Integer
Dim ls_Name As String
Dim lo_Column As ArmColumn

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetColumn")
#End If
  
  Set GetColumn = Nothing
  If IsArray(mv_ColumnIndexes) Then
    If isNumeric(av_Column) Then
      li_Index = CInt(av_Column)
      If (li_Index >= 0) And (li_Index < Cols) Then Set GetColumn = mo_Columns(mv_ColumnIndexes(li_Index))
    Else
      ls_Name = CStr(av_Column)
      For li_Index = 0 To UBound(mv_ColumnIndexes)
        Set lo_Column = mo_Columns(mv_ColumnIndexes(li_Index))
        If StrComp(ls_Name, lo_Column.Name, vbTextCompare) = 0 Then
          Set GetColumn = lo_Column
          Exit For
        End If
      Next
    End If
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetColumn")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetColumn")
#End If
End Function

'clear all columns objects and refresh grid
Private Sub ClearColumns()
    
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ClearColumns")
#End If
    
  Call ClearFilters
  Call ClearCollection(mo_Columns)
  mv_ColumnIndexes = Empty
  ml_KeyCount = 0
  Call InitGrid(0, 0)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ClearColumns")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ClearColumns")
#End If
End Sub

'close active request and initialize variables
Private Sub ResetActiveRequest()
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ResetActiveRequest")
#End If
  
  If IsConnected Then
    If ml_ActiveCursor <> 0 Then Call mo_Db.Close(ml_ActiveCursor)
  End If
  ml_ActiveCursor = 0
  mi_ActiveRequestIndex = 0
  If Not mb_UnBound Then
    mb_FullLoaded = False
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ResetActiveRequest")
#End If
End Sub

'clear all requests for loading data
Private Sub ClearRequests()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ClearRequests")
#End If
    
  Call ResetActiveRequest
  mv_Requests = Empty
    
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ClearRequests")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ClearRequests")
#End If
End Sub

'generic method to clear collection
Private Sub ClearCollection(lo_Collection As Collection)

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ClearCollection")
#End If
  
  If Not (lo_Collection Is Nothing) Then
    While lo_Collection.Count > 0
      Call lo_Collection.Remove(1)
    Wend
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ClearCollection")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ClearCollection", "lo_Collection.Count=" & lo_Collection.Count)
#End If
End Sub

'display information about current line number and total row count in grid
Private Sub DisplayCursorInfo()
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:DisplayCursorInfo")
#End If
    
  lbl_Curs.Caption = CStr(Row + 1) & "/" & CStr(Rows) & IIf(mb_FullLoaded, "", "..")
  Call MoveTitle

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:DisplayCursorInfo")
#End If
End Sub

'fetch next data from sql server
Private Function FetchNext(ab_FetchAll As Boolean) As Boolean
Dim lb_Result As Boolean, lb_LimitReached As Boolean
Dim ll_AllRowsFetched As Long, ll_CurrentRowsFetched As Long
Dim ls_Request As String

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FetchNext")
#End If
  
  lb_Result = False
  lb_LimitReached = False
  ll_AllRowsFetched = 0
  Call SetMousePointer(False)
  If Not mb_UnBound Then
    If Not IsConnected Then
      If Not OpenConnection(ms_Server, ms_Db, ms_User, ms_Pwd, ms_App) Then
        Call ShowMessage(mtCONNECT_FAILED, "#Open connection failed.")
      End If
    End If
  
    If IsConnected And (Not mb_FullLoaded) Then ' ??? And (Cols > 0) Then
     
      Do
        If ml_ActiveCursor > 0 Then
      
          ll_CurrentRowsFetched = mo_Db.Fetch(ml_ActiveCursor)
          If ll_CurrentRowsFetched > 0 Then
            ll_AllRowsFetched = ll_AllRowsFetched + ll_CurrentRowsFetched
            
            
            lb_LimitReached = Not FillGrid
            mb_FullLoaded = lb_LimitReached
          End If
          
          If (ll_CurrentRowsFetched < ml_BlockRowCount) Then
            'close current active request
            Call mo_Db.Close(ml_ActiveCursor)
            ml_ActiveCursor = 0
            'make next request active
            mi_ActiveRequestIndex = mi_ActiveRequestIndex + 1
          End If
        Else
          If IsArray(mv_Requests) Then
            If mi_ActiveRequestIndex <= UBound(mv_Requests) Then
              ls_Request = mv_Requests(mi_ActiveRequestIndex)
              'check for empty request
              If ls_Request <> "" Then
                ls_Request = UpdateWhereFilterSQL(ls_Request)
                Call InitBlockRowCount
                ml_ActiveCursor = mo_Db.OpenSQL(ls_Request, IIf(mb_FetchAll, -1, ml_BlockRowCount))
                
                If ml_ActiveCursor = 0 Then
                  mb_FullLoaded = True
                  lb_Result = False
                  Call DisplayCursorInfo
                  Call SetMousePointer(True)
  #If CompDebug Then
                  Call mo_Trace.WriteTraceSQLError(mo_Db, "ArmGrid:FetchNext", "ls_Request=" & ls_Request, _
                  "mo_Db.LastErrorMessage=" & mo_Db.LastErrorMessage, "mi_ActiveRequestIndex=" & mi_ActiveRequestIndex, _
                  "ml_BlockRowCount=" & ml_BlockRowCount)
  #End If
                  Exit Function
                End If
                
                If mb_AutoColumns And (Cols = 0) Then
                  Call CreateAutoColumns
                End If
                
                Call UpdateColumnTypesFromCursor
                ll_CurrentRowsFetched = mo_Db.RowCount(ml_ActiveCursor)
                ll_AllRowsFetched = ll_AllRowsFetched + ll_CurrentRowsFetched
                
                lb_LimitReached = Not FillGrid
                mb_FullLoaded = lb_LimitReached
              Else
                'ignore empty request
                mi_ActiveRequestIndex = mi_ActiveRequestIndex + 1
              End If
            Else
              mb_FullLoaded = True
            End If
          Else
            mb_FullLoaded = True
          End If
        End If
      Loop Until mb_FullLoaded Or ((ll_AllRowsFetched >= ml_BlockRowCount) And Not ab_FetchAll)
      
      If mb_FullLoaded And (Not lb_LimitReached) Then
        Call CalculateTotals(mb_ShowGrandTotal, mb_RemoveRepeatValue)
      End If
      RaiseEvent DataLoaded(mb_FullLoaded)
      lb_Result = True
    End If
  End If
  
  Call DisplayCursorInfo
  Call SetMousePointer(True)
  FetchNext = lb_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FetchNext")
#End If
  Exit Function
ErrorHandler:
  Call SetMousePointer(True)
  FetchNext = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:FetchNext", "mi_ActiveRequestIndex=" & mi_ActiveRequestIndex, _
  "ls_Request=" & ls_Request, "mb_FullLoaded=" & mb_FullLoaded, "ll_AllRowsFetched=" & ll_AllRowsFetched)
#End If
End Function

Private Function UpdateWhereFilterSQL(as_Sql As String) As String
Dim ls_Where As String
Dim lo_Column As ArmColumn
Dim ll_Index As Long
Dim ls_Like As String

  UpdateWhereFilterSQL = as_Sql
  If mb_FiltersEnabled Then
    'create where statement clause for all selected codes
    ls_Where = ""
    For Each lo_Column In mo_Columns
      If Not lo_Column.IsSortSystemColumn Then
        If Trim(txt_Filter(lo_Column.ColumnIndex)) <> "" Then
        
          If ls_Where <> "" Then ls_Where = ls_Where & " AND "
          If opt_like(0).Value Then
            ls_Like = "'" & txt_Filter(lo_Column.ColumnIndex) & "%'"
          ElseIf opt_like(1).Value Then
            ls_Like = "'%" & txt_Filter(lo_Column.ColumnIndex) & "%'"
          Else
            ls_Like = "'%" & txt_Filter(lo_Column.ColumnIndex) & "'"
          End If
          ls_Where = ls_Where & lo_Column.FieldName & " LIKE " & ls_Like & " "
        End If
      End If
    Next
    If ls_Where <> "" Then
      'try to find where statement in sql
      ll_Index = InStr(1, as_Sql, " WHERE ", vbTextCompare)
      If ll_Index = 0 Then ll_Index = InStr(1, as_Sql, vbLf & "WHERE ", vbTextCompare)
      If ll_Index = 0 Then ll_Index = InStr(1, as_Sql, " WHERE" & vbCr, vbTextCompare)
      If ll_Index = 0 Then ll_Index = InStr(1, as_Sql, vbLf & "WHERE" & vbCr, vbTextCompare)
      'add where statement if it is not found in the criteria select
      If ll_Index > 0 Then
        ls_Where = " AND " & ls_Where
      Else
        ls_Where = " WHERE " & ls_Where
      End If
      'insert where clause into sql
      ll_Index = InStr(1, as_Sql, "GROUP BY", vbTextCompare)
      If ll_Index > 0 Then
        UpdateWhereFilterSQL = Left(as_Sql, ll_Index - 1) & ls_Where & Mid(as_Sql, ll_Index, Len(as_Sql) - ll_Index + 1)
      Else
        ll_Index = InStr(1, as_Sql, "ORDER BY", vbTextCompare)
        If ll_Index > 0 Then
          UpdateWhereFilterSQL = Left(as_Sql, ll_Index - 1) & ls_Where & Mid(as_Sql, ll_Index, Len(as_Sql) - ll_Index + 1)
        Else
          UpdateWhereFilterSQL = as_Sql & ls_Where
        End If
      End If
    End If
  End If
End Function

Private Function CreateAutoColumns() As Boolean
Dim li_Index As Integer
Dim lu_FieldType As ArmSysType
Dim lu_Alignment As AlignmentSettings
Dim ls_Name As String

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:CreateAutoColumns")
#End If
  If ml_ActiveCursor <> 0 Then
  
    Call SetRedraw(False)
    Call ClearColumns
    For li_Index = 0 To mo_Db.FieldCount(ml_ActiveCursor) - 1
    
      lu_FieldType = mo_Db.GetFieldType(ml_ActiveCursor, li_Index)
      lu_Alignment = GetTypeAlignment(lu_FieldType)
      ls_Name = mo_Db.GetFieldName(ml_ActiveCursor, li_Index)
      If Not AddNewColumn(ls_Name, ml_AutoColumnWidth, False, ls_Name, ls_Name, _
        lu_FieldType, "", lu_Alignment) Then GoTo ErrorHandler
    Next
    Call SetRedraw(True)
    CreateAutoColumns = True

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:CreateAutoColumns")
#End If
  Exit Function
ErrorHandler:
  Call ClearColumns
  Call SetRedraw(True)
  CreateAutoColumns = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:CreateAutoColumns", "li_Index=" & li_Index)
#End If
  
  End If
End Function

'add data in active cursor into grid, don't change current line
Private Function FillGrid() As Boolean
Dim lb_Result As Boolean
Dim ll_Row As Long, li_Column As Integer
Dim lo_Column As ArmColumn
Dim lo_MasterData As ArmColumn
Dim lo_MasterIcon As ArmColumn
Dim lo_DetailCount As ArmColumn
Dim lb_LimitReached As Boolean


On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FillGrid")
#End If
  
  lb_Result = False
  lb_LimitReached = False
  Call SetRedraw(False)
  Call SetInternalRowColChange(True)
  Set lo_MasterData = GetColumn(ms_MasterDataCol)
  Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
  Set lo_DetailCount = GetColumn(ms_DetailCountCol)
  If (ml_ActiveCursor > 0) And IsArray(mv_ColumnIndexes) Then
    Call ResetColumnSort
    ll_Row = Rows
    If Rows + mo_Db.RowCount(ml_ActiveCursor) > MaxRows Then
      Call InitGrid(MaxRows, Grid.Cols)
      lb_LimitReached = True
    Else
      Call InitGrid(Rows + mo_Db.RowCount(ml_ActiveCursor), Grid.Cols)
    End If
    
    Call mo_Db.First(ml_ActiveCursor)
    While (Not mo_Db.EOF(ml_ActiveCursor)) And (ll_Row < Rows)
      
      For li_Column = 0 To UBound(mv_ColumnIndexes)
        Set lo_Column = mo_Columns(mv_ColumnIndexes(li_Column))
        'if there is field name, column is linked to cursor field
        If lo_Column.FieldName <> "" Then
          Call lo_Column.SetData(ll_Row, mo_Db.GetFields(ml_ActiveCursor, lo_Column.FieldName))
        End If
      Next
      If Not (lo_MasterData Is Nothing) Then
        If StrComp(lo_MasterData.GetData(ll_Row), ms_MasterData, vbTextCompare) = 0 Then
          If Not (lo_MasterIcon Is Nothing) Then
            If lo_DetailCount Is Nothing Then
              If mb_RequestLoadDetails Then
                Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconExpanded)
              Else
                Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconCollapsed)
              End If
            Else
              If mb_RequestLoadDetails Then
                If lo_DetailCount.GetData(ll_Row) > 0 Then
                  Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconExpanded)
                Else
                  Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconNoDetail)
                End If
              Else
                If lo_DetailCount.GetData(ll_Row) > 0 Then
                  Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconCollapsed)
                Else
                  Call lo_MasterIcon.SetData(ll_Row, ms_MasterIconNoDetail)
                End If
              End If
            End If
          End If
          Call SetRowAttributes(ll_Row, True)
        ElseIf StrComp(lo_MasterData.GetData(ll_Row), ms_DetailData, vbTextCompare) = 0 Then
          Call SetRowAttributes(ll_Row, False)
        End If
      End If
      RaiseEvent RowLoaded(ll_Row)
      Call mo_Db.Next(ml_ActiveCursor)
      ll_Row = ll_Row + 1
    Wend
    
    If lb_LimitReached Then
      Call ShowMessage(mtGRID_LIMIT_REACHED, "#Limit of maximum row count in grid reached.")
    Else
      lb_Result = True
    End If
  End If
  
  FillGrid = lb_Result
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FillGrid")
#End If
  Exit Function
ErrorHandler:
  FillGrid = False
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:FillGrid", "Rows=" & Rows, _
  "mo_Db.RowCount(ml_ActiveCursor)=" & mo_Db.RowCount(ml_ActiveCursor), "li_Column=" & li_Column, _
  "Cols=" & Cols)
#End If
End Function

'init size of block size, when this size was not already set by Load method
Private Sub InitBlockRowCount()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:InitBlockRowCount")
#End If
  
  If (Not mb_BlockRowCountSetByFrameWork) And (ml_ActiveCursor = 0) Then
    If Grid.Rows > 0 Then
        If Grid.RowHeight(0) > 0 Then
             ml_VisibleRowCount = Int(Grid.Height / (Grid.RowHeight(0) / PROP_TWIP_PIX)) - 1
        End If
    End If
    If ml_VisibleRowCount <= 0 Then ml_VisibleRowCount = 100
    ml_BlockRowCount = ml_VisibleRowCount * 3
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:InitBlockRowCount")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:InitBlockRowCount", "ml_VisibleRowCount=" & ml_VisibleRowCount, _
  "Grid.Rows=" & Grid.Rows, "ml_BlockRowCount=" & ml_BlockRowCount, _
  "Grid.Height=" & Grid.Height, "Grid.RowHeight(0)=" & Grid.RowHeight(0))
#End If
End Sub

'update size and position of grid in user control
Private Sub MoveGrid()

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MoveGrid")
#End If
  
  Grid.Left = GRID_MARGIN
  Grid.Top = GRID_MARGIN + IIf(lbl_Curs.Visible Or lbl_Title.Visible Or mb_AllowExcelExport Or mb_AllowPrint, lbl_Title.Height, 0) + IIf(mb_FiltersEnabled, frm_Like.Height + txt_Filter(0).Height, 0)
  Grid.Width = UserControl.ScaleWidth - (GRID_MARGIN * 2)
  Grid.Height = UserControl.ScaleHeight - (GRID_MARGIN * 2) - IIf(lbl_Curs.Visible Or lbl_Title.Visible Or mb_AllowExcelExport Or mb_AllowPrint, lbl_Title.Height, 0) - IIf(mb_FiltersEnabled, frm_Like.Height + txt_Filter(0).Height, 0)
  reload_grid.Top = Grid.Top
  reload_grid.Left = Grid.Left
  
  MoveTitle
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MoveGrid")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:MoveGrid", "Grid.Left=" & Grid.Left, _
  "Grid.Top=" & Grid.Top, "Grid.Width=" & Grid.Width, _
  "Grid.Height=" & Grid.Height, "Grid.Height=" & Grid.Height)
#End If
End Sub

'move label with title and cursor info
Private Sub MoveTitle()
    
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:MoveTitle")
#End If
  
  lbl_Curs.Left = Grid.Width - lbl_Curs.Width
  lbl_Curs.Top = Grid.Top - lbl_Curs.Height
  
  frm_Like.Left = Grid.Left
  pct_Icon(ICON_EXCEL).Left = Grid.Left
  pct_Icon(ICON_EXCEL).Top = Grid.Top - pct_Icon(ICON_EXCEL).Height
  
  pct_Icon(ICON_PRINT).Left = pct_Icon(ICON_EXCEL).Left + pct_Icon(ICON_EXCEL).Width + GRID_MARGIN
  pct_Icon(ICON_PRINT).Top = Grid.Top - pct_Icon(ICON_PRINT).Height
  
  If lbl_Title.Visible Then
    
    lbl_Title.Left = Grid.Left
    
    If mb_AllowExcelExport Then
      lbl_Title.Left = pct_Icon(ICON_EXCEL).Left + pct_Icon(ICON_EXCEL).Width + GRID_MARGIN
    End If
    
    If mb_AllowPrint Then
      lbl_Title.Left = pct_Icon(ICON_PRINT).Left + pct_Icon(ICON_PRINT).Width + GRID_MARGIN
    End If
    
    lbl_Title.Top = Grid.Top - lbl_Title.Height
    
    If lbl_Curs.Visible Then
      lbl_Title.Width = lbl_Curs.Left - lbl_Title.Left - GRID_MARGIN
    Else
      lbl_Title.Width = (Grid.Left + Grid.Width) - lbl_Title.Left
    End If
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:MoveTitle")
#End If
  Exit Sub
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:MoveTitle", "lbl_Curs.Visible=" & lbl_Curs.Visible, _
  "lbl_Title.Visible=" & lbl_Title.Visible, "Grid.Width=" & Grid.Width, _
  "Grid.Left=" & Grid.Left, "Grid.Top=" & Grid.Top)
#End If
End Sub

'refresh data in grid
Public Function Refresh() As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Refresh")
#End If
  
  Call ClearGrid
  Refresh = Execute()

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Refresh")
#End If
End Function

'fire item selected when valid data row double clicked
Private Sub Grid_DblClick()
Dim lo_MasterIcon As ArmColumn
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_DblClick")
#End If
  
  If (Cols > 0) And (Rows > 0) And (Grid.MouseRow > 0) Then
    RaiseEvent DblClick
    Set lo_MasterIcon = GetColumn(ms_MasterIconCol)
    If Not (lo_MasterIcon Is Nothing) Then
      If lo_MasterIcon.ColumnIndex = Grid.Col Then
        If lo_MasterIcon.GetData(Row) = ms_MasterIconCollapsed Then
          Call ExpandMaster(Row)
          RaiseEvent DataLoaded(False)
        ElseIf lo_MasterIcon.GetData(Row) = ms_MasterIconExpanded Then
          Call CollapseMaster(Row)
        End If
      Else
        RaiseEvent ItemSelected
      End If
    Else
      RaiseEvent ItemSelected
    End If
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_DblClick")
#End If
End Sub

'fecth next data into grid when user scrolls
Private Sub Grid_Scroll()
Dim ll_Row As Long

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:Grid_Scroll")
#End If
  
  If (Rows > 0) And ((Grid.TopRow + ml_BlockRowCount / 2) >= Grid.Rows) Then
    Call FetchNext(mb_FetchAll)
  End If
  Call ResizeFilters
  RaiseEvent Scroll
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:Grid_Scroll")
#End If
End Sub

'set current selected row as top row
Private Sub SetCurrentRowAsTopRow()

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetCurrentRowAsTopRow")
#End If
  
  If Grid.Row > 0 Then
    Grid.TopRow = Grid.Row
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetCurrentRowAsTopRow")
#End If
End Sub


'select entire row not only one cell
Private Sub SelectWholeRow(Optional lb_MakeVisible As Boolean = True)
Dim ll_RowSel As Long

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SelectWholeRow")
#End If
  
  Call SetInternalRowColChange(True)
  On Error GoTo ErrorHandler
  If (Grid.Cols >= 1) And (Grid.Rows > 1) And (Grid.Row > 0) Then
    If Not mb_MultiSelect Then Grid.RowSel = Grid.Row
    If mb_FreeSelect Then
      If Not mb_MultiSelect Then Grid.ColSel = Grid.Col
      If Grid.Highlight = flexHighlightNever Then Grid.Highlight = flexHighlightAlways
      If lb_MakeVisible Then
        If (Not Grid.RowIsVisible(Grid.Row)) Then Grid.TopRow = Grid.Row
        If (Not Grid.ColIsVisible(Grid.Col)) Then Grid.LeftCol = Grid.Col
      End If
    Else
      If Grid.Col = Grid.ColSel Then
        ll_RowSel = Grid.RowSel
        Grid.Col = 0
        Grid.ColSel = Grid.Cols - 1
        Grid.RowSel = ll_RowSel
        If lb_MakeVisible Then
          'if row is not visible set is as top row
          If (Not Grid.RowIsVisible(Grid.Row)) Then
            Call SetCurrentRowAsTopRow
          'if next row is visible, there is high probability that this row is partialy
          'visible so set it as top row
          ElseIf Grid.Row + 1 < Grid.Rows Then
            If Not Grid.RowIsVisible(Grid.Row + 1) Then Call SetCurrentRowAsTopRow
          'if this is the last row, it is high probability it is partialy visible
          'set it as top row
          ElseIf (Grid.Row + 1 = Grid.Rows) And (Grid.Rows > ml_VisibleRowCount) Then
            Call SetCurrentRowAsTopRow
          End If
        End If
        If Grid.Highlight = flexHighlightNever Then Grid.Highlight = flexHighlightAlways
      End If
    End If
  End If
  Call SetInternalRowColChange(False)
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SelectWholeRow")
#End If
  Exit Sub
ErrorHandler:
  Call SetInternalRowColChange(False)
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SelectWholeRow", "Grid.Cols=" & Grid.Cols, _
  "Grid.Rows=" & Grid.Rows, "Grid.Col=" & Grid.Col, "Grid.ColSel=" & Grid.ColSel)
#End If
End Sub

'clear current grid selection
Public Function DeselectRow() As Boolean

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:DeselectRow")
#End If
  
  Call SetInternalRowColChange(True)
  On Error GoTo ErrorHandler
  Grid.Highlight = flexHighlightNever
  If Grid.Cols > 0 Then
    Grid.Col = 0
    Grid.ColSel = 0
  End If
  If Grid.Rows > 0 Then Grid.RowSel = 0
  Call SetInternalRowColChange(False)
  If Grid.Rows > 0 Then Grid.Row = 0
  DeselectRow = True
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:DeselectRow")
#End If
  Exit Function
ErrorHandler:
  Call SetInternalRowColChange(False)
  DeselectRow = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:DeselectRow", "Grid.Cols=" & Grid.Cols, _
  "Grid.Rows=" & Grid.Rows, "Grid.Col=" & Grid.Col, "Grid.ColSel=" & Grid.ColSel)
#End If
End Function

'resize user control
Private Sub UserControl_Resize()
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:UserControl_Resize")
#End If
    
  Call MoveGrid
  Call InitBlockRowCount

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:UserControl_Resize")
#End If
End Sub

'check if index of row is reference to valid data row
Private Function IsValidDataRow(al_row As Long) As Boolean
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:IsValidDataRow")
#End If
  
  IsValidDataRow = (al_row >= 0) And (al_row < Rows)

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:IsValidDataRow")
#End If
End Function

'get key of line with number al_Row
Private Function GetRowKey(al_row As Long) As Variant
Dim lv_Key As Variant
Dim li_Index As Integer
Dim ll_KeyIndex As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetRowKey")
#End If
  
  lv_Key = Empty
  If (ml_KeyCount > 0) And IsValidDataRow(al_row) Then
    ReDim lv_Key(ml_KeyCount - 1)
    ll_KeyIndex = 0
    For li_Index = 0 To mo_Columns.Count - 1
      If mo_Columns(li_Index + 1).Key Then
        lv_Key(ll_KeyIndex) = Data(al_row, li_Index)
        ll_KeyIndex = ll_KeyIndex + 1
      End If
    Next
  
  End If
  GetRowKey = lv_Key
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetRowKey")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetRowKey", "al_Row=" & al_row, _
  "ml_KeyCount=" & ml_KeyCount)
#End If
End Function

'get zero base data row (first row with column titles is not counted)
Public Function GetSelectedRowIndex(al_SelectedIndex As Long) As Long
Dim ll_SelStartRow As Long

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:GetSelectedRowIndex")
#End If
  
  GetSelectedRowIndex = -1
  If al_SelectedIndex <= SelectedCount Then
    ll_SelStartRow = IIf(Grid.Row < Grid.RowSel, Grid.Row, Grid.RowSel)
    GetSelectedRowIndex = ll_SelStartRow + al_SelectedIndex - 1
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:GetSelectedRowIndex")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:GetSelectedRowIndex", "al_SelectedIndex=" & al_SelectedIndex, _
  "Grid.Row=" & Grid.Row)
#End If
End Function

'delete line al_row from data grid
Private Function DeleteDataRow(al_row As Long) As Boolean
  
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:DeleteDataRow")
#End If
  
  DeleteDataRow = False

  If IsValidDataRow(al_row) Then
    If Rows = 1 Then
      'cannot use remove item on last non fixed row
      Call InitGrid(0, Grid.Cols)
    Else
      Call Grid.RemoveItem(al_row + 1)
    End If
    DeleteDataRow = True
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:DeleteDataRow")
#End If
  Exit Function
ErrorHandler:
  DeleteDataRow = False
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:DeleteDataRow", "al_Row=" & al_row, _
  "Grid.Rows=" & Grid.Rows)
#End If
End Function

'find and return row number of line with key av_key, search from ll_StartRow
Private Function FindDataRow(ByVal av_key As Variant, Optional ll_StartRow As Long = 0) As Long
Dim ll_Key As Variant
Dim ll_Row As Long, li_Column As Integer, li_KeyIndex As Integer
Dim lo_Column As ArmColumn
Dim lb_Found As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FindDataRow")
#End If
  
  FindDataRow = -1
  If Not IsArray(av_key) Then
    av_key = Array(av_key)
  End If
  If ml_KeyCount = UBound(av_key) + 1 Then
    For ll_Row = ll_StartRow To Rows - 1
      lb_Found = True
      li_KeyIndex = 0
      For li_Column = 0 To mo_Columns.Count - 1
        Set lo_Column = mo_Columns(li_Column + 1)
        If lo_Column.Key Then
          If CStr(lo_Column.GetData(ll_Row)) <> CStr(av_key(li_KeyIndex)) Then
            lb_Found = False
            Exit For
          End If
          li_KeyIndex = li_KeyIndex + 1
        End If
      Next li_Column
      If lb_Found Then
        FindDataRow = ll_Row
        Exit For
      End If
    Next ll_Row
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FindDataRow")
#End If
  Exit Function
ErrorHandler:
  FindDataRow = -1
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:FindDataRow", "ll_StartRow=" & ll_StartRow, _
  "ml_KeyCount=" & ml_KeyCount, "Rows=" & Rows, "ll_Row=" & ll_Row)
#End If
End Function

Private Function ShowMessage(au_MsgCode As MsgType, as_MsgDefault As String, Optional au_Buttons As VbMsgBoxStyle = vbOKOnly) As VbMsgBoxResult
Dim lb_ShowDefault As Boolean

On Error GoTo ErrorHandler:
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:ShowMessage")
#End If

  lb_ShowDefault = True
  If IsArray(mv_Messages) Then
    If au_MsgCode <= UBound(mv_Messages) Then
      If mv_Messages(au_MsgCode) <> "" Then
        ShowMessage = MsgBox(mv_Messages(au_MsgCode), au_Buttons)
        lb_ShowDefault = False
      End If
    End If
  End If
  If lb_ShowDefault Then
    ShowMessage = MsgBox(as_MsgDefault, au_Buttons)
  End If
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:ShowMessage")
#End If
  Exit Function
ErrorHandler:
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:ShowMessage", "au_MsgCode=" & au_MsgCode, _
  "as_MsgDefault=" & as_MsgDefault, "au_Buttons=" & au_Buttons)
#End If
End Function

'**************************************************************************************************************
'turn of on mouse pointer sand hour glass, use counter
Private Static Sub SetMousePointer(lb_Enable As Boolean)
Dim li_Count As Integer
Dim li_OldPointer As Integer

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetMousePointer")
#End If
  
  If lb_Enable Then
    If li_Count > 0 Then li_Count = li_Count - 1
    If li_Count <= 0 Then
      'set back old state
      Screen.MousePointer = li_OldPointer
    End If
  Else
    li_Count = li_Count + 1
    'remember state of pointer before first change
    If li_Count = 1 Then li_OldPointer = Screen.MousePointer
    Screen.MousePointer = vbHourglass
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetMousePointer")
#End If
End Sub

Private Static Sub SetRedraw(lb_Enable As Boolean)
Dim li_Count As Integer

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetRedraw")
#End If
  
  If lb_Enable Then
    If li_Count > 0 Then li_Count = li_Count - 1
    If li_Count <= 0 Then
      'set back old state
      Grid.Redraw = True
    End If
  Else
    li_Count = li_Count + 1
    Grid.Redraw = False
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetRedraw")
#End If
End Sub

Private Static Sub SetInternalRowColChange(lb_Enable As Boolean)
Dim li_Count As Integer

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SetInternalRowColChange")
#End If
  
  If Not lb_Enable Then
    If li_Count > 0 Then li_Count = li_Count - 1
    If li_Count <= 0 Then
      mb_InternalRowColChange = False
    End If
  Else
    li_Count = li_Count + 1
    mb_InternalRowColChange = True
  End If

#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SetInternalRowColChange")
#End If
End Sub

Private Sub UserControl_Show()
#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(True, "ArmGrid:UserControl_Show")
#End If
  
  Call MoveTitle

#If CompDebugGR Then
  If Not (mo_Trace Is Nothing) Then Call mo_Trace.WriteTraceProc(False, "ArmGrid:UserControl_Show")
#End If
End Sub

Private Function CreateFilters() As Boolean
Dim ll_Index As Long

  frm_Like.Visible = mb_FiltersEnabled
  If mb_FiltersEnabled Then
    opt_like(0).Value = True
    If Cols > 0 Then
      For ll_Index = 1 To Cols - 1
        VB.Load txt_Filter(ll_Index)
        txt_Filter(ll_Index).Visible = False
        txt_Filter(ll_Index).Top = frm_Like.Top + frm_Like.Height
      Next
      Call ResizeFilters
    End If
  End If
End Function

Private Sub ResizeFilters()
Dim ll_Index As Long

  frm_Like.Visible = mb_FiltersEnabled
  If mb_FiltersEnabled Then
    If Cols > 0 Then
      For ll_Index = 0 To Cols - 1
        If ll_Index < Grid.LeftCol Then
          txt_Filter(ll_Index).Visible = False
        Else
          If Grid.ColWidth(ll_Index) = 0 Then
            txt_Filter(ll_Index).Visible = False
          Else
            txt_Filter(ll_Index).Visible = True
            txt_Filter(ll_Index).Left = UserControl.ScaleX(Grid.Left, vbTwips, vbPixels) + UserControl.ScaleX(Grid.ColPos(ll_Index), vbTwips, vbPixels) + 7
            txt_Filter(ll_Index).Width = UserControl.ScaleX(Grid.ColWidth(ll_Index), vbTwips, vbPixels)
          End If
        End If
      Next
    End If
  End If
End Sub

Private Sub ClearFilters()
Dim ll_Index As Long

  If mb_FiltersEnabled Then
    For ll_Index = 1 To Cols - 1
      VB.Unload txt_Filter(ll_Index)
    Next
  End If
End Sub

Private Function CalculateTotals(Optional ab_GrandTotal As Boolean = False, Optional ab_RemoveRepeat As Boolean) As Boolean
Dim lo_Column As ArmColumn, lo_GroupColumn As ArmColumn
Dim ll_RowIndex As Long, ll_RowCount As Long
Dim ll_ColumnIndex As Long, ll_MeasureIndex As Long, ll_GroupIndex As Long
Dim lo_MeasureColumns As New Collection
Dim lo_GroupColumns As New Collection
'Dim lo_DisplayGroupColumns As New Collection
Dim ll_GroupCount As Long
Dim lo_LabelColumn As ArmColumn, lo_MeasureColumn As ArmColumn
Dim lv_SumData As Variant, lv_CountData As Variant, lv_GridData As Variant
Dim ls_Label As String
Dim lb_CalcMeasure As Boolean
'Dim lb_Show As Boolean

On Error GoTo ErrorHandler

  CalculateTotals = False
  Call SetMousePointer(False)
  'stop refreshing grid
  Call SetRedraw(False)
  Call SetInternalRowColChange(True)
  If IsArray(mv_ColumnIndexes) Then
    For ll_ColumnIndex = 0 To UBound(mv_ColumnIndexes)
      Set lo_Column = mo_Columns(mv_ColumnIndexes(ll_ColumnIndex))
      If (lo_Column.Width > 0) And (lo_LabelColumn Is Nothing) Then Set lo_LabelColumn = lo_Column
      If lo_Column.Group Then Call lo_GroupColumns.Add(lo_Column)
      If lo_Column.Measure <> MEASURE_NONE Then Call lo_MeasureColumns.Add(lo_Column)
    Next
    lb_CalcMeasure = lo_MeasureColumns.Count > 0
    ll_GroupCount = lo_GroupColumns.Count
    'last one is always for grand total
    If ab_GrandTotal Then ll_GroupCount = ll_GroupCount + 1
    
    'check if we get exactly the number of columns we expect
    If (lo_GroupColumns.Count > 0) Then
      
      ReDim lv_GridData(ll_GroupCount - 1)
      ReDim lv_CountData(ll_GroupCount - 1)
      If lb_CalcMeasure Then ReDim lv_SumData(ll_GroupCount - 1, lo_MeasureColumns.Count - 1)
      
      ll_RowCount = Rows
      ll_RowIndex = 0
      Do While ll_RowIndex < ll_RowCount
        For ll_ColumnIndex = 0 To lo_GroupColumns.Count - 1
          'the last group column will be grand total so never break on it
          Set lo_Column = lo_GroupColumns(ll_ColumnIndex + 1)
          If ll_RowIndex = 0 Then
            'for the first row just store data
            lv_GridData(ll_ColumnIndex) = lo_Column.GetData(ll_RowIndex)
          Else
            If lv_GridData(ll_ColumnIndex) <> lo_Column.GetData(ll_RowIndex) Then
              For ll_GroupIndex = lo_GroupColumns.Count - 1 To ll_ColumnIndex Step -1
                Set lo_GroupColumn = lo_GroupColumns(ll_GroupIndex + 1)
                'group break, add subtotal
                If lo_GroupColumn.Subtotal And lb_CalcMeasure Then
                  ls_Label = IIf(ms_TextSubTotal = "", "#Subtotal ", ms_TextSubTotal) & lo_GroupColumn.Title & _
                                                        " (" & Trim(lv_GridData(ll_GroupIndex)) & ")  "
                  If Not InsertLine(ll_RowIndex) Then GoTo ErrorHandler
                  ll_RowCount = ll_RowCount + 1
                  Call lo_LabelColumn.SetData(ll_RowIndex, ls_Label)
                  LineColor(ll_RowIndex) = ml_TotalRowColor
                  RowData(ll_RowIndex) = ll_GroupIndex + 1

                  For ll_MeasureIndex = 0 To lo_MeasureColumns.Count - 1
                    Set lo_MeasureColumn = lo_MeasureColumns(ll_MeasureIndex + 1)
                    Call lo_MeasureColumn.SetData(ll_RowIndex, lo_MeasureColumn.CalculateMeasure(lv_SumData(ll_GroupIndex, ll_MeasureIndex), lv_CountData(ll_GroupIndex)))
                  Next
                  ll_RowIndex = ll_RowIndex + 1
                End If
                
                If lb_CalcMeasure Then
                  For ll_MeasureIndex = 0 To UBound(lv_SumData, 2)
                    lv_SumData(ll_GroupIndex, ll_MeasureIndex) = 0
                  Next
                  lv_CountData(ll_GroupIndex) = 0
                End If
                
                lv_GridData(ll_GroupIndex) = lo_GroupColumn.GetData(ll_RowIndex)
              Next
              Exit For
            Else
              If lo_Column.Group Then
                If ab_RemoveRepeat Then
                  Call lo_Column.SetData(ll_RowIndex, "")
                End If
              End If
            End If
          End If
        Next
        If lb_CalcMeasure Then
          For ll_ColumnIndex = 0 To UBound(lv_SumData, 1)
            For ll_MeasureIndex = 0 To UBound(lv_SumData, 2)
              Set lo_MeasureColumn = lo_MeasureColumns(ll_MeasureIndex + 1)
              lv_SumData(ll_ColumnIndex, ll_MeasureIndex) = lv_SumData(ll_ColumnIndex, ll_MeasureIndex) + lo_MeasureColumn.GetData(ll_RowIndex)
            Next
            lv_CountData(ll_ColumnIndex) = lv_CountData(ll_ColumnIndex) + 1
          Next
        End If
        ll_RowIndex = ll_RowIndex + 1
      Loop
    
      If lb_CalcMeasure Then
        'calculate last subtotals
        For ll_ColumnIndex = lo_GroupColumns.Count - 1 To 0 Step -1
          Set lo_Column = lo_GroupColumns(ll_ColumnIndex + 1)
          ls_Label = IIf(ms_TextSubTotal = "", "#Subtotal ", ms_TextSubTotal) & lo_Column.Title & " (" & Trim(lv_GridData(ll_ColumnIndex)) & ")  "
          
          If lo_Column.Subtotal And lv_CountData(ll_ColumnIndex) > 0 Then
            If Not AddLine Then GoTo ErrorHandler
            LineColor(ll_RowIndex) = ml_TotalRowColor
            RowData(ll_RowIndex) = ll_ColumnIndex + 1
            Call lo_LabelColumn.SetData(ll_RowIndex, ls_Label)
            
            For ll_MeasureIndex = 0 To lo_MeasureColumns.Count - 1
              Set lo_MeasureColumn = lo_MeasureColumns(ll_MeasureIndex + 1)
              Call lo_MeasureColumn.SetData(ll_RowIndex, lo_MeasureColumn.CalculateMeasure(lv_SumData(ll_ColumnIndex, ll_MeasureIndex), lv_CountData(ll_ColumnIndex)))
            Next
            ll_RowIndex = ll_RowIndex + 1
          End If
        Next
        'calculate grand total
        If ab_GrandTotal Then
          ll_ColumnIndex = ll_GroupCount - 1
          ls_Label = IIf(ms_TextGrandTotal = "", "#Grand Total  ", ms_TextGrandTotal)
          If lv_CountData(ll_ColumnIndex) > 0 Then
            If Not AddLine Then GoTo ErrorHandler
            RowData(ll_RowIndex) = -1
            LineColor(ll_RowIndex) = ml_TotalRowColor
            Call lo_LabelColumn.SetData(ll_RowIndex, ls_Label)
            
            For ll_MeasureIndex = 0 To lo_MeasureColumns.Count - 1
              Set lo_MeasureColumn = lo_MeasureColumns(ll_MeasureIndex + 1)
              Call lo_MeasureColumn.SetData(ll_RowIndex, lo_MeasureColumn.CalculateMeasure(lv_SumData(ll_ColumnIndex, ll_MeasureIndex), lv_CountData(ll_ColumnIndex)))
            Next
            ll_RowIndex = ll_RowIndex + 1
          End If
        End If
      End If
    End If
  End If
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  Call SetMousePointer(True)
  CalculateTotals = True
  Exit Function
ErrorHandler:
  Call SetInternalRowColChange(False)
  Call SetRedraw(True)
  Call SetMousePointer(True)
  CalculateTotals = False
End Function

Public Function GetCustomFormat(av_Value As Variant, ao_Column As ArmColumn) As String
Dim ls_Result As String

  ls_Result = CStr(av_Value)
  RaiseEvent CustomFormatValue(av_Value, ao_Column, ls_Result)
  GetCustomFormat = ls_Result
End Function

Private Sub UpdateColumnTitleAlignment()
Dim ll_OldRow As Long, ll_OldCol As Long
Dim ll_Index As Long

  If Grid.Rows > 0 Then
    Call SetRedraw(False)
    Call SetInternalRowColChange(True)
On Error GoTo ErrorHandler
    ll_OldRow = Grid.Row
    ll_OldCol = Grid.Col
    For ll_Index = 0 To Grid.Cols - 1
      Grid.Row = 0
      Grid.Col = ll_Index
      Grid.CellAlignment = flexAlignLeftCenter
    Next
    Grid.Row = ll_OldRow
    Grid.Col = ll_OldCol
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
  End If
  Exit Sub
ErrorHandler:
    Call SetInternalRowColChange(False)
    Call SetRedraw(True)
End Sub

Public Sub UpdateKeyCount()
Dim lo_Column As ArmColumn

  ml_KeyCount = 0
  For Each lo_Column In mo_Columns
    If lo_Column.Key Then ml_KeyCount = ml_KeyCount + 1
  Next
End Sub

'search for cell with specific string in grid
' return row where cell was found
Public Function SearchText(ab_FromBegin As Boolean, as_searchText As String, Optional ByVal ab_searchAll As Boolean = True) As Long
Static ll_searchPos As Long
Dim ll_Result As Long, ll_FoundPos As Long
Dim lb_SearchAgain As Boolean

On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:SearchText")
#End If

    If ab_FromBegin Or ll_searchPos = 0 Then
        SetIconMode (False)
        ll_searchPos = Grid.Cols - 1   ' skip first row
    End If
    
    ll_Result = -1
    Do
      ll_FoundPos = FindDataText(as_searchText, ll_searchPos + 1)
      lb_SearchAgain = False
      
      If ll_FoundPos <> -1 Then
        ll_searchPos = ll_FoundPos
        ' calculate row from pos
        ll_Result = (ll_searchPos \ Grid.Cols) - 1 ' it is 0 based
        
        SetInternalRowColChange (True)
        Grid.Highlight = flexHighlightNever
        Row = ll_Result
        Grid.Col = ll_searchPos Mod Grid.Cols
        SetInternalRowColChange (False)
      Else
        'if grid is not full loaded load next block and try to find text in loaded block
        If Not mb_FullLoaded And ab_searchAll Then
          'next time search from current last record
          ll_searchPos = Grid.Rows * Grid.Cols
          'search again if we successfuly load next block only
          lb_SearchAgain = FetchNext(False)
        End If
      End If
    Loop While lb_SearchAgain
    SearchText = ll_Result
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:SearchText")
#End If
  Exit Function
ErrorHandler:
  SearchText = 0
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:SearchText", "ab_FromBegin=" & ab_FromBegin, _
  "ll_searchPos=" & ll_searchPos)
#End If
End Function

'find and return cell number, search from ll_searchPos
Private Function FindDataText(ByVal as_Text As String, ByVal ll_searchPos As Long) As Long
On Error GoTo ErrorHandler
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(True, "ArmGrid:FindDataText")
#End If
  
    FindDataText = -1
    For ll_searchPos = ll_searchPos To Grid.Rows * Grid.Cols
        If InStr(1, Grid.TextArray(ll_searchPos), as_Text, 1) <> 0 Then
            FindDataText = ll_searchPos
            Exit For
        End If
    Next ll_searchPos
  
#If CompDebugGR Then
  Call mo_Trace.WriteTraceProc(False, "ArmGrid:FindDataText")
#End If
  Exit Function
ErrorHandler:
  FindDataText = -1
#If CompDebug Then
  Call mo_Trace.WriteTraceError("ArmGrid:FindDataText", "ll_searchPos=" & ll_searchPos, _
  "as_text=" & as_Text, "Rows=" & Rows)
#End If
End Function

Property Let TagVariant(av_TagVariant As Variant)

  mv_TagVariant = av_TagVariant
End Property

Property Get TagVariant() As Variant

  TagVariant = mv_TagVariant
End Property

Private Function ShowPrintDlg(frmOwner As Form, Optional ByRef al_PrintFlags As Long = 0) As Boolean
    Dim PrintDlg As PRINTDLG_TYPE
    Dim DevMode As DEVMODE_TYPE
    Dim DevName As DEVNAMES_TYPE
    Dim ll_PrintResult As Long

    Dim lpDevMode As Long, lpDevName As Long
    Dim bReturn As Integer
    Dim objPrinter As Printer, NewPrinterName As String
    
On Error GoTo ErrHandler
    ShowPrintDlg = False
    ' Use PrintDialog to get the handle to a memory
    ' block with a DevMode and DevName structures

    PrintDlg.lStructSize = Len(PrintDlg)
    If Not (frmOwner Is Nothing) Then
        PrintDlg.hwndOwner = frmOwner.hWnd
    Else
        PrintDlg.hwndOwner = UserControl.hWnd
    End If

    PrintDlg.flags = al_PrintFlags
    On Error Resume Next
    'Set the current orientation and duplex setting
    DevMode.dmDeviceName = Printer.DeviceName
    DevMode.dmSize = Len(DevMode)
    DevMode.dmFields = DM_ORIENTATION Or DM_DUPLEX
    DevMode.dmPaperWidth = Printer.Width
    DevMode.dmOrientation = Printer.Orientation
    DevMode.dmPaperSize = Printer.PaperSize
    DevMode.dmDuplex = Printer.Duplex
    On Error GoTo 0

    'Allocate memory for the initialization hDevMode structure
    'and copy the settings gathered above into this memory
    PrintDlg.hDevMode = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, Len(DevMode))
    If PrintDlg.hDevMode = 0 Then Err.Raise -100, "GlobalAlloc", "GlobalAlloc failed: Len(DevMode)=" & Len(DevMode)
    
    lpDevMode = GlobalLock(PrintDlg.hDevMode)
    If lpDevMode = 0 Then
      Err.Raise -110, "GlobalLock", "GlobalLock failed"
    Else
        CopyMemory ByVal lpDevMode, DevMode, Len(DevMode)
        bReturn = GlobalUnlock(PrintDlg.hDevMode)
    End If

    'Set the current driver, device, and port name strings
    With DevName
        .wDriverOffset = 8
        .wDeviceOffset = .wDriverOffset + 1 + Len(Printer.DriverName)
        .wOutputOffset = .wDeviceOffset + 1 + Len(Printer.Port)
        .wDefault = 0
    End With

    With Printer
        DevName.extra = .DriverName & Chr(0) & .DeviceName & Chr(0) & .Port & Chr(0)
    End With

    'Allocate memory for the initial hDevName structure
    'and copy the settings gathered above into this memory
    PrintDlg.hDevNames = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, Len(DevName))
    If PrintDlg.hDevNames = 0 Then Err.Raise -120, "GlobalAlloc", "GlobalAlloc failed: Len(DevName)=" & Len(DevName)
    
    lpDevName = GlobalLock(PrintDlg.hDevNames)
    If lpDevName = 0 Then
      Err.Raise -100, "GlobalLock", "GlobalLock failed"
    Else
        CopyMemory ByVal lpDevName, DevName, Len(DevName)
        bReturn = GlobalUnlock(lpDevName)
    End If

    'Call the print dialog up and let the user make changes
    ll_PrintResult = PrintDialog(PrintDlg)
    
    'First get the DevName structure.
    lpDevName = GlobalLock(PrintDlg.hDevNames)
    If lpDevName = 0 Then
      Err.Raise -130, "GlobalLock", "GlobalLock failed"
    Else
      CopyMemory DevName, ByVal lpDevName, Len(DevName)
      bReturn = GlobalUnlock(lpDevName)
    End If
    Call GlobalFree(PrintDlg.hDevNames)
    PrintDlg.hDevNames = 0
    
    'Next get the DevMode structure and set the printer
    'properties appropriately
    lpDevMode = GlobalLock(PrintDlg.hDevMode)
    If lpDevMode = 0 Then
      Err.Raise -100, "GlobalLock", "GlobalLock failed"
    Else
      Call CopyMemory(DevMode, ByVal lpDevMode, Len(DevMode))
      bReturn = GlobalUnlock(PrintDlg.hDevMode)
    End If
    Call GlobalFree(PrintDlg.hDevMode)
    PrintDlg.hDevMode = 0
    
    If ll_PrintResult <> 0 Then

        NewPrinterName = right(DevName.extra, Len(DevName.extra) - (DevName.wDeviceOffset - 8))
        NewPrinterName = Left(NewPrinterName, InStr(NewPrinterName, Chr$(0)) - 1)
        If StrComp(Printer.DeviceName, NewPrinterName, vbTextCompare) <> 0 Then
            For Each objPrinter In Printers
                ' I have no idea how to get full device name (not limited to CCHDEVICENAME - 2) even I have no idea why there must be -2 and not -1
                If StrComp(objPrinter.DeviceName, NewPrinterName, vbTextCompare) = 0 Then
                    Set Printer = objPrinter
                    Exit For
                End If
            Next
        End If

        On Error Resume Next
        'Set printer object properties according to selections made
        'by user
        Printer.Copies = DevMode.dmCopies
        Printer.Duplex = DevMode.dmDuplex
        Printer.Orientation = DevMode.dmOrientation
        Printer.PaperSize = DevMode.dmPaperSize
        Printer.PrintQuality = DevMode.dmPrintQuality
        Printer.ColorMode = DevMode.dmColor
        Printer.PaperBin = DevMode.dmDefaultSource
        al_PrintFlags = PrintDlg.flags
        ShowPrintDlg = True
        On Error GoTo 0
    End If
  Exit Function
ErrHandler:
    If PrintDlg.hDevNames <> 0 Then Call GlobalFree(PrintDlg.hDevNames)
    If PrintDlg.hDevMode <> 0 Then Call GlobalFree(PrintDlg.hDevMode)
    Call ErrorHandler("ShowPrintDlg")
End Function

Private Function InitPrinter(ByRef ao_ParentForm As Form, ByRef al_PrintFlags As Long) As Boolean

On Error GoTo ErrHandler

  InitPrinter = False
  If Printers.Count = 0 Then
    Call ShowMessage(mtNOPRINTER_INSTALLED, "#No printer is installed on your computer. Please, contact your system administrator.")
    Exit Function
  End If

  Printer.Font = mo_PrintFont
  Printer.Font.Charset = UserControl.Font.Charset
  Printer.Orientation = 1
  Printer.ScaleMode = vbTwips
  If Not ShowPrintDlg(ao_ParentForm, al_PrintFlags) Then Exit Function
  InitPrinter = True
  Exit Function
ErrHandler:
    Call ErrorHandler("InitPrinter")
End Function

Private Function GetPrintAreaInfo() As PRINTER_INFO
Dim lt_PrinterInfo As PRINTER_INFO
      
On Error GoTo ErrHandler
    'Printer.ScaleHeight = Printer.Height
    'Printer.ScaleWidth = Printer.Width
    
    ' TotalPrtAreaVert is eqivalent to Printer.ScaleHeight
    'lt_PrinterInfo.TotalPrtAreaVert = GetDeviceCaps(Printer.hdc, VERTRES) * Printer.TwipsPerPixelY
    'lt_PrinterInfo.TotalHeight = GetDeviceCaps(Printer.hdc, PHYSICALHEIGHT) * Printer.TwipsPerPixelY
    'lt_PrinterInfo.MarginTop = GetDeviceCaps(Printer.hdc, PHYSICALOFFSETY) * Printer.TwipsPerPixelY
    'lt_PrinterInfo.MarginBottom = lt_PrinterInfo.TotalHeight - lt_PrinterInfo.TotalPrtAreaVert - lt_PrinterInfo.MarginTop
    ' TotalPrtAreaHorz is eqivalent to Printer.ScaleWidth
    'lt_PrinterInfo.TotalPrtAreaHorz = GetDeviceCaps(Printer.hdc, HORZRES) * Printer.TwipsPerPixelX
    'lt_PrinterInfo.TotalWidth = GetDeviceCaps(Printer.hdc, PHYSICALWIDTH) * Printer.TwipsPerPixelX
    'lt_PrinterInfo.MarginLeft = GetDeviceCaps(Printer.hdc, PHYSICALOFFSETX) * Printer.TwipsPerPixelX
    'lt_PrinterInfo.MarginRight = lt_PrinterInfo.TotalWidth - lt_PrinterInfo.TotalPrtAreaHorz - lt_PrinterInfo.MarginLeft
    
    lt_PrinterInfo.TotalHeight = Printer.ScaleHeight
    lt_PrinterInfo.MarginTop = 0.75 * 1440
    lt_PrinterInfo.TotalPrtAreaVert = Printer.ScaleHeight - Printer.TwipsPerPixelY - 2 * lt_PrinterInfo.MarginTop
    lt_PrinterInfo.MarginBottom = lt_PrinterInfo.TotalHeight - lt_PrinterInfo.TotalPrtAreaVert - lt_PrinterInfo.MarginTop
    
    lt_PrinterInfo.TotalWidth = Printer.ScaleWidth
    lt_PrinterInfo.MarginLeft = 0.5 * 1440
    lt_PrinterInfo.TotalPrtAreaHorz = Printer.ScaleWidth - Printer.TwipsPerPixelX - 2 * lt_PrinterInfo.MarginLeft
    lt_PrinterInfo.MarginRight = lt_PrinterInfo.TotalWidth - lt_PrinterInfo.TotalPrtAreaHorz - lt_PrinterInfo.MarginLeft
    
    lt_PrinterInfo.CellMarginX = Printer.TextWidth("i")
    lt_PrinterInfo.CellMarginY = Printer.TextHeight("I") * 0.03
    lt_PrinterInfo.CellHeight = Printer.TextHeight("I") + 2 * lt_PrinterInfo.CellMarginY
    GetPrintAreaInfo = lt_PrinterInfo
    Exit Function
ErrHandler:
    Call ErrorHandler("GetPrintAreaInfo")
End Function

Private Function GetColumnMaxPrintWidth(ByVal al_hDC As Long, ByVal ao_Column As ArmColumn) As Long
Dim ll_Result As Long, ll_Count As Long, ll_Index As Long
Dim ll_ColMaxWidth As Long, ll_Width As Long
Dim ls_Text As String
Dim lt_TextSize As POINTAPI
Dim lo_Pic As StdPicture

On Error GoTo ErrHandler
    If Not (ao_Column Is Nothing) Then
        ll_ColMaxWidth = Printer.TextWidth(ao_Column.Title)
        ll_Count = Rows
        If ao_Column.FieldType = DBTYPE_BMP Then
            ' check width of all images and find the widest
            For ll_Index = 0 To ll_Count - 1
              ls_Text = Trim(ao_Column.GetData(ll_Index))
              If ls_Text <> "" Then
                  Set lo_Pic = LoadResPicture(ls_Text, vbResBitmap)
                  If lo_Pic.Width > ll_ColMaxWidth Then
                      ll_ColMaxWidth = lo_Pic.Width
                  End If
              End If
            Next ll_Index
        Else
            For ll_Index = 0 To ll_Count - 1
                ls_Text = ao_Column.GetData(ll_Index)
                ll_Width = Printer.TextWidth(ls_Text)
                If ll_Width > ll_ColMaxWidth Then
                    ll_ColMaxWidth = ll_Width
                End If
            Next ll_Index
        End If
    End If
    GetColumnMaxPrintWidth = ll_ColMaxWidth
    Exit Function
ErrHandler:
    Call ErrorHandler("GetColumnMaxPrintWidth")
End Function


Public Sub PrintGrid(ByRef ao_ParentForm As Form)
Dim ll_Index As Long, ll_ColumnIndex As Long
Dim ll_Result As Long, ll_Count As Long
Dim lt_PrinterInfo As PRINTER_INFO
Dim ll_PrintLeft As Long
Dim ll_PageX As Long, ll_PageY As Long, ll_RowsPerPage As Long
Dim ll_StartRow As Long, ll_EndRow As Long, ll_PageStartRow As Long, ll_PageEndRow As Long
Dim lo_Column As ArmColumn
Dim lo_PreviousColumn As ArmColumn
Dim lb_SelectedOnly As Boolean, lb_GridFitToOnePageWidth As Boolean
Dim ll_PrintFlags As Long, ll_TotalColumnWidth As Long

On Error GoTo ErrHandler
    If (Cols = 0) Or (Rows = 0) Then Exit Sub
    
    Call SetMousePointer(False)
    
    ll_PrintFlags = IIf(SelectedCount <= 0, PD_NOSELECTION, 0)
    If Not InitPrinter(ao_ParentForm, ll_PrintFlags) Then
        Call SetMousePointer(True)
        Exit Sub
    End If
    
    lb_SelectedOnly = ll_PrintFlags And PD_SELECTION
    
    lt_PrinterInfo = GetPrintAreaInfo
    
    'calculate column position on the page
    ll_Count = Cols
    ll_TotalColumnWidth = 0
    For ll_Index = 0 To ll_Count - 1
        Set lo_Column = Columns(ll_Index)
        If Not (lo_Column Is Nothing) Then
            If lo_Column.Width > 0 Then
                ll_TotalColumnWidth = ll_TotalColumnWidth + lo_Column.Width
            End If
        End If
    Next
    
    'calculate column position on the page
    ll_PrintLeft = lt_PrinterInfo.MarginLeft
    ll_PageX = 1
    ll_Count = Cols
    lb_GridFitToOnePageWidth = True
    Set lo_PreviousColumn = Nothing
    For ll_Index = 0 To ll_Count - 1
        Set lo_Column = Columns(ll_Index)
        If Not (lo_Column Is Nothing) Then
        
            Select Case mt_ColumnWidthPrintMode
            Case cwAutoFitToColumn
                If lo_Column.Width <= 0 Then
                    lo_Column.PrintWidth = 0
                Else
                    lo_Column.PrintWidth = GetColumnMaxPrintWidth(Printer.hdc, lo_Column) + 2 * lt_PrinterInfo.CellMarginX
                    lo_Column.PrintWidth = IIf(lo_Column.PrintWidth > lt_PrinterInfo.TotalPrtAreaHorz, lt_PrinterInfo.TotalPrtAreaHorz, lo_Column.PrintWidth)
                End If
            Case cwAutoFitToPage
                If (lo_Column.Width <= 0) Or (ll_TotalColumnWidth <= 0) Then
                    lo_Column.PrintWidth = 0
                Else
                    lo_Column.PrintWidth = lt_PrinterInfo.TotalPrtAreaHorz * lo_Column.Width \ ll_TotalColumnWidth
                End If
            Case Else
                'manual setting of lo_Column.PrintWidth by LoadConstats function
            End Select
            
            
            'move column to the next page if over right margin
            If (ll_PrintLeft + lo_Column.PrintWidth) > (lt_PrinterInfo.TotalWidth - lt_PrinterInfo.MarginRight) Then
                ll_PrintLeft = lt_PrinterInfo.MarginLeft
                ll_PageX = ll_PageX + 1
                lb_GridFitToOnePageWidth = False
                'update width of previous column to fit on page width
                If Not (lo_PreviousColumn Is Nothing) Then
                    'lo_PreviousColumn.PrintWidth = lt_PrinterInfo.MarginRight - lo_PreviousColumn.PrintLeft
                End If
            End If
            
            lo_Column.PrintPage = ll_PageX
            lo_Column.PrintLeft = ll_PrintLeft
            ll_PrintLeft = ll_PrintLeft + lo_Column.PrintWidth
            If lo_Column.PrintWidth > 0 Then
                Set lo_PreviousColumn = lo_Column
            End If
        End If
    Next
    ' one row is reserved for column header so substract one
    ll_RowsPerPage = (lt_PrinterInfo.TotalPrtAreaVert \ lt_PrinterInfo.CellHeight) - 1
    If lb_SelectedOnly Then
        ll_StartRow = GetSelectedRowIndex(0)
        ll_EndRow = GetSelectedRowIndex(SelectedCount - 1)
    Else
        ll_StartRow = 0
        ll_EndRow = Rows - 1
    End If
    ll_PageStartRow = ll_StartRow
    ll_PageEndRow = ll_StartRow + ll_RowsPerPage - 1
    If ll_PageEndRow > ll_EndRow Then ll_PageEndRow = ll_EndRow
    ll_PageY = 1
    While ll_PageStartRow <= ll_PageEndRow
        ll_PageX = 1
        If ll_PageY > 1 Then Call Printer.NewPage
        Call PrintPageNumber(lt_PrinterInfo, ll_PageY, ll_PageX, lb_GridFitToOnePageWidth)
        ll_Count = Cols
        For ll_ColumnIndex = 0 To ll_Count - 1
            Set lo_Column = Columns(ll_ColumnIndex)
            If Not (lo_Column Is Nothing) Then
                If lo_Column.PrintWidth > 0 Then
                    If ll_PageX <> lo_Column.PrintPage Then
                        Call Printer.NewPage
                        ll_PageX = lo_Column.PrintPage
                        Call PrintPageNumber(lt_PrinterInfo, ll_PageY, ll_PageX, lb_GridFitToOnePageWidth)
                    End If
                    Call PrintColumn(lo_Column, ll_PageStartRow, ll_PageEndRow, lt_PrinterInfo, True)
                End If
            End If
        Next ll_ColumnIndex
        ll_PageStartRow = ll_PageStartRow + ll_RowsPerPage
        ll_PageEndRow = ll_PageStartRow + ll_RowsPerPage - 1
        If ll_PageEndRow > ll_EndRow Then ll_PageEndRow = ll_EndRow
        ll_PageY = ll_PageY + 1
    Wend
    Call Printer.EndDoc
    Call SetMousePointer(True)
    Exit Sub
ErrHandler:
    Printer.KillDoc
    Call SetMousePointer(True)
    Call MsgBox("#Print error: " & Err.Source & " : " & Err.Description & " Nr.: " & Err.Number)
End Sub

Private Sub PrintPageNumber(ByRef at_PrinterInfo As PRINTER_INFO, ByVal al_PageY As Long, ByVal al_PageX As Long, ByVal ab_GridFitToOnePageWidth As Boolean)
Dim ls_Text As String

On Error GoTo ErrHandler
    ls_Text = Str(al_PageY)
    ' if grid do not fit to one page horizontaly, then also also horizontal page position to page number
    If Not ab_GridFitToOnePageWidth Then
        ls_Text = ls_Text & " - " & al_PageX
    End If
    Printer.CurrentX = at_PrinterInfo.TotalWidth / 2 - Printer.TextWidth(ls_Text) / 2
    Printer.CurrentY = at_PrinterInfo.MarginTop + at_PrinterInfo.TotalPrtAreaVert + at_PrinterInfo.CellMarginY
    Printer.Print ls_Text
    Exit Sub
ErrHandler:
    Call ErrorHandler("PrintPageNumber")
End Sub

Private Sub PrintCell(ByVal al_RowIndex As Long, ByVal al_Border As Long, ByVal ao_Column As ArmColumn, ByVal al_CellIndex As Long, ByRef at_PrinterInfo As PRINTER_INFO)
Dim ll_X As Long, ll_Y As Long
Dim lt_Rect As RECT
Dim lo_Pic As StdPicture
Dim la_Text() As Byte
Dim ls_Text As String

On Error GoTo ErrHandler
    ll_X = ao_Column.PrintLeft
    ll_Y = at_PrinterInfo.MarginTop + (al_CellIndex * at_PrinterInfo.CellHeight)
    
    If al_Border And BORDER_LEFT Then
        Printer.Line (ll_X, ll_Y)-(ll_X, ll_Y + at_PrinterInfo.CellHeight)
    End If
    If al_Border And BORDER_TOP Then
        Printer.Line (ll_X, ll_Y)-(ll_X + ao_Column.PrintWidth, ll_Y)
    End If
    If al_Border And BORDER_RIGHT Then
        Printer.Line (ll_X + ao_Column.PrintWidth, ll_Y)-(ll_X + ao_Column.PrintWidth, ll_Y + at_PrinterInfo.CellHeight)
    End If
    If al_Border And BORDER_BOTTOM Then
        Printer.Line (ll_X, ll_Y + at_PrinterInfo.CellHeight)-(ll_X + ao_Column.PrintWidth, ll_Y + at_PrinterInfo.CellHeight)
    End If
        
    If al_RowIndex >= 0 Then
        ls_Text = ao_Column.GetData(al_RowIndex)
    Else
        ls_Text = ao_Column.Title
    End If
    
    If (ao_Column.FieldType = DBTYPE_BMP) And (al_RowIndex >= 0) Then
        ls_Text = Trim(ls_Text)
        If ls_Text <> "" Then
            Set lo_Pic = LoadResPicture(ls_Text, vbResBitmap)
            Call Printer.PaintPicture(lo_Pic, ll_X + ao_Column.PrintWidth / 2 - Printer.ScaleX(lo_Pic.Width, vbHimetric, vbTwips) / 2, ll_Y + at_PrinterInfo.CellHeight / 2 - Printer.ScaleY(lo_Pic.Height, vbHimetric, vbTwips) / 2, , , 0, 0, ao_Column.PrintWidth, at_PrinterInfo.CellHeight)
            Set lo_Pic = Nothing
        End If
    Else
        'this do not support clip and alignment, must be rewritten
        'Printer.CurrentX = ll_X + at_PrinterInfo.CellMarginX
        'Printer.CurrentY = ll_Y + at_PrinterInfo.CellMarginY
        'Printer.Print as_text
        lt_Rect.Left = Printer.ScaleX(ll_X + at_PrinterInfo.CellMarginX, vbTwips, vbPixels)
        lt_Rect.Top = Printer.ScaleY(ll_Y + at_PrinterInfo.CellMarginY, vbTwips, vbPixels)
        lt_Rect.right = Printer.ScaleX(ll_X + ao_Column.PrintWidth - at_PrinterInfo.CellMarginX, vbTwips, vbPixels)
        lt_Rect.bottom = Printer.ScaleY(ll_Y + at_PrinterInfo.CellHeight - at_PrinterInfo.CellMarginY, vbTwips, vbPixels)
        If ls_Text <> "" Then
          If ((ao_Column.FieldType = DBTYPE_STR) Or (al_RowIndex < 0)) And (ml_LocalID <> 0) Then
            'print in unicode
            la_Text = StrConv(StrConv(ls_Text, vbFromUnicode, 1033), vbUnicode, ml_LocalID)
            Call DrawTextW(Printer.hdc, la_Text(0), Len(ls_Text), lt_Rect, ao_Column.PrintAlignment)
          Else
            Call DrawTextA(Printer.hdc, ls_Text, Len(ls_Text), lt_Rect, ao_Column.PrintAlignment)
          End If
        End If
    End If
    Exit Sub
ErrHandler:
    Set lo_Pic = Nothing
    Call ErrorHandler("PrintCell")
End Sub

Private Sub PrintColumn(ByVal ao_Column As ArmColumn, ByVal al_StartRow As Long, ByVal al_EndRow As Long, ByRef at_PrinterInfo As PRINTER_INFO, ByVal ab_PrintTitle As Boolean)
Dim ll_Index As Long
Dim ll_CellIndex As Long

On Error GoTo ErrHandler
    ll_CellIndex = 0
    If ab_PrintTitle Then
        Call PrintCell(-1, BORDER_OUTLINE, ao_Column, ll_CellIndex, at_PrinterInfo)
        ll_CellIndex = ll_CellIndex + 1
    End If
    
    For ll_Index = al_StartRow To al_EndRow
        Call PrintCell(ll_Index, BORDER_OUTLINE, ao_Column, ll_CellIndex, at_PrinterInfo)
        ll_CellIndex = ll_CellIndex + 1
    Next ll_Index
    Exit Sub
ErrHandler:
    Call ErrorHandler("PrintColumn")
End Sub

Private Sub ErrorHandler(ByVal as_Fct As String)
    Err.Raise Err.Number, as_Fct & SEP1 & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext
End Sub

Public Property Get PrintFont() As StdFont
  
  Set PrintFont = mo_PrintFont

End Property

Public Property Get PrintMode() As ColumnWidthPrintMode
  
  PrintMode = mt_ColumnWidthPrintMode

End Property

Public Property Let PrintMode(at_ColumnWidthPrintMode As ColumnWidthPrintMode)
  
 mt_ColumnWidthPrintMode = at_ColumnWidthPrintMode

End Property

