








MGUI

Brief programming guide


INTRODUCTION

MGUI (MORELLO Graphic User Interface) is a user interface 
currently available in DOS (16 and 32 bit), Windows, Win32 and Unix 
X-Window environments.
The main features of MGUI are:

- Both C and C++ API.
- Total source code compatibility among all supported platforms.
- Identical look & feel in all environments (similar to OSF/Motif).
- Coding process is very smart and produces compact source code.
- A manager object automatically positions and resizes its children, 
making layout construction very simple.
- A design tool (MGUI Designer) generates C and C++ source files 
that can be compiled  in all supported environments.
- Supports 16 and 256 colors under DOS, up to 16 millions in 
Windows and X-Window environments.
- BMP, GIF, TIFF and JPEG formats are supported for image 
drawing.
- Log facility with run-time user modifiable tracing level.
- Resource file handling to customize Object attributes as text, font 
and color
- Run-time Object attribute changing (with optional store in the 
resource file)



MGUI has been successfully compiled and tested in the following 
environments:

- Borland C 4.5 with BGI graphics library (real mode) 
- Borland C 4.5 PowerPack with BGI graphics library (DPMI 16 bit) 
- Borland C 4.5 PowerPack with BGI graphics library (DPMI 32 bit).
- Borland C 4.5 with Csaba Biegl GRX 2 graphics library.
- DJGPP  with Csaba Biegl GRX 2 graphics library.
- RSXNTDJ/DJGPP Win32
- GNU-Win32
- Borland C++ 4.5 under Windows 3.1 and Win32
- Visual C++ 5.0 under Windows 95
- SUN Solaris 2.4 (x86 & sparc)
- IBM AIX 3.2.5 Risc 6000
- Linux X11 and svgalib/GRX (console mode)
- Watcom C 11.0 under DOS/4GW, GRX, Windows 3.1 and Win32

These features make MGUI useful to everyone who wants to realize 
multi platform applications, with absolutely no GUI porting needs.

MGUI was initially developed under DOS (Borland BGI), due to the 
missing in this environment of an advanced and simple to use GUI.
The library design was hardly conditioned by the knowledge acquired 
in the Unix X-Window environment. This has made very simple to 
obtain a Unix version of the MGUI library.
Then I decided to port the software in the DJGPP environment, and it 
became possible to create 32 bit DOS applications using the GNU gcc 
compiler and gaining great improvements in the graphics performance.
Finally, after a little tuning in the MGUI API, I realized the porting in 
the Windows environment.


ARCHITECTURE

MGUI is coded in ANSI C and consists currently of about 28.000 
source code lines. A small portion (10% - 15%) is highly system 
dependent, while the remaing 85% - 90% is completely system 
independent. The system specific code implements the interface 
between the specific graphic environment and the library Objects.
	This design makes MGUI easy to evolve and easy to port in 
other environments.
There are four main functional blocks in the library structure:

1) System Event Management		(SEM)
2) Graphics Request Management	(GRM)
3) TopLevel	Window Management	(TWM)
4) MGUI Objects (Widgets)

The first block handles all the system generated events:

- mouse button press
- mouse button release
- mouse move
- keyboard
- timeout
- window move
- window resize
- window expose


	When one of the above event occurs, the SEM block generates 
an event and passes it to the TWM block.
The DOS implementation uses a polling tecnique to handle mouse 
events, so mouse clicks and moves while the program is not waiting for 
input are lost.
	The TWM block major task is to handle all those events the 
system notifies to a window. Every system window owned by the 
application has a MGUI Object bound to, events are received by this 
Object (SHELL) and propagated to the destination Object following 
the layout hierarchy.
Since there is not a Window Manager in the DOS implementation, the 
TWM block handles window movement and focus delivery in this 
environment.
	The GRM block offers the API for the display functions: color 
and font management and drawing primitives.
	Last block, the greater and system independent one, contains the 
MGUI Objects API.
In the remainder of this guide are described details about Objects as 
well as the programming tecnique for a strict MGUI application. Refer 
to MGUI Reference Guide for more details about all available library 
functions.
	Every example mentioned in this guide can be obviously 
compiled and executed without changes in any of the supported 
platforms.


COLOR MANAGEMENT

	Color management reflects perfectly MGUI Library philosophy: 
it's simple, compact (and so limited) but offers a unique approach to 
the DOS, Windows and Unix systems! 
A program can operate in a 'palette' color mode or in a 'static' color 
mode, depending on the presence of a modifiable color palette in the 
graphic system.
	Palette mode is normally available when the number of colors 
simultaneusly viewable is small (typically 16 or 256), and the graphic 
card is capable of a wide range of selectable colors (256K in the 
standard VGA). When operating in this mode, the pixel value in the 
video memory is used as an index by the display adapter to address a 
table containing the actual R-G-B values. By using 8 bit per pixel 
(planes) it's possible to address 256 different entries in the table and 
any combination of R-G-B values (256K in the standard VGA) can be 
assigned to every entry (palette cell).
This mode is efficient because permits you to select colors in a wide 
range of available ones. Moreover it makes possible to realize color 
animation by simply changing R-G-B values in the palette.
	In the static mode, the graphic system operates with a fixed 
color palette. This can be achieved in 16 color mode under Windows or 
in HiColor mode which offers 15, 16 or 24 bit per pixel with 
respectively 32K, 64K or 16M colors.
HiColor mode grants a superior photographic resolution, but reaches 
slower performances because requires 2 or 3 byte to code a single 
pixel, where the palette mode with 256 colors needs only one byte.
HiColor mode is not supported in DOS environment.
	If the MGUI application does not require color animation, it can 
actually ignore the number of available colors and the operanting mode 
(palette or static).
	If the application wants to know some detail about the graphic 
environment in which operates, it can call the MPaletteMode() 
function which return 'True' when in palette mode. The MGetNColors() 
function returns the number of simultaneously viewable colors in the 
system and MGetNPlanes() returns the number of color planes.
	Before using a color, the program must allocate it in the library. 
There are several allocation functions, each returning the color 
identifier that can be used in the drawing functions or to set a new 
color to an Object. The MAllocColor() function allocates a color 
specified by the three argument values (in the range 0-255), identifying 
the R-G-B color components. When the color is no more needed, it 
should be freed by calling the MFreeColor() function.


Palette Mode

When operating in palette mode, a copy of the video palette is hold in 
memory by the library with a counter and a status flags for each entry 
(color cell). If so, a color identifier is equivalent to a cell index. Each 
counter value is initially set to zero and the status flag to 'free'. As a 
color allocation request occurs, the following situations can be in 
effect:
If none of the already used cells contains the desired R-G-B values and 
there is a free cell, it's status is set to 'used', the counter to 1 and cell R-
G-B values are filled in with the requested ones. The cell index is 
returned as the new color identifier.
If there is a cell containing the desired R-G-B values, then its counter is 
incremented by one and its index is assumed as the color identifier.
Finally, if none cell contains the right values and theree aree no free 
cell available, the library computes which is the cell whose R-G-B 
values are closest to the requested ones, increments its counter and 
returns the cell index as the color identifier.
	When a color is freed, the corresponding palette index counter is 
