DEGUI
Version 1.21
designed for Allegro 2.2  and DJGPP

C++ objects for Allegro's GUI

By Doug Eleveld, November 1997
D.J.Eleveld@anest.azg.nl

#include <std.disclaimer.h>

"I do not accept responsibility for any effects, adverse or
otherwise, that this code may have on you, your computer, your
sanity, your dog, and anything else that you can think of.  Use it
at your own risk."


Table of Contents

1-   What is DEGUI
2-   Copyright
3-   Using DEGUI
4-   DEGUI Class Structure
5 -  How DEGUI Dialog Objects Work
6-   DEGUI Objects
               dialog_object
               proc_object
               clear_screen_object
               box_object
               panel_raised_object
               panel_sunken_object
               panel_ridge_object
               panel_groove_object
               shadow_box_object
               bitmap_object
               text_object
               centerd_text_object
               button_object
               checkbox_object
               radio_button_object
               keyboard_object
               edittext_object
               list_object
               hidden_list_object
               textbox_object
               progress_bar_object
               menu
               menu_object
               dialog
               window_object
               color_scheme
7-   Related Objects
               INIfile
8-   DEGUI Functions
               degui_alert
               degui_alert3
               degui_file_select
               degui_gfx_mode_select
9 -  Allegro DIALOG procedures
               Replacements for Allegro dialog procedures
                    d_degui_clear_proc
                    d_degui_box_proc
                    d_degui_shadow_box_proc
                    d_degui_text_proc
                    d_degui_ctext_proc
                    d_degui_button_proc
                    d_degui_checkbox_proc
                    d_degui_radio_button_proc
                    d_degui_edit_proc
                    d_degui_list_proc
                    d_degui_menu_proc
               New Allegro dialog object procedures
                    d_degui_progress_bar_proc
                    d_degui_panel_raised_proc
                    d_degui_panel_sunken_proc
                    d_degui_panel_ridge_proc
                    d_degui_panel_groove_proc
10-  Dialog with DEGUI - C only
11-  Dialogs with DEGUI - C++ style
12-  Dialogs with DEGUI - Mixed C/C++ style
               Method 1
               Method 2
13-  Changing DEGUI Object Behavior
               Callback functions
               Derived objects
14-  Bugs
15-  Change log
16-  Future Work
17-  In Conclusion
18-  Thanks

 
What is DEGUI

DEGUI is a set of programs, C++ objects and C routines that allow
users of Allegro's gui to make dialogs using better looking gui
objects and extend the objects using more familiar normal C++
methods.  With the addition of a few lines C programs that use
Allegro's gui can have better looking objects with pseudo 3d
effects and multiple colors.  If you are using C++ you can extend
the basic dialog_object with class derivation which is simpler and
cleaner than the mixing of procedures that was necessary using
standard Allegro d_xxx_proc procedures.  Also the user can easily
and simply interface with object callback functions to find out
when a button is clicked, a checkbox is selected, etc.  A gui
builder program is provided that can be used to graphically build
dialogs and export the dialogs to program code.   DEGUI objects can
also be easily interfaced with DIALOG structures for mixed C/C++
style dialogs and pure C programs can us the new look of the DEGUI
objects with minimal changes in their code.  DEGUI also has an
INIfile class for support for windows-like INI files.

DEGUI is not very useful without Allegro since it relies on many of
Allegro's procedures.  The latest version of Allegro can always be
found on ftp://x2ftp.oulu.fi, in  /pub/msdos/programmer/djgpp2/,
and on the Allegro homepage, 
http://www.talula.demon.co.uk/allegro/.

The latest version of DEGUI is always available at:
http://huizen.dds.nl/~deleveld/index.htm  It might or might not
also be available at ftp://x2ftp.oulu.fi, in 
/pub/msdos/programmer/djgpp2/


Copyright

I think that the way Allegro is copyrighted is fantastic and so I'm
going to copyright DEGUI in the same way.  This is from Allegros'
readme.txt with the name Allegro exchanged with DEGUI.

DEGUI is swap-ware. You may use, modify, redistribute, and
generally hack it about in any way you like, but if you do you must
send me something in exchange. This could be a complimentary copy
of a game, an addition or improvement to DEGUI, a bug report, some
money (this is particularly encouraged if you use DEGUI in a
commercial product), or just a copy of your autoexec.bat if you
don't have anything better. If you redistribute parts of DEGUI or
make a game using it, it would be nice if you mentioned me
somewhere in the credits, but if you just want to pinch a few
routines that is OK too. I'll trust you not to rip me off.

Of course you must also read Allegro's copyright and for DJGPP too,
I guess.


Using DEGUI

Since DEGUI is only useful with Allegro and DJGPP, and I will
assume that you know something about compiling, linking, object
files, library files etc.   If you don't understand these terms,
you are pretty much going to have to learn them before using DEGUI,
or DJGPP for that matter.

The best way to use DEGUI is to include degui.h in you header files
and use libdegui.a as a library and use it during your linking
process.  Make sure that you link the DEGUI library before
Allegro's library or you will get unresolved externals.


DEGUI Class Structure

This is the basic DEGUI class structure.  There are actually more
classes, but these are the most useful ones.

dialog_object --+- clear_screen_object
                |- panel_object --|- box_object
                |                 |- shadow_box_object
                |                 |- panel_raised_object
                |                 |- panel_sunken_object
                |                 |- panel_ridge_object
                |                 \- panel_groove_object
                |- bitmap_object
                |- text_object --+- centerd_text_object
                |                \- button_object --+-
checkbox_object     
                |                                   \-
radio_button_object 
                |- edittext_object      
                |- keyboard_object           
                |- list_object --+- textbox_object
                |                \- hidden_list_object 
                |- proc_object                    
                |- progress_bar_object
                \- menu_object          
menu

color_scheme

dialog_do ---- dialog ---- window_object

INIfile


How DEGUI Dialog Objects Work

DEGUI works by replacing the d_xxx_proc in Allegro's dialog with a
common message handling procedure.  This procedure receives
Allegro's messages and calls the msg_xxx functions of
dialog_object.  These functions are virtual and can be declared in
derived classes to determine an objects behavior.

