I started adding to the keyboard handler, cursor keys move around and it supports _KeyHit and _Button methods.
Next it needs to track "edit mode" and let the user type in a value & exit when they hit Tab or Enter (or Esc to cancel editing).
Need to figure out how to highlight the cell and show the blinking cursor while editing.
Updated with a "todo" list of features needed to make this useful (when you think about it, Excel really does a lot!):
Next it needs to track "edit mode" and let the user type in a value & exit when they hit Tab or Enter (or Esc to cancel editing).
Need to figure out how to highlight the cell and show the blinking cursor while editing.
Updated with a "todo" list of features needed to make this useful (when you think about it, Excel really does a lot!):
Code: (Select All)
' ################################################################################################################################################################
' QB64PE SPREADSHEET
' There is value in this being a reusable programmable datagrid type control
' you can use in your own QB64 programs.
' MOSTLY BY UNSEEN MACHINE, ORIGINAL CODE AT:
' https://qb64phoenix.com/forum/showthread.php?tid=4417&pid=39358#pid39358
' SOME MODS BY MADSCIJR (STARTED IMPLEMENTING KEYBOARD INPUT)
' -----------------------------------------------------------------------------
' TODO Phase 1
' -----------------------------------------------------------------------------
' Editing:
' When user clicks on a cell, remember the column number as temporary first column
' When user presses F2 or double-clicks a cell, enter edit mode:
' - highlight the cell
' - let user type values or a formula in the cell
' - show a blinking cursor as they type
' - if user presses Tab key, save value to cell and move celection to next cell to the right
' - if user presses Enter key, save value to cell and move selection down 1 row, starting at temporary first column
' - if user presses Esc key, exit edit mode - cancel editing and do not save value to cell (revert cell to old value)
' but don't let Esc key inadvertantly quit the program (user must release key before they can press it again to quit)
' Moving around:
' Add horizontal and vertical scroll bars to move around sheet
' for editing sheets larger than the screen.
' Support a large number of rows/columns comparable to Excel
' Clipboard:
' Support cut, copy & paste to/from clipboard
' - support standard Ctrl-x, Ctrl-c, Ctrl-v keyboard shortcuts
' - Copy should copy the cells to clipboard as a standard grid
' (like copying from Excel or Word or an HTML table)
' which can be pasted into Word, OneNote or other rich text editor that supports pasting from Excel or HTML
' - or to Notepad as tab-delimited text
' - Paste should copy from the clipboard to the cells
' (like copying from an HTML or Word table or tab-delimited text into Excel)
' Simple math operations:
' Support simple formulas with basic math operations (add, subtract, multiply, divide)
' See Petr's implementation at https://qb64phoenix.com/forum/showthread.php?tid=4417&pid=39353#pid39353
' String concatenation operations:
' - Concatenate string data, Excel-like cell references like A1 or $A$1.
' Load + Save
' Implement saving/loading a spreadsheet to common formats:
' ODS (OpenOffice and LibreOffice), XLSX, tab-delimited, CSV, XML, JSON
' -----------------------------------------------------------------------------
' TODO Phase 2
' -----------------------------------------------------------------------------
' Support absolute or relative cell references for copying cells with operations
' TYPE IF CELL CONTAINS AND IS COPIED TO THE COPY WOULD BE
' Relative reference B3 =A2 C4 =B3
' Absolute reference B3 =$A$2 C4 =$A$2
' Partially relative B3 =$A2 C4 =$A3
' Partially relative B3 =A$2 C4 =B$2
' -----------------------------------------------------------------------------
' TODO Phase 3
' -----------------------------------------------------------------------------
' Cell formatting:
' Support ability to format a cell using Excel's cell formatting syntax for
' - # of decimal places for numbers
' - 1000 separator
' - date format (m, mm, mmm, mmmm, d, dd, ddd, dddd, y, yy, yyyy, h, hh, mm, ss, AM/PM
' Call formulas defined in the QB64PE program:
' - Formulas are QB64PE functions
' - formula name is just the name of the QB64PE function
' - formula can pass in a cell address in notation such a A2
' or absolute notation such as $A2 or A$2 or $A$2
' or a range of cells in notation A2:C4
' - Implement a basic =SUM formula, e.g., =SUM(A2:A10) would add the values
' -----------------------------------------------------------------------------
' TODO Phase 4 + beyond
' -----------------------------------------------------------------------------
' Multiple sheets?
' Support adding multiple sheets in a document.
' Named ranges
' Support associating a cell address or range of cells with a name
' and referencing it from within another cell or formula.
' Ability to remove or edit a name or the associated address.
' Ability to reference & work with from within a QB64PE function or sub.
' Dropdown controls
' Support defining a dropdown control in a cell (or range) that pulls its values
' from a given range of cells on a given sheet.
' Formula implementation?
' For the formulas we could go a few different ways
' * Use a big CASE statement to redirect to known / supported functions.
' * Support some kind of "eval" function that passes the parameters to the desired function,
' which is a security risk, so only support functions that are safe security-wise
' (basically no networking)
' * Support QB64-script type parser to allow defining functions inside the data
' to save with the sheet. This is non-trivial but Fellippe wrote one already
' that can be repurposed at
' https://github.com/FellippeHeitor/QB64-interpreter
' -----------------------------------------------------------------------------
' HOW TO RUN
' -----------------------------------------------------------------------------
' REQUIRES "QB_Sheets.h" IN SAME FOLDER AS THIS FILE TO COMPILE
' ################################################################################################################################################################
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' CONSTANTS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' KEY CODE INDEX CONSTANTS
Const cKeyNone = 0
Const cKeyEsc = 1
Const cKeyF1 = 2
Const cKeyF2 = 3
Const cKeyF3 = 4
Const cKeyF4 = 5
Const cKeyF5 = 6
Const cKeyF6 = 7
Const cKeyF7 = 8
Const cKeyF8 = 9
Const cKeyF9 = 10
Const cKeyF10 = 11
Const cKeyF11 = 12
Const cKeyF12 = 13
Const cKeyBackTick = 14
Const cKey1 = 15
Const cKey2 = 16
Const cKey3 = 17
Const cKey4 = 18
Const cKey5 = 19
Const cKey6 = 20
Const cKey7 = 21
Const cKey8 = 22
Const cKey9 = 23
Const cKey0 = 24
Const cKeyMinus = 25
Const cKeyEqual = 26
Const cKeyBackspace = 27
Const cKeyTilde = 28
Const cKeyExclamation = 29
Const cKeyAt = 30
Const cKeyPound = 31
Const cKeyDollar = 32
Const cKeyPercent = 33
Const cKeyCaret = 34
Const cKeyAmpersand = 35
Const cKeyAsterisk = 36
Const cKeyParenOpen = 37
Const cKeyParenClose = 38
Const cKeyUnderscore = 39
Const cKeyPlus = 40
Const cKeyInsert = 41
Const cKeyHome = 42
Const cKeyPageUp = 43
Const cKeyDelete = 44
Const cKeyEnd = 45
Const cKeyPageDown = 46
Const cKeyTab = 47
Const cKeyCapsLock = 48
Const cKeyPrintScreen = 49
Const cKeyScrollLock = 50
Const cKeyPauseBreak = 51
Const cKeyEnter = 52
Const cKeySquareOpen = 53
Const cKeySquareClose = 54
Const cKeyBackSlash = 55
Const cKeyCurlyOpen = 56
Const cKeyCurlyClose = 57
Const cKeyPipe = 58
Const cKeyComma = 59
Const cKeyPeriod = 60
Const cKeySlash = 61
Const cKeyLt = 62
Const cKeyGt = 63
Const cKeyQuestion = 64
Const cKeySemicolon = 65
Const cKeyApostrophe = 66
Const cKeyColon = 67
Const cKeyQuote = 68
Const cKeyShiftLeft = 69
Const cKeyShiftRight = 70
Const cKeyCtrlLeft = 71
Const cKeyCtrlRight = 72
Const cKeyAltLeft = 73
Const cKeyAltRight = 74
Const cKeyWinLeft = 75
Const cKeyWinRight = 76
Const cKeyMenu = 77
Const cKeySpace = 78
Const cKeyLeftArrow = 79
Const cKeyUpArrow = 80
Const cKeyDownArrow = 81
Const cKeyRightArrow = 82
Const cKeyNumLock = 83
Const cKeyA = 84
Const cKeyB = 85
Const cKeyC = 86
Const cKeyD = 87
Const cKeyE = 88
Const cKeyF = 89
Const cKeyG = 90
Const cKeyH = 91
Const cKeyI = 92
Const cKeyJ = 93
Const cKeyK = 94
Const cKeyL = 95
Const cKeyM = 96
Const cKeyN = 97
Const cKeyO = 98
Const cKeyP = 99
Const cKeyQ = 100
Const cKeyR = 101
Const cKeyS = 102
Const cKeyT = 103
Const cKeyU = 104
Const cKeyV = 105
Const cKeyW = 106
Const cKeyX = 107
Const cKeyY = 108
Const cKeyZ = 109
Const cKeyUpperA = 110
Const cKeyUpperB = 111
Const cKeyUpperC = 112
Const cKeyUpperD = 113
Const cKeyUpperE = 114
Const cKeyUpperF = 115
Const cKeyUpperG = 116
Const cKeyUpperH = 117
Const cKeyUpperI = 118
Const cKeyUpperJ = 119
Const cKeyUpperK = 120
Const cKeyUpperL = 121
Const cKeyUpperM = 122
Const cKeyUpperN = 123
Const cKeyUpperO = 124
Const cKeyUpperP = 125
Const cKeyUpperQ = 126
Const cKeyUpperR = 127
Const cKeyUpperS = 128
Const cKeyUpperT = 129
Const cKeyUpperU = 130
Const cKeyUpperV = 131
Const cKeyUpperW = 132
Const cKeyUpperX = 133
Const cKeyUpperY = 134
Const cKeyUpperZ = 135
Const cKeypadSlash = 136
Const cKeypadMultiply = 137
Const cKeypadMinus = 138
Const cKeypad7Home = 139
Const cKeypad8Up = 140
Const cKeypad9PgUp = 141
Const cKeypadPlus = 142
Const cKeypad4Left = 143
Const cKeypad5 = 144
Const cKeypad6Right = 145
Const cKeypad1End = 146
Const cKeypad2Down = 147
Const cKeypad3PgDn = 148
Const cKeypadEnter = 149
Const cKeypad0Ins = 150
Const cKeypadPeriodDel = 151
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' TYPES
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Type MouseState
X As Long: Y As Long: B1 As _Byte: Wheel As Long
End Type
' Single-Cell Messenger UDT
' This is used to "fetch" and "commit" all properties of one cell at once.
Type CellProperties
Value As String: IntValue As _Integer64: DblValue As Double
BG As _Unsigned Long: FG As _Unsigned Long: Locked As _Byte: VarType As _Byte
End Type
Type GridView
Handle As _Offset
TopRow As Long: LeftCol As Long
SelR1 As Long: SelC1 As Long: SelR2 As Long: SelC2 As Long
IsDragging As _Byte
End Type
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' LIBRARY
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Declare Library "QB_Sheets"
' Lifecycle Management
Function Sheet_New%& Alias "QBS_New" (ByVal r As Long, ByVal c As Long)
Sub Sheet_Free Alias "QBS_Free" (ByVal h As _Offset)
' Persistence (Binary Save/Load)
Function Sheet_Save& Alias "QBS_Save" (ByVal h As _Offset, filename As String)
Function Sheet_Load%& Alias "QBS_Load" (filename As String)
' Data Setters (Individual)
Sub Sheet_SetSTR Alias "QBS_SetStr" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long, v As String)
Sub Sheet_SetINT Alias "QBS_SetInt" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long, ByVal v As _Integer64)
Sub Sheet_SetDBL Alias "QBS_SetDbl" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long, ByVal v As Double)
' Data Getters (Individual)
Function Sheet_GetSTR$ Alias "QBS_GetStr" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long)
Function Sheet_GetINT& Alias "QBS_GetInt" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long)
Function Sheet_GetDBL# Alias "QBS_GetDbl" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long)
' Metadata Fetching
' Note: Pass _OFFSET(CellPropertiesVariable.BG) to fill the UDT instantly from C++
Sub Sheet_GetInfo Alias "QBS_GetInfo" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long, ByVal infoPtr As _Offset)
' Formatting and Physical Layout
Sub Sheet_Format Alias "QBS_Format" (ByVal h As _Offset, ByVal r As Long, ByVal c As Long, ByVal bg As _Unsigned Long, ByVal fg As _Unsigned Long, ByVal lock As Long)
Sub Sheet_Size Alias "QBS_Size" (ByVal h As _Offset, ByVal idx As Long, ByVal size As Long, ByVal isRow As Long)
Function Sheet_GetSize% Alias "QBS_GetSize" (ByVal h As _Offset, ByVal idx As Long, ByVal isRow As Long)
End Declare
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' GLOBALS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dim Shared Mouse As MouseState, OldMouse As MouseState
Dim Shared MainGrid As GridView
Dim Shared arrKeyHitDown(1 To 151) As Integer ' _KEYHIT key down event codes
Dim Shared arrKeyHitUp(1 To 151) As Integer ' _KEYHIT key up event codes
Dim Shared arrButton(1 To 151) As Integer ' _BUTTON key codes
Dim Shared arrKeyState(1 To 151) As Integer ' tracks keydown and keyup for _BUTTON
Dim Shared ClickedColumn As Long: ClickedColumn = 0 ' remember column user clicks on, when they hit Enter, move down 1 row to this column
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' QB64 Spreadsheet Core
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' Initialize
'$Dynamic
Screen _NewImage(1024, 768, 32)
_Title "QB_Sheets Core Engine"
InitKeyCodes
MainGrid.Handle = Sheet_New(10000, 100)
' Populate some test data
For i = 0 To 50: Sheet_SetSTR MainGrid.Handle, i, 0, "Item" + Str$(i): Next i
' ================================================================================================================================================================
' MAIN LOOP
' ================================================================================================================================================================
Do
_Limit 60
HandleGridInput MainGrid
Cls '_RGB32(50, 50, 50)
DrawGridWithHeaders MainGrid, 0, 0, _Width, _Height
_Display
Loop Until _KeyDown(27)
Sheet_Free MainGrid.Handle
System
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' SUBS
' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
' /////////////////////////////////////////////////////////////////////////////
Sub HandleGridInput (G As GridView)
' --- Header Constants (Must match your DrawGridWithHeaders) ---
Dim hdrW As Integer: hdrW = 40 ' Width of Row labels
Dim hdrH As Integer: hdrH = 25 ' Height of Col labels
Dim r As Long, c As Long
' Update mouse input
OldMouse = Mouse
Do While _MouseInput
Mouse.X = _MouseX: Mouse.Y = _MouseY: Mouse.B1 = _MouseButton(1)
Mouse.Wheel = Mouse.Wheel + _MouseWheel
Loop
' Check if mouse is actually inside the data area (not on headers)
If Mouse.X >= hdrW And Mouse.Y >= hdrH Then
' Adjust mouse coordinates to Grid-Space
' We subtract the header offsets so GetCellAtMouse sees the "0,0" of the data
GetCellAtMouse G, Mouse.X - hdrW, Mouse.Y - hdrH, r, c
' Start Select
If Mouse.B1 And Not OldMouse.B1 Then
G.SelR1 = r: G.SelC1 = c
G.SelR2 = r: G.SelC2 = c
G.IsDragging = -1
End If
' Drag Select
If Mouse.B1 And G.IsDragging Then
G.SelR2 = r: G.SelC2 = c
End If
ElseIf Mouse.B1 And Not OldMouse.B1 Then
' User clicked in the header area
' TIP: You could add logic here to select an entire Row or Column
End If
' End Drag (Always reset regardless of where the mouse is)
If Not Mouse.B1 Then G.IsDragging = 0
' Scroll (Mouse wheel works anywhere on the window)
If Mouse.Wheel <> OldMouse.Wheel Then
G.TopRow = G.TopRow - (Mouse.Wheel - OldMouse.Wheel)
If G.TopRow < 0 Then G.TopRow = 0
' Reset wheel delta so it doesn't accumulate
Mouse.Wheel = 0: OldMouse.Wheel = 0
End If
' Keyboard input
HandleKeyboard G
End Sub ' HandleGridInput
' /////////////////////////////////////////////////////////////////////////////
Sub DrawGrid (G As GridView, x1, y1, x2, y2)
' --- Move all DIMS to start ---
Dim curY As Long, curX As Long
Dim r As Long, c As Long
Dim rowH As Integer, colW As Integer
Dim Prop As CellProperties
curY = y1
r = G.TopRow
Do While curY < y2 And r < 10000
rowH = Sheet_GetSize(G.Handle, r, 1)
curX = x1
c = G.LeftCol
Do While curX < x2 And c < 100
colW = Sheet_GetSize(G.Handle, c, 0)
' Fetch metadata (BG, FG, Lock, VarType)
Sheet_GetInfo G.Handle, r, c, _Offset(Prop.BG)
' Draw Cell Background and Border
Line (curX, curY)-(curX + colW - 1, curY + rowH - 1), Prop.BG, BF
Line (curX, curY)-(curX + colW - 1, curY + rowH - 1), _RGB32(200, 200, 200), B
' Selection Overlay
If IsInRange(r, c, G.SelR1, G.SelC1, G.SelR2, G.SelC2) Then
Line (curX, curY)-(curX + colW - 1, curY + rowH - 1), _RGB32(0, 120, 215, 80), BF
End If
Color Prop.FG, Prop.BG
' Draw Text Content
If Prop.VarType > 0 Then
_PrintString (curX + 4, curY + (rowH / 2 - 8)), Sheet_GetSTR$(G.Handle, r, c)
End If
curX = curX + colW
c = c + 1
Loop
curY = curY + rowH
r = r + 1
Loop
End Sub ' DrawGrid
' /////////////////////////////////////////////////////////////////////////////
Sub GetCellAtMouse (G As GridView, mx, my, outR As Long, outC As Long)
' --- Move all DIMS to start ---
Dim curX As Long, curY As Long
Dim w As Integer, h As Integer
' Find Column
curX = 0 ' Assuming grid starts at 0, adjust if x1 > 0
outC = G.LeftCol
Do
w = Sheet_GetSize(G.Handle, outC, 0)
If mx >= curX And mx < curX + w Then Exit Do
curX = curX + w
outC = outC + 1
' Safety break to prevent infinite loops on 0-width or off-screen
If outC > 16384 Or curX > _Width Then Exit Do
Loop
' Find Row
curY = 0
outR = G.TopRow
Do
h = Sheet_GetSize(G.Handle, outR, 1)
If my >= curY And my < curY + h Then Exit Do
curY = curY + h
outR = outR + 1
If outR > 1000000 Or curY > _Height Then Exit Do
Loop
End Sub ' GetCellAtMouse
' /////////////////////////////////////////////////////////////////////////////
Function IsInRange (r, c, r1, c1, r2, c2)
Dim minR, maxR, minC, maxC
If r1 < r2 Then minR = r1: maxR = r2 Else minR = r2: maxR = r1
If c1 < c2 Then minC = c1: maxC = c2 Else minC = c2: maxC = c1
If r >= minR And r <= maxR And c >= minC And c <= maxC Then IsInRange = -1
End Function ' IsInRange
' /////////////////////////////////////////////////////////////////////////////
Function GetColName$ (c As Long)
Dim num As Long: num = c + 1 ' Convert 0-based index to 1-based
Dim colName As String
Dim remainder As Integer
Do While num > 0
remainder = (num - 1) Mod 26
colName = Chr$(65 + remainder) + colName
num = (num - remainder) \ 26
Loop
GetColName$ = colName
End Function ' GetColName$
' /////////////////////////////////////////////////////////////////////////////
Sub DrawGridWithHeaders (G As GridView, x1, y1, x2, y2)
Dim curY As Long, curX As Long
Dim r As Long, c As Long
Dim rowH As Integer, colW As Integer
Dim Prop As CellProperties
Dim hdrW As Integer: hdrW = 40 ' Row labels width
Dim hdrH As Integer: hdrH = 25 ' Column labels height
Color _RGB32(0, 0, 0), _RGBA32(0, 0, 0, 0)
' 1. Draw Corner Block (Intersection of headers)
Line (x1, y1)-(x1 + hdrW - 1, y1 + hdrH - 1), _RGB32(220, 220, 220), BF
Line (x1, y1)-(x1 + hdrW - 1, y1 + hdrH - 1), _RGB32(100, 100, 100), B
' 2. Draw Column Headers (A, B, C...)
curX = x1 + hdrW: c = G.LeftCol
Do While curX < x2
colW = Sheet_GetSize(G.Handle, c, 0)
Line (curX, y1)-(curX + colW - 1, y1 + hdrH - 1), _RGB32(220, 220, 220), BF
Line (curX, y1)-(curX + colW - 1, y1 + hdrH - 1), _RGB32(100, 100, 100), B
_PrintString (curX + (colW \ 2 - 4), y1 + 5), GetColName$(c)
curX = curX + colW: c = c + 1
Loop
' 3. Draw Row Headers (1, 2, 3...)
curY = y1 + hdrH: r = G.TopRow
Do While curY < y2
rowH = Sheet_GetSize(G.Handle, r, 1)
Line (x1, curY)-(x1 + hdrW - 1, curY + rowH - 1), _RGB32(220, 220, 220), BF
Line (x1, curY)-(x1 + hdrW - 1, curY + rowH - 1), _RGB32(100, 100, 100), B
_PrintString (x1 + 5, curY + 5), LTrim$(Str$(r + 1))
curY = curY + rowH: r = r + 1
Loop
' 4. Draw Main Grid Data (Shifted by hdrW and hdrH)
DrawGrid G, x1 + hdrW, y1 + hdrH, x2, y2
End Sub ' DrawGridWithHeaders
' /////////////////////////////////////////////////////////////////////////////
Sub HandleKeyboard (G As GridView)
Dim kh%
' UPDATE KEYBOARD INPUT
While _DeviceInput(1): Wend ' clear and update the keyboard buffer
' -----------------------------------------------------------------------------
' DETECT KEYS WITH _KEYHIT *** DOESN'T WORK ***
' -----------------------------------------------------------------------------
kh% = _KeyHit
' ARROW KEYS MOVE UP
If kh% = arrKeyHitDown(cKeyUpArrow) Then
If arrKeyState(cKeyUpArrow) = _FALSE Then
arrKeyState(cKeyUpArrow) = _TRUE
G.SelR1 = G.SelR1 - 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyUpArrow) Then
arrKeyState(cKeyUpArrow) = _FALSE
GoTo ClearKeyboardBuffer
End If
' ARROW KEYS MOVE DOWN
If kh% = arrKeyHitDown(cKeyDownArrow) Then
If arrKeyState(cKeyDownArrow) = _FALSE Then
arrKeyState(cKeyDownArrow) = _TRUE
G.SelR1 = G.SelR1 + 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyDownArrow) Then
arrKeyState(cKeyDownArrow) = _FALSE
GoTo ClearKeyboardBuffer
End If
' ARROW KEYS MOVE LEFT
If kh% = arrKeyHitDown(cKeyLeftArrow) Then
If arrKeyState(cKeyLeftArrow) = _FALSE Then
arrKeyState(cKeyLeftArrow) = _TRUE
G.SelC1 = G.SelC1 - 1: G.SelC2 = G.SelC1
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyLeftArrow) Then
arrKeyState(cKeyLeftArrow) = _FALSE
GoTo ClearKeyboardBuffer
End If
' ARROW KEYS MOVE RIGHT
If kh% = arrKeyHitDown(cKeyRightArrow) Then
If arrKeyState(cKeyRightArrow) = _FALSE Then
arrKeyState(cKeyRightArrow) = _TRUE
G.SelC1 = G.SelC1 + 1: G.SelC2 = G.SelC1
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyRightArrow) Then
arrKeyState(cKeyRightArrow) = _FALSE
GoTo ClearKeyboardBuffer
End If
' ENTER SHOULD END DATA ENTRY MODE AND MOVE TO NEXT LINE
If kh% = arrKeyHitDown(cKeyEnter) Then
If arrKeyState(cKeyEnter) = _FALSE Then
arrKeyState(cKeyEnter) = _TRUE
'CloseEditMode(G)
'Sheet_SetSTR MainGrid.Handle, G.SelR1, G.SelC1, "Enter"
G.SelR1 = G.SelR1 + 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyEnter) Then
arrKeyState(cKeyEnter) = _FALSE
GoTo ClearKeyboardBuffer
End If
' F2 ENTERS EDIT MODE
If kh% = arrKeyHitDown(cKeyF2) Then
If arrKeyState(cKeyF2) = _FALSE Then
arrKeyState(cKeyF2) = _TRUE
'TriggerEditMode(G)
Sheet_SetSTR MainGrid.Handle, G.SelR1, G.SelC1, "F2=edit"
GoTo ClearKeyboardBuffer
End If
ElseIf kh% = arrKeyHitUp(cKeyF2) Then
arrKeyState(cKeyF2) = _FALSE
GoTo ClearKeyboardBuffer
End If
' -----------------------------------------------------------------------------
' DETECT KEYS WITH _BUTTON
' -----------------------------------------------------------------------------
If _TRUE = _FALSE Then
' ARROW KEYS MOVE
If _Button(arrButton(cKeyUpArrow)) Then
If arrKeyState(cKeyUpArrow) = _FALSE Then
arrKeyState(cKeyUpArrow) = _TRUE
G.SelR1 = G.SelR1 - 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
Else
arrKeyState(cKeyUpArrow) = _FALSE
End If
' ARROW KEYS MOVE DOWN
If _Button(arrButton(cKeyDownArrow)) Then
If arrKeyState(cKeyDownArrow) = _FALSE Then
arrKeyState(cKeyDownArrow) = _TRUE
G.SelR1 = G.SelR1 + 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
Else
arrKeyState(cKeyDownArrow) = _FALSE
End If
' ARROW KEYS MOVE LEFT
If _Button(arrButton(cKeyLeftArrow)) Then
If arrKeyState(cKeyLeftArrow) = _FALSE Then
arrKeyState(cKeyLeftArrow) = _TRUE
G.SelC1 = G.SelC1 - 1: G.SelC2 = G.SelC1
GoTo ClearKeyboardBuffer
End If
Else
arrKeyState(cKeyLeftArrow) = _FALSE
End If
' ARROW KEYS MOVE RIGHT
If _Button(arrButton(cKeyRightArrow)) Then
If arrKeyState(cKeyRightArrow) = _FALSE Then
arrKeyState(cKeyRightArrow) = _TRUE
G.SelC1 = G.SelC1 + 1: G.SelC2 = G.SelC1
GoTo ClearKeyboardBuffer
End If
Else
arrKeyState(cKeyRightArrow) = _FALSE
End If
' ENTER SHOULD END DATA ENTRY MODE AND MOVE TO NEXT LINE
If _Button(arrButton(cKeyEnter)) Then
If arrKeyState(cKeyEnter) = _FALSE Then
arrKeyState(cKeyEnter) = _TRUE
'CloseEditMode(G)
'Sheet_SetSTR MainGrid.Handle, G.SelR1, G.SelC1, "Enter"
G.SelR1 = G.SelR1 + 1: G.SelR2 = G.SelR1
GoTo ClearKeyboardBuffer
End If
ElseIf _KeyHit = arrKeyHitUp(cKeyEnter) Then
arrKeyState(cKeyEnter) = _FALSE
End If
' F2 ENTERS EDIT MODE
If _Button(arrButton(cKeyF2)) Then
If arrKeyState(cKeyF2) = _FALSE Then
arrKeyState(cKeyF2) = _TRUE
'TriggerEditMode(G)
Sheet_SetSTR MainGrid.Handle, G.SelR1, G.SelC1, "F2=edit"
GoTo ClearKeyboardBuffer
End If
ElseIf _KeyHit = arrKeyHitUp(cKeyF2) Then
arrKeyState(cKeyF2) = _FALSE
End If
End If
'' -----------------------------------------------------------------------------
'' DETECT KEYS WITH InKey$
'' -----------------------------------------------------------------------------
'Dim k$: k$ = InKey$
'If k$ = "" Then Exit Sub
'Select Case k$
' Case Chr$(0) + "H": ' Up Arrow
' G.SelR1 = G.SelR1 - 1: G.SelR2 = G.SelR1
' Case Chr$(0) + "P": ' Down Arrow
' G.SelR1 = G.SelR1 + 1: G.SelR2 = G.SelR1
' Case Chr$(0) + "K": ' Left Arrow
' G.SelC1 = G.SelC1 - 1: G.SelC2 = G.SelC1
' Case Chr$(0) + "M": ' Right Arrow
' G.SelC1 = G.SelC1 + 1: G.SelC2 = G.SelC1
' Case Chr$(13): ' Enter Key - Start Editing
' 'TriggerEditMode(G)
' Case Chr$(0) + Chr$(60): ' F2 key = start editing
' 'TriggerEditMode(G)
' 'Sheet_SetSTR MainGrid.Handle, G.SelR1, G.SelC1, "F2=edit"
'End Select
' -----------------------------------------------------------------------------
' RESUME AFTER KEY DETECTION
' -----------------------------------------------------------------------------
ClearKeyboardBuffer:
_KeyClear ' CLEAR KEYBOARD BUFFER
'_Delay .25
' Keep selection within bounds
If G.SelR1 < 0 Then G.SelR1 = 0: G.SelR2 = 0
If G.SelC1 < 0 Then G.SelC1 = 0: G.SelC2 = 0
' Auto-Scroll Viewport if selection goes off-screen
If G.SelR1 < G.TopRow Then G.TopRow = G.SelR1
' (Repeat similar logic for Bottom/Right bounds)
End Sub ' HandleKeyboard
' /////////////////////////////////////////////////////////////////////////////
Sub ExportToJSON (G As GridView, FileName As String)
Dim Q As String: Q = Chr$(34) ' Store the quote character
Open FileName For Output As #1
Print #1, "{"
Print #1, " " + Q + "rows" + Q + ": ["
For r = 0 To TotalRows - 1
Print #1, " ["
For c = 0 To TotalCols - 1
txt$ = Sheet_GetSTR$(G.Handle, r, c)
' Wraps content in quotes: "Value"
Print #1, " " + Q + txt$ + Q;
If c < TotalCols - 1 Then Print #1, "," Else Print #1, ""
Next c
Print #1, " ]";
If r < TotalRows - 1 Then Print #1, "," Else Print #1, ""
Next r
Print #1, " ]"
Print #1, "}"
Close #1
End Sub ' ExportToJSON
' /////////////////////////////////////////////////////////////////////////////
Sub ExportToXML (G As GridView, FileName As String)
Dim Q As String: Q = Chr$(34)
Open FileName For Output As #1
Print #1, "<?xml version=" + Q + "1.0" + Q + " encoding=" + Q + "UTF-8" + Q + "?>"
Print #1, "<Workbook>"
For r = 0 To TotalRows - 1
' Example: <Row id="0">
Print #1, " <Row id=" + Q + LTrim$(Str$(r)) + Q + ">"
For c = 0 To TotalCols - 1
txt$ = Sheet_GetSTR$(G.Handle, r, c)
Print #1, " <Cell col=" + Q + LTrim$(Str$(c)) + Q + ">" + txt$ + "</Cell>"
Next c
Print #1, " </Row>"
Next r
Print #1, "</Workbook>"
Close #1
End Sub ' ExportToXML
' /////////////////////////////////////////////////////////////////////////////
Sub InitKeyCodes
' Keydown codes for _KeyHit (value of 0 means undetectable or ambiguous, use _BUTTON or other method)
arrKeyHitDown(cKeyEsc) = 27
arrKeyHitDown(cKeyF1) = 15104
arrKeyHitDown(cKeyF2) = 15360
arrKeyHitDown(cKeyF3) = 15616
arrKeyHitDown(cKeyF4) = 15872
arrKeyHitDown(cKeyF5) = 16128
arrKeyHitDown(cKeyF6) = 16384
arrKeyHitDown(cKeyF7) = 16640
arrKeyHitDown(cKeyF8) = 16896
arrKeyHitDown(cKeyF9) = 17152
arrKeyHitDown(cKeyF10) = 17408
arrKeyHitDown(cKeyF11) = -31488
arrKeyHitDown(cKeyF12) = -31232
arrKeyHitDown(cKeyBackTick) = 96
arrKeyHitDown(cKey1) = 49
arrKeyHitDown(cKey2) = 50
arrKeyHitDown(cKey3) = 51
arrKeyHitDown(cKey4) = 52
arrKeyHitDown(cKey5) = 53
arrKeyHitDown(cKey6) = 54
arrKeyHitDown(cKey7) = 55
arrKeyHitDown(cKey8) = 56
arrKeyHitDown(cKey9) = 57
arrKeyHitDown(cKey0) = 48
arrKeyHitDown(cKeyMinus) = 45
arrKeyHitDown(cKeyEqual) = 61
arrKeyHitDown(cKeyBackspace) = 8
arrKeyHitDown(cKeyTilde) = 126
arrKeyHitDown(cKeyExclamation) = 33
arrKeyHitDown(cKeyAt) = 64
arrKeyHitDown(cKeyPound) = 35
arrKeyHitDown(cKeyDollar) = 36
arrKeyHitDown(cKeyPercent) = 37
arrKeyHitDown(cKeyCaret) = 94
arrKeyHitDown(cKeyAmpersand) = 38
arrKeyHitDown(cKeyAsterisk) = 42
arrKeyHitDown(cKeyParenOpen) = 40
arrKeyHitDown(cKeyParenClose) = 41
arrKeyHitDown(cKeyUnderscore) = 95
arrKeyHitDown(cKeyPlus) = 43
arrKeyHitDown(cKeyInsert) = 20992
arrKeyHitDown(cKeyHome) = 18176
arrKeyHitDown(cKeyPageUp) = 18688
arrKeyHitDown(cKeyDelete) = 21248
arrKeyHitDown(cKeyEnd) = 20224
arrKeyHitDown(cKeyPageDown) = 20736
arrKeyHitDown(cKeyTab) = 9
arrKeyHitDown(cKeyCapsLock) = -30771
arrKeyHitDown(cKeyPrintScreen) = 30771
arrKeyHitDown(cKeyScrollLock) = -145
arrKeyHitDown(cKeyPauseBreak) = 0
arrKeyHitDown(cKeyEnter) = 13
arrKeyHitDown(cKeySquareOpen) = 91
arrKeyHitDown(cKeySquareClose) = 93
arrKeyHitDown(cKeyBackSlash) = 92
arrKeyHitDown(cKeyCurlyOpen) = 123
arrKeyHitDown(cKeyCurlyClose) = 125
arrKeyHitDown(cKeyPipe) = 124
arrKeyHitDown(cKeyComma) = 44
arrKeyHitDown(cKeyPeriod) = 46
arrKeyHitDown(cKeySlash) = 47
arrKeyHitDown(cKeyLt) = 60
arrKeyHitDown(cKeyGt) = 62
arrKeyHitDown(cKeyQuestion) = 63
arrKeyHitDown(cKeySemicolon) = 59
arrKeyHitDown(cKeyApostrophe) = 39
arrKeyHitDown(cKeyColon) = 58
arrKeyHitDown(cKeyQuote) = 34
arrKeyHitDown(cKeyShiftLeft) = 30768
arrKeyHitDown(cKeyShiftRight) = 30769
arrKeyHitDown(cKeyCtrlLeft) = -30766
arrKeyHitDown(cKeyCtrlRight) = -30767
arrKeyHitDown(cKeyAltLeft) = -30764
arrKeyHitDown(cKeyAltRight) = -30765
arrKeyHitDown(cKeyWinLeft) = 0
arrKeyHitDown(cKeyWinRight) = 0
arrKeyHitDown(cKeyMenu) = 0
arrKeyHitDown(cKeySpace) = 32
arrKeyHitDown(cKeyLeftArrow) = 19200
arrKeyHitDown(cKeyUpArrow) = 18432
arrKeyHitDown(cKeyDownArrow) = 20480
arrKeyHitDown(cKeyRightArrow) = 19712
arrKeyHitDown(cKeyNumLock) = 30772
arrKeyHitDown(cKeyA) = 97
arrKeyHitDown(cKeyB) = 98
arrKeyHitDown(cKeyC) = 99
arrKeyHitDown(cKeyD) = 100
arrKeyHitDown(cKeyE) = 101
arrKeyHitDown(cKeyF) = 102
arrKeyHitDown(cKeyG) = 103
arrKeyHitDown(cKeyH) = 104
arrKeyHitDown(cKeyI) = 105
arrKeyHitDown(cKeyJ) = 106
arrKeyHitDown(cKeyK) = 107
arrKeyHitDown(cKeyL) = 108
arrKeyHitDown(cKeyM) = 109
arrKeyHitDown(cKeyN) = 110
arrKeyHitDown(cKeyO) = 111
arrKeyHitDown(cKeyP) = 112
arrKeyHitDown(cKeyQ) = 113
arrKeyHitDown(cKeyR) = 114
arrKeyHitDown(cKeyS) = 115
arrKeyHitDown(cKeyT) = 116
arrKeyHitDown(cKeyU) = 117
arrKeyHitDown(cKeyV) = 118
arrKeyHitDown(cKeyW) = 119
arrKeyHitDown(cKeyX) = 120
arrKeyHitDown(cKeyY) = 121
arrKeyHitDown(cKeyZ) = 122
arrKeyHitDown(cKeyUpperA) = 65
arrKeyHitDown(cKeyUpperB) = 66
arrKeyHitDown(cKeyUpperC) = 67
arrKeyHitDown(cKeyUpperD) = 68
arrKeyHitDown(cKeyUpperE) = 69
arrKeyHitDown(cKeyUpperF) = 70
arrKeyHitDown(cKeyUpperG) = 71
arrKeyHitDown(cKeyUpperH) = 72
arrKeyHitDown(cKeyUpperI) = 73
arrKeyHitDown(cKeyUpperJ) = 74
arrKeyHitDown(cKeyUpperK) = 75
arrKeyHitDown(cKeyUpperL) = 76
arrKeyHitDown(cKeyUpperM) = 77
arrKeyHitDown(cKeyUpperN) = 78
arrKeyHitDown(cKeyUpperO) = 79
arrKeyHitDown(cKeyUpperP) = 80
arrKeyHitDown(cKeyUpperQ) = 81
arrKeyHitDown(cKeyUpperR) = 82
arrKeyHitDown(cKeyUpperS) = 83
arrKeyHitDown(cKeyUpperT) = 84
arrKeyHitDown(cKeyUpperU) = 85
arrKeyHitDown(cKeyUpperV) = 86
arrKeyHitDown(cKeyUpperW) = 87
arrKeyHitDown(cKeyUpperX) = 88
arrKeyHitDown(cKeyUpperY) = 89
arrKeyHitDown(cKeyUpperZ) = 90
arrKeyHitDown(cKeypadSlash) = 0
arrKeyHitDown(cKeypadMultiply) = 0
arrKeyHitDown(cKeypadMinus) = 0
arrKeyHitDown(cKeypad7Home) = 0
arrKeyHitDown(cKeypad8Up) = 0
arrKeyHitDown(cKeypad9PgUp) = 0
arrKeyHitDown(cKeypadPlus) = 0
arrKeyHitDown(cKeypad4Left) = 0
arrKeyHitDown(cKeypad5) = 53
arrKeyHitDown(cKeypad6Right) = 0
arrKeyHitDown(cKeypad1End) = 0
arrKeyHitDown(cKeypad2Down) = 0
arrKeyHitDown(cKeypad3PgDn) = 0
arrKeyHitDown(cKeypadEnter) = 0
arrKeyHitDown(cKeypad0Ins) = 0
arrKeyHitDown(cKeypadPeriodDel) = 0
' Keyup codes for _KeyHit (value of 0 means undetectable or ambiguous, use _BUTTON or other method)
arrKeyHitUp(cKeyEsc) = 27
arrKeyHitUp(cKeyF1) = -15104
arrKeyHitUp(cKeyF2) = -15360
arrKeyHitUp(cKeyF3) = -15616
arrKeyHitUp(cKeyF4) = -15872
arrKeyHitUp(cKeyF5) = -16128
arrKeyHitUp(cKeyF6) = -16384
arrKeyHitUp(cKeyF7) = -16640
arrKeyHitUp(cKeyF8) = -16896
arrKeyHitUp(cKeyF9) = -17152
arrKeyHitUp(cKeyF10) = -17408
arrKeyHitUp(cKeyF11) = 31488
arrKeyHitUp(cKeyF12) = 31232
arrKeyHitUp(cKeyBackTick) = -96
arrKeyHitUp(cKey1) = -49
arrKeyHitUp(cKey2) = -50
arrKeyHitUp(cKey3) = -51
arrKeyHitUp(cKey4) = -52
arrKeyHitUp(cKey5) = -53
arrKeyHitUp(cKey6) = -54
arrKeyHitUp(cKey7) = -55
arrKeyHitUp(cKey8) = -56
arrKeyHitUp(cKey9) = -57
arrKeyHitUp(cKey0) = -48
arrKeyHitUp(cKeyMinus) = -45
arrKeyHitUp(cKeyEqual) = -61
arrKeyHitUp(cKeyBackspace) = -8
arrKeyHitUp(cKeyTilde) = -126
arrKeyHitUp(cKeyExclamation) = -33
arrKeyHitUp(cKeyAt) = -64
arrKeyHitUp(cKeyPound) = -35
arrKeyHitUp(cKeyDollar) = -36
arrKeyHitUp(cKeyPercent) = -37
arrKeyHitUp(cKeyCaret) = -94
arrKeyHitUp(cKeyAmpersand) = -38
arrKeyHitUp(cKeyAsterisk) = -42
arrKeyHitUp(cKeyParenOpen) = -40
arrKeyHitUp(cKeyParenClose) = -41
arrKeyHitUp(cKeyUnderscore) = -95
arrKeyHitUp(cKeyPlus) = -43
arrKeyHitUp(cKeyInsert) = -20992
arrKeyHitUp(cKeyHome) = -18176
arrKeyHitUp(cKeyPageUp) = -18688
arrKeyHitUp(cKeyDelete) = -21248
arrKeyHitUp(cKeyEnd) = -20224
arrKeyHitUp(cKeyPageDown) = -20736
arrKeyHitUp(cKeyTab) = -9
arrKeyHitUp(cKeyCapsLock) = -20
arrKeyHitUp(cKeyPrintScreen) = -44
arrKeyHitUp(cKeyScrollLock) = -145
arrKeyHitUp(cKeyPauseBreak) = 0
arrKeyHitUp(cKeyEnter) = -13
arrKeyHitUp(cKeySquareOpen) = -91
arrKeyHitUp(cKeySquareClose) = -93
arrKeyHitUp(cKeyBackSlash) = -92
arrKeyHitUp(cKeyCurlyOpen) = -123
arrKeyHitUp(cKeyCurlyClose) = -125
arrKeyHitUp(cKeyPipe) = -124
arrKeyHitUp(cKeyComma) = -44
arrKeyHitUp(cKeyPeriod) = -46
arrKeyHitUp(cKeySlash) = -47
arrKeyHitUp(cKeyLt) = -60
arrKeyHitUp(cKeyGt) = -62
arrKeyHitUp(cKeyQuestion) = -63
arrKeyHitUp(cKeySemicolon) = -59
arrKeyHitUp(cKeyApostrophe) = -39
arrKeyHitUp(cKeyColon) = -58
arrKeyHitUp(cKeyQuote) = -34
arrKeyHitUp(cKeyShiftLeft) = -30768
arrKeyHitUp(cKeyShiftRight) = -30769
arrKeyHitUp(cKeyCtrlLeft) = 30766
arrKeyHitUp(cKeyCtrlRight) = 30767
arrKeyHitUp(cKeyAltLeft) = 30764
arrKeyHitUp(cKeyAltRight) = 30765
arrKeyHitUp(cKeyWinLeft) = 0
arrKeyHitUp(cKeyWinRight) = 0
arrKeyHitUp(cKeyMenu) = 0
arrKeyHitUp(cKeySpace) = -32
arrKeyHitUp(cKeyLeftArrow) = -19200
arrKeyHitUp(cKeyUpArrow) = -18432
arrKeyHitUp(cKeyDownArrow) = -20480
arrKeyHitUp(cKeyRightArrow) = -19712
arrKeyHitUp(cKeyNumLock) = -144
arrKeyHitUp(cKeyA) = -97
arrKeyHitUp(cKeyB) = -98
arrKeyHitUp(cKeyC) = -99
arrKeyHitUp(cKeyD) = -100
arrKeyHitUp(cKeyE) = -101
arrKeyHitUp(cKeyF) = -102
arrKeyHitUp(cKeyG) = -103
arrKeyHitUp(cKeyH) = -104
arrKeyHitUp(cKeyI) = -105
arrKeyHitUp(cKeyJ) = -106
arrKeyHitUp(cKeyK) = -107
arrKeyHitUp(cKeyL) = -108
arrKeyHitUp(cKeyM) = -109
arrKeyHitUp(cKeyN) = -110
arrKeyHitUp(cKeyO) = -111
arrKeyHitUp(cKeyP) = -112
arrKeyHitUp(cKeyQ) = -113
arrKeyHitUp(cKeyR) = -114
arrKeyHitUp(cKeyS) = -115
arrKeyHitUp(cKeyT) = -116
arrKeyHitUp(cKeyU) = -117
arrKeyHitUp(cKeyV) = -118
arrKeyHitUp(cKeyW) = -119
arrKeyHitUp(cKeyX) = -120
arrKeyHitUp(cKeyY) = -121
arrKeyHitUp(cKeyZ) = -122
arrKeyHitUp(cKeyUpperA) = -65
arrKeyHitUp(cKeyUpperB) = -66
arrKeyHitUp(cKeyUpperC) = -67
arrKeyHitUp(cKeyUpperD) = -68
arrKeyHitUp(cKeyUpperE) = -69
arrKeyHitUp(cKeyUpperF) = -70
arrKeyHitUp(cKeyUpperG) = -71
arrKeyHitUp(cKeyUpperH) = -72
arrKeyHitUp(cKeyUpperI) = -73
arrKeyHitUp(cKeyUpperJ) = -74
arrKeyHitUp(cKeyUpperK) = -75
arrKeyHitUp(cKeyUpperL) = -76
arrKeyHitUp(cKeyUpperM) = -77
arrKeyHitUp(cKeyUpperN) = -78
arrKeyHitUp(cKeyUpperO) = -79
arrKeyHitUp(cKeyUpperP) = -80
arrKeyHitUp(cKeyUpperQ) = -81
arrKeyHitUp(cKeyUpperR) = -82
arrKeyHitUp(cKeyUpperS) = -83
arrKeyHitUp(cKeyUpperT) = -84
arrKeyHitUp(cKeyUpperU) = -85
arrKeyHitUp(cKeyUpperV) = -86
arrKeyHitUp(cKeyUpperW) = -87
arrKeyHitUp(cKeyUpperX) = -88
arrKeyHitUp(cKeyUpperY) = -89
arrKeyHitUp(cKeyUpperZ) = -90
arrKeyHitUp(cKeypadSlash) = 0
arrKeyHitUp(cKeypadMultiply) = 0
arrKeyHitUp(cKeypadMinus) = 0
arrKeyHitUp(cKeypad7Home) = 0
arrKeyHitUp(cKeypad8Up) = 0
arrKeyHitUp(cKeypad9PgUp) = 0
arrKeyHitUp(cKeypadPlus) = 0
arrKeyHitUp(cKeypad4Left) = 0
arrKeyHitUp(cKeypad5) = -53
arrKeyHitUp(cKeypad6Right) = 0
arrKeyHitUp(cKeypad1End) = 0
arrKeyHitUp(cKeypad2Down) = 0
arrKeyHitUp(cKeypad3PgDn) = 0
arrKeyHitUp(cKeypadEnter) = 0
arrKeyHitUp(cKeypad0Ins) = 0
arrKeyHitUp(cKeypadPeriodDel) = 0
' Keyboard codes for _BUTTON (value of 0 means undetectable, use _KEYHIT or other method)
arrButton(cKeyEsc) = 2
arrButton(cKeyF1) = 60
arrButton(cKeyF2) = 61
arrButton(cKeyF3) = 62
arrButton(cKeyF4) = 63
arrButton(cKeyF5) = 64
arrButton(cKeyF6) = 65
arrButton(cKeyF7) = 66
arrButton(cKeyF8) = 67
arrButton(cKeyF9) = 68
arrButton(cKeyF10) = 0 ' detect with _KEYHIT
arrButton(cKeyF11) = 88
arrButton(cKeyF12) = 89
arrButton(cKeyBackTick) = 42
arrButton(cKey1) = 3
arrButton(cKey2) = 4
arrButton(cKey3) = 5
arrButton(cKey4) = 6
arrButton(cKey5) = 7
arrButton(cKey6) = 8
arrButton(cKey7) = 9
arrButton(cKey8) = 10
arrButton(cKey9) = 11
arrButton(cKey0) = 12
arrButton(cKeyMinus) = 13
arrButton(cKeyEqual) = 14
arrButton(cKeyBackspace) = 15
arrButton(cKeyTilde) = 42
arrButton(cKeyExclamation) = 3
arrButton(cKeyAt) = 4
arrButton(cKeyPound) = 5
arrButton(cKeyDollar) = 6
arrButton(cKeyPercent) = 7
arrButton(cKeyCaret) = 8
arrButton(cKeyAmpersand) = 9
arrButton(cKeyAsterisk) = 10
arrButton(cKeyParenOpen) = 11
arrButton(cKeyParenClose) = 12
arrButton(cKeyUnderscore) = 13
arrButton(cKeyPlus) = 14
arrButton(cKeyInsert) = 339
arrButton(cKeyHome) = 328
arrButton(cKeyPageUp) = 330
arrButton(cKeyDelete) = 340
arrButton(cKeyEnd) = 336
arrButton(cKeyPageDown) = 338
arrButton(cKeyTab) = 16
arrButton(cKeyCapsLock) = 59
arrButton(cKeyPrintScreen) = 0 ' detect with _KEYHIT
arrButton(cKeyScrollLock) = 71
arrButton(cKeyPauseBreak) = 0 ' detect with _KEYHIT
arrButton(cKeyEnter) = 29
arrButton(cKeySquareOpen) = 27
arrButton(cKeySquareClose) = 28
arrButton(cKeyBackSlash) = 44
arrButton(cKeyCurlyOpen) = 27
arrButton(cKeyCurlyClose) = 28
arrButton(cKeyPipe) = 44
arrButton(cKeyComma) = 52
arrButton(cKeyPeriod) = 53
arrButton(cKeySlash) = 54
arrButton(cKeyLt) = 52
arrButton(cKeyGt) = 53
arrButton(cKeyQuestion) = 54
arrButton(cKeySemicolon) = 40
arrButton(cKeyApostrophe) = 41
arrButton(cKeyColon) = 40
arrButton(cKeyQuote) = 41
arrButton(cKeyShiftLeft) = 43
arrButton(cKeyShiftRight) = 55
arrButton(cKeyCtrlLeft) = 30
arrButton(cKeyCtrlRight) = 286
arrButton(cKeyAltLeft) = 0 ' detect with _KEYHIT
arrButton(cKeyAltRight) = 0 ' detect with _KEYHIT
arrButton(cKeyWinLeft) = 348
arrButton(cKeyWinRight) = 349
arrButton(cKeyMenu) = 350
arrButton(cKeySpace) = 58
arrButton(cKeyLeftArrow) = 332
arrButton(cKeyUpArrow) = 329
arrButton(cKeyDownArrow) = 337
arrButton(cKeyRightArrow) = 334
arrButton(cKeyNumLock) = 326
arrButton(cKeyA) = 31
arrButton(cKeyB) = 49
arrButton(cKeyC) = 47
arrButton(cKeyD) = 33
arrButton(cKeyE) = 19
arrButton(cKeyF) = 34
arrButton(cKeyG) = 35
arrButton(cKeyH) = 36
arrButton(cKeyI) = 24
arrButton(cKeyJ) = 37
arrButton(cKeyK) = 38
arrButton(cKeyL) = 39
arrButton(cKeyM) = 51
arrButton(cKeyN) = 50
arrButton(cKeyO) = 25
arrButton(cKeyP) = 26
arrButton(cKeyQ) = 17
arrButton(cKeyR) = 20
arrButton(cKeyS) = 32
arrButton(cKeyT) = 21
arrButton(cKeyU) = 23
arrButton(cKeyV) = 48
arrButton(cKeyW) = 18
arrButton(cKeyX) = 46
arrButton(cKeyY) = 22
arrButton(cKeyZ) = 45
arrButton(cKeyUpperA) = 31
arrButton(cKeyUpperB) = 49
arrButton(cKeyUpperC) = 47
arrButton(cKeyUpperD) = 33
arrButton(cKeyUpperE) = 19
arrButton(cKeyUpperF) = 34
arrButton(cKeyUpperG) = 35
arrButton(cKeyUpperH) = 36
arrButton(cKeyUpperI) = 24
arrButton(cKeyUpperJ) = 37
arrButton(cKeyUpperK) = 38
arrButton(cKeyUpperL) = 39
arrButton(cKeyUpperM) = 51
arrButton(cKeyUpperN) = 50
arrButton(cKeyUpperO) = 25
arrButton(cKeyUpperP) = 26
arrButton(cKeyUpperQ) = 17
arrButton(cKeyUpperR) = 20
arrButton(cKeyUpperS) = 32
arrButton(cKeyUpperT) = 21
arrButton(cKeyUpperU) = 23
arrButton(cKeyUpperV) = 48
arrButton(cKeyUpperW) = 18
arrButton(cKeyUpperX) = 46
arrButton(cKeyUpperY) = 22
arrButton(cKeyUpperZ) = 45
arrButton(cKeypadSlash) = 310
arrButton(cKeypadMultiply) = 56
arrButton(cKeypadMinus) = 75
arrButton(cKeypad7Home) = 72
arrButton(cKeypad8Up) = 73
arrButton(cKeypad9PgUp) = 74
arrButton(cKeypadPlus) = 79
arrButton(cKeypad4Left) = 76
arrButton(cKeypad5) = 77
arrButton(cKeypad6Right) = 78
arrButton(cKeypad1End) = 80
arrButton(cKeypad2Down) = 81
arrButton(cKeypad3PgDn) = 82
arrButton(cKeypadEnter) = 285
arrButton(cKeypad0Ins) = 83
arrButton(cKeypadPeriodDel) = 84
' Track key state
Dim index As Long
For index = LBound(arrKeyState) To UBound(arrKeyState)
arrKeyState(index) = _FALSE
Next index
End Sub ' InitKeyCodes
' ****************************************************************************************************************************************************************
' BEGIN COPY LINES BETWEEN <header> and </header> TO NEW FILE, UNCOMMENT AND SAVE AS "QB_Sheets.h" TO SAME FOLDER AS MAIN PROGRAM
' ****************************************************************************************************************************************************************
'<header>
'// Header for Grid Logic and C++ Interface
'#ifndef QB_SHEETS_H
'#define QB_SHEETS_H
'
'#include <vector>
'#include <string>
'#include <fstream>
'#include <cstdint>
'
'extern "C" {
' typedef intptr_t QBSHandle;
'
' // Internal cell storage structure
' struct QBCell {
' std::string s;
' int64_t i = 0;
' double d = 0.0;
' uint8_t type = 0; // 0:Empty, 1:Str, 2:Int, 3:Dbl
' uint32_t bg = 0xFFFFFFFF; // Opaque White
' uint32_t fg = 0xFF000000; // Opaque Black
' bool lock = false;
' };
'
' // Fast-fetch structure for QB64 (binary compatible)
' struct QBCellInfo {
' uint32_t bg = 0xFFFFFFFF; // Opaque White
' uint32_t fg = 0xFF000000; // Opaque Black
' bool lock;
' uint8_t varType;
' };
'
' struct QBRow {
' int16_t height = 20;
' std::vector<QBCell> cells;
' };
'
' struct QBSheet {
' std::vector<QBRow> rows;
' std::vector<int16_t> colWidths;
' };
'
' // --- Lifecycle Management ---
' __declspec(dllexport) QBSHandle QBS_New(int r, int c) {
' QBSheet* s = new QBSheet();
' s->rows.resize(r);
' for (int i = 0; i < r; ++i) s->rows[i].cells.resize(c);
' s->colWidths.assign(c, 100);
' return (QBSHandle)s;
' }
'
' __declspec(dllexport) void QBS_Free(QBSHandle h) {
' delete (QBSheet*)h;
' }
'
' // --- Universal Data Setters ---
' __declspec(dllexport) void QBS_SetStr(QBSHandle h, int r, int c, const char* v) {
' auto* s = (QBSheet*)h;
' if (s && r < s->rows.size() && c < s->rows[r].cells.size()) {
' s->rows[r].cells[c].s = v; s->rows[r].cells[c].type = 1;
' }
' }
' __declspec(dllexport) void QBS_SetInt(QBSHandle h, int r, int c, int64_t v) {
' auto* s = (QBSheet*)h;
' if (s && r < s->rows.size() && c < s->rows[r].cells.size()) {
' s->rows[r].cells[c].i = v; s->rows[r].cells[c].type = 2;
' }
' }
' __declspec(dllexport) void QBS_SetDbl(QBSHandle h, int r, int c, double v) {
' auto* s = (QBSheet*)h;
' if (s && r < s->rows.size() && c < s->rows[r].cells.size()) {
' s->rows[r].cells[c].d = v; s->rows[r].cells[c].type = 3;
' }
' }
'
' // --- Universal Data Getters ---
' __declspec(dllexport) const char* QBS_GetStr(QBSHandle h, int r, int c) {
' auto* s = (QBSheet*)h;
' if (!s || r >= s->rows.size() || c >= s->rows[r].cells.size()) return "";
' return s->rows[r].cells[c].s.c_str();
' }
' __declspec(dllexport) int64_t QBS_GetInt(QBSHandle h, int r, int c) {
' auto* s = (QBSheet*)h;
' return (s && r < s->rows.size() && c < s->rows[r].cells.size()) ? s->rows[r].cells[c].i : 0;
' }
' __declspec(dllexport) double QBS_GetDbl(QBSHandle h, int r, int c) {
' auto* s = (QBSheet*)h;
' return (s && r < s->rows.size() && c < s->rows[r].cells.size()) ? s->rows[r].cells[c].d : 0.0;
' }
'
' // --- High-Speed Info Fetch ---
' // Update this function in QB_Sheets.h
' __declspec(dllexport) void QBS_GetInfo(QBSHandle h, int r, int c, intptr_t infoPtr) {
' auto* s = (QBSheet*)h;
' if (s && r < s->rows.size() && c < s->rows[r].cells.size()) {
' // Cast the raw address back to our struct type
' QBCellInfo* info = (QBCellInfo*)infoPtr;
' auto& cell = s->rows[r].cells[c];
' info->bg = cell.bg;
' info->fg = cell.fg;
' info->lock = cell.lock;
' info->varType = cell.type;
' }
' }
' // --- Formatting & Layout ---
' __declspec(dllexport) void QBS_Format(QBSHandle h, int r, int c, uint32_t bg, uint32_t fg, bool lock) {
' auto* s = (QBSheet*)h;
' if (s && r < s->rows.size() && c < s->rows[r].cells.size()) {
' auto& cell = s->rows[r].cells[c];
' cell.bg = bg; cell.fg = fg; cell.lock = lock;
' }
' }
'
' __declspec(dllexport) void QBS_Size(QBSHandle h, int idx, int size, bool isRow) {
' auto* s = (QBSheet*)h; if (!s) return;
' if (isRow && idx < s->rows.size()) s->rows[idx].height = (int16_t)size;
' else if (!isRow && idx < s->colWidths.size()) s->colWidths[idx] = (int16_t)size;
' }
'
' __declspec(dllexport) int QBS_GetSize(QBSHandle h, int idx, bool isRow) {
' auto* s = (QBSheet*)h; if (!s) return 0;
' return isRow ? s->rows[idx].height : s->colWidths[idx];
' }
'
' // --- Persistence (Binary Save/Load) ---
' __declspec(dllexport) int QBS_Save(QBSHandle h, const char* filename) {
' auto* s = (QBSheet*)h;
' std::ofstream ofs(filename, std::ios::binary);
' if (!ofs) return 0;
' uint32_t rows = s->rows.size(), cols = s->colWidths.size();
' ofs.write((char*)&rows, 4); ofs.write((char*)&cols, 4);
' for (auto w : s->colWidths) ofs.write((char*)&w, 2);
' for (auto& r : s->rows) {
' ofs.write((char*)&r.height, 2);
' for (auto& c : r.cells) {
' ofs.write((char*)&c.type, 1); ofs.write((char*)&c.bg, 4);
' ofs.write((char*)&c.fg, 4); ofs.write((char*)&c.lock, 1);
' ofs.write((char*)&c.i, 8); ofs.write((char*)&c.d, 8);
' uint32_t slen = c.s.size(); ofs.write((char*)&slen, 4);
' ofs.write(c.s.data(), slen);
' }
' }
' return 1;
' }
'
' __declspec(dllexport) QBSHandle QBS_Load(const char* filename) {
' std::ifstream ifs(filename, std::ios::binary);
' if (!ifs) return 0;
' uint32_t rows, cols;
' ifs.read((char*)&rows, 4); ifs.read((char*)&cols, 4);
' QBSheet* s = new QBSheet();
' s->rows.resize(rows); s->colWidths.resize(cols);
' for (int i = 0; i < cols; ++i) ifs.read((char*)&s->colWidths[i], 2);
' for (int i = 0; i < rows; ++i) {
' s->rows[i].cells.resize(cols);
' ifs.read((char*)&s->rows[i].height, 2);
' for (int j = 0; j < cols; ++j) {
' auto& c = s->rows[i].cells[j];
' ifs.read((char*)&c.type, 1); ifs.read((char*)&c.bg, 4);
' ifs.read((char*)&c.fg, 4); ifs.read((char*)&c.lock, 1);
' ifs.read((char*)&c.i, 8); ifs.read((char*)&c.d, 8);
' uint32_t slen; ifs.read((char*)&slen, 4);
' c.s.resize(slen); ifs.read(&c.s[0], slen);
' }
' }
' return (QBSHandle)s;
' }
'}
'#endif
'</header>
' ****************************************************************************************************************************************************************
' END COPY LINES BETWEEN <header> and </header> TO NEW FILE, UNCOMMENT AND SAVE AS "QB_Sheets.h" TO SAME FOLDER AS MAIN PROGRAM
' ****************************************************************************************************************************************************************
