Common Desktop Environment: Help System Author's and Programmer's Guide

Chapter 10 Responding to Help Requests

This chapter explains how to display different types of help information by setting Help Dialog widget resources.

Requesting Help

When a user requests help while using your application, it's the application's responsibility to determine what help topic should be displayed.

Context Sensitivity

Some help requests amount to an explicit request for specific information, such as help on "version" (which usually displays the copyright topic). Other help requests, however, may require some degree of context sensitivity. That is, some processing might be needed to determine the appropriate help topic based on the user's current context within the application.

For instance, your application might test the status of certain modes or settings to determine the appropriate help topic. Or, it might test the value of an input field and provide detailed help if the value is not valid, and general help if the value is valid.

Entry Points

An entry point is a specific location within a help volume--usually the beginning of a topic--that can be directly accessed by requesting help within the application.

From the author's point of view, entry points are established by assigning IDs at the appropriate places within the help volume. From the programmer's point of view, entry points are created by enabling the user to request help and using the appropriate ID when a particular request is made.

There are four general ways for users to request help:

Displaying Help Topics

When a help request is made, the application determines what help topic to display. It then creates (if necessary) and manages a help dialog, and sets the appropriate resources to display a help topic.

Most requests display help topics that are part of the application's help volume. But, the Help System's help dialogs are also capable of displaying man pages, text files, and simple text strings.

The Help System's help dialogs are based exclusively on Xt Intrinsics and OSF/Motif programming, so you change the values within a help dialog just like any other widget: by setting resources.

The DtNhelpType resource determines what type of information is displayed. It can be set to any of these values:

These values are defined in the Help.h file.

See Also

To Display a Help Topic

  1. Create a help dialog.

  2. Set the following resources for the help dialog:

    DtNhelpType

    Set to DtHELP_TYPE_TOPIC.

    DtNhelpVolume

    Set to the volume name for your application.

    DtNlocationId

    Set to the topic ID that you want to display.

    You can also set other values for the dialog, such as its size and title.

  3. Manage the dialog using XtManageChild().

Example

This program segment displays a topic with the ID getting-started in the volume MyVolume.

ac = 0;
XtSetArg (al[ac], DtNhelpType,   DtHELP_TYPE_TOPIC);  ac++;
 XtSetArg (al[ac], DtNhelpVolume,  "MyVolume");         ac++;
 XtSetArg (al[ac], DtNlocationId,  "getting-started");  ac++;
 XtSetArg (al[ac], DtNcolumns,    40);                  ac++;
 XtSetArg (al[ac], DtNrows,       12);                  ac++;
 XtSetValues (helpDialog, al, ac);
 XtManageChild (helpDialog);

If the help volume MyVolume is not registered, then a complete path to the MyVolume.sdl file is required for the value of DtNhelpVolume.

To Display a String of Text

  1. Create a quick help dialog.

    You can use a general help dialog to display string data, but this isn't recommended because most of its features do not apply to string data.

  2. Set the following resources for the help dialog:

    DtNhelpType

    Set to DtHELP_TYPE_DYNAMIC_STRING (if you want word wrap enabled) or DtHELP_TYPE_STRING (if you want the line breaks within the string to be maintained) .

    DtNstringData

    Set to the string you want to display. A copy of the string is kept internally, so you need not maintain your copy of it.

    You can also set other values for the dialog, such as its size and title.

  3. Manage the dialog using XtManageChild().

Example

This program segment displays a string stored in the variable descriptionString.

ac = 0;
 XtSetArg (al[ac], DtNhelpType,   DtHELP_TYPE_DYNAMIC_STRING); ac++;
 XtSetArg (al[ac], DtNstringData, (char *)descriptionString);   ac++;
 XtSetValues (quickHelpDialog, al, ac);
 XtManageChild (quickHelpDialog);

If the string is no longer needed within the application, the memory can be freed, because the help dialog makes its own copy of the data.

 XtFree (descriptionString);

