
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:

- 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 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.

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

- Borland C 4.5 with BGI graphics library.
- Borland C 4.5 with Csaba Biegl LIBGRX 1.03 graphics library.
- DJGPP  with Csaba Biegl LIBGRX 1.03 graphics library.
- Borland C 4.5 under Windows 3.1 and Win32
- Visual C 4.0 under Windows 95
- SUN Solaris 2.4 (x86 & sparc)
- IBM AIX 3.2.5 Risc 6000
- Linux Slackware 3.0

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.
Tanks to my friends Paolo (first MGUI user) and Sergio for their 
contribution in the testing and for their suggestions.


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

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 eight preallocated fonts in any platform, referenced 
in the source code as macros. There are four fixed picth fonts: 
DEFAULT_FONT, SMALL_FONT, MEDIUM_FONT and LARGE_FONT. The others are 
proportional  Times ed Helvetica: TIMES_L_FONT, TIMES_N_FONT, HELV_L_FONT 
and HELV_N_FONT.
Except for the DOS BGI platform, 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 reccomended 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.
 

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 '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 them 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 Objects
- SHELL window realize
- Wait for user interaction in a library loop 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_L_FONT);
	pushbutton = MCreatePButton(shell, "Ok", TIMES_L_FONT);
	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_L_FONT);
	pushbutton = MCreatePButton(shell, "Ok", TIMES_L_FONT);
	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.
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.
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_L_FONT); 
	pb = MCreatePButton(cform, "Quit", TIMES_L_FONT);
	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_L_FONT); 
	pb = MCreatePButton(cform, "Quit", TIMES_L_FONT);
	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_L_FONT);
		MRButtonSetCallback(rb, RadioButtonCB, (void *)ii);
	}
	label = MCreateLabel(rform, "", DEFAULT_FONT);
	MRButtonActivate(rb);

	pb = MCreatePButton(shell, "Quit", TIMES_L_FONT);
	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_L_FONT);
		MTButtonSetCallback(tb, ToggleButtonCB, (void *)ii);
	}
	label = MCreateLabel(rform, "", DEFAULT_FONT); 
	MTButtonActivate(tb);

	pb = MCreatePButton(shell, "Quit", TIMES_L_FONT);
	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_L_FONT);
	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;


... 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 yo 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.

