Daniel Becker Bighelini

SPEL API: WAND

Blog Post created by Daniel Becker Bighelini on Jun 13, 2016

Contents:

    Overview

        How to use WAND

      Advanced Features

    Language

      Description

      Keywords

ADD

DEFAULTS

DIRECT

DISPOB

DOB

ENUM

GLOBAL

HIER

LIST

MANAGER

METHOD

PULLDOWN

RESTYLE

SMART

TRIGGER

    Default Dispobs

           

 

 

 

Overview

 

    What part of the problem does WAND address?

 

        Consider designing a new application.  One would start out by

        defining major subsections - for example Trouble ticketing.

 

        The next step would be to break trouble ticketing down into a series

        of screens showing lists of trouble tickets, ticket details which may

        or may not include failed resource lists and/or action lists, details

        for failed resources and actions, etc.

 

        Another step might be to define how a user might invoke the trouble

        ticket section or its components from other sections with context.

        For example it might be useful to bring up a trouble ticket detail

        both from a list of open tickets as well as from a list of work

        assigned to "Fred Smith".

 

        This is the part of the application process that WAND attempts to

        address.  In WAND the designer describes whole sections of

        applications possibly consisting of multiple forms and the

        relationships between the forms of an application subsection.

 

        WAND also allows the designer to specify the context information

        required to invoke a form or subsection, which will allow the

        subsection to be hooked up to any part of the app that has the

        context available.  For example a ticket detail subsection (with any

        associated action, incident, resource, etc screens) could be defined

        to require a ticket id as its only context, and can then be easily

        hooked into any part of the application that has ticket id's - a list

        of work assigned to a contact for example.

 

        After each subsection of an app has been specified in WAND the final

        step is to describe the entry point for the application and provide

        navigation to all the subsections.  Using WAND and MAJIC a complete

        simple database-style application can be built with no compilation.

        Of course no application will be that simple, so C++ code can be

        written and called from WAND to do the interesting stuff.

 

 

    How to use WAND

 

      Basic functionality

 

            The main unit of configuration is the DISPOB or Display object.

            A DISPOB describes a "displayable unit" - a related group of

            information that can be displayed.  For example an example of a

            DISPOB might be all info for a location.

 

            It is useful to think of a DISPOB as corresponding to a form.

            That is not strictly true, a DISPOB can be displayed in a grid

            row or in a special section of a parent form, but it's useful to

            start with a form.

 

            The dispob definition is kind of like laying out a form except

            that the pieces are bigger.  Instead of laying out fields a

            designer lays out objects and specifies rules for determining

            which instances of objects to use based on context information

            imported into the DISPOB.

 

            A simple example:  a DISPOB for a location detail form will

            probably contain only a single DOB statement which will make the

            location object data available to the form.

 

            A more complex example - a Trouble ticket form.  The form will

            show the data from a single trouble ticket object, a grid of

            actions and a gird of failed resources.  The only context this

            form will require is a Trouble ticket id.  The DISPOB for this

            form will import a Trouble ticket id.  It will contain a DOB

            statement which makes any data in the Trouble ticket record

            available to be displayed on the form.  It will contain two LIST

            statements, one for the action grid and the other for the failed

            resource list.  There will also be several button bindings

            defined.  These will be described later.

 

            As mentioned above a DISPOB can be displayed in a grid row rather

            then on a form.  In the Trouble ticket example the rows of the

            action and resource grids display using action and resource

            DISPOB's defined elsewhere.

 

      Building an app subsection

 

            Lets work through an example of a full subsection.  Beginning with

            a Trouble ticket detail form, list of actions, list of attached

            incidents, list of failed resources, and list of delays for

            actions.

 

            At this point I need to mention a few of the primitives that show

            up in DISPOB definitions.

 

            DOB - Attributes of the specified object are available.

 

            LIST - Another DISPOB is to be displayed on this form

                   either as rows of a grid or using a dedicated section

                   of the form.

                   NOTE: The LIST may also display its DISPOB as details

                   on separate forms.

 

            METHOD - Script code to be executed when buttons are pressed,

                   mouse clicks happen or field events such as "entry

                   complete" happen.

 

            STYLE, RESTYLE - Allow data to be displayed and entered in

                         other than the default way and attach

                         script code to field events.

 

