Pour compiler sans erreurs il faut inclure le fichier <Xm/DrawingA.h>.
Il existe aussi un widget de type DrawnButton qui est la combinaison d'une DrawingArea et d'un PushButton. Si on utilise des DrawnButton dans une application, il faut inclure <Xm/DrawnB.h>.
La ressource XmNresizePolicy permet de règler les
autorisations de changement de taille par le window manager. Trois
valeurs possibles : XmRESIZE_ANY,
XmRESIZE_GROW et XmRESIZE_NONE.
Par défaut on peut associer trois types de callbacks à une DrawingArea :
Nous allons étudier de petits programmes d'exemples. Tous procèdent de la même manière :
Nous reprenons le programme menu_pull.c déjà étudié
lors du cours sur les MainWindow et les menus, et le modifions pour
intégrer une DrawingArea. Ce programme permet le dessin de rectangles
"à la Mac Draw", le menu "colors" servant à sélectionner la couleur du
rectangle que l'on veut dessiner.
Dump d'écran de draw_input.c :
L'exécutable se trouve dans :
Les DrawingArea widgets possèdent deux ressources permettant d'appeler
des callbacks en cas d'entrées clavier ou souris, mais ils ne
possèdent pas de ressources permettant de traiter le déplacement de la
souris.
La solution consiste à enrichir la table de
translations de la DrawingArea.
Rappel: chaque widget possède
une table de translations qui se compose de :
Extrait d'une table de translation (celle de netscape, prise dans son fichier de ressources
Bien que l'on puisse traiter les autres événements souris avec les méthodes d'ajout de callback standards (XtAddCallBack(), etc...), nous allons nous servir de la table de translation pour traiter l'appui et le relâchement du bouton 1 de la souris. La table de translation finale ressemble à ceci :
Il faut également, lors de la création de la DrawingArea, indiquer que
nous avons enrichi la table de translations par défaut avec les
actions associées aux nouvelles translations. Ceci
est réalisé à l'aide de la fonction XtAppAddActions(). Ainsi
il sera possible de postionner ces nouvelles translations dans un
fichier de ressources, l'application reconnaîtra les nouveaux
événements.
Avec notre exemple :
listing complet du programme draw_input.c :
Ressources et callbacks des widget de type DrawingArea
En général on place les DrawingArea à l'intérieur d'un container
widget : une Form, une MainWindow, une ScrolledWindow, etc... si bien
qu'il est rare que l'on doive spécifier sa taille car cette dernière
est héritée du widget père. On peut cependant positionner les
ressources XmNheight et XmNwidth
pour fixer la taille.
Utilisation pratique d'une DrawingArea
Nous allons maintenant réaliser du dessin 2D dans une application
MOTIF. Toutes les opérations graphiques seront réalisées à l'aide de
fonctions de la Xlib, il va falloir récurpèrer des informations de
bas niveau à partir des widgets MOTIF possèdant un de haut
niveau d'abstraction.
Premier programme d'exemple: draw.c
Ce petit programme illustre les principes de base de l'utilisation conjointe de fonctions Xlib, Xt et MOTIF.
L'exécutable de draw.c se trouve dans :
draw, dans une MainWindow.
Display et du Screen (au sens Xlib) sont obtenus à l'aide des fonctions XtDisplay() et XtScreen().
foreground noire, obtenue à l'aide de la macro Xlib BlackPixelOfScreen() (remarque : on aurait pu aussi utiliser BlackPixel(), l'une prend un screen en argument, l'autre un display).
Voici ce que produit le programme draw.c : /home/profs/cours/minfo/motif/drawing/draw
#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/DrawingA.h>
#include <Xm/RowColumn.h>
/* 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;
void quit_call(), draw_cbk();
top_wid = XtVaAppInitialize(&app, "Draw", NULL, 0,
&argc, argv, NULL,
XmNwidth, 500,
XmNheight, 500,
NULL);
/* create a main window */
main_w = XtVaCreateManagedWidget("main_window",
xmMainWindowWidgetClass, top_wid,
NULL);
/* create a menu bar */
menu_bar = XmCreateMenuBar(main_w, "main_list",
NULL, 0);
XtManageChild(menu_bar);
/* create quit widget in menu bar + 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 MenuBar and the DrawingArea as menu bar and the
"work area" of main window. Like that they are partly managed
by the MainWindow */
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);
/* because we changed some ressources after the Managed Creation */
XtManageChild(draw);
XtRealizeWidget(top_wid);
XtAppMainLoop(app);
}
/* CALL BACKS */
/*--------------*/
void quit_call()
/*--------------*/
{
printf("Quitting program\n");
exit(0);
}
/*----------------------------------------------*/
void draw_cbk(Widget w, XtPointer data,
XmDrawingAreaCallbackStruct *cbk)
/*----------------------------------------------*/
/* DrawingArea Callback. NOTE: cbk->reason says type of
callback event */
{
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;
void load_font();
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);
}
}
Deuxième exemple : le programme draw_input.c
Avec cet exemple, nous allons voir comment traiter les entrées/sorties dans une DrawingArea.
Nous avons déjà vu pendant les TDs Xlib comment il fallait procèder
pour dessiner des rectangles "élastiques". Il faut traiter trois types
d'événements : l'appui d'un bouton de la souris, le déplacement de la
souris et le relâchage du bouton souris. /home/profs/cours/minfo/motif/drawing/draw_input
/usr1/local/lib/X11/app-defaults/Netscape) :
Pour ajouter le traitement de l'événement "déplacement de la souris bouton 1 appuyé", il faut ajouter la translations suivante :
.
.
.
<Btn1Down>: ArmLink()
<Btn2Down>: ArmLink()
.
.
.
où
<Btn1Motion>: draw_cbk(motion)
draw_cbk() est la fonction de callback qui effectue le
dessin des rectangles. Chaque fois que la souris sera déplacée bouton 1 appuyé dans la DrawingArea, draw_cbk() sera appelée. De plus, il est possible de passer à la fonction de callback des paramètres de type String! Avec l'exemple ci-dessus, la chaîne de caractères "motion" sera passée (plus de détails sur le traitement de ces paramètres seront donnés page suivante).
En pratique, dans un programme MOTIF, nous initialisons d'abord la table de translations dans une variable de type String, puis l'on attache cette table à la DrawingArea en positionnant la ressource XmNtranslations avec comme valeur le résultat de XtParseTranslationTable(String translations).
<Btn1Motion>: draw_cbk(motion)
<Btn1Up>: draw_cbk(up)
<Btn1Down>: draw_cbk(down)
XtActionsRec actions;
...
actions.string = "draw_cbk";
actions.proc = draw_cbk;
XtAppAddActions(app, &actions, 1);
Récupération des paramètres par la fonction de callback:
Voici l'en-tête de la fonction de callback:
Les paramètres sont passés dans la variable args, le nombre de paramètres dans num_args. Comme nous ne passons qu'un seul paramètre avec la table de translations correspondant à cet exemple, nous testerons juste la valeur de la variable args[0]. es différentes valeurs up, motion and down nous renseigneront sur la nature de l'événement qui a déclenché l'appel de la fonction de callback.
void draw_cbk(Widget w, XButtonEvent *event,
String *args, int *num_args)
{
.
.
.
}
#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/CascadeB.h>
#include <Xm/DrawingA.h>
#include <Xm/RowColumn.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;
void quit_call(), clear_call(), colour_call(),
draw_cbk();
XtActionsRec actions;
String translations =
"<Btn1Motion>: draw_cbk(motion) \n\
<Btn1Down>: draw_cbk(down) \n\
<Btn1Up>: draw_cbk(up) ";
top_wid = XtVaAppInitialize(&app, "Draw", NULL, 0,
&argc, argv, NULL,
XmNwidth, 500,
XmNheight, 500,
NULL);
/* Create MainWindow */
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));
}
/*---------------------------*/
void colour_call(w, item_no)
/*---------------------------*/
/* called from any of the "Colour" menu items.
Change the color of the
label widget.
Note: we have to use dynamic setting with setargs()..
*/
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;
}
michel.buffa@essi.fr