The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Curses::Forms -- Curses-based form handling for Curses::Widgets

Doc/Module Version info

$Id: Forms.pod,v 0.2 2000/02/29 08:47:25 corliss Exp $

SYNOPSIS

        use Curses::Forms;

        $form = Curses::Forms->new({ 'Y' => 1, 'X' => 0, 
                'LINES' => $LINES - 2, 'COLS' => $COLS });
        $form->add( { 'type' => 'list_box', 'name' => 'cal_list',
                      'ypos' => 0, 'xpos' => 0,
                      'lines' => 8, 'cols' => $COLS - 2,
                      'title' => ' Calendars ', 'list' => \@list },
                    { 'type' => 'txt_field', 'name' => 'record',
                      'ypos' => 10, 'xpos' => 0, 'regex' => "\tqQ",
                      'lines' => $LINES - 14, 'cols' => $COLS - 2,
                      'title' => ' Record ', 'edit' => 0 });
        $form->set_defaults( DEF_FUNC => \&clock);
        $form->tab_order("cal_list");
        $form->bind(
                ["cal_list", "[ \n]", "Mod_Oth", \&rtrv_cal_rec, 
                        "record" ],
                ["cal_list", "[qQ]", "Quit_Form"],
                ["cal_list", "[bB]", "Mod_Oth", \&browse_cal, 
                        "record" ]);
        $form->activate;

REQUIREMENTS

Requires the Curses module, Curses or nCurses libraries, and Curses::Widgets. You must still 'use Curses;' in your script, as well as 'use Curses::Widgets'. Curses::Widgets v1.1 is the minimum version needed.

DESCRIPTION

This module provides an object-oriented approach to managing separate forms composed of multiple widgets. This approach removes the need to code any handling for form navigation. Only the list of widgets, the widget tab order, and any special key/function bindings need be used. Once the form is initialised, it handles everything else on its own.

PUBLIC METHODS

  • new

  • add

  • bind

  • tab_order

  • set_defaults

  • activate

  • refresh_forms

METHODS

new

The 'new' method returns an object handle by which the form is both initialised and managed. One argument, in the form of an anonymous hash, much be passed which specifies the desired geometry.

This method merely creates and initialises the object, no error checking is done outside of verifying that the following parameters were passed:

        Parameter
        ------------------------------------------------
        Y       The y coordinate for the top-left corner
                of the form
        X       The x coordinate for the top-left corner
                of the form
        LINES   The number of lines in the form
        COLS    The number of columns in the form

        B<Example>

        $form = Curses::Forms->new({ 'Y' => 1, 'X' => 0, 
                'LINES' => $LINES - 2, 'COLS' => $COLS });

add

The 'add' method is used to add widgets to the form, each widget being passed as an anonymous hash. All the necessary options for the specific widget type must be passed, along with two other parameters:

        Parameters
        -----------
        type    The type of widget, as specified by the function name
                in Curses::Widgets
        name    A unique name for referencing on the form (this will be
                the identifier used in the tab_order and bind methods)

The only error checking done at this point is ensuring that each widget is named, and uniquely so, as well as ensuring that the widget type is a type known to the module. To get a list of options for the different types of widgets, see the documentation for Curses::Widgets.

        B<Example>

        $form->add( { 'type' => 'list_box', 'name' => 'cal_list',
                      'ypos' => 0, 'xpos' => 0,
                      'lines' => 8, 'cols' => $COLS - 2,
                      'title' => ' Calendars ', 'list' => \@list },
                    { 'type' => 'txt_field', 'name' => 'record',
                      'ypos' => 10, 'xpos' => 0, 'regex' => "\tqQ",
                      'lines' => $LINES - 14, 'cols' => $COLS - 2,
                      'title' => ' Record ', 'edit' => 0 });

bind

The 'bind' method allows certain functions and actions to be bound to specific widgets, and triggered by specific key strokes. Each action/function binding is passed as an anonymous array, each containing the following fields, in the following order:

        Bind record
        -----------
        $anon[0]        The name of widget to bind the action to
        $anon[1]        The input regex which will trigger the 
                        action (the widget must not trap for the 
                        regex above, but must exit with that input 
                        to allow the form to call this binding)
        $anon[2]        The action to be taken
        $anon[3]        The function to call (only needed for 
                        Mod_Own/Mod_Oth)
        $anon[4]        The name of the widget to call the action 
                        on (only needed for Mod_Oth)

        Actions
        ------------
        Quit_Form       Exit the activate routine, and return all 
                        the current widget values
        Nxt_Wdgt        Move the active widget focus to the next 
                        widget as specified by the tab order.
        Mod_Own         Call the specified function and pass it a 
                        reference to the current widget's state hash
        Mod_Oth         Call the specified function and pass it a 
                        reference to both the current and the 
                        specified widget

