@node Part III Container Objects
@chapter Container Objects

@ifnottex

@menu
* Folder Object:          Folder Object
* FormBrowser Object:     FormBrowser Object
@end menu

@end ifnottex


@node Folder Object
@section Folder Object

A tabbed folder is a special container class that is capable of
holding multiple groups of objects (folders) to maximize the
utilization of the screen real estate. Each folder has its own tab the
user can click on to call up a specific folder from which option can
be selected.

@ifhtml
@center @image{images/folders}
@end ifhtml
@ifnothtml
@center @image{images/folders,10cm}
@end ifnothtml


@ifnottex

@menu
* Adding Folder Objects:     Adding Folder Objects
* Folder Types:              Folder Types
* Folder Interaction:        Folder Interaction
* Other Folder Routines:     Other Folder Routines
* Remarks:                   Folder Remarks
@end menu

@end ifnottex


@node Adding Folder Objects
@subsection Adding Folder Objects

To add a tabbed folder to a form use the routine
@findex fl_add_tabfolder()
@anchor{fl_add_tabfolder()}
@example
FL_OBJECT *fl_add_tabfolder(int type, FL_Coord x, FL_Coord y,
                            FL_Coord w, FL_Coord h, const char *label);
@end example
@noindent
The geometry indicated by @code{x}, @code{y}, @code{w}, and @code{h}
is the total area of the tabbed folders, including the area used for
the tab riders.

@node Folder Types
@subsection Folder Types

The following types are available:
@table @code
@tindex FL_TOP_TABFOLDER
@anchor{FL_TOP_TABFOLDER}
@item FL_TOP_TABFOLDER
Tabs on top of the folders.

@tindex FL_BOTTOM_TABFOLDER
@anchor{FL_BOTTOM_TABFOLDER}
@item FL_BOTTOM_TABFOLDER
Tabs at the bottom of the folders.

@tindex FL_LEFT_TABFOLDER
@anchor{FL_LEFT_TABFOLDER}
@item FL_LEFT_TABFOLDER
Tabs on the left of the folders (not yet functional).

@tindex FL_RIGHT_TABFOLDER
@anchor{FL_RIGHT_TABFOLDER}
@item FL_RIGHT_TABFOLDER
Tabs on the right of the folders (not yet functional).
@end table


@node Folder Interaction
@subsection Folder Interaction

The folders displayed by the tabbed folder class are simply regular
forms (of type @code{FL_FORM}), which in turn contain objects. Each
folder is associated with a name (shown on the tab rider). The folder
interacts with the user just like any other form. Different from other
top-level forms is that only one folder is active at any time. The
user selects different folders by clicking on the tab rider associated
with a folder.

To set up when either the application is notified about events of the
tabfolder or the tabfolders callback is invoked (if installed) use
@example
void fl_set_object_return(FL_OBJECT *obj, unsigned int when);
@end example
@noindent
where the @code{when} argument can be one of
@table @code
@item @ref{FL_RETURN_NONE}
Never report or invoke callback even if the selected folder has been
changed.

@item @ref{FL_RETURN_CHANGED}
@item @ref{FL_RETURN_END_CHANGED}
Result in a notification whenever the a different folder from the
currently active one has been selected (this is the default for the
tabfolder object).

@item @ref{FL_RETURN_END}
@item @ref{FL_RETURN_ALWAYS}
Notify when either a new or the already active folder has been
selected.
@end table

In older releases of the library on changes of the selected folder
only a callback for the older was executed (if one was installed),
but the folder object wasn't reported back via e.g.@:
@code{@ref{fl_do_forms()}} etc. This has changed with version 1.0.92.
To get the old behaviour you have to build XForms with the
@code{--enable-bwc-bs-hack} being set.



