Obligement - L'Amiga au maximum

Jeudi 23 novembre 2017 - 10:04  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : MUI - dérivation des classes MUI
(Article écrit par Mathias Parnaudeau - octobre 2004)


Chapitre 7

Notre programme DisKo, bien qu'extrêmement dépouillé, présente l'apparence et les fonctionnalités d'un vrai lecteur de CD. D'un côté, il faudrait améliorer la gestion du module cdmanager mais ce n'est pas l'objet de la série. Ainsi, cette partie restera notre facteur limitant. D'un autre côté, pour arriver jusque-là nous avons acquis une connaissance de MUI suffisante pour découvrir le concept le plus attendu : l'héritage de classe !

En programmation objet, l'héritage permet de créer une classe qui découle d'une autre. Ainsi, elle en récupère les propriétés mais l'intérêt est de lui en ajouter pour un besoin particulier. Par exemple, en regardant ce qui existe, la classe String découle de la classe Area, c'est pourquoi elle possède aussi les attributs Frame ou Font. Mais pour les besoins qui font toute sa spécificité, la classe String s'est enrichie des attributs MaxLen, Format, Contents et autres qui lui sont propres. Si on souhaitait développer un composant qui permette la saisie facilitée de dates, on pourrait dériver cette classe String en lui ajoutant des traitements propres aux dates.

Application à DisKo

Entre la gestion des clics sur les boutons d'action, l'affichage de la liste, la mise à jour du numéro de piste courante, etc. on peut commencer à s'y perdre. C'est pour cela qu'il faut confier des tâches à un objet d'un nouveau type. Pour DisKo, nous allons donc créer une nouvelle classe qui gèrera tous les traitements qui constituent le coeur d'un lecteur de CD. Normalement, cette classe pourrait être réutilisable dans une autre application bien que dans ce cas, ce ne soit pas évident.

En gros, nous voulons créer une boîte noire à qui l'on s'adresse pour qu'elle réalise les tâches de base d'un lecteur. Nous allons donc dériver la classe Group qui peut contenir plusieurs objets et dans notre cas : une liste, un indicateur de la piste courante et un composant Busy pour signaler l'activité du lecteur. L'avantage c'est que tout ce qui concerne l'interface n'utilise que des appels MUI : désormais il sera inutile d'utiliser des hooks (Cf. chapitres précédents) pour réaliser une action sur un composant donné. Au lieu de ça, on adressera des messages (appels de méthodes ou de set() pour changer un attribut) à notre nouvel objet, qui lui, en interne, va appeler des fonctions non MUI. On réalise une encapsulation des traitements, ce qui représente aussi une caractéristique de la programmation objet. Ici on s'affranchit totalement de savoir comment sont traités les accès aux lecteurs CD.

Un peu de technique

Dans la pratique, dériver une classe consiste à :
  • Définir le contenu de la nouvelle classe pour déclarer une structure de données interne à la classe.
  • Créer une "custom class" à partir d'une classe MUI existante et utiliser celle-ci dans la description de l'interface.
  • Implémenter, comme dans la technologie objet classique, un constructeur et un destructeur.
  • Ajouter ce que l'on appelle une fonction "dispatcher", terme expliqué ci-dessous.
  • Ajouter des fonctions spécifiques à la nouvelle classe.
En tant que classe fille, la nouvelle classe doit implémenter les attributs et méthodes de la mère. Donc chaque appel adressé à l'objet fils doit remonter au père, après avoir éventuellement appliqué ses spécificités. Le dispatcher est une fonction spéciale par qui passent tous les appels aux méthodes de la classe. Si une méthode n'a pas été redéfinie, on appelle la méthode de l'objet père avec la syntaxe établie : "DoSuperMethodA(cl, obj, msg)". En revanche, si la méthode est implémentée dans la nouvelle classe, c'est elle qui est appelée et qui est éventuellement chargée d'appeler la méthode mère.

Voici à quoi ressemble notre dispatcher :

