Section 12 Home page Section 14

13. Dialogs

A dialog is a collection of widgets, such as text fields, check boxes and buttons, where the user can view or provide information. Dialogs usually require a response from the user before the program will continue processing. The file selector is an important example of a dialog: when a file selector is shown, the user must either select a file or cancel the dialog to proceed.

There are two ways of creating our own dialogs. The first presents a simple set of information, and allows one of up to three buttons to be clicked in response. The second way allows for arbitrarily complex dialogs to be constructed.

13.1. File Selector

This is an important and useful dialog, enabling the user to select a filename to save or open. The main function to use is fsel_exinput. This works on all TOS versions from 1.04 up. The function will call whichever file selector the user has installed: either the basic TOS file selector, the one supplied by XaAES, or even a custom third-party file selector.

We call the function in the following way:

char path[1000], name[200];                                     // <1>
int i, button;                                                  // <2>

for (i = 0; i < 1000; path[i++] = '\0');                        // <3>
for (i = 0; i < 200; name[i++] = '\0');
path[0] = Dgetdrv() + 65;                                       // <4>
strcpy (&path[1], ":\\*.*");                                    // <5>
fsel_exinput (path, name, &button, "Select text file to open"); // <6>

if (button == 1) {                                              // <7>
        // DO SOMETHING WITH THE FILE
}
  1. Set aside some space for the path and filename.

  2. Create some variables: button is used for the clicked button.

  3. Clear out the path and filename.

  4. Set the initial path to the current drive (Dgetdrv returns 0 for drive A, 2 for drive C etc, so add to ASCII for A to get the drive letter).

  5. Add the general file pattern to the end of the path, along with a file mask.

  6. Call the file selector. The last argument is a message, displayed on the dialoy. This lets you provide a hint to the user of what they need to do.

  7. Check if the OK button was clicked.

Note how we need some space for the path and filename. These need to be large enough to accommodate nested folders, and also, if you are using a modern AES, long filenames. The file mask added to the end of the path may be general, like "*.*" or more restricted, like "*.C" or "*.TXT". The file selector will initially show the files at the given path using any mask, and will modify the path and name values based on what the user selects. button is returned with the value 0 for cancel and 1 for OK.

(If you are using a very old version of TOS, replace fsel_exinput with fsel_input, which works the same except that there is no message label.)

13.2. Form Alerts

The form alert is the simplest kind of dialog. The user is presented with up to 5 lines of text, and up to 3 buttons to choose from. An optional icon may also be displayed.

Creating and showing a dialog is very simple. The dialog’s description is created in a string, made of three parts:

  1. icon: the number of the icon to use, 1 for warning, 2 for query, 3 for stop.

  2. text: up to 5 lines of text, separated by | marks (up to 32 characters each line).

  3. buttons: up to 3 button labels, separated by | marks (up to 20 characters per button - but the width must be less than the text width).

The call is simply:

int result = form_alert (1, "[icon][text][buttons]"); // <1>
  1. The first parameter indicates the default button, which is triggered by pressing return. result holds the button number that was clicked.

For example, a query about whether the program should overwrite an existing file might go:

int result = form_alert (1, "[2][File already exists|Overwrite?][Overwrite|Cancel]");

if (result == 1) {
        // overwrite the file
} else {
        // cancel the operation
}

13.3. User-Defined Dialogs

More complex user-defined dialogs may be built using your resource construction program, such as ResourceMaster. The definitions are saved within the RSC file, and can be retrieved by our program. (The sample code in this section is taken from the temperature conversion sample program.)

Creating and deleting a dialog is fairly straightforward:

rsrc_gaddr (R_TREE, TEMP, &dial_addr);                                  // <1>

form_center(dial_addr, &dial_x, &dial_y, &dial_w, &dial_h);             // <2>
form_dial(FMD_START, 0, 0, 10, 10, dial_x, dial_y, dial_w, dial_h);     // <3>

// WORK WITH DIALOG

