C.
RECANATI
Interfaces
Graphiques
ILOG
2, septembre 2004
TP n°4 : fin Xlib et
début Motif
Exercice 1: Finir
l’exercice 2 du TP précédent (programme Menu.c), et me l’envoyer par mail, en binôme ou
en solo, à
cathy@lipn.univ-paris13.fr.
Faites figurer vos noms dans le mail,
et en tête du fichier
C.
Exercice 2: (programme
editeur.c)
1) Créez une
fenêtre blanche et une fille de même fond que vous placerez dans la
mère de sorte qu’elle occupe la moitié inférieure.
Modifiez l’attribut cursor de la fille pour que la
souris change de forme quand elle entre dans cette fenêtre.
(#include
<X11/cursorfont.h> pour utiliser un nom de curseur en argument
de XCreateFontCursor).
2) Dans la partie
supérieure (la mère), vous afficherez un titre au
centre avec XDrawImageString, une fonte assez large et les deux couleurs de votre
choix.
3) Dans la partie inférieure (la fille), vous
éditerez sur une ligne en noir les caractères lus au clavier. Le
caractère Return effacera
la ligne courante et les caractères Delete et Backspace permettront de reculer.
F A chaque
entrée d’un caractère, on efface la ligne courante (effacer
= afficher en inverse video), et on met à jour le tampon qui
mémorise la ligne, puis on réaffiche intégralement la
ligne correctement mise à jour. Cela évite d’avoir à
positionner les caractères un à un.
Exercice 3:
(exercice pushButton.c)
Tester le premier programme
du cours MOTIF. Compilation : gcc exo.c –lXm –lXt
–lX11
Exercice
4: (exercice rowColumn.c + fichiers de
ressources)
Faire un programme qui
crée 6 ToggleButton à l’intérieur
d’un RowColumn (lui-même placé dans le widget de plus
haut niveau). Essayer le programme tel quel, puis charger des fichiers de
ressources à l’aide de la commande xrdb pour
modifier les ressources des widgets du programme (sans le recompiler !),
de façon à :
1) positionner les 6
boutons sur deux colonnes (modifier les ressources numColumns et packing du RowColumn dans un fichier de ressource).
2)
modifier la fonte d’un des boutons (avec 9x15 par
exemple).
3) modifier le
deuxième bouton pour qu’il affiche un Pixmap au lieu d’une chaîne. (cf. labelPixmap et labelType hérités
de Label).
F Pour la ressource labelPixmap, on peut donner l’adresse complète
d’un fichier au format bitmap.
(Dans /usr/include/X11/bitmaps il y a des fichiers au format bitmap, et
vous pouvez en créer avec bitmap).
4) afficher tous les
boutons horizontalement.
5) modifier les
ressources du RowColumn pour que ces boutons aient un comportement de boutons
radios. (cf. radioBehavior et radioAlwaysOne).
---------------------
Exercice
2 du TP 3: (programme Menu.c)
Dans
une fenêtre initiale, on veut faire apparaître un popup menu
proposant un choix d’items. Le nom de la fonte utilisée pour
écrire les items pourra être passée en argument au
programme, ou on utilisera la fonte par défaut. La taille du menu sera
fonction de cette fonte. Le menu n’est pas affiché initialement,
mais apparaît sous la souris quand on clique dans la fenêtre
principale. Une fois le menu apparu, on peut maintenir le bouton de la souris
enfoncé, et la faire glisser sur les différents items.
L’item sur lequel se trouve la souris est alors peint en inverse video
(en réalité on dessine un rectangle plein en inverse video - ce
qui fait à la fois apparaître le fond et le texte en
inverse - mais il n’y a pas réécriture à
proprement parler du texte de l’item). Quand on relâche la souris
sur un item, le texte correspondant à l’item ainsi
« sélectionné » est imprimé à
la console. Si on relâche la souris à l’extérieur du
menu, on n’imprime rien. Dans les deux cas, une fois la souris
relâchée, le menu disparaît.
Indications : 1) La hauteur
des items et celle de la fenêtre menu sont paramétrées par la hauteur de la
fonte et le nombre d’items. On commence par charger la fonte et les
informations sur la fonte. Une
fois la hauteur de fonte déterminée, on peut calculer la hauteur
du menu en fonction du nombre d’items. Pour sa largeur, on calculera la
largeur maximum des différentes chaînes d’items dans cette
fonte (avec XTextWidth).
2) On utilise trois variables
de type Window : une pour la fenêtre principale
(win), une pour le fond de la fenêtre du
menu (menu), et une autre variable (w) pour définir de petites fenêtres de
classe InputOnly permettant de repérer les mouvements de la
souris sur les items. Ces fenêtres
« transparentes »
sont positionnées au dessus des items et permettent de savoir quand la
souris pénètre sur un item. Elles permettent de
sélectionner des événements d’entrée/sortie
de la souris, mais on ne dessine pas dedans. Tous les dessins seront
effectués directement dans la fenêtre menu. Quand la fenêtre menu est exposée initialement, on y dessine tous
les textes d’items. Quand la souris entre ou sort d’une
sous-fenêtre w
délimitant un item, on peindra dans la fenêtre
menu un rectangle en inverse video à la hauteur
concernée.
Les fenêtres
InputOnly seront créées dans une boucle utilisant
toujours la même variable w.
Un contexte d’association (XContext) permet de
sauvegarder après chaque création (et avant la suivante) :
1. la position initiale en y de cette fenêtre, et 2. l’item correspondant. Pour
mémoriser et associer ces informations à chaque fenêtre, on
utilisera un tableau de structures contenant de telles paires. Ainsi, dans la boucle de
création, on associe à chaque fenêtre (via le contexte)
l’adresse de la structure correspondante [oubien simplement un entier qui
serait l’indice correspondant aux données de cette fenêtre
dans le tableau].
Dans la boucle de gestion des
événements, connaissant la fenêtre qui a reçu un
événement de type EnterNotify ou LeaveNotify, on peut récupérer, grâce au
contexte d’association, les informations permettant d’identifier
l’item et sa position dans le menu. On peut alors repeindre un rectangle
en inverse video, pour sélectionner l’item ou pour le
désélectionner.
Le programme a donc
l’allure suivante :
char * items[] = { « premier item »,
« deuxième »,
…,
« dernier »} ;
#define NITEM sizeof(items)/sizeof(char*)
typedef struct {
int pos_y ;
char* string ;
} infos ;
infos tab[NITEM] ;
void main(argc, argv) {
Window win, menu, root, w ;
…
// charger et récupérer les infos sur la fonte
// XLoadQueryFont (ou XLoadFont et XQueryFont)
// puis déterminer la largeur et la hauteur de la fenêtre menu
// créer un contexte graphique gc ayant cette fonte
// et un contexte graphique gc_invert permettant
// de faire de l’inverse video
// créer un contexte d’association (XContext) context
// pour gérer les données associées aux sous-fenêtres w
// ENSUITE SEULEMENT !
// créer win et menu
// sélectionner ButtonPress sur win (pour afficher le menu)
// sélectionner Expose sur menu (pour écrire les items)
// création des sous-fenêtres InputOnly
for (i=0 ; i<NITEM ; i++) {
w = XCreateWindow(…,x, yi , …) ;
// selectionner les ev. EnterNotify et LeaveNotify
XSelectInput(dpy, w, …) ;
tab[i].pos_y = yi ; // sauvegarde la position en y
tab[i].string = item[i] ; // et la chaîne associée
XSaveContext(dpy, context, w, …);
}
// afficher win et
les sous-fenêtres de menu
// mais ne pas afficher
menu
events() ;
}
F Attention : sur un clic de souris, il y a un monopole (grab) automatique de la souris pour la fenêtre qui a sélectionné l’événement ButtonPress. Il faut donc ici empêcher cette saisie automatique au niveau de l’application (en ajoutant OwnerGrabButtonMask à ButtonPressMask), car sinon les sous-fenêtres w ne recevront pas les événements souris d’entrée/sortie. En effet, en l’absence de OwnerGrabButtonMask la fenêtre win monopolise la souris et les autres fenêtres ne reçoivent plus d’événements.