DISPATCHERPROTO(DisKoDispatcher)
{
	DISPATCHERARG

	struct DisKoData *data = (struct DisKoData *)INST_DATA(cl, obj);

	/* Un appel de méthode a été fait sur l'objet DisKo, on identifie laquelle c'est */

	switch(msg->MethodID){
		case OM_NEW :						return DisKoNew(cl, obj, (APTR)msg);
		case OM_DISPOSE :					return DisKoDispose(cl, obj, (APTR)msg);
		case OM_SET:						return DisKoSet(cl, obj, (APTR)msg);

		case MUIM_DisKo_Play :			return DisKoPlay(data);
		case MUIM_DisKo_Stop :			return DisKoStop(data);
		case MUIM_DisKo_Previous :		return DisKoPrevious(data);
		case MUIM_DisKo_Next :			return DisKoNext(data);

	}

	/* Si la méthode n'appartient à aucune que l'on a défini, on remonte l'appel à l'objet père */
	return DoSuperMethodA(cl, obj, msg);
}

Les termes DISPATCHERPROTO et DISPATCHERARG peuvent sembler curieux : il s'agit de macros qui permettent un fonctionnement à la fois en 68k et en PowerPC. Comme pour les hooks, c'est lié à l'utilisation de registres particuliers pour le passage des paramètres. Ces macros sont données dans le source (elles utilisent également d'autres éléments du fichier "declgate.h" fourni).

Le dipatcher montre clairement, grâce au switch, que pour chaque message reçu en paramètre, on appelle la méthode associée, interne à notre nouvelle classe. Si le message ne concerne pas cette dernière, on remonte le message à l'objet père.

Les méthodes de notre classes, tout comme les attributs, doivent être identifiées de manière unique. C'est pourquoi on affecte à chacun(e) un ID différent, construit sur une base définie par les règles de la programmation système AmigaOS. La valeur TAG_USER contient une base commune à laquelle on ajoute une valeur de notre choix sur 16 bits :

#define DISKOTAGBASE			(TAG_USER + 0x61843716)

#define MUIA_DisKo_Device				(DISKOTAGBASE + 0)
#define MUIA_DisKo_Unit					(DISKOTAGBASE + 1)
#define MUIA_DisKo_Track				(DISKOTAGBASE + 2)

#define MUIM_DisKo_Play					(DISKOTAGBASE + 20)
#define MUIM_DisKo_Stop					(DISKOTAGBASE + 21)
#define MUIM_DisKo_Previous			(DISKOTAGBASE + 22)
#define MUIM_DisKo_Next					(DISKOTAGBASE + 23)

Continuons de reprendre chaque élément cité plus haut, en commençant par le contenu de la classe. Nous avons décidé que notre groupe dérivé contiendrait une liste, un composant busy et un champ texte. Il faut que notre nouvel objet conserve un pointeur sur chacun de ces composants et contienne également les variables de ses propriétés internes, le tout réuni dans une unique structure :

struct DisKoData {
	Object *lv_titles;
	Object *lst_titles;
	Object *busy;
	Object *txt_info;

	char device[64];
	int unit;

	int track;
};

Dès qu'un objet du type de notre nouvelle classe sera créé (ce qu'on appelle une instanciation), il y aura donc une allocation automatique de cette structure. Ceci est rendu possible car pour que notre classe soit utilisable, il faut la créer dynamiquement avec la fonction MUI_CreateCustomClass. Attention : il est important de distinguer une classe et un objet de cette classe !

cl_disko = MUI_CreateCustomClass(NULL, MUIC_Group, NULL, sizeof(struct DisKoData), &DisKoDispatcher);

C'est ça qui permet aussi au programme d'envoyer les messages à notre objet, vu qu'on lui associe ici le dispatcher. Cette fonction MUI_CtreateCustomClass() doit être appelée avant que l'on ne construise l'interface. On peut donc effectuer cette opération dans la fonction d'initialisation du programme. Ce qui permet d'ajouter un composant de notre classe dans la fenêtre principale de l'interface, qui devient :

