[ Previous | Next | Contents | Glossary | Home | Search ]
Motif 2.1 Widget Writer's Guide



Writing Your Own DialogBox Button

The ExmCommandButton widget demonstrates how to code a button widget for a DialogBox. As Figure 11-1 shows, ExmCommandButton is a subclass of ExmString. ExmCommandButton therefore inherits ExmString's ability to display a compound string. ExmCommandButton layers on the additional ability to function as an activatable button inside a DialogBox, much the way an XmPushButton is used as an OK button inside a standard selection box.

Figure 6. ExmCommandButton Position in Hierarchy.




View figure.

In the following subsections, we examine a few features of the ExmCommandButton.

Activation

Every DialogBox button widget must be activatable. In other words, when the user explicitly or implicitly chooses your button, your button must call the appropriate activate callbacks. The ExmCommandButton widget does the following in order to become activatable:

  1. Provides an activate callback.

  2. Installs the XmQTactivatable trait on the widget

  3. Providing an Activate Callback

    ExmCommandButton provides an activate callback resource named XmNactivateCallback. The XmNactivateCallback resource is defined in the resources array of CommandB.c as follows:

    {
      XmNactivateCallback,
      XmCCallback,
      XmRCallback,
      sizeof(XtCallbackList),
      XtOffsetOf( ExmCommandButtonRec, command_button.activate_callback),
      XmRPointer,
      (XtPointer) NULL
    },

    When an ExmCommandButton is activated, it must initialize a callback structure and then call XtCallCallbackList. Both of these things are done in several different action methods of ExmCommandButton. For example, following is the ExmCommandButtonActivate action method:

    ExmCommandButtonActivate (
            Widget w,
            XEvent *event,
            String *params,
            Cardinal *num_params
    )
    {
     ExmCommandButtonWidget cw = (ExmCommandButtonWidget)w;
     XmAnyCallbackStruct cb;
     
      if (cw->command_button.activate_callback) {
     /* Initialize a callback structure. */
         cb.reason = XmCR_ACTIVATE;
         cb.event = event;
         XFlush (XtDisplay(cw));
     /* Call XtCallCallbackList. */
         XtCallCallbackList (w, cw->command_button.activate_callback,
    &cb);
      }
    }

    The XmNactivateCallback resource of ExmCommandButton uses the generic callback structure XmAnyCallbackStruct. Your own DialogBox button widget may define a more elaborate activation callback structure.

    Installing the XmQTactivatable Trait

    The XmQTactivatable trait tells a DialogBox that the ExmCommandButton widget wishes to be treated as a DialogBox button. In other words, Motif DialogBox widgets check their children for this trait. Only those children holding this trait are eligible to become DialogBox buttons.

    The XmQTactivatable trait defines a trait method called changeCB. A DialogBox calls this trait method to add or remove a callback from the list of activate callbacks held by the DialogBox button widget.

    The following shows how ExmCommandButton codes the changeCB trait method:

    ChangeCB(
             Widget widget,
             XtCallbackProc activCB,
             XtPointer clientData,
             Boolean setUnset)
    {
      if (setUnset)
        XtAddCallback (widget, XmNactivateCallback, activCB, clientData);
      else
        XtRemoveCallback (widget, XmNactivateCallback, activCB, clientData);
    }

    The Default DialogBox Button

    The Motif Style Guide mandates that a DialogBox widget have a default action associated with it at all times. For example, consider an XmSelectionBox having three DialogBox buttons: an OK button, a Cancel button, and an Apply button. Of these three buttons, suppose the OK button is the current default button. When the user activates the default action (typically by pressing Return anywhere in the DialogBox), the DialogBox must act as if the OK button was explicitly pushed.

    The default button must provide some visual cue so that users will know that it is, in fact, the default button. Typically, the default button does this by displaying a border around itself; however, the widget writer is somewhat free to pick a different kind of visual cue. (See the Motif Style Guide for the exact constraints regarding this visual cue.)

    To support default activation, Motif provides the XmQTtakesDefault trait. All dialog buttons must install this trait. This trait announces to DialogBox widgets that the dialog button is capable of changing its appearance to show that it is the default button.

    The XmQTtakesDefault trait provides only one trait method, showAsDefault. The DialogBox managing the button will typically call this trait method one or more times, each time asking the button to get into a different state. The DialogBox will probably ask each of its buttons to get into the XmDEFAULT_READY state first. Each ExmCommandButton responds to this by expanding its margins. The DialogBox widget will then ask one of its buttons to become the default button by asking it to go into the XmDEFAULT_ON state. ExmCommandButton responds to this state by displaying distinctive highlighting. If a different button becomes the default button, the DialogBox widget asks the former default button to go into the XmDEFAULT_OFF state.ExmCommandButton responds to this request by removing its distinctive highlighting.What follows is the implementation of the showAsDefault trait method of ExmCommandButton:

    ShowAsDefault(Widget w,
                  XtEnum state)
    {
     ExmCommandButtonWidgetClass cbwc =
    (ExmCommandButtonWidgetClass)XtClass(w);
     ExmCommandButtonWidget cbw = (ExmCommandButtonWidget)w;
     Position   start_x_of_outer_shadow,  start_y_of_outer_shadow;
     Dimension  margin_push_out;
     Dimension  width_of_outer_shadow, height_of_outer_shadow;
     int   dx, dy, width, height;
     GC    top_GC, bottom_GC;
     Dimension outer_shadow_thickness;
     int       outer_shadow_type;
     int       margins_were_pushed_out=0;
    #define MARGIN_BETWEEN_HIGHLIGHT_AND_OUTER_SHADOW 2
     
      start_x_of_outer_shadow = cbw->primitive.highlight_thickness +
                                MARGIN_BETWEEN_HIGHLIGHT_AND_OUTER_SHADOW;
      start_y_of_outer_shadow = cbw->primitive.highlight_thickness +
                                MARGIN_BETWEEN_HIGHLIGHT_AND_OUTER_SHADOW;
      width_of_outer_shadow  = cbw->core.width - (2 *
    start_x_of_outer_shadow);
      height_of_outer_shadow = cbw->core.height - (2 *
    start_y_of_outer_shadow);
      outer_shadow_thickness = 3;
     
       switch (state) {
         case XmDEFAULT_READY:
           /* Push out the margins to make room for subsequent increases in
              the shadow thickness. The request to push out the margins will
              increase the size of the CommandButton widget assuming that
    its
              manager has the space to spare. */
     
              if (cbw->primitive.shadow_thickness < 5)
                margin_push_out = 5;
              else
                margin_push_out = cbw->primitive.shadow_thickness;
              margins_were_pushed_out = 1;
              XtVaSetValues((Widget)cbw,
                 XmNmarginWidth,  cbw->simple.margin_width +
    margin_push_out,
                 XmNmarginHeight, cbw->simple.margin_height +
    margin_push_out,
                 NULL);
              break;
         case XmDEFAULT_ON:
           /* Draw an outer shadow. The outer shadow is drawn outside the
              widget's margins but inside the border highlight.
              The inner shadow is drawn by the DrawShadow method. */
              top_GC = cbw->primitive.top_shadow_GC;
              bottom_GC = cbw->primitive.bottom_shadow_GC;
              outer_shadow_type = cbw->command_button.visual_armed ?
                                             XmSHADOW_ETCHED_IN:
                                             XmSHADOW_ETCHED_OUT;
              XmeDrawShadows(XtDisplay(w), XtWindow(w),
                             top_GC,
                             bottom_GC,
                             start_x_of_outer_shadow,
                             start_y_of_outer_shadow,
                             width_of_outer_shadow,
                             height_of_outer_shadow,
                             outer_shadow_thickness,
                             outer_shadow_type);
              break;
         case XmDEFAULT_OFF:
           /* Erase the outer shadow when the widget is no longer the
              default. */
              XmeClearBorder(XtDisplay(w), XtWindow(w),
                             start_x_of_outer_shadow,
                             start_y_of_outer_shadow,
                             width_of_outer_shadow,
                             height_of_outer_shadow,
                             outer_shadow_thickness);
              break;
         case XmDEFAULT_FORGET:
         default:
           /* The widget is not a potential default button. If
    XmDEFAULT_FORGET
              is called at some point after XmDEFAULT_READY was called, then
              we have to restore the margins back to their original size. */
              if (margins_were_pushed_out)
                XtVaSetValues((Widget)cbw,
                  XmNmarginWidth,  cbw->simple.margin_width -
    margin_push_out,
                  XmNmarginHeight, cbw->simple.margin_height -
    margin_push_out,
                  NULL);
              break;
        }
    }

    [ Previous | Next | Contents | Glossary | Home | Search ]