# App Builder Concepts

- ABProcess

- ABProjectsModules

- ABMessageExample

- ABAboutAttachments

## Summary of the App Builder Process

The process of creating an App Builder user interface can be summarized as follows.

1. Design the interface; draw a picture of it.

2. Perform an App Builder "spec" of the interface (determine what App Builder objects will be used for each of the parts of the interface design).

3. Start App Builder.

4. Drag and drop a main window onto the desktop; this will be the starting point for all functions in the finished application.

5. Drag and drop a control pane onto the main window; controls will be dropped onto the control pane.

6. Drag and drop the appropriate controls onto the control pane; use the group, alignment, and attachment tools of App Builder to position the controls properly.

7. Drag and drop other main windows and custom dialogs onto the desktop to create the other parts of the interface.

8. Make programmatic connections between objects in the interface; test the connections in test mode.

9. Refine the interface and continue testing until it is right.

10. Generate code by running the App Builder code generator; add user code to complete the functionality of the application.

11. Compile, run, test, iteratively until the application is complete.

## App Builder Projects and Modules

- ABProjectDef

- ABModuleDef

### What Is a Project?

When you use App Builder to create a graphical user interface, you are working on a project, which is comprised of one or more modules. App Builder, which was built with itself, was a single project, comprised of over 30 modules.

A project file is started when you select New Project in the File menu or when you drag and drop a window onto the desktop in a new session of App Builder.

A project file is saved when you select Save Project in the File menu or when you select Save in the Project menu in the Project Organizer. A project file has a `.bip` (builder interface project) suffix.

### See Also

1. - ABModuleDef

### What Is a Module?

A module is a logical unit of a project. Each window and dialog in App Builder is a module of the App Builder project, for instance.

All module files in a project are saved when the project is saved. You can explicitly save a particular module by selecting Save in the Module menu in the Project Organizer.

A saved module file has a `.bil` (builder interface language) suffix.

### See Also

1. - ABProjectDef

## Example: Writing Code for Messages

Once you have created a message as described in ABCreateMessage, you must determine when and how it should be displayed. Usually messages are displayed after a certain piece of logic has been executed. For example, if a user types digits in a text field that is designed to accept a name, you will want to post an error message informing the user that digits are not valid.

Message boxes in Motif can be displayed in one of two ways: modally or non-modally (equivalently, blocking or non-blocking). The App Builder code generator (`dtcodegen`) supplies two routines, corresponding to the two modes of display. They are found in `dtb_utils.c` and are named:

- `dtb_show_modal_message()`

- `dtb_show_message()`

If you want to display a particular message modally, use `dtb_show_ modal_message()`. If you want to display a particular message non-modally, use `dtb_show_message()`.

## See Also

1. - ABCodingModal

   - ABCodingNonModal

### Coding for Modal Messages

As explained above, if a message is to be displayed modally, use `dtb_show_modal_message()`. This routine returns a value which indicates which message box button the user has pressed. The value is an `enum` that is defined in `dtb_utils.h`:

    /*
    	 * Returns answer value for modal MessageBox
    	 */
    	typedef enum {
    	    DTB_ANSWER_NONE,
    	    DTB_ANSWER_ACTION1,
    	    DTB_ANSWER_ACTION2,
    	    DTB_ANSWER_ACTION3,
    	    DTB_ANSWER_CANCEL,
    	    DTB_ANSWER_HELP
    	} DTB_MODAL_ANSWER;

You can then examine the return value (for example via a switch statement) and execute the appropriate piece of code.

Here's an example of displaying a message modally. Say that you have created a simple application, named `foo`. The project is named `foo.bip` and consists of one module, `foo.bil`. The module `foo.bil` consists of a main window, control pane, and two text fields, one for the user to enter a person's first name and the other to enter the last name. If the user types digits, an error message should be posted, informing the user that digits are not allowed, and giving the user a couple of options. The user can start over, which means the text entered will be erased, or the user can continue, which means that the text entered will be left intact, giving the user discretion as to how to modify the text.

A call-function connection is made for both text fields, which will be called each time the user types something. The function for the first text field will check if the character typed is a digit. If so, it will post the error message modally:

    void
    verify_first_nameCB(
        Widget widget,
        XtPointer clientData,
        XtPointer callData
    )
    {
        /*** DTB_USER_CODE_START vvv Add C variables and code below vvv ***/
        char                *text = (char *)NULL;
        int                 textlen = 0;
        DTB_MODAL_ANSWER    answer = DTB_ANSWER_NONE;
        DtbFooMainwindowInfo instance = (DtbFooMainwindowInfo) clientData;

        /*** DTB_USER_CODE_END   ^^^ Add C variables and code above ^^^ ***/

        /*** DTB_USER_CODE_START vvv Add C code below vvv ***/

        text = XmTextFieldGetString(widget);
        if ((text != NULL) && (*text != NULL))
        {
            textlen = strlen(text);
            if (isdigit(text[textlen-1]))
            {
                dtb_foo_message_initialize(&dtb_foo_message);
                answer = dtb_show_modal_message(instance->textfield,
                            &dtb_foo_message, NULL, NULL, NULL);
                switch (answer)
                {
                    case DTB_ANSWER_ACTION1:        /* Start Over */
                        XmTextFieldSetString(widget, "");
                        break;

                    case DTB_ANSWER_ACTION2:	/* Continue */
                        break;
                }
            }
        }

        /*** DTB_USER_CODE_END   ^^^ Add C code above ^^^ ***/
    }

### See Also

1. - ABCodingNonModal

### Coding for Non-Modal Messages

If you want to post a non-modal message, you should use `dtb_show_message()`. The callbacks for the message box buttons should be specified via the Connections Editor, as described in ABConnectMessage. The buttons that are specified for the message box are displayed as When items for the message object in the Connections Editor.