app = (Object *)ApplicationObject,
	MUIA_Application_Author, "corto@guru-meditation.net",
	MUIA_Application_Base, "DISKO",
	MUIA_Application_Title, "DisKo - Exemple 7",
	MUIA_Application_Version, "$VER: DisKo 1.07 (14/10/04)",
	MUIA_Application_Copyright, "Mathias PARNAUDEAU",
	MUIA_Application_Description, "Lecteur de CD audio minimaliste",
	MUIA_Application_HelpFile, NULL,
	MUIA_Application_UsedClasses, ClassList,

	SubWindow, window = WindowObject,
		MUIA_Window_Title, "DisKo - release 7",
		MUIA_Window_ID, MAKE_ID('W', 'I', 'N', '1'),
		WindowContents, VGroup,

			Child, RegisterGroup(Pages),
				MUIA_Register_Frame, TRUE,

				// Onglet utilisation
				Child, VGroup,
					Child, HGroup,
						Child, obj_disko = NewObject(cl_disko->mcc_Class, NULL, /
						MUIA_DisKo_Unit, config.unit, MUIA_DisKo_Device, config.device, TAG_DONE),

						Child, VGroup,
							Child, Label("Volume"),
							Child, sl_volume = SliderObject,
								MUIA_Group_Horiz, FALSE,
								MUIA_Numeric_Min, 0,
								MUIA_Numeric_Max, 100,
								MUIA_Numeric_Value, 38,
								MUIA_Numeric_Reverse, TRUE,
							End,
						End,
					End,

					Child, HGroup,
						Child, bt_previous = KeyButton("Précédent", 'p'),
						Child, bt_next = KeyButton("Suivant", 'v'),
						Child, bt_play = KeyButton("Jouer", 'j'),
						Child, bt_pause = KeyButton("Pause", 'a'),
						Child, bt_stop = KeyButton("Stopper", 's'),
						Child, bt_eject = KeyButton("Ejecter", 'e'),
					End,

				End,

				// Onglet configuration
				Child, VGroup, GroupFrameT("Lecteur CD"),
					Child, ColGroup(2),
						Child, Label2("Device"),
						Child, str_device = String(config.device, 32),
						Child, Label1("Unit" ),
						Child, sl_unit = SliderObject,
							MUIA_Group_Horiz, TRUE,
							MUIA_Numeric_Min, 0,
							MUIA_Numeric_Max, 7,
							MUIA_Numeric_Value, config.unit,
						End,
					End,
					Child, KeyButton("Valider", 'd'),
				End,
			End,
		End,
	End,
End;

Nous avons vu précédemment que pour la structure contenant les données internes (membres privés, en terme de programmation objet), il n'y a pas à gérer l'allocation. En revanche, c'est au programmeur de la remplir en affectant les pointeurs de ses composants du groupe. Ceci a lieu dans le constructeur de la classe, appelé par l'application en envoyant un message OM_NEW au dispatcher, qui appelle ici la fonction DisKoNew() :

static ULONG DisKoNew(struct IClass *cl, Object *obj, struct opSet *msg)
{
	Object *lv_titles, *lst_titles, *busy, *txt_info;
	int i, tracks;

	obj = DoSuperNew(cl, obj,
					MUIA_Frame, MUIV_Frame_Group,
					Child, lv_titles = ListviewObject,
						MUIA_Listview_List, lst_titles = ListObject,
							MUIA_Frame, MUIV_Frame_InputList,
						End,
					End,
					Child, HGroup,
						Child, txt_info = TextObject,
							MUIA_Frame, MUIV_Frame_Text,
							MUIA_Text_Contents, "Information piste",
						End,
						Child, busy = BusyObject,
							MUIA_Busy_Speed, MUIV_Busy_Speed_Off,
						End,
					End,
					TAG_MORE, msg->ops_AttrList);

	/* Si l'objet a bien été créé, on poursuit son initialisation */

	if (obj){
		struct DisKoData *data = (struct DisKoData *)INST_DATA(cl, obj);
		struct TagItem *tags,*tag;

		/* On sauvegarde dans la structure les références sur les objets du groupe,
         sinon elles seront perdues à la fermeture de la fonction constructeur */

		data->lv_titles = lv_titles;
		data->lst_titles = lst_titles;
		data->busy = busy;
		data->txt_info = txt_info;

		strcpy(data->device, "ide.device");
		data->unit = 2;

		data->track = 1;

		/* parse initial taglist */

		for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);){
			switch (tag->ti_Tag){
				case MUIA_DisKo_Unit:
					set(obj, MUIA_DisKo_Unit, tag->ti_Data);
					break;
				case MUIA_DisKo_Device:
					set(obj, MUIA_DisKo_Device, tag->ti_Data);
					break;
			}
		}

		tracks = CDM_GetNumTracks();
		for (i=1 ; i<=tracks ; i++){
			sprintf(titles[i], "Piste %d", i);
			DoMethod(data->lst_titles, MUIM_List_InsertSingle, titles[i], MUIV_List_Insert_Bottom);
		}

		set(data->lst_titles, MUIA_List_Active, MUIV_List_Active_Top);

		DoMethod(data->lst_titles, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
			obj, 1, MUIM_DisKo_Play);

	}

	return (ULONG)obj;
}

