Ressources, ressources étendus et objets USERDEF

par Dominique Béréziat

Sommaire:

Fichiers ressources
Ressources étendus
Nouveaux objets
Rappel: les objets GEM
La routine de dessin
Insertion des routines: types étendues
Voir aussi

Fichiers ressources

Le GEM utilise des ressources graphiques stockées dans des fichiers ressources (l'extension est RSC). Ces ressources contiennent des objets tels que boîtes, boutons, champs d'édition etc... Pour créer des fichiers ressources, il faut un éditeur de ressource. Citons ORCS (domaine publique, ne gère pas les types étendues), RSC3 (domaine publique) ou Interface 2 (commercial, distribué par Parx).

Pour charger un ressource, l'AES offre une fonction : rsrc_load(). Sachez que l'AES utilise la variable d'environnement 'PATH' pour chercher le ressource. Ainsi, on peut placer tout les fichiers ressources dans un seul répertoire, par exemple: 'PATH=C:\bin;C:\GEMSYS\RSC'. Notez que certain programme utilise leur propre routine de chargement de ressource et n'utilise pas la variable PATH ce qui est une erreur.

Ressources étendus

Les versions de l'AES les plus récentes (depuis la version 3.4) (TOS Falcon)utilise un nouveau format de fichier ressource dit ressource étendu. Les nouveautés sont:

Ils n'y a pas à proprement parlé de nouveaux objets dans les ressources étendus à part les icônes couleurs. Depuis l'AES 3.4 il existe de nouveaux flags pour gérer des effets de relief dans les formulaires mais cela dépend pas du ressource.

Nouveaux objets

Comme nous l'avons dit précédemment, les ressources étendus n'intègre pas de nouveaux objets. Toutefois, l'AES permet de coder ses propres objets et cela quelquesoit la version des AES sur les machines Atari. C'est pourquoi, il existe plusieurs bibliothèque GEM proposant des nouveaux objets. Citons par exemple MyDials ou BiG et la liste est assez longue! En général, ces librairies offrent des boutons rond, ou carré avec une croix dedans, des cadres titre, du texte avec des attributs tel que gras, souligné etc ...

Nous exposons ici les techniques pour programmer des objets :

Rappel: les objets GEM

Les objets GEM sont constitués d'une arborescence de structure OBJECT. Cette arboresence donne le contenu et la hiérarchie d'un formulaire. Les deux champs qui nous intéresse ici sont le type de l'objet 'ob_type' et le champ 'ob_spec' qui pointe vers des données spécifique à chaque type d'objet.

typedef struct
{
        int             ob_next;
        int             ob_head;
        int             ob_tail;
        unsigned int    ob_type;
        unsigned int    ob_flags;
        unsigned int    ob_state;
        long            ob_spec;
        int             ob_x;
        int             ob_y;
        int             ob_width;
        int             ob_height;
} OBJECT;

Les objets utilisateurs possède le type G_USERDEF (24). Ces objets ont leur champs 'ob_type' qui pointe vers une structure 'USERBLK' dont voici la déclaration C:

typedef struct
{
        int cdecl (*ub_code)(struct __parmblk *parmblock);
        long      ub_parm;
} USERBLK;

Certains compilateurs définissent cette structure sous le nom APPBLK (dont il semble que ce soit le nom officiel donné par Atari). Cette structure possède deux champs. Le premier, 'ub_code', est l'adresse de la routine qui est appelé par l'AES pour dessiner l'objet.

Disgression : Les dessins des objets se font par l'intermédaire des fonctions objc_draw() et objc_change() à deux exceptions toutefois : les formulaires de fond de bureau et les toolbars (depuis l'AES 4.1) sont directement dessinés par l'AES.

La routine reçoit en paramètre un pointeur sur la structure:

typedef struct __parmblk
{
        OBJECT  *pb_tree;
        int     pb_obj;
        int     pb_prevstate;
        int     pb_currstate;
        int     pb_x, pb_y, pb_w, pb_h;
        int     pb_xc, pb_yc, pb_wc, pb_hc;
        long    pb_parm;
} PARMBLK;

Cette structure est mise à jour par l'AES et nous servira à dessiner notre objet. La valeur de retour a aussi une importance que nous soulignerons dans le paragraphe suivant.

Le second paramètre, 'ub_parm', est un paramètre laissé libre à l'utilisateur. Il sera recopié dans la variable 'parmblock' passé à la routine de dessin dans le champs 'pb_parm'.

La routine de dessin

Passons au vif du sujet, le dessin de notre l'objet. La variable 'parmblk' contient toutes les informations nécessaires:

Pour obtenir plus d'information sur notre objet, il suffit de faire référence à 'parmblk->pb_tree[parmblk->pb_index]'.