To find out which folder is currently active, the following routines
the tab riders are available
@findex fl_get_active_folder()
@anchor{fl_get_active_folder()}
@findex fl_get_active_folder_number()
@anchor{fl_get_active_folder_number()}
@findex fl_get_active_folder_name()
@anchor{fl_get_active_folder_name()}
@example 
FL_FORM *fl_get_active_folder(FL_OBJECT *obj);
int fl_get_active_folder_number(FL_OBJECT *obj);
const char *fl_get_active_folder_name(FL_OBJECT *obj);
@end example
@noindent
All three functions essentially perform the same task, i.e.@: return a
handle of the active folder, but the kind of handle returned is
different. The first function returns the form associated with the
folder, the second function the folder sequence number starting from 1
on the left, and the third the folder name. Depending on the
application setup, one routine might be more convenient than the other
two.

To find out what the last active folder is (which may be of greater
interest than the currently active one) the following can be used:
@findex fl_get_folder()
@anchor{fl_get_folder()}
@findex fl_get_folder_number()
@anchor{fl_get_folder_number()}
@findex fl_get_folder_name()
@anchor{fl_get_folder_name()}
@example
FL_FORM *fl_get_folder(FL_OBJECT *obj)
int fl_get_folder_number(FL_OBJECT *obj)
const char *fl_get_folder_name(FL_OBJECT *obj)
@end example
@noindent
Again, depending on the application, one might prefer one routine to
the other two.


@node Other Folder Routines
@subsection Other Folder Routines

To populate a tabbed folder, use the following routine
@findex fl_addto_tabfolder()
@anchor{fl_addto_tabfolder()}
@example
FL_OBJECT *fl_addto_tabfolder(FL_OBJECT *obj, const char *tab_name,
                              FL_FORM *folder)
@end example
@noindent
where @code{tab_name} is a string (with possible embedded newlines in
it) indicating the text of the tab rider and @code{folder} is a
regular form created between calls of @code{@ref{fl_bgn_form()}} and
@code{@ref{fl_end_form()}}. Only the pointer to the form is required.
This means that the application program should not destroy a form that
has been added to a tabbed folder. The function returns the folder tab
object, which is an object of class @code{FL_BUTTON}. The initial
object color, label color, and other attributes (gravities, for
example) of the tab button are inherited from the tabbed folder object
@code{obj} and the location and size of the tab are determined
automatically. You can change the attributes of the returned object
just like any other objects, but not all possibilities result in a
pleasing appearance. Note that although there is no specific
requirement of what the backface of the folder/form should be, a
boxtype other than @code{FL_FLAT_BOX} or @code{FL_NO_BOX} may not look
nice. If the backface of the form is of @code{FL_FLAT_BOX} the
associated tab will take on the color of the backface when activated.

One thing to note is that each tab must have its own form, i.e.@: you
should not associate the same form with two different tabs. However,
you can create copies of a form and use these copies.

To access the individual forms on the tabfolder, e.g.@: in order to
modify something on it, use the following routines
@findex fl_get_tabfolder_folder_bynumber()
@anchor{fl_get_tabfolder_folder_bynumber()}
@findex fl_get_tabfolder_folder_byname()
@anchor{fl_get_tabfolder_folder_byname()}
@example
FL_FORM *fl_get_tabfolder_folder_bynumber(FL_OBJECT *obj, int num);
FL_FORM *fl_get_tabfolder_folder_byname(FL_OBJECT *obj,
                                        const char *name);
@end example
@noindent
where @code{num} and @code{name} are the sequence number (the first
tab on the left has a sequence number 1, the second 2 etc) and the tab
name, respectively. The functions return the form associated with the
number or the name. If the requested number or name is invalid,
@code{NULL} is returned.

If there are more tabs than that can be shown, the right-most tab will
be shown as "broken". Clicking on the "broken" tab scrolls the tab to
the right one per each click. To scroll to the left (if there are tabs
scrolled off screen from the left), clicking on the first tab scrolls
right.

