Container Objects
*****************

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.

Adding Folder Objects
---------------------

To add a tabbed folder to a form use the routine

     FL_OBJECT *fl_add_tabfolder(int type, FL_Coord x, FL_Coord y,
                                 FL_Coord w, FL_Coord h, const char *label);

The geometry indicated by x, y, w, and h is the total area of the tabbed folders, including the area used for the tab riders.

Folder Types
------------

The following types are available:

FL_TOP_TABFOLDER
     Tabs on top of the folders.

FL_BOTTOM_TABFOLDER
     Tabs at the bottom of the folders.

Folder Interaction
------------------

The folders displayed by the tabbed folder class are simply regular forms (of type 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 the application is notified about events of the tabfolder or the tabfolders callback is invoked (if installed) use

     void fl_set_object_return(FL_OBJECT *obj, unsigned int when);

where the when argument can be one of

FL_RETURN_NONE
     Never report or invoke callback even if the selected folder has been changed.

FL_RETURN_CHANGED

FL_RETURN_END_CHANGED
     Result in a notification when a folder other that the currently active one has been selected (this is the default).

FL_RETURN_END

FL_RETURN_ALWAYS
     Notify when either a new or the already active folder has been selected.

In the releases before version 1.0.92 of the library only a callback for the folder was executed (if one was installed) on change of the selected folder bur not via e.g., fl_do_forms() etc. This has changed with version 1.0.92. To get the old behaviour you have to build XForms with the --enable-bwc-bs-hack being set.

To find out which folder is currently active the following routines the tab riders are available

     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);

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 previous active folder was (which may be of similar interest as the currently active one) the following functions can be used:

     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)

Again, depending on the application, one might prefer one routine to the other two.

Other Folder Routines
---------------------

To populate a tabbed folder, use the following routine

     FL_OBJECT *fl_addto_tabfolder(FL_OBJECT *obj, const char *tab_name,
                                   FL_FORM *folder)

where tab_name is a string (with possible embedded newlines in it) indicating the text of the tab rider and folder is a regular form created between calls of fl_bgn_form() and 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 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 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 FL_FLAT_BOX or FL_NO_BOX may not look nice. If the backface of the form is of 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

     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);
     FL_FORM *fl_get_tabfolder_folder_byname_f(FL_OBJECT *obj,
                                               const char *fnt, ...);

The functions take either the sequence number (the first tab on the left has a sequence number 1, the second 2 etc) or the tab name, which can either be passed directly as a string or via a format string like for printf() etc. and the corresponding (unspecified) arguments. The functions return the form associated with the number or the name. If the requested number or name is invalid, 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. How many tabs are "hidden" on the left can be determined and also set using the functions

     int fl_get_tabfolder_offset(FL_OBJECT *ojb);
     int gl_set_tabfolder_offset(FL_OBJECT *obj, int offset);

where offset is the number of tabs hidden on the left.

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 fl_do_forms() or 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

     int fl_get_tabfolder_numfolders(FL_OBJECT *obj);

To remove a folder, the following routine is available

     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);
     void fl_delete_folder_byname_f(FL_OBJECT *obj, const char *fmt, ...);

(the last two function differ in the way the tab names gets passed, the first is to be called with a simple string while the second expects a format string as used for printf() etc. and the appropriate number of arguments, from which the tab name gets constructed). wNote 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

     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 *name);
     void fl_set_folder_byname_f(FL_OBJECT *obj, const char *fmt, ...);