To Display a Text File

  1. Create a quick help dialog or retrieve one from your dialog cache.

    You can use a general help dialog to display a text file, but this isn't recommended because most of its features are useful only for standard help topics.

  2. Set the following resources for the help dialog:

    DtNhelpType

    Set to DtHELP_TYPE_FILE..

    DtNhelpFile

    Set to the file name you want to display. If the file is not in the application's current directory, provide a path to the file.

    You can also set other values for the dialog, such as its size and title. In particular, you might want to set the width to 80 columns, which is the standard width for text files.

  3. Manage the dialog using XtManageChild().

Example

The following program segment displays a file named /tmp/printer.list. It also sets the size of the dialog to better suit a text file.

ac = 0;
 XtSetArg (al[ac], DtNhelpType, DtHELP_TYPE_FILE);      ac++;
 XtSetArg (al[ac], DtNhelpFile,  "/tmp/printer.list");  ac++;
 XtSetArg (al[ac], DtNcolumns,  80);                    ac++;
 XtSetArg (al[ac], DtNrows,     20);                    ac++;
 XtSetValues (quickHelpDialog, al, ac);
 XtManageChild (quickHelpDialog);

To Display a Man Page

  1. Create a quick help dialog.

    You can use a general help dialog to display a man page, but this isn't recommended because most of its features are useful only with standard help topics.

  2. Set the following resources for the help dialog:

    DtNhelpType

    Set to DtHELP_TYPE_MAN_PAGE.

    DtNmanPage

    Set to the name of the man page. The value of this resource is passed directly to the system man command. So, to specify a particular section of a man page, precede the man page name by a section number, just as you would if you were typing the man command conventionally.

    You can also set other values for the dialog, such as its size and title.

  3. Manage the dialog using XtManageChild().

Example

The following program segment displays the man page for the grep command. It also sets the size of the dialog to better suit a man page.

ac = 0;
 XtSetArg (al[ac], DtNhelpType, DtHELP_TYPE_MAN_PAGE);  ac++;
 XtSetArg (al[ac], DtNmanPage,   "grep");                ac++;
 XtSetArg (al[ac], DtNcolumns,  80);                     ac++;
 XtSetArg (al[ac], DtNrows,     20);                     ac++;
 XtSetValues (quickHelpDialog, al, ac);
 XtManageChild (quickHelpDialog);

Enabling the Help Key (F1)

The help key mechanism is a feature built into all OSF/Motif manager widgets and primitive widgets. The help key is enabled by adding a help callback to the widget where you want the help key active.

Within your application, you should add a help callback to each widget where you want a unique entry point into help. The help callback mechanism automatically "walks" up the widget hierarchy (up to the shell widget) until it finds a widget with a help callback, then invokes that callback.

If you add a help callback to a manager widget, when the help key is pressed for any of its children, the manager's help callback is invoked (unless the child widget has a help callback of its own).

To Add a Help Callback

    Use the XtAddCallback() function as follows:

XtAddCallback (
       Widget          widget,
       String          DtNhelpCallback,
       XtCallbackProc  HelpRequestCB,
       XtPointer       clientData );

Where:

widget

The widget where you want to activate the help key.

HelpRequestCB()

The function in your application that handles the help request when the user presses the help key.

clientData

The data you want passed to the HelpRequestCB() function. Typically, this data identifies the topic to be displayed.

When the user presses the help key, the help callback is invoked for the widget with the current keyboard focus. If that widget does not have a help callback, the help callback for its nearest ancestor that does have a help callback is invoked.

If no help callbacks are found, nothing happens. Therefore, it is recommended that you add a help callback to each shell in your application. This ensures that no user requests for help are lost.

Adding a help callback to a dialog shell automatically enables the Help button on the dialog to invoke the help callback.

Importance of Client Data