Les champs pb_prevstate et pb_currstate permettent de savoir si l'on doit rédessiner complètement un objet (cas où pb_prevstate == pb_currstate) ou bien si l'on doit changer seulement un ou plusieur état.

La routine doit retourner un entier. Cette entier indique à l'AES que sont les états qu'il doit redessiné par dessus notre objet! Si on retourne 0, l'AES ne fera rien. Les états DISABLED, OUTLINED, CROSSED, CHECKED sont pris en compte par L'AES mais pas SHADOWED et SELECTED.

Voici un exemple de routine d'objet, un simple bouton. Ce code amenera quelques commentaires.

exemple pour PureC/Gcc
extern int handle; /* id de la station de travail virtuelle */

int cdecl bouton( PARMBLK *pblk)
{
	int xy[4];
	char *txt = (char *)pblk->ub_parm;

	/* Masquons notre objet */
	xy[0] = pblk -> pb_xc;
  	xy[1] = pblk -> pb_yc;
   	xy[2] = pblk -> pb_wc + xy[0] - 1;
   	xy[3] = pblk -> pb_hc + xy[1] - 1;
	vs_clip( handle, 1, xy);
	
	if( pblk -> pb_oldstate == pblk -> pb_currstate) {

	   /* Dessinons le cadre */
	   vswr_mode( app.handle, MD_REPLACE);
	   vsf_interior( handle, WHITE);
	   vsf_perimeter( handle, 1);
	   xy[0] = pblk -> pb_x;
	   xy[1] = pblk -> pb_y;
	   xy[2] = pblk -> pb_w + xy[0] - 1;
	   xy[3] = pblk -> pb_h + xy[1] - 1;
	   v_bar( handle, xy);
	
	   /* Dessinons le texte (centré) */

	   vqt_extend(handle, str, xy); /* taille du texte */
	   v_gtext( handle,  
	            pblk -> pb_x + (pblk -> pb_w - xy[2] + xy[0])/2,
		    pblk -> pb_y + (pblk -> pb_h + hcar), 
		    str);
	   res = 0;
        } else {
	   res = pblk->currstate & (DISABLED|OUTLINED|CROSSED|CHECKED|SELECTED);
	}
	vs_clip( handle, 0, tab);

	/* pour que l'aes redessine les autres états */
	return res;
}

La variable handle est l'identificateur VDI d'une station de travail virtuelle. Vous pouvez utiliser celui ouvert par l'AES (donné par la fonction graf_handle()) mais il est recommandé d'utiliser sa propre station (fonction v_opnvwrk()).

L'opération de masquage est hautement recommandé en particulier. On trouve ce masquage avec les fonctions objc_draw() et objc_change() qui demande une zone de masque en paramètre. Le masquage sera tres important lors d'intégration des formulaires dans des fenêtres.

Par contre, on ne pas utiliser les fonctions objc_draw() ou objc_change() dans une routine USERBLK sous peine de plantage. De plus, les variables locales utilisé dans les routine userblk doivent être réduit au minimun, il faut utilisez plutot des variables globales.

Insertion des routines: types étendus

Après codages des nouvelles routines, il faut ensuite les installer dans les objets. La plupart des librairies GEM utilise le type étendu des objets. Il s'agit de l'octet de poid fort du champs ob_type de la structure OBJECT. En effet le GEM ne regarde que l'octet de poid faible, ce qui laisse donc cet octet libre. Il suffit maintenant d'attribuer un type étendu qui caractérisera la routine à un objet.

Pour notre routine de bouton, on pourra prendre un objet de type bouton et lui attribué le type etendue 18 par exemple. Il faudra également récupérer le texte du bouton et le passer à la routine d'où l'utilité du champ ub_parm de USERBLK.

#define G_BOUTON  18

/* cette routine utilise le type etendus des objets pour installer
   la routine correspondante dans un formulaire
*/

void InstallUserblk( OBJECT *tree)
{
   int dum=-1;
   int xtype;
   int type;
   int bouton( PARMBLK *);
   /* definier les autres routines */
   USERBLK *pblk;

   do {
      dum ++;
      xtype = (tree[dum].ob_type & 0xFF00)>>8;
      type = tree[dum].ob_type & 0x00FF;
      switch( xtype) {
      case G_BOUTON:
         pblk = (USERBLK*)malloc(sizeof(USERBLK));
         tree[dum].ob_type = xtype | USERDEF;
	 pblk -> ub_code = bouton;
	 pblk -> ub_code = (long)tree[dum].ob_spec;
	 tree[dum].ob_spec = (long) pblk;
	 break;
      /* gerer les autres routines */
      }
   } while( !(tree[dum].ob_flags & LASTOB));
}
Bien sur, il faudra coder une autre routine pour librérer la mémoire alloué (les malloc) avant de terminer le programme.

Voir aussi

La programmation GEM, Les variables d'environnements