(The latter two functions only differ in the way the tab name gets passed top them, the first accepts a simple string while the second expects a format string as used for printf() etc. and the appropriate number of (unspecified arguments, from which the tab name is constructed.)

Since the area occupied by the tabbed folder contains the space for tabs, the following routine is available to obtain the actual folder size

     void fl_get_folder_area(FL_OBJECT *obj, FL_Coord *x, FL_Coord *y,
                             FL_OBJECT *w, FL_OBJECT *h)

where x and 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

     int fl_set_tabfolder_autofit(FL_OBJECT *obj, int how);

where how can be one of the following constants:

FL_NO
     Do not scale the form.

FL_FIT
     Always scale the form.

FL_ENLARGE_ONLY
     Scale the form only if it is smaller than the folder area.

The function returns the old setting.

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

     int fl_set_default_tabfolder_corner(int n);

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

Some visual effects like colors and label font of the tab rider buttons can be set all at once by calling the corresponding functions (i.e., fl_set_object_color(), fl_set_object_lstyle() etc.) with the tabbed folder object as the first argument. Individual tab rider buttons can also be modified by calling those function with the corresponding return value of fl_addto_tabfolder() as the first argument.

fl_free_object(tabfolder) does not free the individual forms that make up the tabfolder.

See the demo program folder.c for an example use of tabbed folder class.

A nested tabfolder might not work correctly at the moment.

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 (spl@ucsd.edu).

Adding FormBrowser Objects
--------------------------

Adding an object To add a formbrowser object to a form use the routine

     FL_OBJECT *fl_add_formbrowser(int type, FL_Coord x, FL_Coord y,
                                   FL_Coord w, FL_Coord h,
                                   const char *label);

The geometry indicated by x, y, w and h is the total area of the formbrowser, including scrollbars.

FormBrowser Types
-----------------

There's only a single type of formbrowser available, the FL_NORMAL_FORMBROWSER.

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 fl_do_forms() or 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

     void fl_set_object_return(FL_OBJECT *obj, unsigned int when);

where the when argument can be one of

FL_RETURN_NONE
     Never report or invoke callback (this is the default for the formbrowser object)

FL_RETURN_CHANGED
     Result in a notification whenever the position of one of the scrollbars has changed.

FL_RETURN_END_CHANGED
     Notification is sent if the position of a scrollbar has changed and the mouse button has been released.

FL_RETURN_END
     Notification on release of the mouse button.

FL_RETURN_ALWAYS
     Notify if the position of a scrollbar has changed or the mouse button has been released.

Other FormBrowser Routines
--------------------------

To populate a formbrowser, use the following routine

     int fl_addto_formbrowser(FL_OBJECT *obj, FL_FORM *form);

where form is a pointer to a regular form created between calls of fl_bgn_form() and 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

     int fl_get_formbrowser_numforms(FL_OBJECT *formbrowser);

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 fl_do_forms() or 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

     int fl_delete_formbrowser(FL_OBJECT *obj, FL_FORM *form);
     FL_FORM* fl_delete_formbrowser_bynumber(FL_OBJECT *obj, int num);

In the first function you specify the form to be removed from the formbrowser by a pointer to the form. If the form was removed successfully the function returns the remaining number of forms in the formbrowser, otherwise -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 NULL if num was invalid, otherwise it returns address of the form that was removed.

To replace a form in formbrowser, the following routine is available

     FL_FORM *fl_replace_formbrowser(FL_OBJECT *obj, int num,
                                     FL_FORM *form);

where num is the sequence number of the form that is to be replaced by form. For example, to replace the first form in the browser with a different form, you should use 1 for num. The function returns the form that has been replaced on success, otherwise NULL is returned.

You can also insert a form into a formbrowser at arbitrary locations using the following routine

     int fl_insert_formbrowser(FL_OBJECT *obj, int num, FL_FORM *form);

where num is the sequence number before which the new form 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

     int fl_find_formbrowser_form_number(FL_OBJECT *obj, FL_FORM *form);

The function returns a number between 1 and the number of forms in the formbrowser on success, otherwise 0.

To obtain the form handle from the sequence number, use the following routine

     int fl_get_formbrowser_form(FL_OBJECT *obj, int num);

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

     void fl_set_formbrowser_hscrollbar(FL_OBJECT *obj, int how);
     void fl_set_formbrowser_vscrollbar(FL_OBJECT *obj, int how);

where how can be one of the following

FL_ON
     Always on.

FL_OFF
     Always off.

FL_AUTO
     On when needed. This is the default.

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

     void fl_set_formbrowser_scroll(FL_OBJECT *obj, int how)

where how can be one of the following

FL_SMOOTH_SCROLL
     The default.

FL_JUMP_SCROLL
     Scrolls in form increments.

To obtain the form that is currently the first form in the formbrowser visible to the user, the following can be used

     FL_FORM *fl_get_formbrowser_topform(FL_OBJECT *obj);

You can also set which form to show by setting the top form using the following routine

     int fl_set_formbrowser_topform(FL_OBJECT *obj, FL_FORM *form);
     FL_FORM* fl_set_formbrowser_topform_bynumber(FL_OBJECT *obj, int num);

The first function returns the sequence number of the form and the second function returns the form with sequence number 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

     void fl_get_formbrowser_area(FL_OBJECT *obj, int *x, int *y,
                                  int *w, int *h);

where x and 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

     int fl_set_formbrowser_xoffset(FL_OBJECT *obj, int offset);
     int fl_set_formbrowser_yoffset(FL_OBJECT *obj, int offset);

where 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 left. Similarly an y-offset of 10 means the content is scrolled by 10 pixels upwards.

To obtain the current offsets, use the following routines

     int fl_get_formbrowser_xoffset(FL_OBJECT *obj);
     int fl_get_formbrowser_yoffset(FL_OBJECT *obj);

Remarks
-------

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

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