As a tree the sample application looks like:

 

                      TTdetail

                      / \

Resource-list Action-list

                                |

                              Action-detail

                                |

                              Delay-list

                                |

                              Delay-detail

 

The DISPOB's are similarly created and layed out in a tree.

 

As mentioned above a DISPOB defines the context it needs to

be provided to do its job.  That wan't quite accurate.  Only

the DISPOB which is the entry point to the subsection needs to

define the context it imports. DISPOB's that know they always

appear in a tree related to some other DISPOB can grab their

context directly from their parent or ancestor.

 

Here's how it would work:

 

            DISPOB TTdetail would import a TT reference.  It would

            contain a Trouble ticket DOB, a LIST of actions and a

            LIST of failed resources.  Add METHOD's to pop up an

                action-list DISPOB and resource-list DISPOB as separate

                forms.

 

            For flexablilty create a resource-list DIBPOB which would

            contain a LIST of failed resources.  It will be popped up

            by pressing a button on the TTdetail form.  This will allow

            the end user to have the failed resource list either on the

            main form or as a separate form.  Since this DISPOB is only

            used as a child of the TTdetail DISPOB it doesn't specify

            context to import.  It grabs whatever context it needs

            directly from its parent form.

 

            Create an action-list DISPOB.  (Same comments as resource-list

            DISPOB above).

 

            Create an action-detail DISPOB.  This dispob contains an

            action DOB and a LIST of delays.  Since it is used to display

            rows of a grid on a parent form it must import the action

            reference.

 

            Create a delay-detail DISPOB.  This one will contain a

            single delay DOB.

 

            The question of where the "save" button should be will be

            discussed below under the "group_leader" context imported

            into DISPOB's.

 

            The question of how to bring up detail forms from a LIST,

            (re-use existing detail form, bring up new form, edit on grid)

            will be discussed under LIST below.

 

 

      Advanced features

 

Modifying default generated DISPOBS.

 

                It is possible to add to a default generated DISPOB without

                overriding it.  This is done using the keyword ADD.  DOB's,

                LIST's, METHOD's and attributes can be added to any default

                generated DISPOB.

 

Group leaders - locking

 

                Locking tries to be automatic.  The locking scheme used is to

                try to acquire a lock as soon as the first keystroke is typed

                in a field.  So, just looking at a record doesn't lock

                anything.  Locking has to be done against a BOA group leader.

                Any DISPOB can be defined as a group leader by importing an

                int variable group_leader with a value of TRUE.  Only group

                leaders can have save buttons.  They save any DISPOB's under

                them in the tree.

 

Non-persistent  fields

 

            It will sometimes be useful to have fields on forms that

            don't correspond to any BOA object.

            The DISPOB entry can contain data definitions like

 

int show_all;    // Can be a check box on the form

string search_where_clause;  // String field on the form

 

            This data can be accessed by methods just like BOA object

            attributes.

            NOTE:  Currently pulldown lists can't be attached to

            non-persistent fields.  But that support will be added later.

 

Triggers

 

            TRIGGERs are methods that can be attached to any set of data

            on a DISPOB.  They will execute when any of the watched data

            changes.  They can be used to keep calculated data up to date.

 

