Next: draw_input2.c - drawing to a pixmap Up: Using DrawingAreas in Practice Previous: draw.c - Basic Drawing

draw_input.c - Input to a DrawingArea

The draw_input.c program accepts mouse input in the DrawingArea. It allows the user to select a colour (as we have seen previously) and then draw a variable size rectangle that is shaded with a colour. A clear DrawingArea facility is also provided.

Fig. 13.3.2 Output of draw_input.c

How are we going to achieve this?

Fig. 13.3.2 Output of draw_sil.c

To detect mouse clicks up and down we can use the XmNinputCallback resource.

However the default setting of Callback resources in a DrawingArea Widget does not allow for mouse motion to be detected as we would like.

We therefore have to override the default callback options.

Every Widget has a Translation Table that contains a list of events it can receive and actions to take upon receipt of an event. We basically have to create a new translation table.

Translation Tables

A translation table consist of events like this (This is an excerpt of the default DrawingArea translation):


   ..............
   <Btn1Down>:    DrawingAreaInput() ManagerGadgetArm() 
   <Btn1Up>:      DrawingAreaInput() ManagerGadgetActivate()
   <Btn1Motion>:  ManagerGadgetButtonMotion() 
   ..............

Our problem is that Button motion does not get passed to the DrawingAreaInput() function notifies the program of an input event.

To Create a new translation table we simply include the functions and events we need. In this case:


 <Btn1Down>:   draw_cbk(down) ManagerGadgetArm() 
 <Btn1Up>:     draw_cbk(up) ManagerGadgetActivate()
 <Btn1Motion>: draw_cbk(motion) ManagerGadgetButtonMotion()

where draw_cbk() is our callback that performs the drawing and we send a message that gets picked up so we can detect each action. The arm, activate and motion gadget manager functions control the (default) display of an event action.

We Must register the callback with the actions associated with the translation events. We use XtAppAddActions() function to do this.

In a motif program, we set up our translation table in a String structure and use the XtParseTranslationTable(String*) to attach a translation table to the XmNtranslations resource.

