1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039
|
V-code Technical Reference
-------------------------------------------------
== Introduction ==
V-code got its name from a void pointer. As bytecode is comprised of 'char', so
V-code is of 'void *'. Therefore, "V".
V-code engine runs mtPaint's GUI - and its commandline and scripts - but it is
less a widget toolkit per se, than a technique of abstraction. The idea was to
decouple the semantics of controls from the particulars of implementation - and
to get rid of the mind-numbing boilerplate that invariably accompanies anything
having to do with a GUI. And when I compare what it takes to create a new dialog
now with V-code, to what it had taken without - I much prefer the new way.
V-code is declarative. I describe an interface component, and leave it to the
engine to make it - or to pretend convincingly enough, so that the rest of the
program does not notice a difference. Whatever specialcasing is needed, is done
within the engine, never to poke its ugly snout outside.
While the declarative part is quite extensive, the runtime API is simplistic. It
does not need complexity; when creation is firmly separated into its own phase,
only a few tweaks remain to be done at runtime.
Another reason for the simplicity is that I made things interchangeable. There
are no special accessor functions for any type of widget, and callback signatures
never differ without a very compelling reason. This way, one event handler can
serve a number of different widgets with minimal effort.
This absence of widget functions/methods is an intentional feature and the core
difference of V-code from usual GUI toolkits. All actions are done through a
compact set of call points, and while internally tasks get routed to specific
effector modules, calling code takes no part in the decisions what to use, how,
and when. Interdependences of some operations arising in some cases are thus
easily tracked and handled, entirely within the engine. Equally easy are
conditional replacements of a component with a differently implemented, or a
simulated one.
V-code is not told what to do; it receives commands as to what it should make
happen. It is the principle of the thing.
The function handlers are minimalistic. No sanity checks; if something is done
wrong, code signals that by crashing. But no strictness either; if a function is
called on an unhandled widget type, it simply does nothing. Default values are
sometimes substituted, but only where doing it once in the handler made more
sense than repeating the same at several call sites.
Neither declarative nor runtime part is providing complete coverage; only the
widgets and operations needed by mtPaint. When for a new feature I need something
that is not yet there, I extend the V-code engine.
== Overview ==
The pegasus-eye view of how V-code is used, is this.
- You prepare a V-code description of, say, a dialog window.
- You fill a struct with initial values for it.
- You hand off both to **run_create_()**.
- It executes the description: allocates memory, copies the struct in there,
creates widgets, setups callbacks, does initializations, and shows the finished
result to the user.
- User does some things to controls in the dialog, changing some values, then
presses "OK".
- The callback sees that it was "OK", calls **run_query()** to read back values
from all the controls.
- The new values in the copy of struct and/or in global variables are used to do
something.
- The callback calls **run_destroy()** to finish off the window, and returns.
- Done.
There are ways and means to do more complicated things, but this simple scenario
is done this simply; in exactly 3 function calls.
This is how it can work.
Every V-code command describing a control - say, a spinbutton or a text entry -
has a memory reference; either to a global variable, or to a field in a struct.
When the control is created, it is initialized from that memory; when it is read,
the result goes to that memory. When **run_create()** makes that control, it
saves the address of that V-code command, and the widget it created, into its
"tape" (a flat array of "slots"). When you call **run_query()**, it iterates over
slots in the "tape" and looks at commands; it reads back values from every control
it finds, each into the place that its command refers to. When you call
**run_destroy()**, after the widgets get destroyed it again iterates over slots
and looks at commands, to do cleanup actions if needed; after that, the attached
struct and "tape" are freed.
When you need to add a callback, you put an //EVENT()// command, with a type ID
and a function reference, after a control; this command also gets a slot in the
"tape". The function gets called when the control sitting before it in the "tape"
wants to raise an event of that type. With the //TRIGGER// command, you can raise
the preceding //EVENT()// just after **run_create()** finishes creating things;
doing it to a list control's //SELECT// event is the natural way to make other
controls reflect initial selection in the list. Some commands that create
controls, have //EVENT()// built in, some even two of them; for them, function
references go as parameters to the commands. A //TRIGGER// command after a
two-event control triggers its second builtin //EVENT()//.
When you need to, say, hide a control, you pass address of its slot to
**cmd_showhide()**. To get that address, you put a //REF()// command right before
the command that creates the control. //REF()//, again, has a memory reference
(to a field of type **void**** ; for a variable, you use //REFv()//); into that
memory, **run_create()** will put the current position in the "tape".
For a container widget, you create it with a command such as //VBOX//, and
everything you create after it will go into it, till you close it with //WEND//.
One-place containers, such as //FRAME()//, close by themselves after accepting a
widget (or you can close them by //WEND// leaving them empty). One command can
also create more than one container; //DOCK()// has two panes, each filled
separately and finished by its own //WEND//.
Most container widgets do not get slots of their own, but are fill-and-forget. If
you need to interact with them, say hide a box with all its contents, you need a
version with a slot; those are tagged with "r", like //VBOXr// vs //VBOX//.
When you want to include some commands only if a condition holds (say, with an
RGB image you offer to select red, green, or blue channel, and with indexed ones
you don't), you can use simple conditionals //IF()// and //UNLESS()//, affecting
one command, or extended ones //IFx()// and //UNLESSx()// that affect everything
till the matching //ENDIF()//. They get a memory reference (to an **int**), and
check it for being zero or nonzero.
When you need to allocate some memory (say, a buffer for a histogram image to be
displayed), you use the //TALLOC()// command; it takes two fields - from one it
takes the size, into the other it puts the resulting pointer. Such memory is
allocated as part of the same memory block that holds the struct and the "tape",
so if there isn't enough memory for it, then nothing at all gets allocated and
**run_create()** itself fails (returning NULL).
If you put a pointer to some separately allocated object into the struct, you can
even tell **run_destroy()** to free it along with everything else; for that, you
use the //CLEANUP()// command.
A complete command sequence is one that creates a self-contained interface
element (usually a toplevel window, but not necessarily). There are two things
required from it. It has to define something in where all the controls will go;
a //WINDOW()//, //TOPVBOX// etc. And it has to terminate the sequence, telling
**run_create()** what to do with it; use //WDIALOG()// command to run it as a
dialog, //WSHOW// to show it to the user, or //WEND// to hand it off to the
program to be shown later.
== V-code as written ==
Declarative V-code is an array of pointers to void:
**void *whatever_code[] = { ... };**
The array is initialized by a sequence of command macros, with their parameters.
In case the commands are referring to **ddata** fields, the struct type to use
must be set before that, as the //WBbase// macro:
**#define WBbase whatever_dd**
So nearly all V-code chunks in mtPaint look like this:
```
#define WBbase whatever_dd
void *whatever_code[] = { ... };
#undef WBbase
```
In a simplest case, one V-code chunk creates something from beginning to end, but
there are also other possibilities.
The most complex part of mtPaint, the main window, is defined part by part in
several chunks in several source files, which the main chunk, fittingly named
**main_code[]**, calls one by one using the //CALL()// command; some then call
yet other chunks in their turn.
The least complex parts, the lowly filter windows, use a reverse approach; a
small filter-specific code chunk which is a V-code subroutine, indirectly called
(with //CALLp()// command) by **filterwindow_code[]** chunk between it building
the generic top part and the equally generic bottom part of the window.
== In-memory structure ==
The memory block allocated by **run_create_()** is structured this way:
| ddata | User-provided backing struct |
| vdata | Internal window-info struct |
| wdata | "Tape" of "slots" referring to widgets, events, etc. |
| dtail | Extra memory allocated to widgets and to/by other V-code commands |
All this is allocated as one single block, which not only greatly simplifies
memory management, but also, with all references to anything of note sitting in a
flat array of "slots", makes all kinds of reflection trivial. This is by virtue
of V-code being processed in two passes; first, **predict_size()** counts how
many "slots" and how much extra memory the V-code sequence wants, then memory for
all that is allocated and partitioned up, the user-provided struct copied in, and
only then the commands actually get executed one after another; creating and
setting up widgets, taking up chunks of dtail area, and filling "slots".
A canonical reference to a block is the address of its **wdata**; i.e. it points
into middle of the block. The reason for that is purely historical; nothing
prevents setting up **ddata** and **vdata** after **dtail** area instead, but
given that a block should never be deallocated in any other way than by calling
**run_destroy()**, the arrangement is not relevant to anything.
A "slot" in **wdata** consists of 3 pointers:
| 0 | Widget/something else |
| 1 | V-code command that set up the slot |
| 2 | Dtail chunk |
Slot where "something else" is its **dtail** chunk (i.e. 0th cell equals 2nd) is
considered "unreal"; such slots are set up by pseudo-widgets used to simulate
real ones when running scripts, thus the name. With the V-code command obviously
being the same in either case (real and simulated), this is the only way to tell
the two apart.
The first slot of **wdata** is a fake one, used for linking all this together:
| 0 | **ddata** |
| 1 | **vdata** |
| 2 | Dtail chunk |
The **vdata** structure masquerades as a V-code command //WDONE//; as no regular
//WDONE// gets a slot of its own, the slot is thus unique. The **wdata_slot()**
function just steps through the "tape" before a slot till it finds this marker,
and returns its address.
The second slot must belong to a toplevel widget. No other thing should be put
into the "tape" before it.
And the last slot in **wdata**, marking its end, is all NULLs; all functions
iterating over slots, depend on it being there. //WEND//, //WSHOW// and
//WDIALOG()// put it into the "tape", before doing all other finishing touches.
Macro **GET_DDATA()** gets from **wdata** to **ddata** (just reads the 0th cell
of the first slot). The macro GET_VDATA(), internal to vcode.c, gets from
**wdata** to **vdata** (by reading the 1st cell).
== Structure of V-codes ==
As said above, V-code is an array of pointers to void. The array contains a
sequence of commands - each consisting of one or more pointer values: an
instruction header, and a specified number of parameters.
The parameters can be either constants, or memory references. The latter may be
either addresses of globals (string constants, variables, cells in global arrays,
functions), or offsets in **ddata** struct, of the type designated by the
//WBbase// macro. Encoding the field references is done with the //WBfield()//
macro:
**..., WBfield(field), ...**
Other things are encoded as per usual; variables with the "&" operator, strings,
arrays and functions as is, constants with a "(void *)" cast:
**&(variable), "string", array, array + 2, function, (void *)1**
The responsibility of a high-level command macro is to pack its parameters in the
way and order that the underlying instruction code expects them. Like this:
**OKBTN(_("OK"), conf_done)**
translates to this:
**WBr2h_x(OKBTN, 1 + 2), (_("OK")), EVENT(OK, conf_done)**
Here you see a command macro expanding into an instruction header macro, a string
constant (wrapped in a no-op translation marker), and a nested command macro
which in its turn expands to another header and a function constant:
**WBrh(EVT_OK, 1), (conf_done)**
Instruction headers are in fact 31-bit integer values, to be able to comfortably
reside in a pointer on a 32-bit system, and to avoid the signed/unsigned hassle.
As of mtPaint 3.50, the bits are allocated this way:
|| Bits | Function | Values |
| 0-11 | Instruction code | op_* |
| 12-15 | Packing mode | pk_* |
| 16-17 | Number of slots | WB_REF* |
| 18 | Indirection flag | WB_NFLAG |
| 19 | Field flag | WB_FFLAG |
| 20 | Script flag | WB_SFLAG |
| 21-23 | Reserved | 0 |
| 24-30 | Number of parameters | 0-127 |
The number of parameters is how many pointers after the header are part of the
command in question; i.e. how many to skip to get to the next header. In the
example above it is 1 + 2: 1 for the string, and 2 more for the nested
//EVENT()// command (which in its own header has 1, for the function).
The first pointer after the header (given the number of parameters is nonzero) is
intended to be interpreted as pointer to data; this is in no way a hard
requirement, and many instructions use it for constants instead, but the prologue
code in V-code functions pre-interprets this field in accordance to field flag and
indirection flag, so if an instruction refers to in-memory data, it is easiest to
place the most used (or only) location in there.
- Initially, the first pointer is read as, well, a pointer (and by default, left
unmodified);
- With the field flag on, it is reinterpreted as an integer offset from the start
of **ddata**, and the current **ddata** base address gets added to it;
- With the indirection flag on, it is interpreted as a pointer to a pointer, and
that pointer is read in.
The script flag, if set, selects nondefault scripting-related behaviour for some
few commands (specifics depend on command). Not even looked at for all the rest
of them.
The number of slots (from 0 to 3) tells //run_create_()// how many slots in the
"tape" this command needs. The first slot refers to the command itself, the
second and third are set up for its builtin //EVENT()// subcommands (each one a
pair of pointers at end of the parameters area). A command not taking any slots,
is executed and forgotten; this fits the widgets that do not need any interaction
(such as most containers) and modifier commands. The example command above takes
two slots; it is what the "r2" in its header macro means. The //EVENT// command
on its own takes one, thus the plain "r" in its.
The packing mode denotes how the widget the command creates should be packed into
its container, in case the container supports this mode of packing. The small
assortment of choices is this:
| pk_NONE | not intended to be packed (modifier, event, etc.) |
| pk_PACK | from beginning of container, not expanding |
| pk_XPACK | from beginning, expanding |
| pk_EPACK | from end, expanding |
| pk_PACKEND | from end, not expanding |
| pk_TABLE1x | into first free cell in first/second column of a table, not expanding |
| pk_TABLE | into specified row and column(s) of a table, not expanding |
| pk_TABLEx | into specified row and column(s) of a table, expanding |
- //pk_PACK// and //pk_XPACK// are allowed for any kind of container, other modes
require a matching container type.
- //pk_TABLE// and //pk_TABLEx// take an extra pointer value, from the very end
of command's parameters, and interpret it as 3 byte values denoting X, Y, and
length, as packed by the //WBxyl()// macro. Like in this command macro for a
horizontal box that can take several table columns:
**#define TLHBOXl(X,Y,L) WBh_t(HBOX, 1), WBxyl(X, Y, L)**
Returning to the example, //OKBTN()// command does //pk_XPACK//, as denoted by
the "_x" in its header macro; the //EVENT()// command does //pk_NONE//, as
denoted by the lack of "_" in its.
And finally, the instruction code tells //run_create_()// what to do with it all
(and later, is used to recognize the objects to which the slots in the "tape"
refer). In the example, it is //op_OKBTN// for the command itself, and
//op_EVT_OK// for its builtin //EVENT()// command.
On the preprocessor side of things, instruction codes are assembled in stages.
First, a set of macros is used as shorthand representations for every combination
of flags and slots that ever arises in some command. For example, //WB_R2F//
denotes two slots asked for, and the field flag set:
**#define WB_R2F (WB_REF2 + WB_FFLAG)**
Then goes the basic macro encoding the instruction header: //WBp_()//, taking
instruction code, length, packing mode, and a flags and slots combination.
However, for brevity, actual command macros use one of the many pre-encoded
//WB*h*()// macros that need take only instruction name and length. Like the one
from the example:
**#define WBr2h_x(NM,L) WBp_(op_##NM, L, XPACK, R2)**
In these macros, the part between "WB" and "h" denotes the slots and flags, and
the part after "h", packing mode.
Additionally, there is a set of helper macros that some commands use to pack two
byte values into a single pointer: //WBwh()//, //WBnh()//, and //WBbs()//; the
difference is purely semantic, they do the same thing. And another set for three
values: //WBpbs()// and //WBppa()//, again doing the same.
== V-code commands ==
All high-level command macros are listed below. The list is not final, for new
commands can be derived from opcodes and lower-level macros any time when existing
combinations of parameters do not cover a new use case.
In the descriptions below, unless noted differently, the string parameters are
constants or global **char** arrays, the numeric parameters are constants.
Packing mode for the commands producing widgets (controls and containers),
unless noted differently, is the default one, //pk_PACK//.
Naming guidelines (not 100% followed) for the macros:
- "**u**" prefixed to "unreal" (script-only, simulated) version of a widget:
//uSPIN()//
- "**a**" affixed to a version that takes an array instead of several constant
parameters: //TSPINa()//
- "**c**" affixed to a version that is centered: //MLABELc()//
- "**e**" affixed to a version that has a builtin event: //RPACKe()//
- "**p**" affixed to a version that takes string parameter by reference instead
of a string constant: //WINDOWp()//
- "**r**" affixed to a version that has a slot in the "tape": //VBOXr//
- "**s**" affixed to a version that is scriptable: //MENUITEMs()//
- "**v**" affixed to a version that refers to a variable/array instead of a field:
//CSCROLLv()//
- "**x**" affixed to a version that has extra parameters: //TLABELx()//
- "**T**" or "**T1**" prefixed to a version packed in first/second table column:
//TLABEL()//, //TSPIN()//, //T1SPIN()//
- "**TL**" prefixed to a version packed into table at (//x, y//): //TLLABEL()//
- "**TL...l**" circumfixed to a version packed into table at (//x, y//)
taking //length// cells: //TLLABELl()//
- "**X**" prefixed to a version that is packed expanding: //XTABLE()//
- "**F**" prefixed to a version that has a frame around: //FTABLE()//
=== Finalizers ===
All these commands finish element creation and cause **run_create()** to return.
They differ in what else they do between those two points.
: //WDONE// :
do nothing extra
: //WSHOW// :
show the toplevel
: //WDIALOG(field)// :
show, then process input till the **void**** field changes to non-NULL
=== Toplevels ===
All toplevels are one-place containers, unless noted differently. All have a slot
in the "tape" (and extra slots for builtin events, if have any).
: //MAINWINDOW(title, icon, width, height)// :
program's main window, with specified title, XPM icon, default width and height
: //WINDOW(title)// :
a regular non-modal window with specified title
: //WINDOWp(field)// :
as above, with title passed in a **char*** field
: //WINDOWm(title)// :
a regular modal window with title
: //WINDOWpm(field)// :
as above, with title in a **char*** field
: //DIALOGpm(field)// :
a modal dialog, with title in a **char*** field; is a double container: the top
(the first accessible) is dialog's "content area" (a vertical box), the
bottom/second is its "action area" (the one with buttons, a horizontal box)
: //FPICKpm(title_field, mode_field, filename_field, OK_handler, CANCEL_handler)// :
a file selector window, with **char*** title, **int** mode, and **char[]**
filename (in system encoding) in fields, and handlers for //OK// and //CANCEL//
events; is a box container (horizontal) for extra controls
: //POPUP(title)// :
a popup window with specified title (which, while not shown, is still useful for
identifying the window in window manager's lists)
: //TOPVBOX// :
a pseudo-toplevel, for elements created to sit in //MOUNT// containers or purely
for scripting; is a box container (vertical)
: //TOPVBOXV// :
same thing but with different sizing: fills space vertically, but horizontally
is only centered, not stretched
: //IDENT(id)// :
set a non-default identifying string for the toplevel; is needed when one
toplevel has separate V-code descriptions for different parts: the case in point
is file selector, where the //FPICKpm()// command builds its fixed part through
a nested **run_create()** using an //IDENT()//
=== Containers ===
Table containers have a border area by default; its size can be set by
//BORDER(TABLE)// (see "Sizing and placement" category below). Other containers
do not, unless specifically noted.
: //WEND// :
close the current container
: //TABLE(columns, rows)// :
a table of given dimensions (those currently are initial, not a hard limit)
: //TABLE2(rows)// :
a two-column table
: //TABLEs(columns, rows, spacing)// :
a table with specified spacing (same for rows and columns)
: //TABLEr(columns, rows)// :
a table that gets a slot in the "tape"
: //XTABLE(columns, rows)// :
a table that is packed expanding (//pk_XPACK//)
: //ETABLE(columns, rows)// :
a table that is packed from the end (//pk_PACKEND//)
: //VBOX// :
a vertical box
: //VBOXr// :
a vertical box with slot in the "tape"
: //VBOXbp(spacing, border, padding)// :
a vertical box with specified spacing, border, and padding
: //VBOXP// :
a vertical box with padding of default size (5 pixels)
: //VBOXB// :
a vertical box with border of default size (5 pixels)
: //VBOXS// :
a vertical box with spacing of default size (5 pixels)
: //VBOXPS// :
a vertical box with padding and spacing of default size (5 pixels each)
: //VBOXBS// :
a vertical box with border and spacing of default size (5 pixels each)
: //VBOXPBS// :
a vertical box with padding, border and spacing of default size (5 pixels each)
: //XVBOX// :
a vertical box that is packed expanding
: //XVBOXbp(spacing, border, padding)// :
same, with specified spacing, border, and padding
: //XVBOXP// :
a vertical box, packed expanding, with default padding
: //XVBOXB// :
a vertical box, packed expanding, with default border
: //XVBOXBS// :
a vertical box, packed expanding, with default border and spacing
: //EVBOX// :
a vertical box, packed from the end
: //HBOX// :
a horizontal box
: //HBOXbp(spacing, border, padding)// :
a horizontal box with specified spacing, border, and padding
: //HBOXP// :
a horizontal box, with default padding
: //HBOXPr// :
same, with slot in the "tape"
: //HBOXB// :
a horizontal box, with default border
: //XHBOX// :
a horizontal box, packed expanding
: //XHBOXbp(spacing, border, padding)// :
same, with specified spacing, border, and padding
: //XHBOXP// :
a horizontal box, packed expanding, with default padding
: //XHBOXS// :
a horizontal box, packed expanding, with default spacing
: //XHBOXBS// :
a horizontal box, packed expanding, with default border and spacing
: //TLHBOXl(x, y, length)// :
a horizontal box, packed into //length// columns in a table, starting at
column //x//, row //y//
: //TLHBOXpl(x, y, length)// :
same, with default padding
: //EQBOX// :
a horizontal box with equal space allocated to contents
: //EQBOXbp(spacing, border, padding)// :
same, with specified spacing, border, and padding
: //EQBOXP// :
a horizontal box with equal space, with default padding
: //EQBOXB// :
a horizontal box with equal space, with default border
: //EQBOXS// :
a horizontal box with equal space, with default spacing
: //EEQBOX// :
a horizontal box with equal space, packed from the end
==== Frames ====
Frames have an empty border area around them by default, its size can be set by
//BORDER(FRAME)//. A container sitting inside a frame can also have a border of
its own, even if both are created by one command.
: //FRAME(name)// :
a frame with specified name
: //XFRAME(name)// :
same, packed expanding
: //XFRAMEp(field)// :
a frame, packed expanding, with name passed in a **char*** field
: //EFRAME// :
a frame without name, with an "etched out" look
: //FTABLE(name, columns, rows)// :
a table of given dimensions sitting in a named frame
: //FVBOX(name)// :
a vertical box sitting in a named frame
: //FVBOXB(name)// :
a vertical box in a named frame, with default border
: //FVBOXBS(name)// :
a vertical box in a named frame, with default border and spacing
: //FXVBOX(name)// :
a vertical box in a named frame, packed expanding
: //FXVBOXB(name)// :
same, with default border
: //EFVBOX// :
a vertical box with twice the default border (10 pixels), sitting in an
"etched out" nameless frame
: //FHBOXB(name)// :
a horizontal box in a named frame, with default border
==== Scrolling ====
Scrolling containers have a default border area: //BORDER(SCROLL)//.
: //SCROLL(horiz_mode, vert_mode)// :
a scrolling container, with display modes for its horizontal and vertical
scrollbar: 0 - do not show, 1 - show when needed, 2 - show always
: //XSCROLL(horiz_mode, vert_mode)// :
same, packed expanding
: //FSCROLL(horiz_mode, vert_mode)// :
a scrolling container in an unnamed frame, packed expanding
: //CSCROLLv(array)// :
a scrolling container acting as a control: with its own slot, and with
horizontal and vertical positions read into/reset from **int[2]** array
(zeroed out by **run_create()**); horizontal and vertical scrollbars are shown
when needed
==== Notebook ====
Regular notebook widgets have a default border area: //BORDER(NBOOK)//.
: //NBOOK// :
a notebook with tabs on top, packed expanding; is a container for //PAGE//'s
: //NBOOKr// :
same, with a slot in the "tape"
: //NBOOKl// :
a notebook with tabs on the left, packed expanding
: //PAGE(name)// :
a page for a notebook, with a name; is a box container (vertical)
: //PAGEvp(var)// :
same, with name passed in a **char*** variable
: //PAGEi(icon, spacing)// :
a page for a notebook, with an icon instead of name, and specified spacing
: //PAGEir(icon, spacing)// :
same, with a slot in the "tape"
: //PLAINBOOK// :
a "notebook" without any visible trappings, just to show either of two boxes in
same place; has a slot in the "tape", through which to command it to switch
pages; is a double container - page 0 is the top, all pages vertical boxes
: //PLAINBOOKn(num_pages)// :
same but with specified number of pages (3 or more); a multiple container, page
0 is the top
: //BOOKBTN(name, field)// :
a toggle button with a name, for switching pages of a //PLAINBOOK// whose slot
is in the **void**** field: show page 1 if toggled, page 0 if not
==== Special ====
: //DOCK(ini_var_name)// :
a splitter adding a dock pane on the right, that can be hidden/shown; the
specified inifile variable (integer) holds the pane's width when it is shown;
has a slot in the "tape"; is a double container, with left (main) pane the top,
and dock pane the bottom, both panes are vertical boxes
: //HVSPLIT// :
a splitter that can be switched between single-pane, horizontal, and vertical;
packed expanding, has a slot; is a container for two widgets, the first for
single/left/top pane, the second for hidden/right/bottom one; still needs
//WEND// after them
: //VSPLIT// :
a regular vertical splitter, packed expanding, has a slot; a container for two
widgets, first is for the top pane, the second for the bottom, needs //WEND//
after them
: //TWOBOX// :
a container for two widgets, holding them in one row if enough horizontal space,
or in two rows if not
: //MOUNT(field, create_func, CHANGE_handler)// :
a container holding and "leasing" an element created by a specified **mnt_fn**
function (returns **wdata**), with **int** field set to TRUE while holding it,
and a handler for //CHANGE// event, triggered when leasing/unleasing; has a
slot for itself and another for the event
: //PMOUNT(field, create_func, CHANGE_handler, ini_var_name, def_height)// :
same, but with the element held in a resizable vertical pane with specified
default height, with specified inifile variable (integer) storing modified
height
: //REMOUNTv(var)// :
a container that, when created, "leases" the element from the //MOUNT// whose
slot is in the **void**** variable, and when destroyed, returns it back; is
packed expanding, has a slot
: //HEIGHTBAR// :
a modifier that requests the max height of all the widgets following it in the
same container, whether visible or not; used to prevent jitter when those
widgets get shown/hidden later
==== Statusbar ====
: //STATUSBAR// :
a statusbar, packed from the end, has a slot, is a box container (horizontal)
: //STLABEL(width, align)// :
a statusbar label, with specified width and alignment: 0 - left, 1 - center,
2 - right; has a slot
: //STLABELe(width, align)// :
same, packed from the end
=== Separators ===
: //HSEP// :
a horizontal separator
: //HSEPl(width)// :
same, with specified minimum width
: //HSEPt// :
a thin (minimum height) horizontal separator
=== Labels ===
All labels, when packed into boxes and tables, have padding by default; its size
can be set by //BORDER(LABEL)//. Labels are left aligned by default.
Labels identify adjacent controls to scripts, unless prevented from it or have no
control to attach to.
: //MLABEL(text)// :
a regular label with text
: //MLABELr(text)// :
same, with a slot of its own
: //MLABELc(text)// :
a centered label with text
: //MLABELcp(field)// :
same, with text passed in a **char*** field
: //MLABELxr(text, x_padding, y_padding, x_align)// :
a label with text, with specified extra horizontal and vertical padding, and
horizontal alignment at //x_align/10//: 0 - left, 5 - center, 10 - right; has
a slot
: //MLABELpx(field, x_padding, y_padding, x_align)// :
same, but with text passed in a **char*** field, and without a slot of its own
: //WLABELp(field)// :
a centered label with text passed in a **char*** field, packed expanding,
ignored by scripts
: //XLABELcr(text)// :
a centered label with text, packed expanding, has a slot
: //TLABEL(text)// :
a label with text, packed into the first free cell of the first column of a
table
: //TLABELr(text)// :
same, with a slot of its own
: //TLABELx(text, x_padding, y_padding, x_align)// :
same, with specified padding and alignment, and without a slot
: //TLLABELl(text, x, y, length)// :
a label with text, packed into //length// columns in a table, starting at
column //x//, row //y//
: //TLLABEL(text, x, y)// :
same, packed into one column
: //TLLABELx(text, x, y, x_padding, y_padding, x_align)// :
same, with specified padding and alignment
: //TLLABELxr(text, x, y, x_padding, y_padding, x_align)// :
same, with a slot of its own
: //TLLABELp(field, x, y)// :
a label with text passed in a **char*** field, packed into a table at
column //x//, row //y//
: //TLLABELpx(field, x, y, x_padding, y_padding, x_align)// :
same, with specified padding and alignment
: //TXLABEL(text, x, y)// :
a label with text, packed expanding into a table at column //x//, row //y//,
aligned at 0.3 of its width
: //HLABELp(field)// :
a helptext label with text passed in a **char*** field
: //HLABELmp(field)// :
same with monospace font
: //TLTEXT(text, x, y)// :
text, separated into columns by tabs and into rows by newlines, is placed into
a table starting at column //x//, row //y//
: //TLTEXTf(field, x, y)// :
same, with text stored in a **char** array field
: //TLTEXTp(field, x, y)// :
same, but with text passed in a **char*** field
=== Image display ===
All widgets in this group have slots in the "tape".
: //COLORPATCHv(rgb_array, width, height)// :
areа of specified size, filled with RGB color passed in an **unsigned char[3]**
array (or a string constant), packed expanding
: //RGBIMAGE(image_field, w_h_array_field)// :
area of size passed in **int[2]** array field, displaying an image pointed to
by **unsigned char*** field
: //TLRGBIMAGE(image_field, w_h_array_field, x, y)// :
same, packed into a table at column //x//, row //y//
: //RGBIMAGEP(array_field, width, height)// :
area of specified size, displaying an image stored in **unsigned char[]**
array field
: //CANVASIMGv(array, width, height)// :
framed canvas widget (one able to properly receive clicks, releases, and
movement, and efficiently handle scrolling) of specified size, displaying an
image stored in **unsigned char[]** array
: //CCANVASIMGv(array, width, height)// :
same, packed from the end, expanding (//pk_EPACK//)
: //CANVASIMGB(image_field, w_h_bkg_array_field)// :
framed canvas widget of minimum size passed in first 2 cells of **int[3]** array
field, displaying an image pointed to by **unsigned char*** field, and filling
the extra space, if any, with RGB color packed in an **int** in the cell 2 of
the **int[3]** array
: //FCIMAGEP(image_field, x_y_array_field, w_h_array_field)// :
focusable widget displaying an image pointed to by **unsigned char*** field,
of size passed in **int[2]** array field, contoured by a frame, with a
ring-shaped location marker on it at coordinates given in another **int[2]**
array field
: //TLFCIMAGEP(image_field, x_y_array_field, w_h_array_field, x, y)// :
same, packed into a table at column //x//, row //y//
: //TLFCIMAGEPn(image_field, w_h_array_field, x, y)// :
same, but without a location marker
: //CANVAS(init_width, init_height, cost, EXT_handler)// :
framed canvas widget of specified initial size, that triggers //EXT// event to
redraw an area (handled by an **evtxr_fn**); the //cost// value is defined as
how many pixels likely could be redrawn in the time needed to call the event
once more (i.e. its init+teardown cost), and is used for deciding whether to
redraw multiple subregions one by one, or their encompassing region once; has
a slot for itself and an extra one for the event
The //EXT// handler of //CANVAS()// receives in its //xdata// parameter a pointer
to **rgbcontext** (see mygtk.h). Its task is to render into the buffer there an
RGB image of the denoted area of canvas, and return TRUE.
=== Spinbuttons and spinsliders ===
All spinbuttons and spinsliders, when packed into boxes and tables, have padding
by default; its size can be set by //BORDER(SPIN)// and //BORDER(SPINSLIDE)//.
They all have slots in the "tape", unless specifically noted.
: //NOSPINv(var)// :
displays an **int** variable in a non-modifiable spinbutton, has no slot
: //TLNOSPIN(field, x, y)// :
displays an **int** field value in a non-modifiable spinbutton, packed into a
table at column //x//, row //y//, has no slot
: //TLNOSPINr(field, x, y)// :
same but has a slot
: //TLSPIN(field, min, max, x, y)// :
a spinbutton with **int** field, range //min// to //max//, packed into a table
at column //x//, row //y//
: //TLXSPIN(field, min, max, x, y)// :
same, packed there expanding
: //TLXSPINv(var, min, max, x, y)// :
same, with **int** variable
: //T1SPIN(field, min, max)// :
a spinbutton with **int** field, packed into the first free cell of the second
column of a table
: //TSPIN(text, field, min, max)// :
same combined with label that goes into the first column
: //TSPINv(text, var, min, max)// :
same, with **int** variable
: //TSPINa(text, array_field)// :
same, but with value, min and max in **int[3]** array field
: //SPIN(field, min, max)// :
a spinbutton with **int** field, range //min// to //max//
: //SPINv(var, min, max)// :
same, with **int** variable
: //SPINc(field, min, max)// :
a spinbutton with **int** field, centering the value
: //XSPIN(field, min, max)// :
a spinbutton with **int** field, packed expanding
: //FSPIN(field, min, max)// :
a fixedpoint spinbutton with **int** field holding value * 100, range //min//
to //max// (both * 100)
: //FSPINv(field, min, max)// :
same, with **int** variable
: //TFSPIN(text, field, min, max)// :
a fixedpoint spinbutton with **int** field, packed into the second column of
a table, combined with label that goes into the first column
: //FSPIN(field, min, max, x, y)// :
a fixedpoint spinbutton with **int** field, packed into a table at column //x//,
row //y//
: //SPINa(array_field)// :
a spinbutton with value, min and max in **int[3]** array field
: //XSPINa(array_field)// :
same, packed expanding
: //uSPIN(field, min, max)// :
a script-only simulated spinbutton with **int** field, range //min// to //max//
: //uSPINv(var, min, max)// :
same, with **int** variable
: //uFSPINv(var, min, max)// :
same, but fixedpoint
: //uSPINa(array_field)// :
a script-only simulated spinbutton with value, min and max in **int[3]** array
field
: //uSCALE(field, min, max)// :
a script-only simulated spinbutton with **int** field, with extended syntax:
interprets values like "x1.5" as "original value multiplied by 1.5"
: //TLSPINPACKv(array, count, CHANGE_handler, width, x, y)// :
a grid of //count// spinbuttons arranged in //width// columns, packed into a
table starting at column //x//, row //y//, with values, min and max for each in
**int[][3]** array which is automatically updated when any of values changes,
with a handler for //CHANGE// event that is triggered after (handled by an
**evtx_fn**); has a slot for itself and another for the event
: //T1SPINSLIDE(field, min, max)// :
a spinslider with **int** field, range //min// to //max//, packed into the first
free cell of the second column of a table, with preset size (255 x 20 pixels)
: //TSPINSLIDE(text, field, min, max)// :
same combined with label that goes into the first column
: //TSPINSLIDEa(text, array_field)// :
same, but with value, min and max in **int[3]** array field
: //TLSPINSLIDE(field, min, max, x, y)// :
a spinslider with **int** field, packed into a table at column //x//, row //y//
: //TLSPINSLIDEvs(var, min, max, x, y)// :
same, with **int** variable and preset width (150 pixels)
: //TLSPINSLIDExl(field, min, max, x, y, length)// :
a spinslider with **int** field, packed expanding into //length// columns in a
table, starting at column //x//, row //y//
: //TLSPINSLIDEx(field, min, max, x, y)// :
same, packed into one column
: //SPINSLIDEa(array_field)// :
a spinslider with value, min and max in **int[3]** array field
: //XSPINSLIDEa(array_field)// :
same, packed expanding
The //CHANGE// handler of //TLSPINPACKv()// receives in its //xdata// parameter
an **int** index of the spinbutton that changed, cast into **void***.
=== Checkbuttons ===
All checkbuttons have a default border area: //BORDER(CHECK)//. They all have
slots in the "tape".
: //CHECK(name, field)// :
a named checkbutton with **int** field
: //CHECKv(name, var)// :
same, with **int** variable
: //CHECKb(name, field, ini_var_name)// :
a named checkbutton with **int** field, with specified inifile variable
(integer) storing its value
: //XCHECK(name, field)// :
a named checkbutton with **int** field, packed expanding
: //TLCHECKl(name, field, x, y, length)// :
same, packed into //length// columns in a table, starting at column //x//,
row //y//
: //TLCHECK(name, field, x, y)// :
same, packed into one column
: //TLCHECKvl(name, var, x, y, length)// :
a named checkbutton with **int** variable, packed into //length// columns in a
table, starting at column //x//, row //y//
: //TLCHECKv(name, var, x, y)// :
same, packed into one column
: //uCHECK(name, field)// :
a script-only simulated named checkbutton with **int** field
: //uCHECKv(name, var)// :
same, with **int** variable
=== Radiobutton packs ===
All radiobutton packs have a default border area: //BORDER(RPACK)//. They all have
slots in the "tape", those with a builtin event have an extra slot for the event.
: //RPACK(names_array, count, height, field)// :
a box of radiobuttons with **int** field that stores the index of the active
one, with names in **char*[]** array, //count// of them in total (0 if array of
names is NULL-terminated), arranged in columns of no more than //height// (0 if
all in a single column), packed expanding
: //RPACKv(names_array, count, height, var)// :
same, with **int** variable
: //FRPACK(frame_name, names_array, count, height, field)// :
a box of radiobuttons with **int** field, sitting in a named frame
: //FRPACKv(frame_name, names_array, count, height, var)// :
same, with **int** variable
: //RPACKe(names_array, count, height, field, SELECT_handler)// :
a box of radiobuttons with **int** field and a handler for //SELECT// event,
packed expanding
: //FRPACKe(frame_name, names_array, count, height, field, SELECT_handler)// :
same, sitting in a named frame
: //RPACKD(names_field, height, field)// :
a box of radiobuttons with **int** field, with **char**** field pointing to a
NULL-terminated **char*[]** array of names, packed expanding
: //RPACKDv(names_field, height, var)// :
same, with **int** variable
: //RPACKDve(names_field, height, var, SELECT_handler)// :
same, with a handler for //SELECT// event
In case a radiobutton's name is an empty string, the corresponding index is just
skipped.
=== Option menus and comboboxes ===
All option menus and comboboxes have a default border area: //BORDER(OPT)//. They
all have slots in the "tape", those with a builtin event have an extra slot for
the event.
: //OPT(names_array, count, field)// :
an option menu with **int** field, with list of choices' names in **char*[]**
array, //count// of them in total (0 if array of choices is NULL-terminated)
: //OPTv(names_array, count, var)// :
same, with **int** variable
: //TOPTv(text, names_array, count, var)// :
same, packed into the second column of a table, combined with label that goes
into the first column
: //TLOPT(names_array, count, field, x, y)// :
an option menu with **int** field, packed into a table at column //x//,
row //y//
: //TLOPTv(names_array, count, var, x, y)// :
same, with **int** variable
: //OPTe(names_array, count, field, SELECT_handler)// :
an option menu with **int** field and a handler for //SELECT// event
: //OPTve(names_array, count, var, SELECT_handler)// :
same, with **int** variable
: //XOPTe(names_array, count, field, SELECT_handler)// :
an option menu with **int** field and a handler for //SELECT// event, packed
expanding
: //TLOPTle(names_array, count, field, SELECT_handler, x, y, length)// :
same, packed into //length// columns in a table, starting at column //x//,
row //y//
: //TLOPTvle(names_array, count, var, SELECT_handler, x, y, length)// :
same, with **int** variable
: //TLOPTve(names_array, count, var, SELECT_handler, x, y)// :
same, packed into one column
: //OPTD(names_field, field)// :
an option menu with **int** field, with **char**** field pointing to a
NULL-terminated **char*[]** array of choices' names
: //XOPTD(names_field, field)// :
same, packed expanding
: //TOPTDv(text, names_field, field)// :
same with **int** variable, packed into the second column of a table, combined
with label that goes into the first column
: //OPTDe(names_field, field, SELECT_handler)// :
an option menu with **int** field, **char**** field pointing to choices' names,
and a handler for //SELECT// event
: //XOPTDe(names_field, field, SELECT_handler)// :
same, packed expanding
: //TOPTDe(text, names_field, field, SELECT_handler)// :
same, packed into the second column of a table, combined with label that goes
into the first column
: //COMBO(names_array, count, field)// :
a combobox with **int** field, with list of choices' names in **char*[]**
array, //count// of them in total (0 if array of choices is NULL-terminated)
: //PCTCOMBOv(var, array, CHANGE_handler)// :
a combobox displaying percent zoom values, with **int** variable, an **int[]**
array (0-terminated) of some values, and a handler for //CHANGE// event; has no
border
In case a choice's name is an empty string, the corresponding index is just
skipped.
=== Specialized controls ===
All widgets in this group have slots in the "tape", those with a builtin event
have an extra slot for the event.
: //PROGRESSp(text_field)// :
a progressbar, with text for it passed in a **char*** field
: //GRADBAR(chan_field, idx_field, len_field, max, map_array_field, cmap_array_field, SELECT_handler)// :
a "gradient bar" for displaying and selecting points in a gradient, with
currently selected index in **int** //idx_field//, gradient's length in
//len_field//, gradient's channel in **int** //chan_field//, colormap for
non-RGB cases in **unsigned char[768]** //cmap_array_field//, gradient's
values/colors at points in **unsigned char[]** //map_array_field//, and a
handler for //SELECT// event
: //KEYBUTTON(field)// :
a button for choosing key combos, with **char*** field where it places keyname
string
: //TABLETBTN(name)// :
a named button for calling up tablet configuration dialog
: //FONTSEL(array_field)// :
a font chooser, with font description string and the text to render in a
**char*[2]** array field, packed expanding
: //EYEDROPPER(field, CHANGE_handler, x, y)// :
a button calling up eyedropper, with color it picked up in an **int** field, and
a handler for //CHANGE// event, packed into a table at column //x//, row //y//
: //COLOR(array_field)// :
a color chooser for opaque colors, with RGBA in **unsigned char[4]** array field
: //TCOLOR(array_field)// :
same for colors with alpha
=== Lists ===
The //LISTCC*// widgets have a default border area: //BORDER(LISTCC)//. All list
widgets and columns have slots in the "tape", those with a builtin event have an
extra slot for the event, those with two, extra two slots.
: //COLORLIST(names_field, idx_field, rgb_array_field, SELECT_handler, EXT_handler)// :
a list of named colors, with currently selected index in **int** field, a
**char**** field pointing to a NULL-terminated **char*[]** array of names,
colors themselves in **unsigned char[]** array field, a handler for //SELECT//
event, and a handler for //EXT// event (an **evtx_fn**)
: //COLORLISTN(cnt_field, idx_field, rgb_array_field, SELECT_handler, EXT_handler)// :
same for numbered colors, with count of them in **int** field
: //LISTCCHr(idx_field, len_field, max, SELECT_handler)// :
a simpler (headerless, unsorted) columned list, with index and length (dynamic,
limited to //max// items) in **int** fields, and a handler for //SELECT// event
: //LISTCCHr(idx_field, len_field, SELECT_handler)// :
same with static (unchanging) length
: //LISTC(idx_field, len_field, SELECT_handler)// :
a complex (with column headers and sorting) columned list, with index and length
in **int** fields, and a handler for //SELECT// event
: //LISTCu(idx_field, len_field, SELECT_handler)// :
same, unsorted
: //LISTCd(idx_field, len_field, SELECT_handler)// :
same, with draggable rows
: //LISTCS(idx_field, len_field, sortmode_field, SELECT_handler)// :
a complex columned list, with selectable sort column and direction; the sort
mode in **int** field is column+1 if sorted ascending, and the same negated if
descending
: //LISTCX(idx_field, len_field, sortmode_field, map_field, SELECT_handler, EXT_handler)// :
same, with user-resizable columns and filtering: **int**** field points to
mapping array (row indices to display, in order; also used for sorting); and
with a handler for //EXT// event (an **evtx_fn**)
Index in //LISTC()// and its brethren refers to raw (unsorted and unfiltered)
data. The //EXT// handler of //LISTCX()// is triggered by right click on a row,
and receives in its //xdata// parameter an **int** index of that row.
The //EXT// handler of //COLORLIST()// and //COLORLISTN()// is triggered by click
on a color, and receives in its //xdata// parameter a pointer to **colorlist_ext**.
The columns of a columned list sit between //WLIST// command and the list itself,
and describe what, how, and where from goes into each. They can refer either to
an array, or to a structure and a field in it; if the latter, the //COLUMNDATA()//
command is needed, describing an array of those structures.
: //WLIST// :
start a group of columns
: //COLUMNDATA(field, step)// :
set a **void*** field as pointer to the array of data structures for columns in
this group
: //IDXCOLUMN(init, step, width, align)// :
add a column of indices, starting at //init// and changing by //step//,
//width// pixels wide, aligned per //align//: 0 - left, 1 - center, 2 - right
: //TXTCOLUMNv(array, step, width, align)// :
add a column of text strings from **char[]** array (buffers, not pointers), with
specified step, width and alignment
: //XTXTCOLUMNv(array, step, width, align)// :
same, expanding
: //NTXTCOLUMNv(name, array, step, width, align)// :
add a column of text strings from **char[]** array, with column name/title
: //NTXTCOLUMND(name, struct_type, struct_field, width, align)// :
same, from a **char[]** array field (buffer) in //COLUMNDATA//
: //PTXTCOLUMN(array_field, step, width, align)// :
add a column of text strings from **char*[]** array field (pointers)
: //PTXTCOLUMNp(field, step, width, align)// :
same, from **char*[]** array pointed to by //field//
: //RTXTCOLUMNDi(width, align)// :
add a column of text strings relative to elements of **int[]** array that is
//COLUMNDATA//: each element holds an offset **from itself** to the string
: //RTXTCOLUMND(struct_type, struct_field, width, align)// :
same, relative to **int** field in //COLUMNDATA//
: //NRTXTCOLUMND(name, struct_type, struct_field, width, align)// :
same, with column name/title
: //NRTXTCOLUMNDax(name, index, width, align, ini_var_name)// :
same, relative to //index//-th element of **int[]** sub-array in //COLUMNDATA//,
with inifile variable (integer) storing modified width
: //NRTXTCOLUMNDaxx(name, index, width, align, ini_var_name, test_string)// :
same, at least wide enough for //test_string//
: //NRFILECOLUMNDax(name, index, width, align, ini_var_name)// :
add a column of filenames relative to element of sub-array: the 0th character
denotes the type: 'F' for file, 'D' for directory, ' ' for ".."
: //CHKCOLUMNv(array, step, width, align, CHANGE_handler)// :
add a column of checkbuttons from **int[]** array, with a handler for //CHANGE//
event (an **evtxr_fn**)
The //CHANGE// handler of //CHKCOLUMNv()// receives in its //xdata// parameter
an **int** index of the row that was toggled.
=== Text entry fields ===
All entry widgets, when packed into boxes and tables, have padding by default;
its size can be set by //BORDER(ENTRY)//. All pathboxes have a default border
area: //BORDER(PATH)//. All widgets in this group have slots in the "tape", those
with a builtin event have an extra slot for the event.
: //XENTRY(field)// :
an entry with **char*** field, packed expanding
: //XLENTRY(field, max)// :
same, with max length in chars
: //TLENTRY(field, max, x, y, length)// :
same, packed into //length// columns in a table, starting at column //x//,
row //y//
: //MLENTRY(field)// :
a multiline entry (accepts **Ctrl+Enter** for a newline) with **char*** field
: //XPENTRY(field, max)// :
an entry for filenames (in system encoding) with **char*** field, with max
length in chars, packed expanding
: //TPENTRYv(text, var, max)// :
an entry for filenames with **char*** variable, with max length, packed into the
second column of a table, combined with label that goes into the first column
: //PATH(name, fsel_name, mode, field)// :
a pathbox (a named frame with entry in system encoding and button calling up
file selector) with **char*** field, with name and mode for the fileselector
: //PATHv(name, fsel_name, mode, var)// :
same, with **char*** variable
: //PATHv(name, fsel_name, mode, ini_var_name)// :
same, with inifile variable (string)
: //TPATHv(name, fsel_name, mode, var)// :
a frameless pathbox with **char*** variable, packed into the second column of a
table, combined with label that goes into the first column
: //uPATHSTR(field)// :
a script-only simulated entry for filenames with **char*** field
: //TEXT(field)// :
a text widget with **char*** field, packed expanding
: //COMBOENTRY(field, list_field, OK_handler)// :
an entry with dropdown list with **char*** field, with **char**** //list_field//
pointing to NULL-terminated **char*[]** array of choices, and a handler for
//OK// event, packed expanding
: //HEXENTRY(field, CHANGE_handler, x, y)// :
an entry for color hex code / name, with **int** field (packed RGB), and a
handler for //CHANGE// event, packed into a table at column //x//, row //y//
The //XENTRY//, //*LENTRY// and //COMBOENTRY// widgets immediately replace the
string values used for initialization with copies owned by the widgets, so that
there is no chance of incidental access if the original strings are freed after
**run_create()**. The //TEXT// widget just replaces it with NULL, for the same
reason.
=== Buttons ===
All buttons have a default border area: //BORDER(BUTTON)//. They all have two
slots in the "tape": for themselves and for their builtin event.
Buttons, except toggle buttons, are not scriptable by default, unless explicitly
tagged by the "script flag"; some of macros below are for such buttons.
: //OKBTN(name, OK_handler)// :
a named button reacting to **Enter** key, with //OK// event handler, packed
expanding
: //uOKBTN(OK_handler)// :
a script-only simulated button with //OK// event handler
: //CANCELBTN(name, CANCEL_handler)// :
a named button reacting to **Esc** key, with //CANCEL// event handler, packed
expanding
: //CANCELBTNp(field, CANCEL_handler)// :
same, with name in a **char*** field
: //UCANCELBTN(name, CANCEL_handler)// :
a cancel button packed the default way (//pk_PACK//)
: //ECANCELBTN(name, CANCEL_handler)// :
same, packed from the end
: //UDONEBTN(name, OK_handler)// :
a named button reacting to **Enter** and **Esc** keys, with //OK// event handler
: //TOGGLE(name, field, CHANGE_handler)// :
a named toggle button with an **int** field and //CHANGE// event handler, packed
expanding
: //UTOGGLEv(name, var, CHANGE_handler)// :
same with an **int** variable, packed the default way
: //BUTTON(name, CLICK_handler)// :
a named button with //CLICK// event handler, packed expanding
: //BUTTONs(name, CLICK_handler)// :
same, scriptable
: //BUTTONp(field, CLICK_handler)// :
same, with name in a **char*** field, not scriptable
: //UBUTTON(name, CLICK_handler)// :
a named button with //CLICK// event handler, packed the default way
: //EBUTTON(name, CLICK_handler)// :
same, packed from the end
: //EBUTTONs(name, CLICK_handler)// :
same, scriptable
: //TLBUTTON(name, CLICK_handler, x, y)// :
a named button with //CLICK// event handler, packed into a table at column
//x//, row //y//
: //TLBUTTONs(name, CLICK_handler, x, y)// :
same, scriptable
: //uBUTTONs(name, CLICK_handler)// :
a script-only simulated named button with //CLICK// event handler, scriptable
(obviously)
: //OKBOX(ok_name, OK_handler, cancel_name, CANCEL_handler)// :
a convenience macro creating an //EQBOX// with //CANCELBTN// and //OKBTN//; the
box is left unclosed (without //WEND//) but no practical reason to add more
things into it, and it is normally the last thing in a dialog anyway
: //OKBOXP(ok_name, OK_handler, cancel_name, CANCEL_handler)// :
same, with a box with padding (//EQBOXP//)
: //OKBOXB(ok_name, OK_handler, cancel_name, CANCEL_handler)// :
same, with a box with border (//EQBOXB//)
: //OKBOX3(ok_name, OK_handler, cancel_name, CANCEL_handler, button_name, CLICK_handler)// :
an //EQBOX// with //CANCELBTN//, regular //BUTTON//, and //OKBTN//
: //OKBOX3B(ok_name, OK_handler, cancel_name, CANCEL_handler, button_name, CLICK_handler)// :
same, with a box with border
=== Toolbar ===
Toolbars are containers for their buttons, and need //WEND// to close.
//TOOLBAR*// widgets have a default border area: //BORDER(TOOLBAR)//. All toolbars
and toolbar buttons have slots in the "tape"; the toolbars have an extra slot or
two for their builtin events. Toolbar buttons call their toolbar's event handlers
when things happen to them, so that they do not need //EVENT//s of their own.
: //TOOLBAR(CHANGE_handler)// :
a regular toolbar with //CHANGE// event handler (to which, despite the name,
regular toolbuttons getting pressed is routed too)
: //TOOLBARx(CHANGE_handler, CLICK_handler)// :
same, with also a //CLICK// event handler for right-clicking on a button
: //SMARTTBAR(CHANGE_handler)// :
a toolbar that, if buttons do not fit, adds a dropdown with the overflow ones;
with //CHANGE// event handler
: //SMARTTBARx(CHANGE_handler, CLICK_handler)// :
same, with also a //CLICK// event handler
: //SMARTTBMORE(name)// :
a button that shows said dropdown; after it go regular widgets (not toolbuttons)
that will stay displayed regardless
: //TBBUTTON(name, icon, action)// :
a toolbar button with icon, tooltip (also used for scripting), and action code
(two numbers packed into one by //ACTMOD()// macro)
: //TBBUTTON(name, icon, action, rclick_action)// :
same, with another action code for right click
: //TBTOGGLE(name, icon, action, field)// :
a toolbar toggle button with an **int** field, icon, tooltip, and action code
: //TBTOGGLEv(name, icon, action, var)// :
same, with an **int** variable
: //TBTOGGLExv(name, icon, action, rclick_action, var)// :
same, with another action code for right click
: //TBBOXTOGxv(name, icon, action, rclick_action, var)// :
a regular toggle button that actually goes into the container that the toolbar
sits in, but still acts like //TBTOGGLExv//
: //TBRBUTTONv(name, icon, action, var)// :
a toolbar radiobutton with an **int** variable, icon, tooltip, and action code
: //TBRBUTTONxv(name, icon, action, rclick_action, var)// :
same, with another action code for right click
: //TBSPACE// :
a toolbar separator
=== Menu ===
Menubars and submenus are containers for menuitems and submenus, and need //WEND//
to close. They and menuitems all have slots in the "tape"; the menubars have an
extra slot for their builtin event. Menuitems call their menubar's event handler
when they activate.
Menuitems are not scriptable by default; those that are (tagged by "script flag")
have their macros' names ending in "s".
: //MENUBAR(CHANGE_handler)// :
a regular menubar with //CHANGE// event handler, to which all its menuitems'
activations get routed
: //SMARTMENU(CHANGE_handler)// :
a menubar that, if not wide enough, moves some of regular submenus into an
overflow submenu; with //CHANGE// event handler
: //SUBMENU(name)// :
a submenu
: //ESUBMENU(name)// :
a submenu placed at the end of menubar
: //SSUBMENU(name)// :
the overflow submenu
: //MENUITEM(name, action)// :
a menuitem with action code (//ACTMOD()// made)
: //MENUITEMs(name, action)// :
same, scriptable
: //MENUITEMi(name, action, icon)// :
a menuitem with an icon, with action code
: //MENUITEMis(name, action, icon)// :
same, scriptable
: //MENUCHECKv(name, action, var)// :
a check menuitem with an **int** variable, with action code
: //MENUCHECKvs(name, action, var)// :
same, scriptable
: //MENURITEMv(name, action, var)// :
a radio menuitem with an **int** variable, with action code
: //MENURITEMvs(name, action, var)// :
same, scriptable
: //MENUTEAR// :
a tearoff menuitem
: //MENUSEP// :
a menu separator
: //MENUSEPr// :
same, with a slot of its own (to hide/show it)
: //uMENUBAR(CHANGE_handler)// :
a script-only simulated menubar with //CHANGE// event handler
: //uMENUITEM(name, action)// :
a script-only simulated menuitem; not scriptable, is used for keybindings
: //uMENUITEMs(name, action)// :
same, scriptable
=== Control flow ===
The commands in this group control what other commands get executed after.
: //GOTO(array)// :
go execute specified V-code array
: //CALL(array)// :
push current execution address onto return stack, and go execute specified
V-code array
: //CALLp(field)// :
same, with address of the array given in **void**** field
: //RET// :
pop execution address from return stack, and continue from there
: //IF(field)// :
if **int** field is 0, skip the next command (caution: do not put before those
macros that are internally multiple commands)
: //IFv(var)//
same, if **int** variable is 0
: //IFx(field, id)// :
if **int** field is 0, skip the following commands till an //ENDIF()// with the
same id number
: //IFx(var, id)// :
same, if **int** variable is 0
: //UNLESS(field)// :
if **int** field isn't 0, skip the next command
: //UNLESSv(field)// :
same, if **int** variable isn't 0
: //UNLESSx(field, id)// :
if **int** field isn't 0, skip the following commands till an //ENDIF()// with
the same id
: //UNLESSbt(ini_var_name)// :
if inifile variable (boolean) isn't FALSE, skip the next command
: //ENDIF(id)// :
mark the point for //IFx/UNLESSx// with corresponding id, otherwise do nothing
Complex conditions can be implemented in three ways; either the flag
variable/field itself can be calculated from the condition, or //IFx/UNLESSx// can
be wrapped around another (sharing the same //ENDIF//), or //IF//UNLESS// can
precede another conditional to skip it: "//IF(A), IF(B), command//" results in
"if (!a || b) command".
=== Memory allocation and referencing ===
: //REF(field)// :
put address of current slot in the "tape" into **void**** field
: //REFv(var)// :
same, into **void**** variable
: //CLEANUP(field)// :
in **run_destroy()** free the memory block pointed to by the **void*** field
: //TALLOC(field, length_field)// :
in **run_create()** allocate **int** //length_field// bytes (**void*** aligned)
below current **dtail** and put their address (new **dtail**) into **void***
field
: //TCOPY(field, length_field)// :
same but copy whatever the **void*** field was pointing to into the allocated
block before overwriting the field with its (now the copy's) address
In case //TALLOC()// is used to allocate **double** arrays on a 32-bit
architecture, the block will need be allocated with one **double** extra, and
properly aligned on **double** afterwards.
=== Keymap and action map ===
Action map serves to easily enable/disable or show/hide multiple widgets in
response to combinations of various conditions.
: //VISMASK(mask)// :
choose which bits of action map will control visibility instead of sensitivity
(default is none)
: //ACTMAP(mask)// :
the widget after whose slot in the "tape" this goes, will be sensitive/shown
when any bits in the mask are set in the action map, and insensitive/hidden
when none are
Keymap serves to dynamically map keys to widgets (represented as their slots in
the "tape"). The default mappings are set in V-code, and keymap allows to modify
them afterwards and saves/restores the modifications using the inifile.
: //KEYMAP(field, name)// :
add a named keymap, with **void**** field to put the found slot into
: //SHORTCUTs(string)// :
add a shortcut represented in GTK+ string format, like "<Alt><Shift>F1"
: //SHORTCUT(keyval, mods)// :
add a shortcut representes as keyname and modifiers, like //SHORTCUT(F2, CS)//
for Ctrl+Shift+F2
: //SHORTCUT0//
add no shortcut but tell the keymap to allow user to add one (or more)
All //SHORTCUT*// commands affect the last widget with a slot in the "tape" before
them, as does //ACTMAP// and other "postfix modifier" commands.
=== Grouping ===
Group marks serve to direct **cmd_reset()** to a range of widgets all at once,
and to differentiate groups of like-named widgets for scripting. They all have a
slot in the "tape".
: //GROUPR// :
begin a resetting-group
: //GROUPN// :
begin a scripting-group, taking its name from the preceding label
: //GROUP(name)// :
begin a scripting-group with specified name
: //GROUP0// :
end the current group
=== Sizing and placement ===
: //BORDER(category, num)// :
set border/spacing for specified category of widgets to specified number of
pixels
: //DEFBORDER(category)// :
set border/spacing for specified category of widgets to the default (5 pixels)
: The categories are:
| TABLE | tables
| NBOOK | notebooks
| SCROLL | scrolledwindows
| SPIN | spinbuttons
| SPINSLIDE | spinsliders
| LABEL | labels
| OPT | option menus
| BUTTON | buttons
| TOOLBAR | toolbars
| POPUP | popup windows
| TOPVBOX | toplevel vboxes
| CHECK | checkbuttons
| FRAME | frames
| RPACK | radiobutton packs
| ENTRY | text entries
| LISTCC | simple lists
| PATH | pathboxes
: //MKSHRINK// :
make toplevel window shrinkable (to prevent it becoming larger than screen)
: //NORESIZE// :
make toplevel window not user-resizable (to make it automatically shrink when
widgets get hidden)
: //WANTMAX// :
tell scrolledwindow that goes //after// it to request full size of its contents
: //WANTMAXW// :
same, for width only
: //WXYWH(prefix, width, height)//
make the toplevel save/restore its size and position using inifile variables
with specified prefix, and set default width and height for it
: //DEFW(width)// :
set default width for toplevel
: //DEFH(height)// :
set default height for toplevel
: //DEFSIZE(width, height)// :
set default width and height for toplevel
: //WPMOUSE// :
tell window manager to show the toplevel at the mouse cursor location
: //WPWHEREVER// :
tell window manager to show the toplevel wherever it chooses
: //WIDTH(width)// :
set width for the next widget
: HEIGHT(height)// :
set height for the next widget
: //MINWIDTH(width)// :
set minimum width for the next widget
: //KEEPWIDTH// :
make the //preceding// widget to never shrink in width
: //KEEPHEIGHT// :
same in height
: //ONTOP(field)// :
make the toplevel sit above the one whose **wdata** is passed in **void****
field (set it as its transient parent)
: //ONTOP0// :
let the toplevel sit either over or under the main window (which is default
transient parent of everything else **run_create()** makes); i.e. unset
transient parent
By default, **run_create()** tells window manager to show the toplevels in the
center of the screen.
=== Initial state ===
: //HIDDEN// :
make the preceding widget initially hidden
: //INSENS// :
make it initially insensitive
: //FOCUS// :
make it initially focused
: //RAISED// :
initially raise the toplevel above other windows
=== Cursors ===
These commands make cursors for later use. They all have slots in the "tape",
by which the cursors can later be referred to.
: //XBMCURSOR(xbm_name, x, y)// :
a 21x21 cursor from two XBM bitmaps (image and mask) specified by the variable
part of their filenames ("select" for xbm_select.xbm and xbm_select_mask.xbm,
for example), with specified hotspot coordinates
: //SYSCURSOR(id)// :
a builtin cursor, identified as per GdkCursorType
=== Events ===
Event handlers, in V-code, get a slot in the "tape" after the widget they are
attached to. There should be only one handler for a given event type per widget.
: //EVENT(type, handler)// :
add a handler for specified event type to the preceding widget
: //TRIGGER// :
trigger the preceding event handler after **run_create()** finishes creating
widgets (but before the toplevel gets shown); has a slot of its own
: //MTRIGGER(handler)// :
trigger specified event handler (should be the same as on the menubar) for the
preceding menuitem; has a slot of its own and another for the //CHANGE// event
: //WANTKEYS(handler)// :
install priority //KEY// event handler for when the preceding widget is focused;
has a slot of its own and another for the //KEY// event
The event types and their causes (widgets denoted by underlying opcodes):
: OK :
- pressing //OKBTN// or //DONEBTN//
- pressing //Enter// in text entry or pathbox's entry
- choosing from list or pressing //Enter// in //COMBOENTRY//
- double click or pressing //Enter// in a complex list
: CANCEL :
- pressing //CANCELBTN//
- closing a window (call **run_destroy()** if agreeing to it, return without
doing that if ignoring the request)
: CLICK :
- pressing //BUTTON//
- right click on a toolbar button
: SELECT :
- changing selection in radiobutton pack, option menu, list, or //GRADBAR//
: CHANGE :
- changing value in spinbutton or spinslider
- changing value in //TLSPINPACK//, //PCTCOMBO//, //HEXENTRY//, //EYEDROPPER//
- changing text in text entry, pathbox, or //TEXT//
- changing color in color selector
- changing state of //TOGGLE// or checkbutton
- pressing a toolbar button
- selecting a menuitem
- leasing/unleasing the element from //MOUNT//
- scrolling a //CSCROLL//
- resizing //CANVAS//
- changing sort mode in //LISTCX// (should re-sort its map array)
: DESTROY :
- running **run_destroy()** (after the toplevel is unrealized but before most
everything else)
: SCRIPT :
- setting value in the widget from a script
: MULTI :
- setting a list of values to the widget from a script - **evtxr_fn**
: KEY :
- pressing a key when focus is in the widget - **evtxr_fn**
: MOUSE and XMOUSE:
- pressing a mouse button in the widget - **evtxr_fn**
: MMOUSE and MXMOUSE:
- moving mouse in the widget - **evtxr_fn**
: RMOUSE and RXMOUSE:
- releasing a mouse button after pressing it in the widget - **evtxr_fn**
: CROSS :
- cursor entering or leaving the widget - **evtx_fn**
: SCROLL :
- rotating mouse scroll wheel in the widget - **evtx_fn**
: EXT :
- needing to render an area of //CANVAS// - **evtxr_fn**
- right click in //LISTCX// - **evtx_fn**
- click in //COLORLIST// - **evtx_fn**
: DRAGFROM :
- dragging from //DRAGDROP//'s widget - **evtxr_fn**
: DROP :
- dropping to //DRAGDROP//'s widget - **evtx_fn**
: COPY :
- data request from //CLIPBOARD// - **evtx_fn**
: PASTE :
- data received by //CLIPBOARD// - **evtxr_fn**
Unless otherwise noted, the event handlers are of type **evt_fn**. For those of
types **evtx_fn** and **evtxr_fn**, what they get in the extra parameter and what
their return value does, is described under "Callbacks" below.
The difference between //MOUSE/MMOUSE/RMOUSE// and //XMOUSE/MXMOUSE/RXMOUSE//
events is, the latter 3 ask for, and report, tablet pressure.
Outside //EVENT()// commands, event types are referred to by their opcodes, which
have "//op_EVT_//" prefix; like //op_EVT_OK// for //OK// event.
=== Clipboard and drag-drop ===
Clipboard and drag-drop use the same type of data format descriptions. The
//CLIPFORM// command prepares those descriptions for them; its slot in the "tape"
is then passed to the commands that use the formats.
: //CLIPFORM(array, count)// :
a group of formats from **clipform_dd[]** array; has a slot in the "tape"
: //DRAGDROP(field, DRAGFROM_handler, DROP_handler)// :
add handlers for drag and/or drop to the preceding widgets, with formats in
**void**** field; has a slot for itself and two more for the events
: //DRAGDROPm(field, DRAGFROM_handler, DROP_handler)// :
the same but allows the "move" type of drop in addition to "copy" (some programs
use the "move" type when they really should not)
: //CLIPBOARD(field, which, COPY_handler, PASTE_handler)// :
add handlers for copy and paste for specified clipboards (//which//: 1 if the
regular clipboard, 2 if the "primary selection" i.e. what is highlighted, 3 if
both at once), with formats in **void**** field; has a slot for itself and two
more for the events
The //DROP// event handler receives in its //xdata// parameter a pointer to
**drag_ext**; the //PASTE// event, to **copy_ext** and returns TRUE if
successfully imported the data. The //DRAGFROM// and //COPY// events are handled
differently from most others; their handlers receive in their //xdata// parameter
a slot (dynamically created) with which they then interact to send or receive the
data. //DRAGFROM// handler also should return TRUE if it agrees to initiate drag,
FALSE otherwise.
=== Scripting ===
The below commands exist solely to facilitate scripting.
: //ALTNAME(name)// :
add another name (identifier) for the preceding widget; has a slot in the
"tape"
: //FLATTEN// :
make option names from the preceding option menu or radiobutton pack be
directly referrable from script like widget names (in addition to being
referrable as values); has a slot in the "tape"
: //OPNAME(name)// :
set (override) name for the preceding widget
: //OPNAME0// :
unset the current name (the initial empty-string one, or the preceding
unattached label) to prevent it mis-attaching to the next widget
: //UNNAME// :
hide the preceding widget from scripts (by setting it an impossible name)
: //SCRIPTED// :
tell **run_create()** to start a range of "live-scriptable" widgets (those
get extra //ALTNAME// slots automatically added, to make them visible from
script despite being real widgets)
: //ENDSCRIPT// :
end a range of "live-scriptable" widgets; has a slot in the "tape" (to tell
script interpreter where the range ends)
== Functions and macros ==
There are two main groups of functions; the **run_*()** that affect the entire
"tape", and the **cmd_*()** affecting a single slot. Also a few assorted helper
functions and macros.
: **void **run_create_(void **ifcode, void *ddata, int ddsize, char **script);**
build a dialog window out of V-code decription; takes an array with that
description, address and size of the struct that will be copied into its
**ddata**, and an array of script commands or NULL; returns **wdata** of what
got built (possibly already freed, if script was passed in), or NULL if failed
: **void **run_create(void **ifcode, void *ddata, int ddsize);**
same without script commands (a convenience macro)
: **void run_query(void **wdata);**
query dialog contents using its "tape"; takes dialog's **wdata**
: **void run_destroy(void **wdata);**
destroy a dialog; takes dialog's **wdata**
: **void cmd_event(void **slot, int op);**
raise event (specified by opcode) on slot
: **void cmd_sensitive(void **slot, int state);**
set sensitive state on slot
: **void cmd_showhide(void **slot, int state);**
set visible state on slot
: **void cmd_set(void **slot, int v);**
set value on slot
: **int cmd_setstr(void **slot, char *s);**
set text-encoded value on slot; returns -1 if failed, 0 if value was left
unused (when only the fact of setting something mattered), 1 if set
: **void *cmd_read(void **slot, void *ddata);**
read back slot value (as is) given **ddata** (to calculate fields' addresses
faster); returns the value's storage location
: **void cmd_repaint(void **slot);**
repaint slot
: **void cmd_reset(void **slot, void *ddata);**
reset slot or group given **ddata**; causes the widget(s) to reinitialize
: **void cmd_cursor(void **slot, void **cursor);**
set cursor on slot (for its widget's window) given the cursor's slot
: **int cmd_run_script(void **slot, char **strs);**
run script on slot; returns -1 if error happened, 0 if //OK// event handler got
activated, 1 if not
: **void **get_wdata(GtkWidget *widget, char *id);**
from widget to its **wdata**
: **void **wdata_slot(void **slot);**
from slot to its **wdata**
: **void **origin_slot(void **slot);**
from event to its originator
: **void *slot_data(void **slot, void *ddata);**
from slot to its storage location (its own, //not// originator's), given
**ddata**
: **void **find_slot(void **slot, char *id, int l, int mlevel);**
find slot by text name (with explicit length) and menu level (//MLEVEL_*//)
: **void do_evt_1_d(void **slot);**
run event handler in slot, defaulting to run_destroy() if NULL there
: **void dialog_event(void *ddata, void **wdata, int what, void **where);**
handle dialog buttons' events (from //OKBTN// and //CANCELBTN//)
The next three are for those (widget-type-specific) actions that the above
functions do not cover. Such actions were not made into functions to avoid the
interface ballooning up with functions that are applicable to very few widgets
each. Sending requests and receiving responses through a generic interface
instead, is convenient enough in practice.
: **void cmd_peekv(void **slot, void *res, int size, int idx);**
read extra data from slot, given data's index/id, destination, and size
: **void cmd_setv(void **slot, void *res, int idx);**
set extra data on slot, given data's index/id and source; for some functions,
//res// itself is the value (**int** converted to pointer)
: **int cmd_checkv(void **slot, int idx);**
check extra state on slot, given state's index/id
Now, the indices/ids per slot type, what each does, and what type of data expects.
Types are denoted by representative opcodes. Most actions are either get or set,
only very few are valid for both; the check actions are not valid for either get
or set and vice versa, as in totally unrelated.
- //FPICK// :
: //FPICK_VALUE//
get/set the filename with full path, in system encoding: **char[]**
: //FPICK_RAW//
get/set the raw filename; only the file part without path, in UTF8: **char[]**
- //PATH// and //PENTRY// :
: //PATH_VALUE//
get/set the filename: **char[]**
: //PATH_RAW//
get/set the raw filename: **char[]**
- //LISTCC// :
: //LISTCC_RESET_ROW//
set the row index to reset: **int**
- //LISTC// :
: //LISTC_RESET_ROW//
set the row index to reset: **int**
: //LISTC_ORDER//
get sort order: **int[]**
: //LISTC_SORT//
set sort mode: **int**
- //LABEL// and //MENUITEM// :
: //LABEL_VALUE//
set label text: **char***
- //NBOOK// :
: //NBOOK_TABS//
set show/hide tabs: **int**
- //ENTRY// and //COMBOENTRY// :
: //ENTRY_VALUE//
set value: **char***
- //TEXT// :
: //TEXT_VALUE//
set value: **char***
- **wdata** :
: //WDATA_ACTMAP//
set action map: **unsigned int**
: //WDATA_TABLET//
get name of tablet device: **char****
- //WINDOW// :
: //WINDOW_TITLE//
set window title: **char***
: //WINDOW_ESC_BTN//
set slot to react (get "clicked") to //Esc// keypress in window: **void****
: //WINDOW_FOCUS//
set focus to slot (or to nowhere if NULL): **void****
: //WINDOW_RAISE//
set window to raise; value is ignored: **void**
: //WINDOW_DISAPPEAR//
set window to hide from view (to allow a screenshot), or to unhide: **int**
: //WINDOW_DPI//
get DPI: **int***
: //WINDOW_TEXTENG//
set to render text: **texteng_dd***
- //COLOR// :
: //COLOR_RGBA//
set current color and alpha: **int[2]**
: //COLOR_ALL//
set current and previous color and alpha: **int[4]**
- //SPIN// and //SPINSLIDE// :
: //SPIN_ALL//
set value, min and max: **int[3]**
- //COLORLIST// :
: //COLORLIST_RESET_ROW//
set the row index to reset: **int**
- //PROGRESS// :
: //PROGRESS_PERCENT//
set progress percentage: **int**
- //CSCROLL// :
: //CSCROLL_XYSIZE//
get x offset, y offset, onscreen width, onscreen height: **int[4]**
: //CSCROLL_LIMITS//
get full width minus onscreen width, and full height minus onscreen height:
**int[2]**
: //CSCROLL_XYRANGE//
set x offset, y offset, full width, and full height, for a delayed resize:
**int[4]**
- //CANVASIMG//:
: CANVAS_SIZE
set full size: **int[2]**
- //CANVAS// :
: CANVAS_SIZE
get onscreen size / set full size: **int[2]**
: CANVAS_VPORT
get viewport (x, y, x + width, y + height): **int[4]**
: CANVAS_REPAINT
set area that needs repaint (x, y, x + width, y + height): **int[4]**
: CANVAS_PAINT
set area you want to repaint (//rgb// = NULL) - bounds will be modified and
buffer allocated / set image data (//rgb// != NULL) - buffer will be painted
into the bounded area: **rgbcontext***
: CANVAS_FIND_MOUSE
get mouse state: **mouse_ext***
: CANVAS_BMOVE_MOUSE
set to nudge mouse cursor by dx, dy: **int[2]**
- //FCIMAGE// :
: //FCIMAGE_XY//
set location marker to x, y: **int[2]**
- //KEYMAP// :
: //KEYMAP_KEY//
set key to map to slot: **key_ext***
: //KEYMAP_MAP//
get/set keymap: **keymap_dd***
- //EV_MOUSE// :
: //MOUSE_BOUND//
check to keep cursor inside canvas' visible area, by scrolling the canvas &
moving the cursor back in; return TRUE if moved it
- //EV_DRAGFROM// :
: //DRAG_DATA//
set start & end of data exported through drag: **char*[2]**
: //DRAG_ICON_RGB//
set RGB color (packed) for drag icon (color patch): **int**
- //EV_COPY// :
: //COPY_DATA//
set start & end of data exported through copying: **char*[2]**
- //CLIPBOARD// :
: //CLIP_TEXT//
set text to export through clipboard: **char***
: //CLIP_OFFER//
check to offer data through clipboard; return TRUE if successful
: //CLIP_PROCESS//
check to request data from clipboard; return TRUE if successful
- all regular widgets :
: //SLOT_SENSITIVE//
check sensitive state; return TRUE if sensitive
: //SLOT_FOCUSED//
check focused state; return TRUE if focus in toplevel window is inside the
widget
: //SLOT_SCRIPTABLE//
check scriptability; return TRUE if script flag is set in opcode
: //SLOT_UNREAL//
check unreality; return TRUE if the widget is simulated
: //SLOT_RADIO//
check radio-ness; return TRUE if the widget is radio toolbutton or radio
menuitem
- //FONTSEL// :
: //FONTSEL_DPI//
set DPI: **int**
Finally, the macros.
: //VSLOT_SIZE// :
size of a slot, in void pointers: 3
: //GET_DDATA(wdata)// :
from **wdata** to **ddata**: **void***
: //GET_WINDOW(wdata)// :
from **wdata** to its toplevel widget's slot: **void****
: //GET_REAL_WINDOW(wdata)// :
from **wdata** to actual toplevel widget: **void***, in fact **GtkWidget***
: //NEXT_SLOT(slot)// :
from slot to the next one: **void****
: //PREV_SLOT(slot)// :
from slot to the previous one: **void****
: //SLOT_N(slot, n)// :
from slot to the n-th one after: **void****
: //TOOL_ID(slot)// :
get toolbutton's action code: **int**
: //TOOL_IR(slot)// :
get toolbutton's right-click action code: **int**
: //ACTMOD(action, mode)// :
combine action and mode into action code
== Callbacks ==
Most events are handled by **evt_fn** function (the default one):
**void (*evt_fn)(void *ddata, void **wdata, int what, void **where);**
The //ddata// and //wdata// parameters are precisely that; the backing struct, and
the "tape". The //what// parameter is the event's opcode, and the //where// is
its slot.
One handler for all (or most) events happening in the dialog, is the usual
pattern when using V-code. When inside it need do different things depending on
what is happening to what, there are 3 ways to make the decision.
One is to use the "//what//" parameter; it is mostly done to separate out final
button presses (the //op_EVT_OK// and //op_EVT_CANCEL//) from everything else.
Another is to do "**void *cause = cmd_read(where, ddata);**" and compare the
"//cause//" to addresses of variables and/or fields that V-code commands refer to.
This serves for most types of controls, and with them **cmd_read()** is usually
needed anyway, to get the newly-modified value (a few are "self-reading" but that
is rarely significant; only affects when to save the previous value if you need
it).
The third is to do "**void **slot = origin_slot(where);**" and compare the slot
with controls' slots stored using //REF()// commands. This in practice is only
needed when several //BUTTON()//s are in one dialog; with them all having
//CLICK// events and not referring to any memory locations, their slots are the
only thing differentiating them.
Now, some events need send some extra data to their handler. Those are handled by
**evtx_fn** function:
**void (*evtx_fn)(void *ddata, void **wdata, int what, void **where, void *xdata);**
What, specifically, the handler receives in //xdata//, depends on the event.
: //CROSS// :
TRUE if entering, FALSE if leaving: **int** converted to pointer
: //SCROLL// :
pointer to **scroll_ext**
: //EXT// from //LISTCX// :
row index where right click happened: **int** converted to pointer
: //EXT// from //COLORLIST//:
pointer to **colorlist_ext**
: //DROP// :
pointer to **drag_ext**
: //COPY// :
pointer to **copy_ext**
Yet other events not only send extra data to the handler, but also await a return
value from it. The value, for nearly all, is simply TRUE or FALSE, and the function
handling them is **evtxr_fn**:
**int (*evtxr_fn)(void *ddata, void **wdata, int what, void **where, void *xdata);**
What the handler receives in //xdata//, and what its return value means, again
depends on the event.
: //KEY// :
pointer to **key_ext**; return TRUE if the key was handled, FALSE otherwise
: //MOUSE//, //XMOUSE//, //MMOUSE//, //MXMOUSE//, //RMOUSE//, //RXMOUSE//:
pointer to **mouse_ext**; return TRUE if the mouse event was handled
: //EXT// from //CANVAS//:
pointer to **rgbcontext**; return TRUE if something was rendered into buffer
: //DRAGFROM// :
pointer to **drag_ext**; return TRUE to initiate drag
: //PASTE// :
pointer to **copy_ext**; return TRUE if data successfully imported
: //MULTI// :
pointer to **multi_ext**; return 0 if error, 1 if success and the //xdata// not
needed anymore, -1 if success and the struct should be kept (not freed)
Handling some events can include system-dependent interactions with their
//xdata//. Instead of saddling some poor unsuspecting widget with those, an
"intercessor" capable of processing that is passed as the originating event, in
the "//where//" parameter. The intercessors' opcodes are //op_EV_*// to their
events' //op_EVT_*//; their functions are described under the headings
//EV_MOUSE//, //EV_DRAGFROM//, and //EV_COPY// in the "Functions and macros"
chapter above. They are accessed like this: "**cmd_setv(where, pp, DRAG_DATA);**".
== THE END ==
|