Specifying a unique value for clientData in each help callback you add saves you the trouble of writing a separate function to process each help callback. Your application can have a single callback procedure to process all help requests (see "To Add a Help Callback"). Within the callback procedure, use the clientData to identify where the user requested help. That is, each time you add a help callback, you should provide a unique value for clientData.

Example

The following example demonstrates one way to associate IDs with entry points. A HelpEntryIds.h file is used to define a unique integer for each clientData value for each help callback. Also defined are two ID strings for each widget: one for normal F1 help, the other for item help mode (where the user picks a widget to get a description).

For this example, assume that the application's user interface is just a main window with three input fields: Name, Address, and Telephone Number. Here's what the HelpEntryIds.h file would contain:

#define HELP_volumeName            "MyVolume"
 #define HELP_MainWindow            100
 #define HELP_MainWindow_ID         "basic-tasks"
 #define HELP_MainWindow_ITEM_ID    "main-window-desc" 
 #define HELP_NameField             101
 #define HELP_NameField_ID          "specifying-a-name"
 #define HELP_NameField_ITEM_ID     "name-field-desc" 
 #define HELP_AddressField          102
 #define HELP_AddressField_ID       "specifying-an-address"
 #define HELP_AddressField_ITEM_ID  "address-field-desc" 
 #define HELP_PhoneField            103
 #define HELP_PhoneField_ID         "specifying-a-phone-no"
 #define HELP_PhoneField_ITEM_ID    "phone-field-desc"

Within the part of the application that initially creates the widgets, a help callback is added to each widget as follows:

XtAddCallback (mainWindow, DtNhelpCallback,
               HelpRequestCB, HELP_MainWindow);
 XtAddCallback (nameField, DtNhelpCallback,
                HelpRequestCB, HELP_NameField);
 XtAddCallback (addressField, DtNhelpCallback,
                HelpRequestCB, HELP_AddressField);
 XtAddCallback (phoneField, DtNhelpCallback,
                HelpRequestCB, HELP_PhoneField);

Within the HelpRequestCB() function, the clientData parameter is used to dispatch the help requests (through a switch() statement). Within each case, the value of a global flag itemHelp is tested to see if the help callback was invoked by the F1 key (the flag is "false") or by the user picking the widget in item help mode (the flag is "true").

XtCallbackProc HelpRequestCB (
       Widget     w,
       XtPointer  clientData,
       XtPointer  callData )
 {
    char    *topicToDisplay;
    Boolean  useQuickHelpDialog;
    /* Determine the topic ID for the given ` clientData.' */
    switch ((int)clientData)
      {
        case HELP_MainWindow:
          useQuickHelpDialog = False;
          if (itemHelpFlag)
            topicToDisplay = HELP_MainWindow_ITEM_ID;
          else
            topicToDisplay = HELP_MainWindow_ID;
          break;       case HELP_NameField:
          useQuickHelpDialog = True;
          if (itemHelpFlag)
            topicToDisplay = HELP_NameField_ITEM_ID;
          else
            topicToDisplay = HELP_NameField_ID;
          break;       case HELP_AddressField:
          useQuickHelpDialog = True;
          if (itemHelpFlag)
            topicToDisplay = HELP_AddressField_ITEM_ID;
          else
            topicToDisplay = HELP_AddressField_ID;
          break;       case HELP_PhoneField:
          useQuickHelpDialog = True;
          if (itemHelpFlag)
            topicToDisplay = HELP_PhoneField_ITEM_ID;
          else
            topicToDisplay = HELP_PhoneField_ID;
          break;       default:
          /* An unknown clientData was received. */
          /* Put your error handling code here. */
          return;
          break;
      }
    /* Display the topic. */
    ac = 0;
    XtSetArg (al[ac], DtNhelpType,   DtHELP_TYPE_TOPIC); ac++;
    XtSetArg (al[ac], DtNhelpVolume, HELP_volumeName);    ac++;
    XtSetArg (al[ac], DtNhelpType,   topicToDisplay);     ac++;
    if (useQuickHelpDialog)
      {
         XtSetValues (mainQuickHelpDialog, al, ac);
         XtManageChild (mainQuickHelpDialog);
      }
    else
      {
         XtSetValues (mainHelpDialog, al, ac);
         XtManageChild (mainHelpDialog);
      }
    /* Clear the ` item help' flag. */
    itemHelpFlag = False;
  }

