<SKILL_VISPRO_REXX_CUA>
## CUA & GUI Architecture
- Native Architecture: VisPro natively supports multithreading (semaphores, thread communication), Shared Memory, DDE, OS/2 Drag &amp; Drop, and Database Designer (SQL joins/DDL).
- Structure: Use Notebook-Controls for categorizing settings/views. Use Container-Controls for lists of objects.
- Dynamic Data: For system information, prefer Multiline Text Controls (Read-Only, Monospace) over rigidly placed Static Text fields to prevent truncation.
- Non-Blocking GUI: Never place long-running loops directly in the main GUI thread.
- Scope Safety (Critical): VisPro compiles event scripts into `_run.cmd`. ALWAYS prefix subroutine names uniquely (e.g., `Info_AddToContainer`) to prevent `Error 40` crashes across modules.
- Notebook Controls: Child forms MUST be explicitly set as "Notebook Page" types.
- Notebook Empty Check (Crash Prevent): NEVER use `VpGetItemCount` on a Notebook (causes Error 40). To check if a Notebook is empty, use `VpGetIndex(window, ITEMID, 'TOP') = 0`.
- Status Bar Polling &amp; PM Deadlocks: Use a central background timer (`STARTTIMER`) on the main window instead of hooking error-prone MLE events (Char, Changed, Mouse Moved) to track cursor/status updates.
- Timer I/O Pausing (Critical): ALWAYS pause background timers (`CALL VpWindow window, 'STOPTIMER'`) BEFORE executing blocking I/O operations (`VpLoadFromFile`, `VpSaveToFile`) or heavy GUI rebuilds (`VpOpenForm`), and restart them afterward. Firing SENDMSG at a locked MLE during I/O causes fatal OS/2 PM deadlocks.
- MLE SENDMSG PM Bug (Memory Corruption): NEVER pass `'1'` to the `mp2` (parameter 2) argument when sending line-query messages (e.g., `0x01be` MLM_LINEFROMCHAR, `0x01bd`, `0x01bf`) to an MLE. It is reserved OS/2 memory; passing non-zero corrupts the internal PM line-offset array. ALWAYS pass `'0'`.
- MLE Polling Race Conditions: When polling the current MLE line via `0x01be`, pass `'-1'` as the first parameter (`mp1`) to safely query the PM's internal cursor. Do not pass a manually queried cursor_idx (desync risk during fast typing).
- MLE Buffer Locks: Validate EVERY return value from SENDMSG polling using `DataType(var, 'W')`. Rapid typing temporarily locks the MLE buffer, causing SENDMSG to return an empty string and crashing subsequent math operations.
- STDIO Window Debugging: The standard I/O window (`SAY` outputs) is hidden by default in Macros (`.mac`). Enable it via `CALL VpWindow window,'ENABLESTDIO'`. For Executables (`.exe`), use `CALL VpWindow window,'DISABLESTDIO'` to explicitly hide output from users.
- VERIFIED MLE Line Limit (32 KB): The OS/2 Presentation Manager MLE-Control (as well as VisPro's `VpSetItemValue` and the clipboard) has no hard limit for total text size (multiple megabytes are possible). However, there is a strict PM limit of approx. 32 KB (32,767 bytes) for the length of a single text line. If Carriage Return / Line Feed (`'0d0a'x`) are missing, the text is harshly rejected or leads to PM deadlocks. Workaround: For massively generated text blocks, strictly enforce regular line breaks.

## VisPro Architecture Rule
- The file `_run.cmd` is a temporary compilation artifact of the IDE. It must never be modified or returned as a whole. Code changes must strictly be provided as individual event scripts (e.g., `261_1001.0:`) or separate subroutines.

## External File Compilation &amp; Label Generation
- VERIFIED Compiler Behavior: VisPro/REXX includes external subroutines by blindly appending their text into the compiled `_run.cmd`. VisPro automatically generates a label based on the filename (e.g., `filename.cmd:`) directly above the inserted code.

## Strict First-Line Rule (Error 17 Prevention)
- Rule: External `.cmd` files must NEVER contain a manual label on the first line (prevents duplicate labels).
- Rule: Line 1 of the external file MUST strictly be `Procedure` or `Procedure Expose ...`. This satisfies the Classic REXX rule that `Procedure` must be the first instruction following the (VisPro-generated) label. Scope leaks and REXX "Error 17: Unexpected PROCEDURE" are systematically avoided.

## External Function Call Syntax (Error 43 Prevention)
- VERIFIED Function Call Rule: When an external file is called as a function (with a return value and parentheses), the call MUST strictly include the `.cmd` suffix.
  - WRONG: `rc = MyFunc(var)` -&gt; Results in REXX "Error 43: Routine not found" because REXX looks for the label `MyFunc:`.
  - CORRECT: `rc = MyFunc.cmd(var)` -&gt; REXX finds the exact VisPro-generated label `MyFunc.cmd:`.
- Normal procedure calls via `CALL` remain unchanged since they already include the suffix: `CALL MyFunc.cmd`.
## VERIFIED PM-RACE-CONDITION WORKAROUNDS (DO NOT REFACTOR)
- Tab Creation Sequence: When creating new notebook tabs dynamically, the strict execution sequence MUST be:
  1. index = VpAddItem(...)          /* Create empty tab */
  2. CALL VpSelect ..., index        /* MUST happen BEFORE VpOpenForm to suppress the 256_1000.0 change event! */
  3. nb.index = VpOpenForm(...)      /* Load the child form */
  4. CALL VpSetItemValue ..., 'PAGE' /* Bind form to tab */
  NEVER rearrange this sequence. Moving VpSelect after VpOpenForm triggers a premature synchronous PM Event that causes fatal UI deadlocks.

</SKILL_VISPRO_REXX_CUA>

<SKILL_VISPRO_REFERENCE>
[VERIFIED COMMANDS ONLY]
## 1. Adding Elements (VpAddItem)
* At current cursor position (MLE): `CALL VpAddItem window, ITEMID, 'CURRENT', value`
* At the beginning of list/container: `index = VpAddItem(window, ITEMID, 'BEGIN', value)`
* At the beginning with icon (Container): `index = VpAddItem(window, ITEMID, 'BEGIN', value, icon)`
* At the end of list/container: `index = VpAddItem(window, ITEMID, 'END', value)`
* At the end with icon (Container): `index = VpAddItem(window, ITEMID, 'END', value, icon)`
* Multiple elements at the end: `index = VpAddItem(window, ITEMID, 'END', value1, value2)`
* After a specific element: `index = VpAddItem(window, ITEMID, index, value)`
* After an element with icon: `index = VpAddItem(window, ITEMID, index, value, icon)`
* As child element (Container Tree): `index = VpAddItem(window, ITEMID, 'CHILD', value, parent_index)`
* As child element with icon: `index = VpAddItem(window, ITEMID, 'CHILD', value, parent_index, icon)`
* Alphabetically ascending: `index = VpAddItem(window, ITEMID, 'ASCENDING', value)`
* Alphabetically descending: `index = VpAddItem(window, ITEMID, 'DESCENDING', value)`
* Bulk-load from stem: `index = VpAddItem(window, ITEMID, 'LIST', 'STEMNAME.')`
* Notebook page (Major Tab): `CALL VpAddItem window, ITEMID, 'Tab-Title', form_handle_or_0`
* Notebook page (Minor Tab): `CALL VpAddItem window, ITEMID, 'MINOR', 'Tab-Title', form_handle_or_0`
* Notebook page (No Tab): `CALL VpAddItem window, ITEMID, form_handle_or_0`

## 2. Selecting &amp; Checking Status (VpSelect / VpIsSelected)
* Mark/check element: `CALL VpSelect window, ITEMID, 1` (lists: `CALL VpSelect window, ITEMID, index, 1`)
* Unmark: `CALL VpSelect window, ITEMID, 0` (lists: `CALL VpSelect window, ITEMID, index, 0`)
* Set checkbox to 3-State: `CALL VpSelect window, ITEMID, 2`
* Mark all list elements: `CALL VpSelect window, ITEMID, 'ALL', 1`
* Unmark all: `CALL VpSelect window, ITEMID, 'ALL', 0`
* Mark ValueSet cell: `CALL VpSelect window, ITEMID, row, column`
* Mark text range (MLE): `CALL VpSelect window, ITEMID, first_char, last_char`
* Check if button/checkbox is active: `value = VpIsSelected(window, ITEMID)` (Returns 1 for True, 0 for False)
* Check if list element is selected: `value = VpIsSelected(window, ITEMID, index)`

## 3. Deleting Elements (VpDeleteItem)
* Delete specific element/page: `CALL VpDeleteItem window, ITEMID, index`
* Delete all elements: `CALL VpDeleteItem window, ITEMID, 'ALL'`

## 4. Querying Indices &amp; Counters (VpGetIndex / VpGetItemCount)
* Query element count: `count = VpGetItemCount(window, ITEMID)`
* First element index: `index = VpGetIndex(window, ITEMID, 'NEXT', 0)`
* Next element index: `index = VpGetIndex(window, ITEMID, 'NEXT', current_index)`
* First selected element: `index = VpGetIndex(window, ITEMID, 'SELECTED', 0)`
* Next selected element: `index = VpGetIndex(window, ITEMID, 'SELECTED', current_index)`
* First child element: `index = VpGetIndex(window, ITEMID, 'FIRSTCHILD', parent_index)`
* Parent element: `index = VpGetIndex(window, ITEMID, 'PARENT', child_index)`
* Search (index by string): `index = VpGetIndex(window, ITEMID, 'SEARCH', search_string)`
* Load all selected indices into stem: `CALL VpGetIndex window, ITEMID, 'ALLSELECTED', 'STEMNAME.'`
* Notebook pages: `index = VpGetIndex(window, ITEMID, 'FIRST' | 'NEXT' | 'TOP')`
* Text search in MLE (first/last bounds): `range = VpGetIndex(window, ITEMID, search_string)`
* ValueSet coordinates: `rowcol = VpGetIndex(window, ITEMID)` (returns string "row col")

## 5. Setting and Reading Values (VpSetItemValue / VpGetItemValue)
* Set generic value: `CALL VpSetItemValue window, ITEMID, value`
* Read generic value: `value = VpGetItemValue(window, ITEMID)`
* Set value at specific index: `CALL VpSetItemValue window, ITEMID, value, index`
* Container Details VERIFIED Workaround: Updating a single row in a multi-column Container (DETAILS view) requires a specific asymmetry between getting and setting values.
  * Rule: While `VpGetItemValue` strictly requires the 'DETAILS' keyword to read a row into a stem, `VpSetItemValue` MUST omit the 'DETAILS' keyword and directly pass the stem name string (including the dot) alongside the index for an in-place update.
  * Crash Prevention: Never use `VpAddItem` with a relative index (e.g., `sel_idx - 1`) and a stem array (Error 40). Do not delete/recreate rows; use in-place updates.
  * Code Pattern:
    `CALL VpGetItemValue window, ITEMID, 'DETAILS', 'crow.', index`
    `crow.1 = '[X]'`
    `CALL VpSetItemValue window, ITEMID, 'crow.', index`
* Read value at index: `value = VpGetItemValue(window, ITEMID, '', index)`
* Load all values into stem: `CALL VpGetItemValue window, ITEMID, 'ALL', 'STEMNAME.'`
* Read container columns into stem: `CALL VpGetItemValue window, ITEMID, 'DETAILS', 'STEMNAME.', index`
* Set cell value (ValueSet): `CALL VpSetItemValue window, ITEMID, value, row, column`
* Set graphic data (Charts): `rc = VpSetItemValue(window, ITEMID, 'DATA.')`
* Tab/Status text in Notebook: `CALL VpSetItemValue window, ITEMID, 'TABTEXT' | 'STATUSTEXT', index, string`
* Set slider arm position: `CALL VpSetItemValue window, ITEMID, 'SETARMPOS', tick_index`
* Read slider range: `value = VpGetItemValue(window, ITEMID, 'GETRANGE')`

## 6. Layout, Position &amp; Styles (VpItem / VpSetItemStyle)
* Disable / Enable: `CALL VpItem window, ITEMID, 'DISABLE' | 'ENABLE'`
* Hide / Show: `CALL VpItem window, ITEMID, 'HIDE' | 'SHOW'`
* Request focus: `CALL VpItem window, ITEMID, 'FOCUS'`
* Suspend/force redraw: `CALL VpItem window, ITEMID, 'DISABLEPAINT' | 'ENABLEPAINT' | 'PAINT'`
* Read position and size: `xy = VpItem(window, ITEMID, 'ITEMPOS' | 'ITEMSIZE')`
* Set position and size: `CALL VpItem window, ITEMID, 'SETITEMPOS', x, y, width, height`
* Read font: `font = VpItem(window, ITEMID, 'GETFONT')`
* Change font: `CALL VpItem window, ITEMID, 'FONT', '12.Helv'`
* Set colors (text/RGB): `CALL VpItem window, ITEMID, 'BACKCOLOR' | 'FORECOLOR' | 'BACKCOLORRGB' | 'FORECOLORRGB', color_value`
* Read PM Window Handle: `handle = VpItem(window, ITEMID, 'GETHANDLE')`
* Send PM Message directly: `rc = VpItem(window, ITEMID, 'SENDMSG', hex_message, [param1, param2...])`
* Change Container View: `rc = VpSetItemStyle(window, ITEMID, 'DETAILS' | 'ICON' | 'TREE' | 'TEXTTREE')`
* Change Column Title: `rc = VpSetItemStyle(window, ITEMID, 'COLNAME', col_number, 'Column Name')`
* Read &amp; calculate RGB colors: `color = VpItem(window, ITEMID, 'GETBACKCOLORRGB' | 'GETFORECOLORRGB')`
  (Note: OS/2 stores colors as `COLOR = R * 65536 + G * 256 + B`. Reverse math: `R = col % 65536`, `G = (col // 65536) % 256`, `B = col // 256`)
* Multi-Column Containers: Define `.0` index, pass stem string WITH trailing dot: `Call VpAddItem window, 1000, 'END', 'ZEILE.'`
* Container Splitbar Disable: `rc = VpSetItemStyle(window, itemid, 'SPLITBAR', 0, -1)`

## 7. Window Management (VpWindow &amp; Dialogs)
* Position/scale window: `CALL VpWindow window, 'SETPOS', x, y, xsize, ysize`
* Read window size/position: `xy = VpWindow(window, 'GETSIZE' | 'GETPOS')` (Parse with `PARSE VAR xy x y`)
* Hide/show/activate window: `CALL VpWindow window, 'HIDE' | 'SHOW' | 'ACTIVATE'`
* Minimize/maximize/restore: `CALL VpWindow window, 'MINIMIZE' | 'MAXIMIZE' | 'RESTORE'`
* Close/destroy window: `CALL VpWindow window, 'CLOSE' | 'DESTROY'`
* Set window title: `CALL VpWindow window, 'SETTITLE', value`
* Call pop-up menu: `CALL VpWindow window, 'POPUP', menuid`
* Query screen resolution: `xy = VpWindow(window, 'GETSCREENSIZE')` (Parse with `PARSE VAR xy x y`)
* Change mouse pointer: `CALL VpWindow window, 'SETPOINTER', icon_handle`
* Copy to clipboard: `CALL VpWindow window, 'SETCLIPBOARD', 'TEXT', value`
* Paste from clipboard: `value = VpWindow(window, 'GETCLIPBOARD', 'TEXT')`
* Start/change timer: `CALL VpWindow window, 'STARTTIMER', duration_in_ms`
* Stop timer: `CALL VpWindow window, 'STOPTIMER'`
* Enable/Disable STDIO window: `CALL VpWindow window, 'ENABLESTDIO' | 'DISABLESTDIO'`
* Call Message Box: `answer = VpMessageBox(window, 'Title', 'Text', 'YESNOCNCL ICONQUESTION')`
  * Valid calls:
    * `rc = VpMessageBox(window, 'title', 'message')`
    * `rc = VpMessageBox(window, 'title', 'message', 'OKCNCL')`
    * `rc = VpMessageBox(window, 'title', 'message', 'YESNO')`
    * `rc = VpMessageBox(window, 'title', 'message', 'YESNOCNCL')`
    * `rc = VpMessageBox(window, 'title', 'message', 'RETRY')`
    * `rc = VpMessageBox(window, 'title', 'message', 'IGNORE')`
  * Invalid call:
    * `rc = VpMessageBox(window, 'title', 'message', 'OK')`

## 8. File &amp; Picture Operations (VpLoadPicture / VpFileDialog)
* Load picture from file: `handle = VpLoadPicture('FILE', filename)`
* Load system icon: `handle = VpLoadPicture('SYSICON', icon_index_1_to_22)`
* Load resource icon: `handle = VpLoadPicture('RESICON', resource_id)`
* Remove graphic from memory: `CALL VpFreePicture handle`
* Load file content into object: `CALL VpLoadFromFile window, ITEMID, filename`
* Call file open/save dialog: `filename = VpFileDialog(window, 'SAVE' | 'OPEN', '*.*', 'Title')`

## 9. Drawing (VpDraw) - Free-form Windows
* Set start point: `rc = VpDraw(window, ITEMID, 'MOVE', x, y)`
* Draw line / box / text: `rc = VpDraw(window, ITEMID, 'LINE' | 'BOX' | 'TEXT', x, y)`
* Manage paths: `rc = VpDraw(window, ITEMID, 'BEGINPATH' | 'ENDPATH' | 'FILLPATH' | 'STROKEPATH')`

## 10. Container DETAILS View - Single vs. Multi-Column Behavior [VERIFIED]
VisPro/REXX dynamically changes how it handles Container Controls in DETAILS view based on the number of configured text columns:
1. Multi-Column (>= 2 text columns):
   - You MUST use a stem name string (with trailing dot) for insertion.
   - Example: 
     row.0 = 3  /* 1=Hidden Icon, 2=Text1, 3=Text2 */
     row.1 = ''
     row.2 = 'Data 1'
     row.3 = 'Data 2'
     CALL VpAddItem window, ITEMID, 'END', 'row.'
2. Single-Column (Exactly 1 text column):
   - VisPro internally downgrades the container to a simple list.
   - You MUST NOT pass a stem name string. Doing so will print the literal string (e.g., "row.") into the UI.
   - You MUST pass the text variable directly.
   - Example: 
     CALL VpAddItem window, ITEMID, 'END', string_variable
   - Reading Data: When querying a single-column container via `CALL VpGetItemValue window, ITEMID, 'DETAILS', 'crow.', index`, the text value is found in `crow.1` (VisPro does not count hidden columns when reading back single-column containers).
</SKILL_VISPRO_REFERENCE>
