@node Part I Goodies
@chapter Goodies

A number of special routines are provided that make working with simple
forms even simpler. All these routines build simple forms and handle the
interaction with the user.


@ifnottex

@menu
* Messages and Questions::
* Command Log::
* Colormap::
* Color Chooser::
* File Selector::
@end menu

@end ifnottex


@node Messages and Questions
@section Messages and Questions

The following routines are meant to give messages to the user and to ask
simple questions:
@findex fl_show_message()
@anchor{fl_show_message()}
@example
void fl_show_message(const char *s1, const char *s2, const char *s3);
@end example
@noindent
 It shows a simple form with three lines of text and a button labeled OK
on it. The form is so shown such that the mouse pointer is on the
button.

Sometimes, it may be more convenient to use the following routine
@findex fl_show_messages()
@anchor{fl_show_messages()}
@example
void fl_show_messages(const char *str);
@end example
@noindent
when the message is a single line or when you know the message in
advance. Embed newlines in @code{str} to get multi-line messages.

As a third alternative you can also use
@findex fl_show_messages_f()
@anchor{fl_show_messages_f()}
@example
void fl_show_messages_f(const char * fmt, ...);
@end example
@noindent
The only required argument @code{fmt} is a format string as you would
use it for e.g., @code{printf(3)}, which then is followed by as many
arguments as there are format specifiers in the format string. The
string resulting from expanding the format string, using the
remaining arguments, can have arbitrary length and embedded newline
characters (@code{'\n'}), producing line breaks. The size of the
message box is automatically made to fit the whole text.

Both of the message routines block execution and do not return
immediately (but idle callbacks and asynchronous IO continue to be
run and checked). Execution resumes when the OK button is pressed or
@code{<Return>} is hit, or when the message form is removed from the
screen by the following routine (for example, triggered by a timeout or
idle callback):
@findex fl_hide_message()
@anchor{fl_hide_message()}
@example
void fl_hide_message(void)
@end example


There is also a routine that can be used to show a one-line message that
can only be removed programmatically
@findex fl_show_oneliner()
@anchor{fl_show_oneliner()}
@findex fl_hide_oneliner()
@anchor{fl_hide_oneliner()}
@example
void fl_show_oneliner(const char *str, FL_COORD x, FL_COORD y);
void fl_hide_oneliner(void);
@end example
@noindent
where @code{str} is the message and @code{x} and @code{y} are the
coordinates (relative to the root window) the message should be placed.
Note that multi-line messages are possible by embedding the newline
character into @code{str}. See the demo program @file{preemptive.c} for an
example of its use.

By default, the background of the message is yellow and the text black.
To change this default, use the following routine
@findex fl_set_oneliner_color()
@anchor{fl_set_oneliner_color()}
@example
void fl_set_oneliner_color(FL_COLOR background, FL_COLOR textcol);
@end example

A similar routine exists to change the font style and size
@findex fl_set_oneliner_font()
@anchor{fl_set_oneliner_font()}
@example
void fl_set_oneliner_font(int style, int size);
@end example
@noindent

@findex fl_show_alert()
@anchor{fl_show_alert()}
@findex fl_hide_alert()
@anchor{fl_hide_alert()}
@example
void fl_show_alert(const char *s1, const char *s2, const char *s3,
                   int centered);
void fl_hide_alert(void);
@end example
@noindent
work the same as @code{@ref{fl_show_messages()}} goodie except that an
alert icon (!) is added and the first string is shown bold-faced. The
extra parameter @code{centered} controls whether to display the form
centered on the screen.

As in the case of messages also another function is avaialble
@findex fl_show_alert_f()
@anchor{fl_show_alert_f()}
@example
void fl_show_alert2(int centered, const char *fmt, ...);
@end example
@noindent
@code{centered} controls if the alert message is centered and
@code{fmt} must be a format string as e.g., used for
@code{printf(3)}. After the format string as many further arguments
are required as there are format specifiers in the format string. The
string resulting from expanding the format string, using the rest of
the arguments, can have arbitrary length and the first embedded
form-feed character (@code{'\f'}) is used as the separator between the
title string and the message of the alert box. Embedded newline
characters (@code{'\n'}) produce line breaks.

In combination with @code{@ref{fl_add_timeout()}}, it is easy to develop a
timed alert routine that goes away when the user pushes the OK button or
when a certain time has elapsed:
@example
static void dismiss_alert(int ID, void *data) @{
    fl_hide_alert();
@}

