Posts: 347
Threads: 45
Joined: Jun 2024
Reputation:
32
Code: (Select All) #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
Again save that as QB_Sheets.h
Code: (Select All)
'-------------------------------------------------------------------------
' QB_Sheets.bi - Header for Grid Logic and C++ Interface
'-------------------------------------------------------------------------
' --- 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
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
' QB64 Spreadsheet Core
'$DYNAMIC
SCREEN _NEWIMAGE(1024, 768, 32)
_TITLE "QB_Sheets Core Engine"
' --- GLOBALS ---
DIM SHARED Mouse AS MouseState, OldMouse AS MouseState
DIM SHARED MainGrid AS GridView
' Initialize
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
' --- MAIN LOOP ---
DO
_LIMIT 60
UpdateInput
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 UpdateInput
OldMouse = Mouse
DO WHILE _MOUSEINPUT
Mouse.X = _MOUSEX: Mouse.Y = _MOUSEY: Mouse.B1 = _MOUSEBUTTON(1)
Mouse.Wheel = Mouse.Wheel + _MOUSEWHEEL
LOOP
END SUB
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
' 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
END SUB
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
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
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
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
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
SUB HandleKeyboard (G AS GridView)
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)
END SELECT
' 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
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
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
Looks a bit more professional now...i hope it helps! And this time i promise to leave it be!
Unseen
Posts: 1,215
Threads: 162
Joined: Apr 2022
Reputation:
34
(01-26-2026, 11:07 PM)Unseen Machine Wrote: Save that as QB_Sheets.h in your Qb64 folder
...
I wont add anything more to it but as @Petr will grasp it in a heartbeat, maybe itll prove of some use.
...
Again save that as QB_Sheets.h
...
Looks a bit more professional now...i hope it helps! And this time i promise to leave it be!
Unseen
Wow... I'll give this a look... thanks!
Posts: 56
Threads: 4
Joined: Apr 2022
Reputation:
7
This is an interesting approach that reminds me of the good old "Multiplan" spreadsheet from the 80's.
Any initiative to bring QB64pe closer to Excel, to create bridges between them, is welcome.
Well done...
Why not yes ?
Posts: 902
Threads: 38
Joined: Apr 2022
Reputation:
72
How much of this was "vibe coded"
The noticing will continue
Posts: 513
Threads: 65
Joined: May 2022
Reputation:
83
01-28-2026, 02:26 PM
(This post was last modified: 01-28-2026, 03:00 PM by Petr.)
@SpriggsySpriggs
The vibes were just too strong to resist. I think we’re both speaking the same 'language' here.
Work on this will continue. But first I'll finish a lot of other unfinished business.
Posts: 347
Threads: 45
Joined: Jun 2024
Reputation:
32
Mine basically 100% But that's the world nowadays. It's not like it comes out in one hit, has to be debugged and tested, features added etc...
Posts: 902
Threads: 38
Joined: Apr 2022
Reputation:
72
@grok, is this true?
The noticing will continue
Posts: 1,215
Threads: 162
Joined: Apr 2022
Reputation:
34
01-28-2026, 07:05 PM
(This post was last modified: 01-28-2026, 08:03 PM by madscijr.)
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!):
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
' ****************************************************************************************************************************************************************
Posts: 347
Threads: 45
Joined: Jun 2024
Reputation:
32
01-28-2026, 07:22 PM
(This post was last modified: 01-28-2026, 07:28 PM by Unseen Machine.)
IsDragging As _Byte - Add a new value here, i.e : if IsDragging is false then if IsEditing (Might want to use it as a mode flag so you can add Replacing or Editing Existing)
Tile selection would come from a double click maybe!
But im glad it helped you!
Unseen
@SpriggsySpriggs and @Petr : I dont think you sarcastic and rude remarks are very nice, we have Pete for that and i find it quite hurtful. So wither be constructive or butt out the conversation!
Posts: 513
Threads: 65
Joined: May 2022
Reputation:
83
@Unseen Machine
Sorry about my “vibes” comment — it was meant as self-deprecating humor (as in “I prototyped fast”), not a jab at you or your work. I genuinely appreciate the time and effort you put into this. Let’s keep it constructive and push it forward.
|