Although a regular form (top-level) and a form used as a folder behave
almost identically, there are some differences. In a top-level form,
objects that do not have callbacks bound to them will be returned,
when their states change, to the application program via
@code{@ref{fl_do_forms()}} or @code{@ref{fl_check_forms()}}. When a
form is used as a folder, objects that do not have a callback are
ignored even when their states changes. The reason for this behavior
is that presumably the application does not care while the changes take
place and they only become relevant when the the folder is switched
off and at that time the application program can decide what to do
with these objects' states (apply or ignore for example). If immediate
reaction is desired, just use callback functions for these objects.

To obtain the number of folders in the tabfolder, the following
routine can be used
@findex fl_get_tabfolder_numfolders()
@anchor{fl_get_tabfolder_numfolders()}
@example
int fl_get_tabfolder_numfolders(FL_OBJECT *obj);
@end example

To remove a folder, the following routine is available
@findex fl_delete_folder()
@anchor{fl_delete_folder()}
@findex fl_delete_folder_bynumber()
@anchor{fl_delete_folder_bynumber()}
@findex fl_delete_folder_byname()
@anchor{fl_delete_folder_byname()}
@example
void fl_delete_folder(FL_OBJECT *obj, FL_FORM *folder);
void fl_delete_folder_bynumber(FL_OBJECT *obj, int num);
void fl_delete_folder_byname(FL_OBJECT *obj, const char *name);
@end example
@noindent
Note that after deletion, the number of folders in the tabfolder as
well as the sequence numbers are updated. This means if you want to
delete all folders after the second folder, you can do that by
deleting the third folder repeatedly.

The application program can select which folder to show by using the
following routines
@findex fl_set_folder()
@anchor{fl_set_folder()}
@findex fl_set_folder_bynumber()
@anchor{fl_set_folder_bynumber()}
@findex fl_set_folder_byname()
@anchor{fl_set_folder_byname()}
@example
void fl_set_folder(FL_OBJECT *obj, FL_FORM *folder);
void fl_set_folder_bynumber(FL_OBJECT *obj, int num);
void fl_set_folder_byname(FL_OBJECT *obj, const char *tab);
@end example

Since the area occupied by the tabbed folder contains the space for
tabs, the following routine is available to obtain the actual folder
size
@findex fl_get_folder_area()
@anchor{fl_get_folder_area()}
@example
void fl_get_folder_area(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y,
                        FL_OBJECT *w, FL_OBJECT *h)
@end example
@noindent
where @code{x} and @code{y} are relative to the (top-level) form the
tabbed folder belongs to. The size information may be useful for
resizing the individual forms that has to go into the tabbed folder.
Note that the folder area may not be constant depending on the current
tabs (For example, adding a multi-line tab will reduce the area for
the folders).

Since tab size can vary depending on monitor/font resolutions, it is
in general not possible to design the forms (folders) so they fit
exactly into the folder area. To dynamically adjust the sizes of the
folders so they fit, the following routine is available
@findex fl_set_tabfolder_autofit()
@anchor{fl_set_tabfolder_autofit()}
@example
int fl_set_tabfolder_autofit(FL_OBJECT *obj, int how);
@end example
@noindent
where @code{how} can be one of the following constants:
@table @code
@tindex FL_NO
@item FL_NO
Do not scale the form.
@tindex FL_FIT
@item FL_FIT
Always scale the form.
@tindex FL_ENLARGE_ONLY
@item FL_ENLARGE_ONLY
Scale the form only if it is smaller than the folder area.
@end table
@noindent
The function returns the old setting.


@node Folder Remarks
@subsection Remarks

By default, the tab for each folder is drawn with a corner of 3 pixels
so it appears to be a trapezoid rather than a square. To change the
appearance of the tabs, you can adjust the corner pixels using the
following routine
@findex fl_set_default_tabfolder_corner()
@anchor{fl_set_default_tabfolder_corner()}
@example
int fl_set_default_tabfolder_corner(int n);
@end example
@noindent
where @code{n} is the number of corner pixels. A value of 1 or 0 makes
the tabs appear to be squarish. The function returns the old value.