Style, Restyle

 

                By default each data type displays and enters in a certain

                way.  Strings are entered directly, numbers only allow

                entering certain characters, lookups either do a popdown list

                or bring up a separate helper screen depending on what

                factory the lookup is on.  And by default there is no field

                level validation.

 

            The STYLE entry defines a new way of displaying data and can

            attach behavior to mouse events or field events and can

            attach field level validation.  The STYLE entry doesn't

            automatically cause any fields to use it, that is done

            using RESTYLE for each field to be displayed in the new style.

 

            Managers

 

                DISPOB's can be displayed as separate forms either unmanaged,

                managed by a list, or managed by a manager.

 

                Within an app subsection DISPOB's are either managed by

                LIST's (displayed as rows of a grid or as one or more details

                of grid rows), or unmanaged (popped up when the user presses

                a button, clicks on a field, etc).  The form popped up is a

                child of the parent form which popped it up.

 

            DISPOB's managed by a manager have no parent.  They must

            be self contained and either read-only or have their own

            save button.  The manager allows only one DISPOB for a

            reference to be active at a time.  That is, if the app wishes

            to bring up a managed detail from for "Gertrude Getz", the

            manager will first see if one is up and either pop it to

            the front or make a new one.

 

Morphing app subsection trees

 

            DISPOB's are displayed by name, and the name is a string

            expression which is evaluated at popup time, so it is

            possible to write an app where the form to be popped up

            can be context sensitive.  For example, clicking on a

            resource in a grid could bring up a detail form appropriate

            to the type of resource.

 

 

Sharing subsections among apps

 

            WAND allows entire subsections of apps to be modeled indepent

            of any other subsections.  Each subsection description can be

            placed in a separate WAND config file and loaded into any

            app which requires access to the subsection.  This could

            easily allow a service order app for example to show trouble

            tickets associated with service order actions without having

            to change any code of either app.

 

Button context

           

            Three parameters are passed when a button is pressed.  The

            method identified by the button can ignore them and need not

            declare them as formal arguments.  They are:

            dispob - the full path name of the dispob the button is on

            anchor - the full path name of the first ancestor dispob

            tag - the full tag name of the button.

 

            The tag field of a button in a .vr file can be used to store

            a small amount of context to be passed to a wand METHOD.

            If the tag contains a '#' only the part of the tag before the

            '#' will be used to match the method name.  The entire tag

            is passed as a formal argument to the method with the name tag.

 

            For example:

            .vr file tag 'foo#This is a test'

 

            will call

 

            METHOD foo (string tag)

{

string context [1]

extract (context, tag, "#\\(.*\\)");

printf ("%s\n", context [0]);

}

 

            which will print 'This is a test'

 

 

Language

 

    Description

 

      WAND config files contain the following types of entries:

 

DISPOB - defines a Display object or displayable unit

ADD - modifies a default generated DISPOB.

DEFAULTS - set default lists and style info for a factory

GLOBAL - define global data and methods

MANAGER - Defines an object used to manage and re-use forms.

STYLE - defines a style for displaying, entering and validating data

 

      These entries in turn contain other entries.  The WAND keywords will

      be listed in alphabetical order followed by examples of DISPOB, STLYE

      and MANAGER statements.

 

        NOTE: STYLE and DISPOB entries can be defined inside of STYLE and

        DISPOB blocks.  The blocks act as if they were defined outside of

      any block except that their name is scoped so that they are only

      visible within the block in which they are defined.

 

 

    Keywords

 

      C-like expressions

 

These expressions show up as defaults in formal

parameter lists, as passed parameters and in script code.

These are just what they sound like, C expressions.  The expressions

can include constants as well as any variables defined and/or

parent or ancestor data. Variables local to DISPOB's, LIST's or

STYLE's are referenced by name as if they were C++ class members.

DOB attributes are accessed as dob-name.attr-name .

 

            In cases where the tree structure of an app is known, variables

            in ancestor DISPOB's can be accessed by ancestor-name.var-name .

See disambiguation in METHOD's below.

 

      ADD dispob-name

{

dispob_body

}

 

      Dispob-name is the name of the DISPOB to be modified.  This DISPOB

      typically will be a default generated for a factory, but could be

      defined in a configuration file.

 

      Dispob-body is a list of DOB's, LIST's, RESTYLE's, METHOD's, TRIGGER's

      and variable declaraions.

      See DISPOB below.

 

 

      DEFAULTS<factory>

{

[list template];

[ STYLE "string"];

}

      When you define a LIST, you need to define what dispob to display in

      the list, etc.  There is a short style of the list that uses the

      default list information .  This defines the list information to use

      for the short style.

 

      See the LIST section for more info.

      STYLE is similar.

 

 

 

      DIRECT