Using the same example as above, make the last name text field display the error message non-modally if the user types a digit. As previously mentioned, first you'll need to make a couple of call-function connections for the two buttons in the message box, labelled "Start Over" and "Continue." When code is generated, add code to those routines to do the right thing. The start over routine will clear out the text field and the continue routine will do nothing, in this case.

    void
    verify_last_nameCB(
        Widget widget,
        XtPointer clientData,
        XtPointer callData
    )
    {
        /*** DTB_USER_CODE_START vvv Add C variables and code below vvv ***/
        char                *text = (char *)NULL;
        int                 textlen = 0;
        DtbFooMainwindowInfo instance = (DtbFooMainwindowInfo) clientData;

        /*** DTB_USER_CODE_END   ^^^ Add C variables and code above ^^^ ***/

        /*** DTB_USER_CODE_START vvv Add C code below vvv ***/

        text = XmTextFieldGetString(widget);
        if ((text != NULL) && (*text != NULL))
        {
            textlen = strlen(text);
            if (isdigit(text[textlen-1]))
            {
                dtb_foo_message_initialize(&dtb_foo_message);
                dtb_show_message(instance->textfield,
                            &dtb_foo_message, NULL, NULL);
            }
        }

        /*** DTB_USER_CODE_END   ^^^ Add C code above ^^^ ***/
    }

    void
    start_overCB(
        Widget widget,
        XtPointer clientData,
        XtPointer callData
    )
    {
        /*** DTB_USER_CODE_START vvv Add C variables and code below vvv ***/

        DtbFooMainwindowInfo instance = (DtbFooMainwindowInfo) clientData;

        /*** DTB_USER_CODE_END   ^^^ Add C variables and code above ^^^ ***/

        /*** DTB_USER_CODE_START vvv Add C code below vvv ***/

        XmTextFieldSetString(dtb_foo_mainwindow.textfield2, "");

        /*** DTB_USER_CODE_END   ^^^ Add C code above ^^^ ***/
    }

    void
    continueCB(
        Widget widget,
        XtPointer clientData,
        XtPointer callData
    )
    {
        /*** DTB_USER_CODE_START vvv Add C variables and code below vvv ***/
        /*** DTB_USER_CODE_END   ^^^ Add C variables and code above ^^^ ***/

        /*** DTB_USER_CODE_START vvv Add C code below vvv ***/
        /*** DTB_USER_CODE_END   ^^^ Add C code above ^^^ ***/
    }

The two routines above, `start_overCB()` and `continueCB()`, are added as callbacks for the two buttons via the call to `dtb_show_message()`. Here is the code fragment that adds the callback (from `dtb_utils.c`):

    /* Add Callbacks if necessary */
        if (mbr->action1_callback != (XtCallbackProc) NULL)
            XtAddCallback(msg_dlg, XmNokCallback, mbr->action1_callback, NULL);
        if (mbr->cancel_callback != (XtCallbackProc) NULL)
            XtAddCallback(msg_dlg, XmNcancelCallback, mbr->cancel_callback,
    NULL);
        if (mbr->action2_callback != (XtCallbackProc) NULL)
        {
            action_btn = dtb_MessageBoxGetActionButton(msg_dlg, DTB_ACTION2_BUTTON);
            if (action_btn != NULL)
                XtAddCallback(action_btn, XmNactivateCallback,
                              mbr->action2_callback, NULL);
        }
        if (mbr->action3_callback != (XtCallbackProc) NULL)
        {
            action_btn = dtb_MessageBoxGetActionButton(msg_dlg, DTB_ACTION3_BUTTON);        if (action_btn != NULL)
                XtAddCallback(action_btn, XmNactivateCallback,
                              mbr->action3_callback, NULL);
        }

The structure `mbr` contains all the necessary information for the message. The structure is filled in with the values specified in the Message Editor when the message object was created via the `dtb_<modulename>_<messagename>_initialize()` routine -- in this example, `dtb_foo_message_initialize()`.

### See Also

1. - ABCodingModal

## About Attachments

Attachments (and groups, which are based on attachments) establish dynamic layout behavior for objects in the interface. Dynamic layout behavior ensures that objects will maintain consistent relationships during resize activities. Attachments enable an internationalized application to work well in a number of locales. All child objects are attached by their top and left edges to the top and left edge of their parent object, by default. Thus a control pane dropped on a main window is attached by its left and top edges to the left and top edges of the main window. Similarly, a button dropped on the control pane is attached to the control pane.

If the parent object is resized in an upward or leftward direction, the child object moves with the parent, maintaining the distance from the top and left edge of the parent.

If a pane object is dropped on the top or left edge of its parent it will be attached to that edge with an offset of 0. If it is dropped some distance to the right and below the left and top edges of its parent, it will have positive offsets.

If a pane object is resized from its right and bottom edges so that it spans its parent object, it will be attached to the right and bottom edges of its parent.

## Attachment Example: Custom Dialog

1. See an App Builder custom dialog object for an example of attachments. Each of the buttons at the bottom of the custom dialog are attached to the top and sides of their enclosing dialog panel. They are attached five pixels from the top of the panel and varying percentages from the left edge of the panel (Button1 left edge 10%, right edge 30%; Button2 40% and 60%; Button3 70% and 90%).

   The left edge of Button1 will always be 10% from the edge of the panel and the right edge of Button1 will always be 30% from the edge of the panel. Button1 will, therefore, always be as wide as 20% of the total width of the panel. Button2's edges are 40% and 60% from the left edge of the panel; Button3's edges are 70% and 90% from the left edge of the panel. The three buttons will grow and shrink as the panel grows and shrinks, and the distance between them will always be 10% of the total width of the panel.