A tabbed folder is a composite object consisting of a canvas and
several foldertab buttons. Each individual form is shown inside the
canvas. Folder switching is accomplished by some internal callbacks
bound to the foldertab button. Should the application change the
callback functions of the foldertab buttons, these new callback
functions must take the responsibility of switching the active folder.

@code{fl_free_object(tabfolder)} does not free the individual forms
that make up the tabfolder.

See demo program @file{folder.c} for an example use of tabbed folder
class.

A nested tabfolder might not work correctly at the moment.


@node FormBrowser Object
@section FormBrowser Object

A form browser is another container class that is capable of holding
multiple forms, the height of which in aggregate may exceed the screen
height. The form browser also works obviously for a single form that
has a height that is larger than the screen height.

This object class was developed with contributed code from Steve
Lamont of UCSD and the National Center for Microscopy and Imaging
Research (@email{spl@@ucsd.edu}).

@ifnottex

@menu
* Adding FormBrowser Objects:    Adding FormBrowser Objects
* FormBrowser Types:             FormBrowser Types
* FormBrowser Interaction:       FormBrowser Interaction
* Other FormBrowser Routines:    Other FormBrowser Routines
* Remarks:                       FormBrowser Remarks
@end menu

@end ifnottex


@node Adding FormBrowser Objects
@subsection Adding FormBrowser Objects

Adding an object To add a formbrowser object to a form use the routine
@findex fl_add_formbrowser()
@anchor{fl_add_formbrowser()}
@example
FL_OBJECT *fl_add_formbrowser(int type, FL_Coord x, FL_Coord y,
                              FL_Coord w, FL_Coord h,
                              const char *label);
@end example
@noindent
The geometry indicated by @code{x}, @code{y}, @code{w} and @code{h} is
the total area of the formbrowser, including scrollbars.


@node FormBrowser Types
@subsection FormBrowser Types

There's only a single type of formbrowser available, the
@tindex FL_NORMAL_FORMBROWSER
@code{FL_NORMAL_FORMBROWSER}.


@node FormBrowser Interaction
@subsection FormBrowser Interaction

Once a formbrowser is populated with forms, you can scroll the forms
with the scrollbars and interact with any of the forms. All objects on
the forms act, for the most part, the same way as they would if they
were on separate forms, i.e., if there are callback functions bound to
the objects, they will be invoked by the main loop when the states of
the objects change. However, objects on the form that do not have
callbacks bound to them will not be returned by
@code{@ref{fl_do_forms()}} or @code{@ref{fl_check_forms()}}.

Your application can be notified about changes of the scrollbars of
the formbrowser. To set up under which conditions the application is
notified or the formbrowsers callback is invoked (if installed) use
@example
void fl_set_object_return(FL_OBJECT *obj, unsigned int when);
@end example
@noindent
where the @code{when} argument can be one of
@table @code
@item @ref{FL_RETURN_NONE}
Never report or invoke callback (this is the default for the
formbrowser object)

@item @ref{FL_RETURN_CHANGED}
Result in a notification whenever the position of one of the
scrollbars has changed.

@item @ref{FL_RETURN_END_CHANGED}
Notification is sent if the position of a scrollbar has changed and the
mouse button has been released.

@item @ref{FL_RETURN_END}
Notification on release of the mouse button.

@item @ref{FL_RETURN_ALWAYS}
Notify if the position of a scrollbar has changed or the mouse
button has been released.
@end table


@node Other FormBrowser Routines
@subsection Other FormBrowser Routines

To populate a formbrowser, use the following routine
@findex fl_addto_formbrowser()
@anchor{fl_addto_formbrowser()}
@example
int fl_addto_formbrowser(FL_OBJECT *obj, FL_FORM *form);
@end example
@noindent
where @code{form} is a pointer to a regular form created between calls
of @code{@ref{fl_bgn_form()}} and @code{@ref{fl_end_form()}}. Only the
form pointer is passed to the function, which means that the form
should be valid for the duration of the formbrowser and the
application program should not destroy a form that is added to a
formbrowser before deleting the form from the formbrowser first. The
function returns the total number of forms in the formbrowser. Note
that although there is no specific requirement on what the backface of
the form should be, not all boxtypes look nice.

