Add MainMenu, PopupMenu, MenuItem, and RadioGroup control types
Menus use 0 0 0 0 geometry (non-visual) with Parent property for hierarchy. TFormCtrlRec.Control widened from TControl to TComponent to support non-visual menu components. RadioGroup auto-wires Click with ItemIndex data. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2d5ed2a3b1
commit
03d44440fd
4 changed files with 362 additions and 108 deletions
|
|
@ -322,6 +322,10 @@ messages are available, dispatching each command as it arrives.
|
||||||
| `Panel` | TPanel | Cosmetic container panel |
|
| `Panel` | TPanel | Cosmetic container panel |
|
||||||
| `ScrollBar` | TScrollBar | Horizontal or vertical scrollbar|
|
| `ScrollBar` | TScrollBar | Horizontal or vertical scrollbar|
|
||||||
| `MediaPlayer` | TMediaPlayer | MCI media player control |
|
| `MediaPlayer` | TMediaPlayer | MCI media player control |
|
||||||
|
| `MainMenu` | TMainMenu | Form main menu bar |
|
||||||
|
| `PopupMenu` | TPopupMenu | Context (right-click) menu |
|
||||||
|
| `MenuItem` | TMenuItem | Menu item (child of menu) |
|
||||||
|
| `RadioGroup` | TRadioGroup | Grouped radio buttons |
|
||||||
|
|
||||||
### Creating Controls
|
### Creating Controls
|
||||||
|
|
||||||
|
|
@ -359,12 +363,12 @@ CTRL.SET 1 3 Text="world" Enabled=0
|
||||||
|
|
||||||
### Caption
|
### Caption
|
||||||
|
|
||||||
- **Applies to:** Label, Button, CheckBox, GroupBox, RadioButton, Panel
|
- **Applies to:** Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup
|
||||||
- **Format:** Quoted string
|
- **Format:** Quoted string
|
||||||
- **Example:** `Caption="Submit"`
|
- **Example:** `Caption="Submit"`
|
||||||
|
|
||||||
The display text for labels, buttons, check boxes, group boxes, radio
|
The display text for labels, buttons, check boxes, group boxes, radio
|
||||||
buttons, and panels.
|
buttons, panels, menu items, and radio groups.
|
||||||
|
|
||||||
### Text
|
### Text
|
||||||
|
|
||||||
|
|
@ -381,7 +385,7 @@ CTRL.SET 1 5 Text="Line one\nLine two\nLine three"
|
||||||
|
|
||||||
### Items
|
### Items
|
||||||
|
|
||||||
- **Applies to:** ListBox, ComboBox
|
- **Applies to:** ListBox, ComboBox, RadioGroup
|
||||||
- **Format:** Quoted string, items separated by `\n`
|
- **Format:** Quoted string, items separated by `\n`
|
||||||
- **Example:** `Items="Red\nGreen\nBlue"`
|
- **Example:** `Items="Red\nGreen\nBlue"`
|
||||||
|
|
||||||
|
|
@ -390,7 +394,7 @@ new items are added.
|
||||||
|
|
||||||
### Checked
|
### Checked
|
||||||
|
|
||||||
- **Applies to:** CheckBox, RadioButton
|
- **Applies to:** CheckBox, RadioButton, MenuItem
|
||||||
- **Format:** `0` (unchecked) or `1` (checked)
|
- **Format:** `0` (unchecked) or `1` (checked)
|
||||||
- **Example:** `Checked=1`
|
- **Example:** `Checked=1`
|
||||||
|
|
||||||
|
|
@ -433,7 +437,7 @@ Maximum number of characters the user can type.
|
||||||
|
|
||||||
### ItemIndex
|
### ItemIndex
|
||||||
|
|
||||||
- **Applies to:** ListBox, ComboBox
|
- **Applies to:** ListBox, ComboBox, RadioGroup
|
||||||
- **Format:** Integer (-1 = no selection)
|
- **Format:** Integer (-1 = no selection)
|
||||||
- **Example:** `ItemIndex=2`
|
- **Example:** `ItemIndex=2`
|
||||||
|
|
||||||
|
|
@ -588,6 +592,41 @@ Pseudo-property that triggers a method call instead of setting a
|
||||||
value. Valid commands: `Open`, `Play`, `Stop`, `Close`, `Pause`,
|
value. Valid commands: `Open`, `Play`, `Stop`, `Close`, `Pause`,
|
||||||
`Resume`, `Rewind`, `Next`, `Previous`.
|
`Resume`, `Rewind`, `Next`, `Previous`.
|
||||||
|
|
||||||
|
### Parent
|
||||||
|
|
||||||
|
- **Applies to:** MenuItem
|
||||||
|
- **Format:** Integer (ctrlId of parent menu or menu item)
|
||||||
|
- **Example:** `Parent=1`
|
||||||
|
|
||||||
|
Specifies the parent for a menu item. The parent can be a MainMenu,
|
||||||
|
PopupMenu, or another MenuItem (for submenus).
|
||||||
|
|
||||||
|
### Columns
|
||||||
|
|
||||||
|
- **Applies to:** RadioGroup
|
||||||
|
- **Format:** Integer
|
||||||
|
- **Example:** `Columns=2`
|
||||||
|
|
||||||
|
Number of columns for the radio button layout.
|
||||||
|
|
||||||
|
### ShortCut
|
||||||
|
|
||||||
|
- **Applies to:** MenuItem
|
||||||
|
- **Format:** Integer (Delphi ShortCut value)
|
||||||
|
- **Example:** `ShortCut=16467`
|
||||||
|
|
||||||
|
Keyboard accelerator for the menu item. Uses Delphi's ShortCut
|
||||||
|
encoding (virtual key + modifier flags).
|
||||||
|
|
||||||
|
### PopupMenu
|
||||||
|
|
||||||
|
- **Applies to:** Any visual control
|
||||||
|
- **Format:** Integer (ctrlId of a PopupMenu)
|
||||||
|
- **Example:** `PopupMenu=2`
|
||||||
|
|
||||||
|
Associates a PopupMenu with a control. When the user right-clicks
|
||||||
|
the control, the popup menu is displayed.
|
||||||
|
|
||||||
### BasePath
|
### BasePath
|
||||||
|
|
||||||
`BasePath` is a property on `TFormClient` (not a protocol property).
|
`BasePath` is a property on `TFormClient` (not a protocol property).
|
||||||
|
|
@ -619,6 +658,8 @@ No `EVENT.BIND` command is needed.
|
||||||
| ListBox | Select | `<index> "selected text"` |
|
| ListBox | Select | `<index> "selected text"` |
|
||||||
| ComboBox | Select | `<index> "selected text"` |
|
| ComboBox | Select | `<index> "selected text"` |
|
||||||
| ComboBox | Change | `"new text"` |
|
| ComboBox | Change | `"new text"` |
|
||||||
|
| MenuItem | Click | (none) |
|
||||||
|
| RadioGroup | Click | `<itemIndex>` |
|
||||||
|
|
||||||
### Opt-in Events
|
### Opt-in Events
|
||||||
|
|
||||||
|
|
@ -666,6 +707,25 @@ EVENT 1 3 KeyDown 13 (client sends back when Enter pressed)
|
||||||
EVENT.UNBIND 1 3 KeyDown (server disconnects)
|
EVENT.UNBIND 1 3 KeyDown (server disconnects)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Menu Hierarchy Example
|
||||||
|
|
||||||
|
Menus are built using flat CTRL.CREATE commands with `Parent` properties
|
||||||
|
to establish the hierarchy:
|
||||||
|
|
||||||
|
```
|
||||||
|
CTRL.CREATE 1 1 MainMenu 0 0 0 0
|
||||||
|
CTRL.CREATE 1 2 MenuItem 0 0 0 0 Caption="&File" Parent=1
|
||||||
|
CTRL.CREATE 1 3 MenuItem 0 0 0 0 Caption="&Open" Parent=2 ShortCut=16463
|
||||||
|
CTRL.CREATE 1 4 MenuItem 0 0 0 0 Caption="&Save" Parent=2 ShortCut=16467
|
||||||
|
CTRL.CREATE 1 5 MenuItem 0 0 0 0 Caption="E&xit" Parent=2
|
||||||
|
CTRL.CREATE 1 6 MenuItem 0 0 0 0 Caption="&Help" Parent=1
|
||||||
|
CTRL.CREATE 1 7 MenuItem 0 0 0 0 Caption="&About" Parent=6
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a menu bar with File (Open, Save, Exit) and Help (About).
|
||||||
|
MenuItem ctrl 2 is a child of MainMenu ctrl 1; ctrl 3-5 are children of
|
||||||
|
the File item (ctrl 2); ctrl 7 is a child of the Help item (ctrl 6).
|
||||||
|
|
||||||
## String Encoding
|
## String Encoding
|
||||||
|
|
||||||
All strings in the protocol are double-quoted. The following escape
|
All strings in the protocol are double-quoted. The following escape
|
||||||
|
|
@ -697,9 +757,8 @@ any protocol or application code.
|
||||||
## Limitations
|
## Limitations
|
||||||
|
|
||||||
- **RadioButton grouping:** All RadioButtons on a form belong to one
|
- **RadioButton grouping:** All RadioButtons on a form belong to one
|
||||||
group. There is no support for multiple independent radio groups
|
group. Use RadioGroup for multiple independent radio groups per
|
||||||
per form (the protocol has a flat parent model with no child
|
form.
|
||||||
containment).
|
|
||||||
- **Image format:** Only BMP files are supported for the Picture
|
- **Image format:** Only BMP files are supported for the Picture
|
||||||
property.
|
property.
|
||||||
- **dfm2form and Picture:** The converter cannot extract image
|
- **dfm2form and Picture:** The converter cannot extract image
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,11 @@ typedef enum {
|
||||||
ctRadioButton,
|
ctRadioButton,
|
||||||
ctPanel,
|
ctPanel,
|
||||||
ctScrollBar,
|
ctScrollBar,
|
||||||
ctMediaPlayer
|
ctMediaPlayer,
|
||||||
|
ctMainMenu,
|
||||||
|
ctPopupMenu,
|
||||||
|
ctMenuItem,
|
||||||
|
ctRadioGroup
|
||||||
} CtrlTypeE;
|
} CtrlTypeE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -103,6 +107,11 @@ typedef struct {
|
||||||
bool hasAutoOpen;
|
bool hasAutoOpen;
|
||||||
bool hasFileName;
|
bool hasFileName;
|
||||||
bool hasDeviceType;
|
bool hasDeviceType;
|
||||||
|
int32_t parentCtrlIdx;
|
||||||
|
int32_t columns;
|
||||||
|
int32_t shortCut;
|
||||||
|
bool hasColumns;
|
||||||
|
bool hasShortCut;
|
||||||
} DfmCtrlT;
|
} DfmCtrlT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -143,7 +152,7 @@ static void escapeStr(const char *src, char *dst, int32_t dstSize);
|
||||||
static void initCtrl(DfmCtrlT *ctrl);
|
static void initCtrl(DfmCtrlT *ctrl);
|
||||||
static void initForm(DfmFormT *form);
|
static void initForm(DfmFormT *form);
|
||||||
static CtrlTypeE mapClassName(const char *className);
|
static CtrlTypeE mapClassName(const char *className);
|
||||||
static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot);
|
static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot, int32_t parentIdx);
|
||||||
static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, DfmCtrlT *ctrl, bool isForm);
|
static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, DfmCtrlT *ctrl, bool isForm);
|
||||||
static int32_t readByte(const uint8_t *data, int32_t size, int32_t *pos);
|
static int32_t readByte(const uint8_t *data, int32_t size, int32_t *pos);
|
||||||
static int32_t readInt16LE(const uint8_t *data, int32_t size, int32_t *pos);
|
static int32_t readInt16LE(const uint8_t *data, int32_t size, int32_t *pos);
|
||||||
|
|
@ -360,6 +369,18 @@ static CtrlTypeE mapClassName(const char *className) {
|
||||||
if (stricmp_local(className, "TMediaPlayer") == 0) {
|
if (stricmp_local(className, "TMediaPlayer") == 0) {
|
||||||
return ctMediaPlayer;
|
return ctMediaPlayer;
|
||||||
}
|
}
|
||||||
|
if (stricmp_local(className, "TMainMenu") == 0) {
|
||||||
|
return ctMainMenu;
|
||||||
|
}
|
||||||
|
if (stricmp_local(className, "TPopupMenu") == 0) {
|
||||||
|
return ctPopupMenu;
|
||||||
|
}
|
||||||
|
if (stricmp_local(className, "TMenuItem") == 0) {
|
||||||
|
return ctMenuItem;
|
||||||
|
}
|
||||||
|
if (stricmp_local(className, "TRadioGroup") == 0) {
|
||||||
|
return ctRadioGroup;
|
||||||
|
}
|
||||||
return ctUnknown;
|
return ctUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,13 +391,14 @@ static CtrlTypeE mapClassName(const char *className) {
|
||||||
|
|
||||||
static void initCtrl(DfmCtrlT *ctrl) {
|
static void initCtrl(DfmCtrlT *ctrl) {
|
||||||
memset(ctrl, 0, sizeof(DfmCtrlT));
|
memset(ctrl, 0, sizeof(DfmCtrlT));
|
||||||
ctrl->enabled = 1;
|
ctrl->enabled = 1;
|
||||||
ctrl->visible = 1;
|
ctrl->visible = 1;
|
||||||
ctrl->itemIndex = -1;
|
ctrl->itemIndex = -1;
|
||||||
ctrl->tabOrder = -1;
|
ctrl->tabOrder = -1;
|
||||||
ctrl->bevelOuter = 1; // bvRaised
|
ctrl->bevelOuter = 1; // bvRaised
|
||||||
ctrl->largeChange = 1;
|
ctrl->largeChange = 1;
|
||||||
ctrl->smallChange = 1;
|
ctrl->smallChange = 1;
|
||||||
|
ctrl->parentCtrlIdx = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -790,6 +812,12 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
|
||||||
ctrl->autoOpen = readIntValue(data, size, pos, tag);
|
ctrl->autoOpen = readIntValue(data, size, pos, tag);
|
||||||
}
|
}
|
||||||
ctrl->hasAutoOpen = true;
|
ctrl->hasAutoOpen = true;
|
||||||
|
} else if (!isForm && stricmp_local(propName, "Columns") == 0) {
|
||||||
|
ctrl->columns = readIntValue(data, size, pos, tag);
|
||||||
|
ctrl->hasColumns = true;
|
||||||
|
} else if (!isForm && stricmp_local(propName, "ShortCut") == 0) {
|
||||||
|
ctrl->shortCut = readIntValue(data, size, pos, tag);
|
||||||
|
ctrl->hasShortCut = true;
|
||||||
} else {
|
} else {
|
||||||
skipValue(data, size, pos, tag);
|
skipValue(data, size, pos, tag);
|
||||||
}
|
}
|
||||||
|
|
@ -801,7 +829,7 @@ static void parseProperties(const uint8_t *data, int32_t size, int32_t *pos, Dfm
|
||||||
// Parse a component (form or child control) recursively
|
// Parse a component (form or child control) recursively
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot) {
|
static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmFormT *form, bool isRoot, int32_t parentIdx) {
|
||||||
// Check for flags byte (Delphi 1.0 rarely uses this but handle it)
|
// Check for flags byte (Delphi 1.0 rarely uses this but handle it)
|
||||||
uint8_t peek = data[*pos];
|
uint8_t peek = data[*pos];
|
||||||
if ((peek & 0xF0) == 0xF0) {
|
if ((peek & 0xF0) == 0xF0) {
|
||||||
|
|
@ -833,7 +861,7 @@ static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmF
|
||||||
(*pos)++; // consume terminator
|
(*pos)++; // consume terminator
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parseComponent(data, size, pos, form, false);
|
parseComponent(data, size, pos, form, false, -1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Child control
|
// Child control
|
||||||
|
|
@ -851,7 +879,7 @@ static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmF
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parseComponent(data, size, pos, form, false);
|
parseComponent(data, size, pos, form, false, -1);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -861,22 +889,24 @@ static bool parseComponent(const uint8_t *data, int32_t size, int32_t *pos, DfmF
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
DfmCtrlT *ctrl = &form->ctrls[form->ctrlCount];
|
int32_t myIdx = form->ctrlCount;
|
||||||
|
DfmCtrlT *ctrl = &form->ctrls[myIdx];
|
||||||
initCtrl(ctrl);
|
initCtrl(ctrl);
|
||||||
ctrl->type = type;
|
ctrl->type = type;
|
||||||
|
ctrl->parentCtrlIdx = parentIdx;
|
||||||
snprintf(ctrl->name, sizeof(ctrl->name), "%s", instName);
|
snprintf(ctrl->name, sizeof(ctrl->name), "%s", instName);
|
||||||
|
|
||||||
parseProperties(data, size, pos, form, ctrl, false);
|
parseProperties(data, size, pos, form, ctrl, false);
|
||||||
form->ctrlCount++;
|
form->ctrlCount++;
|
||||||
|
|
||||||
// Skip nested children (controls within controls, e.g., panels)
|
// Recurse into children (menu items, panels, etc.)
|
||||||
while (*pos < size) {
|
while (*pos < size) {
|
||||||
peek = data[*pos];
|
peek = data[*pos];
|
||||||
if (peek == 0x00) {
|
if (peek == 0x00) {
|
||||||
(*pos)++;
|
(*pos)++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
parseComponent(data, size, pos, form, false);
|
parseComponent(data, size, pos, form, false, myIdx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -931,13 +961,16 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
|
||||||
"Unknown", "Label", "Edit", "Button",
|
"Unknown", "Label", "Edit", "Button",
|
||||||
"CheckBox", "ListBox", "ComboBox", "Memo",
|
"CheckBox", "ListBox", "ComboBox", "Memo",
|
||||||
"Image", "GroupBox", "RadioButton", "Panel",
|
"Image", "GroupBox", "RadioButton", "Panel",
|
||||||
"ScrollBar", "MediaPlayer"
|
"ScrollBar", "MediaPlayer",
|
||||||
|
"MainMenu", "PopupMenu", "MenuItem", "RadioGroup"
|
||||||
};
|
};
|
||||||
char escaped[8192];
|
char escaped[8192];
|
||||||
|
|
||||||
fprintf(out, "CTRL.CREATE %d %d %s %d %d %d %d",
|
if (ctrl->type == ctMainMenu || ctrl->type == ctPopupMenu || ctrl->type == ctMenuItem) {
|
||||||
formId, ctrlId, typeNames[ctrl->type],
|
fprintf(out, "CTRL.CREATE %d %d %s 0 0 0 0", formId, ctrlId, typeNames[ctrl->type]);
|
||||||
ctrl->left, ctrl->top, ctrl->width, ctrl->height);
|
} else {
|
||||||
|
fprintf(out, "CTRL.CREATE %d %d %s %d %d %d %d", formId, ctrlId, typeNames[ctrl->type], ctrl->left, ctrl->top, ctrl->width, ctrl->height);
|
||||||
|
}
|
||||||
|
|
||||||
// Inline properties
|
// Inline properties
|
||||||
if (ctrl->hasCaption) {
|
if (ctrl->hasCaption) {
|
||||||
|
|
@ -1023,6 +1056,15 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
|
||||||
if (ctrl->hasAutoOpen && ctrl->autoOpen) {
|
if (ctrl->hasAutoOpen && ctrl->autoOpen) {
|
||||||
fprintf(out, " AutoOpen=1");
|
fprintf(out, " AutoOpen=1");
|
||||||
}
|
}
|
||||||
|
if (ctrl->type == ctMenuItem && ctrl->parentCtrlIdx >= 0) {
|
||||||
|
fprintf(out, " Parent=%d", ctrl->parentCtrlIdx + 1);
|
||||||
|
}
|
||||||
|
if (ctrl->hasColumns && ctrl->columns != 0) {
|
||||||
|
fprintf(out, " Columns=%d", ctrl->columns);
|
||||||
|
}
|
||||||
|
if (ctrl->hasShortCut && ctrl->shortCut != 0) {
|
||||||
|
fprintf(out, " ShortCut=%d", ctrl->shortCut);
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(out, "\n");
|
fprintf(out, "\n");
|
||||||
|
|
||||||
|
|
@ -1030,7 +1072,7 @@ static void emitCtrl(FILE *out, int32_t formId, int32_t ctrlId, DfmCtrlT *ctrl)
|
||||||
// Auto-wired: Button/CheckBox→Click, Edit→Change, ListBox→Select,
|
// Auto-wired: Button/CheckBox→Click, Edit→Change, ListBox→Select,
|
||||||
// ComboBox→Select+Change, Memo→Change
|
// ComboBox→Select+Change, Memo→Change
|
||||||
|
|
||||||
bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton);
|
bool autoClick = (ctrl->type == ctButton || ctrl->type == ctCheckBox || ctrl->type == ctRadioButton || ctrl->type == ctMenuItem || ctrl->type == ctRadioGroup);
|
||||||
bool autoChange = (ctrl->type == ctEdit || ctrl->type == ctComboBox || ctrl->type == ctMemo || ctrl->type == ctScrollBar);
|
bool autoChange = (ctrl->type == ctEdit || ctrl->type == ctComboBox || ctrl->type == ctMemo || ctrl->type == ctScrollBar);
|
||||||
bool autoSelect = (ctrl->type == ctListBox || ctrl->type == ctComboBox);
|
bool autoSelect = (ctrl->type == ctListBox || ctrl->type == ctComboBox);
|
||||||
|
|
||||||
|
|
@ -1160,7 +1202,7 @@ int main(int argc, char *argv[]) {
|
||||||
DfmFormT form;
|
DfmFormT form;
|
||||||
initForm(&form);
|
initForm(&form);
|
||||||
int32_t pos = 4; // skip TPF0
|
int32_t pos = 4; // skip TPF0
|
||||||
parseComponent(data, (int32_t)fileSize, &pos, &form, true);
|
parseComponent(data, (int32_t)fileSize, &pos, &form, true, -1);
|
||||||
|
|
||||||
// Default caption if empty
|
// Default caption if empty
|
||||||
if (form.caption[0] == '\0') {
|
if (form.caption[0] == '\0') {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ unit FormCli;
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, MPlayer, WinTypes, WinProcs;
|
SysUtils, Classes, Controls, Forms, StdCtrls, ExtCtrls, Menus, MPlayer, WinTypes, WinProcs;
|
||||||
|
|
||||||
const
|
const
|
||||||
MaxMsgLen = 4096;
|
MaxMsgLen = 4096;
|
||||||
|
|
@ -29,7 +29,8 @@ type
|
||||||
TCtrlTypeE = (ctUnknown, ctLabel, ctEdit, ctButton,
|
TCtrlTypeE = (ctUnknown, ctLabel, ctEdit, ctButton,
|
||||||
ctCheckBox, ctListBox, ctComboBox, ctMemo,
|
ctCheckBox, ctListBox, ctComboBox, ctMemo,
|
||||||
ctImage, ctGroupBox, ctRadioButton, ctPanel,
|
ctImage, ctGroupBox, ctRadioButton, ctPanel,
|
||||||
ctScrollBar, ctMediaPlayer);
|
ctScrollBar, ctMediaPlayer,
|
||||||
|
ctMainMenu, ctPopupMenu, ctMenuItem, ctRadioGroup);
|
||||||
|
|
||||||
{ Bound event flags }
|
{ Bound event flags }
|
||||||
TBoundEvent = (beDblClick, beKeyDown, beKeyUp,
|
TBoundEvent = (beDblClick, beKeyDown, beKeyUp,
|
||||||
|
|
@ -42,7 +43,7 @@ type
|
||||||
TFormCtrlRec = record
|
TFormCtrlRec = record
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
CtrlType: TCtrlTypeE;
|
CtrlType: TCtrlTypeE;
|
||||||
Control: TControl;
|
Control: TComponent;
|
||||||
Bound: TBoundEvents;
|
Bound: TBoundEvents;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -81,8 +82,8 @@ type
|
||||||
procedure FreeCtrlRec(CR: PFormCtrlRec);
|
procedure FreeCtrlRec(CR: PFormCtrlRec);
|
||||||
|
|
||||||
{ Property application }
|
{ Property application }
|
||||||
procedure ApplyProp(CR: PFormCtrlRec; Key, Value: PChar);
|
procedure ApplyProp(FR: PFormRec; CR: PFormCtrlRec; Key, Value: PChar);
|
||||||
procedure ApplyInlineProps(CR: PFormCtrlRec; P: PChar);
|
procedure ApplyInlineProps(FR: PFormRec; CR: PFormCtrlRec; P: PChar);
|
||||||
|
|
||||||
{ Event wiring }
|
{ Event wiring }
|
||||||
procedure WireAutoEvents(CR: PFormCtrlRec);
|
procedure WireAutoEvents(CR: PFormCtrlRec);
|
||||||
|
|
@ -109,7 +110,9 @@ type
|
||||||
procedure HandleClick(Sender: TObject);
|
procedure HandleClick(Sender: TObject);
|
||||||
procedure HandleRadioButtonClick(Sender: TObject);
|
procedure HandleRadioButtonClick(Sender: TObject);
|
||||||
procedure HandleScrollBarChange(Sender: TObject);
|
procedure HandleScrollBarChange(Sender: TObject);
|
||||||
|
procedure HandleMenuItemClick(Sender: TObject);
|
||||||
procedure HandleMediaPlayerNotify(Sender: TObject);
|
procedure HandleMediaPlayerNotify(Sender: TObject);
|
||||||
|
procedure HandleRadioGroupClick(Sender: TObject);
|
||||||
|
|
||||||
{ 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);
|
||||||
|
|
@ -333,8 +336,12 @@ end;
|
||||||
|
|
||||||
procedure TFormClient.FreeCtrlRec(CR: PFormCtrlRec);
|
procedure TFormClient.FreeCtrlRec(CR: PFormCtrlRec);
|
||||||
begin
|
begin
|
||||||
|
{ Menu items are owned by their parent menu and freed automatically }
|
||||||
if CR^.Control <> nil then
|
if CR^.Control <> nil then
|
||||||
CR^.Control.Free;
|
begin
|
||||||
|
if not (CR^.Control is TMenuItem) then
|
||||||
|
CR^.Control.Free;
|
||||||
|
end;
|
||||||
Dispose(CR);
|
Dispose(CR);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -403,7 +410,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Click', '');
|
SendEvent(FormId, CtrlId, 'Click', '');
|
||||||
|
|
@ -416,7 +423,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Click', '');
|
SendEvent(FormId, CtrlId, 'Click', '');
|
||||||
|
|
@ -431,7 +438,7 @@ var
|
||||||
Escaped: array[0..4095] of Char;
|
Escaped: array[0..4095] of Char;
|
||||||
Txt: string;
|
Txt: string;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Txt := (Sender as TEdit).Text;
|
Txt := (Sender as TEdit).Text;
|
||||||
|
|
@ -449,7 +456,7 @@ var
|
||||||
Escaped: array[0..4095] of Char;
|
Escaped: array[0..4095] of Char;
|
||||||
Txt: string;
|
Txt: string;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Txt := (Sender as TMemo).Text;
|
Txt := (Sender as TMemo).Text;
|
||||||
|
|
@ -467,7 +474,7 @@ var
|
||||||
Escaped: array[0..4095] of Char;
|
Escaped: array[0..4095] of Char;
|
||||||
Txt: string;
|
Txt: string;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Txt := (Sender as TComboBox).Text;
|
Txt := (Sender as TComboBox).Text;
|
||||||
|
|
@ -486,7 +493,7 @@ var
|
||||||
Escaped: array[0..4095] of Char;
|
Escaped: array[0..4095] of Char;
|
||||||
Txt: string;
|
Txt: string;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Idx := (Sender as TListBox).ItemIndex;
|
Idx := (Sender as TListBox).ItemIndex;
|
||||||
|
|
@ -510,7 +517,7 @@ var
|
||||||
Escaped: array[0..4095] of Char;
|
Escaped: array[0..4095] of Char;
|
||||||
Txt: string;
|
Txt: string;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Idx := (Sender as TComboBox).ItemIndex;
|
Idx := (Sender as TComboBox).ItemIndex;
|
||||||
|
|
@ -531,7 +538,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'DblClick', '');
|
SendEvent(FormId, CtrlId, 'DblClick', '');
|
||||||
|
|
@ -544,7 +551,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Enter', '');
|
SendEvent(FormId, CtrlId, 'Enter', '');
|
||||||
|
|
@ -557,7 +564,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Exit', '');
|
SendEvent(FormId, CtrlId, 'Exit', '');
|
||||||
|
|
@ -571,7 +578,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'KeyDown', IntToStr(Key));
|
SendEvent(FormId, CtrlId, 'KeyDown', IntToStr(Key));
|
||||||
|
|
@ -585,7 +592,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'KeyUp', IntToStr(Key));
|
SendEvent(FormId, CtrlId, 'KeyUp', IntToStr(Key));
|
||||||
|
|
@ -600,7 +607,7 @@ var
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
Btn: Integer;
|
Btn: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Btn := Ord(Button);
|
Btn := Ord(Button);
|
||||||
|
|
@ -617,7 +624,7 @@ var
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
Btn: Integer;
|
Btn: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
Btn := Ord(Button);
|
Btn := Ord(Button);
|
||||||
|
|
@ -633,7 +640,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'MouseMove',
|
SendEvent(FormId, CtrlId, 'MouseMove',
|
||||||
|
|
@ -647,7 +654,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Click', '');
|
SendEvent(FormId, CtrlId, 'Click', '');
|
||||||
|
|
@ -660,7 +667,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Click', '');
|
SendEvent(FormId, CtrlId, 'Click', '');
|
||||||
|
|
@ -673,7 +680,7 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Change', IntToStr((Sender as TScrollBar).Position));
|
SendEvent(FormId, CtrlId, 'Change', IntToStr((Sender as TScrollBar).Position));
|
||||||
|
|
@ -686,13 +693,39 @@ var
|
||||||
FormId: Integer;
|
FormId: Integer;
|
||||||
CtrlId: Integer;
|
CtrlId: Integer;
|
||||||
begin
|
begin
|
||||||
Tag := (Sender as TControl).Tag;
|
Tag := (Sender as TComponent).Tag;
|
||||||
FormId := Tag shr 16;
|
FormId := Tag shr 16;
|
||||||
CtrlId := Tag and $FFFF;
|
CtrlId := Tag and $FFFF;
|
||||||
SendEvent(FormId, CtrlId, 'Notify', '');
|
SendEvent(FormId, CtrlId, 'Notify', '');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TFormClient.HandleMenuItemClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
Tag: Longint;
|
||||||
|
FormId: Integer;
|
||||||
|
CtrlId: Integer;
|
||||||
|
begin
|
||||||
|
Tag := (Sender as TComponent).Tag;
|
||||||
|
FormId := Tag shr 16;
|
||||||
|
CtrlId := Tag and $FFFF;
|
||||||
|
SendEvent(FormId, CtrlId, 'Click', '');
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
procedure TFormClient.HandleRadioGroupClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
Tag: Longint;
|
||||||
|
FormId: Integer;
|
||||||
|
CtrlId: Integer;
|
||||||
|
begin
|
||||||
|
Tag := (Sender as TComponent).Tag;
|
||||||
|
FormId := Tag shr 16;
|
||||||
|
CtrlId := Tag and $FFFF;
|
||||||
|
SendEvent(FormId, CtrlId, 'Click', IntToStr((Sender as TRadioGroup).ItemIndex));
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TFormClient.HandleFormClose(Sender: TObject;
|
procedure TFormClient.HandleFormClose(Sender: TObject;
|
||||||
var Action: TCloseAction);
|
var Action: TCloseAction);
|
||||||
var
|
var
|
||||||
|
|
@ -730,6 +763,10 @@ begin
|
||||||
(CR^.Control as TRadioButton).OnClick := HandleRadioButtonClick;
|
(CR^.Control as TRadioButton).OnClick := HandleRadioButtonClick;
|
||||||
ctScrollBar:
|
ctScrollBar:
|
||||||
(CR^.Control as TScrollBar).OnChange := HandleScrollBarChange;
|
(CR^.Control as TScrollBar).OnChange := HandleScrollBarChange;
|
||||||
|
ctMenuItem:
|
||||||
|
(CR^.Control as TMenuItem).OnClick := HandleMenuItemClick;
|
||||||
|
ctRadioGroup:
|
||||||
|
(CR^.Control as TRadioGroup).OnClick := HandleRadioGroupClick;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -738,7 +775,8 @@ procedure TFormClient.WireOptEvent(CR: PFormCtrlRec; const EventName: string);
|
||||||
begin
|
begin
|
||||||
if EventName = 'DblClick' then
|
if EventName = 'DblClick' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnDblClick := HandleDblClick;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnDblClick := HandleDblClick;
|
||||||
CR^.Bound := CR^.Bound + [beDblClick];
|
CR^.Bound := CR^.Bound + [beDblClick];
|
||||||
end
|
end
|
||||||
else if EventName = 'Enter' then
|
else if EventName = 'Enter' then
|
||||||
|
|
@ -767,17 +805,20 @@ begin
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseDown' then
|
else if EventName = 'MouseDown' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseDown := HandleMouseDown;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseDown := HandleMouseDown;
|
||||||
CR^.Bound := CR^.Bound + [beMouseDown];
|
CR^.Bound := CR^.Bound + [beMouseDown];
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseUp' then
|
else if EventName = 'MouseUp' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseUp := HandleMouseUp;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseUp := HandleMouseUp;
|
||||||
CR^.Bound := CR^.Bound + [beMouseUp];
|
CR^.Bound := CR^.Bound + [beMouseUp];
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseMove' then
|
else if EventName = 'MouseMove' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseMove := HandleMouseMove;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseMove := HandleMouseMove;
|
||||||
CR^.Bound := CR^.Bound + [beMouseMove];
|
CR^.Bound := CR^.Bound + [beMouseMove];
|
||||||
end
|
end
|
||||||
else if EventName = 'Click' then
|
else if EventName = 'Click' then
|
||||||
|
|
@ -803,7 +844,8 @@ procedure TFormClient.UnwireOptEvent(CR: PFormCtrlRec; const EventName: string);
|
||||||
begin
|
begin
|
||||||
if EventName = 'DblClick' then
|
if EventName = 'DblClick' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnDblClick := nil;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnDblClick := nil;
|
||||||
CR^.Bound := CR^.Bound - [beDblClick];
|
CR^.Bound := CR^.Bound - [beDblClick];
|
||||||
end
|
end
|
||||||
else if EventName = 'Enter' then
|
else if EventName = 'Enter' then
|
||||||
|
|
@ -832,17 +874,20 @@ begin
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseDown' then
|
else if EventName = 'MouseDown' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseDown := nil;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseDown := nil;
|
||||||
CR^.Bound := CR^.Bound - [beMouseDown];
|
CR^.Bound := CR^.Bound - [beMouseDown];
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseUp' then
|
else if EventName = 'MouseUp' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseUp := nil;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseUp := nil;
|
||||||
CR^.Bound := CR^.Bound - [beMouseUp];
|
CR^.Bound := CR^.Bound - [beMouseUp];
|
||||||
end
|
end
|
||||||
else if EventName = 'MouseMove' then
|
else if EventName = 'MouseMove' then
|
||||||
begin
|
begin
|
||||||
(CR^.Control as TControl).OnMouseMove := nil;
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).OnMouseMove := nil;
|
||||||
CR^.Bound := CR^.Bound - [beMouseMove];
|
CR^.Bound := CR^.Bound - [beMouseMove];
|
||||||
end
|
end
|
||||||
else if EventName = 'Click' then
|
else if EventName = 'Click' then
|
||||||
|
|
@ -866,7 +911,7 @@ end;
|
||||||
|
|
||||||
{ ----- Property application ----------------------------------------------- }
|
{ ----- Property application ----------------------------------------------- }
|
||||||
|
|
||||||
procedure TFormClient.ApplyProp(CR: PFormCtrlRec; Key, Value: PChar);
|
procedure TFormClient.ApplyProp(FR: PFormRec; CR: PFormCtrlRec; Key, Value: PChar);
|
||||||
var
|
var
|
||||||
S: string;
|
S: string;
|
||||||
Unesc: array[0..4095] of Char;
|
Unesc: array[0..4095] of Char;
|
||||||
|
|
@ -874,6 +919,7 @@ var
|
||||||
Lines: TStringList;
|
Lines: TStringList;
|
||||||
P: PChar;
|
P: PChar;
|
||||||
Start: PChar;
|
Start: PChar;
|
||||||
|
PCR: PFormCtrlRec;
|
||||||
begin
|
begin
|
||||||
S := StrPas(Key);
|
S := StrPas(Key);
|
||||||
|
|
||||||
|
|
@ -887,6 +933,8 @@ begin
|
||||||
ctGroupBox: (CR^.Control as TGroupBox).Caption := StrPas(Unesc);
|
ctGroupBox: (CR^.Control as TGroupBox).Caption := StrPas(Unesc);
|
||||||
ctRadioButton: (CR^.Control as TRadioButton).Caption := StrPas(Unesc);
|
ctRadioButton: (CR^.Control as TRadioButton).Caption := StrPas(Unesc);
|
||||||
ctPanel: (CR^.Control as TPanel).Caption := StrPas(Unesc);
|
ctPanel: (CR^.Control as TPanel).Caption := StrPas(Unesc);
|
||||||
|
ctMenuItem: (CR^.Control as TMenuItem).Caption := StrPas(Unesc);
|
||||||
|
ctRadioGroup: (CR^.Control as TRadioGroup).Caption := StrPas(Unesc);
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else if S = 'Text' then
|
else if S = 'Text' then
|
||||||
|
|
@ -975,6 +1023,26 @@ begin
|
||||||
if Start^ <> #0 then
|
if Start^ <> #0 then
|
||||||
(CR^.Control as TComboBox).Items.Add(StrPas(Start));
|
(CR^.Control as TComboBox).Items.Add(StrPas(Start));
|
||||||
end;
|
end;
|
||||||
|
ctRadioGroup:
|
||||||
|
begin
|
||||||
|
(CR^.Control as TRadioGroup).Items.Clear;
|
||||||
|
P := Unesc;
|
||||||
|
Start := P;
|
||||||
|
while P^ <> #0 do
|
||||||
|
begin
|
||||||
|
if P^ = #10 then
|
||||||
|
begin
|
||||||
|
P^ := #0;
|
||||||
|
(CR^.Control as TRadioGroup).Items.Add(StrPas(Start));
|
||||||
|
Inc(P);
|
||||||
|
Start := P;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Inc(P);
|
||||||
|
end;
|
||||||
|
if Start^ <> #0 then
|
||||||
|
(CR^.Control as TRadioGroup).Items.Add(StrPas(Start));
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else if S = 'Checked' then
|
else if S = 'Checked' then
|
||||||
|
|
@ -983,17 +1051,25 @@ begin
|
||||||
if CR^.CtrlType = ctCheckBox then
|
if CR^.CtrlType = ctCheckBox then
|
||||||
(CR^.Control as TCheckBox).Checked := (N <> 0)
|
(CR^.Control as TCheckBox).Checked := (N <> 0)
|
||||||
else if CR^.CtrlType = ctRadioButton then
|
else if CR^.CtrlType = ctRadioButton then
|
||||||
(CR^.Control as TRadioButton).Checked := (N <> 0);
|
(CR^.Control as TRadioButton).Checked := (N <> 0)
|
||||||
|
else if CR^.CtrlType = ctMenuItem then
|
||||||
|
(CR^.Control as TMenuItem).Checked := (N <> 0);
|
||||||
end
|
end
|
||||||
else if S = 'Enabled' then
|
else if S = 'Enabled' then
|
||||||
begin
|
begin
|
||||||
N := StrToIntDef(StrPas(Value), 1);
|
N := StrToIntDef(StrPas(Value), 1);
|
||||||
CR^.Control.Enabled := (N <> 0);
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).Enabled := (N <> 0)
|
||||||
|
else if CR^.Control is TMenuItem then
|
||||||
|
(CR^.Control as TMenuItem).Enabled := (N <> 0);
|
||||||
end
|
end
|
||||||
else if S = 'Visible' then
|
else if S = 'Visible' then
|
||||||
begin
|
begin
|
||||||
N := StrToIntDef(StrPas(Value), 1);
|
N := StrToIntDef(StrPas(Value), 1);
|
||||||
CR^.Control.Visible := (N <> 0);
|
if CR^.Control is TControl then
|
||||||
|
(CR^.Control as TControl).Visible := (N <> 0)
|
||||||
|
else if CR^.Control is TMenuItem then
|
||||||
|
(CR^.Control as TMenuItem).Visible := (N <> 0);
|
||||||
end
|
end
|
||||||
else if S = 'MaxLength' then
|
else if S = 'MaxLength' then
|
||||||
begin
|
begin
|
||||||
|
|
@ -1025,8 +1101,9 @@ begin
|
||||||
begin
|
begin
|
||||||
N := StrToIntDef(StrPas(Value), -1);
|
N := StrToIntDef(StrPas(Value), -1);
|
||||||
case CR^.CtrlType of
|
case CR^.CtrlType of
|
||||||
ctListBox: (CR^.Control as TListBox).ItemIndex := N;
|
ctListBox: (CR^.Control as TListBox).ItemIndex := N;
|
||||||
ctComboBox: (CR^.Control as TComboBox).ItemIndex := N;
|
ctComboBox: (CR^.Control as TComboBox).ItemIndex := N;
|
||||||
|
ctRadioGroup: (CR^.Control as TRadioGroup).ItemIndex := N;
|
||||||
end;
|
end;
|
||||||
end
|
end
|
||||||
else if S = 'Stretch' then
|
else if S = 'Stretch' then
|
||||||
|
|
@ -1196,11 +1273,45 @@ begin
|
||||||
except
|
except
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
end
|
||||||
|
else if S = 'Parent' then
|
||||||
|
begin
|
||||||
|
if CR^.CtrlType = ctMenuItem then
|
||||||
|
begin
|
||||||
|
N := StrToIntDef(StrPas(Value), 0);
|
||||||
|
PCR := FindCtrl(FR, N);
|
||||||
|
if PCR <> nil then
|
||||||
|
begin
|
||||||
|
if PCR^.Control is TMenu then
|
||||||
|
(PCR^.Control as TMenu).Items.Add(CR^.Control as TMenuItem)
|
||||||
|
else if PCR^.Control is TMenuItem then
|
||||||
|
(PCR^.Control as TMenuItem).Add(CR^.Control as TMenuItem);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else if S = 'Columns' then
|
||||||
|
begin
|
||||||
|
N := StrToIntDef(StrPas(Value), 1);
|
||||||
|
if CR^.CtrlType = ctRadioGroup then
|
||||||
|
(CR^.Control as TRadioGroup).Columns := N;
|
||||||
|
end
|
||||||
|
else if S = 'ShortCut' then
|
||||||
|
begin
|
||||||
|
N := StrToIntDef(StrPas(Value), 0);
|
||||||
|
if CR^.CtrlType = ctMenuItem then
|
||||||
|
(CR^.Control as TMenuItem).ShortCut := N;
|
||||||
|
end
|
||||||
|
else if S = 'PopupMenu' then
|
||||||
|
begin
|
||||||
|
N := StrToIntDef(StrPas(Value), 0);
|
||||||
|
PCR := FindCtrl(FR, N);
|
||||||
|
if (PCR <> nil) and (PCR^.Control is TPopupMenu) and (CR^.Control is TControl) then
|
||||||
|
(CR^.Control as TControl).PopupMenu := PCR^.Control as TPopupMenu;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
procedure TFormClient.ApplyInlineProps(CR: PFormCtrlRec; P: PChar);
|
procedure TFormClient.ApplyInlineProps(FR: PFormRec; CR: PFormCtrlRec; P: PChar);
|
||||||
var
|
var
|
||||||
Token: array[0..4095] of Char;
|
Token: array[0..4095] of Char;
|
||||||
Key: array[0..63] of Char;
|
Key: array[0..63] of Char;
|
||||||
|
|
@ -1248,7 +1359,7 @@ begin
|
||||||
StrCopy(Value, Eq);
|
StrCopy(Value, Eq);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ApplyProp(CR, Key, Value);
|
ApplyProp(FR, CR, Key, Value);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1286,6 +1397,14 @@ begin
|
||||||
Result := ctScrollBar
|
Result := ctScrollBar
|
||||||
else if S = 'MediaPlayer' then
|
else if S = 'MediaPlayer' then
|
||||||
Result := ctMediaPlayer
|
Result := ctMediaPlayer
|
||||||
|
else if S = 'MainMenu' then
|
||||||
|
Result := ctMainMenu
|
||||||
|
else if S = 'PopupMenu' then
|
||||||
|
Result := ctPopupMenu
|
||||||
|
else if S = 'MenuItem' then
|
||||||
|
Result := ctMenuItem
|
||||||
|
else if S = 'RadioGroup' then
|
||||||
|
Result := ctRadioGroup
|
||||||
else
|
else
|
||||||
Result := ctUnknown;
|
Result := ctUnknown;
|
||||||
end;
|
end;
|
||||||
|
|
@ -1378,7 +1497,7 @@ var
|
||||||
FR: PFormRec;
|
FR: PFormRec;
|
||||||
CR: PFormCtrlRec;
|
CR: PFormCtrlRec;
|
||||||
CType: TCtrlTypeE;
|
CType: TCtrlTypeE;
|
||||||
Ctrl: TControl;
|
Comp: TComponent;
|
||||||
begin
|
begin
|
||||||
FormId := ParseInt(P);
|
FormId := ParseInt(P);
|
||||||
CtrlId := ParseInt(P);
|
CtrlId := ParseInt(P);
|
||||||
|
|
@ -1398,62 +1517,82 @@ begin
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
{ Create the control }
|
{ Create the control }
|
||||||
Ctrl := nil;
|
Comp := nil;
|
||||||
case CType of
|
case CType of
|
||||||
ctLabel:
|
ctLabel:
|
||||||
begin
|
begin
|
||||||
Ctrl := TLabel.Create(FR^.Form);
|
Comp := TLabel.Create(FR^.Form);
|
||||||
(Ctrl as TLabel).AutoSize := False;
|
(Comp as TLabel).AutoSize := False;
|
||||||
end;
|
end;
|
||||||
ctEdit:
|
ctEdit:
|
||||||
Ctrl := TEdit.Create(FR^.Form);
|
Comp := TEdit.Create(FR^.Form);
|
||||||
ctButton:
|
ctButton:
|
||||||
Ctrl := TButton.Create(FR^.Form);
|
Comp := TButton.Create(FR^.Form);
|
||||||
ctCheckBox:
|
ctCheckBox:
|
||||||
Ctrl := TCheckBox.Create(FR^.Form);
|
Comp := TCheckBox.Create(FR^.Form);
|
||||||
ctListBox:
|
ctListBox:
|
||||||
Ctrl := TListBox.Create(FR^.Form);
|
Comp := TListBox.Create(FR^.Form);
|
||||||
ctComboBox:
|
ctComboBox:
|
||||||
Ctrl := TComboBox.Create(FR^.Form);
|
Comp := TComboBox.Create(FR^.Form);
|
||||||
ctMemo:
|
ctMemo:
|
||||||
Ctrl := TMemo.Create(FR^.Form);
|
Comp := TMemo.Create(FR^.Form);
|
||||||
ctImage:
|
ctImage:
|
||||||
begin
|
begin
|
||||||
Ctrl := TImage.Create(FR^.Form);
|
Comp := TImage.Create(FR^.Form);
|
||||||
(Ctrl as TImage).AutoSize := False;
|
(Comp as TImage).AutoSize := False;
|
||||||
end;
|
end;
|
||||||
ctGroupBox:
|
ctGroupBox:
|
||||||
Ctrl := TGroupBox.Create(FR^.Form);
|
Comp := TGroupBox.Create(FR^.Form);
|
||||||
ctRadioButton:
|
ctRadioButton:
|
||||||
Ctrl := TRadioButton.Create(FR^.Form);
|
Comp := TRadioButton.Create(FR^.Form);
|
||||||
ctPanel:
|
ctPanel:
|
||||||
Ctrl := TPanel.Create(FR^.Form);
|
Comp := TPanel.Create(FR^.Form);
|
||||||
ctScrollBar:
|
ctScrollBar:
|
||||||
Ctrl := TScrollBar.Create(FR^.Form);
|
Comp := TScrollBar.Create(FR^.Form);
|
||||||
ctMediaPlayer:
|
ctMediaPlayer:
|
||||||
Ctrl := TMediaPlayer.Create(FR^.Form);
|
Comp := TMediaPlayer.Create(FR^.Form);
|
||||||
|
ctMainMenu:
|
||||||
|
begin
|
||||||
|
Comp := TMainMenu.Create(FR^.Form);
|
||||||
|
FR^.Form.Menu := Comp as TMainMenu;
|
||||||
|
end;
|
||||||
|
ctPopupMenu:
|
||||||
|
Comp := TPopupMenu.Create(FR^.Form);
|
||||||
|
ctMenuItem:
|
||||||
|
Comp := TMenuItem.Create(FR^.Form);
|
||||||
|
ctRadioGroup:
|
||||||
|
Comp := TRadioGroup.Create(FR^.Form);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if Ctrl = nil then
|
if Comp = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
{ Set parent and geometry }
|
{ Set parent and geometry for visual controls }
|
||||||
if Ctrl is TWinControl then
|
if Comp is TWinControl then
|
||||||
(Ctrl as TWinControl).Parent := FR^.Form
|
begin
|
||||||
else
|
(Comp as TWinControl).Parent := FR^.Form;
|
||||||
Ctrl.Parent := FR^.Form;
|
(Comp as TControl).Left := Left;
|
||||||
|
(Comp as TControl).Top := Top;
|
||||||
|
(Comp as TControl).Width := Width;
|
||||||
|
(Comp as TControl).Height := Height;
|
||||||
|
end
|
||||||
|
else if Comp is TControl then
|
||||||
|
begin
|
||||||
|
(Comp as TControl).Parent := FR^.Form;
|
||||||
|
(Comp as TControl).Left := Left;
|
||||||
|
(Comp as TControl).Top := Top;
|
||||||
|
(Comp as TControl).Width := Width;
|
||||||
|
(Comp as TControl).Height := Height;
|
||||||
|
end;
|
||||||
|
{ else: non-visual (menus, menu items) — no parent or geometry }
|
||||||
|
|
||||||
Ctrl.Left := Left;
|
Comp.Tag := (Longint(FormId) shl 16) or CtrlId;
|
||||||
Ctrl.Top := Top;
|
|
||||||
Ctrl.Width := Width;
|
|
||||||
Ctrl.Height := Height;
|
|
||||||
Ctrl.Tag := (Longint(FormId) shl 16) or CtrlId;
|
|
||||||
|
|
||||||
{ Create control record }
|
{ Create control record }
|
||||||
New(CR);
|
New(CR);
|
||||||
CR^.CtrlId := CtrlId;
|
CR^.CtrlId := CtrlId;
|
||||||
CR^.CtrlType := CType;
|
CR^.CtrlType := CType;
|
||||||
CR^.Control := Ctrl;
|
CR^.Control := Comp;
|
||||||
CR^.Bound := [];
|
CR^.Bound := [];
|
||||||
|
|
||||||
FR^.Ctrls.Add(CR);
|
FR^.Ctrls.Add(CR);
|
||||||
|
|
@ -1462,7 +1601,7 @@ begin
|
||||||
WireAutoEvents(CR);
|
WireAutoEvents(CR);
|
||||||
|
|
||||||
{ Apply inline properties }
|
{ Apply inline properties }
|
||||||
ApplyInlineProps(CR, P);
|
ApplyInlineProps(FR, CR, P);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1484,7 +1623,7 @@ begin
|
||||||
if CR = nil then
|
if CR = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
|
||||||
ApplyInlineProps(CR, P);
|
ApplyInlineProps(FR, CR, P);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ Event data varies by event type:
|
||||||
|
|
||||||
| Event | Data |
|
| Event | Data |
|
||||||
|-----------|-------------------------------|
|
|-----------|-------------------------------|
|
||||||
| Click | (none) |
|
| Click | (none), or `<itemIndex>` (RadioGroup) |
|
||||||
| DblClick | (none) |
|
| DblClick | (none) |
|
||||||
| Change | `"new text"` or `<position>` (ScrollBar) |
|
| Change | `"new text"` or `<position>` (ScrollBar) |
|
||||||
| Select | `<index> "selected text"` |
|
| Select | `<index> "selected text"` |
|
||||||
|
|
@ -101,28 +101,38 @@ Event data varies by event type:
|
||||||
| Panel | TPanel | (none) |
|
| Panel | TPanel | (none) |
|
||||||
| ScrollBar | TScrollBar | Change |
|
| ScrollBar | TScrollBar | Change |
|
||||||
| MediaPlayer | TMediaPlayer | (none) |
|
| MediaPlayer | TMediaPlayer | (none) |
|
||||||
|
| MainMenu | TMainMenu | (none) |
|
||||||
|
| PopupMenu | TPopupMenu | (none) |
|
||||||
|
| MenuItem | TMenuItem | Click |
|
||||||
|
| RadioGroup | TRadioGroup | Click |
|
||||||
|
|
||||||
|
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
|
||||||
|
parent menu or menu item. One MainMenu per form (auto-attached).
|
||||||
|
PopupMenu is associated with any control via the `PopupMenu` property.
|
||||||
|
|
||||||
GroupBox and Panel are cosmetic containers (flat parent model — no child
|
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), DblClick, KeyDown, KeyUp, Enter, Exit,
|
||||||
MouseDown, MouseUp, MouseMove.
|
MouseDown, MouseUp, MouseMove. Menu and RadioGroup components do
|
||||||
|
not support opt-in events.
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Applies To | Value Format |
|
| Property | Applies To | Value Format |
|
||||||
|-------------|---------------------------------------------|-------------------------------------------|
|
|-------------|---------------------------------------------|-------------------------------------------|
|
||||||
| Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel | Quoted string |
|
| Caption | Label, Button, CheckBox, GroupBox, RadioButton, Panel, MenuItem, RadioGroup | Quoted string |
|
||||||
| Text | Edit, ComboBox, Memo | Quoted string (`\n` for line breaks) |
|
| Text | Edit, ComboBox, Memo | Quoted string (`\n` for line breaks) |
|
||||||
| Items | ListBox, ComboBox | Quoted string (`\n`-delimited) |
|
| Items | ListBox, ComboBox, RadioGroup | Quoted string (`\n`-delimited) |
|
||||||
| Checked | CheckBox, RadioButton | 0 or 1 |
|
| Checked | CheckBox, RadioButton, MenuItem | 0 or 1 |
|
||||||
| Enabled | All | 0 or 1 |
|
| Enabled | All | 0 or 1 |
|
||||||
| Visible | All | 0 or 1 |
|
| Visible | All | 0 or 1 |
|
||||||
| MaxLength | Edit | Integer |
|
| MaxLength | Edit | Integer |
|
||||||
| ReadOnly | Edit, Memo | 0 or 1 |
|
| ReadOnly | Edit, Memo | 0 or 1 |
|
||||||
| ScrollBars | Memo | 0-3 (ssNone..ssBoth) |
|
| ScrollBars | Memo | 0-3 (ssNone..ssBoth) |
|
||||||
| ItemIndex | ListBox, ComboBox | Integer (-1 = none) |
|
| ItemIndex | ListBox, ComboBox, RadioGroup | Integer (-1 = none) |
|
||||||
| TabOrder | All windowed controls | Integer |
|
| TabOrder | All windowed controls | Integer |
|
||||||
| Stretch | Image | 0 or 1 |
|
| Stretch | Image | 0 or 1 |
|
||||||
| Center | Image | 0 or 1 |
|
| Center | Image | 0 or 1 |
|
||||||
|
|
@ -141,6 +151,10 @@ MouseDown, MouseUp, MouseMove.
|
||||||
| DeviceType | MediaPlayer | Quoted string (e.g., `dtWaveAudio`) |
|
| DeviceType | MediaPlayer | Quoted string (e.g., `dtWaveAudio`) |
|
||||||
| AutoOpen | MediaPlayer | 0 or 1 |
|
| AutoOpen | MediaPlayer | 0 or 1 |
|
||||||
| Command | MediaPlayer | Quoted string (pseudo-property, see below)|
|
| Command | MediaPlayer | Quoted string (pseudo-property, see below)|
|
||||||
|
| Parent | MenuItem | Integer (ctrlId of parent menu/item) |
|
||||||
|
| Columns | RadioGroup | Integer (number of columns) |
|
||||||
|
| ShortCut | MenuItem | Integer (Delphi ShortCut value) |
|
||||||
|
| PopupMenu | Any TControl | Integer (ctrlId of PopupMenu) |
|
||||||
|
|
||||||
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.,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue