Cette partie ne présente que ce qu'est une fenêtre pour la bibliothèque, elle suppose connu ce qu'est une fenêtre dans le système considéré.
Une fenêtre est définie comme l'association :
En pratique, une fenêtre est définie comme une structure, déclarée comme type Fenetre. Les champs de cette structure accessibles à l'utilisateur sont les suivants :
typedef struct fenetre { struct fenetre *suivante; /* chaînage simple */ (selon_l'OS) handle; /* Code de la fenêtre proprement dite */ int attributs; /* Attributs de cette fenêtre */ int x,y,larg,haut; /* Coordonnées externes de la fenêtre */ int w_x,w_y,w_larg,w_haut; /* Coordonnées internes de la fenêtre */ int o_x,o_y,o_larg,o_haut; /* Anciennes coordonnées externes */ int type; /* Type de fenêtre */ int numero_arbre; /* Numéro de l'arbre ou de l'image associée */ boolean drapeau; /* Pour un drapeau utilisateur quelconque... */ struct /* Une série de drapeaux divers */ { unsigned repliee : 1; /* VRAI si elle est repliée */ unsigned icone : 1; /* VRAI si elle est iconifiée */ unsigned redessin: 1; /* VRAI si il faut tout redessiner en cas de changement de taille */ unsigned pointeur: 1; /* VRAI si elle contient un pointeur souris */ unsigned ouverte : 1; /* VRAI si elle est ouverte */ unsigned util_3 : 1; /* Utilisateur, 3 */ unsigned util_2 : 1; /* Utilisateur, 2 */ unsigned util_1 : 1; /* Utilisateur, 1 */ } drapeaux; char *titre; /* Ligne de titre */ char *infos; /* Ligne d'informations */ Arbre *arbre; /* Adresse de l'arbre associé à la fenêtre */ void *adresse; /* Adresse de qq chose associé à la fenêtre : utilisateur */ void *pointeur; /* Adresse d'une autre chose associée à la fenêtre : utilisateur */ void (*redessin) (struct fenetre *, int x, int y, int larg, int haut); /* Redessin */ void (*ascenseur) (struct fenetre *, int type, int ss_type, int position); /* Ascenseurs */ boolean (*clavier)(struct fenetre *, unsigned int touche, int etat); /* Clavier */ boolean (*clic) (struct fenetre *, int bouton, int n_clic, int etat, int x, int y); /* Clic */ boolean (*message)(struct fenetre *); boolean (*rect) (struct fenetre *, int x, int y); /* Premier événement rectangle => pointeur */ boolean (*temps) (struct fenetre *); /* Gestion du timer */ struct { unsigned taille : 1; /* VRAI -> appeler message si chgmt de taille */ unsigned position : 1; /* et ainsi de suite... */ unsigned plein_ecran : 1; unsigned premier_plan: 1; unsigned arriere_plan: 1; unsigned fermeture : 1; unsigned autres : 1; unsigned utilisateur : 1; } msg_masque; /* Spécifie les messages qui doivent être transmis à la fenêtre */ [...] } Fenetre;
La structure peut contenir d'autres champs, nécessaires à la réalisation pour un type de système donné.
De façon générale, à l'exception des champs adresse, pointeur et msg_masque définis explicitement par l'utilisateur, il ne devrait jamais être nécessaire de modifier ces valeurs dans le programme : ces valeurs sont surtout accessibles dans un but informatif (adaptation de l'échelle pour le dessin dans la fenêtre, en particulier).
Le nombre de fenêtres n'est a priori pas limité, si ce n'est par la mémoire disponible et les limites du système utilisé.
Fenetre *creer_fenetre(int attributs, char *titre, char *infos, void (*redessin) (Fenetre *, int, int, int, int), void (*ascenseur) (Fenetre *, int, int, int), boolean (*clavier)(Fenetre *, unsigned int, int), boolean (*clic) (Fenetre *, int, int, int , int, int), boolean (*message)(Fenetre *, Message message), boolean (*rect) (Fenetre *, int, int, int, int), boolean (*temps) (Fenetre *) );
Les coordonnées souris et de redessin sont exprimées de façon absolue par rapport à l'écran, pour être directement utilisables dans les routines de la VDI.
Lors de l'appel de la fonction de redessin, la souris a été coupée et l'AES passé dans le mode adéquat (wind_update(BEG_UPDATE)) ; quand toutes les zones ont été redessinées (donc une fois la fonction de redessin quittée), la souris est remise et l'AES repassé dans le mode normal. La fonction de redessin n'a donc pas à se préoccuper de cet aspect.
De même, le clipping VDI a été installé à la zone à redessiner (celle passée en paramètres à la fonction) lors de l'appel, pour la station VDI associée au programme (station _PRG_station->handle).
Les attributs sont tous gérés, sous réserve que la version de l'AES sur laquelle tourne le programme les connaisse.
Les coordonnées de redessin et de la souris sont exprimées de façon relative à la fenêtre, pour être directement utilisables dans les routines X.
Parmi les différents attributs, seuls la barre d'information, le
bouton de fermeture et les ascenseurs sont gérés par la
bibliothèque. La barre de titre et tout ce qui concerne les changements
de taille et de position sont laissés à la charge du
gestionnaire de fenêtre. Le programme a ainsi un aspect homogène
avec les autres programmes X.
La fermeture depuis le gestionnaire de fenêtre est bloquée.
Fenetre *creer_gem_fenetre(int attributs, int numero_arbre, char *titre, int x, y)
Création et ouverture d'une fenêtre dédiée
à l'affichage d'une boîte de dialogue.
attributs précise les attributs de la fenêtre (voir ci-dessus).
titre indique le titre à donner
à la fenêtre.
numero_arbre est le numéro de l'arbre contenu par la
fenêtre. En cas d'échec, la fonction renvoie NULL.
x et y indiquent la position originelle de la fenêtre. Si des
valeurs négatives sont passées pour l'une des coordonnées, la fenêtre est
centrée à l'écran selon cette direction.
Cet arbre est complètement non-bloquant, le programme reste entièrement utilisable en parallèle. La bibliothèque s'occupe entièrement de la mise à la taille de la fenêtre, des redessins, de la gestion des clics et du clavier (en tenant compte des informations associées à l'arbre indiqué). La taille de la fenêtre ne peut pas être modifiée (même s'il existe un attribut SIZER), les déplacements sont pris en charge. En revanche, la gestion des autres événements reste à la charge du programme. La fenêtre apparaît initialement au centre de l'écran.
Un arbre ne peut être utilisé que dans une seule fenêtre à la fois (que la fenêtre soit simple ou mixte). Si cette fonction est appelée alors qu'une fenêtre contenant cet arbre existe déjà, la fenêtre existante est passée au premier plan.
S'il existe un gadget FULLER, ce gadget est utilisé pour plier ou déplier le formulaire : la forme dépliée est la forme normale, la forme pliée ne laisse apparaître que la barre de titre.
Si la fenêtre est fermée puis réouverte, la position est mémorisée et la fenêtre réapparaît à l'ancienne position.
Fenetre *creer_mixte_fenetre(int attributs, int numero_arbre, char mode, void (*redessin)(Fenetre *, int, int, int, int), boolean (*clavier) (Fenetre *, unsigned int, int), boolean (*clic) (Fenetre *, int, int, int, int, int), boolean (*message) (Fenetre *), boolean (*rect) (Fenetre *, int, int), boolean (*temps) (Fenetre *) )
Cette fonction crée et ouvre une fenêtre dont un côté est occupé par un arbre et le reste est d'usage libre. La taille de l'arbre est adaptée, si nécessaire, de telle sorte qu'il occupe toute la largeur ou toute la hauteur de la fenêtre (certains objets peuvent donc être invisibles si la fenêtre est trop petite).
Mode | Position de l'arbre |
---|---|
AUCUN | Laissée aux soins du programme |
GAUCHE | Côté gauche, sur toute la hauteur |
DROITE | Côté droit, sur toute la hauteur |
HAUT | En haut, sur toute la largeur |
BAS | En bas, sur toute la largeur |
les paramètres suivants précisent les différentes fonctions qui doivent être appelées lorsqu'un événement survient sur la fenêtre ; ils ont la même signification que dans la fonction creer_fenetre.
Ces fonctions ne sont appelées que si l'événement concerne la partie libre de la fenêtre, tous les événements concernant l'arbre sont directement pris en charge par le programme comme si l'arbre occupait seul la fenêtre. L'arbre, en particulier, est déplacé, redimensionné et redessiné avec la fenêtre ; la fonction de redessin n'est appelée que si la partie libre est concernée. En cas d'appui sur une touche, la touche est d'abord transmise aux routines de gestion de l'arbre puis à la routine associée à la fenêtre. Cela permet d'utiliser des objets textes dans l'arbre et de permettre une mise à jour « en temps réel » de la région libre en fonction des champs textes de l'arbre.
Un arbre ne peut être utilisé que dans une seule fenêtre à la fois (que la fenêtre soit simple ou mixte). Si cette fonction est appelée alors qu'une fenêtre contenant cet arbre existe déjà, la fenêtre existante est passée au premier plan.
int ouvre_fenetre(Fenetre *fenetre, int x, int y, int larg, int haut)
Demande l'ouverture de la fenêtre fenetre. Les quatre paramètres suivants indiquent les dimensions que doit avoir la fenêtre à l'ouverture ; ces dimensions représentent la taille totale de la fenêtre, y compris les gadgets. Si la fenêtre contient un arbre GEM, le curseur est placé sur le premier objet éditable actif de l'arbre.
Si tout s'est bien passé, la fonction renvoie VRAI. Si fenetre vaut NULL ou si la fenêtre est déjà ouverte, la fonction renvoie FAUX.
La fenêtre doit être déclarée au système pour que l'ouverture fonctionne. Elle doit donc avoir été créée juste auparavant, ou fermée avec FEN_ferme(fenetre, FAUX).
int FEN_reouvre(Fenetre *fenetre)
Ouvre la fenetre fenetre en utilisant les dimensions et la position de la fenêtre lors de sa fermeture. La fenêtre doit avoir été fermée par FEN_ferme.
De façon générale, la bibliothèque essaye de prendre en charge autant que possible la gestion des différentes fenêtres, de telle sorte que le programme proprement dit n'ait à réaliser que ce que la bibliothèque ne peut pas deviner, par exemple les redessins.
Par analogie avec les systèmes graphiques usuels, tout tourne autour d'une simple fonction d'attente des événements. Cette fonction se charge d'appeler les différentes fonctions du programme en fonction des opérations réalisées par l'utilisateur : modification des fenêtres, choix d'une option de menu, clic dans une boîte de dialogue,... Dans le cas le plus simple, revenir de cette fonction revient à vouloir quitter le programme.
int attend_evenement(int masque, int rect, int x_rc, int y_rc, int larg_rc, int haut_rc, unsigned long compteur, int *x, int *y, int *bouton, int *etat, unsigned int *touche, int *n_clic)
C'est cette fonction qui se charge de la détection et du traitement des événements. Les différents paramètres servent à contrôler la nature des événements.
Masque | Type d'événement |
---|---|
MU_KEYBD | clavier |
MU_BUTTON | clic de la souris |
MU_M2 | entrée/sortie de la souris d'une zone de l'écran |
MU_TIMER | chronomètre |
MU_MESAG | événements fenêtre |
Ce paragraphe décrit la façon dont se comporte la routine attend_evenement lorsque survient un événement, dans les différentes situations pouvant survenir.
La bibliothèque cherche quelle est la fenêtre cliquée.
Si la bibliothèque ne trouve pas de fonction de gestion des clics associée à la fenêtre (fenetre->clic == NULL), l'événement est ignoré.
La fonction de gestion des clics reçoit comme paramètres la fenêtre concernée, le bouton utilisé, le nombre de clics, l'état des touches spéciales (control, alternate, shift) et la position de la souris (directement utilisable). Elle doit renvoyer VRAI si elle a pu traiter l'événement, _FIN_PRG si l'événement conduit à devoir quitter le programme, _QUITTER s'il conduit à devoir interrompre le travail en cours (par exemple en fermant la fenêtre) et FAUX dans les autres cas. Si elle renvoie FAUX, l'événement sera considéré comme non-traité et la fonction attend_evenement sera terminée.
La bibliothèque commence par chercher la fenêtre à laquelle doit se rapporter l'événement. Le choix de la fenêtre dépend du système considéré :
Si cette fenêtre contient un arbre, l'événement clavier est transmis aux routines de gestion de la barre de menu.
S'il n'y a pas de menu, ou que l'événement n'a pas été traité par les routines associé, l'événement est considéré comme n'ayant pas pu être traité et la routine attend_evenement est quittée.
Le programme appelle la fonction de gestion associée au menu, avec comme paramètre le numéro de l'option choisie. Cette fonction n'est appelée que si l'option choisie est active. Elle doit renvoyer VRAI si elle a pu traiter l'événement, _FIN_PRG si l'événement conduit à devoir quitter le programme, _QUITTER s'il conduit à devoir interrompre le travail en cours (par exemple en fermant la fenêtre) et FAUX dans les autres cas.
La fenêtre est mise à la nouvelle dimension ; les anciennes dimensions extérieures sont enregistrées dans fenetre->o_x, fenetre->o_y, fenetre->o_larg et fenetre->o_haut.
Si la fenêtre possède une routine de gestion des messages et si le drapeaux fenetre->msg_masque.taille vaut VRAI, la routine de gestion des messages est appelée. Gardez bien en mémoire le fait que cet appel a lieu après le changement de taille de la fenêtre. Les dimensions dans la structure fenêtre sont donc les nouvelles dimensions.
Si le drapeaux fenetre->drapeaux.redessin vaut VRAI, un message de redessin portant sur toute la fenêtre est envoyé. Ceci permet de contourner les problèmes qui peuvent apparaître lors d'une réduction de la taille de la fenêtre, qui n'envoie pas toujours de message de redessin.
Note : version GEM. Les demandes de passage en plein écran (message WM_FULLED) sont traitées exactement comme des changements de taille ; la bibliothèque se charge du bascul entre le plein écran et la taille normale.
La bibliothèque se charge de remettre à jour les valeurs concernées dans la structure Fenetre impliquée.
La bibliothèque essaye d'appeler la fonction de gestion des ascenseurs associée à la fenêtre (fenetre->ascenseur). Cette fonction reçoit comme paramètres la fenêtre concernée et les indications nécessaires au traitement de l'événement - voir plus haut.
Si cette fonction n'existe pas (fenetre->ascenseur == NULL), l'événement est ignoré.
Si le drapeaux fenetre->drapeaux.pointeur vaut VRAI et si la fenêtre a une fonction de gestion associée (fenetre->rect != NULL), cette fonction est appelée. Cette fonction reçoit comme paramètres la fenêtre concernée, les nouvelles coordonnées de la souris et les anciennes coordonnées.
Note : version GEM. L'appel n'a lieu que si la souris se trouve sur la fenêtre au premier plan.
Si la fenêtre a une fonction de gestion associée (fenetre->rect != NULL), cette fonction est appelée. Cette fonction reçoit comme paramètres la fenêtre concernée et les nouvelles coordonnées de la souris. Les anciennes coordonnées (deux derniers paramètres de la fonction) sont forcées à -1.
Note : version GEM. L'appel n'a lieu que si la souris entre sur la fenêtre au premier plan.
Si la fenêtre a une fonction de gestion associée (fenetre->rect != NULL), cette fonction est appelée. Cette fonction reçoit comme paramètres la fenêtre concernée, comme nouvelles coordonnées de la souris, (-1, -1) et les anciennes coordonnées.
Note : version GEM. L'appel n'a lieu que si la souris sort de la fenêtre au premier plan.
Si la fenêtre contient un arbre, toute la portion occupée par l'arbre est entièrement redessinée par la bibliothèque.
S'il faut redessiner des portions libres, la bibliothèque appelle la fonction de redessin associée à la fenêtre (fonction fenetre->redessin). Cette fonction reçoit comme paramètres la fenêtre à redessiner et les coordonnées de la zone à redessiner. Avant l'appel, tout est préparé de telle sorte que la fonction de redessin n'ait qu'à redessiner, sans avoir à s'occuper de quoi que ce soit d'autre.
Attention ! Cette fonction peut être
appelée à n'importe quel moment, en particulier dans des
systèmes asynchrones non bloquants comme X-Window. Si vous utilisez des
paramètres spéciaux, et en particulier des pointeurs pour le
redessin, mieux vaut vérifier qu'ils ont bien été
définis avant de redessiner, sans quoi une erreur bus est
prévisible.
Exemple Le programme suivant plante à coup sûr dans la
version X-Window :
static void redessin(Fenetre *, int, int, int, int); char *texte = NULL; int main(void) { Fenetre *fen; if ( initialise(NULL, 0, 0) == FAUX ) { fprintf(stderr, "Lancement impossible\n"); return -1; } fen = creer_fenetre(TS_SF_ASC, "Méthode dangereuse", "(C) 1999, Emmanuel CURIS", redessin, NULL, NULL, NULL, NULL, NULL, NULL); ouvre_fenetre(fen, x_gem, y_gem, larg_gem, haut_gem); form_alert(1, "[3][Pas encore de texte][Bien]"); texte = "Ceci est un texte d'essai"; attend_evenement(MU_TIMER, 0, 0, 0, 0, 0, 20000, NULL, NULL, NULL, NULL, NULL, NULL); ferme_fenetre(fen); fin_programme(-1); return 0; } void redessin(Fenetre *fenetre, int x, int y, int larg, int haut) { size_t lg_texte; lg_texte = strlen(texte); /* On affiche au milieu de la fenêtre le texte */ v_gtext(_PRG_station->handle, fenetre->w_x + (fenetre->w_larg >> 1), fenetre->w_y + (fenetre->w_haut >> 1), texte); /* Pour une raison ou une autre, on a besoin de lg_texte après */ }
En effet, sous X, la boîte d'alerte est ouverte comme une fenêtre et sa gestion demande la gestion des messages de redessin (pour que le déplacement de cette fenêtre ne laisse pas un écran sale). La fonction de redessin est donc appelée avant que texte soit défini, et le résultat est indéfini. Une version correcte serait, par exemple :
void redessin(Fenetre *fenetre, int x, int y, int larg, int haut) { size_t lg_texte; if (texte == NULL) { v_gtext(_PRG_station->handle, fenetre->w_x + (fenetre->w_larg >> 1), fenetre->w_y + (fenetre->w_haut >> 1), "Pas encore de texte disponible"); return; } lg_texte = strlen(texte); /* On affiche au milieu de la fenêtre le texte */ v_gtext(_PRG_station->handle, fenetre->w_x + (fenetre->w_larg >> 1), fenetre->w_y + (fenetre->w_haut >> 1), texte); }
Les coordonnées sont données de façon absolue, comme utilisé par la VDI pour le dessin. Elles sont donc directement utilisables.
La souris est coupée et l'AES averti avant l'appel de la fonction de redessin.
Les coordonnées sont données relativement à la fenêtre, comme utiliséé par les routines X de dessin. Elles sont donc directement utilisables.
int FEN_ascenseur(Fenetre *fenetre, int ascenseur, int position, int taille)
Modifie l'un des deux ascenseurs de la fenêtre fenetre. Si
position vaut -1, la position n'est pas changée, sinon l'ascenseur
est mis à cette position exprimée en pour-milles de l'intervalle possible.
Si taille vaut -1, la taille n'est pas changée, sinon l'ascenseur
est mis à cette dimension, exprimée en pour-milles de la taille du fond de
l'ascenseur.
Si ascenseur = ASC_VERTICAL, l'ascenseur modifié est
l'ascenseur vertical ; s'il vaut ASC_HORIZONTAL, l'ascenseur modifié
est l'ascenseur horizontal.
int FEN_infos(Fenetre *fenetre, char *infos)
Affiche le texte infos dans la barre d'informations de la fenêtre fenetre. Attention ! Ce texte ne doit pas être libéré tant que la fenêtre existe.
int FEN_titre(Fenetre *fenetre, char *titre)
Définit le texte infos comme titre de la fenêtre fenetre. Attention ! Ce texte ne doit pas être libéré tant que la fenêtre existe.
Fenetre *FEN_premierplan(Fenetre *fenetre)
Force la fenêtre fenetre à passer au premier plan. La fonction renvoie, si possible, la nouvelle fenêtre en premier plan (NULL si ce n'est pas une fenêtre du programme ou si c'est impossible à savoir). Si fenetre = NULL, la fonction se contente de chercher la fenêtre au premier plan.
boolean FEN_redessin(Fenetre *fenetre)
Demande le redessin de la totalité de la fenêtre fenetre.
void FEN_ferme(Fenetre *fenetre, boolean detruit)
Demande la femeture de la fenêtre fenetre. Si detruit == FAUX, la fenêtre disparaît de l'écran mais elle reste déclarée au système. Si detruit == VRAI, la fenêtre disparaît de l'écran et est enlevée des ressources systèmes. En revanche, la structure reste en mémoire avec ses informations. Il sera donc possible de la rouvrir avec la même position, par exemple, ou sans avoir à redéfinir les différents paramètres.
Dans le deuxième cas, la fenêtre doit être réouverte avec FEN_reouvre uniquement. Dans le premier cas, elle peut être ouverte indifféremment par FEN_reouvre ou ouvre_fenetre.
int ferme_fenetre(Fenetre *fenetre)
Ferme la fenêtre fenetre, l'enlève des ressources du système et détruit la structure associée. Cette structure ne doit donc plus être utilisée par la suite ! Toutes les informations associées à la fenêtre sont définitivement perdues.