This is a style type specifier - see STYLE

 

      DISPOB name (formal-args)

{

dispob-body

}

 

Formal-args is a list of context arguments to be imported into

the DISPOB when it is used.

The syntax is C++ - like.

 

data_type name [= default-value][, more formal args]

The valid data types are:

            int, long, double, date, duration, string, object.

The optional default-value is a C-like expression.

 

All dispobs import at least the following formal args:

 

            string form - The name of the Galaxy form to use for details

            int is_group_leader - BOOLEAN TRUE if this DISPOB is one.

            object group_leader_ref - group leader ref if not NULL.

 

NOTE:  All actual context args are passed by name, so defaulted

args don't need to be the last ones declared.

            The defaults are evaluated when the DISPOB is invoked rather than

            at compile time.

 

NOTE:  DISPOB's have several builtin methods that can be called

from METHOD scripts - see builtins

 

      dispob-body is a list of DOB's, LIST's, RESTYLE's, METHOD's, TRIGGER's

      and variable declaraions.

 

      DOB<factory> name (passed-context) ;

         

A DOB entry makes all attributes of the BOA object available for

use as fields on a form.

 

Factory is the name of the DOB's factory.  It is used to determine

the attribute list for the DOB.

 

Name is the name of the DOB to be used in METHOD's or as fields

on a form.  The attributes are accessed as name.attr_name.

 

Passed-context consists of at least dob_ref := C-like expression,

but may include such things as read_only := C-like expression.

NOTE:  This is an example of passing args by name.  For example

(read_only := 1, dob_ref := contact.pers_id)

 

Extra context passed into the DOB can be accessed by STYLE's used

to display attributes of the DOB.

 

      ENUM

This is a style type specifier - see STYLE

 

      GLOBAL

{

method and attribute definitions

}

      GLOBAL blocks can appear multiple times in the configuration.

 

      HIER

This is a style type specifier - see STYLE

 

      METHODS and attributes are define exactly as in DISPOB blocks.

        These can be accessed with normal name scoping or can be

        disambiguated by prepending their name with root.  For example

        root.scratch_pad_text.

 

      LIST<factory> [flags] list-name(list-context-args);

      LIST<factory> [flags] list-name(list-context-args)

                      OF dispob_name (dispob-context-args) ;

      LIST<factory> [flags] list-name(list-context-args)

                      OF dispob_name (dispob-context-args)

{

list-body

}

 

The first form of LIST is a reference to the default list for

the factory.  The list template defined in a DEFAULTS block

specifies the dispob_name, dispob-context-args and any

attributes or methods.

 

Factory is the name of the factory the list applies to.

 

Flags optionally consist of a combination of

            DETAIL_MULTIPLE

            DETAIL_MANAGED

            SAVE_ALWAYS

            SAVE_NEVER

            DEFER_INIT

 

DETAIL_MULTILE allows multiple details for the same list to be up

at one time.  The default is a single detail with next and previous

methods.

 

DETAIL_MANAGED uses the factory default manager to pop up details.

 

SAVE_ALWAYS means that the user must always save or revert changes

when closing a detail form.

 

            SAVE_NEVER means that saving can never be done from a detail form

            which allows the rows of the list to share their parent's group

            leader.

 

            DEFER_INIT This flag instructs the vbop to defer initialization

            and firing triggers until the detail is requested for the dob.

 

List-name is the name of the list. It is used to access methods

of the LIST object as well as to bind form fields.  See Galaxy

tag bindings below.

 

            List-context-args consist of source and anchor.

 

source is set to either a string DOMSET name or a reference to

a DOMSET, QREL or LREL.

 

Dispob-context-args are the context args passed to the named dispob

which will be used to display rows of the grid, in a section of

the parent form or on a separate form.

 

Any LIST contains two attributes which may be passed as context