When using DEGUI in a pure C program, there are objects such as
d_degui_box_proc procedure which uses Allegro's d_box_proc for most
message handling, but catches the MSG_DRAW and draws itself in a
nice pseudo 3D style.  In degui.h there is a #define d_box_proc
d_degui_box_proc, so you don't even have to explicitly call the
degui procedures.  These procedures and defines are repeated for
all the standard Allegro gui objects, even alert and
select_gfx_mode.

Class proc_object is a helper class that interfaces a d_xxx_proc
procedure to a dialog_object.  Once the proc_object is setup with
correct d_xxx_proc and *dp information, it will accept all the
msg_xxx virtual function calls and send the correct message to the
d_xxx_proc procedure.  This allows object to be derived from object
that are derived from a proc_object to essentially extend the
behavior of a d_xxx_proc procedure through normal C++ inheritance.

If you are using DEGUI for something and would like help or advice
you can always email me and I'll try to help.  I'll also try to
have your question explained in the next version's documentation


DEGUI Objects

dialog_object
The dialog object is the important base class in DEGUI.  All object
are manipulated through pointers to a dialog_object so if you
really want to understand everything you are going to have to look
at the class declaration in degui.h.  All messages send to objects
are through virtual functions so you can overload them in a derived
class to extend the functionality of the objects.

Here are the message functions.  They are all virtual so you can
derive a class from this one and receive all these messages.  They
are not pure virtual functions so you don't have to use each
message if you don't want to.

virtual void msg_start (void);
virtual void msg_end (void);
virtual void msg_draw (void);
virtual void msg_click (void);
virtual void msg_dclick (void);
virtual void msg_key (void);
virtual bool msg_char (const int);
virtual bool msg_xchar (const int);
virtual bool msg_wantfocus (void);
virtual void msg_gotfocus (void);
virtual void msg_lostfocus (void);
virtual void msg_gotmouse (void);
virtual void msg_lostmouse (void);
virtual void msg_idle (void);
virtual void msg_radio (const int);
virtual void msg_unknown (const int);

Most of the message functions return void so you don't have to
worry about returning anything.  However some of them return bool
and you must make sure that you give an appropriate return value. 
For the msg_char and msg_xchar you must return true if you used the
keypress, false otherwise.  Of course you must return true to the
msg_wantfocus if you want you object to receive the focus.

The passed arguments for msg_char and msg_xchar is the Allegro
keypress number.  For msg_radio, it is the button group.

It is important to note that calling these function directly
bypasses any message callbacks for the object.

It is also possible to send a message through the Allegro
interacting procedure.  You can do that with the following
function:

int send_message (const int, const int = 0);

For example to ask an object to redraw itself you can do this:

send_message(MSG_DRAW,0);

Calling the send_message function like this will correctly observe
any object callbacks.

Here are the function that are used to set the message callbacks. 
These function allow you to set function to be called whenever an
object receives specific messages.

void set_all_callback       (void(*callback)(void)) { all_callback
= callback; };
void set_start_callback     (int(*callback)(void))  {
start_callback = callback; };
void set_end_callback       (int(*callback)(void))  { end_callback
= callback; };
void set_draw_callback      (int(*callback)(void))  { draw_callback
= callback; };
void set_click_callback     (int(*callback)(void))  {
click_callback = callback; };
void set_dclick_callback    (int(*callback)(void))  {
dclick_callback = callback; };
void set_key_callback       (int(*callback)(void))  { key_callback
= callback; };
void set_char_callback      (int(*callback)(int))   { char_callback
= callback; };
void set_xchar_callback     (int(*callback)(int))   {
xchar_callback = callback; };
void set_wantfocus_callback (int(*callback)(void))  {
wantfocus_callback = callback; };
void set_gotfocus_callback  (int(*callback)(void))  {
gotfocus_callback = callback; };
void set_lostfocus_callback (int(*callback)(void))  {
lostfocus_callback = callback; };
void set_gotmouse_callback  (int(*callback)(void))  {
gotmouse_callback = callback; };
void set_lostmouse_callback (int(*callback)(void))  {
lostmouse_callback = callback; };
void set_idle_callback      (int(*callback)(void))  { idle_callback
= callback; };
void set_radio_callback     (int(*callback)(int))   {
radio_callback = callback; };
void set_unknown_callback   (int(*callback)(int))   {
unknown_callback = callback; };

Inside a callback, you may also want the object to perform it's
default behavior for that message.  For example, say you set a
message callback for a button to catch the msg_click message.  If
you just execute the callback, the button won't change state (i.e.
from selected to unselected).  See EX2.CC for an example of how to
use the callback functions.  You can ask the button to select or
deselect itself inside a msg_click callback by calling the
dialog_object static function:

static void default_behavior (void);

You would call the function like this:

dialog_object::default_behavior();

This function can perform the default behavior for all the types of
message callbacks.  If you want to remove a callback from an
object, set the callback to NULL.

There are some simple function to ask about the status of the
object:

Information about size and positioning:
int x  (void) const { return d->x; };
int y  (void) const { return d->y; };
int w  (void) const { return d->w; };
int h  (void) const { return d->h; };
int x2 (void) const { return d->x + d->w; };
int y2 (void) const { return d->y + d->h; };

Information about color NOTE: These fields are unused for
dialog_objects
int fg (void) const { return d->fg; };
int bg (void) const { return d->bg; };

Information about shortcut keys
int key (void) const { return d->key; };

Information about Allegro style flags NOTE: there are better ways
to get the flag info
int flags (void) const { return d->flags; };

Information about the state of the object
bool has_focus (void) const { if(d->flags&D_GOTFOCUS) return true;
return false; }
bool has_mouse (void) const { if(d->flags&D_GOTMOUSE) return true;
return false; }
bool hidden    (void) const { if(d->flags&D_HIDDEN  ) return true;
return false; }
bool will_exit (void) const { if(d->flags&D_EXIT    ) return true;
return false; }
bool selected  (void) const { if(d->flags&D_SELECTED) return true;
return false; }
bool disabled  (void) const { if(d->flags&D_DISABLED) return true;
return false; }

