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



Using a DrawingArea for Graphics

DrawingArea is an appropriate widget to use as a canvas or as a manager that requires graphics operations in addition to children. An application can use Xlib graphics facilities to draw into a DrawingArea. See Xlib--C Language X Interface for more information on Xlib graphics operations.

An interactive graphics application can use the XmNinputCallback procedure to respond to user input. For example, when the user presses a mouse button, drags, and then releases the button, this procedure might draw a line from the point of the button press to the point of the button release. The XmNinputCallback procedures are invoked on button press and release events and on key press and release events. To receive pointer motion events, the application can provide translations that invoke the DrawingAreaInput() action.

An application that needs to produce graphics but does not require children or interaction with the user in the canvas might use a DrawnButton instead of a DrawingArea. DrawnButton has no input callbacks, but it does provide exposure and resize callbacks.

Following is some of the drawing code from the draw.c demo. This example implements the rubber-band effect in which a line starts at an anchor point and follows the pointer as the user moves it.

The example maintains an internal data structure with information about the DrawingArea and its graphic objects. The application initially stores a GC for use in drawing and erasing the rubber-band lines. This GC uses a foreground color that results from combining the DrawingArea's foreground and background using XOR. The GC also uses the GXxor function.

The remainder of the example code updates the internal data structures and draws lines as appropriate when the user starts a line, moves the pointer, and ends a line.

/* Initialize data structures */
static void InitDraw(graph, app_data)
Graphic * graph;
ApplicationData * app_data;
{
    XGCValues val;
    Arg args[5];
    int n;
    Cardinal i;
    Dimension width, height;
    String pstr, wstr;
    int x, y;
    Widget newpush;
 
    /* create the gc used for the rudder banding effect */
    n = 0;
    XtSetArg (args[n], XmNforeground, &val.foreground);  n++;
    XtSetArg (args[n], XmNbackground, &val.background);  n++;
    XtGetValues (graph->work_area, args, n);
 
    val.foreground = val.foreground ^ val.background;
    val.function = GXxor;
    graph->drag_gc = XtGetGC(graph->work_area,
             GCForeground | GCBackground | GCFunction, &val);
 
    /* initialize the graphic stuff */
    graph->in_drag = False;
 
    graph->num_graphics = 0;
    for (i=0; i < MAX_GRAPH; i++) {
        graph->graphics[i].num_points = 0;
    }
 
    /* polylines syntax:
       draw.lines: 10_10, 20_30, 28_139. 11_112, 145_60. 211_112, 45_60
    */
    wstr = XtNewString(app_data->lines);
    for(pstr = (char *) strtok(wstr, ".,"); pstr;
        pstr = (char *) strtok( NULL, ".,")) {
        while (*pstr && isspace(*pstr)) pstr++;
        if (*pstr == ' ') break;
 
        sscanf(pstr, "%d_%d", &x, &y);
        graph->graphics[graph->num_graphics].points
            [graph->graphics[graph->num_graphics].num_points].x = x;
        graph->graphics[graph->num_graphics].points
            [graph->graphics[graph->num_graphics].num_points].y = y;
        graph->graphics[graph->num_graphics].num_points ++;
        graph->graphics[graph->num_graphics].type = POLYLINE;
 
        /* look in the original to see if it is a new unit */
        if (app_data->lines[pstr - wstr + strlen(pstr)] == '.')
            graph->num_graphics ++;
    }
    XtFree(wstr);
 
    if (strlen(app_data->lines)) graph->num_graphics ++;
 
    /* Towns syntax:
         draw.towns: Boston, Woburn, SanJose
         draw*Boston.x: 30
         draw*Boston.y: 30
         draw*Woburn.x: 130
         draw*Woburn.y: 30
         draw*SanJose.x: 30
         draw*SanJose.y: 130
    */
    wstr = XtNewString(app_data->towns);
    for(pstr = (char *) strtok(wstr, ".,"); pstr;
        pstr = (char *) strtok( NULL, ".,")) {
        while (*pstr && isspace(*pstr)) pstr++;
        if (*pstr == ' ') break;
        newpush = XmCreatePushButton(graph->work_area, pstr, NULL, 0);
        XtAddCallback (newpush, XmNactivateCallback, PushCB, NULL);
        XtManageChild (newpush);
    }
    XtFree(wstr);
}
 
static void StartUnit(graph, x, y)
Graphic * graph;
Position x, y;
{
    Widget w = graph->work_area;
 
    graph->drag_point.x = graph->anchor_point.x = x;
    graph->drag_point.y = graph->anchor_point.y = y;
    graph->in_drag = True;
    XDrawLine(XtDisplay(w), XtWindow(w),
              graph->drag_gc,
              graph->anchor_point.x, graph->anchor_point.y,
              graph->drag_point.x, graph->drag_point.y);
}
 