to the DISPOB.  They are int current_index, and object current_dob.

 

            Current_index and current_dob should be used by the DISPOB to

            determine which BOP objects to show on the grid.

 

            List-body is optional.  If present it is a list of attributes and

            METHOD's.  The METHOD's can be called like any other METHOD's,

            but additionally certain METHOD names will be invoked on mouse

            actions on grid rows.  They are:

Select_1, Select_2, Select_3, Menu_1, Menu_2, Menu_3,

            and Drag_1, Drag_2 Drag_3.  These are invoked on single, double

            and tripple left-button click and single, double and tripple

            right and/or middle button click.  Additionally, the method

command will be invoked on maped function keys or field-level

menus.

 

            NOTE:  The names Select, Menu and Drag come from Galaxy.

 

NOTE:  LIST's have several builtin methods that can be called from

METHOD scripts:

                next ()  - advances the detail display to the next element of

                     the list.

 

                prev () - advances the detail display to the previous element

                    of the list.

 

            move (int now_loc) - moves the detail display to the specified

                             element of the list.

 

            detail_cursed () - Open or re-use a detail form to display

                           a detail for the cursed row.

 

            search () - Re-popuate the list by doing a search on the

                      LIST's domset.

 

            new_dob (object copy_dob_ref) - Create a new dob in the

                                   list.  If copy_dob_ref

                                   is passed a copy will be

                                   created.

            NOTE:  Currently new_dob () won't work for DETAIL_MANAGED lists.

            use LIST.add (dob_ref := MANAGER.manage_new_dob ()) instead.

 

            add (object dob_ref, int location) - Add a dob ref to

                                        a list at location or at

                                        the beginning if location

                                        is not specified.

 

 

      MANAGER<factory> manager-name OF dispob-name (dispob-context-args) ;

      MANAGER manager-name OF dispob-name (dispob-context-args) ;

 

Manager-name is the name of the manager.

Dispob-name is the name of the DISPOB being managed.

Dispob-context-args is the context passed to the DISPOB.

 

NOTE:  The context available to pass to the DISPOB is

string persid which is the string persistent id of the object

for which the DISPOB is being managed and object dob_ref if

<factory> is specified.

 

NOTE:  There are three builtin functions.

 

manage_dispob (string persid);

            // Show a form for the object.

object manage_new_dispob (objecy copy_dob_ref);

            // Create a new dispob and manage it - optionally copying

            // an existing one.

unmanage_dispob (string persid);

            // Close the managed form.

 

      METHOD name (formal-args)

{

script code

}

 

            The script code is written very much like "C".  It is almost

            identical to the MAJIC interpreter language with the following

            differences:

 

            Unsupported functions:

set_ilimit ()

sleep ()

set_error ()

set_return_data ()

 

            Fucntion calls pass args by name as name := value

 

            Discuss path.attr access here somewhere.

 

 

      PULLDOWN

This is a style type specifier - see STYLE

 

      RESTYLE attr-name [style-name] [(context-args)] ;

 

Attr name is the name of the attribute to have its style modified.

If style-name is present the names style will be used to display

the attribute.  If context-args is present the args will be passed

as context to the attribute's style.

 

      SMART

This is a style type specifier - see STYLE

 

      NOTE see discussion of STYLE under DEFAULTS.  Usually the automatically

      crteated defaults styles are all you ever need.

      STYLE name type (formal-args)

{

style-body

}

 

Name is the name of the style. RESTYLE is used to associate

            the style with one or more attributes.

            Additionally there are some special style names that must be

            defined once per configuration.  The special names are the

            default styles to be used for attributes that don't have their

            own style set with RESTYLE.  The names are:

int_style      - default style for int fields

real_style     - default style for float fields

string_style   - default style for string fields

date_style     - default style for date fields

duration_style - default style for duration fields

lookup_style   - default style for SREL fields

 

            Additionally styles can be defined per factory as the

            default style for SREL fields on that factory.  There

            names are <factory>_lookup_style.  ie.  CT_lookup_style.

 

            SDT 4149 - Note the formal arg 'display' is a string name of the

            attribute you wish to display in the text box - typically the REL_ATTR

            of the object.

 

