Dans cette section nous allons étudier un autre type de "container", le widget de type MainWindow. Nous étudierons particulièrement les relations entre les widgets de ce type et les différents menus. Nous verrons qu'il est également possible de mettre d'autre types de widgets dans une MainWindow.
D`après le Motif Style Guide, le widget de type MainWindow sert à décrire la fenêtre principale d'une application. On crée en général une seule MainWindow par application, comme unique fils du widget retourné par XtAppInitialize(). la fonction d'initialisation du Toolkit. Le widget retourné par cette dernière fonction est de la classe ApplicationShell, et comme tous les Shells, correspond à une fenêtre manipulable par le Window Manager.
Une MainWindow est prévue pour contenir :
Toutes ces parties sont optionnelles et il est très possible d'avoir une application sans zone de messages ni zone de commandes. La MainWindow assure juste une maintenance plus simple de ces composants s`ils existent.
Remarque importante : pour certaines applications très simple, on peut très bien se passer de MainWindow, comme dans l'exemple du premier chapitre : Premiers pas avec Motif.
L`exemple ci-dessous montre une MainWindow comportant une barre de menu, une ScrolledList dans la zone de travail (work area), un command widget et une zone de messages d'une ligne en bas de la fenêtre.
![]() |
![]() |
Source disponible : main_window.c
#include <Xm/MainW.h>
#include <Xm/Label.h>
#include <Xm/List.h>
#include <Xm/RowColumn.h>
#include <Xm/Command.h>
#include <stdio.h>
#define list_items "Ceci, Est, Une, ScrolledList, dans la, WorkArea, de la, Main Window"
main(int argc, char **argv)
{
Widget toplevel, main_w, list_w, menubar, menu, message_area, command_w;
XmString file, quit;
XtAppContext app;
Pixmap pixmap;
/* A mettre systematiquement */
XtSetLanguageProc(NULL, NULL, NULL);
/* Initialisation du toolkit */
toplevel = XtVaAppInitialize(&app, "Demos",
NULL, 0, &argc, argv, NULL, NULL);
/* La main window */
main_w = XtVaCreateManagedWidget("main_window",
xmMainWindowWidgetClass, toplevel,
NULL);
/* Une ScrolledList que l'on met dans la work area */
list_w = XmCreateScrolledList(main_w, "main_list", NULL, 0);
XtVaSetValues(list_w,
XtVaTypedArg, XmNitems, XmRString,
list_items, strlen(list_items),
XmNitemCount, 8,
XmNvisibleItemCount, 5,
NULL);
XtManageChild(list_w);
/* Barre de menu avec un seul menu deroulant */
file = XmStringCreateLocalized("File");
menubar = XmVaCreateSimpleMenuBar(main_w, "menubar",
XmVaCASCADEBUTTON, file, 'F',
NULL);
XmStringFree(file);
XtManageChild(menubar);
/* Un seul bouton dans le menu deroulant */
quit = XmStringCreateLocalized("Quit");
menu = XmVaCreateSimplePulldownMenu(menubar, "file_menu", 0, exit,
XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
NULL);
XmStringFree(quit);
/* Un label dans la zone de messages */
message_area =
XtVaCreateManagedWidget("Ceci est la zone de messages",
xmLabelWidgetClass, main_w,
XmNalignment, XmALIGNMENT_BEGINNING,
NULL);
/* Un Command widget dans la command area */
file = XmStringCreateLocalized("Entrez une commande :");
command_w = XtVaCreateManagedWidget("command_w", xmCommandWidgetClass,
main_w, XmNpromptString, file,
NULL);
XmStringFree(file);
/* Positionnement des attributs de la Main Window pour lui indiquer quels
widgets sont dans les differentes zones, et quelles sont leurs
positions rezpectives */
XtVaSetValues(main_w,
XmNmenuBar, menubar,
XmNworkWindow, XtParent(list_w),
XmNmessageWindow, message_area,
XmNcommandWindow, command_w,
XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
NULL);
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
}
Plusieurs remarques concernant ce petit bout de code :
Paramètres de cette fonction :
Cette fonction transforme une chaîne au sens du C en une chaîne Motif.
Il est nécessaire de libérer une XmString sitôt qu'elle a été utilisée. Cette opération est réalisée à l'aide de la fonction XmStringFree(XmString chaîne_motif);
Un chapitre entier du cours est consacré à l'étude détaillée des chaîne Motif.
XtVaSetValues(main_w,
XmNmenuBar, menubar,
XmNworkWindow, XtParent(list_w),
XmNmessageWindow, message_area,
XmNcommandWindow, command_w,
XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
NULL);
Remarquons plusieurs choses :
Il faudra faire très attention par la suite aux widgets de type Scrolled* car leurs fonctions de création ne renvoient pas un pointeurs sur la ScrolledWindow qui les contient. Ne pas oublier d'y penser lorsqu'on voudra spécifier la position et la taille de ces widgets.
On pourra utiliser avec un widget de type MainWindow tous les attributs, toutes les ressources disponibles pour la classe ScrolledWindow. Par exemple, si le contenu de la zone de travail est plus grand que cette dernière, on pourra indiquer à la MainWindow de faire apparaître automatiquement des ScrollBars.


Le source est disponible : main_window_scroll.c
#include <Xm/MainW.h>
#include <Xm/Label.h>
#define msg_label \
"Ceci est un texte trop grand\n\
Pour rentrer dans la MainWindow, alors des barres de\n\
scrolling sont apparues !"
main(int argc, char **argv)
{
Widget toplevel, main_w, label;
XtAppContext app;
XtSetLanguageProc(NULL, NULL, NULL);
toplevel = XtVaAppInitialize(&app, "Demos",
NULL, 0, &argc, argv, NULL, NULL);
/* La main window */
main_w = XtVaCreateManagedWidget("main_window",
xmMainWindowWidgetClass, toplevel,
XmNscrollingPolicy, XmAUTOMATIC,
XmNscrollBarDisplayPolicy, XmAS_NEEDED,
NULL);
/* Un label dans la work area */
label =
XtVaCreateManagedWidget(msg_label,
xmLabelWidgetClass, main_w,
XmNalignment, XmALIGNMENT_BEGINNING,
NULL);
/* Positionnement des attributs de la Main Window pour lui indiquer quels
widgets sont dans les differentes zones, et quelles sont leurs
positions rezpectives */
XmMainWindowSetAreas(label, NULL, NULL, NULL, NULL, NULL);
XtRealizeWidget(toplevel);
XtAppMainLoop(app);
}
Quelques remarques sur cet exemple :
*.main_window.scrollingPolicy: XmAUTOMATIC *.main_window.displayScrollBarDisplaypolicy: XmAS_NEEDED
Les menus Motif proposent un mécanisme offrant à l'utilisateur la possibilité d'effectuer des choix qui vont déclencher des actions.
Motif propose trois types de menus
Motif fournit trois types de menus : Popup, Pulldown et Option.
Des widgets de base sont nécessaires pour composer des menus :
La barre de menu est un widget de type MenuBar.
Il s'agit en fait d'une RowColumn horizontale particulière: lors de la création avec XmCreateMenuBar() ou XmVaCreateSimpleMenuBar(), étudiés dans cette section, Motif initialise plusieurs ressources de la classe RowColumn à notre insu.
Il ne faut donc pas créer de MenuBar à l'aide de fonctions de la librairie Xt, comme XtVaCreateManagedWidget(), au risque d'avoir de désagréables surprises.
La création d'un véritable menu comme on en retrouve dans la plupart des applications est une tâche relativement complexe. C'est pourquoi nous allons découper l'étude des menus en deux étapes.
Les boutons que va contenir une barre de menu ou un menu déroulants sont d'un type particulier : le type CascadeButton. Vous comprendrez bien vite pourquoi on les a nommé de la sorte.
Etudions maintenant un petit exemple.
Le source est disponible : menu_cascade.c
#include <Xm/Xm.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/RowColumn.h> void quit_call(), help_call(); /* callbacks */ /*---------------------------*/ main(int argc, char *argv[]) /*---------------------------*/ { Widget top_wid, main_win, menu_bar, quit, help; XtAppContext app; int n=0; Arg args[10];
/* Initialisation du toolkit */
top_wid = XtVaAppInitialize(&app, "menu_cascade",
NULL, 0, &argc, argv, NULL, NULL);
/* Creation de la MainWindow */
n=0;
main_win = XmCreateMainWindow(top_wid, "main_window", args, n);
XtManageChild(main_win);
/* Creation de la barre de menu */
n=0;
menu_bar = XmCreateMenuBar(main_win, "main_list", args, n);
XtManageChild(menu_bar);
/* Bouton quit + callback */
n=0;
XtSetArg(args[n], XmNmnemonic, 'Q'); n++;
quit = XmCreateCascadeButton(menu_bar, "Quit", args, n); n++;
XtManageChild(quit);
XtAddCallback(quit, XmNactivateCallback, quit_call, NULL);
/* Creation du bouton help + callback */
n=0;
XtSetArg(args[n], XmNmnemonic, 'H'); n++;
help = XmCreateCascadeButton(menu_bar, "Help", args, n); n++;
XtManageChild(help);
XtAddCallback(help, XmNactivateCallback, help_call, NULL);
/* On indique a la barre de menu de mettre le bouton Help a droite */ XtVaSetValues(menu_bar(XmNmenuHelpWidget, help, NULL);
XtRealizeWidget(top_wid);
XtAppMainLoop(app);
}
/*---------------*/
void quit_call()
/*---------------*/
{
printf("On sort du programme!\n");
exit(0);
}
/*---------------*/
void help_call()
/*---------------*/
{
printf("Désolé je ne peux pas vous aider\n");
}
Les premières lignes de ce programme devraient maintenant vous être familières.
Dans la fenêtre top_level nous mettons une MainWindow (variable main_win), nous mettons aussi une MenuBar (variable menu_bar) comme enfant de main_win et finalement nous ajoutons deux CascadeButton (quit et help) dans la MenuBar.
Remarque : pour varier les plaisirs, la création des divers widgets a été cette fois-ci réalisée à l'aide des fonctions Motif standards de type XmCreate<nom du widget>(). Les ressources ont été positionnées dans le code à l'aide du mécanisme standard proposé par la librairie Xt :
Exemple :
n=0;
XtSetArg(args[n], XmNmnemonic, 'Q'); n++;
quit = XmCreateCascadeButton(menu_bar, "Quit", args, n); n++;
En général, un widget de type CascadeButton sert à déclencher l'affichage d'un menu déroulant. Lorsqu'on clique dessus le menu se déroule en cascade, c'est pourquoi on les appelle des CascadeButton. Ils se conduisent exactement comme des PushButton et peuvent avoir des callbacks (ressource XmNactivateCallback).
Un CascadeButton peut très bien ne pas avoir de menu déroulant associé. Il se conduit alors exactement comme un bouton poussoir. C'est le cas dans le programme menu_cascade.c. Dans cet exemple, nous avons créé deux boutons auxquels nous avons attaché des callbacks très simples.
Raccourcis clavier : nous avons installé pour chacun des boutons un raccourcis clavier équivalent à un click souris sur le bouton. Dans l'exemple, si on tape meta-q on sort du programme comme si on avait cliqué sur le bouton quit. Idem pour le bouton help : l'appui de meta-h est équivalent à clicker dessus.
Nous avons utilisé pour celà la Ressource XmNmnemonic. Son utilisation est très simple (voir menu_cascade.c).
On va tout d'abord créer une MainWindow contenant une MenuBar et un widget de type Frame dans la WorkArea.
#include <Xm/Xm.h> #include <Xm/RepType.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/RowColumn.h> #include <Xm/Frame.h> /*---------------------------*/ main(int argc, char *argv[]) /*---------------------------*/ { XtAppContext app_context; Widget topLevel, mainWindow, menuBar, frame; Widget fileButton, fileMenu, quit, help, helpButton, helpMenu, helpBox; /* On indique que l'on desire utiliser le langage par defaut (anglais). Cette fonction initialise le support international de l'application. Les parametres inidiquent que l'on prend les valeurs par defaut */ XtSetLanguageProc(NULL, (XtLanguageProc) NULL, NULL); topLevel = XtVaAppInitialize(&app_context, "XMainWIndow", NULL, 0, &argc, argv, NULL, NULL); /* On cree la main window */ mainWindow = XtVaCreateManagedWidget("mainWindow", xmMainWindowWidgetClass, topLevel, NULL); /* On cree la barre de menus. On utilise une fonction Xm ici car la fonction XmCreateMenuBar effectue des operations qui n'ont pas d'equivalent avec Xt */ menuBar = XmCreateMenuBar(mainWindow, "menuBar", NULL, 0); XtManageChild(menuBar); /* On cree une frame dans la work area de la main window */ frame = XtVaCreateManagedWidget("frame", xmFrameWidgetClass, mainWindow, NULL); /* On positionne les parties de la main window */ XmMainWindowSetAreas(mainWindow, menuBar, NULL, NULL, NULL, frame); XtRealizeWidget(topLevel); XtAppMainLoop(app_context); }
![]()
Exercice: créér le fichier de ressources pour cette application tel que la MainWindow soit large de 200 pixels, et la Frame fasse 300 x 300 pixels. La work area de la MainWindow étant en fait une ScrolledWindow, on pourra positionner la ressource scrollingPolicy de la MainWindow pour faire apparaître des ScrollBars.
Nous étudierons plus en détails les ScrolledWindows et les ScrollBars dans la suite du cours.
On va maintenant ajouter des menus à cette application.
Un menu déroulant est un widget de la classe PulldownMenu. Il s'agit en réalité d'un RowColumn vertical. Même remarque que pour les MenuBars : ne pas utiliser de fonctions de création de la librairie Xt !
Le source est disponible xmainwindow.c
#include <Xm/Xm.h> #include <Xm/RepType.h> #include <Xm/MainW.h> #include <Xm/CascadeB.h> #include <Xm/RowColumn.h> #include <Xm/Frame.h> #include <Xm/PushB.h> void QuitCB(Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs); /*---------------------------*/ main(int argc, char *argv[]) /*---------------------------*/ { . . . . /* Creation du menu: 1) On cree un cascade bouton File dans la menubar */ fileButton = XtVaCreateManagedWidget("fileButton", xmCascadeButtonWidgetClass, menuBar, NULL); /* 2) Creation du PullDownMenu vide. ATTENTION: on utilise une fonction Xm! Remarquez que le menu n'est pas manage !*/ fileMenu = XmCreatePulldownMenu(menuBar, "fileMenu", NULL, 0); /* 3) Creation d'un bouton quit dans le menu File */ quit = XtVaCreateManagedWidget("quit", xmPushButtonWidgetClass, fileMenu, NULL); /* 4) On indique au CascadeButton le menu qu'il doit derouler */ XtVaSetValues(fileButton, XmNsubMenuId, fileMenu, NULL); /* On cree un callback pour le bouton quit */ XtAddCallback(quit, XmNactivateCallback, QuitCB, 0); XtRealizeWidget(topLevel); XtAppMainLoop(app_context); } /*----------------------------------------------*/ void QuitCB(Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs) /*----------------------------------------------*/ { exit(0); }
![]()
Exercices :
Nous allons étudier le programme menu_pull.c proposant deux menus déroulants. Le premier, "Quitter", contient un seul item (une seule entrée) qui nous permettra de sortir du programme.
Le second menu, "Couleur" propose 5 items permettant de changer la couleur du fond du Label widget attaché à la MainWindow.
Le début du programme ressemble à l'exemple précédent : on crée un top_level, on crée une MainWindow dans le top_level, une MenuBar dans la MainWindow, des CascadeButton dans la MenuBar, et ensuite, plein de trucs nouveaux ! Alors etudiez-bien ce petit bout de code sans vous affoler, et rendez-vous après ces quelques lignes de source C !
Le source est disponible : pull_down.c
#include <Xm/Xm.h> #include <Xm/MainW.h> #include <Xm/Label.h> #include <Xm/RowColumn.h> Widget top_wid, label, main_w; String colours[] = { "Black", "Red", "Green", "Blue", "Grey"}; Display *display; /* Le display, on va en avoir besoin pour dessiner */ Colormap cmap; /*---------------*/ main(int argc, char **argv) /*---------------*/ { Widget menubar, menu, quit_w, widget; XtAppContext app; XColor back, fore, spare; XmString quit, colour, red, green, blue, black, grey, label_str; void quit_call(), colour_call(); int n = 0; Arg args[20]; /* Initialisation du toolkit */ top_wid = XtVaAppInitialize(&app, "Pull_down", NULL, 0, &argc, argv, NULL, NULL); /* La MainWindow contient un MenuBar et un Label */ n=0; XtSetArg(args[n], XmNwidth, 300); n++; XtSetArg(args[n], XmNheight, 300); n++; main_w = XmCreateMainWindow(top_wid, "main_window", args, n); XtManageChild(main_w); /* Creation d'une simple MenuBar contenant 3 menus */ quit = XmStringCreateSimple("Quit"); colour = XmStringCreateSimple("Colour"); menubar = XmVaCreateSimpleMenuBar(main_w, "menubar", XmVaCASCADEBUTTON, quit, 'Q', XmVaCASCADEBUTTON, colour, 'C', NULL); XmStringFree(colour); /* On libere la chaine */ /* Premier menu = quit. callback = quit_call() */ quit_w = XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 0, quit_call, XmVaPUSHBUTTON, quit, 'Q', NULL, NULL, NULL); XmStringFree(quit); /* Le second menu contient les couleurs. Meme callback pour toutes les entrees : la fonction colour_call() */ black = XmStringCreateSimple(colours[0]); red = XmStringCreateSimple(colours[1]); green = XmStringCreateSimple(colours[2]); blue = XmStringCreateSimple(colours[3]); grey = XmStringCreateSimple(colours[4]); menu = XmVaCreateSimplePulldownMenu(menubar, "edit_menu", 1, 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, /* Ressourc de la RowColumn */ XmNradioBehavior, True, /* comportement radiobox dans le menu */ XmNradioAlwaysOne, True, NULL); XmStringFree(black); XmStringFree(red); XmStringFree(green); XmStringFree(blue); /* On initialise le menu pour que "black soit la selection par defaut */ if (widget = XtNameToWidget(menu, "button_0")) XtVaSetValues(widget, XmNset, True, NULL); XtManageChild(menubar); /* Label dans la work area. C'est lui qui va changer de couleur ! */ label_str = XmStringCreateLocalized("Colorez-moi"); label = XtVaCreateManagedWidget("main_window", xmLabelWidgetClass, main_w, XmNlabelString, label_str, NULL); XmStringFree(label_str); /* Label = work area */ XtVaSetValues(main_w, XmNmenuBar, menubar, XmNworkWindow, label, NULL); /* background et foreground */ cmap = DefaultColormapOfScreen(XtScreen(label)); display = XtDisplay(label); XAllocNamedColor(display, cmap, colours[0], &back, &spare); XAllocNamedColor(display, cmap, "white", &fore, &spare);
/* Ci-dessous, on ferait mieux d'utiliser XtVaSetValues() */
n=0;
XtSetArg(args[n],XmNbackground, back.pixel);
++n;
XtSetArg(args[n],XmNforeground, fore.pixel);
++n;
XtSetValues(label, args, n);
XtRealizeWidget(top_wid);
XtAppMainLoop(app);
}
/*-------------------------*/
void quit_call(w, item_no, call_data)
/*-------------------------*/
/* N'importe quelle entree dans le menu file appelle cette fonction.
Si l'entree est la premiere (item_no == 0), elle fait exit() */
*/
Widget w; /* entree du menu qui a declenche le callback */
int item_no; /* index de l'entree dans le menu (part de 0) */
XtPointer call_data;
{
if (item_no == 0) /* L'entree "quit" */
exit(0);
}
/*--------------------------*/
void colour_call(w, item_no, call_data)
/*--------------------------*/
/* Appelee lorsqu'une entree du menu Color est selectionnee
Change la couleur du label de la work area
Note: Pour modifier dynamiquement la couleur on utilisera la fonction
XtSetArg() avec XtSetValues.
*/
Widget w;
int item_no;
XtPointer call_data;
{
int n =0;
Arg args[1];
XColor xcolour, spare;
if (XAllocNamedColor(display, cmap, colours[item_no],
&xcolour, &spare) == 0)
return;
n=0;
XtSetArg(args[n],XmNbackground, xcolour.pixel);
++n;
XtSetValues(label, args, n);
}
Bon. Respirez-un grand coup... Hmmmmmffffff ! Pas de panique !
Beaucoup de nouveautés ici, des fonctions bizarres qui ressemblent à celles déjà étudiées mais légèrement différentes, de la couleur (oui, de la couleur !), des chaînes de caractères utilisées de manière bizarre, etc.... Nous allons étudier tout ceci calmement...
Dans cet exemple, nous avons créé la MenuBar à l'aide de la fonction XmVaCreateSimpleMenuBar() qui est plus simple à utiliser que la fonction XmCreateMenuBar() de l'exemple précédent. Elle permet d'indiquer les CascadeButtons que l'on veut mettre dans la barre de menu, dès la création de cette dernière.
Comme XtVaSetValues() et XtVaCreateManagedWidget() cette fonction possède un nombre d'arguments variables. En plus des paires Ressource/Valeur déjà rencontrées, elle accepte des arguments spécifiques permettant d'indiquer quels éléments on va insérer dans la MenuBar.
Les ressources que l'on peut spécifier sont les mêmes que pour un RowColumn puisque le widget MenuBar est un RowColumn déguisé.
Paramètres de la fonction XmVaCreateSimpleMenuBar() :
Comment modifier le label d'un CascadeButton de la MenuBar dans le fichier de ressources ?
Puisqu'on ne crée plus les boutons poussoirs à l'aide des fonctions standards (XmCreateCascadeButton(), XtVaCreateManagedWidget(), etc...), on ne sait pas quels noms leurs sont associés. Puisque les boutons sont crées séquentiellement, un nom de type "bouton_n" est alloué automatiquement au n-ième bouton n, le premier bouton ayant comme indice 0.
Important : il faut prendre l'habitude de libèrer systématiquement la place occupée par une XmString dès qu'on en a plus besoin. Cette opération est réalisée en appelant la fonction XmStringFree().
Cette fonction permet en un seul appel d'effectuer 3 des 4 opérations nécessaires à la création d'un menu déroulant. Avec XmVaCreateSimplePulldownMenu() on créée le PulldownMenu avec tous ses éléments, on spécifie une fonction de callback (la même pour toutes les éléments), et on spécifie même les raccourcis clavier !
Regardons comment nous avons créé le menu déroulant Quit :
quit_w = XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 0, quit_call, XmVaPUSHBUTTON, quit, 'Q', NULL, NULL, NULL);
![]()
Comme pour XmVaCreateSImpleMenuBar(), cette fonction accepte un nombre variable d'arguments :
La différence entre un raccourci et un accélérateur est subtile : le raccourci exécute l'action correspondant à la sélection souris du bouton, si le menu est déjà déroulé. L'accélérateur fait la même chose même si le menu n'est pas déroulé.
La sélection de ce choix dans le menu appelle la fonction de callback en passant comme client_data l'indice du bouton poussoir dans le menu. L'indice commence à 0 pour le premier choix dans le menu.
Remarque : les menus déroulants imbriqués sont beaucoup plus simples à gérer à l`aide de la méthode de création de menus standard.
ATTENTION : Les menus ne sont pas censés évoluer dynamiquement. Une fois crées, on ne rajoute rien dedans (sauf cas exceptionnel). En tout cas, on ne rajoute rien dans les menus crées avec les fonctions XmVaCreateSimple....
Le menu Colour est créé de manière similaire si ce n'est qu'il possède cinq choix au lieu d'un seul dans le menu Quit.
Il reste à étudier une dernière chose : comment traiter les choix faits par l'utilisateur lors de l'exécution de notre programme ?
Chaque PullDown menu possède sa propre fonction de callback. La fonction de callback possède trois paramètres :
Le paramètre client_data de la fonction de callback contient l'indice de l'élément du menu qui a déclenché l'action.
Le champ reason de la structure de callback servira à faire la différence entre ToggleButtons et PushButtons si le menu contient ces deux sortes d'éléments. En effet, pour le PushButton, reason vaudra CR_ACTIVATE, pour le ToggleButton CR_VALUE_CHANGED.
Ainsi, dans la fonction de callback du menu Quit, quit_call(), une seule sélection est possible (la variable item_no doit être égale à zéro).
Dans la fonction de callback du menu Colour, colour_call(), la valeur du choix va provoquer une action (changer la couleur du fond de widget de type Label qui se trouve dans la work area de la MainWindow).
Ne vous affolez pas à propos de la couleur, reportez-vous au cours.correspondant. Pour comprendre l'exemple, il suffit de savoir que la structure de données xcolour, de type XColor stocke la valeur de la couleur que nous désirons dans le champ xcolour.pixel après allocation par l'appel de XAllocNamedColor(). Il suffit ensuite de positionner la ressource XmNbackgound du widget de type Label à l'aide des fonctions XtSetArg() et XtSetValues()
A compléter...
A compléter