form_dial(FMD_FINISH, 0, 0, 10, 10, dial_x, dial_y, dial_w, dial_h);    // <4>
  1. Retrieve the address of the dialog from the RSC file.

  2. Centre the dialog on the screen: the variables are given the values for the dialog’s x,y,w,h coordinates.

  3. Create the dialog (START). The 0,0,10,10 is the origin of the growing boxes animation.

  4. Delete the dialog (FINISH). The 0,0,10,10 is the target of the shrinking boxes animation.

The main interactions with a dialog are through any text that the user may input, and buttons that the user may click. (Different versions of AES and Resource Construction programs may offer additional widgets.)

The dialog is managed using a version of the event loop, already familiar to us. Dialogs have their own event function, form_do.

int choice;

do {

        objc_draw(dial_addr, 0, 8, dial_x, dial_y, dial_w, dial_h);     // <1>
        choice = form_do (dial_addr, TEMP_TEMP);                        // <2>

        /* do action */
        switch (choice) {                                               // <3>

                // CASE STATEMENTS, ONE FOR EACH BUTTON
        }

        /* revert the button to normal state */
        dial_addr[choice].ob_state = NORMAL;                            // <4>

} while (choice != TEMP_CLOSE);                                         // <5>
  1. Draw the dialog on the screen, at given location/size.

  2. Allow the user to interact with the dialog, and return the value of any button that was clicked. The second argument should identify the widget to take initial focus: in this case we use the input text field.

  3. Do the appropriate action for the clicked button.

  4. Like menu items, buttons are inverted when clicked: we must set their state back to normal when we have finished processing them.

  5. Exit the event loop when the close button has been clicked.

Strings are handled by locating a pointer to the string in the dialog box. The following function locates a given string:

/* returns a pointer to an editable string in a dialog box */
char * get_tedinfo_str (OBJECT * tree, int object) {
        return tree[object].ob_spec.tedinfo->te_ptext;
}

Strings can be read from and displayed on the dialog directly from the returned pointer. For example, the sample program has the following sequence:

char * temp;                                                            // <1>
char * result;

rsrc_gaddr (R_TREE, TEMP, &dial_addr);

temp = get_tedinfo_str (dial_addr, TEMP_TEMP);                          // <2>
result = get_tedinfo_str (dial_addr, TEMP_RESULT);

sprintf (temp, "");                                                     // <3>

// .... IN SWITCH STATEMENT

        case TEMP_CONVERT:
                sprintf (result, "Fahr: %d", 32+(9*atoi(temp))/5);      // <4>
                break;
  1. Create pointers for the strings in the dialog.

  2. Associate the pointers with the actual dialog strings.

  3. Clear the input part of one of the strings.

  4. Read the value in the string temp, for the value typed by the user, and write the value to display directly into result.

Note
In the sample program, the first field TEMP_TEMP is an input, of type FTEXT. It has an initial displayed string, and then the value. Only the value is read or changed in our program. The second field TEMP_RESULT is for the output, and is of type TEXT. When we write to this, we need to also write the initial title for the string, because it was included in the field when the dialog was constructed.

13.4. Sample Program: Version 8

Version 8 of the program now includes an option to open a file stored on disk as a text file, illustrating the use of a file selector dialog. A useful part of the code combines the path and name from the file selector into a complete filename:

if (button == 1) {
        /* Create some space to store the combined path and name */
        char * filename = malloc (sizeof(char) * (strlen(path)+strlen(name)+1));
        /* Add the path to the filename */
        strcpy (filename, path);
        /* find last \ (to remove the file mask at end of path)
           Every path must have a \ character.
         */
        for (i = strlen(path); i >= 0 && filename[i] != '\\'; i -= 1);
        /* Add the name to filename, overwriting any extension mask */
        strcpy (filename+i+1, name);

        // DO SOMETHING WITH THE FILE

        /* Return the space allocated */
        free (filename);
}

13.5. Sample Program: Temperature Converter

This sample program, in the folder "TEMPCONV", is a standalone program. It provides a simple dialog which converts temperatures from fahrenheit to celsius. It illustrates how to access information in text fields, and how to respond to button clicks.