Type is one of DIRECT, ENUM, PULLDOWN or SMART.

            DIRECT is used for non SREL attributes.  It supports direct

            typing into a field.

 

                ENUM is used for non SREL integer or string attributes that

                can contain limited values.  It allows the values to be

                selected from a pulldown list.

 

            // NOTE: ENUM style must have an ENUM section in their

            style_body.  The ENUM section works as follows:

            ENUM

{

"display-string1" = value1,

"display-string2" = value2,

.

.

.

}

            Value must be a number or string depending on the type of the

            attribute using the style.

 

                PULLDOWN should be used for small SREL lookups where all

                entries will fit into a pulldown list.

 

            SMART should be used for larger SREL lookups where a separate

            helper form is needed.

 

            HIER should be used for SREL lookups which are hierarchal.

 

            All style contain a builtin method:

 

set_newval (data newval);  // NOTE:  The actual data type

                                     // for newval depend on the attr.

 

                  This function can only be called from within a

                  validation function to force the field to accept

                  a value other than the one entered.

 

                All styles look for several methods (defined for the style

                with METHOD).  If they are defined they are called at

                appropriate times.  They are:

 

entry_started ()

                  Called at the first keystroke in a field

 

entry_finished ()

                  Called when an entry has been accepted

 

entry_cancelled ()

                  Called when an entry is cancelled or didn't validate

 

string validate (data oldval, data newval)

                  Called after the user enters into a field.  This

                  function can do one of three things:

 

                      Return "" - accepts the entry.

 

                      Return error message - pop up a box and reject entry

 

                            Call set_newval () to set the value to something

                            else.

 

Select_1 () - left mouse button single click

Select_2 () - left mouse button double click

Select_3 () - left mouse button triple click

 

Drag_1 () - middle mouse button single click

Drag_2 () - middle mouse button double click

Drag_3 () - middle mouse button triple click

 

Menu_1 () - right mouse button single click

Menu_2 () - right mouse button double click

Menu_3 () - right mouse button triple click

 

command (string tag) - mapped function key or field menu

 

NOTE:  The Select, Drag and Menu strings come from Galaxy.

 

            In addition SMART styles look for a method

            no_match (string newval, object domset).

            If the method is defined it is called when the user entry

            doesn't uniquely identify an entry.

            Newval is the string typed

            Domset is a domset containing the matched entries.

 

 

      TRIGGER attr [, more attrs]

{

script code

}

 

            A TRIGGER statement specifies a block of script code to be

            executed when any of the named attributes changes.  See METHOD

            below for a description of the script code.

 

 

 

   

Default Dispobs

 

 

      Several things get created by default for each factory.  They are

      DISPOB factory_detail

      DISPOB factory_list

      MANAGER factory_manager

      STYLE factory_pulldown_style

      STYLE factory_smart_style

      LIST OF factory_detail - list template.

      All of these can be overridden.

      DISPOB, MANAGER and STYLE defaults can be overridden by

      explicitly creating entries with the same name which will prevent

      the default from being created.  The LIST default can be

      overridden by creating one in the DEFAULTS block for the factory.

 

      The list template is refered to by the short form LIST definition.

      It supplies the detail dispob and any extra methods the list needs.

 

      The syntax for a LIST default is

      LIST OF dispob_name (dispob-context-args) optional body.

      See LIST below.

 

      HELPER_ATTR "string" supplies the name of the factory's attribute

      to be displayed for SREL's against the factory and is used to

      build the default STYLE entries for the factory.

 

      STYLE "string" is used to specify the default style for separate

      form helpers for SRELS on the factory.  The possible values for

      "string" are "smart" or "hier".  If not specified it defautls to

      "smart".

 

      The defaults created per factory are as follows:

 

      DISPOB factory_detail (object dob_ref,

                        string form = "factory_detail",

                        string function = "factory's FUNCTION_GROUP",

                        int is_group_leader = 1,

                        object group_leader_ref)