Lors de la création d'un objet DisKo, c'est lui qui est appelé en premier. Il effectue toutes les initialisations nécessaires. Déjà, l'objet est créé en appelant le constructeur du père (de classe Group) avec la méthode DoSuperNew() puis on décrit l'organisation des composants qui font l'identité de notre groupe dérivé.

Si l'objet est bien constitué, on récupère le pointeur sur ces données internes (la fameuse structure DisKoData) grâce à la macro INST_DATA. On initialise alors chaque champ, y compris les références sur les objets MUI utilisés car dès que la fonction constructeur va se refermer, les variables locales sont détruites, il faut bien qu'on garde une trace de ces objets. Quand on décrit une interface, on associe des attributs (MUIA_) à affecter aux objets à leur création. C'est pourquoi dans le constructeur il est nécessaire de parcourir les attributs transmis, d'où la boucle sur les tags. Enfin, on peut poursuivre par quelques initialisations...

A vous de découvrir le fonctionnement des autres méthodes, le source regroupe dans une même partie tout ce qui concerne la classe. Elle apparaît alors relativement indépendante. Il sera même possible de la faire migrer dans un fichier séparé pour encore plus de clarté. Depuis l'application, tout est devenu plus simple : on crée un seul objet DisKo et on s'adresse simplement à lui à l'aide de méthodes compréhensible (et c'est important !). Il n'y a plus de gymnastique à effectuer pour manipuler la liste depuis l'application principale qui n'en a plus connaissance ! Et ça permet en plus de la supprimer des variables globales. De la même façon, on constate que le nombre de DoMethod() qui suit le code de l'interface a fortement diminué. En on évite les hooks qui ne sont pas recommandés dans MUI (quand on a le choix), on a juste laissé celui sur le volume à titre d'exemple.

Voici le code source complet de DisKo :

/*
 * DisKo7.c (11/10/04) - Dérivation de classe
 * 
 * VBCC OS4 :
 * vc -ISDK:Local/common/include/ -ICoding:MUI/DisKoTutorial/cdmanager/ -D__USE_INLINE__ 
 *  -D__USE_BASETYPE__ -DDoSuperMethodA=IDoSuperMethodA -c DisKo7.c 
 *
 * On voit avec le type de UtilityBase la complexité qui apparaît, due à des variantes entre les différents SDK.
 */

#include <stdio.h>
#include <string.h>

#include <clib/alib_protos.h>
#include <libraries/mui.h>
#include <proto/intuition.h>
#include <proto/muimaster.h>
#include <proto/exec.h>
#include <proto/utility.h>

#include <mui/Busy_mcc.h>

#include <SDI_hook.h>
#include <SDI_stdarg.h>

#include "cdmanager.h"

#define MAKE_ID(a,b,c,d)  ((ULONG) (a)<<24 | (ULONG) (b)<<16 | (ULONG) (c)<<8 | (ULONG) (d))

#if defined(__VBCC__)
#define UTILITYBASE_TYPE 	struct UtilityBase
#elif defined(__MORPHOS__) || defined(__SASC)
#define UTILITYBASE_TYPE	struct Library
#else
#define UTILITYBASE_TYPE 	struct UtilityBase
#endif

struct IntuitionBase	*IntuitionBase	= NULL;
struct Library			*MUIMasterBase	= NULL;
UTILITYBASE_TYPE		*UtilityBase = NULL;

#ifdef __amigaos4__
struct IntuitionIFace *IIntuition;
struct MUIMasterIFace *IMUIMaster;
struct UtilityIFace *IUtility;
#endif

#define	MUIA_Application_UsedClasses	0x8042e9a7	/* V20 STRPTR *	i..	*/

static char *ClassList[] =
{
	"Busy.mcc",
	NULL
};

static Object *app = NULL;
static Object *window = NULL;