The preceding function assumes that the application uses two help dialogs for all help requests (mainHelpDialog and mainQuickHelpDialog), and that those dialogs have already been created. It also assumes that al and ac (used in assembling Xt argument lists) are declared elsewhere.

Providing a Help Menu

The CDE Style Guide and Certification Checklist recommends that each menu bar include a Help menu. The Help menu may contain a variety of commands that let the user access different types of online help for your application.

The most important commands include:

See Also

Supporting Item Help Mode

Some applications provide an On Item or Help Mode command in their Help menu. This command temporarily redefines the mouse pointer as a ? (question mark) to prompt the user to select an item on the screen. When an item is selected, the application is expected to display a description of the item.

The convenience function, DtHelpReturnSelectedWidgetId(), changes the pointer to a question mark and waits for the user to pick a widget. The ID of the selected widget is returned. This function is similar to the XmTrackingLocate() function except that DtHelpReturnSelectedWidgetId() returns NULL if the user presses Escape to cancel the operation.

To display help on the selected item, your application can simply invoke the help callback for the returned widget. This is equivalent to the user pressing F1 while using that widget.

If you want the application to differentiate between item help and F1 help, you can set a flag before calling the widget's help callback. The help callback procedure can then use that flag to determine that the callback was invoked as a result of item help and alter its response accordingly.

To Add Support for Item Help

  1. Write a function that uses the DtHelpReturnSelectedWidgetId() function. Within this function, invoke the help callback for the selected widget. In the following steps, this function is called ProcessOnItemHelp(), but you can name it whatever you want.

  2. Add to your Help menu a command button labeled On Item. Add an activate callback that invokes your ProcessOnItemHelp() function.

  3. Add a help callback to each widget in your application where you want item help to be available.

    If the selected widget does not have a help callback, the application should try its parent widget. Similarly, if the parent does not have a help callback, the application should continue to walk up the widget hierarchy until it finds a help callback.

Example

The following procedure is a sample ProcessOnItemHelp() function that would be invoked by choosing On Item from the Help menu.

void  ProcessOnItemHelp(
   Widget widget)
 {
  /* Declare a variable for the selected widget. */ 
  Widget selWidget=NULL;
   int status=DtHELP_SELECT_ERROR;
  /* Get an application shell widget from our widget hierarchy to
        * pass into DtHelpReturnSelectedWidgetId().
    */
  while (!XtIsSubclass(widget, applicationShellWidgetClass))
                    widget = XtParent(widget);
  status = DtHelpReturnSelectedWidgetId(widget, NULL, &selWidget);
  switch ((int)status)
     {
       case DtHELP_SELECT_ERROR:
         printf("Selection Error, cannot continue\n");
      break;
      case DtHELP_SELECT_VALID:
          /* We have a valid widget selection, now let's look for a registered help
                        * callback to invoke.
           */
         while (selWidget != NULL)
           {
             if ((XtHasCallbacks(selWidget, XmNhelpCallback)
                                      == XtCallbackHasSome))
               {
                 /* Found a help callback, so just call it */
                XtCallCallbacks((Widget)selWidget,
                                 XmNhelpCallback,NULL);
                 break;
               }
            else
              /* No help callback on current widget, so try the widget's parent  */
                 selWidget = XtParent(selWidget);
           }
      break;
       case DtHELP_SELECT_ABORT:
         printf("Selection Aborted by user.\n");
      break;
      case DtHELP_SELECT_INVALID:
         printf("You must select a component within your app.\n");
      break;
     }
 }