The form so added is appended to the list of forms that are already in
the formbrowser. You can also use the following routine to obtain the
total number of forms in a formbrowser
@findex fl_get_formbrowser_numforms()
@anchor{fl_get_formbrowser_numforms()}
@example
int fl_get_formbrowser_numforms(FL_OBJECT *formbrowser);
@end example

Although a regular form (top-level) and a form used inside a
formbrowser behave almost identically, there are some differences. In
a top-level form, objects that do not have callbacks bound to them
will be returned to the application program when their states change
via @code{@ref{fl_do_forms()}} or @code{@ref{fl_check_forms()}}. When
a form is used as member of a formbrowser those objects that do not
have callbacks are ignored even when their states change.

To remove a form from the formbrowser, the following routine is available
@findex fl_delete_formbrowser()
@anchor{fl_delete_formbrowser()}
@findex fl_delete_formbrowser_bynumber()
@anchor{fl_delete_formbrowser_bynumber()}
@example
int fl_delete_formbrowser(FL_OBJECT *obj, FL_FORM *form);
FL_FORM* fl_delete_formbrowser_bynumber(FL_OBJECT *obj, int num);
@end example
@noindent
In the first function you specify the form to be removed from the
formbrowser by a pointer to the form. The function returns the current
(after deletion) number of forms in the formbrowser if the form is
removed successfully else it returns -1.

In the second function, you indicate the form to be removed with a
sequence number, an integer between 1 and the number of forms in the
browser. The sequence number is basically the order in which forms
were added to the formbrowser. After a form is removed, the sequence
numbers are re-adjusted so they are always consecutive. The function
returns @code{NULL} if the @code{num} is invalid, otherwise it returns
address of the form that is removed.

To replace a form in formbrowser, the following routine is available
@findex fl_replace_formbrowser()
@anchor{fl_replace_formbrowser()}
@example
FL_FORM *fl_replace_formbrowser(FL_OBJECT *obj, int num,
                                FL_FORM *form);
@end example
@noindent
where @code{num} is the sequence number of the form that is to be
replaced by @code{form}. For example, to replace the first form in the
browser with a different form, you should use 1 for @code{num}. The
function returns the form that has been replaced on success, otherwise
@code{NULL} is returned.

You can also insert a form into a formbrowser at arbitrary locations
using the following routine
@findex fl_insert_formbrowser()
@anchor{fl_insert_formbrowser()}
@example
int fl_insert_formbrowser(FL_OBJECT *obj, int num, FL_FORM *form);
@end example
@noindent
where @code{num} is the sequence number before which the new form @code{form}
is to be inserted into the formbrowser. If successful the function
returns the number of forms in the formbrowser, otherwise -1.

To find out the sequence number of a particular form, the following
routine is available
@findex fl_find_formbrowser_form_number()
@anchor{fl_find_formbrowser_form_number()}
@example
int fl_find_formbrowser_form_number(FL_OBJECT *obj, FL_FORM *form);
@end example

To obtain the form handle from the sequence number, use the following
routine
@findex fl_get_formbrowser_form()
@anchor{fl_get_formbrowser_form()}
@example
int fl_get_formbrowser_form(FL_OBJECT *obj, int num);
@end example

By default, if the size of the forms exceeds the size of the
formbrowser, scrollbars are added automatically. You can use the
following routines to control the scrollbars
@findex fl_set_formbrowser_hscrollbar()
@anchor{fl_set_formbrowser_hscrollbar()}
@findex fl_set_formbrowser_vscrollbar()
@anchor{fl_set_formbrowser_vscrollbar()}
@example
void fl_set_formbrowser_hscrollbar(FL_OBJECT *obj, int how);
void fl_set_formbrowser_vscrollbar(FL_OBJECT *obj, int how);
@end example
@noindent
where @code{how} can be one of the following
@table @code
@tindex FL_ON
@item FL_ON
Always on.
@tindex FL_OFF
@item FL_OFF
Always off.
@tindex FL_AUTO
@item FL_AUTO
On when needed. This is the default.
@end table