static char *Pages[] = { "Utilisation", "Configuration", NULL };

struct Config {
	ULONG unit;
	char device[32];
} config;

static char titles[24][64];

struct MUI_CustomClass *cl_disko;

/*
 * Attention, cas spécial. DoSuperNew n'est défini que sous MorphOS
 * Il existe deux moyens : celui utilisant les SDI_headers ne fonctionnant pas avec vbcc 68k,
 * on laisse les deux pour exemple.
 */

#ifndef __MORPHOS__
#ifdef __amigaos4__
Object * STDARGS VARARGS68K DoSuperNew(struct IClass *cl, APTR obj, ...)
{
	Object *rc;
	VA_LIST args;

	VA_START(args, obj);
	rc = (Object *)DoSuperMethod(cl, obj, OM_NEW, VA_ARG(args, ULONG), NULL);
	VA_END(args);

	return rc;
}
#else
APTR __stdargs DoSuperNew(struct IClass *cl, APTR obj, ULONG tag1, ...)
{
	return ((APTR)DoSuperMethod(cl, obj, OM_NEW, &tag1, NULL));
}
#endif
#endif


/*************************************************************************************/

struct DisKoData {
	Object *lv_titles;
	Object *lst_titles;
	Object *busy;
	Object *txt_info;

	char device[64];
	int unit;

	int track;
};

#define DISKOTAGBASE			(TAG_USER + 0x61843716)

#define MUIA_DisKo_Device				(DISKOTAGBASE + 0)
#define MUIA_DisKo_Unit					(DISKOTAGBASE + 1)
#define MUIA_DisKo_Track				(DISKOTAGBASE + 2)

#define MUIM_DisKo_Play					(DISKOTAGBASE + 20)
#define MUIM_DisKo_Stop					(DISKOTAGBASE + 21)
#define MUIM_DisKo_Previous			(DISKOTAGBASE + 22)
#define MUIM_DisKo_Next					(DISKOTAGBASE + 23)


static ULONG DisKoNew(struct IClass *cl, Object *obj, struct opSet *msg)
{
	Object *lv_titles, *lst_titles, *busy, *txt_info;
	int i, tracks;

	obj = DoSuperNew(cl, obj,
					MUIA_Frame, MUIV_Frame_Group,
					Child, lv_titles = ListviewObject,
						MUIA_Listview_List, lst_titles = ListObject,
							MUIA_Frame, MUIV_Frame_InputList,
						End,
					End,
					Child, HGroup,
						Child, txt_info = TextObject,
							MUIA_Frame, MUIV_Frame_Text,
							MUIA_Text_Contents, "Information piste",
						End,
						Child, busy = BusyObject,
							MUIA_Busy_Speed, MUIV_Busy_Speed_Off,
						End,
					End,
					TAG_MORE, msg->ops_AttrList);

	/* Si l'objet a bien été créé, on poursuit son initialisation */

	if (obj){
		struct DisKoData *data = (struct DisKoData *)INST_DATA(cl, obj);
		struct TagItem *tags,*tag;

		/* On sauvegarde dans la structure les références sur les objets du groupe,
         sinon elles seront perdues à la fermeture de la fonction constructeur */

		data->lv_titles = lv_titles;
		data->lst_titles = lst_titles;
		data->busy = busy;
		data->txt_info = txt_info;

		strcpy(data->device, "ide.device");
		data->unit = 2;

		data->track = 1;

		/* parse initial taglist */

		for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);){
			switch (tag->ti_Tag){
				case MUIA_DisKo_Unit:
					set(obj, MUIA_DisKo_Unit, tag->ti_Data);
					break;
				case MUIA_DisKo_Device:
					set(obj, MUIA_DisKo_Device, tag->ti_Data);
					break;
			}
		}

		tracks = CDM_GetNumTracks();
		for (i=1 ; i<=tracks ; i++){
			sprintf(titles[i], "Piste %d", i);
			DoMethod(data->lst_titles, MUIM_List_InsertSingle, titles[i], MUIV_List_Insert_Bottom);
		}


		set(data->lst_titles, MUIA_List_Active, MUIV_List_Active_Top);

		DoMethod(data->lst_titles, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime,
			obj, 1, MUIM_DisKo_Play);
	}

	return (ULONG)obj;
}


