Add StringGrid control type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Duensing 2026-03-05 15:35:09 -06:00
parent dd115d3727
commit da21cc71f6
4 changed files with 392 additions and 8 deletions

View file

@ -336,6 +336,7 @@ messages are available, dispatching each command as it arrives.
| `Bevel` | TBevel | Cosmetic beveled line/box | | `Bevel` | TBevel | Cosmetic beveled line/box |
| `Header` | THeader | Column header bar | | `Header` | THeader | Column header bar |
| `ScrollBox` | TScrollBox | Scrollable container | | `ScrollBox` | TScrollBox | Scrollable container |
| `StringGrid` | TStringGrid | Editable string grid |
### Creating Controls ### Creating Controls
@ -728,6 +729,91 @@ Input mask string (mask;save literals;blank char).
- **Format:** `0` (bsLowered) or `1` (bsRaised) - **Format:** `0` (bsLowered) or `1` (bsRaised)
- **Example:** `Style=1` - **Example:** `Style=1`
### ColCount
- **Applies to:** StringGrid
- **Format:** Integer (default 5)
- **Example:** `ColCount=10`
Number of columns in the grid.
### RowCount
- **Applies to:** StringGrid
- **Format:** Integer (default 5)
- **Example:** `RowCount=20`
Number of rows in the grid.
### FixedCols
- **Applies to:** StringGrid
- **Format:** Integer (default 1)
- **Example:** `FixedCols=1`
Number of non-scrollable columns on the left.
### FixedRows
- **Applies to:** StringGrid
- **Format:** Integer (default 1)
- **Example:** `FixedRows=1`
Number of non-scrollable rows at the top.
### DefaultColWidth
- **Applies to:** StringGrid
- **Format:** Integer (pixels)
- **Example:** `DefaultColWidth=80`
### DefaultRowHeight
- **Applies to:** StringGrid
- **Format:** Integer (pixels)
- **Example:** `DefaultRowHeight=20`
### Options (StringGrid)
- **Applies to:** StringGrid
- **Format:** Integer (bitmask)
- **Example:** `Options=1549`
Bitmask of TGridOption values:
| Bit | Value | Option | Description |
|-----|--------|---------------------|---------------------------|
| 0 | 0x0001 | goFixedVertLine | Vertical lines on fixed |
| 1 | 0x0002 | goFixedHorzLine | Horizontal lines on fixed |
| 2 | 0x0004 | goVertLine | Vertical lines on cells |
| 3 | 0x0008 | goHorzLine | Horizontal lines on cells |
| 4 | 0x0010 | goRangeSelect | Allow range selection |
| 5 | 0x0020 | goDrawFocusSelected | Draw focused cell selected|
| 6 | 0x0040 | goRowSizing | Allow row resizing |
| 7 | 0x0080 | goColSizing | Allow column resizing |
| 8 | 0x0100 | goRowMoving | Allow row moving |
| 9 | 0x0200 | goColMoving | Allow column moving |
| 10 | 0x0400 | goEditing | Allow in-place editing |
| 11 | 0x0800 | goTabs | Tab between cells |
| 12 | 0x1000 | goThumbTracking | Track scrollbar thumb |
### Cells
- **Applies to:** StringGrid
- **Format:** Quoted string (tab-delimited columns, `\n`-delimited rows)
- **Example:** `Cells="Name\tAge\nAlice\t30\nBob\t25"`
Bulk-loads all cell data. Columns are separated by tab characters,
rows by newlines. Row 0 is the first row (typically fixed header).
### Cell
- **Applies to:** StringGrid
- **Format:** Quoted string (`col,row,value`)
- **Example:** `Cell="1,2,Hello"`
Sets a single cell value. Column and row are zero-based indices.
### BasePath ### BasePath
`BasePath` is a property on `TFormClient` (not a protocol property). `BasePath` is a property on `TFormClient` (not a protocol property).
@ -766,6 +852,7 @@ No `EVENT.BIND` command is needed.
| TabSet | Change | `<tabIndex>` | | TabSet | Change | `<tabIndex>` |
| TabbedNotebook | Change | `<pageIndex>` | | TabbedNotebook | Change | `<pageIndex>` |
| MaskEdit | Change | `"new text"` | | MaskEdit | Change | `"new text"` |
| StringGrid | SelectCell | `<col> <row>` |
### Opt-in Events ### Opt-in Events
@ -784,6 +871,7 @@ client will send them. Use `EVENT.UNBIND` to disconnect.
| MouseDown | `<x> <y> <button>` | 0=left, 1=right, 2=middle | | MouseDown | `<x> <y> <button>` | 0=left, 1=right, 2=middle |
| MouseUp | `<x> <y> <button>` | 0=left, 1=right, 2=middle | | MouseUp | `<x> <y> <button>` | 0=left, 1=right, 2=middle |
| MouseMove | `<x> <y> 0` | Coordinates relative to control | | MouseMove | `<x> <y> 0` | Coordinates relative to control |
| SetEditText | `<col> <row> "text"` | StringGrid in-place edit |
### Form Close Event ### Form Close Event