static void DragUnit(graph, x, y)
Graphic * graph;
Position x, y;
{
    Widget w = graph->work_area;
 
    if (!graph->in_drag) return;
 
    XDrawLine(XtDisplay(w), XtWindow(w),
              graph->drag_gc,
              graph->anchor_point.x, graph->anchor_point.y,
              graph->drag_point.x, graph->drag_point.y);
 
    graph->drag_point.x = x;
    graph->drag_point.y = y;
 
    XDrawLine(XtDisplay(w), XtWindow(w),
              graph->drag_gc,
              graph->anchor_point.x, graph->anchor_point.y,
              graph->drag_point.x, graph->drag_point.y);
}
 
 
static Boolean NearPoint (point, x, y)
XPoint point;
Position x, y;
{
#define ERROR 5
    if ((point.x > x - ERROR) &&
        (point.x < x + ERROR) &&
        (point.y > y - ERROR) &&
        (point.y < y + ERROR)) return True;
    else return False;
}
 
 
static void EndUnit(graph, x, y)
Graphic * graph;
Position x, y;
{
    Widget w = graph->work_area;
    Cardinal num_points;
 
    /* no matter what happens, we need to remove the current rubber band
*/
    XDrawLine(XtDisplay(w), XtWindow(w),
              graph->drag_gc,
              graph->anchor_point.x, graph->anchor_point.y,
              graph->drag_point.x, graph->drag_point.y);
 
    /* if the given point if the same as the anchor, we're done with
       this polyline, exit drag mode and be ready for the next
       graphic unit, i.e increment num_graphics */
 
    if (NearPoint(graph->anchor_point, x, y)) {
        graph->in_drag = False;
        /* now see if a new unit needs to be created */
        if (graph->graphics[graph->num_graphics].num_points) {
            graph->graphics[graph->num_graphics].type = POLYLINE;
            if (graph->num_graphics < MAX_GRAPH)
              graph->num_graphics ++;
            else
              printf("The graphic buffer is full, overwrite the
last...\n");
        }
    } else {
 
        /* draw the real line and store it in the structure */
        XDrawLine(XtDisplay(w), XtWindow(w),
          XDefaultGC(XtDisplay(w),XDefaultScreen(XtDisplay(w))),
          graph->anchor_point.x, graph->anchor_point.y,
          x, y);
 
        /* first point in a unit is actually special */
        num_points = graph->graphics[graph->num_graphics].num_points;
        if (num_points == 0) {
            graph->graphics[graph->num_graphics].points[num_points].x =
                graph->anchor_point.x;
            graph->graphics[graph->num_graphics].points[num_points].y =
                graph->anchor_point.y;
            graph->graphics[graph->num_graphics].num_points ++;
            num_points ++;
        }
        graph->graphics[graph->num_graphics].points[num_points].x = x;
        graph->graphics[graph->num_graphics].points[num_points].y = y;
        if (graph->graphics[graph->num_graphics].num_points <
MAX_POINT)
            graph->graphics[graph->num_graphics].num_points ++;
        else printf("The unit buffer is full, overwrite the
last...\n");
 
        /* now start the new unit */
        graph->drag_point.x = graph->anchor_point.x = x;
        graph->drag_point.y = graph->anchor_point.y = y;
        XDrawLine(XtDisplay(w), XtWindow(w),
                  graph->drag_gc,
                  graph->anchor_point.x, graph->anchor_point.y,
                  graph->drag_point.x, graph->drag_point.y);
    }
}
 
static void DeleteUnit(graph, x, y)
Graphic * graph;
Position x, y;
{
    Widget w = graph->work_area;
    Cardinal i,j;
    int a = -1;
 
    /* try to find a unit under this point */
    for (i=0; (i < graph->num_graphics) && (a == -1); i++) {
        for (j=0; j < graph->graphics[i].num_points; j++) {
            if (NearPoint(graph->graphics[i].points[j], x, y)) {
                a = i;
                break;
            }
        }
    }
 
    if (a != -1) {
        /* found a unit under the current point, delete and redisplay */
        for (i = a; i < graph->num_graphics; i++) {
            graph->graphics[i] = graph->graphics[i+1];
        }
        graph->num_graphics --;
 
        XClearArea(XtDisplay(w), XtWindow(w),
                   0, 0, graph->old_width, graph->old_height, True);
    }
}

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