decremented by one. Moreover, if the counter reaches the zero value, 
the cell flag is reset to 'free' and the palette entry becomes available for 
further allocations.
This handling policy causes an optimal use of the palette resource, 
provided that the application correctly frees all no more needed colors.
	To obtain the palette animation feature, the application should 
reserve one or more palette cells for this purpose via the MAllocCell() 
function call. The cell becomes 'reserved' and its R-G-B values can be 
set at any time by using the MSetCellColor() function call. Moreover 
its index is not used by the library for successive color allocation 
requests, until the program frees the cell using the MFreeCell() 
function.
If there are no free cells available, MAllocCell() returns, as an error, 
the black color identifier. In this case, every attempt to set the cell R-G-
B values will obviously fail.


FONT MANAGEMENT

MGUI library provides ten preallocated (static) fonts in any platform, 
referenced in the source code as macros. There are four fixed picth 
fonts: DEFAULT_FONT, FIXED_SMALL, FIXED_MEDIUM and 
FIXED_LARGE. The others are proportional  Times ed Helvetica: 
TIMES_SMALL, TIMES_MEDIUM, TIMES_LARGE, 
HELV_SMALL, HELV_MEDIUM and HELV_LARGE.
Except for the Borland BGI and Watcom DOS/4GW platforms, it's 
possible to load additional fonts via the MLoadFont() function call, 
specifying the font family, font size, bold and italic options. The font 
family is a case unsentive string which specifies the font name.
The standard "Times", "Helvetica", "Courier" and "Symbol" values are 
recognized in all environments (except for DOS BGI), but the program 
may load, if required, system specific fonts like 'Terminal' in Windows.
The font size specifies the desired font height. The returned font can 
have an actual height smaller than the requested one, if the system 
doesn't support scaleable fonts or the font itself is fixed size.
An application can load up to 24 new fonts. Notice that, since every 
font loading request allocates an entry even if the the requested font 
has already been loaded before, it's highly recommended to unload any 
unused font via the MUnloadFont() function call.
If the MLoadFont() request cannot be satisfied, the function returns the 
predefined value DEFAULT_FONT as an error. This also offers the 
application the opportunity to continue without testing the returned 
font identifier since it's always valid.


RESOURCE HANDLING