void show_timed_alert(const char *s1, const char *s2,
                      const char *s3, int centered) @{
    fl_add_timeout( 10000, dismiss_alert, 0 ); /* ten seconds */

    /* fl_show_alert blocks, and returns only when the OK button
       is pushed or when the timeout, in this case, 10 seconds,
       has elapsed */

    fl_show_alert(s1, s2, s3, centered);
@}
@end example
@noindent
Then you can use @code{show_timed_alert()} just as
@code{@code{fl_show_alert()}} but with added functionality that the
alert will remove itself after 10 seconds even if the user does not
push the OK button.


@findex fl_show_question()
@anchor{fl_show_question()}
@findex fl_hide_question()
@anchor{fl_hide_question()}
@example
int fl_show_question(const char *message, int def);
void fl_hide_question(void);
@end example
@noindent
Again shows a message (with possible embedded newlines in it) but this
time with a Yes and a No button. @code{def} controls which button the
mouse pointer should be on: 1 for Yes, 0 for No and any other value
causes the form to be shown so the mouse pointer is at the center of the
form. It returns whether the user pushed the Yes button. The user can
also press the @code{<Y>} key to mean Yes and the @code{<N>} key to mean
No.

If the question goodie is removed programmatically via
@code{@ref{fl_hide_question()}}, the default @code{def} as given in
@code{@ref{fl_show_question()}} is taken. If no default is set, 0 is
returned by @code{@ref{fl_show_question()}}. The following code
segment shows one way of using @code{@ref{fl_hide_question()}}
@example
void timeout_yesno(int id, void *data) @{
    fl_hide_question();
@}

...

fl_add_timeout(5000, timeout_yesno, 0);

/* show_question blocks until either timeouts or
   one of the buttons is pushed */

if (fl_show_question("Want to Quit ?", 1))
    exit(0);

/* no is selected, continue */

