In this section we will look at how we create and use Motif's DrawingArea Widget which is concerned with the display of graphics. We will also put into practice the Xlib drawing and event scheduling routines met in Chapter 16.
To create a DrawingArea Widget, use
XtVaCreateManagedWidget() with
xmDrawingAreaWidgetClass or use
XmCreateDrawingArea() . Remember to include the
<Xm/DrawingA.h>
header file.
There is also a DrawnButton Widget which is a combination of a DrawingArea
and a PushButton. There is a
xmDrawnButtonWidgetClass and definitions are
in the <Xm/DrawnB.h>
header file.
A DrawingArea will usually be placed inside a container widget --e.g. a Frame or a MainWindow -- and it is frequently scrolled. As such, it usually inherits size and other resources from its parent widget. You can, however, set resources like XmNwidth and XmNheight for the DrawingArea directly. The XmNresizePolicy resource may also need to be set to allow changes in dimension of the DrawingArea in a program. Possible values are:
Three callbacks are associated with this widget :
We are now in a position to draw 2D graphics in Motif. Recall that all graphics drawing is performed at the Xlib level and so we have to attach the higher level motif widgets to the lower level Xlib structures (Chapter 16).
In order to draw anything in a DrawingArea Widget we basically need to do the following:
The draw.c program illustrates basic Motif/Xlib drawing principles:
Note: this method can be used to pass any type of data not just graphics as illustrated here.
Fig. 17.1 Output of draw.c
The full program listing is:
#include <Xm/Xm.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/DrawingA.h> /* Prototype functions */ void quit_call(void); void draw_cbk(Widget , XtPointer , XmDrawingAreaCallbackStruct *); void load_font(XFontStruct **); /* XLIB Data */ Display *display; Screen *screen_ptr; main(int argc, char *argv[]) { Widget top_wid, main_w, menu_bar, draw, quit; XtAppContext app; XGCValues gcv; GC gc; top_wid = XtVaAppInitialize(&app, "Draw", NULL, 0, &argc, argv, NULL, XmNwidth, 500, XmNheight, 500, NULL); main_w = XtVaCreateManagedWidget("main_window", xmMainWindowWidgetClass, top_wid, NULL); menu_bar = XmCreateMenuBar(main_w, "main_list", NULL, 0); XtManageChild(menu_bar); /* create quit widget + callback */ quit = XtVaCreateManagedWidget( "Quit", xmCascadeButtonWidgetClass, menu_bar, XmNmnemonic, 'Q', NULL); XtAddCallback(quit, XmNactivateCallback, quit_call, NULL); /* Create a DrawingArea widget. */ draw = XtVaCreateWidget("draw", xmDrawingAreaWidgetClass, main_w, NULL); /* get XLib Display Screen and Window ID's for draw */ display = XtDisplay(draw); screen_ptr = XtScreen(draw); /* set the DrawingArea as the "work area" of main window */ XtVaSetValues(main_w, XmNmenuBar, menu_bar, XmNworkWindow, draw, NULL); /* add callback for exposure event */ XtAddCallback(draw, XmNexposeCallback, draw_cbk, NULL); /* Create a GC. Attach GC to the DrawingArea's XmNuserData. NOTE : This is a useful method to pass data */ gcv.foreground = BlackPixelOfScreen(screen_ptr); gc = XCreateGC(display, RootWindowOfScreen(screen_ptr), GCForeground, &gcv); XtVaSetValues(draw, XmNuserData, gc, NULL); XtManageChild(draw); XtRealizeWidget(top_wid); XtAppMainLoop(app); } /* CALL BACKS */ void quit_call() { printf("Quitting program\n"); exit(0); } /* DrawingArea Callback. NOTE: cbk->reason says type of callback event */ void draw_cbk(Widget w, XtPointer data, XmDrawingAreaCallbackStruct *cbk) { char str1[25]; int len1, width1, font_height; unsigned int width, height; int x, y, angle1, angle2, x_end, y_end; unsigned int line_width = 1; int line_style = LineSolid; int cap_style = CapRound; int join_style = JoinRound; XFontStruct *font_info; XEvent *event = cbk->event; GC gc; Window win = XtWindow(w); if (cbk->reason != XmCR_EXPOSE) { /* Should NEVER HAPPEN for this program */ printf("X is screwed up!!\n"); exit(0); } /* get font info */ load_font(&font_info); font_height = font_info->ascent + font_info->descent; /* get gc from Drawing Area user data */ XtVaGetValues(w, XmNuserData, &gc, NULL); /* DRAW A RECTANGLE */ x = y = 10; width = 100; height = 50; XDrawRectangle(display, win, gc, x, y, width, height); strcpy(str1,"RECTANGLE"); len1 = strlen(str1); y += height + font_height + 1; if ( (x = (x + width/2) - len1/2) < 0 ) x = 10; XDrawString(display, win, gc, x, y, str1, len1); /* Draw a filled rectangle */ x = 10; y = 150; width = 80; height = 70; XFillRectangle(display, win, gc, x, y, width, height); strcpy(str1,"FILLED RECTANGLE"); len1 = strlen(str1); y += height + font_height + 1; if ( (x = (x + width/2) - len1/2) < 0 ) x = 10; XDrawString(display, win, gc, x, y, str1, len1); /* draw an arc */ x = 200; y = 10; width = 80; height = 70; angle1 = 180 * 64; /* 180 degrees */ angle2 = 90 * 64; /* 90 degrees */ XDrawArc(display, win, gc, x, y, width, height, angle1, angle2); strcpy(str1,"ARC"); len1 = strlen(str1); y += height + font_height + 1; if ( (x = (x + width/2) - len1/2) < 0 ) x = 200; XDrawString(display, win, gc, x, y, str1, len1); /* draw a filled arc */ x = 200; y = 200; width = 100; height = 50; angle1 = 270 * 64; /* 270 degrees */ angle2 = 180 * 64; /* 180 degrees */ XFillArc(display, win, gc, x, y, width, height, angle1, angle2); strcpy(str1,"FILLED ARC"); len1 = strlen(str1); y += height + font_height + 1; if ( (x = (x + width/2) - len1/2) < 0 ) x = 200; XDrawString(display, win, gc, x, y, str1, len1); /* SOLID LINE */ x = 10; y = 300; /* start and end points of line */ x_end = 200; y_end = y - 30; XDrawLine(display, win, gc, x, y, x_end, y_end); strcpy(str1,"SOLID LINE"); len1 = strlen(str1); y += font_height + 1; if ( (x = (x + x_end)/2 - len1/2) < 0 ) x = 10; XDrawString(display, win, gc, x, y, str1, len1); /* DASHED LINE */ line_style = LineOnOffDash; line_width = 2; /* set line attributes */ XSetLineAttributes(display, gc, line_width, line_style, cap_style, join_style); x = 10; y = 350; /* start and end points of line */ x_end = 200; y_end = y - 30; XDrawLine(display, win, gc, x, y, x_end, y_end); strcpy(str1,"DASHED LINE"); len1 = strlen(str1); y += font_height + 1; if ( (x = (x + x_end)/2 - len1/2) < 0 ) x = 10; XDrawString(display, win, gc, x, y, str1, len1); } void load_font(XFontStruct **font_info) { char *fontname = "fixed"; XFontStruct *XLoadQueryFont(); /* load and get font info structure */ if (( *font_info = XLoadQueryFont(display, fontname)) == NULL) { /* error - quit early */ printf("%s: Cannot load %s font\n", "draw.c", fontname); exit(-1); } }
The previous program only illustrated one aspect of the DrawingArea widget, i.e. displaying graphics. Another important aspect of this widget is how the widget accepts input. In this Section we will develop a program that illustrate how input is processed in the DrawingArea. We will write a program, draw_input1.c, that highlights some deficiencies in the default event handling capabilities within a practical application.
The draw_input1.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 the chosen colour. A clear DrawingArea facility is also provided.
Fig. 17.2 Output of draw_input1.c
In order to achieve a practical and intuitive manner of user interaction we will need to detect 3 different mouse events (all events described below refer to the left mouse button):
Fig. 17.3 Silhouette outline of rectangle during input ( draw_input1.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 (Section 5.8.2) that contains a list of events that it can receive and actions that it is to take upon receipt of an event. We basically have to create a new translation table to achieve our desired interaction described above.
We have already defined the translation table format in Section 5.8.2. A translation table consists of events like the below excerpt of the default DrawingArea translation:
.............. <Btn1Down>: DrawingAreaInput() ManagerGadgetArm() <Btn1Up>: DrawingAreaInput() ManagerGadgetActivate() <Btn1Motion>: ManagerGadgetButtonMotion() ..............
Our particular problem is that button motion does not get passed to the DrawingAreaInput() function that notifies the program of an input event.
To create a new translation table, for our purpose, 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. We use the same callback to detect each mouse button down, up or motion action. This is achieved by sending a message to the callback that identifies each action. The arm, activate and motion gadget manager functions control the (default) display of an event action.
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. We must also register the callback with the actions associated with the translation events. We use the XtAppAddActions() function to do this.
For the above example we create the String as follows:
String translations = "<Btn1Motion>: draw_cbk(motion) ManagerGadgetButtonMotion() \n\ <Btn1Down>: draw_cbk(down) ManagerGadgetArm() \n\ <Btn1Up>: draw_cbk(up) ManagerGadgetActivate()";
and register the callback and create a DrawingArea widget with the correct actions with the following code:
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);
The Callback function would be prototyped by:
draw_cbk(Widget w, XButtonEvent *event, String *args, int *num_args).
On calling the function, we simply inspect the args[0]
String to
see if an up, down or motion event has occurred and take the
approptriate actions described below:
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.
The complete program listing of draw_input1.c is :
#include <Xm/Xm.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/DrawingA.h> /* Prototype callbacks */ void quit_call(void); void clear_call(void); void colour_call(Widget , int); void draw_cbk(Widget , XButtonEvent *, String *, int *); 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; 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 = XmStringCreateLocalized("Quit"); clears = XmStringCreateLocalized("Clear"); colourss = XmStringCreateLocalized("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 = XmStringCreateLocalized(colours[0]); red = XmStringCreateLocalized(colours[1]); green = XmStringCreateLocalized(colours[2]); blue = XmStringCreateLocalized(colours[3]); grey = XmStringCreateLocalized(colours[4]); white = XmStringCreateLocalized(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 colour of the label widget. Note: we have to use dynamic setting with setargs().. */ void colour_call(Widget w, int item_no) /* w -- menu item that was selected */ /* item_no --- the index into the menu */ { int n =0; Arg args[1]; XColor xcolour, spare; /* xlib colour 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; }
One problem the draw_input1.c program has is that if the window was covered and then exposed the picture would not redraw itself (Section 16.6). To see this for yourself run the draw_input1.c obscure the MainWindow with another window and then click on the draw_input1.c frame to bring it to the foreground and note the appearance of the window.
Indeed, the best method to store the picture we draw is via a Pixmap . Since the drawing in this application is an interactive process it would be very difficult to redraw the picture unless we used Pixmaps. There is no way that we could predict how the user would use the application and storing each drawing stroke would become complex. The best approach is to store the drawing data in a Pixmap by writing directly to a Pixmap. Obtaining an immediate visual feedback is also important (the user needs to see what he has drawn) so we also draw directly to the display when data is being input. When an expose event is detected all we need to do is remap the pixmap to the window.
The draw_input2.c program does exactly the same task as draw_input1.c but draws to a pixmap which can be remapped to the window upon an exposure.
The major differences between the programs are:
The draw_input2.c program listing is as follows:
#include <Xm/Xm.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/DrawingA.h> /* Prototype callbacks */ void quit_call(void); void clear_call(void); void colour_call(Widget , int); void draw_cbk(Widget , XButtonEvent *, String *, int *); void expose(Widget , XtPointer , XmDrawingAreaCallbackStruct *); GC gc; XGCValues gcv; Widget draw; Display *display; /* xlib id of display */ Screen *screen; Colormap cmap; Pixmap pix; Dimension width, height; /* store size of pixmap */ String colours[] = { "Black", "Red", "Green", "Blue", "Grey", "White"}; long int fill_pixel = 1; /* stores current colour of fill - black default */ 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; 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, NULL); main_w = XtVaCreateManagedWidget("main_window", xmMainWindowWidgetClass, top_wid, NULL); /* Create a simple MenuBar that contains three menus */ quits = XmStringCreateLocalized("Quit"); clears = XmStringCreateLocalized("Clear"); colourss = XmStringCreateLocalized("Colour"); menu_bar = XmVaCreateSimpleMenuBar(main_w, "main_list", XmVaCASCADEBUTTON, quits, 'Q', XmVaCASCADEBUTTON, clears, 'C', XmVaCASCADEBUTTON, colourss, 'o', NULL); XtManageChild(menu_bar); /* First menu is the 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 the 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 = XmStringCreateLocalized(colours[0]); red = XmStringCreateLocalized(colours[1]); green = XmStringCreateLocalized(colours[2]); blue = XmStringCreateLocalized(colours[3]); grey = XmStringCreateLocalized(colours[4]); white = XmStringCreateLocalized(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 set */ 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)), XmNwidth, 500, XmNheight, 500, NULL); cmap = DefaultColormapOfScreen(XtScreen(draw)); display = XtDisplay(draw); screen = XtScreen(draw); /* Create a GC. Attach GC to the DrawingArea's XmNuserData. */ gcv.foreground = BlackPixelOfScreen(XtScreen(draw)); gc = XCreateGC(XtDisplay(draw), RootWindowOfScreen(XtScreen(draw)), GCForeground, &gcv); /* get pixmap of DrawingArea */ XtVaGetValues(draw, XmNwidth, &width, XmNheight, &height, NULL); pix = XCreatePixmap(display, RootWindowOfScreen(screen), width, height, DefaultDepthOfScreen(screen)); /* initial white pixmap */ XSetForeground(XtDisplay(draw), gc, WhitePixelOfScreen(XtScreen(draw))); XFillRectangle(display, pix, gc, 0, 0, width, height); /* reset gc with current colour */ XSetForeground(display, gc, fill_pixel); /* set the DrawingArea as the "work area" of the main window */ XtVaSetValues(main_w, XmNmenuBar, menu_bar, XmNworkWindow, draw, NULL); /* add callback for exposure event */ XtAddCallback(draw, XmNexposeCallback, expose, NULL); XtManageChild(draw); XtRealizeWidget(top_wid); XtAppMainLoop(app); } /* CALL BACKS */ void quit_call() { printf("Quitting program\n"); exit(0); } void clear_call(Widget w, int item_no) /* clear work area */ { /* clear pixmap with white */ XSetForeground(XtDisplay(draw), gc, WhitePixelOfScreen(XtScreen(draw))); XFillRectangle(display, pix, gc, 0, 0, width, height); /* reset gc with current colour */ XSetForeground(display, gc, fill_pixel); /* copy pixmap to window of drawing area */ XCopyArea(display, pix, XtWindow(draw), gc, 0, 0, width, height, 0, 0); } /* expose is called whenever all or portions of the drawing area is exposed. */ void expose(Widget draw, XtPointer client_data, XmDrawingAreaCallbackStruct *cbk) { XCopyArea(cbk->event->xexpose.display, pix, cbk->window, gc, 0, 0, width, height, 0, 0); } /* called from any of the "Colour" menu items. Change the colour of the label widget. Note: we have to use dynamic setting with setargs(). */ void colour_call(Widget w, int item_no) /* w = menu item that was selected 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 (i.e., 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; get GC and use in XDrawLine() */ 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); /* only need to draw final selection to pixmap */ XDrawLine(event->display, pix, gc, x, y, event->x, y); XDrawLine(event->display, pix, gc, event->x, y, event->x, event->y); XDrawLine(event->display, pix, gc, event->x, event->y, x, event->y); XDrawLine(event->display, pix, gc, x, event->y, x, y); XFillRectangle(event->display, pix, gc, x, y, width, height); } last_x = event->x; last_y = event->y; }
NEED SOME EXERCISE -- RUN