View file

@ -46,7 +46,8 @@ typedef enum {
ctOutline, ctOutline,
ctBevel, ctBevel,
ctHeader, ctHeader,
ctScrollBox ctScrollBox,
ctStringGrid
} CtrlTypeE; } CtrlTypeE;
typedef struct { typedef struct {
@ -140,6 +141,21 @@ typedef struct {
bool hasShape; bool hasShape;
bool hasStyle; bool hasStyle;
bool hasEditMask; bool hasEditMask;
int32_t colCount;
int32_t rowCount;
int32_t fixedCols;
int32_t fixedRows;
int32_t defaultColWidth;
int32_t defaultRowHeight;
int32_t options;
bool hasColCount;
bool hasRowCount;
bool hasFixedCols;
bool hasFixedRows;
bool hasDefaultColWidth;
bool hasDefaultRowHeight;
bool hasOptions;
bool hasOnSetEditText;
} DfmCtrlT; } DfmCtrlT;
typedef struct { typedef struct {
@ -439,6 +455,9 @@ static CtrlTypeE mapClassName(const char *className) {
if (stricmp_local(className, "TScrollBox") == 0) { if (stricmp_local(className, "TScrollBox") == 0) {
return ctScrollBox; return ctScrollBox;
} }
if (stricmp_local(className, "TStringGrid") == 0) {
return ctStringGrid;
}
return ctUnknown; return ctUnknown;
} }
@ -1013,6 +1032,59 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
ctrl->style = readIntValue(data, size, pos, tag); ctrl->style = readIntValue(data, size, pos, tag);
} }
ctrl->hasStyle = true; ctrl->hasStyle = true;
} else if (!isForm && stricmp_local(propName, "ColCount") == 0) {
ctrl->colCount = readIntValue(data, size, pos, tag);
ctrl->hasColCount = true;
} else if (!isForm && stricmp_local(propName, "RowCount") == 0) {
ctrl->rowCount = readIntValue(data, size, pos, tag);
ctrl->hasRowCount = true;
} else if (!isForm && stricmp_local(propName, "FixedCols") == 0) {
ctrl->fixedCols = readIntValue(data, size, pos, tag);
ctrl->hasFixedCols = true;
} else if (!isForm && stricmp_local(propName, "FixedRows") == 0) {
ctrl->fixedRows = readIntValue(data, size, pos, tag);
ctrl->hasFixedRows = true;
} else if (!isForm && stricmp_local(propName, "DefaultColWidth") == 0) {
ctrl->defaultColWidth = readIntValue(data, size, pos, tag);
ctrl->hasDefaultColWidth = true;
} else if (!isForm && stricmp_local(propName, "DefaultRowHeight") == 0) {
ctrl->defaultRowHeight = readIntValue(data, size, pos, tag);
ctrl->hasDefaultRowHeight = true;
} else if (!isForm && stricmp_local(propName, "Options") == 0) {
if (tag == vaSet) {
ctrl->options = 0;
while (*pos < size) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
if (strBuf[0] == '\0') {
break;
}
if (stricmp_local(strBuf, "goFixedVertLine") == 0) { ctrl->options |= 0x0001; }
else if (stricmp_local(strBuf, "goFixedHorzLine") == 0) { ctrl->options |= 0x0002; }
else if (stricmp_local(strBuf, "goVertLine") == 0) { ctrl->options |= 0x0004; }
else if (stricmp_local(strBuf, "goHorzLine") == 0) { ctrl->options |= 0x0008; }
else if (stricmp_local(strBuf, "goRangeSelect") == 0) { ctrl->options |= 0x0010; }
else if (stricmp_local(strBuf, "goDrawFocusSelected") == 0) { ctrl->options |= 0x0020; }
else if (stricmp_local(strBuf, "goRowSizing") == 0) { ctrl->options |= 0x0040; }
else if (stricmp_local(strBuf, "goColSizing") == 0) { ctrl->options |= 0x0080; }
else if (stricmp_local(strBuf, "goRowMoving") == 0) { ctrl->options |= 0x0100; }
else if (stricmp_local(strBuf, "goColMoving") == 0) { ctrl->options |= 0x0200; }
else if (stricmp_local(strBuf, "goEditing") == 0) { ctrl->options |= 0x0400; }
else if (stricmp_local(strBuf, "goTabs") == 0) { ctrl->options |= 0x0800; }
else if (stricmp_local(strBuf, "goThumbTracking") == 0) { ctrl->options |= 0x1000; }
}
} else {
ctrl->options = readIntValue(data, size, pos, tag);
}
ctrl->hasOptions = true;
} else if (stricmp_local(propName, "OnSetEditText") == 0) {
if (tag == vaIdent) {
readStr(data, size, pos, strBuf, sizeof(strBuf));
} else {
skipValue(data, size, pos, tag);
}
if (!isForm) {
ctrl->hasOnSetEditText = true;
}
} else { } else {
skipValue(data, size, pos, tag); skipValue(data, size, pos, tag);
} }
@ -1160,7 +1232,7 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
"MainMenu", "PopupMenu", "MenuItem", "RadioGroup", "MainMenu", "PopupMenu", "MenuItem", "RadioGroup",
"BitBtn", "SpeedButton", "TabSet", "Notebook", "BitBtn", "SpeedButton", "TabSet", "Notebook",
"TabbedNotebook", "MaskEdit", "Outline", "Bevel", "TabbedNotebook", "MaskEdit", "Outline", "Bevel",
"Header", "ScrollBox" "Header", "ScrollBox", "StringGrid"
}; };
char escaped[8192]; char escaped[8192];
@ -1291,6 +1363,27 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
if (ctrl->hasStyle && ctrl->style != 0) { if (ctrl->hasStyle && ctrl->style != 0) {
fprintf(out, " Style=%d", ctrl->style); fprintf(out, " Style=%d", ctrl->style);
} }
if (ctrl->hasColCount) {
fprintf(out, " ColCount=%d", ctrl->colCount);
}
if (ctrl->hasRowCount) {
fprintf(out, " RowCount=%d", ctrl->rowCount);
}
if (ctrl->hasFixedCols) {
fprintf(out, " FixedCols=%d", ctrl->fixedCols);
}
if (ctrl->hasFixedRows) {
fprintf(out, " FixedRows=%d", ctrl->fixedRows);
}
if (ctrl->hasDefaultColWidth) {
fprintf(out, " DefaultColWidth=%d", ctrl->defaultColWidth);
}
if (ctrl->hasDefaultRowHeight) {
fprintf(out, " DefaultRowHeight=%d", ctrl->defaultRowHeight);
}
if (ctrl->hasOptions) {
fprintf(out, " Options=%d", ctrl->options);
}
fprintf(out, "\n"); fprintf(out, "\n");
@ -1329,6 +1422,9 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
if (ctrl->hasOnMouseUp) { if (ctrl->hasOnMouseUp) {
fprintf(out, "EVENT.BIND %d %d MouseUp\n", formId, ctrlId); fprintf(out, "EVENT.BIND %d %d MouseUp\n", formId, ctrlId);
} }
if (ctrl->hasOnSetEditText) {
fprintf(out, "EVENT.BIND %d %d SetEditText\n", formId, ctrlId);
}
// Suppress unused variable warning for autoSelect // Suppress unused variable warning for autoSelect
(void)autoSelect; (void)autoSelect;