...  /* rest of the code *.
@end example
@noindent
In the above example, the user is given 5 seconds to think if he wants
to quit. If within the 5 seconds he can't decide what to do, the
timeout is triggered and @code{@ref{fl_show_question()}} returns 1.
If, on the other hand, he pushes the No button before the timeout
triggers, @code{@ref{fl_show_question()}} returns normally and
@code{@ref{fl_hide_question()}} becomes a no-op.

@findex fl_show_choice()
@anchor{fl_show_choice()}
@findex fl_show_choices()
@anchor{fl_show_choices()}
@findex fl_set_choices_shortcut()
@anchor{fl_set_choices_shortcut()}
@findex fl_hide_choice()
@anchor{fl_hide_choice()}
@example
int fl_show_choice(const char *s1, const char *s2, const char *s3,
                   int numb, const char *b1, const char *b2,
                   const char *b3, int def);

int fl_show_choices(const char *s, int numb,
                    const char *b1, const char *b2, const char *b3,
                    int def);

void fl_set_choices_shortcut(const char *s1, const char *s2,
                             const char *s3);

void fl_hide_choice(void);
@end example
@noindent
The first routine shows a message (up to three lines) with one, two or
three buttons. @code{numb} indicates the number of buttons. @code{b1},
@code{b2} and @code{b3} are the labels of the buttons. @code{def} can
be 1, 2 or 3, indicating the default choice. The second routine is
similar to the first except that the message is passed as a single
string with possible embedded newlines in it. Both routines return the
number of the button pressed (1, 2 or 3). The user can also press the
@code{<1>}, @code{<2>} or @code{<3>} key to indicate the first,
second, or third button. More mnemonic hotkeys can be defined using
the shortcut routine, @code{s1}, @code{s2} and @code{s3} are the
shortcuts to bind to the three buttons. If the choice goodie is
removed by @code{@ref{fl_hide_choice()}}, the default @code{def} is
returned.

To change the font used in all messages, use the following routine
@findex fl_set_goodies_font()
@anchor{fl_set_goodies_font()}
@example
void fl_set_goodies_font(int style, int size);
@end example

To obtain some text from the user, use the following routine
@findex fl_show_input()
@anchor{fl_show_input()}
@findex fl_hide_input()
@anchor{fl_hide_input()}
@example
const char *fl_show_input(const char *str1, const char *defstr);
void fl_hide_input(void);
@end example
@noindent
This shows a box with one line of message (indicated by @code{str1}),
and an input field into which the user can enter a string. @code{defstr}
is the default input string placed in the input box. In addition, three
buttons, labeled @code{Cancel}, @code{OK} and @code{Clear} respectively,
are added. The button labeled @code{Clear} deletes the string in the
input field. The routine returns the string in the input field when the
user presses the @code{OK} button or the @code{<Return>} key. The
function also returns when button @code{Cancel} is pressed. In this
case, instead of returning the text in the input field, @code{NULL} is
returned. This routine can be used to have the user provide all kinds of
textual input.

Removing the input field programmatically by calling
@code{@ref{fl_hide_input()}} results in @code{NULL} being returned by
@code{@ref{fl_show_input()}}, i.e., it's equivalent to pressing the
@code{Cancel} button.

A similar but simpler routine can also be used to obtain textual input
@findex fl_show_simple_input()
@anchor{fl_show_simple_input()}
@example
const char *fl_show_simple_input(const char *str1, const char *defstr);
@end example
@noindent
The form shown in this case only has the @code{OK} button. The example
program @file{goodies.c} shows you these goodies.


It is possible to change some of the built-in button labels via the
following resource function with proper resource names
@findex fl_set_resource()
@example
void fl_set_resource(const char *res_str, const char *value)
@end example
@noindent
To, for example, change the label of the @code{Dismiss} button to
@code{"Go"} in the alert form, code similar to the following can be
used after calling @code{@ref{fl_initialize()}} but before any use of
the alert goodie:
@example
fl_set_resource("flAlert.dismiss.label", "Go");
@end example

Currently the following goodies resources are supported:
@table @code
@item flAlert.title
The window title of the alert goodie
@item flAlert.dismiss.label
The label of the @code{Dismiss} button
@item flQuestion.yes.label
The label of the @code{Yes} button
@item flQuestion.no.label
The label of the @code{No} button
@item flQuestion.title
The window title of the Question goodie
@item flChoice.title
The window title of the Choice goodie
@item *.ok.label
The label of the @code{OK} button
@end table

Note that all goodies are shown with @code{FL_TRANSIENT} and not all
window managers decorate such forms with titles. Thus the title setting
in the above listing may not apply.


@node Command Log
@section Command Log

In a number of situations, a GUI is created specifically to make an
existing command-line oriented program easier to use. For stylistic
considerations, you probably don't want to have the output
(@code{stderr} and @code{stdout}) as a result of running the command
printed on the terminal. Rather you want to log all the messages to a
browser so the user can decide if and when to view the log. For this, a
goodie is available
@findex fl_exe_command()
@anchor{fl_exe_command()}
@example
long fl_exe_command(const char *cmd, int block);
@end example
@noindent
This function, similar to a @code{system(3)} call, forks a new process
that runs the command @code{cmd}, which must be a (null-terminated)
string containing a command line passed to the (sh) shell. The output
(both @code{stderr} and @code{stdout}) of @code{cmd} is logged into a
browser, which can be presented to the user when appropriate (see
below). The @code{block} argument is a flag indicating if the function
should wait for the child process to finish. If the argument
@code{block} is true (non-zero), the function waits until the command
@code{cmd} completes and then returns the exit status of the command
@code{cmd} (i.e., the status one gets form @code{wait()} or
@code{waitpid()}, so use @code{WEXITSTATUS()} on it if you want the
return or @code{exit()} value from the program started)). If the
argument @code{block} is false (0), the function returns immediately
without waiting for the command to finish. In this case, the function
returns the process ID of the child process or -1 if an error occured.

Unlike other goodies, @code{@ref{fl_exe_command()}} does not
deactivate other forms even in blockng mode. This means that the user
can interact with the GUI while @code{@ref{fl_exe_command()}} waits
for the child process to finish. If this is not desired, you can use
@code{@ref{fl_deactivate_all_forms()}} and
@code{@ref{fl_activate_all_forms()}} to wrap the function.

If @code{@ref{fl_exe_command()}} is called in non-blocking mode, the
following function should be called to clean up related processes and
resources before the caller exits (otherwise a zombie process may
result)
@findex fl_end_command()
@anchor{fl_end_command()}
@example
int fl_end_command(long pid);
@end example
@noindent
where @code{pid} is the process ID returned by
@code{@ref{fl_exe_command()}}. The function suspends the current
process and waits until the child process is completed, then it
returns the exit status of the child process or -1 if an error has
occurred.

There is another routine that will wait for all the child processes
initiated by @code{@ref{fl_exe_command()}} to complete
@findex fl_end_all_command()
@anchor{fl_end_all_command()}
@example
int fl_end_all_command(void)
@end example
@noindent
The function returns the status of the last child process.

You can also poll the status of a child process using the following
routine
@findex fl_check_command()
@anchor{fl_check_command()}
@example
int fl_check_command(long pid);
@end example
@noindent
where @code{pid} is the process ID returned by
@code{@ref{fl_exe_command()}}. The function returns the following
values: 0 if the child process is finished; 1 if the child process
still exists (running or stopped) and -1 if an error has occurred
inside the function.

If some interaction with the command being executed is desired, the
following functions may be more appropriate. These functions operates
almost exactly as the @code{popen(3)} and @code{pclose(3)} functions:
@findex fl_popen()
@anchor{fl_popen()}
@findex fl_pclose()
@anchor{fl_pclose()}
@example
FILE *fl_popen(const char *command, const char *type);
int fl_pclose(FILE *stream);
@end example
@noindent
The @code{@ref{fl_popen()}} function executes the command in a child
process, and logs the @code{stderr} messages into the command log.
Further, if type is @code{"w"}, @code{stdout} will also be logged into
the command browser. @code{@ref{fl_pclose()}} should be used to clean
up the child process.

To show or hide the logs of the command output, use the following functions
@findex fl_show_command_log()
@anchor{fl_show_command_log()}
@findex fl_hide_command_log()
@anchor{fl_hide_command_log()}
@example
int fl_show_command_log(int border);
void fl_hide_command_log(void);
@end example
@noindent
where @code{border} is the same as that used in
@code{@ref{fl_show_form()}}. These two routines can be called anytime
anywhere after @code{@ref{fl_initialize()}} has been invoked.

The command log is by default placed at the top-right corner of the
screen. To change the default placement, use the following routine
@findex fl_set_command_log_position()
@anchor{fl_set_command_log_position()}
@example
void fl_set_command_log_position(int x, int y);
@end example
@noindent
where @code{x} and @code{y} are the coordinates of the upper-left
corner of the form relative to the root window. The logging of the
output is accumulative, i.e., @code{@ref{fl_exe_command()}} does not
clear the browser. To clear the browser, use the following routine
@findex fl_clear_command_log()
@anchor{fl_clear_command_log()}
@example
void fl_clear_command_log(void);
@end example

It is possible to add arbitrary text to the command browser via the
following routine
@findex fl_addto_command_log()
@anchor{fl_addto_command_log()}
@findex fl_addto_command_log_f()
@anchor{fl_addto_command_log_f()}
@example
void fl_addto_command_log(const char *txt);
void fl_addto_command_log_f(const char *fmt, ...);
@end example
@noindent
where @code{txt} for @code{fl_addto_command_log()} is a string and
@code{fmt} for @code{fl_addto_command_log_f()} is a format string like
for @code{printf()} that gets expanded using the following arguments.
This string, with possible embedded newlines, gets added to the last
line of the browser using @code{@ref{fl_addto_browser_chars()}}.

Finally, there is a routine that can be used to obtain the GUI structure
of the command browser
@findex fl_get_command_log_fdstruct()
@anchor{fl_get_command_log_fdstruct()}
@example
typedef struct @{
    FL_FORM   * form;           /* the form */
    FL_OBJECT * browser;        /* the browser */
    FL_OBJECT * close_browser;  /* the close button */
    FL_OBJECT * clear_browser;  /* the clear button */