Information about Allegro style d1 and d2 fields NOTE: these are
not used by dialog_objects
int d1 (void) const { return d->d1; };
int d2 (void) const { return d->d2; };

You can also find out the dialog in which the object is running. 
With this pointer you can ask the parent dialog to redraw of close
or whatever you want.  When you have dialog inside of dialogs, this
will point to the main dialog.  The main dialog is defined as the
dialog to which sends the object it's messages.
dialog* parent (void) const { return _parent; };

A dialog_object can also modify itself.  These functions are
virtual so that they can be overridden in derived classes.  Here
they are, and I won't explain each of the in detail:
virtual void set_x     (const int i) { d->x = i; _redraw = true; };
virtual void set_y     (const int i) { d->y = i; _redraw = true; };
virtual void set_w     (const int i) { d->w = i; _redraw = true; };
virtual void set_h     (const int i) { d->h = i; _redraw = true; };
virtual void place  (const int nx, const int ny) { set_x(nx);
set_y(ny); };
virtual void resize (const int nw, const int nh) { set_w(nw);
set_h(nh); };
virtual void shape  (const int nx, const int ny, const int nw,
const int nh) { set_x(nx); set_y(ny); set_w(nw); set_h(nh); };
virtual void set_fg    (const int i) { d->fg = i; _redraw = true;
};
virtual void set_bg    (const int i) { d->bg = i; _redraw = true;
};
virtual void set_key   (const int i) { d->key = i; _redraw = true;
};
virtual void select    (void) { d->flags |= D_SELECTED; _redraw =
true; };
virtual void deselect  (void) { d->flags &= ~D_SELECTED; _redraw =
true; };
virtual void set_flag  (const int i) { d->flags |= i; _redraw =
true;};
virtual void clear_flag(const int i) { d->flags &= ~i; _redraw =
true; };
virtual void enable    (void) { d->flags &= ~D_DISABLED; _redraw =
true; };
virtual void disable   (void) { d->flags |= D_DISABLED; _redraw =
true; };
virtual void hide      (void) { d->flags |= D_HIDDEN; };
virtual void unhide    (void) { d->flags &= ~D_HIDDEN; _redraw =
true; };
virtual void make_exit (void) { d->flags |= D_EXIT; _redraw = true;
};
virtual void dont_exit (void) { d->flags &= ~D_EXIT; _redraw =
true; };
virtual void set_d1    (const int i) { d->d1 = i; _redraw = true;
};
virtual void set_d2    (const int i) { d->d2 = i; _redraw = true;
};

This is a special function that causes the dialog_object to be
redrawn after the next message.  It is useful so that you can very
quickly mark an object for redraw, and it will get redrawn later,
without wasting time now.  This means that if an object is marked
for redraw multiple times between messages, it will only be redrawn
once, at the next message.  This saves time and makes a quicker,
more efficient gui.  The function is:
virtual void redraw    (void) { _redraw = true; };

Each dialog_object has a pointer to a color_scheme object.  If the
pointer is NULL, the default colors will be used.  The pointer is
defined as:
color_scheme *color;

You use the create_DIALOG function to interface the dialog_objects
with Allegro C gui routines.  See EX1.CC for an example.  the
function is:
DIALOG create_DIALOG (const int = -1, const int = -1, const int =
-1, const int = -1, const int = -1, const int = -1);

Each dialog_object has a pointer that you can use to connect each
object to some data.  the builder program uses this pointer to
connect an object description to each in the builder.  The pointer
is defined as:
void *dataptr;

void connect (DIALOG*);
void disconnect (void);
These function associate the object with a particular DIALOG.  This
is necessary so that the objects can be properly interfaced with
Allegro's do_dialog function inside the dialog class.  You
shouldn't need to use these functions.  

proc_object
The proc_object is a helper object for interfacing Allegro dialog
procedures to the base dialog_object's virtual message functions. 
Originally I had used it to simplify the interfacing of the C++
classes to the d_xxx_proc procedures, but now all the objects are
self-contained and don't need interfacing any more.  You can still
use the proc_object for interfacing your own d_xxx_proc procedures
to DEGUI.  Look at the source for the proc_object to see how you
could interface your own Allegro dialog procedures to a
dialog_object.  If you have any objects or d_xxx_proc procedures
that may be useful, send them to me and I'll add them to DEGUI.  

clear_screen_object
This object works just like the Allegro procedure d_clear_proc.  It
just clears the screen when it is drawn.

Constructor:
clear_screen_object::clear_screen_object (void)

Example:  
clear_screen_object clear;

box_object
shadow_box_object
This object works similarly to the Allegro procedures d_box_proc
and d_shadow_box_proc.  They draw boxes on the screen, with or
without a shadow.  The boxes are drawn with shading and have a
pseudo 3D effect.

Constructor:
box_object::box_object (void)
shadow_box_object::shadow_box_object (void)

Example:
box_object box;               // Draw a box
shadow_box_object shadow_box; // Draw a box with a shadow

panel_raised_object
panel_sunken_object
panel_ridge_object
panel_groove_object
These objects draw panels that are raised or sunken or grooved etc.


Constructor:
panel_raised_object::panel_raised_object (void)
panel_sunken_object::panel_sunken_object (void)
panel_ridge_object::panel_ridge_object (void)
panel_groove_object::panel_groove_object (void)

Example:
panel_raised_object box1;      // Draw a raised panel
panel_sunken_object box2;      // Draw a sunken panel
panel_ridge_object  box3;      // Draw a panel with a ridge around
it
panel_raised_object box4;      // Draw a panel with a groove around
it

bitmap_object
This object works just like the Allegro procedure d_bitmap_proc. 
It draws a bitmap on the screen.  You can define the bitmap using
a BITMAP* of load the bitmap from a file.

Constructor:
bitmap_object::bitmap_object (BITMAP* bit)
bitmap_object::bitmap_object (const char *filename)

Example:  
bitmap_object bitmap1(bit);          // bit must be a BITMAP* and
point to a valid bitmap
bitmap_object bitmap2("about.pcx");  // This will load a bitmap
from the file 'about.pcx'

text_object
This object works just like the Allegro procedure d_text_proc.  It
draws some text on the screen.  You can define it with some text or
with a pointer.  You can also change the text by using the member
function set_text.

Constructor:
text_object::text_object (const char *text)

Example:
text_object text1("Hello");   // Draw 'Hello' on the screen
char *text_ptr = "Pointer";
text_object text2(text_ptr);  // Draw 'Pointer' on the screen
text1.set_text("ouch");       // text1 will now print out 'ouch'

centerd_text_object
This object works just like the Allegro procedure d_ctext_proc.  It
is almost the same as the text_object except that the text is drawn
centered around the x,y values.  You can also change the text by
using the member function set_text.

Constructor:
centerd_text_object::centerd_text_object (const char *text)

Example:
centerd_text_object text1("Hello");   // Draw 'Hello' on the screen
char *text_ptr = "Pointer";
centerd_text_object text2(text_ptr);  // Draw 'Pointer' on the
screen
text1.set_text("ouch");               // text1 will now print out
'ouch'

button_object
This object works just like the Allegro procedure d_button_proc. 
It draws a button on the screen that can be clicked and selected. 
You can define a shortcut key for the button and say if the dialog
should be closed when the button is clicked.  You can also change
the text by using the member function set_text.  The bool in the
constructor defined if the parent dialog should be closed when this
button is clicked.  This should be true for an "exit" or "close"
button.

Constructor:
button_object::button_object (const char *text, const int shortcut,
const bool close)

Example:
button_object button1("Button1",0,false);  // Button text
'Button1', will not close dialog
button_object button2("Close",'c',true);   // Button text 'Close',
closes dialog when selected
button1.set_text("ouch");                  // button1 will now
print out 'ouch' instead of 'Button1'

checkbox_object
This object works just like the Allegro procedure d_check_proc.  It
draws a checkbox than can be selected and unselected.  You can also
change the text by using the member function set_text.  In contrast
to Allegros's d_check_proc the checkbox ins on the left side of the
text.

Constructor:
checkbox_object::checkbox_object (const char *text)

Example:
checkbox_object check1("Check1");
check1.set_text("ouch"); // Checkbox text changed to 'ouch'

radio_button_object
This object works just like the Allegro procedure d_radio_proc.  It
draws a radio button that can be selected and unselected.  Only one
radio button of each group can be selected at one time.  Selecting
a radio button automatically unselects other radio buttons
belonging to the same group.  You can also change the text by using
the member function set_text.  You can choose between square and
round radio buttons.

Constructor:
radio_button_object::radio_button_object (const char *text, const
int group, const bool square)

Example:
radio_button_object radio1("radio1",0,false); // Round radio button
text 'radio1' from group 0
radio_button_object radio2("radio2",0,true); // Square radio button
from group 0
radio1.set_text("ouch"); // Radio button text changed to 'ouch'

keyboard_object
This object works just like the Allegro procedure d_keyboard_proc. 
It calls a function when the key is pressed.  This object is
invisible.

Constructor:
keyboard_object::keyboard_object (int(*procedure)(void), const int
key)

Example:
keyboard_object key(helper,KEY_F1); // Procedure 'helper' called
when F1 is pressed

edittext_object
This object works something like the Allegro procedure d_edit_proc,
however it keeps it's own buffer for the editable text.  You must
use the get_text function to retrieve the edited text.   The text
will scroll left and right so you can fit alot of text in a small
space.  If this object has the focus (i.e. it accepts the
keypresses), then the text containing the cursor is shown, if not
the text is printed from the beginning.

Constructor:
edittext_object::edittext_object (char *editable, const int
max_length)

Example:
char sample_text[100];
strcpy(sample_text,"ouch");
edittext_object editor(sample_text,99); // Edit sample_text to 99
places

list_object
hidden_list_object
This object is works like the Allegro procedure d_list_proc.  It
displays a list of choices on the screen possibly with a vertical
scrollbar.  The hidden_list_object displays only one line of the
list, but when it is clicked, it displays the entire list.  For
both of these objects the list procedure follows the same form as
with Allegro's d_list_proc: char *foobar (int index, int
*list_size);

Constructor:
list_object::list_object(char*(*pro)(int,int*))
hidden_list_object::hidden_list_object(char*(*pro)(int,int*), const
int n)

Example:
list_object list1(foobar); // Draw a listbox using the list
procedure 'foobar'
hidden_list_object list2(foobar,5); // Draw a list using procedure
'foobar' that displays a list of 5 elements when clicked

textbox_object
This object is not based on any Allegro procedure.  This object
displays multiple lines of text, possibly with a scrollbar.  There
can be character wrapping or word wrapping or no wrapping at all.

Constructor:
textbox_object::textbox_object(char *text, const bool wrap, const
bool wordwrap)

Example:
textbox_object textbox(text,true,true);  // Draw char* text in a
window with word wrapping

You can change the text using the set_text function, and the
wrapping parameters with set_wrap.

grid_object
This object draws is not based on any Allegro procedure.  This
object displays a grid on the screen with the specified x and y
spacings.  You can change the grid spacing with the set_granularity
function.  It's used in the builder as the background.

Constructor:
grid_object::grid_object(const int, const int)
Example:
grid_object grid(5,5);  // Grid with dots every 5 pixels

progress_bar_object
This object draws a progress bar.  You set the degree of progress
with the progress() member function and the progress bar is drawn
as a percentage of the objects scale which is set with the scale()
member function.  The default scale is 100.0 and the default
progress is 0.0.

Constructor:
progress_bar_object(const bool, const float)

Example:
progress_bar_object prog(true,1000.0);  // A progress bar with
1000.0 as full scale value and display the number
prog.progress(100.0);  // Set the progress to 100.0 or 10% of full
scale

menu
This class is a wrapper for Allegro's MENU struct.  You can define
sub menus or procedures.  These classes are only useful to be added
to a menu_object.

Constructor:
menu::menu (const char *menu_text, int(*proc)(void) )
menu::menu (const char *menu_text, const menu &submenu)

Example:
menu menu1("Choice1",helper); // Call function 'helper' when
'Choice1' is clicked
menu menu2("Main",menu1); // Make menu1 a submenu of menu2

menu_object
This object is based on the Allegro procedure d_menu_proc.  You
define some (class) menu and then add them to this menu_object
which can be added to a dialog.  The colors for the menu come from
the default colors and not anywhere else.

Constructor:
menu_object::menu_object (const menu &men)

Example:
menu_object main_menu(menu2); // Put menu2 into an object to be
added to the dialog

dialog
This class is a wrapper for Allegro's DIALOG definitions and
do_dialog function.  After you have defined some objects they can
be added to the dialog with the add member function.  In the
version 1.1, the objects are dynamically allocated, but on this
version you must tell how many objects you plan to have in each
dialog.  The default number is 50.

 Here is a short explanation of the public member functions.

void add (dialog_object&, const int x = 0, const int y = 0, const
int w = 0, const int h = 0, const int fg = gui_fg_color, const int
bg = gui_bg_color);
This function adds a C++ object  to a dialog.  Objects can even be
added to dialogs while they are still executing.

void add (dialog&);
This function adds a DEGUI dialog  to a dialog.  Objects can even
be added to dialogs while they are still executing.  All the
objects in the sub-dialog will be added to the main dialog.

void add (int(*proc)(int,DIALOG*,int), const int = 0,  const int =
0, const int = 0, const int = 0, const int = gui_fg_color, const
int = gui_bg_color, const int = 0, const int = 0, const int = 0,
const int = 0, void* = NULL );
This function adds a normal Allegro dialog procedure to a dialog. 
These procedures can even be added to dialogs while they are still
executing.

void remove (dialog_object&);
void remove (dialog&);
These functions remove objects from a dialog.  Objects can even be
removed from dialogs while they are still executing.  See EX7.CC
for an example of adding and removing sub-dialogs from dialogs.

void redraw (const bool immed = false);
This function causes a compete redraw of all the object in the
dialog.  If the argument is true then the redraw is immediate, if
not then the dialog will redraw everything during the next idle
period.

void close (void);
Force a dialog to close at the next idle period.

void use_palette (PALETTE);
You can change palettes while running a dialog.  All objects colors
will be set to the closest color in the new palette.  Objects using
the default colors will not change much in how they look because
the default colors are recalculated to be the closest colors in the
new palette to the old ones.

void center (const int begin = 0);
This function moves all the objects in a dialog so that the objects
are in the center of the screen and any relative distances within
the dialog are preserved.  You can change the begin value to only
center a portion of the objects in the dialog.  Default is to
center all the objects.

void scale_to_font (FONT *);
This function scales the dialog for use with another font.  Most
fonts are designed using the 8x8 font and if the font is changed,
then the dialog will look all scrunched up.  If the dialog is
scaled to the new font then everything will remain readable,
although possibly a bit too spread out.  If you figure out a better
way to do this, tell me.

dialog_object* popup (const dialog_object* focus_object = NULL);
dialog_object* execute (const dialog_object* focus_object = NULL);
These functions actually execute the dialog.  The popup function
first saves any graphics underneath itself and replaces the
background when it exits.  The execute function just executes the
dialog and the graphics from the dialog may still be on screen when
the dialog is exited.  The focus object is the object that is
initially given the input focus.  These function return a pointer
to the dialog object that closed the dialog.

window_object
This object displays a window with some title text.  The object
contains other objects and works just like a dialog.  You can add
dialog_objects and dialogs to a window_object.  Clicking on the
title area of the window allows you to change the window placement. 
See EX11.CC for an example of how to use a window_object.  Right
now there is no special handling for buffering or active windows. 
In the future I want to make these objects with proper buffering. 
I'm not sure exactly how I can efficiently do that, so if you know,
send me an email.

Constructor:
window_object::window_object (const char *text = NULL, const int n
= 50)

Example:  
window_object win("win",50);   // Construct window with title "win"
that can carry 50 sub-elements

color_scheme
This object is used by a dialog_object to set it's colors. 
Creating a color_scheme will set it to the default values.  Once
you have created a color scheme, you can force a particular object
to use those colors like this:

color_scheme new_colors; // Create new color scheme
new_colors.set_fore(255);     // Set the foreground color to 255

text_object test("This should be in non-default color");
test.color = &new_colors;

Now test will have a foreground color of 255.  Setting the
dialog_object member color to NULL forces the object to use the
default colors;

test.color = NULL;       // Make test use the default colors

Constructor:
color_scheme::color_scheme(void)

Example:
color_scheme new_colors;  // Create a new color scheme
new_color.set_fore(255);  // Set the foreground color to 255


Related Objects

INIfile
This class allows you to use windows style INI files with your
application.  It has no special connection to DEGUI so you can use
the INIfile class without having to worry about anything from the
rest of DEGUI.  If you use the LIBDEGUI.A library file for linking
and only use the INIfile class then the rest of DEGUI won't be
added to your program code if you don't use DEGUI stuff.  You'll
have to include have an #include "inifile.h" in your code to use
the INIfile class.

You create an INIfile like this:

INIfile ini;

And then you open the file like this:

bool worked = ini.open("filename.ini");

If the file FILENAME.INI doesn't exist you can use the create
function:

bool worked = ini.create("filename.ini");

If the file doesn't exist, an new empty INI file will be created. 
You can read the stuff from the INI file with the following
functions.

char*  get(char *section, char *param, char *c);
int    get(char *section, char *param, int i);
double get(char *section, char *param, double d);
bool   get(char *section, char *param, bool b);

The char* section and char* param define the names of the section
and the parameter name in the INI file.  The third argument is the
default response.  If for some reason the correct parameter cannot
be read from the INIfile then you will be returned your default
value.

These functions will be the most used from the INIfile class so
their functionality is duplicated using operator() with the
following functions:

char*  operator()(char *section, char *param, char *c);
int    operator()(char *section, char *param, int i);
double operator()(char *section, char *param, double d);
bool   operator()(char *section, char *param, bool b);

You can save any changes you made to the INIfile variables by using
the following functions:

bool set(char *section, char *param, char *value);
bool set(char *section, char *param, int i);
bool set(char *section, char *param, double d);
bool set(char *section, char *param, bool b);

When you are done with the INIfile, don't forget to close it with
the following function:

bool close();

If you forget to close the INIfile, it will be closed for you in
the INIfile destructor.  The changes to the parameters won't be
written to disk until the INIfile is closed and this will happen
during object destruction.  Check out EX6.CC to see an example of
how to use the INI file class.


DEGUI functions

There are a number of functions in DEGUI that replace some Allegro
functions.  For example there is the degui_alert function which
works just like Allegro's alert function but uses nicer looking
objects.  Before using these functions you must also call
set_default_object_colors with some indexes of colors to use.

degui_alert            // Replacement for Allegro's alert function
degui_alert3           // Replacement for Allegro's alert3 function
degui_file_select       // Replacement for Allegro's file_select
function
degui_gfx_mode_select  // Replacement for Allegro's gfx_mode_select
function
do_degui_menu          // Replacement for Allegro's do_menu
function

These routines are used as default when degui.h is included due to
some #defines.  If you really need to access the old Allegro style
function you will have to #undef the function name.  For example to
use the new style degui alert function you can do the following:

alert(....); // This is #defined to call degui_alert

or of course just:

degui_alert(....);

If you really need to access Allegro's alert function you will have
to do this:

#undef alert
alert(....);   // Combined with the #undef, calls Allegro's alert
function
Allegro DIALOG procedures

There are a number of extensions to the Allegro dialog procedures
in DEGUI.  You can use then just like any other Allegro dialog
procedures, and that means that you will have to read the Allegro
docs regarding the gui.

Replacements for Allegro dialog procedures
These are just replacements for the basic Allegro dialog objects. 
Most of the procedures only catch the MSG_DRAW message and draw the
object using the new DEGUI look.  That means that the colors used
to draw the objects are the degui_xxx_color variables.  The normal
dialog colors like d->fg and d->bg are ignored so you don't have to
worry about that.

Some objects have a little added functionality such as the
d_degui_edit_proc also scrolls right if the string gets too long
for the space.  You can use these in your C program in the same way
as with C++ programs, include degui.h and link with libdegui.a

The functions are:
d_degui_clear_proc
d_degui_box_proc
d_degui_shadow_box_proc
d_degui_text_proc
d_degui_ctext_proc
d_degui_button_proc
d_degui_checkbox_proc
d_degui_radio_button_proc
d_degui_edit_proc
d_degui_list_proc
d_degui_menu_proc

New Allegro dialog object procedures
These functions are dialog procedures that you can use in your C
programs to take advantage of the DEGUI objects.  
d_degui_progress_bar_proc   /* draws a progress bar d1 is the
progress and d2 is the scale bar is plotted in % of d1/d2 */
d_degui_panel_raised_proc   /* Draws a raised panel */
d_degui_panel_sunken_proc   /* Draws a sunken panel */
d_degui_panel_ridge_proc    /* Draws a panel with a ridge around
it*/
d_degui_panel_groove_proc   /* Draws a panel with a groove around
it*/

Dialogs with DEGUI - C only

DEGUI was originally designed for C++, but you can easily make use
of the new dialog objects using only C.  Just #include "degui.h" in
your C program and call the function set_default_object_colors
function with 8 palette indexes in which to use for the object
colors.  This means that if you want to use the new DEGUI objects
in your C program, you only have to add 2 lines, at the beginning
of the program:
#include "degui.h" 

and after setting your palette call:

set_default_object_color(235,236,237,238,239,240,241,242);

which lets DEGUI use the colors 235 to 242 for it's gui objects. 
Of course you need to recompile and link with LIBDEGUI.A.  See
EX8.C for an example which is Allegro's EX13.C with only 2 lines
added to the source.  Compare how they look.  

You can access the individual colors directly through their
declaration in DEGUI.H which is:

extern int degui_fore_color;
extern int degui_mid_color;
extern int degui_back_color;
extern int degui_light_shad_color;
extern int degui_dark_shad_color;
extern int degui_select_color;
extern int degui_deselect_color;
extern int degui_disable_color;

When using DEGUI like this all the objects use the same color set
and changing one of the colors will affect all of the objects.


Dialogs with DEGUI - C++ style

Making dialogs using C++ object is easy.  First declare the objects
that you want, then add them to a dialog at the position and color
that you want.  Then execute the dialog.  Here is a simple 'Hello
world' example.

...
// Include the C++ objects
#include "degui.h"
...

// Set the default color indices
set_default_object_colors(235,236,237,238,239,240,241,242);

// Declare the C++ objects
clear_screen_object clear_obj;
button_object button("Hello world",0,true);

// Declare the C++ dialog
dialog cpp_style;

// Add the objects to the dialog
cpp_style.add(clear_obj);
cpp_style.add(button);

// Now center everything and then do the dialog
cpp_style.center();
cpp_style.execute();

Remember that when you are adding objects to a dialog that the
objects are drawn in the order that they are added.  This means
that any clear_screen_object must be the first object in a dialog.

Dialogs with DEGUI - Mixed C/C++ style

Method 1
Using C++ objects in your Allegro DIALOG structures is easy.  First
declare the objects that you want, then add them to your DIALOG
structure at the position and color that you want, making sure that
you call the function create_DIALOG while adding the object to the
DIALOG structure.  Then just use the do_dialog function.  Here is
a simple 'Hello world' example.

...
// Include the C++ objects
#include "degui.h"
...

// Set the default color indices
set_default_object_colors(235,236,237,238,239,240,241,242);

// Declare the C++ objects
button_object button("This is a C++ button",0,false);
button_object done("Done",0,true);

DIALOG c_style[] =
      {
      /* (dialog proc)     (x)  (y)  (w)  (h) (fg)
(bg)(key)(flags)(d1)(d2)(dp) */

      // Here are some normal C objects
      { d_clear_proc,      0,   0,   192, 172, 255, 0,  0,   0,   
 0,  0,  NULL },
      { d_button_proc,     50,  50,  250, 30,  255, 0,  0,   0,   
 0,  0,  "This is a C button" },

      // Here are some C++ objects in the middle of a C style
dialog !!!
      button.create_DIALOG(50,  10,  250, 30),  // There are
defaults for fg and bg
      done  .create_DIALOG(50,  100, 50,  30),

      // The terminating NULL dialog, dont forget this or there
will be trouble
      { NULL,               0,   0,   0,   0,   0,   0,   0,   0, 
   0,   0,   NULL }
      };

   // Center and do the dialog - C style
   c entre_dialog(c_style);
   do_dialog(c_style,-1);

Method 2
It is also possible to add normal Allegro dialog procedures to your
C++ dialogs.

...
// Include the C++ objects
#include "degui.h"
...

// Set the default color indices
set_default_object_colors(235,236,237,238,239,240,241,242);

// Declare the C++ objects
clear_screen_object clear_obj;
button_object button("This is a C++ button",0,true);

// Declare the C++ dialog
dialog cpp_style;

// Add the objects to the dialog
// The color arguments have defaults so we don't have to fill then
in
// The size and position arguments default to 0,0 and 0,0 so we had
// better fill them in if it's important
                        // (x)   (y)   (w)   (h)   (fg)  (bg)
cpp_style.add(clear_obj);
cpp_style.add(button,        0,   0,   250,  30);

// Here a Allegro dialog procedure added to a C++ dialog !!!
cpp_style.add(d_button_proc, 50,  50,  250,  30,    255,  
0,0,0,0,0,"This is a C button");

// Now center everything and then do the dialog
cpp_style.center();
cpp_style.execute();


Changing DEGUI Object Behavior

There are two main ways to change the behavior of a DEGUI object. 
One is to use callback functions and the other way is to derive an
object from a base object and catch the messages to the object
using virtual functions.

Callback functions
Setting a callback function for an object is easy.  The function
must be of the correct form and all you have to do is call one of
the set_xxx_callback function to set the callback function. Setting
a callback function for an object allows you to catch the actions
and do your own processing instead.  For example to call the
function 'foobar' whenever button 'foo' is clicked:

foo.set_click_callback(foobar);

Now whenever button 'foo' is clicked the function 'foobar' will be
called.  To remove a callback function just set the callback to
NULL and the callback function will be removed.

The default behavior for buttons is to become selected or
unselected on a click.  But when there is a callback for clicks,
the default behavior isn't done and the button will never be
selected or unselected.  To invoke the default behavior of an
object within an callback, call the function:

dialog_object::default_behaviour();

When this function is called within an callback function the
default behavior of the object will be done.  For the above
example, this function will select or unselect the button 'foo'.

Example program EX2.CC has an example of how to use callback
function.

Derived objects
You can derive your own objects from the DEGUI objects.  For
example:

class my_object
   :public button_object
   {
   private:

   protected:
        // Taking over the msg_draw function
        virtual void msg_draw (void);

   public:

   };

void my_object::msg_draw (void)
   {
   // Do any dray stuff for my_object
   }

This object is derived from the button_object class and overloads
only one virtual function, msg_draw.  My_object will act just like
a normal button, but will look different.  You can use the
my_object anywhere you would use on of the other dialog objects.

Example program EX3.CC has an example of how to derive object from
DEGUI objects.

Change log

Version 1.21  - November 1997
- Added some bugs warnings in BUGS.TXT for using some callbacks for
some objects will cause a crash.  I'd like to fix it, but it would
most likely be a lot of work and I'd rather spend my time working
on the next version which shouldn't have that problem.  I'd rather
not encourage using callbacks, since object derivation is really
the way to go for anything but trivial behaviour modifications.

- Fixed example 4 to return the correct text from the
edittext_object.

- I'm not sure exactly when, but I slightly changed the behaviour
of the hiddenlist_object to clicks to be a little more like a
windows combo box.

- I'm sure you have already noticed that the docs are in .TXT
format and no longer in .RTF format.  I'll keep making the text in
.RTF so if you want an .RTF file I can mail it to you.

Version 1.2  - August 1997

- Created the hidden_list_object

- Created the window_object and an example.

- The dialog is now derived from the dialog_object class and is now
added to the DIALOG array whenever a dialog is added to another
dialog (making it a sub-dialog).  Right now it doesn't do anything,
but I'm pretty that it will be useful later.

- Added the progress_bar_object, and the raised, sunken, grooved
and ridged panels.

- The edittext_object now scrolls when editing text longer than can
be displayed.

- The builder now saves the graphic mode information and the
builder granularity in the builder INI file.

- Updated the INIfile class from code contributed by Matthias Baas. 
Error handling should be better and more extensive now.  There is
a public const char* that indicates the last error that has
occurred.  Note that if an INI parameter doesn't exist and the
default is used, it is not considered an error.

- Created the scrollable_object which contains enough info to draw
a scrollbar on an object.  It is used as a base class for the
list_object and textbox_object, which means that the textbox_object
is no longer derived from listbox_object.  This should make
simplification of the textbox_object and future objects with a
scrollbar possible.

- Added a possibility to move more than one object at once in the
builder with the multi-move button

- The edittext_object now edits it's own buffer.  That means that
you don't have to supply the entire buffer for the
edittext_objects, and if you want to get the text from the object
you must use the get_text function.

- If you include degui.h and then call an Allegro alert or
file_select function, you are really calling special DEGUI
functions.  If you must call the old allegro function you must
undefine it's name.  For example the code:
alert("Hello",NULL,NULL,"OK',NULL,0,0);
actually calls the function:
degui_alert("Hello",NULL,NULL,"OK',NULL,0,0);
If you really must call the old alert you must do this:
#undef alert
alert("Hello",NULL,NULL,"OK',NULL,0,0);

- Changed the looks of all the objects for some simple 3D effects. 
Thanks to Matthias Baas for his help.

- Remove the reliance on makefiles make by RHIDE.  There are now
normal makefiles for each part.  I'll leave the RHIDE projects in
to make it easier for people who use RHIDE (like me).

- The dialog_object colors are now handled differently.  Each
object has a pointer to a color_scheme object which it uses to
determine what colors it should be.  If you want to change the
colors of an object, create a color_scheme object and use the
set_color function to set the object's color.  If you don't set any
colors, the objects will use the default object colors.  Changing
some of the degui_xxx_color will change the colors of all the
objects that use the default colors.

-  The dialog_objects no longer require the corresponding
d_xxx_proc.  They are now all completely self-contained and thus
the proc_object isn't used for the library anymore, although you
can still use it if you want to be able to derive objects from your
own d_xxx_proc procedures.  

- I converted the do_dialog function into a class and brought it
into DEGUI so that my dialog object could have more control over
the do_dialog internals.  The class is called dialog_do and you
shouldn't need to use it directly, because the dialog class uses it
internally.

- You can now add and remove entire dialogs from running dialogs. 
When you add a dialog to another dialog all the sub-dialog_objects
are added to the parent dialog.

- When declaring a dialog, you must now tell it how many objects
that the dialog can contain.  If you try to add more objects than
this number, the adds are just ignored.  The default number is 50. 
I will try to remove this necessity in the future.

- Fixed some bugs relating to the passing of the focus in the
object_message_proc.  Sometimes dialogs exit as soon as they
started and look like they crashed.

Version 1.1  - May 1997

- Added dialog.add(int(*proc)(int,DIALOG*,int),int x,int y, ....
,void *dp) procedure to add Allegro dialog procedures directly to
the C++ dialog.

- Now checks all failed memory allocation and added error handlers
for a failed new.

- Added all_callback function which may be called for every
message.

- The internal DIALOG *d of objects is now always valid and now
objects can be selected/deselected etc at any time.  Any
positioning/size/flag information is preserved when adding an
object to a dialog, except if they are overridden in the
dialog.add() function itself.

- Added the x(), y()..., set_x(), set_y()... functions for object
positioning.

- Added the textbox_object that displays char* with or without word
wrapping.  It's still pretty new so if you see any bugs please tell
me.

- Message function are now public.  Could be dangerous, but keeping
them protected doesn't allow us to make envelope and letter class
structures. (A neat technique I learned from a C++ book that looks
quite useful.)

- No more support for exporting C DIALOGS in the builder.  If you
must for some bizarre reason use the objects in a C DIALOG array
then you will have to put them in manually using the
create_DIALOG() function. See EX1.CC for an example.

- Added a void *dataptr to the dialog_object.  It really helps
connecting weird things to the objects and doesn't take up much
space if you don't use it.  I used essentially the same technique
to attach the object descriptions to the objects in the builder as
I used to link the C++ objects with Allegro's DIALOG structure.  In
the future this may change into a dedicated dialog_object_data
class, holding data associated with an object.  I'm still open for
ideas on how this could be most elegantly done.

- Each dialog object now has a pointer to it's parent dialog.  If
the object is used in a C DIALOG array with create_DIALOG() then
the parent is NULL.

- Drastically changed the builder class structure using the dataptr
in the dialog object as pointer to object descriptions.

- dialog_object now have a _redraw flag that forces a redraw at the
next idle period.  Functions that change how the object look such
as set_x() etc... set this flag so that the object is automatically
redrawn.

Version 1.0  - April 1997

  - Very first version


Future Work

- Extend functionality of wondow_objects in the builder
- Make C interface to all the objects and replace the C exporting
funnction in the builder
- Make scrollable bitmap
- Make Allegro grabber help dialog
- Support for the upcoming 15 and 16 bit video modes in Allegro
- Handling of several different fonts in a dialog
- Form objects that take sub-objects and automatically stack them
vertically or horizontally
- Cooperative multi tasking using a dialog.check() function or
multi tasking objects
- A simple HTTP reader so that help files and/or dialogs can de
made in HTTP
- An editable textbox object just like the textbox_object
- A scrolling textbox that keeps all the old text
- Make object modifications in the builder using right click
instead of keypress stuff
- Modify dialog_do to use Tarray<DIALOG> instead of the pointer
stuff.
- Call all the Allegro routines through pointers to functions so
that we can use DEGUI with other graphic libraries i.e. GRX
- Add key to duplicate objects in the builder
- Add dialogs to change the properties of dialog objects.  Possibly
make all creations the default and only change properties
- Clean up code for textbox_object.  It could be much simpler and
cleaner than it is.

Do you have any suggestions about what to add to DEGUI? Just e-mail
me.


In Conclusion

Why is DEGUI free?  Why are the sources freely available?  Because
I want DEGUI to be the best gui that there is.  When you release
your source code and give away your program for free people will
use your program and look at your source code.  When they find bugs
and mistakes they'll tell you and sometimes even fix the bugs
themselves.  They couldn't do that without your source.  The end
result is that your software gets better and better.  There is no
way in hell that I could have made DEGUI without the freely
available source of Allegro and I really appreciate the help that
Shawn Hargreaves has given me through e-mail and through his source
code.  I have tried to return the favor by suggesting some minor
improvements to the Allegro gui code and by making my source code
freely available.


Thanks

I would like to thank a number of people for making DEGUI possible:

D. J. Delorie and all the people connected with DJGPP for making
such a powerful tool available for DOS users.  It runs in 32 bit
protected mode, has got tons of virtual memory and mind boggling
optimization.  It's amazingly powerful and yet free. 

Shawn Hargreaves and everyone connected with Allegro, the amazing
graphics, sound and hardware programming library for DJGPP.  Fast,
complete, easy to use, and yet free.

Robert Hohne and everyone connected with the extremely useful
RHIDE, an IDE for DJGPP that has the look and feel of the Borland
C++ 3.1 IDE.  One of the best IDE's I have ever used, and yet free.

Everyone at comp.os.msdos.djgpp for being patient and helpful. 
It's too bad that the other newsgroups aren't useful and as
friendly as this one.  Its free.

Matthias Baas who has helped me debug lots of stuff and contributed
the INI file code and got me started with the new look for the
obejcts.  That was free too.


Doug Eleveld
Herepoortenmolendrift 14
9711 DG, Groningen
The Netherlands

D.J.Eleveld@anest.azg.nl