/*
 * Cette méthode "dispose" n'a aucun intérêt ici, à part montrer sa construction.
 * Vu qu'on n'a aucune donnée allouée dynamiquement dans notre structure, l'appel à la méthode dispose
 * de la classe mère dans le dispatcher aurait suffi
 */
static ULONG DisKoDispose(struct IClass *cl, Object *obj, struct opSet *msg)
{
	struct DisKoData *data;

	data = (struct DisKoData *)INST_DATA(cl, obj);

	/* Ici, code de libération des ressources dynamiques des données internes */



	return (DoSuperMethodA(cl, obj, msg));
}


static ULONG DisKoSet(struct IClass *cl, Object *obj, struct opSet *msg)
{
	struct DisKoData *data;
	struct TagItem *tags, *tag;

	data = (struct DisKoData *)INST_DATA(cl, obj);

	for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);){
		switch (tag->ti_Tag){

			case MUIA_DisKo_Unit:
				data->unit = tag->ti_Data;
				break;
			case MUIA_DisKo_Device:
				if (tag->ti_Data){
					strcpy(data->device, (char *)tag->ti_Data);
				}
				break;
		}
	}

	return (DoSuperMethodA(cl, obj, msg));
}

static ULONG DisKoPlay(struct DisKoData *data)
{
	int val;

	get(data->lst_titles, MUIA_List_Active, &val);
	val++;

	CDM_PlayTrack(val);

	set(data->busy, MUIA_Busy_Speed, 20);

	return TRUE;
}

static ULONG DisKoStop(struct DisKoData *data)
{
	CDM_StopTrack();

	set(data->busy, MUIA_Busy_Speed, MUIV_Busy_Speed_Off);

	return TRUE;
}

static ULONG DisKoPrevious(struct DisKoData *data)
{
	set(data->lst_titles, MUIA_List_Active, MUIV_List_Active_Up);

	return TRUE;
}

static ULONG DisKoNext(struct DisKoData *data)
{
	set(data->lst_titles, MUIA_List_Active, MUIV_List_Active_Down);

	return TRUE;
}


DISPATCHERPROTO(DisKoDispatcher)
{
	struct DisKoData *data = (struct DisKoData *)INST_DATA(cl, obj);

	/* Un appel de méthode a été fait sur l'objet DisKo, on identifie laquelle c'est */

	switch(msg->MethodID){
		case OM_NEW :						return DisKoNew(cl, obj, (APTR)msg); break;
		case OM_DISPOSE :					return DisKoDispose(cl, obj, (APTR)msg); break;
		case OM_SET:						return DisKoSet(cl, obj, (APTR)msg); break;

		case MUIM_DisKo_Play :			return DisKoPlay(data); break;
		case MUIM_DisKo_Stop :			return DisKoStop(data); break;
		case MUIM_DisKo_Previous :		return DisKoPrevious(data); break;
		case MUIM_DisKo_Next :			return DisKoNext(data); break;

	}

	/* Si la méthode n'appartient à aucune que l'on a défini, on remonte l'appel à l'objet père */
	return DoSuperMethodA(cl, obj, msg);
}



/*************************************************************************************/

/* On conserve le hook sur le volume pour montrer comment ça fonctionne */


HOOKPROTONH(ChangeVolume, ULONG, APTR obj, struct TagItem *tag_list)
{
	int val;

	get(obj, MUIA_Numeric_Value, &val);
	printf("Nouvelle valeur : %d\n", val);

	return TRUE;
}
MakeStaticHook(hook_ChangeVolume, ChangeVolume);


/*
 * Construction et ouverture de la fenêtre principale.
 * Cette fonction est appelée quand on sait que tout a bien été initialisé.
 */