@} FD_CMDLOG;

FD_CMDLOG *fl_get_command_log_fdstruct(void);
@end example
@noindent
From the information returned the application program can change
various attributes of the command browser and its associated objects.
Note however, that you should not hide/show the form or free any members
of the structure.


@node Colormap
@section Colormap

In a number of applications the user has to select a color from the
colormap. For this a goody has been created. It shows the first 64
entries of the colormap. The user can scroll through the colormap to see
more entries. Once the user presses the mouse one of the entries the
corresponding index is returned and the colormap is removed from the
screen. To display the colormap use the routine
@findex fl_show_colormap()
@anchor{fl_show_colormap()}
@example
int fl_show_colormap(int oldcol);
@end example
@noindent
@code{oldcol} should be the current or default color. The user can
decide not to change this color by pressing the @code{Cancel} button in
the form. The procedure returns the index of the color selected (or the
index of the old color).


@node Color Chooser
@section Color Chooser

It's also not uncommon that an application lets the user use an
arbitrary color (not necessarily already in the colormap). The color
chooser shows a form that allows the user to select a new color
either using a HSV color wheel and a slider for the intensity or
by entering HSV or RGB values directly.

@ifhtml
@center @image{xforms_images/colchooser}
@end ifhtml
@ifnothtml
@center @image{xforms_images/colchooser,15cm}
@end ifnothtml