The Callback function (draw_cbk(Widget w, XButtonEvent *event, String *args, int *num_args).

In this program we can inspect the args[0] String to see if an up, down or motion event has occurred.

The actions that we take in the draw_cbk() function is as follows:

Mouse Down
- just remember the coordinates of the mouse. These are found in the x,y elements of the event structure.
Mouse Motion
- draw a dashed line silhouette of the box whose current size is determined by mouse down and current mouse position.

NOTE: We set the Graphics Context Logical Function to GXinvert which means that pixels simply get inverted. We must invert them once more to get them back to their original state before we redraw again at another mouse position.

Mouse Up
- we finally draw our rectangle with the desired colour.

The complete program listing of draw_input.c is :


#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/DrawingA.h>


GC gc;
XGCValues gcv;
Widget draw;
String colours[] = { "Black",  "Red", "Green", "Blue", 
                     "Grey", "White"};
long int fill_pixel = 1; /* stores current colour 
                         of fill - black default */
Display *display; /* xlib id of display */
Colormap cmap;


main(int argc, char *argv[])

{   Widget top_wid, main_w, menu_bar, quit, clear, colour;
    XtAppContext app;
    XmString  quits, clears, colourss, red, green, 
              blue, black, grey, white;
    XtActionsRec actions;
    void quit_call(), clear_call(), colour_call(), 
         draw_cbk();

    
    String translations = 
"<Btn1Motion>: draw_cbk(motion) 
               ManagerGadgetButtonMotion() \n\
<Btn1Down>: draw_cbk(down) ManagerGadgetArm() \n\
<Btn1Up>: draw_cbk(up) ManagerGadgetActivate()";

    top_wid = XtVaAppInitialize(&app, "Draw", NULL, 0, 
        &argc, argv, NULL,
        XmNwidth,  500,
        XmNheight, 500,
        NULL);

     
    main_w = XtVaCreateManagedWidget("main_window",
        xmMainWindowWidgetClass,   top_wid,
        XmNwidth, 500,         
        XmNheight, 500,
        NULL);
        
    /* Create a simple MenuBar that contains three menus */
    quits = XmStringCreateSimple("Quit");
    clears = XmStringCreateSimple("Clear");
    colourss = XmStringCreateSimple("Colour");

        
    menu_bar = XmVaCreateSimpleMenuBar(main_w, "main_list",
        XmVaCASCADEBUTTON, quits, 'Q',
        XmVaCASCADEBUTTON, clears, 'C',
        XmVaCASCADEBUTTON, colourss, 'o',
        NULL); 
               
    XtManageChild(menu_bar);


/* First menu is quit menu -- callback is quit_call() */
    
    XmVaCreateSimplePulldownMenu(menu_bar, "quit_menu", 0, 
        quit_call, XmVaPUSHBUTTON, quits, 'Q', NULL, NULL,
        NULL);
    XmStringFree(quits);
    
/* Second menu is clear menu -- callback is clear_call() */
    
    XmVaCreateSimplePulldownMenu(menu_bar, "clear_menu", 1, 
        clear_call, XmVaPUSHBUTTON, clears, 'C', NULL, NULL,
        NULL);
    XmStringFree(clears);

    
     /* create colour pull down menu */
    
    black = XmStringCreateSimple(colours[0]);
    red = XmStringCreateSimple(colours[1]);
    green = XmStringCreateSimple(colours[2]);
    blue = XmStringCreateSimple(colours[3]);
    grey = XmStringCreateSimple(colours[4]);
    white = XmStringCreateSimple(colours[5]);

    
    colour = XmVaCreateSimplePulldownMenu(menu_bar, 
        "edit_menu", 2, colour_call,
        XmVaRADIOBUTTON, black, 'k', NULL, NULL,
        XmVaRADIOBUTTON, red, 'R', NULL, NULL,
        XmVaRADIOBUTTON, green, 'G', NULL, NULL,
        XmVaRADIOBUTTON, blue, 'B', NULL, NULL,
        XmVaRADIOBUTTON, grey, 'e', NULL, NULL,
        XmVaRADIOBUTTON, white, 'W', NULL, NULL,
        XmNradioBehavior, True,     
        /* RowColumn resources to enforce */
        XmNradioAlwaysOne, True,    
        /* radio behavior in Menu */
        NULL);


    XmStringFree(black);
    XmStringFree(red);
    XmStringFree(green);
    XmStringFree(blue);
    XmStringFree(grey);
    XmStringFree(white);


    /* Create a DrawingArea widget. */
    /* make new actions */
    
    actions.string = "draw_cbk";
    actions.proc = draw_cbk;
    XtAppAddActions(app, &actions, 1);
    
    draw = XtVaCreateWidget("draw",
     xmDrawingAreaWidgetClass, main_w,
     XmNtranslations, XtParseTranslationTable(translations),
     XmNbackground, WhitePixelOfScreen(XtScreen(main_w)),
     NULL);

        
    cmap = DefaultColormapOfScreen(XtScreen(draw));
    display = XtDisplay(draw);
    
/* set the DrawingArea as the "work area" of main window */
    XtVaSetValues(main_w,
        XmNmenuBar,    menu_bar,
        XmNworkWindow, draw,
        NULL);


/* Create a GC. Attach GC to DrawingArea's XmNuserData. */
    gcv.foreground = BlackPixelOfScreen(XtScreen(draw));
    gc = XCreateGC(XtDisplay(draw),
        RootWindowOfScreen(XtScreen(draw)), 
        GCForeground, &gcv);

    XtManageChild(draw);
    XtRealizeWidget(top_wid);
    XtAppMainLoop(app);
}


/* CALL BACKS */

void quit_call()

{   printf("Quitting program\n");
    exit(0);
}

void clear_call() /* clear work area */

{ XClearWindow(display, XtWindow(draw)); 
}


/* called from any of the "Colour" menu items.  
   Change the color of the
   label widget. 
   Note: we have to use dynamic setting with setargs()..
 */
void
colour_call(w, item_no)
Widget w;     /* menu item that was selected */
int item_no;  /* the index into the menu */
{
    int n =0;
    Arg args[1];
    
    XColor xcolour, spare; /* xlib color struct */

    
     if (XAllocNamedColor(display, cmap, colours[item_no], 
        &xcolour, &spare) == 0)
       return;
    
    /* remember new colour */        
    fill_pixel = xcolour.pixel;     
}

/*  DrawingArea Callback.*/ 
 
void  draw_cbk(Widget w, XButtonEvent *event, 
               String *args, int *num_args)

{   static Position x, y, last_x, last_y;
    Position width, height;
    
    int line_style;
    unsigned int line_width = 1;
    int cap_style = CapRound;
    int join_style = JoinRound;

   
        if (strcmp(args[0], "down") == 0) 
          {  /* anchor initial point (save its value) */
            x = event->x;
            y = event->y;
          } 
       else 
         if (strcmp(args[0], "motion") == 0) 
          { /* draw "ghost" box to show where it could go */
             /* undraw last box */
             
             line_style = LineOnOffDash;

             
             /* set line attributes */

             XSetLineAttributes(event->display, gc,  
              line_width, line_style, cap_style, join_style);
             
             gcv.foreground 
               = WhitePixelOfScreen(XtScreen(w));

             XSetForeground(event->display, gc, 
                            gcv.foreground);
             
             XSetFunction(event->display, gc, GXinvert);

             
            XDrawLine(event->display, event->window, gc,  
                       x, y, last_x, y);
            XDrawLine(event->display, event->window, gc, 
                       last_x, y, last_x, last_y);
            XDrawLine(event->display, event->window, gc, 
                       last_x, last_y, x, last_y);
            XDrawLine(event->display, event->window, gc,  
                       x, last_y, x, y);

         
            /* Draw New Box */
            gcv.foreground 
              = BlackPixelOfScreen(XtScreen(w));
            XSetForeground(event->display, gc, 
                   gcv.foreground);
          
            XDrawLine(event->display, event->window, gc, 
                      x, y, event->x, y);
            XDrawLine(event->display, event->window, gc, 
                      event->x, y, event->x, event->y);
            XDrawLine(event->display, event->window, gc, 
                      event->x, event->y, x, event->y);
            XDrawLine(event->display, event->window, gc,  
                      x, event->y, x, y);
          }


        else       
         if (strcmp(args[0], "up") == 0)             
          { /* draw full line */
          
             XSetFunction(event->display, gc, GXcopy);
          
             line_style = LineSolid;
             
             /* set line attributes */

             XSetLineAttributes(event->display, gc, 
        line_width, line_style, cap_style, join_style);
             
             XSetForeground(event->display, gc, fill_pixel);

             
            XDrawLine(event->display, event->window, gc, 
                      x, y, event->x, y);
            XDrawLine(event->display, event->window, gc, 
                      event->x, y, event->x, event->y);
            XDrawLine(event->display, event->window, gc, 
                      event->x, event->y, x, event->y);
            XDrawLine(event->display, event->window, gc, 
                      x, event->y, x, y);

            
            width = event->x - x;
            height = event->y - y;
            XFillRectangle(event->display, event->window, 
                           gc, x, y, width, height);
          }
          last_x = event->x;
          last_y = event->y;
              
}



Next: draw_input2.c - drawing to a pixmap Up: Using DrawingAreas in Practice Previous: draw.c - Basic Drawing


Dave.Marshall@cm.cf.ac.uk
Tue May 24 16:52:56 BST 1994