{

DOB<factory> factory (dob_ref := dob_ref);

 

// for each qrel or lrel attribute...

LIST <qrel factory> qrel attr name (source := qrel);

}

 

      DISPOB factory_list (string form = "factory_list",

                      string domset = "RLIST",

                      string search_domset = "MLIST_DYNAMIC",

                        string function = "factory's FUNCTION_GROUP",

                        int is_group_leader = 1,

                        object group_leader_ref)

{

// Use the default list

LIST<factory> list (source := domset,

                        search_domset := search_domset);

METHOD new()

            {

            list.new_dob ();

            list.detail_cursed ();

            }

METHOD copy()

            {

            if (!is_null (list.current_dob))

{

list.new_dob (copy_dob_ref := list.current_dob);

list.detail_cursed ();

}

            }

}

 

      DEFAULTS <factory>

{

LIST OF factory_detail (dob_ref := current_dob);

}

 

 

      MANAGER<factory> factory_manager

                      OF factory_dispob (dob_ref := dob_ref);

 

      MANAGER factory_list_manager OF factory_list ();

 

      STYLE factory_pulldown_style PULLDOWN (string display = ???,

                      string entry_started, string entry_finished,

                      string entry_cancel, string validate,

                      string Select_2, string Menu_1, string Menu_2,

                      string Drag_1, string Drag_2, string command)

{

METHOD Select_2 ()

            { if (!is_null (Select_2)) call (Select_2) (); }

METHOD Menu_1 ()

            { if (!is_null (Menu_1)) call (Menu_1) (); }

METHOD Menu_2 ()

            { if (!is_null (Menu_2)) call (Menu_2) (); }

METHOD Drag_1 ()

            { if (!is_null (Drag_1)) call (Drag_1) (); }

METHOD Drag_2 ()

            { if (!is_null (Drag_2)) call (Drag_2) (); }

METHOD command (string tag)

            { if (!is_null (command)) call (command) (tag := tag); }

METHOD entry_started ()

            { if (!is_null (entry_started)) call (entry_started) (); }

METHOD entry_finished ()

            { if (!is_null (entry_finished)) call (entry_finished) (); }

METHOD entry_cancel ()

            { if (!is_null (entry_cancel)) call (entry_cancel) (); }

METHOD validate (string oldval, string newval)

            {

            if (is_null (validate)) return "";

            else return call (validate)

                            (oldval := oldval, newval := newval);

            }

}

 

      STYLE factory_smart_style SMART (string display = ???,

                      string entry_started, string entry_finished,

                      string entry_cancel, string validate,

                      string Select_2, string Menu_1, string Menu_2,

                      string Drag_1, string Drag_2, string command)