To show such a form call
@findex fl_show_color_chooser()
@anchor{fl_show_color_chooser()}
@example
int fl_show_color_chooser(const int *rgb_in, int * rgb_out);
@end example
@noindent
The first argument is a pointer to an array with the 3 RGB values
to use for the color to be displayed when the color chooser is
shown. If it is @code{NULL} white is used. The second argument is
another pointer to an array for the 3 RGB values of the selected
color to be returned on success. On success the function returns 1
(and sets the @code{rgb_out} array), but if the user clicked on the
"Cancel" button 0 gets returns (and the @code{rgb_out} array is not
modified.

Please keep in mind that there's no 1-to-1 mapping between the HSV
and RGB color space, there are a lot more HSV than RGB triples
and some colors don't even have a unique representation in HSV
space like, for example, all shades of grey, including white and
black.


@node File Selector
@section File Selector

The most extended predefined form is the file selector. It provides an
easy and interactive way to let the user select files. It is called as
follows:
@findex fl_show_fselector()
@anchor{fl_show_fselector()}
@example
const char *fl_show_fselector(const char *message, 
                              const char *directory,
                              const char *pattern,
                              const char *default);
@end example
@noindent
A form will be shown in which all files in directory @code{directory}
are listed that satisfy the pattern @code{pattern} (see Fig 6.1).
@code{pattern} can be any kind of regular expression, e.g.,
@code{[a-f]*.c}, which would list all files starting with a letter
between @code{a} and @code{f} and ending with @code{.c}. @code{default}
is the default file name. @code{message} is the message string placed at
the top of the form. The user can choose a file from the list given and
the function then returns a pointer to a static buffer that contains the
filename selected, or @code{NULL} if the @code{Cancel} button is pressed
(see below).

The user can also walk through the directory structure, either by
clicking on the box with the name of the currently displayed directory
to edit it manually, or by double-clicking on the name of a directory
(shown with a 'D' in front of it) shown in the list. If the directory
content changes while it is being displayed in the file selector the
@code{ReScan} button can be used to request a rescan of the directory.

@ifhtml
@center @image{xforms_images/fselect}
@end ifhtml
@ifnothtml
@center @image{xforms_images/fselect,7cm}
@end ifnothtml


In a typical application, once the file selector goodie is shown, it is
up to the user when the file selector should be dismissed by pushing
@code{Ready} or @code{Cancel} button. In some situations the application
may want to remove the file selector on it's own. To this end, the
following routine is available
@findex fl_hide_fselector()
@anchor{fl_hide_fselector()}
@example
void fl_hide_fselector(void);
@end example
@noindent
The effect of removing the file selector programmatically is the same as
pushing the @code{Cancel} button. There are total of
@tindex FL_MAX_FSELECTOR
@code{FL_MAX_FSELECTOR} (6) file selectors in the Forms Library with each
having its own current directory and content cache. All the file
selector functions documented manipulate the currently active file
selector, which can be set using the following routine
@findex fl_use_fselector()
@anchor{fl_use_fselector()}
@example
int fl_use_fselector(int n);
@end example
@noindent
where @code{n} is a number between 0 and @code{FL_MAX_FSELECTOR - 1}.

To change the font the file selector uses, the following routine can be
used:
@findex fl_set_fselector_fontsize()
@anchor{fl_set_fselector_fontsize()}
@findex fl_set_fselector_fontstyle()
@anchor{fl_set_fselector_fontstyle()}
@example
void fl_set_fselector_fontsize(int font_size);
void fl_set_fselector_fontstyle(int font_style);
@end example
@noindent
These routines change the font for all the objects on the form. It is
possible to change the font for some of the objects (e.g., browser only)
using @code{@ref{fl_get_fselector_fdstruct()}} explained later.

The window title of the file selector can be changed anytime using the
following routine
@findex fl_set_fselector_title()
@anchor{fl_set_fselector_title()}
@example
void fl_set_fselector_title(const char *title);
@end example

To force an update programmatically, call
@findex fl_invalidate_fselector_cache()
@anchor{fl_invalidate_fselector_cache()}
@example
void fl_invalidate_fselector_cache(void);
@end example
@noindent
before @code{@ref{fl_show_fselector()}}. Note that this call only
forces an update once, and on the directory that is to be browsed. To
disable caching altogether, the following routine can be used:
@findex fl_disable_fselector_cache()
@anchor{fl_disable_fselector_cache()}
@example
void fl_disable_fselector_cache(int yes);
@end example
@noindent
A false (0) parameter (re)enables directory caching.

The user can also change the pattern by clicking the mouse on top of it
it. Note that directories are shown independent of whether they satisfy
the pattern. He can also type in a file name directly.

Complete keyboard navigation is built-in. E.g., you can use
@code{<Alt>d} to change the directory instead of using the mouse.

When the user is satisfied, i.e., found the correct directory and
indicated the file name required, he can press the button labeled
@code{Ready} or press the @code{<Return>} key. He can also double click
on the file name in the browser. The full path to the filename is
returned by the procedure. If the user presses the @code{Cancel} button
@code{NULL} is returned.

It is also possible to set a callback routine so that whenever the user
double clicks on a filename, instead of returning the filename, the
callback routine is invoked with the filename as the argument. To set
such a callback, use the following routine
@findex fl_set_fselector_callback()
@anchor{fl_set_fselector_callback()}
@example
void fl_set_fselector_callback(int (*callback)(const char *, void *),
                               void *user_data);
@end example
@noindent
where the second argument of the callback is the @code{user data}. The
return value of the callback function is currently not used. Note that
the behavior of the file selector is slightly different when a callback
is present. Without the callback, a file selector is always modal.

Please note that when a file selector has a callback installed the
field for manually entering a file name isn't shown.

The placement of the file selector is by default centered on the screen,
which can be changed by the following routine
@findex fl_set_fselector_placement()
@anchor{fl_set_fselector_placement()}
@example
void fl_set_fselector_placement(int place);
@end example
@noindent
where place is the placement request same as in
@code{@ref{fl_show_form()}}. The default is @code{FL_PLACE_CENTER |
FL_FREE_SIZE}.

By default, an fselector is displayed with transient property set. To
change the default, use the following routine
@findex fl_set_fselector_border()
@anchor{fl_set_fselector_border()}
@example
void fl_set_fselector_border(int border);
@end example
@noindent
The @code{border} request by this function is the same as in
@code{@ref{fl_show_form()}}, but @code{FL_NOBORDER} is ignored.

If the arguments @code{directory}, @code{pattern} or @code{default}
passed to @code{@ref{fl_show_form()}} are empty strings or
@code{NULL}, the previous value is used (with some reasonable defaults
getting used when this happens the first time). Thus the file selector
"remembers" all the settings the selector had last time. The
application program can figure out the directory, pattern and file
name (without the path) after the user changed them using the routines
@findex fl_get_directory()
@anchor{fl_get_directory()}
@findex fl_get_pattern()
@anchor{fl_get_pattern()}
@findex fl_get_filename()
@anchor{fl_get_filename()}
@example
const char *fl_get_directory(void);
const char *fl_get_pattern(void);
const char *fl_get_filename(void);
@end example

It is also possible to programatically set new values for the default
directory and pattern by using the functions
@findex fl_set_directory()
@anchor{fl_set_directory()}
@findex fl_set_pattern()
@anchor{fl_set_pattern()}
@example
int fl_set_directory( const char * dir );
void fl_set_pattern( const char * pattern );
@end example
@noindent
@code{@ref{fl_set_directory()}} returns 0 on success and 1 on failure,
either because the argument was a @code{NULL} pointer or not a valid
directory.

There are other routines that make the fselector more flexible. The most
important of which is the ability to accommodate up to three application
specific button:
@findex fl_add_fselector_appbutton()
@anchor{fl_add_fselector_appbutton()}
@example
void fl_add_fselector_appbutton(const char *label,
                                void (*callback)(void *),
                                 void *data);
@end example
@noindent
The argument @code{data} is passed to the callback. Whenever this
application specific button is pushed, the callback function is invoked.

To remove an application specific button, use the following routine
@findex fl_remove_fselector_appbutton()
@anchor{fl_remove_fselector_appbutton()}
@example
void fl_remove_fselector_appbutton(const char *label);
@end example

Within the callback function, in addition to using the routines
mentioned above, the following routines can be used:
@findex fl_refresh_fselector()
@anchor{fl_refresh_fselector()}
@example
void fl_refresh_fselector(void);
@end example
@noindent
This function causes the file selector to re-scan the current directory
and to list all entries in it.


If, for whatever reasons, there is a need to get the fselector's form
the following routine can be used:
@findex fl_get_fselector_form()
@anchor{fl_get_fselector_form()}
@example
FL_FORM *fl_get_fselector_form(void);
@end example
@noindent
See @file{fbrowse.c} for the use of the file selector.

Although discouraged, it is recognized that direct access to the
individual objects of a fselector's form maybe necessary. To this end,
the following routine exists
@tindex FD_FSELECTOR
@findex fl_get_fselector_fdstruct()
@anchor{fl_get_fselector_fdstruct()}
@example
typedef struct @{
    FL_FORM   * fselect;
    void      * vdata;
    char      * cdata;
    long        ldata;
    FL_OBJECT * browser,
              * input,
              * prompt,
              * resbutt;
    FL_OBJECT * patbutt,
              * dirbutt,
              * cancel,
              * ready;
    FL_OBJECT * dirlabel,
              * patlabel;
    FL_OBJECT * appbutt[3];
@} FD_FSELECTOR;

FD_FSELECTOR *fl_get_fselector_fdstruct(void);
@end example


You can, for example, change the default label strings of various
buttons via members of the @code{FD_FSELECTOR} structure:
@example
FD_FSELECTOR *fs = fl_get_fselector_fdstruct();

fl_set_object_label(fs->ready, "Go !");
fl_fit_object_label(fs->ready, 1, 1);
@end example
@noindent
Since the return value of @code{@ref{fl_get_fselector_fdstruct()}} is
a pointer to an internal structures, the members of this structure
should not be modified.

In the listing of files in a directory special files are marked with a
prefix in the browser (for example, @code{D} for directories, @code{p}
for pipes etc.). To change the prefix, use the following routine
@findex fl_set_fselector_filetype_marker()
@anchor{fl_set_fselector_filetype_marker()}
@example
void fl_set_fselector_filetype_marker(int dir,
                                      int fifo,
                                      int socket,
                                      int cdev,
                                      int bdev);
@end example
@noindent
where @code{dir} is the marker character for directories, @code{fifo}
the marker for pipes and FIFOs, @code{socket} the marker for sockets,
@code{cdev} the marker for character device files and, finally,
@code{bdev} the marker character for block device files.

Although file systems under Unix are similar, they are not identical. In
the implementation of the file selector, the subtle differences in
directory structures are isolated and conditionally compiled so an
apparent uniform interface to the underlying directory structure is
achieved.

To facilitate alternative implementations of file selectors, the
following (internal) routines can be freely used:

To get a directory listing, the following routine can be used
@tindex FL_Dirlist
@findex fl_get_dirlist()
@anchor{fl_get_dirlist()}
@example
const FL_Dirlist *fl_get_dirlist(const char *dirname,
                                 const char *pattern,
                                 int *nfiles, int rescan);
@end example
@noindent
where @code{dirname} is the directory name; @code{pattern} is a regular
expression that is used to filter the directory entries; @code{nfiles}
on return is the total number of entries in directory @code{dirname}
that match the pattern specified by @code{pattern} (not exactly true,
see below). The function returns the address of an array of type
@code{FL_Dirlist} with @code{nfiles} if successful and @code{NULL}
otherwise. By default, directory entries are cached, i.e. when called
with the same arguments for @code{dirname} and @code{pattern} again
the exact same list is usually returned (and no checks are done if
any files have been added or removed in the mean time). By passing
the function a true (non-zero) value for the @code{rescan} argument
requests a re-read, whch then returns

@code{FL_Dirlist} is a structure defined as follows
@example
typedef struct @{
    char          * name;        /* file name */
    int             type;        /* file type */
    long            dl_mtime;    /* file modification time */
    unsigned long   dl_size;     /* file size in bytes */
@} FL_Dirlist;
@end example
@noindent
where @code{type} is one of the following file types
@table @code
@tindex FT_REG
@item FT_REG
a regular file (note: in older versions instead
@tindex FT_FILE
@code{FT_FILE} was used!)
@tindex FT_DIR
@item FT_DIR
a directory
@tindex FT_SOCK
@item FT_SOCK
a socket
@tindex FT_FIFO
@item FT_FIFO
a pipe or FIFO
@tindex FT_LINK
@item FT_LINK
a symbolic link
@tindex FT_BLK
@item FT_BLK
a block device
@tindex FT_CHR
@item FT_CHR
a character device
@item FT_OTHER
?
@end table

Once you're done with the directory list obtained from
@code{@ref{fl_get_dirlist()}} you must free it, using the following
call:
@findex fl_free_dirlist()
@anchor{fl_free_dirlist()}
@example
void fl_free_dirlist(const FL_Dirlist *dl);
@end example
@noindent
Of course, the argument passed to @code{@ref{fl_free_dirlist()}} must
be a pointer you obtained from a call of @code{@ref{fl_get_dirlist()}}.
See demo program @file{dirlist.c} for an example use of these functions.

Per default not all types of files are returned by
@code{@ref{fl_get_dirlist()}}. The specific rules for which
types of file are returned are controlled by an additional filter
after the pattern filter. It has the type
@example
int default_filter(const char *name, int type);
@end example
@noindent
and is called for each entry found in the directory that matched the
pattern. This filter function should return true (non-zero) if the
entry is to be included in the directory list. The default filter is
similar to the following
@example
int ffilter(const char *name, int type) @{
    return type == FT_DIR || type == FT_FILE || type == FT_LINK;
@}
@end example
@noindent
i.e., per default only directories, normal files and symbolic links
are shown (the first argument of the function, the file name, isn't
used by the default filter).

To change the default filter, use the following routine
@tindex FL_DIRLIST_FILTER
@findex fl_set_dirlist_filter()
@anchor{fl_set_dirlist_filter()}
@example
typedef int (*FL_DIRLIST_FILTER)(const char *, int);
FL_DIRLIST_FILTER fl_set_dirlist_filter(FL_DIRLIST_FILTER filter);
@end example

As noted before, directories are by default not subject to filtering.
If, for any reason, it is desirable to filter also directories, use the
following routine with a true flag
@findex fl_set_dirlist_filterdir()
@anchor{fl_set_dirlist_filterdir()}
@example
int fl_set_dirlist_filterdir(int flag);
@end example
@noindent
The function returns the old setting. Since there is only one filter
active at any time in XForms, changing the filter affects all subsequent
uses of file browsers.

By default, the files returned are sorted alphabetically. You can change
the default sorting using the following routine:
@findex fl_set_dirlist_sort()
@anchor{fl_set_dirlist_sort()}
@example
int fl_set_dirlist_sort(int method);
@end example
@noindent
where @code{method} can be one of the following
@table @code
@tindex FL_NONE
@item FL_NONE
Don't sort the entries
@tindex FL_ALPHASORT
@item FL_ALPHASORT
Sort the entries in alphabetic order - this is the default
@tindex FL_RALPHASORT
@item FL_RALPHASORT
Sort the entries in reverse alphabetic order
@tindex FL_MTIMESORT
@item FL_MTIMESORT
Sort the entries according to the modification time
@tindex FL_RMTIMESORT
@item FL_RMTIMESORT
Sort the entries according to the modification time, but reverse the
order, i.e., latest first.
@tindex FL_SIZESORT
@item FL_SIZESORT
Sort the entries in increasing size order
@tindex FL_RSIZESORT
@item FL_RSIZESORT
Sort the entries in decreasing size order
@tindex FL_CASEALPHASORT
@item FL_CASEALPHASORT
Sort the entries in alphabetic order with no regard to case
@tindex FL_RCASEALPHASORT
@item FL_RCASEALPHASORT
Sort the entries in reverse alphabetic order with no regard to case.
@end table

The function returns the old sort method. For directories having large
numbers of files, reading the directory can take quite a long time due
to sorting and filtering. Electing not to sort and (to a lesser degree)
not to filter the directory entries (by setting the filter to
@code{NULL}) can speed up the directory reading considerably.
