Previous: Color Chooser, Up: Part I Goodies [Contents][Index]

### 6.5 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:

    const char *fl_show_fselector(const char *message,
                                  const char *directory,
                                  const char *pattern,
                                  const char *default);

A form will be shown in which all files in directory `directory` are listed that satisfy the pattern `pattern` (see Fig 6.1). `pattern` can be any kind of regular expression, e.g., `[a-f]*.c`, which would list all files starting with a letter between `a` and `f` and ending with `.c`. `default` is the default file name. `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 `NULL` if the `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 `ReScan` button can be used to request a rescan of the directory.

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 `Ready` or `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

    void fl_hide_fselector(void);

The effect of removing the file selector programmatically is the same as pushing the `Cancel` button. There are total of `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

    int fl_use_fselector(int n);

where `n` is a number between 0 and `FL_MAX_FSELECTOR - 1`.

To change the font the file selector uses, the following routine can be used:

    void fl_set_fselector_fontsize(int font_size);
    void fl_set_fselector_fontstyle(int font_style);

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 `fl_get_fselector_fdstruct()` explained later.

The window title of the file selector can be changed anytime using the following routine

    void fl_set_fselector_title(const char *title);

To force an update programmatically, call

    void fl_invalidate_fselector_cache(void);

before `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:

    void fl_disable_fselector_cache(int yes);

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 `<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 `Ready` or press the `<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 `Cancel` button `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

    void fl_set_fselector_callback(int (*callback)(const char *, void *),
                                   void *user_data);

where the second argument of the callback is the `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

    void fl_set_fselector_placement(int place);

where place is the placement request same as in `fl_show_form()`. The default is `FL_PLACE_CENTER | FL_FREE_SIZE`.

By default, an fselector is displayed with transient property set. To change the default, use the following routine

    void fl_set_fselector_border(int border);

The `border` request by this function is the same as in `fl_show_form()`, but `FL_NOBORDER` is ignored.

If the arguments `directory`, `pattern` or `default` passed to `fl_show_form()` are empty strings or `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

    const char *fl_get_directory(void);
    const char *fl_get_pattern(void);
    const char *fl_get_filename(void);

It is also possible to programatically set new values for the default directory and pattern by using the functions

    int fl_set_directory( const char * dir );
    void fl_set_pattern( const char * pattern );

`fl_set_directory()` returns 0 on success and 1 on failure, either because the argument was a `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:

    void fl_add_fselector_appbutton(const char *label,
                                    void (*callback)(void *),
                                     void *data);

The argument `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

    void fl_remove_fselector_appbutton(const char *label);

Within the callback function, in addition to using the routines mentioned above, the following routines can be used:

    void fl_refresh_fselector(void);

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:

    FL_FORM *fl_get_fselector_form(void);

See `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

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

You can, for example, change the default label strings of various buttons via members of the `FD_FSELECTOR` structure:

    FD_FSELECTOR *fs = fl_get_fselector_fdstruct();

    fl_set_object_label(fs->ready, "Go !");
    fl_fit_object_label(fs->ready, 1, 1);

Since the return value of `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, `D` for directories, `p` for pipes etc.). To change the prefix, use the following routine

    void fl_set_fselector_filetype_marker(int dir,
                                          int fifo,
                                          int socket,
                                          int cdev,
                                          int bdev);

where `dir` is the marker character for directories, `fifo` the marker for pipes and FIFOs, `socket` the marker for sockets, `cdev` the marker for character device files and, finally, `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

    const FL_Dirlist *fl_get_dirlist(const char *dirname,
                                     const char *pattern,
                                     int *nfiles, int rescan);

where `dirname` is the directory name; `pattern` is a regular expression that is used to filter the directory entries; `nfiles` on return is the total number of entries in directory `dirname` that match the pattern specified by `pattern` (not exactly true, see below). The function returns the address of an array of type `FL_Dirlist` with `nfiles` if successful and `NULL` otherwise. By default, directory entries are cached. Passing the function a true (non-zero) value for the `rescan` argument requests a re-read.

`FL_Dirlist` is a structure defined as follows

    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;

where `type` is one of the following file types

`FT_FILE`

  a regular file

`FT_DIR`

  a directory

`FT_SOCK`

  a socket

`FT_FIFO`

  a pipe or FIFO

`FT_LINK`

  a symbolic link

`FT_BLK`

  a block device

`FT_CHR`

  a character device

`FT_OTHER`

  ?

To free the list cache returned by `fl_get_dirlist()`, use the following call

    void fl_free_dirlist(FL_Dirlist *dl);

Note that a cast may be required to get rid of compiler warnings due to the `const` qualifier of the return value of `fl_get_dirlist()`. See demo program `dirlist.c` for an example use of `fl_get_dirlist()`.

Per default not all types of files are returned by `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

    int default_filter(const char *name, int type);

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

    int ffilter(const char *name, int type) {
        return type == FT_DIR || type == FT_FILE || type == FT_LINK;
    }

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

    typedef int (*FL_DIRLIST_FILTER)(const char *, int);
    FL_DIRLIST_FILTER fl_set_dirlist_filter(FL_DIRLIST_FILTER filter);

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

    int fl_set_dirlist_filterdir(int flag);

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:

    int fl_set_dirlist_sort(int method);

where `method` can be one of the following

`FL_NONE`

  Don’t sort the entries

`FL_ALPHASORT`

  Sort the entries in alphabetic order - this is the default

`FL_RALPHASORT`

  Sort the entries in reverse alphabetic order

`FL_MTIMESORT`

  Sort the entries according to the modification time

`FL_RMTIMESORT`

  Sort the entries according to the modification time, but reverse the order, i.e., latest first.

`FL_SIZESORT`

  Sort the entries in increasing size order

`FL_RSIZESORT`

  Sort the entries in decreasing size order

`FL_CASEALPHASORT`

  Sort the entries in alphabetic order with no regard to case

`FL_RCASEALPHASORT`

  Sort the entries in reverse alphabetic order with no regard to case.

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 `NULL`) can speed up the directory reading considerably.

Previous: Color Chooser, Up: Part I Goodies [Contents][Index]