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.