View file

@ -14,7 +14,7 @@ interface
uses uses
SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, Menus, Buttons, Tabs, SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, Menus, Buttons, Tabs,
TabNotBk, Mask, Outline, MPlayer, WinTypes, WinProcs; TabNotBk, Mask, Outline, Grids, MPlayer, WinTypes, WinProcs;
const const
MaxMsgLen = 4096; MaxMsgLen = 4096;
@ -34,12 +34,12 @@ type
ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup, ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup,
ctBitBtn, ctSpeedButton, ctTabSet, ctNotebook, ctBitBtn, ctSpeedButton, ctTabSet, ctNotebook,
ctTabbedNotebook, ctMaskEdit, ctOutline, ctBevel, ctTabbedNotebook, ctMaskEdit, ctOutline, ctBevel,
ctHeader, ctScrollBox); ctHeader, ctScrollBox, ctStringGrid);
{ Bound event flags } { Bound event flags }
TBoundEvent = (beDblClick, beKeyDown, beKeyUp, TBoundEvent = (beDblClick, beKeyDown, beKeyUp,
beEnter, beExit, beMouseDown, beMouseUp, beMouseMove, beEnter, beExit, beMouseDown, beMouseUp, beMouseMove,
beClick, beNotify); beClick, beNotify, beSetEditText);
TBoundEvents = set of TBoundEvent; TBoundEvents = set of TBoundEvent;
{ Per-control record } { Per-control record }
@ -120,6 +120,8 @@ type
procedure HandleRadioGroupClick(Sender: TObject); procedure HandleRadioGroupClick(Sender: TObject);
procedure HandleTabSetChange(Sender: TObject); procedure HandleTabSetChange(Sender: TObject);
procedure HandleTabbedNotebookChange(Sender: TObject); procedure HandleTabbedNotebookChange(Sender: TObject);
procedure HandleStringGridSelectCell(Sender: TObject; ACol, ARow: Longint; var CanSelect: Boolean);
procedure HandleStringGridSetEditText(Sender: TObject; ACol, ARow: Longint; const Value: string);
{ Outgoing event helpers } { Outgoing event helpers }
procedure SendEvent(FormId, CtrlId: Integer; const EventName: string; const Data: string); procedure SendEvent(FormId, CtrlId: Integer; const EventName: string; const Data: string);
@ -777,6 +779,36 @@ begin
end; end;
procedure TFormClient.HandleStringGridSelectCell(Sender: TObject; ACol, ARow: Longint; var CanSelect: Boolean);
var
Tag: Longint;
FormId: Integer;
CtrlId: Integer;
begin
Tag := (Sender as TComponent).Tag;
FormId := Tag shr 16;
CtrlId := Tag and $FFFF;
SendEvent(FormId, CtrlId, 'SelectCell', IntToStr(ACol) + ' ' + IntToStr(ARow));
CanSelect := True;
end;
procedure TFormClient.HandleStringGridSetEditText(Sender: TObject; ACol, ARow: Longint; const Value: string);
var
Tag: Longint;
FormId: Integer;
CtrlId: Integer;
Escaped: array[0..4095] of Char;
begin
Tag := (Sender as TComponent).Tag;
FormId := Tag shr 16;
CtrlId := Tag and $FFFF;
StrPCopy(FTmpBuf, Value);
EscapeString(FTmpBuf, Escaped, SizeOf(Escaped));
SendEvent(FormId, CtrlId, 'SetEditText', IntToStr(ACol) + ' ' + IntToStr(ARow) + ' "' + StrPas(Escaped) + '"');
end;
procedure TFormClient.HandleFormClose(Sender: TObject; procedure TFormClient.HandleFormClose(Sender: TObject;
var Action: TCloseAction); var Action: TCloseAction);
var var
@ -828,6 +860,8 @@ begin
(CR^.Control as TTabbedNotebook).OnChange := HandleTabbedNotebookChange; (CR^.Control as TTabbedNotebook).OnChange := HandleTabbedNotebookChange;
ctMaskEdit: ctMaskEdit:
(CR^.Control as TMaskEdit).OnChange := HandleMaskEditChange; (CR^.Control as TMaskEdit).OnChange := HandleMaskEditChange;
ctStringGrid:
(CR^.Control as TStringGrid).OnSelectCell := HandleStringGridSelectCell;
end; end;
end; end;
@ -897,6 +931,12 @@ begin
if CR^.Control is TMediaPlayer then if CR^.Control is TMediaPlayer then
(CR^.Control as TMediaPlayer).OnNotify := HandleMediaPlayerNotify; (CR^.Control as TMediaPlayer).OnNotify := HandleMediaPlayerNotify;
CR^.Bound := CR^.Bound + [beNotify]; CR^.Bound := CR^.Bound + [beNotify];
end
else if EventName = 'SetEditText' then
begin
if CR^.Control is TStringGrid then
(CR^.Control as TStringGrid).OnSetEditText := HandleStringGridSetEditText;
CR^.Bound := CR^.Bound + [beSetEditText];
end; end;
end; end;
@ -966,6 +1006,12 @@ begin
if CR^.Control is TMediaPlayer then if CR^.Control is TMediaPlayer then
(CR^.Control as TMediaPlayer).OnNotify := nil; (CR^.Control as TMediaPlayer).OnNotify := nil;
CR^.Bound := CR^.Bound - [beNotify]; CR^.Bound := CR^.Bound - [beNotify];
end
else if EventName = 'SetEditText' then
begin
if CR^.Control is TStringGrid then
(CR^.Control as TStringGrid).OnSetEditText := nil;
CR^.Bound := CR^.Bound - [beSetEditText];
end; end;
end; end;
@ -981,6 +1027,10 @@ var
P: PChar; P: PChar;
Start: PChar; Start: PChar;
PCR: PFormCtrlRec; PCR: PFormCtrlRec;
Col: Integer;
Row: Integer;
W: Word;
Opts: TGridOptions;
begin begin
S := StrPas(Key); S := StrPas(Key);
@ -1538,6 +1588,119 @@ begin
N := StrToIntDef(StrPas(Value), 0); N := StrToIntDef(StrPas(Value), 0);
if CR^.CtrlType = ctBevel then if CR^.CtrlType = ctBevel then
(CR^.Control as TBevel).Style := TBevelStyle(N); (CR^.Control as TBevel).Style := TBevelStyle(N);
end
else if S = 'ColCount' then
begin
N := StrToIntDef(StrPas(Value), 5);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).ColCount := N;
end
else if S = 'RowCount' then
begin
N := StrToIntDef(StrPas(Value), 5);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).RowCount := N;
end
else if S = 'FixedCols' then
begin
N := StrToIntDef(StrPas(Value), 1);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).FixedCols := N;
end
else if S = 'FixedRows' then
begin
N := StrToIntDef(StrPas(Value), 1);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).FixedRows := N;
end
else if S = 'DefaultColWidth' then
begin
N := StrToIntDef(StrPas(Value), 64);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).DefaultColWidth := N;
end
else if S = 'DefaultRowHeight' then
begin
N := StrToIntDef(StrPas(Value), 24);
if CR^.CtrlType = ctStringGrid then
(CR^.Control as TStringGrid).DefaultRowHeight := N;
end
else if S = 'Options' then
begin
if CR^.CtrlType = ctStringGrid then
begin
N := StrToIntDef(StrPas(Value), 0);
W := Word(N);
Move(W, Opts, SizeOf(Opts));
(CR^.Control as TStringGrid).Options := Opts;
end;
end
else if S = 'Cells' then
begin
if CR^.CtrlType = ctStringGrid then
begin
UnescapeString(Value, Unesc, SizeOf(Unesc));
Row := 0;
P := Unesc;
while P^ <> #0 do
begin
{ Parse one row: tab-delimited columns }
Col := 0;
Start := P;
while (P^ <> #0) and (P^ <> #10) do
begin
if P^ = #9 then
begin
P^ := #0;
if (Col < (CR^.Control as TStringGrid).ColCount) and (Row < (CR^.Control as TStringGrid).RowCount) then
(CR^.Control as TStringGrid).Cells[Col, Row] := StrPas(Start);
Inc(Col);
Inc(P);
Start := P;
end
else
Inc(P);
end;
{ Last column in row }
if (Col < (CR^.Control as TStringGrid).ColCount) and (Row < (CR^.Control as TStringGrid).RowCount) then
(CR^.Control as TStringGrid).Cells[Col, Row] := StrPas(Start);
if P^ = #10 then
Inc(P);
Inc(Row);
end;
end;
end
else if S = 'Cell' then
begin
if CR^.CtrlType = ctStringGrid then
begin
{ Format: "col,row,value" }
UnescapeString(Value, Unesc, SizeOf(Unesc));
P := Unesc;
{ Parse col }
Start := P;
while (P^ <> #0) and (P^ <> ',') do
Inc(P);
if P^ = ',' then
begin
P^ := #0;
Col := StrToIntDef(StrPas(Start), 0);
Inc(P);
{ Parse row }
Start := P;
while (P^ <> #0) and (P^ <> ',') do
Inc(P);
if P^ = ',' then
begin
P^ := #0;
Row := StrToIntDef(StrPas(Start), 0);
Inc(P);
{ Rest is value }
if (Col < (CR^.Control as TStringGrid).ColCount) and (Row < (CR^.Control as TStringGrid).RowCount) then
(CR^.Control as TStringGrid).Cells[Col, Row] := StrPas(P);
end;
end;
end;
end; end;
end; end;
@ -1656,6 +1819,8 @@ begin
Result := ctHeader Result := ctHeader
else if S = 'ScrollBox' then else if S = 'ScrollBox' then
Result := ctScrollBox Result := ctScrollBox
else if S = 'StringGrid' then
Result := ctStringGrid
else else
Result := ctUnknown; Result := ctUnknown;
end; end;
@ -1833,6 +1998,8 @@ begin
Comp := THeader.Create(FR^.Form); Comp := THeader.Create(FR^.Form);
ctScrollBox: ctScrollBox:
Comp := TScrollBox.Create(FR^.Form); Comp := TScrollBox.Create(FR^.Form);
ctStringGrid:
Comp := TStringGrid.Create(FR^.Form);
end; end;
if Comp = nil then if Comp = nil then

View file

@ -83,6 +83,8 @@ Event data varies by event type:
| Exit | (none) | | Exit | (none) |
| Close | (none) | | Close | (none) |
| Notify | (none) | | Notify | (none) |
| SelectCell| `<col> <row>` |
| SetEditText| `<col> <row> "text"` |
## Control Types ## Control Types
@ -115,6 +117,7 @@ Event data varies by event type:
| Bevel | TBevel | (none) | | Bevel | TBevel | (none) |
| Header | THeader | (none) | | Header | THeader | (none) |
| ScrollBox | TScrollBox | (none) | | ScrollBox | TScrollBox | (none) |
| StringGrid | TStringGrid | SelectCell |
MainMenu and PopupMenu use `0 0 0 0` geometry (non-visual). MenuItem MainMenu and PopupMenu use `0 0 0 0` geometry (non-visual). MenuItem
uses `0 0 0 0` geometry and requires a `Parent` property to specify its uses `0 0 0 0` geometry and requires a `Parent` property to specify its
@ -125,9 +128,9 @@ GroupBox and Panel are cosmetic containers (flat parent model — no child
containment in the protocol). RadioButtons are all in one group per form. containment in the protocol). RadioButtons are all in one group per form.
Opt-in events (require EVENT.BIND): Click (Image, GroupBox, Panel), Opt-in events (require EVENT.BIND): Click (Image, GroupBox, Panel),
Notify (MediaPlayer), DblClick, KeyDown, KeyUp, Enter, Exit, Notify (MediaPlayer), SetEditText (StringGrid), DblClick, KeyDown,
MouseDown, MouseUp, MouseMove. Menu and RadioGroup components do KeyUp, Enter, Exit, MouseDown, MouseUp, MouseMove. Menu and
not support opt-in events. RadioGroup components do not support opt-in events.
## Properties ## Properties
@ -174,6 +177,15 @@ not support opt-in events.
| OutlineStyle| Outline | 0-6 (osText..osTreePictureText) | | OutlineStyle| Outline | 0-6 (osText..osTreePictureText) |
| Shape | Bevel | 0-5 (bsBox..bsRightLine) | | Shape | Bevel | 0-5 (bsBox..bsRightLine) |
| Style | Bevel | 0-1 (bsLowered, bsRaised) | | Style | Bevel | 0-1 (bsLowered, bsRaised) |
| ColCount | StringGrid | Integer (default 5) |
| RowCount | StringGrid | Integer (default 5) |
| FixedCols | StringGrid | Integer (default 1) |
| FixedRows | StringGrid | Integer (default 1) |
| DefaultColWidth | StringGrid | Integer (pixels) |
| DefaultRowHeight| StringGrid | Integer (pixels) |
| Options | StringGrid | Integer (bitmask, see below) |
| Cells | StringGrid | Quoted string (tab-delimited cols, `\n`-delimited rows) |
| Cell | StringGrid | Quoted string (`col,row,value`) |
File path properties (Picture, FileName) are resolved relative to the File path properties (Picture, FileName) are resolved relative to the
client's `BasePath` setting. Subdirectories are allowed (e.g., client's `BasePath` setting. Subdirectories are allowed (e.g.,
@ -185,6 +197,27 @@ MediaPlayer rather than setting a value. Valid commands: `Open`,
`Play`, `Stop`, `Close`, `Pause`, `Resume`, `Rewind`, `Next`, `Play`, `Stop`, `Close`, `Pause`, `Resume`, `Rewind`, `Next`,
`Previous`. `Previous`.
StringGrid Options is an integer bitmask of TGridOption values:
| Bit | Value | Option |
|--------|--------|---------------------|
| 0 | 0x0001 | goFixedVertLine |
| 1 | 0x0002 | goFixedHorzLine |
| 2 | 0x0004 | goVertLine |
| 3 | 0x0008 | goHorzLine |
| 4 | 0x0010 | goRangeSelect |
| 5 | 0x0020 | goDrawFocusSelected |
| 6 | 0x0040 | goRowSizing |
| 7 | 0x0080 | goColSizing |
| 8 | 0x0100 | goRowMoving |
| 9 | 0x0200 | goColMoving |
| 10 | 0x0400 | goEditing |
| 11 | 0x0800 | goTabs |
| 12 | 0x1000 | goThumbTracking |
Cells is a bulk-load property: columns are tab-delimited, rows are
`\n`-delimited. Cell is an individual update: `"col,row,value"`.
## String Encoding ## String Encoding
- Strings in the protocol are always double-quoted. - Strings in the protocol are always double-quoted.