Le GEM n'offre que peu de contrôles en standard. Par contre il a été suffisamment bien conçu pour permettre à quiconque le souhaite de se créer des contrôles personnalisés. D'ailleurs, on en trouve dans à peu près toutes les librairies GEM étendus qui existent dont la mienne (WindGem).
Le langage que j'emploierai pour mon exemple est le C car il offre toutes les fonctionnalités nécessaires à la gestion simple d'objets USERDEFS (n'oublions pas que le GEM a été écrit en C) et que c'est le seul langage avec lequel je me suis réellement penché sur ce problème !
// La définition d'un objet standard
typedef struct object { short ob_next; /* objet suivant */ short ob_head; /* nÆpremier objet enfant */ short ob_tail; /* nÆdernier objet enfant */ unsigned short ob_type; /* --> type de l'objet */ */ unsigned short ob_flags; /* flags */ unsigned short ob_state; /* state */ /* long ob_spec; */ /* "contenu" de l'objet */ OBSPEC ob_spec; /* "contenu" = union */ short ob_x; /* position x */ short ob_y; /* position y */ short ob_width; /* Largeur de l'objet */ short ob_height; /* Hauteur de l'objet */ } OBJECT;
// Dans le cas d'un objet UserDefs, ob_spec pointe sur la structure
// suivante...
typedef struct user_blk { short (*ub_code)(struct parm_blk *p_blk); long ub_parm; // Param supplémentaires } USERBLK;
// Structure reçue comme paramètre par la fonction de traitement de
// votre objet UserDefs.
typedef struct parm_blk { OBJECT *pb_tree; // Arbre d'objet short pb_obj; // Numéro de l'objet short pb_prevstate; // Etat précédent short pb_currstate; // Nouvel état short pb_x, pb_y, pb_w, pb_h; // coord. objet short pb_xc, pb_yc, pb_wc, pb_hc; // zone à clipper long pb_parm; // structure UserBlk } PARMBLK;
+ quelques définitions :
#define USR_INDICATOR 0x800 #define USR_ACTIVATOR 0x1800
Ces définitions sont utiles car on décale les bit 3D des objets UserDefs.
Il ne faut fonc plus tester les bit 9 et 10 mais 11 et 12.
Maintenant que l'on a nos structures de travail, on peut construire notre objet UserDef.
Pour cela, il est possible d'utiliser le type étendue de chaque objet (ob_type de la structure OBJECT).
En effet, en temps normal, le Gem n'utilise que l'octet de poids faible de ce mot pour définir le type de l'objet. On a donc droit à un octet (soit 256 possibilités) pour définir nos types étendus.
Pour récupérer le type étendu, il suffit de calculer ob_type >> 8.
La fonction suivante permet de traiter tous les objets d'un arbres d'objet donné.
void set_user(OBJECT *addr_tree) { register int index = 1; /* Objet Racine + 1 */ OBJECT *addr_obj; do { /* Pointe sur l'objet à traiter */ addr_obj = &addr_tree[index]; /* Si c'est un bouton et oui on parle de BOUTON userdefs */ /* Bien masquer le type à cause du type étendu ! */ if ((addr_obj->ob_type & 0xff) == G_BUTTON) { switch (addr_obj->ob_type >> 8) { case XXXXX : (type étendu choisi) set_objc(addr_obj, XXXX_button); break; } } /* Pendant que j'y suis, je vide les champs EDITABLE */ if (addr_obj->ob_flags & EDITABLE) strcpy((addr_obj->ob_spec.tedinfo)->te_ptext, ""); /* Objet suivant */ index ++; } while(! (addr_obj->ob_flags & LASTOB)); } static void set_objc(OBJECT *addr_obj, int cdecl (*code)(PARMBLK *parmblock)) { register int temp; register USERBLK *user; if ((user = (USERBLK *) malloc(sizeof(USERBLK))) != 0) { user->ub_code = code; addr_obj->ob_type = (addr_obj->ob_type & 0xff00) | G_USERDEF; user->ub_parm = (long) addr_obj->ob_spec.userblk; addr_obj->ob_spec.userblk = user; /*-= Translation des flags 3D en ob_flags_11 et ob_flags_12. ===========-*/ if ((temp = (addr_obj->ob_flags & ACTIVATOR)) != 0) { temp <<= 2; addr_obj->ob_flags = (addr_obj->ob_flags & ~ACTIVATOR) | temp; } } }
Voici la structure que doit avoir la fonction qui est associée à un bouton UserDefs à l'aide de la fonction set_objc.
Cette fonction est exécutée lorsque l'objet doit être redessiné ou que son état a changé.
int cdecl XXXX_button (PARMBLK *parmblock) { register char *texte; int tab[4], flags; /* Définition et mise en place du clipping */ /* Note : fill_tab permet juste de remplir le tableau tab[4] */ fill_tab (tab, 4, parmblock->pb_xc, parmblock->pb_yc, parmblock->pb_wc + parmblock->pb_xc - 1, parmblock->pb_hc + parmblock->pb_yc - 1); vs_clip (Sys->VdiHandle, TRUE, tab); /* Récupère les attributs et les coordonnées de l'objet */ x = parmblock->pb_x; y = parmblock->pb_y; w = parmblock->pb_w; h = parmblock->pb_h; /* on pointe sur le texte de l'objet */ texte = (char *)parmblock->pb_parm; /* Le flags...*/ flags = parmblock->pb_tree[parmblock->pb_obj].ob_flags; ...
// Ici on redessine l'objet
Note : si flags & USR_INDICATOR => objet en 3D !
/* Supprimer le clipping : attention à ne pas l'oublier ! */ vs_clip (Sys->VdiHandle, FALSE, tab); return (parmblock->pb_currstate & ~SELECTED); }
Et voilà, ce n'est pas très compliqué. A vous de jouer.