Documentation on programming using the Coolwidgets library.

For documentation on macro programming Cooledit with the Python
scripting language, see the man page, cooledit(1).

(This document is ALPHA)


*******************************************************************************

Overview: What is the Coolwidgets library?
------------------------------------------

Coolwidgets was written for the X Window System in C as an easy to use, 
GPL'd highlevel API that runs in parrallel with XLib. It supports a
variety of basic widget types, like buttons, pull-down menus,
scrollbars, text display boxes, as well as many convience dialog boxes.
Its most promanent feature is a powerful generic text editor widget.
This widget can be draw with a single command and provides a ready to
use editor with undo, macros, and MSW/MAC highlight/movements keys. Drag
and drop is also supported: XDND version 2. The look and feel of the
library is somewhat like applications from other operating systems. 
Coolwidgets is artistically designed for a most pleasing 3D look. It is
been tested on a variety of unix platforms.

Coolwidgets supports only a single display. It can be used with minimal 
knowledge of XLib, although this knowledge is needed for extending the 
library.

The type of applications you may want to write using Coolwidgets would 
be text based applications that use a single font throughout, and also
applications that would benefit from an internal editor. For example,
mail and news readers. Porting of terminal applications using coolwidgets
is very easy. Other types of applications are not excluded however, since
there are no limitations on the kind of widgets you can create.

Other features:

- Coolwidgets does not require any libraries besides libX11
- single 256-byte structure for all widgets.
- internal AlarmEvent's can be recieved by the application for easy,
    continuous, real time handling of graphics.
- cooledit amalgamates expose events internally so that efficiant exposes
    are reported to graphics applications.
- full proportional font support on all widgets, including the text 
    box, and editor widgets.
- automatic generation of hotkeys for buttons.
- ready to use filebrowser with file filter and seperate directory 
    and file selection lists.
- automatic text history on all input lines.
- efficient redraw code for the editor and textbox's; draws text with
    an absolute minimum of server load - coolwidgets are fast.
- XDND version 2 drag and drop support, to and from input widget,
    editor widget, text box's, filebrowser.



*******************************************************************************

List of Supported Widgets
-------------------------

These are they:

- Text buttons
- Graphical buttons
- Single level pull down menus
- Scrollable text display box and selection boxes
- Scrollable text/selection box with columnated fields for tabulated
		list (think file list with |name|size|permissions|)
- Full screen text editor
- Radio buttons and check boxes
- Text input lines with histories
- Password input lines
- Number input lines
- Scrollbars
- Progress bars



*******************************************************************************

'Hello World' example program
-----------------------------

The best idea to see how the library works is to look at a trivial
application:

(This example appears in the examples directory)

--------------------cut-here---------------------------------------

/* hello.c - simple example usage of the coolwidget X API */
#include <coolwidget.h>

void main (int argc, char **argv)
{
    Window win;
    CInitData cooledit_startup;
    CEvent cwevent;
    int y;

/* intialise the library */
    memset (&cooledit_startup, 0, sizeof (cooledit_startup));
    cooledit_startup.name = argv[0];	/* won't bother with other init's like geom and display */
    cooledit_startup.font = "-*-helvetica-bold-r-*--14-*-*-*-p-*-iso8859-1";
    CInitialise (&cooledit_startup);

/* create main window */
    win = CDrawMainWindow ("hello", "Hello");

    CGetHintPos (0, &y);	/* y position where to start drawing */
    CDrawText ("hellotext", win, 0, y, " Hello World ");	/* the lable "hellotext" may be used to identify the widget later */
    CCentre ("hellotext");	/* we want the text centred from left to right */
    CGetHintPos (0, &y);	/* get the next y position below the last widget drawn */
    CDrawButton ("done", win, 0, y, AUTO_SIZE, " Done ");	/* draw a button... */
    CCentre ("done");		/* ...centred */
    CSetSizeHintPos ("hello");	/* set the window size to just fit the widgets */

    CFocus (CIdent ("done"));
/* show the window */
    CMapDialog ("hello");

/* Run the application. */
    do {
	CNextEvent (0, &cwevent);
	if (cwevent.type == QuitApplication)	/* pressed WM's "close" button */
	    break;
    } while (strcmp (cwevent.ident, "done"));

/* close connection to the X display */
    CShutdown ();
}

--------------------cut-here---------------------------------------



*******************************************************************************

A slightly more elaborate example program
-----------------------------------------

The file editor/smalledit.c contains a more comprehensive example. See 
this file to find out how a powerful text editor is created in just 
under 400 lines.



*******************************************************************************

Is it easy to create my own application, ready for distribution?
----------------------------------------------------------------

Probably the easiest way to create your own application is to just 
copy the entire cooledit distribution, and replace the editor directory 
with your own directory. If you are going to distribute this package as 
freeware, you may want to leave the examples and doc directory intact for 
other programmers. Replace files like NEWS, README, cooledit.1 etc. 
Don't delete the NEWS and README files, however - they are required by 
automake - rather just empty them if you aren't going to use them.

You should then get hold of  automake  and  autoconf. Then edit the 
Makefile.am files and to correspond to your own filelists. You may need 
to incorporate new macros into configure.in. If you are porting an 
existing application, just merge the two configure.in files.

Set
    $cmdline_use_dependencies = 0;
in the automake executable (i.e. /usr/local/bin/automake on my system).

Then just run remake. The autmake Makefile files take care of just about 
everything. See the  autoconf  documentation for more information. If 
you are going to rebuild manually
    automake
    autoheader
    autoconf
will redo everything.

This way, the Coolwidget library will be included in your distribution,
and people won't have to go fetch it. When coolwidget grows, I will 
release a dynamically linked version, and you will then include the
'widget' directory at your own discretion.



*******************************************************************************

Basics: programming with Coolwidgets
------------------------------------

There are a few things you need to know before you jump right in and 
start programming.

A 'widget' in this library is a graphic item like a scrollbar or button.
Each widget type is called a 'kind' and is one of those ennumerated in 
coolwidget.h (eg C_TEXTBOX_WIDGET). Even a dialog box window is a 'widget'
with other widgets drawn over it. Each widget has a parent window, and a
window of its own. Each widget exists by virtue of a structure of type
CWidget. The CWidget structures are allocated internally by the library
and are stored in a global array that can be hold a maximum of 1024 widgets.
There is only one such structure (CWidget) which is presently 248 bytes long.
This structure has members for the data of all the different widgets.
This may seem inefficent, since a button uses hardly any of the structure,
but considering that 100 widgets would be only 25kB, it is not an
appreciable ineffiency. Where a lot of memory is needed (like the editor
widget - which needs a more involved structure), there are hooks to point
to specific structures.

Each widget is given a unique text identifier string. You must ensure 
that no two widgets have the same string and that the strings are than
32 characters long. You can use these strings to identify the widget
after creating it, without having to declare global variables. The library 
uses these strings to remember things about the widget (such as input 
histories of the input line widget). Most procedures which operate on 
widgets take as an argument either the widget's identifier string, or a
pointer to the widget's structure, depending on the typical context of
the procedure.

Functions and macros are provided to convert between the following three
things:
    - The widget's character string identifier
	    (usually declared as 'char *ident')
    - A pointer to the widget's CWidget structure
	    (usually declared as 'CWidget *w' or 'CWidget *wdt')
    - The widgets window
	    (usually declared as 'Window win')



These functions are:

Window CWindowOf (CWidget * w);
char *CIdentOf (CWidget * w);
Window CWindowOfWidget (const char *ident);
CWidget *CWidgetOfWindow (Window win);
CWidget *CIdent (const char *ident);

If a widget matching the specified string identifier is not found,
a NULL value is returned, which means a widget with that name is
not in existance.



*******************************************************************************

Function naming conventions:
----------------------------

A more-or-less consistant naming scheme has been adopted through-out
the library. All functions that create widgets are called CDrawSomething().
All common library functions are named using the Xlib function naming
standard, but with a C (for Coolwidgets) instead of an X. Functions
internal to the library are named using the GNU standard
i.e. some_function(), not SomeFunction(). Low level library functions
(they are acceptable to use, but are likely to be used less often), are
also named with the GNU standard. You should use GNU naming for your
own programs, and X naming for extending the library. Numeric #define's
are named like SCOPE_SOME_NUMERIC_DEFINE, where 'SCOPE' is the widget
or thing that it applies to.

I havn't stuck to all these rules with absolute rigidity.



*******************************************************************************

Initialising, running and exiting the library:
----------------------------------------------

To intialise the library, just create a structure of type CInitData, fill 
in the fields you like (zero for don't-cares) and call CInitialise with 
a pointer to the structure.

Before quiting execute the CShutdown() procedure.

These two function are the start and end of any Coolwidget application. 
The main loop of the application must contain the line
    CNextEvent(...);
This function is the core of the library. It responds to X events, 
handles callbacks, translates various low level events to higher level
events and so on. It returns the low level event XEvent structure, and 
a higher level CEvent structure which is simpler to interpret.

If you don't know how X works just remember that everything that comes 
through your mouse and keyboard is returned by this function. If no 
events are available, it will wait for one.



*******************************************************************************

Commandline options:
--------------------

The library has a very nice function to process command-line options of
the form
    my_prog [-aAbBcC] [-long_a] [-long_b] ... [--some-option <argument>] 
	<filename1> <filename2> ... etc. etc.
in any order. In other words, almost any kind of command line syntax 
seen today.

Use it as follows: (see widget/cmdlineopt.h for full specs)

First declare all the options you would like:

/* anything on the command-line that is not part of an option goes into here */
char *command_line_files[] =
{0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0,
 0, 0, 0, 0, 0, 0, 0, 0};

/* you can specify defaults here: */
char *option_background_color = 0;
char *option_foreground_red = 0;
char *option_foreground_green = 0;
char *option_foreground_blue = 0;
char *option_font = "-*-helvetica-bold-r-*--14-*-*-*-p-*-iso8859-1",
int get_help = 0;
int get_version = 0;
int option_verbose = 0;
char *option_display = 0;
char *option_geometry = 0;

struct prog_options cooledit_options[] =
{
    {' ', "", "", ARG_STRINGS, 0, command_line_files, 0},
    {0, "-bg", "--background-color", ARG_STRING, &option_background_color, 0, 0},
    {'R', "", "--foreground-red", ARG_STRING, &option_foreground_red, 0, 0},
    {'G', "", "--foreground-green", ARG_STRING, &option_foreground_green, 0, 0},
    {'B', "", "--foreground-blue", ARG_STRING, &option_foreground_blue, 0, 0},
    {'f', "-fn", "-font", ARG_STRING, &option_font, 0, 0},
    {'h', "-?", "--help", ARG_SET, 0, 0, &get_help},
    {'H', "-help", "--help", ARG_SET, 0, 0, &get_help},
    {'V', "-v", "--version", ARG_SET, 0, 0, &get_version},
    {0, "", "-verbose", ARG_SET, 0, 0, &option_verbose},
    {'d', "", "-display", ARG_STRING, &option_display, 0, 0},
    {'g', "-geom", "-geometry", ARG_STRING, &option_geometry, 0, 0},
    {0, 0, 0, 0, 0, 0, 0}
};

void process_command_line (int argc, char **argv)
{
    int error;
    error = get_cmdline_options (argc, argv, cooledit_options);

    if (error) {
	fprintf (stderr, "%s: error processing commandline argument %d\n", argv[0], error);
	usage ();
	exit (1);
    }
    if (get_help)
	usage ();
    if (get_version)
	version ();
    if (get_help || get_version)
	exit (0);
}



*******************************************************************************

String utility functions:
-------------------------

The most inportant of these is the catstrs function:

char *catstrs (const char *first,...);

eg.
    a = catstrs ("hello", " ", "there", "!", 0);
returns a as
    "hello there!"

Note: The last arg must always be 0. The result of this function must
*NEVER* be free'd. It automatically free's strings older than 64 calls
to catstrs.

Do *NOT* use the catstrs function inside a loop, because it only stores 
the last 64 results. Hence also do not use it for strings you need to 
keep for a long time (use a = strdup(catstrs(...)); ...; free(a); 
instead).

Other utility functions are:

(see stringtools.c for a more detailed explanation of what these do.)

/* case insensitive strchr */
char *strcasechr (const char *p, int c);
/* returns "-1234" from i = -1234, use result before next call and do not free result */
char *itoa (int i);
/* result must not be free'd */
char *get_current_wd (char *buffer, int size);
/* returns 0 on success */
int change_directory (const char *path);
/* use result before next call and do not free result */
char *name_trunc (const char *txt, int trunc_len);
/* does tilde expansion. result must be free'd, CInitialise() must be called before use */
char *canonicalize_pathname (const char *given_path);
/* strlen, but not more than count */
size_t strnlen (const char *s, size_t count);
/* returns the size of a printf string WITHOUT printing it! */
size_t vfmtlen (const char *fmt, va_list ap);
/* sprintf and allocate in one go, result must be free'd */
char *vsprintf_alloc (const char *fmt, va_list ap);
char *sprintf_alloc (const char *fmt,...);

/* Also: these are set by CInitialise */
/* current directory */
extern char *current_directory;
/* home directory */
extern char *home_directory;

For writing to a dynamically expanding memory pool, use the 'pool' 
functions of pool.h. See dirtools.h for an example.



*******************************************************************************

Compatability and utilities for different Unix's:
-------------------------------------------------

All these are supported, no need to include string.h:

void *memset (void *dest, int c, size_t n);
void *memchr (const void *s, int c, size_t n);
int memcmp (const void *m1, const void *m2, size_t n);
char *strstr (const char *s1, const char *s2);
size_t strspn (const char *s, const char *accept);
void *memmove (void *dest, const void *src, size_t n);
int strcasecmp (const char *p1, const char *p2);
int strncasecmp (const char *p1, const char *p2, size_t n);
char *strdup (const char *s);
int vsprintf (char *str, const char *fmt, va_list ap);



*******************************************************************************

Drawing Widgets and Dialogs
---------------------------


This gets down to the nuts and bolts.
(see the example dialog just below this text. Also run cooledit and 
look at the dialogs to get an idea of what is actually happening)

Each widget has a function of like:
CDraw... (const char *ident, Window parent, int x, int y, ...);

You will have to see the header file coolwidget.h to see the specs, 
but here I will show you the basics.

Now window creation (and each widget has a one) requires two steps 
under X (an extra step if you are creating main windows that the window 
manager has to be aware of). Coolwidgets simplifies this procedure 
considerably:

step 1: create the window (the window exists, can be drawn to, but is 
	not shown).
step 2: set the windows attributes (main windows)
step 3: map the window (show the window)

For all widgets except window widgets, these steps are done 
automatically with a single command: CDraw...()

You can call a CDraw...() function to create a widget. The widget 
window will be mapped, and the widget will appear as soon as you start 
calling CNextEvent() (CNextEvent() 'runs' the library). (This is
provided the parent window is mapped, if it is not mapped, then the
widget will only appear once you map the parent window).

For dialog widgets, the procedure is slightly different. Typically, you 
would want to draw other widgets into a dialog widget.
This means that you don't know how big the dialog is until you have drawn 
its children widgets. Hence we draw the dialog, THEN draw its children, 
THEN set the size of the dialog, and THEN map the dialog.
The command for drawing a window widget is
    Window CDrawDialog (const char *identifier, Window parent, int x, int y);
which will create the widget, but will not map it. Then we draw widgets 
into the dialog; and then we call
    CSetSizeHintPos (char *ident_of_dialog);
to set the dialog to its proper size. Finally, we call
    CMapDialog (char *ident_of_dialog);
before running the dialog.

Here is a typical example:
This draws a dialog to enter the 'Subject:', 'Cc:' and 'To:' fields of a
mail message. A lot of this code is explained later on, just note the
general procedure. (This is very similar to the actual code used in 
cooledit.)


extern void pipe_mail (char *to, char *subject, char *cc);

void mail_subject_to_cc_dialog (Window in, int x, int y)
{
    Window win;
    CEvent cwevent;
    CState s;
    int y2;

    CBackupState (&s);
    CDisable ("*");

/*
   Draw the dialog and record the returned window,
   we need win as an argument to draw children widgets.
 */
    win = CDrawHeadedDialog ("mail", in, x, y, " Send Mail ");
    CGetHintPos (&x, &y);
    y2 = y;

    (CDrawText ("mail.tto", win, x, y, "To: "))->hotkey = 'T';
    CGetHintPos (0, &y);
    (CDrawTextInput ("mail.to", win, x, y, (FONT_MEAN_WIDTH) * 50, AUTO_HEIGHT, 1024, CLastInput ("mail.to")))->hotkey = 'T';
    CGetHintPos (0, &y);

    (CDrawText ("mail.tsubject", win, x, y, "Subject: "))->hotkey = 'S';
    CGetHintPos (0, &y);
    (CDrawTextInput ("mail.subject", win, x, y, (FONT_MEAN_WIDTH) * 50, AUTO_HEIGHT, 1024, CLastInput ("mail.subject")))->hotkey = 'S';
    CGetHintPos (0, &y);

    (CDrawText ("mail.tcc", win, x, y, "Copies to: "))->hotkey = 'C';
    CGetHintPos (0, &y);
    (CDrawTextInput ("mail.cc", win, x, y, (FONT_MEAN_WIDTH) * 50, AUTO_HEIGHT, 1024, CLastInput ("mail.cc")))->hotkey = 'C';
    CGetHintPos (0, &y);

    get_hint_limits (&x, 0);
    CDrawPixmapButton ("mail.send", win, x, y2, PIXMAP_BUTTON_TICK);
    CGetHintPos (0, &y2);
    CDrawPixmapButton ("mail.cancel", win, x, y2, PIXMAP_BUTTON_CROSS);

    (CIdent ("mail"))->position = WINDOW_ALWAYS_RAISED;

/* Set the size of the dialog to exactly fit all the widgets */
    CSetSizeHintPos ("mail");

/* The dialog is ready: map the dialog */
    CMapDialog ("mail");

/* Don't forget to focus on the widget we would like entry to go to */
    CFocus (CIdent ("mail.to"));

/* Now run the dialog */
    for (;;) {
	CNextEvent (NULL, &cwevent);

/* Window was killed by some unknown means (like the window manager) - this won't actually happen here */
	if (!CIdent ("mail"))
	    break;

/* Pressed the 'Esc' key: */
	if (!strcmp (cwevent.ident, "mail.cancel") || cwevent.command == CK_Cancel)
	    break;

/* Pressed the 'Enter' key: */
	if (!strcmp (cwevent.ident, "mail.send") || cwevent.command == CK_Enter) {

/* We want an hour glass in case this takes a few seconds: */
	    CHourGlass (win);

/* The function pipe_mail() does not come with the library: */
	    pipe_mail ((CIdent ("mail.to"))->text,
		       (CIdent ("mail.subject"))->text,
		       (CIdent ("mail.cc"))->text);

/* Turn off hourglass: */
	    CUnHourGlass (win);
	    break;
	}
    }

/* Destroys children widgets also: */
    CDestroyWidget ("mail");
    CRestoreState (&s);
}



*******************************************************************************

Event Handling
--------------

There are two ways to 'run' a dialog using the library. The first way 
is the 'object orientated' way used with the X toolkits. Using this 
method, routines are hooked onto the widgets, using the CAddCallback() 
function. The dialog main loop is just: 
    for (;;) {
	CNextEvent (NULL, NULL);

/* Dialog no long exists */
	if (!CIdent ("mail"))
	    break;
	}
    }

Although object orientated method is more modular, it often has the problem 
that many little subroutines have to created, some of which would be 
much more appropriate as a simple if statement in the body of the code.

The second method is the 'basic C' approach, used in the mail example above.
No callbacks are used, instead, 'if' conditions in the main loop do the 
operations. This way, simple dialogs can be created in one function. 
You must choose to what extent you use either technique, to make your 
code as succinct as possible.



*******************************************************************************

Details of Dialog Creation
--------------------------

One of the problems with creating dialogs is the spacing of widgets. It 
is tedious to calculate the positions of widgets for a typographically 
correct dialog box.

The technique used above is to draw the widget, and the look to see how 
much space it has taken up. Then draw the next widget with this 
information. The library records a 'hint' position which points to the 
position where the next widget may be drawn. This position is reset 
when you draw a window. The command
    void CGetHintPos (int *x, int *y);
returns the x and y pixel coordinates of this position.

In the above dialog, after calling CDrawHeadedWindow(), we issue:
    CGetHintPos (&x, &y);
Which gets a point close to the top left hand corner of the dialog.
Then we draw a label at this position while setting a letter to be 
underlined:
    (CDrawText ("mail.tto", win, x, y, "To: "))->hotkey = 'T';
We want to now draw directly under this widget, so we get the hint of 
the y coordinate:
    CGetHintPos (0, &y);
Then we draw a text-input widget, while setting its hotkey to 'T':
    (CDrawTextInput ("mail.to", win, x, y, (FONT_MEAN_WIDTH) * 50, AUTO_HEIGHT, 1024, CLastInput ("mail.to")))->hotkey = 'T';
Finally we get the hint position for the next widget below the 
text-input widget:
    CGetHintPos (0, &y);
And so on...


Having done the text-input widgets, we now want to draw an 'Ok' and 
'Cancel' button. These will be drawn to the very right of everything 
drawn so far. The function
    void get_hint_limits (int *x, int *y);
get the maximum position thus far reached.



*******************************************************************************

Disabling Widgets
-----------------

Because CNextEvent() runs all the widget's you have ever created, 
other dialogs will continue to operate in parallel with the currently 
created dialog. This behaviour would be confusing to the user, so the
functions
    CDisable (const char *ident);
    CEnable (const char *ident);
are provided to stop user input to those widgets. CDisable() and 
CEnable(), take shell patterns as arguments, so you can disable groups 
of widgets with a single command. You should hence name widgets with 
this in mind using dotted notation eg: call your menu buttons 
"menu.file", "menu.options", "menu.help" etc., so that you can use 
CDisable("menu.*") to disable them all at once.
The commands CBackupState() and CRestoreState() record the state of 
"enablement" of all the widgets in the library. So you can restore your 
application at the end of a dialog. Note that I personally have never 
thus far found use for anything but CDisable("*"). But I can imagine 
circumstances where this would be very useful.



*******************************************************************************

Resizable Dialogs
-----------------

Resizing of dialogs is trickey. Coolwidgets does not support widgets 
moving all over to make space for other widgets. After the dialog is 
created, the command
    void CSetWindowResizable (const char *ident,
	int min_width, int min_height, int max_width, int max_height);
will make the dialog resizable. The position member of all the children 
widgets will then have to set to reflect the way the widget is to be
resized or moved when its parent window is resized. At this stage, all 
the widgets would be spaced correctly (though the dialog would not be 
mapped) and hence all resizing would do would be to track changes in 
the dialog size. The various options are
    - POSITION_RIGHT: the widget moves to keep its distance to the 
	    right border constant
    - POSITION_WIDTH: the widget resizes to keep its distance to the 
	    right border constant
    - POSITION_BOTTOM: the widget moves to keep its distance to the 
	    bottom border constant
    - POSITION_HEIGHT: the widget resizes to keep its distance to the 
	    bottom border constant
    - POSITION_CENTRE: the widget moves to keep in the centre
    - POSITION_FILL: the resizes to be justified on the right border.
These values must be OR'd with the ->position member of the CWidget 
structure, or otherwise, just call
    void CSetMovement (const char *ident, unsigned long position)

This creates a very dynamic effect when resizing subwindows, see 
cooledit and open a file; try resizing the 'Load' dialog box to see what 
happens. If these options don't have enough functionality for you, then 
set the resize member of the CWidget structure to a function of your 
own. Also, don't make dialogs resizable when they don't need to be: like 
error dialogs etc. They should be resizable only when the user may like
to see more of something: like text boxes.

Here is how I create the filebrowser dialog:
(This just draws the dialog, it does not run it.
 It is the most complicated dialog in the whole library.)

Window draw_file_browser (const char *identifier, Window parent, int x, int y,
		    const char *dir, const char *file, const char *label)
{
    char *filelist = 0;
    char *directorylist = 0;
    char *resolved_path, *p;
    int y2, x2, x3, y3;
    Window win;

    if (parent == CRoot)
	win = CDrawMainWindow (identifier, label);
    else
	win = CDrawHeadedDialog (identifier, parent, x, y, label);
    (CIdent (identifier))->options |= WINDOW_ALWAYS_RAISED;
    CHourGlass (CFirstWindow);
    filelist = get_file_list (dir, 'f', CLastInput (catstrs (identifier, ".filt", 0)));
    CUnHourGlass (CFirstWindow);
    if (!filelist || !(directorylist = get_file_list (dir, '/', ""))) {
	CErrorDialog (parent, 20, 20, " File browser ", " Unable to read directory ");
	CDestroyWidget (identifier);
	goto error;
    }
    CGetHintPos (&x, &y);
    resolved_path = canonicalize_pathname (dir);
    p = resolved_path + strlen (resolved_path) - 1;
    if (*p != '/') {
	*++p = '/';
	*++p = '\0';
    }
    (CDrawText (catstrs (identifier, ".dir", 0), win, x, y, resolved_path))->position |= POSITION_FILL;
    free (resolved_path);
    CGetHintPos (0, &y);
    y3 = y;

/* draws two widgets: text box with a scrollbar */
    (CDrawTextbox (catstrs (identifier, ".fbox", 0), win, x, y,
		  FONT_MEAN_WIDTH * 24 + 7, FONT_PIX_PER_LINE * 14 + 6, 0, 0, filelist, TEXT_FILES))->position |= POSITION_WIDTH | POSITION_HEIGHT;
    (CIdent (catstrs (identifier, ".fbox", 0)))->options |= TEXT_MARK_WHOLE_LINES;

/* the vertical scroll bar is named the same as the text box, with a ".vsc" at the end: */
    CSetMovement (catstrs (identifier, ".fbox.vsc", 0), POSITION_HEIGHT | POSITION_RIGHT);
    CGetHintPos (&x2, &y2);
    x3 = x2;

/* draws two widgets: text box with a scrollbar */
    (CDrawTextbox (catstrs (identifier, ".dbox", 0), win, x2, y + TICK_BUTTON_WIDTH + WIDGET_SPACING,
		  FONT_MEAN_WIDTH * 20 + 7, y2 - WIDGET_SPACING * 2 - y - TICK_BUTTON_WIDTH, 0, 0, directorylist, TEXT_FILES))->position |= POSITION_HEIGHT | POSITION_RIGHT;
    (CIdent (catstrs (identifier, ".dbox", 0)))->options |= TEXT_MARK_WHOLE_LINES;

/* the vertical scroll bar is named the same as the text box, with a ".vsc" at the end: */
    CSetMovement (catstrs (identifier, ".dbox.vsc", 0), POSITION_HEIGHT | POSITION_RIGHT);
    CGetHintPos (&x2, &y2);
    (CDrawTextInput (catstrs (identifier, ".finp", 0), win, x, y2,
		    WIDGET_SPACING * 2 - 2, AUTO_HEIGHT, 256, file))->position |= POSITION_FILL | POSITION_BOTTOM;
    CGetHintPos (0, &y2);
    (CDrawText (catstrs (identifier, ".filx", 0), win, x, y2, "Filter : "))->position |= POSITION_BOTTOM;
    CGetHintPos (&x, 0);
    (CDrawTextInput (catstrs (identifier, ".filt", 0), win, x, y2,
		    WIDGET_SPACING * 2 - 2, AUTO_HEIGHT, 256, TEXTINPUT_LAST_INPUT))->position |= POSITION_FILL | POSITION_BOTTOM;
    (CDrawPixmapButton (catstrs (identifier, ".ok", 0), win, x3, y3,
		       PIXMAP_BUTTON_TICK))->position |= POSITION_RIGHT;
    (CDrawPixmapButton (catstrs (identifier, ".cancel", 0), win, x2 - WIDGET_SPACING * 2 - TICK_BUTTON_WIDTH - 20, y3,
		       PIXMAP_BUTTON_CROSS))->position |= POSITION_RIGHT;
    CSetSizeHintPos (identifier);
    CMapDialog (identifier);
    y = (CIdent (identifier))->height;
    CSetWindowResizable (identifier, FONT_MEAN_WIDTH * 40, min (FONT_PIX_PER_LINE * 4 + 210, y), 1600, 1200);	/* minimum and maximum sizes */

  error:
    if (directorylist)
	free (directorylist);
    if (filelist)
	free (filelist);
    return win;
}




*************************
DOCUMENT NOT YET COMPLETE
*************************