Since release 1.9, the library provides support for resource handling 
in ascii text configuration file with standard .ini format under 
Windows and DOS, with standard .Xdefaults format under X11.
A system configuration file, in the /mgui/sys directory, defines 
general resources as the resolution sensitive static fonts or the default 
background and foreground colors.
The name of this file is system dependant: MGui under X11, mgui.ini 
under Windows, mgui.bcf under BGI, mgui.gcf under DJGPP, 
mgui.wcf under Watcom DOS.
Colors are specified in the standard X format (eg: <yellow> or 
<#ffff00>) under X11. Three decimal values, separated by commas, 
are used instead in all other environments (eg: <255,255,0> for 
yellow).
Static fonts in the system configuration file are specified in the 
standard X format under X11, using the font file name (.mfn) under 
BGI and Watcom, using the font file name (.fnt) under DJGPP/GRX. 
In the Windows environment the fonts are defined in the format 
<family,height,bold> (eg: <arial,20,1> for arial 20 points bold).

Another resource file is used to determine the resources of a MGUI 
program. This file is located in same directory where the executable 
resides (in the user home directory under Unix) and has the same 
name of the program, with the appropriate extension (.nomeprog 
under Unix).

A resource is identified by the combination of an object name and a 
resource name.
Valid resources for GUI object are the following attributes: 
background/foreground colors, font, text and shadow.
The Object font resource must be expressed as a static font name (eg: 
TIMES_LARGE or FIXED_MEDIUM).
All resources are loaded at program startup, Object resources are 
enabled via the function call MObjectEnableOptions(), after the 
Object creation.
For example, the code:

	label = MCreateLabel(mainshell, "Ctrl+Shift+MouseClick here to 
customize", FIXED_MEDIUM);
	MObjectEnableOptions(label, "label_1", MO_ALL);

will cause the Object ‘label’ to be customized with all available 
resources for the object named ‘label_1’.
Object resources, enabled via MObjectEnableOptions (), can be 
saved to the application resource file at any time by using the call 
MObjectSaveOptions().

MGUI provides a run-time advanced feature that lets the user 
change, and optionally store in the application file, the resources 
enabled for a given Object. This can be done via a dialog window, 
open by clicking the mouse button in an enabled Object while 
holding down the <Ctrl+Shift> keys.

The library API also lets the programmer load/save general 
application resources, by specifying arbitrary object and resource 
names.

 
EVENT PROPAGATION

Events are messages for MGUI Objects or timeouts that cause a 
callback call.
A callback is a user defined function assigned to a particular event. As 
this event occurs, the library invokes that function as an action for the 
event.
When the application sets a callback, it has the opportunity to join a 
void pointer to it.
This pointer is supplied by the library to callback as the third argument.
Object events are: mouse and keyboard input, window expose and 
resize, or internally generated events such as 'text change', 'color 
change' or 'shadow change'.
Unlike many high level programming environments (like OSF/Motif or 
Windows) which require a system window for each Object, MGUI 
requires only a system window for each individual desktop window, 
whatever complex is the Object hierarchy.
System events are received by the library to each window owned by the 
MGUI application, despatched to the 'SHELL' Object (the only one 
bound to the system window) and then received by the destination 
Object via the layout hierarchy.
Mouse events are normally despatched to the Object below the mouse 
pointer. Once an Object has received a 'mouse button press' event, it 
can 'grab' the mouse so it can receive all mouse events including the 
'button release', even if the mouse is over another Object.
Notice that the 'mouse move' event is currently generated and reported 
only if there is a grabbing Object.
Keyboard events are reported by the system to the top level window 
that has the 'input focus'. This window is normally highlighted with a 
different color in the title bar.
When a MGUI top level window receives the input focus, there is a 
single destination Object that can really process keyboard events and it 
is highlighted with a thin black border to indicate it.
Input focus is shifted to other Objects by explicit user selection with 
the mouse, by a MSelectObject() function call in the program, or by 
spontaneous Object grant (as a result to the user pressing on arrow or 
tab keys.
Up and left arrow keys (if not processed by the Object) cause the input 
focus transferring to the previous 'brother' Object. Similarly, down and 
right arrow keys cause the focus to be transferred to the next brother 
Object. Tab and <Shift+Tab> transfer the input focus (if not 
processed) respectively to the next 'cousin' and the previous cousin.


HELLO WORLD

This paragraph shows the source code needed to realize the MGUI 
version of the 'Hello' program (first example in the Kernighan & 
Ritchie C language bible).

/*
 * example 1
 */
#include "mgui.h"

void MGUIMain(int argc, char **argv)
{
	MMessageDialog("Example 1", "Hello, world", " Ok ", NULL);
}

This program generates a window, named 'Example 1', which shows the 
text 'Hello, world' and a push button with the string 'Ok'. The program 
terminates when the user presses the 'Return' key, or when the left 
mouse button is pressed and then released over the push button.
The first program line includes the MGUI include file which contains 
the library function prototypes and structures definitions needed to the 
application. It includes 'mguidefs.h' which includes 'mevent.h' and 
'keycodes.h'.
As you can notice, the program does not start with the canonical main() 
function, due to Windows compatibility requirement (Windows 
programs start at WinMain()).
Unlike Windows imposed WinMain() entry point, MGUIMain() 
supplies the usual 'argc', 'argv' arguments to the application (including 
the Windows platform).
This sample program uses only the MMessageDialog() utility function. 
The first argument the application must supply is a character string 
which identifies the dialog window title. The second one is the message 
text that appears centered in the dialog window. The first button text is 
specified as the third argument, followed by a NULL terminated list of 
optional additional button text. The function return value is an integer 
that specifies which button has been selected by the user (0 for the first 
button, 1 for the second, and so on).
This example shows how simple and compact is MGUI code.


MGUI OBJECTS

There are four MGUI Object categories:

- Root
- Manager
- Main terminal
- Compound terminal

The Root Object is the SHELL. Its main task is to redirect all system 
events received from the TopLevel Window Manager block (keyboard 
and mouse input, window move, resize and expose), to its descendant 
Objects.
SHELL is the only Object with no parent; it has only one direct child 
which is a FORM Object (created automatically with the SHELL).
The FORM belongs to the Manager Object category and it's the only 
one, except for the SHELL, that may contain other Objects (eventually 
other FORMs). It plays a fundamental role in the library architecture 
taking care of children resizing, positioning policy. In addition it 
despatches to the appropriate child any event, not processed by itself, 
received from its parent.
Besides the simple FORM Object are the 'Paged', the 'Clipped' and the 
'Scrolled' FORMs. The first lets you create several children series and 
manage then one by one. The second makes the visible Object area a 
portion of a larger virtual area. By using the MFormSetClip[XY]() 
function calls, the application can change the starting offset of the 
visible area. The Scrolled FORM is a Clipped FORM with a vertical 
and an horizontal SCROLLBAR Objects that permit the user to easily 
change the visible portion of the FORM content.
Main terminal Objects are all the remaining not compound Objects. 
LABEL, EDIT, PBUTTON (push button), TBUTTON (toggle button), 
RBUTTON (radio button), MENU, DRAWAREA and SCROLLBAR 
belong to this category .
Compound terminal Objects are composed by a Main terminal which 
needs the contribution of some Manager and some other Main terminal 
Object to operate.
This category includes the Option EDIT (a combobox) and all LIST 
varieties.
The Scrolled FORM is actually a Compound Manager because it's 
composed by a simple FORM containg a Clipped FORM and two 
SCROLLBARs.
Except for the SHELL, all Object creation functions requires the 
'parent' Object identifier as the first argument. It must be a FORM or a 
SHELL Object type (the SHELL actually redirects the creation to its 
FORM child).
After the creation, every Object has a gray background and a black text 
color (also if not used). These colors can be changed at any time via the 
MObjectSetColor(), MObjectSetBackgroundRGB() and 
MObjectSetForegroundRGB() function calls.
Objects have a default shadow that can be be modified after the 
creation via the MObjectSetShadow() function call. Allowed shadow 
types are those identified by the macros: WS_SHADOW_IN, 
WS_SHADOW_OUT, 
WS_HATCHED_IN and WS_HATCHED_OUT.
When changing a shadow, you should not contort it, for example, 
making a LABEL look like a PBUTTON.
Additional colors allocation (and free) to generate the 3-D look are 
made by the Object itself, both on creation and when the application 
assignes new colors to it.
Although Object font (when needed) is specified in the creation 
function call, it's possible to change it futher, by using the 
MObjectSetFont() .

The typical MGUI application flow is summarized in the following:

- SHELL Object creation
- Manager and Terminal Object creation
- Callback setting in some Object
- SHELL window realize
- Wait for user interaction in a loop library function
- Callback execution on Object demands
- Program termination when the appropriate callback is called

The following example shows a program, similar to the first example, 
that uses the above flow.

/*
 * example 2
 */
#include <stdlib.h>
#include "mgui.h"

void ExitCB(MOBJECT obj, void *a, void *b)
{
	if (MMessageDialog("EXIT",
	  "Are you sure you want to quit?", "Sure", "Abort", NULL) 
!= 0)
		return;
	obj = MObjectShell(obj); 
	MShellUnrealize(obj);
	MShellDestroy(obj);
	MGUITerm();
	exit(0);
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, pushbutton;

	shell = MCreateShell("Example 2", 0);
	MCreateLabel(shell, "Hello, world", HELV_LARGE);
	pushbutton = MCreatePButton(shell, "Ok", TIMES_LARGE);
	MPButtonSetCallback(pushbutton, ExitCB, NULL);
	MShellRealize(shell);

	MMainLoop();
}

The ExitCB() function uses the typical MGUI Object callback 
prototype. First argument is the calling Object identifier. The second, 
notifying Object data, depends on the particular callback type. The 
third is the application user data, supplied at the callback setting time.
In this case, only the first argument is used. In fact it offers the 
possibility to go back to SHELL Object, ancestor of the calling push 
button Object, via the MObjectShell() function call. The SHELL 
identifier is then used to remove its window from the screen (with 
MUnrealizeShell()) and to destroy the SHELL itself and all its 
descendant by using the MShellDestroy() call.
The callback proceeds calling the termination function MGUITerm() to 
free all resources, and then the canonical exit() function.
Notice that the call to MGUITerm() is particularly important in 
Windows, since it doesn't automatically free resources allocated by a 
terminating program.
	For simplicity, in the following examples, ExitCB() will be used 
as the termination callback, even if its source code will not appear in 
the examples.
	This program code creates a SHELL Object, then a LABEL and 
a PBUTTON as its children.
LABEL Object's task is to show multi line text within a window. By 
default it has no visible shadow and the text is center aligned in the 
horizontal direction. Alignment can be changed via the 
MLabelSetAlignment() function call.
Text to be displayed and the font to be used are speficied respectively 
as the second and third argument of the LABEL creation function.
The PBUTTON Object also shows multi line text, unlike the LABEL it 
has a shadow that gives it a particular 3 D look and provides a callback 
for the mouse button press and release user action.
In this example the push button is created with the "Ok" text, Times 
Large font and the termination callback ExitCB() is set.
The MMainLoop() function call places the program in the application 
events main loop. This function never returns, so the program can 
terminate only via an application defined callback (like ExitCB()).
Next example shows an alternative method to handle the event loop.

/*
 * example 3
 */
#include "mgui.h"

static int quitting = False;

void QuitCB(MOBJECT obj, void *a, void *shell)
{
	MShellUnrealize((MOBJECT)shell);
	MShellDestroy((MOBJECT)shell);
	quitting = True;
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, pushbutton;

	shell = MCreateShell("Example 3", 0);
	MCreateLabel(shell, "Hello, world", HELV_LARGE);
	pushbutton = MCreatePButton(shell, "Ok", TIMES_LARGE);
	MPButtonSetCallback(pushbutton, QuitCB, shell);
	MShellRealize(shell);

	MLoopWhileFlag(&quitting, False);
}

As you can note, the termination callback QuitCB() does not call 
MGUITerm() and exit(). It only destroys the shell and sets to True the 
global variable 'quitting'. When the QuitCB() callback returns, the 
program continues execution within the MLoopWhileFlag() function. 
As the value of 'quitting' is no more equal to False (as required by the 
call), MLoopWhileFlag() returns and so does also MGUIMain(). In this 
case the MGUITerm() and exit() function calls are made internally by 
the library (as happens in the example 1).
When setting the QuitCB() callback, the example code passes the 'shell' 
variable as the callback user data. For this reason QuitCB() receives the 
SHELL identifier as the third argument and can use it to destroy the 
SHELL before terminating.
This event loop handling method, used internally by the 
MMessageDialog(), can be used whenever the application needs to 
temporarily block the program flow (also within a callback execution), 
handling events and callbacks, and to resume the control flow after the 
MLoopWhileFlag().


FORM

The simplest version of the FORM Object acts as a container that 
arranges its children one per row ('RowForm') or one per column 
('ColForm'), separated by a given spacing offset. Its natural dimensions 
are those which make possible to display all children at their default 
dimensions. 
Particularly, the dimension corresponding to the child insert direction 
(height in case of RowForm) is equal to the sum of any child dimension 
plus the spacing offsets. The other dimension equals the maximum 
corresponding one among all children plus the spacing offset.
In some child attachment condition or in response to a user resize 
request, the FORM Object adapts its children dimensions and/or 
positions. This positioning and resizing policy is also conditioned by 
the child Object resize capability. Every Object has a default resize 
capability for each direction that can be modified via the 
MObjectSetResize() function call. For example a LABEL can be 
resized, by default, only in the horizontal dimension, while a 
PBUTTON cannot be resized in both directions.
If all widgets are non resizeable and the FORM’s dimensions are 
enlarged, the FORM changes the children positions centering them into 
virtual boxes corresponding to the dimensions assumed by the children 
if they were resizeable.
The alignment within this virtual box can be changed via the function 
MObjectAlign().
If the container is a table FORM, then the box corresponds to the table 
cell.
When constructing the layout hierarchy, as Objects are added to a 
FORM  the attachment policy acts as follows.
The bottom and right Object outline is always attached to the FORM, 
the top outline is attached to the FORM if it is a 'ColForm', to the 
previous Object otherwise. Finally, the left outline is attached to the 
FORM if it is a 'RowForm', to the previous Object otherwise.
If the container is a table FORM, widgets are inserted by filling the 
table rows.
The library provides two functions to change Object's attachment. 
MObjectSetAttachment() is used to remove any of the four links (top, 
bottom, left, right). MObjectSetOffset() can be used to modify the 
corresponding attachment offsets.
The FORM resize capability depends on its own attachment and on its 
children attachments and resize capability.
The FORM Object is always resizeable in a given direction, unless an 
attachment in the same direction is removed, or if every contained 
child is not resizeable and the FORM is not attached to the same parent 
in both ends.
When a resize occurs in a given direction, if every contained Object is 
linked to a brother or to the parent, the size increase/decrease is equally 
distributed among all those children resizeable in that direction. If no 
Object is resizeable, then child spacing is increased/decreased.
If you want to make a FORM unsensitive to a resize request, it's 
sufficient to remove an attachment in a given direction to any of its 
contained Objects.
Next example shows the FORM's child geometry managent policy.

/*
 * example 4
 */
#include <stdlib.h>
#include "mgui.h"

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, rform, cform, pb, lbl;

	shell = MCreateShell("Example 4", 0);

	rform = MCreateRowForm(shell); 
	lbl = MCreateLabel(rform, "This Label establishes the 
width", DEFAULT_FONT);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	lbl = MCreateLabel(rform, "Small Label", DEFAULT_FONT);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	lbl = MCreateLabel(rform, "Not resizeable Label", 
DEFAULT_FONT); 
	MObjectSetOffset(lbl, -1, -1, 15, -1);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	MObjectSetResize(lbl, False, False); 

	cform = MCreateColForm(shell); 
	pb = MCreatePButton(cform, "dummy", TIMES_LARGE); 
	pb = MCreatePButton(cform, "Quit", TIMES_LARGE);
	MObjectSetAttachment(pb, True, True, False, True);
	MPButtonSetCallback(pb, ExitCB, NULL);
	MShellRealize(shell);

	MMainLoop();
}

Objects which have a SHELL as parent are actually contained in a 
main FORM (SHELL's child), so 'cform' is arranged on bottom of 
'cform'.
The window dimensions equal the main FORM ones, increased by its 
margins.
The FORM width is determined by its largest child Object, while its 
height is equal to the sum of any child dimension plus the spacing 
offsets.
The container 'rform' has three LABEL children with a shadow 
imposed to make their dimensions visible. The first forces the parent 
width because it's the largest child. The second is enlarged by the 
FORM since it's resizeable. The last LABEL is moved since its resize 
capability has been inhibited and its natural width is smaller than it's 
parent one.
The ColForm 'cform' contains two push buttons arranged by columns 
(in the same row). Its natural width is smaller than it's parent one and 
both children are not resizeable in the horizontal direction. However, 
since its left and right ends are attached to the same container, 'cform' 
is resized horizontally to match 'rform' width. The size increase affects 
only the children relative spacing since their relative attachment has 
been removed.


PAGED FORM

The PagedForm contains various children series, visible one at a time, 
each one having its own attachment relationships. The Object's width 
and height are those corresponding to the maxima among all Object 
series.
The active page can be selected in the source code via the 
MPagedFormSetActivePage() function call, or by the user keyboard 
action in the PageUp, PageDown, Home or the End key, while the 
input focus is owned by an Object within the PagedForm. There is also 
an 'extended' version of this Object, which shows the active page 
number in a little LABEL and provides four push buttons to select the 
active page.
The number of pages in the FORM is specified in the creation function 
call. In order to bind an Object to a particular page, this should be 
selected by first calling MFormSetActivePage() (counting from zero).


CLIPPED FORM

This Object has virtual width and height, corresponding to its natural 
dimensions, and actual width and height, corresponding to its visible 
area.
Virtual dimensions only depend on its contained children and are 
unsensitive to external resize, while actual dimensions are enstablished 
on Object creation and can always be resized.
In order to have only one virtual dimension in effect, it's sufficient to 
specify a negative value (invalid) for the other one.
The clipped area (that visible) can be moved within the virtual area by 
changing its x and/or y coordinates via the MFormSetClipX() and 
MFormSetClipY() function calls.


SCROLLED FORM

Scrolled FORM is just a clipped FORM that provides two 
SCROLLBAR Objects to change the clipping coordinates. Therefore 
it's actually a Compound Object, since it needs two 'brother' Objects 
(the SCROLLBAR) and a parent Object (transparent to the 
programmer) which contains both the FORM and the SCROLLBARs.
As with the clipped FORM, it is possible to have only one virtual 
dimension. In this case, only the SCROLLBAR corresponding to such 
dimension is created.
MGUI functions that create compound Objects all return the terminal 
Object identifier. Some user requests made to a compound terminal 
Object are automatically redirected by the library to the ancestor of the 
compound Object as a whole. This happens for map/unmap, 
delete/insert and for attachment change requests.
The following example uses a scrolled FORM to contains the same 
Object hierarchy as the previous example.

/*
 * example 5
 */
#include <stdlib.h>
#include "mgui.h"

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, sform, rform, cform, pb, lbl;

	shell = MCreateShell("Example 5", 0);

	sform = MCreateScrolledForm(shell, 80, 60);
 
	rform = MCreateRowForm(sform);
	lbl = MCreateLabel(rform, "This Label establishes the 
width", DEFAULT_FONT);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	lbl = MCreateLabel(rform, "Small Label", DEFAULT_FONT);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	lbl = MCreateLabel(rform, "Not resizeable Label", 
DEFAULT_FONT); 
	MObjectSetOffset(lbl, -1, -1, 15, -1);
	MObjectSetShadow(lbl, WS_SHADOW_IN, 1, 0); 
	MObjectSetResize(lbl, False, False); 

	cform = MCreateColForm(sform); 
	pb = MCreatePButton(cform, "dummy", TIMES_LARGE); 
	pb = MCreatePButton(cform, "Quit", TIMES_LARGE);
	MObjectSetAttachment(pb, True, True, False, True);
	MPButtonSetCallback(pb, ExitCB, NULL);

	MShellRealize(shell);

	MMainLoop();
}

The scrolled FORM is 80x60 pixel wide and shows both horizontal and 
vertical SCROLLBARs. If you run this program in Windows or X-
Window, you can notice how the SCROLLBAR adapts to the new 
dimensions when the window is resized.


RBUTTON

The radio-button is used to make an exclusive selection in a group of 
available ones. The group is formed simply by creating all buttons as 
children of the same parent. When a radio-button is selected, the one, if 
any, that was previously selected is automatically deselected. Both the 
Objects generate a callback to notify the application about their new 
selection status. The object data supplied by the callback in the second 
argument is a boolean value that specifies the Object selection status.
Radio-buttons are always created deselected, so, to make the initial 
selection in the Object group, you can use the function call 
MRButtonActivate().
This Object has a particular default color assigning policy: it assumes 
its parent background and foreground color. However, it's possible to 
change them via the provided color setting function calls (like 
MObjectSetColor()).

/*
 * example 6
 */
#include <stdio>
#include <stdlib.h>
#include "mgui.h"

MOBJECT label;

void RadioButtonCB(MOBJECT rb, MVALUE status, void *ii)
{
	char str[64];

	if (status) {
		sprintf(str, "Selected button %d", (int)ii);
		MObjectSetText(label, str);
	}
}

void MGUIMain(int argc, char **argv)
{
	int ii;
	char str[16];
	MOBJECT shell, rform, pb, rb;

	shell = MCreateShell("Example 6", 0);

	rform = MCreateRowForm(shell);
	for (ii=0; ii < 4; ii++) {
		sprintf(str, "Radio %d", ii);
		rb = MCreateRButton(rform, str, HELV_LARGE);
		MRButtonSetCallback(rb, RadioButtonCB, (void *)ii);
	}
	label = MCreateLabel(rform, "", DEFAULT_FONT);
	MRButtonActivate(rb);

	pb = MCreatePButton(shell, "Quit", TIMES_LARGE);
	MPButtonSetCallback(pb, ExitCB, NULL);

	MShellRealize(shell);

	MMainLoop();
}

The callback RadioButtonCB() sets the LABEL 'label' text showing 
which is the currently selected button. The Object index is supplied to 
the callback via the application data 'ii', set with the callback. Only 
when the Object status evolves from 0 to 1 the callback changes the 
LABEL text.
Notice that the initial selection made via the MRButtonActivate() call 
is realized after the LABEL 'label' creation. This is necessary because 
the LABEL is referenced in the callback code, so 'label' must be a valid 
Object identifier.


TBUTTON

The toggle-button is an on/off button, used to operate option selections. 
Unlike radio-buttons, toggle-buttons are never grouped, so they always 
have independent selection status. The default color is taken form the 
parent and the activation callback acts as for the radio-button Object.
Via MTButtonActivate() and MTButtonDeactivate() it's possible to set 
the button activation status.

/*
 * example 7
 */
#include <stdio>
#include <stdlib.h>
#include <string.h>
#include "mgui.h"

#define N_TOGGLE_BUTTON	4

MOBJECT label;
int toggle_status[N_TOGGLE_BUTTON];

void ToggleButtonCB(MOBJECT rb, MVALUE status, void *ii)
{
	char str[128];
	int i;

	toggle_status[(int)ii] = (int)status;
	strcpy(str, "Buttons status: ");
	for (i=0; i < N_TOGGLE_BUTTON; i++)
		strcat(str, (toggle_status[i] ? "  ON" : " OFF"));
	MObjectSetText(label, str);
}

void MGUIMain(int argc, char **argv)
{
	int ii;
	char str[64];
	MOBJECT shell, rform, pb, tb;

	shell = MCreateShell("Example 7", 0);

	rform = MCreateRowForm(shell);
	for (ii=0; ii < N_TOGGLE_BUTTON; ii++) {
		sprintf(str, "Toggle %d", ii);
		tb = MCreateTButton(rform, str, HELV_LARGE);
		MTButtonSetCallback(tb, ToggleButtonCB, (void *)ii);
	}
	label = MCreateLabel(rform, "", DEFAULT_FONT); 
	MTButtonActivate(tb);

	pb = MCreatePButton(shell, "Quit", TIMES_LARGE);
	MPButtonSetCallback(pb, ExitCB, NULL);

	MShellRealize(shell);

	MMainLoop();
}

The callback prototype is identical to that for the RBUTTON Object.
By activating the last created toggle-button (MTButtonActivate(tb)), 
the 'label' LABEL changes its text before the SHELL (the window) is 
realized.


EDIT

This Object allows the user to supply text input to the application, 
operating in single or multi line mode. In single line mode the text 
lenght is fixed at creation time, while in multi line mode it’s limited by 
the macro MAXINT.
The EDIT Object provides a filter facility and five callbacks to control 
input on it.
A callback can be set to intercept the ‘Enter’ key press; another one is 
called whenever the Object is about to lose the input focus; a callback 
is also available to intercept and optionally refuse user input; a callback 
can be set to receive notification by the EDIT whenever its content 
changes; the last callback notifies the cursor position changing.
The EDIT Object has a validation status which can be set explicitly or 
within a callback. It’s possible to show the Object in different colors, 
depending on the validation status.
Arrow keys move the cursor within the Object text, ‘Ins’ toggles the 
overwrite/insert mode.

/*
 * example 8
 */
#include <stdlib.h>
#include "mgui.h"

#define IsEven(a)	(!((a)&1))

int ValidateString(char *ps)
{
	int val, even, old;

	val = True;
	old = IsEven(*ps);
	if (*ps != '\0') {
		for (ps++; *ps && val; ps++, old=even)
			val = old ^ (even = IsEven(*ps));
	}
	return val;
}

void ValidateCB(MOBJECT edit, EDIT_VAL *ev, void *ii)
{
	ev->text_validated = ValidateString(ev->text);
	if (!ev->text_validated && !ev->focus_lost)
		ev->focus_leave = False;
}

void ValueChangeCB(MOBJECT edit, EDIT_CHANGE *ec, void *ii)
{
	char buff[128];

	if (!strcmp(ec->current_text, "1234567890")) {
		ec->exec = False;
		return;
	}
	strncpy(buff, ec->current_text, ec->pos);
	strcpy(buff + ec->pos, ec->change);
	strcat(buff, ec->current_text + ec->pos + ec->len);
	ec->exec = ValidateString(buff);
}

void NewValueCB(MOBJECT edit, char *new, void *label)
{
	MObjectSetText((MOBJECT )label, new);
}

void ActivateCB(MOBJECT edit, char *text, void *ed)
{
	static int notify;

	notify ^= 1;
	MEditSetTextExt((MOBJECT )ed, text, False, notify);
}

void NewPosCB(MOBJECT edit, EDIT_CURS *ec, void *label)
{
	char str[128];

	sprintf(str, "%-3d - %-3d", ec->pos_y, ec->pos_x);
	MObjectSetText((MOBJECT )label, str);
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, pb, edit, edit2, label;
	MTColor black, white, seagreen, cadetblue;

	black = MBlackColor();
	white = MWhiteColor();
	cadetblue = MAllocColor(0x60, 0x85, 0x95);
	seagreen = MAllocColor(0x70, 0xa5, 0xa0);

	shell = MCreateShell("Sample 8", 0);

	edit = MCreateEdit(shell, "", 20, DEFAULT_FONT); 
	MEditSetFilter(edit, DIGIT, "");
	MEditSetValCallback(edit, ValidateCB, NULL); 
	MEditSetValidatedColors(edit, cadetblue, white);
	MEditSetNotValidatedColors(edit, seagreen, black);

	edit = MCreateEdit(shell, "", 20, DEFAULT_FONT); 
	MEditSetFilter(edit, DIGIT, "");
	MEditSetValueChangeCallback(edit, ValueChangeCB, NULL);

	label = MCreateLabel(shell, "", DEFAULT_FONT);

	edit = MCreateClippedEdit(shell, "", 15, 20, 
DEFAULT_FONT);
	MEditSetNewValueCallback(edit, NewValueCB, label);

	edit2 = MCreateEdit(shell, "", 20, DEFAULT_FONT);
	MEditSetFilter(edit2, UPALPHA, "_");
	MEditSetActCallback(edit2, ActivateCB, edit);

	label = MCreateLabel(shell, "", DEFAULT_FONT);

	edit = MCreateClippedMLEdit(shell, "", 15, 4, 
DEFAULT_FONT);
	MEditSetCursorPosCallback(edit, NewPosCB, label);

	pb = MCreatePButton(shell, "Quit", TIMES_LARGE);
	MPButtonSetCallback(pb, ExitCB, NULL);

	MShellRealize(shell);

	MMainLoop();
}

Example 8 shows the use of the five callbacks available, each set in a 
different Object.
The function ValidateString() returns ‘True’ when the text string 
argument consists of alternating even and odd ASCII characters.
The ValidateCB() function is called when the first EDIT Object is 
about to lose input focus voluntarily (for example after pressing the 
‘Tab’ key) or by force (for example if another window is activated or 
another Object is selected via MSelectObject()).
The structure EDIT_VAL, passed as pointer in the second argument to 
the callback, is defined as follows :

typedef struct {
	char *text;		/* EDIT text */
	short len;		/* Text lenght */
	int key;		/* Key code originator (0 if not 
applicable) */
	unsigned focus_lost;    /* If True, EDIT will lose the 
focus anyway */
	unsigned focus_leave;   /* If set to False, EDIT will keep 
focus */
	unsigned text_changed;  /* If set to True EDIT redraws 
itself */
	unsigned text_validated; /* If set to True EDIT becomes 
validated */
} EDIT_VAL;

EDIT uses this structure to notify the callback about: the Object text 
(with the ‘text’field); current text lenght (with the ‘len’ field); the code 
of the key that caused the call (with the ‘key’field) which is equal to 
zero if the call occurs due to an event other than a key press; finally the 
‘focus_lost’ field notifies whether the application can request the 
Object to keep the input focus or not.
If the ‘focus_leave’ field is set by the callback to False (and 
‘focus_lost’ is False), then the EDIT Object retains the input focus.
Within the callback code the application can change the ‘text’ string 
and set the ‘text_changed’ field to True. In this case the Object will 
perform a redraw.
The 'focus_leave' field is preset to True by the Object before calling the 
callback. If the application wants the EDIT to keep the input focus, 
then the callback code should set to False this field.
It’s also possible to change the EDIT content from within this callback 
by directly writing the new value in the ‘text’ pointer and setting the 
‘text_changed’ field to True.This feature should only be used in single-
line EDIT Objects.
The ‘text_validated’ field determines the desired validation status for 
the Object.
The ValidateCB() in the above example uses the function 
ValidateString() to set the validation status. Moreover, if  this status is 
False and the input focus not lost, the callback queries the Object to 
keep the input focus.
The ValueChange callback is activated every time the content in the 
EDIT Object changes. This callback lets the application control the 
input in the EDIT and optionally inhibit a change when needed.

In the following is described the structure passed to the callback as the 
second parameter:

typedef struct {
	char *current_text;	/* EDIT text before the change */
	char *change;		/* New text to be inserted */
	int pos;			/* Change start position */
	int len;			/* Changing text lenght */
	unsigned validate;	/* Validation flag */
	unsigned exec;		/* Change accepted by the callback */
} EDIT_CHANGE;

The first field points to the EDIT value before the change occurs, while 
the second points to the new text to inserted (eventually a null string).
The ‘pos’ field specifies the insert position relative to the current text 
value. The new text, specified by ‘change’ will substitute ‘current_text’ 
starting from ‘pos’ and with lenght ‘len’ with the value in ‘change’.
The ‘validate’ field is preset to False, unless the callback is called due 
to the application call to MEditSetTextExt(), in which case it has the 
value specified as the third argument in that call.
This field can be changed within the callback to set the desired 
validation status.
If the last field is set to False, then the change is refused.
The callback function ValueChangeCB() in example 8 refuses a text 
modification when it has reached the value "1234567890". Otherwise 
the change is permitted only if the resulting new text causes a True 
return from the ValidateString() function.
	The NewValue callback is the simplest one that lets the 
application control the input. It receives as the Object data (second 
argument) directly the current text value, after the change occurs.
The NewValueCB() in the above example uses its current text to set 
the value in a LABEL Object, identified by the callback application 
data (the third argument).
Notice that the third EDIT in the example is created via a 
MCreateClippedEdit() function call. This function has an additional 
argument which lets the EDIT Object have an actual lenght greater 
than the visible one.
The Activate callback, called by the EDIT Object when the 
‘Enter’ key is pressed on it, receives as second argument the Object 
text. In the above example the ActivateCB() function is used to set its 
current text in the third EDIT Object. When the value of the ‘notify’ 
parameter passed to MEditSetTextExt() is a boolean True, the third 
Object will call its ValueChange callback.
	Functions MBlackColor() and MWhiteColor() respectively 
return the preallocated black and white color identifiers. The ‘seagreen’ 
and ‘cadetblue’ colors, allocated via the MAllocColor() function, 
should be freed with a call to MFreeColor() when no more needed. 
This is however not necessary since they are used for the entire 
program execution, and they are automatically freed by the 
MGUITerm() function call.
A filter is set to the first EDIT Object making it accepts digit characters 
only. The validation status for this Object is highlighted with different 
colors.
The filter set in the fourth EDIT lets it accept alphabetic characters, 
converted to upper case, and the additional character ‘_’.
Last EDIT is a clipped multi-line type. A scrolled version is also 
available via the MCreateScrolledMLEdit(), with the same arguments.
The CursorPosition callback is set to this Object in the example. It 
traces cursor position within the EDIT while it changes.
The structure notified by the EDIT to this callback (a pointer to it as 
the second parameter) is described in the follwing:

typedef struct {
	int pos;		/* position in the stream */
	int pos_x;		/* position x */
	int pos_y;		/* position y */
} EDIT_CURS;


SCROLLBAR

The SCROLLBAR Object is primarily present in the library to 
implement compound Objects as the scrolled FORM or LIST. It is here 
described because it's useful for creating new compound Objects. 
Available in horizontal and vertical appearance, it's resizable only in 
the scrolling direction and the size is imposed by the Objects it's 
attached to.
The SCROLLBAR has three long integer attributes: Max, N, Pos. Max 
and N are specified by the program at Object creation time, but they 
can be changed later via the API. The last is usually changed via the 
user scrolling action and can be changed via fuction call.


STATUSBAR

The purpose of this Object is to show the status of a job in progress. It 
has its own particular 3D look, so it’s easy to distinguish it from other 
Objects, and displays a text string which can be changed by the 
application during the job progress.
The use is very simple: just create the Object specifying the initial text, 
the ‘end job’ value and the text font. While the job progresses, you can 
increase the STATUSBAR’s value (from 0 to the ‘end job’ value) and 
optionally the text string.

/*
 * example 9
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mgui.h"

void AppTimerCB(TIMEOUT_ID id, void *data)
{
	static int perc;
	char str[128];
	MOBJECT sbar = (MOBJECT )data;

	perc += 10;
	sprintf(str, "%d %%", perc);
	MObjectSetText(sbar, str);
	MStatusBarSetPos(sbar, perc);
	if (perc < 100)
		MAddTimeout(500L, AppTimerCB, data);
	else
        
	MObjectSetSensitive((MOBJECT)MObjectGetUserData(sbar));
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, *pb, *statusbar;

	shell = MCreateShell("Sample 9", 0);

	statusbar = MCreateStatusBar(shell, "0 %", 100, 
DEFAULT_FONT);
	pb = MCreatePButton(shell, "Quit", TIMES_LARGE);
	MPButtonSetCallback(pb, ExitCB, NULL);
	MObjectSetUnsensitive(pb);
MObjectSetUserData(statusbar, pb);
	MAddTimeout(500L, AppTimerCB, statusbar);

	MShellRealize(shell);

	MMainLoop();
}

AppTimerCB() is a Timeout callback which receives, as the second 
parameter, the STATUSBAR’s id. It increments the object’s value by 
10 units, sets the text string to the percentage amount of work 
completed, and sets itself again. When the ‘end of job’ value is reached 
(100), the callback is no longer set and  the push button is set sensitive 
to allow the user close the window. The push button’s id is found as 
user data in the status bar Object.
The callback is set via the MAddTimeout() function specifying a delay 
of 500 msec and the STATUSBAR’s id as user data.
The MGUIMain() function simply creates a SHELL with a 
STATUSBAR and a PUSHBUTTON. The push button id is stored as 
user data in the statusbar object and it is initially set unsensitive. Once 
the AppTimerCB() callback is set the first time, the program evolves 
simulating a background work in progress for 5 seconds.

 
MENU

Two kind of menu are available in MGUI: Pulldown Menu and Popup 
Menu.
The Pulldown Menu consists of a ‘MENUBAR’, usually positioned on 
the top of the window, containing one or more ‘menu titles’. By 
selecting a menu title, a list of items (MENU) is open. You can 
navigate in a menu via the mouse or the arrow keys, as in Windows or 
Motif. An item can be linked to an application callback or it can open 
another MENU (named SubMenu).
The Popup Menu is Menu container that can be open anywhere on the 
screen. Is is primarily used to implement context-sensitive menus 
accessed via the right mouse button click.
The MenuBar is an ordinary Terminal Object, so it has no reserved 
position and you can create it as a child of whatever SHELL or FORM 
you want. However, the canonical position of a MenuBar is the top of a 
window, so you should create it as the first child of the SHELL.
The MenuBar is a special Terminal Object since it is the container for 
its Menu Objects.
Once the menu bar is created, you can attach to it the titles and the 
corresponding Menus. Items are created as children of a Menu with an 
optional callback function and accelerator key. The item can also be a 
container for a SubMenu and finally it can be made unsensitive or 
checked with automatic toggle and radio behaviour.

/*
 * esempio 10
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mgui.h"

void ExitCB(MENU_ITEM item, MVALUE status, void *b)
{
	MOBJECT obj;

	obj = MMenuItemGetMenu(item);
	obj = MObjectShell(obj);
	MShellUnrealize(obj);
	MShellDestroy(obj);
	MGUITerm();
	exit(0);
}

void ToggleItemCB(MENU_ITEM item, MVALUE status, void *udata)
{
	char str[128];

	sprintf(str, "The selected menu item\nhas been %s",
		(status ? "Activated" : "Deactivated"));
	MMessageDialog("", str, "Ok", NULL);
}

void RadioItemCB(MENU_ITEM item, MVALUE status, void *udata)
{
	char str[128];

	if (!status)
		return;
	sprintf(str, "Menu item <%lx> selected", item);
	MMessageDialog("", str, "Ok", NULL);
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT shell, menubar, menu, submenu;
	MENU_ITEM pd_item_id;

	shell = MCreateShell("Sample 10", 0);

	menubar = MCreateMenuBar(shell, HELV_L_FONT);
	MMenuBarSetAcc(menubar, F10);
	menu = MCreateMenu(menubar, "File");
	MMenuAddItem(menu, "Quit", ExitCB, NULL);
	menu = MCreateMenu(menubar, "Submenu");
	MMenuAddToggleItem(menu, "Item 1", ToggleItemCB, NULL);
	MMenuAddToggleItem(menu, "Item 2", ToggleItemCB, NULL);
	pd_item_id = MMenuAddItem(menu, "Item 3", NULL, NULL);
	MMenuAddToggleItem(menu, "Item 4", ToggleItemCB, NULL);

	submenu = MCreateSubMenu(pd_item_id);
	MMenuAddRadioItem(submenu, "RadioItem 3-1", RadioItemCB, NULL);
	MMenuAddRadioItem(submenu, "RadioItem 3-2", RadioItemCB, NULL);
	MMenuAddRadioItem(submenu, "RadioItem 3-3", RadioItemCB, NULL);
	MMenuAddSeparator(submenu);
	MMenuAddRadioItem(submenu, "RadioItem 3-4", RadioItemCB, NULL);
	MMenuAddRadioItem(submenu, "RadioItem 3-5", RadioItemCB, NULL);

	MShellRealize(shell);

	MMainLoop();
}

The ExitCB() callback, linked to the ‘Exit’ item in the ‘File’ menu, 
causes the program termination when selected.
The  ToggleItemCB() callback is called when the corresponding toggle-
item is selected. The item’s check status is reported as the second 
argument of the callback call.
The RadioItemCB() is bound to radio-items. It’s called for both the de-
activated and the activated item. The code in this example opens a 
message window only for the newly activated item.
The check status can be obtained via a call to function 
MMenuItemGetCheckStatus(), specifying the item identifier. Itcan be 
forced via two function calls : MMenuItemSetCheckStatus() and 
MMenuItemToggleCheckStatus(). Unlike the radio and toggle 
buttons, the change is not propagated to the callback.

 
DRAWAREA

MGUI library provides drawing primitives that can be used to draw on 
any kind of Object. Objects in the library such as push-button or edit, 
automatically redraw themselves when required by a window realize or 
re-expose. A special Object is provided to support user drawing : the 
DrawArea.
Input/Output callbacks for this Object are available to interact with 
user input (mouse/keyboard events) and expose/resize events.
The Expose callback notifies the application when a portion of  the 
Object’s area needs to be redrawn (every time an area becomes 
visible).
The Input callback is called by the Object when the user 
presses/releases a mouse button, when the mouse moves inside the 
DrawArea, and when a key is pressed and the Object owns the input 
focus.
The Resize callback notifies the application about a resize request due 
to a user resize in the window or an arrengement request from the 
container before the window is realized.
If the application sets the Input callback on a DrawArea, the mouse is 
automatically grabbed on a button press event so the Object can get 
mouse motion events even if they occur outside the Object’s area.
Coordinates for drawing and coordinates of mouse position are always 
relative to the DrawArea Object.

/*
 * example 11
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "mgui.h"

MTColor black, white, cadetblue, seagreen;

void DrawAreaExposeCB(MOBJECT obj, MEvent *pexp, void *a_data)
{
	int hsize, wsize;

	wsize = ObjectWidth(obj);
	hsize = ObjectHeight(obj);
	MDrawLine(obj, 0, 0, wsize, hsize, black);
	MDrawLine(obj, 0, hsize, wsize, 0, white); 
	MDrawFilledCircle(obj, wsize/2, hsize/2, wsize/4, 
cadetblue);
	MDrawCircle(obj, wsize/2, hsize/2, wsize/4, black);
}

void DrawAreaResizeCB(MOBJECT obj, DRAWAREA_PREF *pref, void 
*label)
{
	char str[128];

	sprintf(str, "Dimensions: %dx%d", pref->new_w, pref-
>new_h);
	MObjectSetText((MOBJECT )label, str);
}

void DrawAreaInputCB(MOBJECT obj, MEvent *pe, void *a_data)
{
	static int x1, y1, x2, y2;

	if (pe->type == E_BUTTON_PRESS) {
		x1 = x2 = pe->mouse.x;
		y1 = y2 = pe->mouse.y;
		MDrawXorLine(obj, x1, y1, x2, y2);
	}
	else if (pe->type == E_MOTION) {
		MDrawXorLine(obj, x1, y1, x2, y2);
		x2 = pe->mouse.x;
		y2 = pe->mouse.y;
		MDrawXorLine(obj, x1, y1, x2, y2);
	}
	else if (pe->type == E_BUTTON_RELEASE) {
		MDrawXorLine(obj, x1, y1, x2, y2);
		x2 = pe->mouse.x;
		y2 = pe->mouse.y;
		MDrawLine(obj, x1, y1, x2, y2, seagreen);
	}
}

void MGUIMain(int argc, char **argv)
{
	MOBJECT pbutton, *shell, *label, *drawa;

	black = MBlackColor();
	white = MWhiteColor();
	cadetblue = MAllocColor(0x60, 0x85, 0x95);
	seagreen = MAllocColor(0x70, 0xa5, 0xa0);

	shell = MCreateShell("Example 11", 0);

	label = MCreateLabel(shell, "", HELV_LARGE);

	drawa = MCreateDrawArea(shell, 256, 256);
	MObjectSetResize(drawa, True, True);
	MDrawAreaSetExposeCallback(drawa, DrawAreaExposeCB, NULL);
	MDrawAreaSetInputCallback(drawa, DrawAreaInputCB, NULL);
	MDrawAreaSetResizeCallback(drawa, DrawAreaResizeCB, 
label);

	pbutton = MCreatePButton(shell, "Quit", TIMES_LARGE);
	MPButtonSetCallback(pbutton, ExitCB, NULL);

	MShellRealize(shell);
	MMainLoop();
}

The DrawAreaExposeCB() callback receives, as second argument, a 
pointer to an Event of type Expose which specifies the portion of the 
Object’s area to be redrawn. Before calling this callback the Object 
automatically sets a clipping area to prevent drawing outside that area. 
So, the application can decide whether to use or not the received area 
coordinates to make only useful redrawings. In either cases the output 
will occurr only in the clipped area.
For simplicity the callback in the example simply redraws the entire 
Object with no care of the actual clipping area to be redrawn.
The DrawAreaInputCB() callback handles mouse input events to draw 
lines.
Notice that drawing in this Object is NOT REMEMBERED. The 
application must take care of storing in some way its drawings to be 
able to redraw them in the Expose callback and/or to save them on 
disk. 


... Something still missing here ...


TRACING LOG MESSAGES

The library provides utility functions to help in tracing the program 
execution, giving the opportunity to write messages in a file and/or to 
show them at any time in a window, activated by the key sequence  
<Shift-Ctrl-F1>.
The optional filename must be specified in the call to MLogInit(), 
normally in the MGUIMain() function. MLogInit() sets also the initial 
tracing level, the log window geometry and the number of messages to 
hold in the buffer.
A log message is generated by the program via the call to Mlog(). The 
first argument specifies the message’s level, the remaining are a format 
string and the value list, with printf() syntax.
The call to Mlog() will take effect only if the current tracing level is 
equal to or higher than the value of the first argument.
For example, the call:

MLog(3, "this is line: %d", __LINE__);

will generate a log message only if the current tracing level is equal to 
or higher than 3.
The tracing level can be changed at any time by the program code via a 
call to the function MLogSetLevel(). The user can also change this 
level run-time, activating the control dialog with the key sequence 
<Shift-Ctrl-F2>.
A more sophisticated filtering can be obtained by using the function 
MMaskedLog().
The message will take effect if the current tracing level is equal to or 
higher than the level in the call and the current bit-mask has at least 
one bit in common with the mask specified in the call.