The vertical scrollbar by default scrolls a fixed number of pixels. To
change it so each action of the scrollbar scrolls to the next forms,
the following routine is available
@findex fl_set_formbrowser_scroll()
@anchor{fl_set_formbrowser_scroll()}
@example
void fl_set_formbrowser_scroll(FL_OBJECT *how)
@end example
@noindent
where @code{how} can be one of the following
@table @code
@tindex FL_SMOOTH_SCROLL
@anchor{FL_SMOOTH_SCROLL}
@item FL_SMOOTH_SCROLL
The default.

@tindex FL_JUMP_SCROLL
@anchor{FL_JUMP_SCROLL}
@item FL_JUMP_SCROLL
Scrolls in form increments.
@end table

To obtain the form that is currently the first form in the
formbrowser visible to the user, the following can be used
@findex fl_get_formbrowser_topform()
@anchor{fl_get_formbrowser_topform()}
@example
FL_FORM *fl_get_formbrowser_topform(FL_OBJECT *obj);
@end example

You can also set which form to show by setting the top form using
the following routine
@findex fl_set_formbrowser_topform()
@anchor{fl_set_formbrowser_topform()}
@findex fl_set_formbrowser_topform_bynumber()
@anchor{fl_set_formbrowser_topform_bynumber()}
@example
int fl_set_formbrowser_topform(FL_OBJECT *obj, FL_FORM *form);
FL_FORM* fl_set_formbrowser_topform_bynumber(FL_OBJECT *obj, int num);
@end example
@noindent
The first function returns the sequence number of the form and
the second function returns the form with sequence number @code{num}.

Since the area occupied by the formbrowser contains the space for the
scrollbars, the following routine is available to obtain the actual
size of the forms area
@findex fl_get_formbrowser_area()
@anchor{fl_get_formbrowser_area()}
@example
void fl_get_formbrowser_area(FL_OBJECT *obj, int *x, int *y,
                             int *w, int *h);
@end example
@noindent
where @code{x} and @code{y} are relative to the (top-level) form the
formbrowser belongs to.

To programatically scroll within a formbrowser in horizontal
and vertical direction, the following routines are available
@findex fl_set_formbrowser_xoffset()
@anchor{fl_set_formbrowser_xoffset()}
@findex fl_set_formbrowser_yoffset()
@anchor{fl_set_formbrowser_yoffset()}
@example
int fl_set_formbrowser_xoffset(FL_OBJECT *obj, int offset);
int fl_set_formbrowser_yoffset(FL_OBJECT *obj, int offset);
@end example
@noindent
where @code{offset} is a positive number, measuring in pixels the
offset from the the natural position from the left and the top,
respectively. In other words, 0 indicates the natural position of the
content within the formbrowser. An x-offset of 10 means the content is
scrolled 10 pixels to the leftleft. Similarly a y-offset of 10 means
the content is scrolled by 10 pixels upwards.

To obtain the current offsets, use the following routines
@findex fl_get_formbrowser_xoffset()
@anchor{fl_get_formbrowser_xoffset()}
@findex fl_get_formbrowser_yoffset()
@anchor{fl_get_formbrowser_yoffset()}
@example
int fl_get_formbrowser_xoffset(FL_OBJECT *obj);
int fl_get_formbrowser_yoffset(FL_OBJECT *obj);
@end example


@node FormBrowser Remarks
@subsection Remarks

A call of @code{fl_free_object(formbrowser)} does not free the
individual forms, it only frees the formbrowser object itself.

See demo program @file{formbrowser.c} for an example use of
formbrowser class. A nested formbrowser might not work correctly at
the moment.