{

DISPOB factory_select (string form = "factory_select",

                      object helper_domset,

                      string helper_search_domset,

                      string sort_key,

                      int initial_loc,

                      string function = factory function group)

            {

            METHOD init ()

{

                  if (!is_null (sort_key)) list.sort (key := sort_key);

if (!is_null (initial_loc))

                  {

                  list.move (key := initial_loc)

                  }

}

            LIST<factory> list (source := helper_domset,

                            search_domset := helper_search_domset)

                      OF factory_detail (dob_ref := current_dob)

{

METHOD Select_2 ()

                  {

                  object selected;

                  selected = current_dob;

                  if (save() < 3) // see builtins for error codes

                        {

                        smart_style_select (selected_dob := selected);

                        close_dispob ();

                        }

}

            METHOD cancel ()

{

smart_style_cancel ();

close_dispob ();

}

            METHOD select ()

{

smart_style_select (selected_dob := list.current_dob);

close_dispob ();

}

            METHOD new()

{

list.new_dob ();

list.detail_cursed ();

}

            METHOD copy()

{

if ( ! is_null( list.current_dob ) )

                  {

                  list.new_dob (copy_dob_ref := list.current_dob);

                  list.detail_cursed ();

                  }

}

            }

 

METHOD no_match (string newval,

                       object helper_domset,

                       string helper_search_domset)

            {

            popup_helper (dispob := "factory_select");

            }

          METHOD Select_2 ()

            {

            if (is_null (Select_2))

{

popup_helper (dispob := "factory_select");

}

            else call (Select_2) ();

            }

METHOD Menu_1 ()

            { if (!is_null (Menu_1)) call (Menu_1) (); }

METHOD Menu_2 ()

            {

            if (!is_null (Menu_2)) call (Menu_2) ();

            else popup_detail ();

            }

METHOD Drag_1 ()

            { if (!is_null (Drag_1)) call (Drag_1) (); }

METHOD Drag_2 ()

            { if (!is_null (Drag_2)) call (Drag_2) (); }

METHOD command (string tag)

            { if (!is_null (command)) call (command) (tag := tag); }

METHOD entry_started ()

            { if (!is_null (entry_started)) call (entry_started) (); }

METHOD entry_finished ()

            { if (!is_null (entry_finished)) call (entry_finished) (); }

METHOD entry_cancel ()

            { if (!is_null (entry_cancel)) call (entry_cancel) (); }

METHOD validate (string oldval, string newval)

            {

            if (is_null (validate)) return "";

            else return call (validate)

                            (oldval := oldval, newval := newval);

            }

}

 

 

      STYLE factory_hier_style HIER (string display = ???,

                      string form = "factory_hier",

                      string entry_started, string entry_finished,

                      string entry_cancel, string validate,

                      string Select_2, string Menu_1, string Menu_2,

                      string Drag_1, string Drag_2, string command)

{

METHOD Select_2 ()

            {

            if (is_null (Select_2)) popup ();

            else call (Select_2) ();

            }

METHOD Menu_1 ()

            { if (!is_null (Menu_1)) call (Menu_1) (); }

METHOD Menu_2 ()

            {

            if (!is_null (Menu_2)) call (Menu_2) ();

            else popup_detail ();

            }

METHOD Drag_1 ()

            { if (!is_null (Drag_1)) call (Drag_1) (); }

METHOD Drag_2 ()

            { if (!is_null (Drag_2)) call (Drag_2) (); }

METHOD command (string tag)

            { if (!is_null (command)) call (command) (tag := tag); }

          METHOD entry_started ()

            { if (!is_null (entry_started)) call (entry_started) (); }

METHOD entry_finished ()

            { if (!is_null (entry_finished)) call (entry_finished) (); }

METHOD entry_cancel ()

            { if (!is_null (entry_cancel)) call (entry_cancel) (); }

METHOD validate (string oldval, string newval)

            {

            if (is_null (validate)) return "";

            else return call (validate)

                            (oldval := oldval, newval := newval);

            }

}

 

 

      in addition default styles are created for int_style, real_style,

      string_style, date_style and duration_style

 

      STYLE type_style DIRECT (

                      string entry_started, string entry_finished,

                      string entry_cancel, string validate,

                      string Select_2, string Menu_1, string Menu_2,

                      string Drag_1, string Drag_2, string command)

{

METHOD Select_2 ()

            { if (!is_null (Select_2)) call (Select_2) (); }

METHOD Menu_1 ()

            { if (!is_null (Menu_1)) call (Menu_1) (); }

METHOD Menu_2 ()

            { if (!is_null (Menu_2)) call (Menu_2) (); }

METHOD Drag_1 ()

            { if (!is_null (Drag_1)) call (Drag_1) (); }

METHOD Drag_2 ()

            { if (!is_null (Drag_2)) call (Drag_2) (); }

METHOD command (string tag)

            { if (!is_null (command)) call (command) (tag := tag); }

METHOD entry_started ()

            { if (!is_null (entry_started)) call (entry_started) (); }

METHOD entry_finished ()

            { if (!is_null (entry_finished)) call (entry_finished) (); }

METHOD entry_cancel ()

            { if (!is_null (entry_cancel)) call (entry_cancel) (); }

METHOD validate (string oldval, string newval)

            {

            if (is_null (validate)) return "";

            else return call (validate)

                            (oldval := oldval, newval := newval);

            }

          }

Outcomes