This method must not be called until the widgets are already added to the form. If you call it before, it will trigger a series of warnings about attempts to bind to a non-existent widget.

One should note that bindings for specific widgets and keys can be stacked, with the bindings for that widget/key combination processed in the order in which they were passed to the form.

It is important to note that any function called must adhere to the following guidelines:

        1)  It must accept two or three arguments, depending on 
            whether the function must serve Mod_Own or Mod_Oth 
            calls, respectively.  The first argument will always 
            be the key pressed which activated the binding.  The 
            second and third will be references to state hashes, 
            where all widget options may be manipulated directly, 
            according to the argument names listed in the 
            B<Curses::Widgets> documentation for those widget types.
        2)  If desired, it may return the string "Quit_Form" if 
            you wish it to exit the current form after calling 
            that function.

In the example below, the following routine will be bound to a list box widget (as will be seen in the next example). Upon pressing the 'a' key, a input box will be displayed. If the user enters either an A, R, W, or O, the contents of the list box will be modified. If the user presses a 'd' while the list box has the focus, though, the highlighted entry will be deleted from the list.

        B<Example>

        local *mod_priv = sub {
            my $key = shift;
            my $in_ref = shift;
            my ($priv, $button, $prompt);

            if (lc($key) eq "a") {
                $prompt = "Type 'A' for Administrative,\n " .
                    "'R' for read,\n 'W' for write,\n or " .
                    "'O' for other.";
                ($priv, $button) = input_box(
                    'title' => 'Add Privilege', 
                    'prompt'=> $prompt, 'function' => \&clock);

                Curses::Forms->refresh_forms;

                if ($button) {
                    $priv = uc($priv);
                    if ($priv eq "A") {
                        push(@{$$in_ref{'list'}}, 'Administrative')
                            unless (grep /Admin/, @{$$in_ref{'list'}});
                     } elsif ($priv eq "R") { 
                        push(@{$$in_ref{'list'}}, 'Read')
                            unless (grep /Read/, @{$$in_ref{'list'}});
                     } elsif ($priv eq "W") {
                        push(@{$$in_ref{'list'}}, 'Write')
                            unless (grep /Write/, @{$$in_ref{'list'}});
                     } elsif ($priv eq "O") {
                        push(@{$$in_ref{'list'}}, 'Other')
                            unless (grep /Other/, @{$$in_ref{'list'}});
                     } else {
                        status_bar(0, "'$_' is not a valid option.");
                        beep();
                     }
                     @{$$in_ref{'list'}} = sort @{$$in_ref{'list'}};
                }
            } elsif ($key =~ /^[Dd]$/) {
                splice(@{$$in_ref{'list'}}, $$in_ref{'selected'}, 1);
            }
        };

The following example shows the above function being bound to the list box, which is named 'privs'.

        B<Example>

        $form->bind(["privs", "[aAdD]", "Mod_Own", \&mod_priv ],
                ["command", "[ \n]", "Mod_Own", \&action ]);

tab_order

The 'tab_order' method takes a list of widget names in the order that they should be given the focus as the user navigates from one to the other. Note that you can leave widgets out of this order if their only function is to display information pushed into them via the Mod_Oth action, etc. You can also redefine widget order as needed, since each time the method is called, the entire order array is cleared before processing the passed list.

The only error-checking this method does is to verify that each widget specified in the order was defined via the 'add' method.

        B<Example>

        $form->tab_order(qw( f_name email name psswd privs command ));

set_defaults