Object * OpenMainWindow(void)
{
	Object *bt_play, *bt_stop, *bt_previous, *bt_next, *bt_pause, *bt_eject;
	Object *sl_volume;

	Object *sl_unit;
	Object *str_device;

	Object *obj_disko;

	/* Description de l'interface et de ses propriétés */	

	app = (Object *)ApplicationObject,
		MUIA_Application_Author, "corto@guru-meditation.net",
		MUIA_Application_Base, "DISKO",
		MUIA_Application_Title, "DisKo - Exemple 7",
		MUIA_Application_Version, "$VER: DisKo 1.07 (14/10/04)",
		MUIA_Application_Copyright, "Mathias PARNAUDEAU",
		MUIA_Application_Description, "Lecteur de CD audio minimaliste",
		MUIA_Application_HelpFile, NULL,
		MUIA_Application_UsedClasses, ClassList,

		SubWindow, window = WindowObject,
			MUIA_Window_Title, "DisKo - release 7",
			MUIA_Window_ID, MAKE_ID('W', 'I', 'N', '1'),
			WindowContents, VGroup,

				Child, RegisterGroup(Pages),
					MUIA_Register_Frame, TRUE,

					// Onglet utilisation
					Child, VGroup,
						Child, HGroup,
							Child, obj_disko = NewObject(cl_disko->mcc_Class, NULL, /
							MUIA_DisKo_Unit, config.unit, MUIA_DisKo_Device, config.device, TAG_DONE),

							Child, VGroup,
								Child, Label("Volume"),
								Child, sl_volume = SliderObject,
									MUIA_Group_Horiz, FALSE,
									MUIA_Numeric_Min, 0,
									MUIA_Numeric_Max, 100,
									MUIA_Numeric_Value, 38,
									MUIA_Numeric_Reverse, TRUE,
								End,
							End,
						End,

						Child, HGroup,
							Child, bt_previous = KeyButton("Précédent", 'p'),
							Child, bt_next = KeyButton("Suivant", 'v'),
							Child, bt_play = KeyButton("Jouer", 'j'),
							Child, bt_pause = KeyButton("Pause", 'a'),
							Child, bt_stop = KeyButton("Stopper", 's'),
							Child, bt_eject = KeyButton("Ejecter", 'e'),
						End,

					End,

					// Onglet configuration
					Child, VGroup, GroupFrameT("Lecteur CD"),
						Child, ColGroup(2),
							Child, Label2("Device"),
							Child, str_device = String(config.device, 32),
							Child, Label1("Unit" ),
							Child, sl_unit = SliderObject,
								MUIA_Group_Horiz, TRUE,
								MUIA_Numeric_Min, 0,
								MUIA_Numeric_Max, 7,
								MUIA_Numeric_Value, config.unit,
							End,
						End,
						Child, KeyButton("Valider", 'd'),
					End,
				End,
			End,
		End,
	End;

	if (app){
		/* On fixe quelques valeurs et notifications */
	
		DoMethod(window,
			MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
			app, 2,
			MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

		DoMethod(sl_volume, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime,
					sl_volume, 2, MUIM_CallHook, &hook_ChangeVolume);
/*
		DoMethod(sl_volume, MUIM_Notify, MUIA_Pressed, FALSE,
					sl_volume, 2, MUIM_CallHook, &hook_ChangeVolume);
*/
		DoMethod(bt_play, MUIM_Notify, MUIA_Pressed, FALSE,
					obj_disko, 1, MUIM_DisKo_Play);

		DoMethod(bt_stop, MUIM_Notify, MUIA_Pressed, FALSE,
					obj_disko, 1, MUIM_DisKo_Stop);

		DoMethod(bt_previous, MUIM_Notify, MUIA_Pressed, FALSE,
					obj_disko, 1, MUIM_DisKo_Previous);

		DoMethod(bt_next, MUIM_Notify, MUIA_Pressed, FALSE,
					obj_disko, 2, MUIM_DisKo_Next);

		SetAttrs(window, MUIA_Window_Open, TRUE, TAG_END);
	}

	return app;
}

/*
 * Initialisation et vérification de tout ce qui est nécessaire à la bonne exécution
 * de l'application : ouverture des bibliothèques, test de présence des classes MCC, ...
 */
int Initialize(void)
{
	int res = 1;
	Object *busy = NULL;
	int cdm;

	// Initialisation de la configuration (chargement éventuel à partir d'un fichier)
#if defined(__amigaos4__)
	config.unit = 1;
	strcpy(config.device, "a1ide.device");
#elif defined(__MORPHOS__)
	config.unit = 2;
	strcpy(config.device, "ide.device");
#else
	// Pour UAE, ça fonctionne pour moi avec : uaescsi.device, unit 0
	config.unit = 2;
	strcpy(config.device, "ide.device");
#endif

	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39L);
	MUIMasterBase = OpenLibrary(MUIMASTER_NAME, MUIMASTER_VMIN);
	UtilityBase = (UTILITYBASE_TYPE *)OpenLibrary("utility.library", 39);
	cdm = CDM_Initialize(config.device, config.unit);

	if (IntuitionBase == NULL){
		printf("Impossible d'ouvrir 'intuition.library' V39\n");
		res = 0;
	}
	if (MUIMasterBase == NULL){
		printf("Impossible d'ouvrir '%s' V%d\n", MUIMASTER_NAME, MUIMASTER_VMIN);
		res = 0;
	}
	if (UtilityBase == NULL){
		printf("Impossible d'ouvrir 'utility.library' V39\n");
		res = 0;
	}

#ifdef __amigaos4__
	IIntuition = (struct IntuitionIFace *)GetInterface((struct Library *)IntuitionBase, "main", 1, NULL);
	if (!IIntuition){
		printf("Impossible d'obtenir l'interface IIntuition\n");
		res = 0;
	}
	IMUIMaster = (struct MUIMasterIFace *)GetInterface(MUIMasterBase, "main", 1, NULL);
	if (!IMUIMaster){
		printf("Impossible d'obtenir l'interface IMUIMaster\n");
		res = 0;
	}
	IUtility = (struct UtilityIFace *)GetInterface(UtilityBase, "main", 1, NULL);
	if (!IUtility){
		printf("Impossible d'obtenir l'interface IUtility\n");
		res = 0;
	}
#endif

	if (cdm){
		printf("Impossible d'initialiser le module CDDA, erreur %d\n", cdm);
		printf("Veuillez modifier le device et l'unité dans la fonction Initialize() de DisKo7.c\n");
		printf("Et vérifiez qu'un CD audio est bien présent dans le lecteur ! ;-)\n");
		res = 0;
	}

	busy = BusyObject, End;
	if (busy == NULL){
		printf("Classe Busy manquante\n");
		res = 0;
	}
	MUI_DisposeObject(busy);

	cl_disko = MUI_CreateCustomClass(NULL, MUIC_Group, NULL, sizeof(struct DisKoData),  ENTRY(DisKoDispatcher));
	if (cl_disko == NULL){
		printf("Impossible de créer la classe interne DisKo\n");
		res = 0;
	}

	return res;
}


