Définir soi-même des objets Userdefs

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).

Comment faire ?

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 !

Tout d'abord, les structures utilisées

// 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.

Etape 1 - Identifier les objets que l'on veut gérer soi-même

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.

Etape 2 - Associer une fonction de traitement à notre objet

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;
    }
  }
}

Etape 3 - La fonction de redessin

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);
}

Conclusion

Et voilà, ce n'est pas très compliqué. A vous de jouer.

Philippe Castella