The 'set_defaults' method allows the setting of form-wide defaults. It is called with a series of key/value pairs, of which the following keys are valid:

        Keys
        ------------
        DEF_FUNC        Subroutine reference, to be used in 
                        the background while each widget is 
                        waiting for input.  Defaults to ''.
        DEF_TAB         String, for interpolation in a regex 
                        which will be the default key triggering 
                        a change in focus to the next widget 
                        in the tab order.  Defaults to "\t".
        DEF_ACTV        String, the default colour to be used 
                        by widgets when they have the focus.  
                        Colour choices are those supported by 
                        the 'select_colour' function provided by
                        B<Curses::Widgets>.  Defaults to 'green'.
        DEF_INACTV      String, the default colour to be used 
                        by widgets when they do not have the 
                        focus.  Defaults to 'red'.
        BORDER          Boolean, a true or false value which 
                        controls whether or not the form has a 
                        border.  Defaults to 0.
        BORDER_COLOUR   String, with the colour to be used when 
                        drawing the form border.  Defaults to 
                        the default foreground colour.
        TITLE           String, the title to be superimposed on 
                        the form border, or where the form border 
                        would be if used.

All arguments are optional, and if the defaults are acceptable, this method can be completely ignored. The only error-checking done is a check on each key/value pair to see if they are a known option.

        B<Example>

        $form->set_defaults(DEF_FUNC => \&clock,
                        BORDER => 1,
                        BORDER_COLOUR => 'blue',
                        TITLE => ' Add User ');

activate

The 'activate' method is used for two purpose: to simply display the form in it's current state and immediately exit, and to display and activate the form, capturing input and managing widget navigation until the 'Quit_Form' action is recieved.

To display the form, pass the method any argument that evaluates to 'true'. To activate the form, call the method with no arguments.

Error checking is done upon each call to ensure that the form geometry won't exceed the current console dimensions, as well as for each widget, to ensure their geometry doesn't exceed the form's dimensions.

        B<Example>

        $form->activate;

refresh_forms

The 'refresh_forms' method is a class method, not an object method (though calling it via the object reference will achieve the same objective). The purpose of this function to clean the screen of any form remnants in background forms. For example, consider the situation of three forms with the following dimensions:

        +-Form 1----------------------------------------------------+
        |                                                           |
        | +-Form 2------------------------------+                   |
        | |                                     |                   |
        | |     +-Form 3-----------------------------+              |
        | |     |                                    |              |
        | |     |                                    |              |
        | |     +------------------------------------+              |
        | |                                     |                   |
        | +-------------------------------------+                   |
        +-----------------------------------------------------------+

The scenario here is that an action taken on Form 1 prompted Form 2 to pop up, and an action on Form 2 prompted Form 3 to be displayed. Because each form is created in its own private window (not a sub or derived window from the master window created during Curses initialisation), when Form 3, disappears, and Form 2 regains the focus, the region outside of Form 2 will not be refreshed to remove the overlapping Form 3 region, leaving you with the following:

        +-Form 1----------------------------------------------------+
        |                                                           |
        | +-Form 2------------------------------+                   |
        | |                                     |                   |
        | |                                     |----+              |
        | |                                     |    |              |
        | |                                     |    |              |
        | |                                     |----+              |
        | |                                     |                   |
        | +-------------------------------------+                   |
        +-----------------------------------------------------------+

Calling this class method will send a touchwin and refresh signal to each of the windows, from back to front, leaving you with:

        +-Form 1----------------------------------------------------+
        |                                                           |
        | +-Form 2------------------------------+                   |
        | |                                     |                   |
        | |                                     |                   |
        | |                                     |                   |
        | |                                     |                   |
        | |                                     |                   |
        | |                                     |                   |
        | +-------------------------------------+                   |
        +-----------------------------------------------------------+

It is good to call this function whenever a child window is closed whose borders extended past the parent window's borders.

        B<Example>

        Curses::Forms->refresh_forms;

Troubleshooting

Curses::Forms will never intentionally kill your script. It does do some basic checks upon creation and before executing certain methods, and if it finds something amiss, it will use the warn function to report the error.

When testing scripts that use this module, you'd be well advised to pipe STDERR to a file, so that it doesn't mess with the current display. Checking that file later will show you what specific areas of the script have problems. Otherwise, the display might become corrupted, and cause perfectly valid function calls to appear screwey, when it was only the fact that the STDERR moved the cursor location before the next STDOUT output could be rendered.

If you run into problems that appear to be the fault of the module, please send me the STDERR output and a script that demonstrates the problem.

HISTORY

See the Changelog for in depth change history.

AUTHOR

All bug reports, gripes, adulations, and comments can be sent to Arthur Corliss, at corliss@odinicfoundation.org.