/*
 * Fermeture et libération de tout ce qui a été initialisé au démarrage.
 */
void DeInitialize(void)
{
	if (cl_disko){
		MUI_DeleteCustomClass(cl_disko);
	}
	CloseLibrary((struct Library *)UtilityBase);
	CloseLibrary(MUIMasterBase);
	CloseLibrary((struct Library *)IntuitionBase);
	CDM_DeInitialize();

#ifdef __amigaos4__
	if (IMUIMaster) {
		DropInterface((struct Interface *)IMUIMaster);
	}
	if (IIntuition) {
		DropInterface((struct Interface *)IIntuition);
	}
	if (IUtility) {
		DropInterface((struct Interface *)IUtility);
	}
#endif

}


/*
 * Programme principal : il appelle les initialisations, ouvre la fenêtre puis gère
 * les événements jusqu'à ce qu'on ferme l'application, condition de libération
 * des ressources
 */
int main(int argc, char **argv)
{
	int res = 0;

	if (Initialize()){
		app = OpenMainWindow();
		if (app){
			/* Boucle de gestion des événements, toujours la même */
			ULONG sigs = 0;

			while (DoMethod(app,MUIM_Application_NewInput,&sigs) != MUIV_Application_ReturnID_Quit)
			{
				if (sigs)
				{
					sigs = Wait(sigs | SIGBREAKF_CTRL_C);
					if (sigs & SIGBREAKF_CTRL_C) break;
				}
			}

			/* Libération des ressources et fermeture */
			set(window, MUIA_Window_Open, FALSE);
			MUI_DisposeObject(app);
		}else{
			res = 2;
		}
	}else{
		res = 1;
	}

	DeInitialize();

	return res;
}

Notions acquises dans ce chapitre
  • La dérivation d'une classe MUI : constructeur, destructeur, dispatcher, données internes...
  • Nouveaux parallèles entre la programmation MUI et orientée objet.


[Retour en haut] / [Retour aux articles] [Article précédent]