Rezilla logo


Ce document est un tutoriel consacrŽ ˆ l'architecture de plugins de l'Žditeur de ressources Rezilla pour Mac OS X. Il explique, au moyen d'exemples, comment Žcrire de nouveaux plugins afin d'ajouter des Žditeurs externes ˆ Rezilla. Le prŽsent document correspond ˆ la version 1.1 de Rezilla.


ƒcrire un Plugin pour Rezilla

Cette section est destinŽe aux dŽveloppeurs souhaitant Žcrire un plugin pour Rezilla.

Une architecture complte de plugins a ŽtŽ mise en place dans Rezilla ˆ partir de la version 1.1. Elle s'appuie sur l'API CFPlugin dŽfinie dans le framework CoreFoundation. Cette interface de programmation est basŽe sur le modle COM (Component Object Model) de Microsoft. Pour en savoir plus sur le modle CFPlugin et l'architecture COM, on pourra se rŽfŽrer ˆ la documentation d'Apple, en particulier: http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFPlugIns/index.html

Le pŽesent document explique comment Žcrire un plugin pour Rezilla, conformŽment ˆ ce modle. Il s'adresse aux dŽveloppeurs souhaitant apporter de nouvelles fonctionalitŽs ˆ Rezilla et comporte les instructions gŽnŽrales ainsi que l'information technique nŽcessaires.

Le modle des plugins

Un plugin implŽmente une interface que l'on peut voir comme l'incarnation d'un service. Selon la terminologie en vigueur, on emploie le mot type pour dŽsigner un service: le type d'un plugin d'Ždition, dans le cas de Rezilla, est le fait d'Žditer les donnŽes d'une ressource particulire (le mot type ici peut prter ˆ confusion et ne doit pas tre confondu avec le type d'une ressource !). Dans la version 1.1, Rezilla ne conna”t que ce type de plugins: le service d'Ždition. Dans l'avenir, Rezilla pourrait aussi introduire des plugins permettant de dŽfinir des fentres de sŽlection de ressources (resource pickers), par exemple, et cela conduirait ˆ introduire un nouveau type puisqu'il s'agit d'un service diffŽrent.

Il n'y a pas forcŽment une interface unique pour un type donnŽ: il peut y en avoir plusieurs. Par exemple, de futures versions de Rezilla, pourraient modifier l'interface actuelle: cela se ferait par le biais d'une nouvelle interface. Dans ce cas, un plugin se devrait d'implŽmenter ˆ la fois l'ancienne et la nouvelle interface et Rezilla pourrait dŽterminer, en fonction du contexte, quelle interface il faut utiliser. La version 1.1 de Rezilla ne dŽfinit qu'une interface pour le service d'Ždition. Une interface peut tre vue comme une liste de fonctions correspondant aux diverses t‰ches ˆ exŽcuter au cours de l'Ždition d'une ressource.

Anatomie du plugin

Techniquement un plugin est simplement une bibliothque dynamique (dynamic library) qui est chargŽe par l'application principale ds que nŽcessaire. Sous OS X, cette bibliothque se prŽsente sous forme de ce que l'on appelle un paquetage (bundle), c'est-ˆ-dire une structure analogue ˆ un rŽpertoire qui contient la bibliothque elle-mme et quelques fichiers complŽmentaires.

Pour en savoir plus sur les paquetages sous OS X, voir: http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFBundles/Concepts/about.html

L'utilisation de cette architecture facilite considŽrablement, du point de vue de l'utilisateur, les t‰ches de maintenance relatives aux plugins, comme l'ajout, la suppression, l'activation ou la dŽsactivation. Toutes ces actions peuvent tre accomplies depuis le Finder de faon transparente (voir la section ƒditeurs Externes dans l'aide de Rezilla). L'utilisateur n'a pas ˆ conna”tre la localisation exacte des plugins ˆ l'intŽrieur de l'application.

Voici, ˆ titre d'exemple, la structure interne du plugin RezImagePlugin, fourni avec la version 1.1 de Rezilla:

Plugin anatomy

Ce plugin s'appelle RezImagePlugin.plugin. Il doit avoir une extension .plugin afin que Rezilla puisse le trouver. Certains ŽlŽments du sous-dossier Contents sont obligatoires, les autres sont facultatifs. Les ŽlŽments obligatoires sont:

Le sous-dossier Resources peut contenir des donnŽes supplŽmentaires. Dans l'exemple prŽcŽdent, on y trouve:

Les UUIDs de plugin

Divers ŽlŽments du modle de plugins dŽfini (l'API CFPlugin dans les Core Foundations de OS X) sont identifiŽs au moyen d'un UUID (Universally Unique Identifier, identificateur universellement unique). Certains UUIDs sont dŽfinis par l'application principale, d'autres le sont par le plugin.

Un UUID est une valeur sur 128-bits dont l'unicitŽ est garantie. Il peut tre reprŽsentŽ sous deux formes diffŽrentes: sous forme d'octets ou sous forme de cha”ne de caractres. Sous forme d'octets, il s'agit d'une structure C dŽfinie dans les Core Foundations (CFUUIDBytes dans le fichier d'en-tte CFUUID.h). L'Žcriture sous forme de cha”ne est une convention pour faciliter la lecture: il s'agit d'une cha”ne ASCII sŽparŽe par des tirets qui est utilisŽe, par exemple, dans le fichier de liste de propriŽtŽs du plugin (voir la section Liste de propriŽtŽs du plugin ci-dessous).

Les UUIDs de Rezilla

Rezilla dŽfinit deux UUIDs pour identifier respectivement le type du plugin et la fonction dite de fabrication (factory function) qui implŽmente l'interface. Ces valeurs sont dŽfinies dans le fichier d'en-tte RezillaPluginInterface.h au moyen de valeurs symboliques: Les UUIDs sont dŽfinis comme ceci:
	#define kRezillaPluginEditorTypeID (CFUUIDGetConstantUUIDWithBytes(NULL,0x30,0x6A,0x0E,0xF3,0x20,0x6E,0x11,0xDA,0x83,0x20,0x00,0x0A,0x95,0xB1,0xFF,0x7C))
	#define kRezillaPluginEditorInterfaceVs1 (CFUUIDGetConstantUUIDWithBytes(NULL,0x30,0x6A,0xE1,0x67,0x20,0x6E,0x11,0xDA,0x83,0x20,0x00,0x0A,0x95,0xB1,0xFF,0x7C))
ce qui s'Žcrit, sous forme de cha”ne:
	306A0EF3-206E-11DA-8320-000A95B1FF7C
	306AE167-206E-11DA-8320-000A95B1FF7C

Les UUIDs des fabriques

Le plugin doit fournir des UUIDs pour toutes les implŽmentations d'interfaces qu'il contient. L'implŽmentation est rŽalisŽe par une fonction de fabrication ou fabrique (factory function). Ces fabriques sont dŽclarŽes dans la liste de propriŽtŽs du plugin et dŽfinies dans le plugin lui-mme. Le plugin RezillaImage, par exemple, supporte le type d'Ždition de ressources de Rezilla et l'interface associŽe, il lui faut donc dŽfinir un seul UUID de fabrique. Son code source en C contient l'instruction suivante:
	#define kRezillaImageFactoryID (CFUUIDGetConstantUUIDWithBytes(NULL,0x09,0x05,0xF8,0x36,0xA2,0x0C,0x11,0xDA,0xBC,0x6C,0x00,0x0A,0x95,0xB1,0xFF,0x7C))
et sa liste de propriŽtŽs dŽclare la mme valeur sous forme de cha”ne:
	0905F836-A20C-11DA-BC6C-000A95B1FF7C
Dans le cas o il existe plusieurs interfaces, chaque interface (et donc chaque fabrique) doit avoir un UUID.

CrŽer un UUID

Il existe un outil de ligne de commande simple appelŽ uuidgen, fourni avec les Developers Tools d'Apple, qui gŽnre des UUIDs. Voici un exemple de son utilisation (ˆ exŽcuter dans une fentre de Terminal):
	shell> uuidgen
	9A347042-427C-11DB-9237-000A95B1FF7C

Le kit de dŽveloppement de Rezilla (Rezilla SDK) fournit aussi un outil un peu plus ŽlaborŽ appelŽ mkuuid. Avec mkuuid, on peut spŽcifier le nombre d'UUIDs ˆ gŽnŽrer et le rŽsultat renvoie les UUIDs ˆ la fois sous forme d'octets (ˆ utiliser dans du code C) et sous forme de cha”ne (ˆ utiliser dans la liste de propriŽtŽs). Voici un exemple de son utilisation:

	shell> mkuuid 2
	UUID as bytes: (NULL,0xC1,0x37,0x86,0xB4,0x42,0x7C,0x11,0xDB,0xA0,0xC4,0x00,0x0A,0x95,0xB1,0xFF,0x7C)
	UUID as string: "C13786B4-427C-11DB-A0C4-000A95B1FF7C"
	
	UUID as bytes: (NULL,0xC1,0x38,0x2C,0x85,0x42,0x7C,0x11,0xDB,0xA0,0xC4,0x00,0x0A,0x95,0xB1,0xFF,0x7C)
	UUID as string: "C1382C85-427C-11DB-A0C4-000A95B1FF7C"

Liste de propriŽtŽs du plugin

Le fichier Info.plist stocke des donnŽes et des informations utiles relatives au plugin. Il utilise un format XML: certaines clŽs sont utilisŽes par le Finder et le Systme, d'autres sont utilisŽs par Rezilla lui-mme.

Les clŽs du Systme

Les clŽs dŽfinies par le Systme sont les balises usuelles que l'on trouve dans les applications construites sous forme de paquetage (application bundle). Dans le cas du plugin RezImage, par exemple, on trouve les paires clŽ/valeur suivantes:

ClŽValeur
CFBundleIdentifiernet.sourceforge.rezilla.RezImagePlugin
CFBundleNameRezImagePlugin
CFBundleShortVersionStringRezImagePlugin 0.1
CFBundlePackageTypeBNDL
CFBundleSignatureRzil
CFBundleExecutableRezImagePlugin
CFBundleVersion0.1
CFBundleIconFileRezillaPlugin.icns
CFBundleDevelopmentRegionEnglish
CFBundleInfoDictionaryVersion6.0
LSRequiresCarbonyes

Le sous-dossier Resources, ˆ l'intŽrieur du paquetage du plugin, peut contenir un fichier d'icone (fichier avec l'extension .icns). Si ce fichier d'icone est dŽclarŽ dans la liste Info.plist sous la clŽ CFBundleIconFile, l'image sera affichŽe par Rezilla dans le panneau Plugin Info appelŽ par la commande Plugins... du menu File.

Les clŽs de CFPlugin

Certaines sont requises par l'interface de programmation CFPlugin:

Les clŽs de Rezilla

Rezilla s'attend ˆ trouver deux clŽs supplŽmentaires dans le fichier Info.plist:

L'interface du plugin

Les membres de l'interface du plugin sont dŽfinis dans le fichier d'en-tte RezillaPluginInterface.h qui se trouve dans le kit de dŽveloppement de Rezilla (Rezilla SDK). Ce fichier doit tre inclus dans le code source du plugin. Il dŽfinit l'interface elle-mme ainsi que quelques autres structures et ŽnumŽrations qui rŽgissent la communication entre l'application principale et les plugins.

La structure de l'interface

L'interface est dŽfinie comme une structure composŽe de pointeurs de fonctions. C'est la liste de toutes les fonctions que le plugin doit dŽfinir pour implŽmenter l'interface. Ce sont les fonctions que Rezilla invoquera pour accomplir les diffŽrentes t‰ches relatives ˆ l'Ždition d'une ressource.

typedef struct SPluginEditorInterface {
    IUNKNOWN_C_GUTS;
    Boolean  (*AcceptResource)(void *myInstance, ResType inType, short inID, Handle inDataH, RezPlugInfo * outInfo);
    OSErr    (*EditResource)(RezPlugRef inPlugref, RezHostInfo inInfo);
    Handle   (*ReturnResource)(RezPlugRef inPlugref, Boolean * outRelease, OSErr * outError);
    OSErr    (*RevertResource)(RezPlugRef inPlugref, Handle inDataH);
    Boolean  (*IsModified)(RezPlugRef inPlugref);
    void     (*CleanUp)(RezPlugRef inPlugref);
    void     (*Refresh)(RezPlugRef inPlugref);
    OSErr    (*ResizeBy)(RezPlugRef inPlugref, SInt16 inWidthDelta, SInt16 inHeightDelta);
    void     (*HandleMenu)(RezPlugRef inPlugref, MenuRef menu, SInt16 inMenuItem);
    void     (*HandleClick)(RezPlugRef inPlugref, const EventRecord * inMacEvent, Point inPortCoords);
    void     (*HandleKeyDown)(RezPlugRef inPlugref, const EventRecord * inKeyEvent);
    Boolean  (*HandleCommand)(RezPlugRef inPlugref, SInt16 inCommand);
}

Cette structure donne les protopypes des fonctions. Le symbole IUNKNOWN_C_GUTS est une macro dŽfinie dans le fichier d'en-tte CFPlugInCOM.h et qui se dŽveloppe en trois prototypes de fonctions qui doivent impŽrativement figurer dans l'interface: QueryInterface, AddRef, et Release. Elles forment la base de l'architecture COM: n'importe quel plugin qui se conforme ˆ ce modle doit les dŽfinir. C'est lˆ que les vŽrifications prŽliminaires ont lieu: par exemple, l'application identifie le plugin et vŽrifie que les UUIDs sont bien ceux que l'on attend. Voici les prototypes des trois fonctions COM:

	HRESULT (*QueryInterface)(void *thisPointer, REFIID iid, LPVOID *ppv); \
	ULONG (*AddRef)(void *thisPointer); \
	ULONG (*Release)(void *thisPointer)

Toutes les autres fonctions sont requises par Rezilla qui compte sur le plugin pour les fournir. Ë l'exception de AcceptResource, le premier argument de toutes ces fonctions est un RezPlugRef: il s'agit d'une rŽfŽrence attribuŽe par le plugin ˆ toute session d'Ždition de ressource, lors de la transaction initiale. Plus prŽcisŽment, un RezPlugRef est un pointeur sur des donnŽes clientes correspondant ˆ la ressource ˆ Žditer: c'est cette rŽfŽrence qui identifie la ressource concernŽe ainsi que les donnŽes qui y sont attachŽes, car un plugin peut avoir plusieurs ressources ˆ gŽrer simultanŽment. Rezilla n'intervient pas dans ces donnŽes clientes et se contente de passer le pointeur RezPlugRef dans tous les appels ˆ une fonction du plugin. Le type d'un RezPlugRef est dŽfini dans le fichier d'en-tte RezillaPluginInterface.h comme ceci:

	typedef void *	RezPlugRef;

Lorsque le plugin est invoquŽ pour le premire fois, Rezilla le charge en utilisant les fonctions adŽquates de l'API CFPlugin. C'est ˆ ce moment que la fonction de fabrication de l'interface est appelŽe: le nom de cette fonction est trouvŽ, comme indiquŽ prŽcŽdemment dans la section Liste de propriŽtŽs du plugin, dans le fichier de liste de propriŽtŽs Info.plist.

Interaction avec le plugin

Lorsque Rezilla doit Žditer une ressource au moyen d'un plugin, une transaction prŽliminaire a lieu entre les deux. Cette transaction s'exŽcute en deux Žtapes qui font intervenir les fonctions AcceptResource et EditResource successivement:
  1. Rezilla invoque tout d'abord la fonction AcceptResource afin de demander au plugin s'il accepte d'Žditer la ressource: il lui transmet le type, le numŽro et l'adresse des donnŽes de la ressource afin qu'il puisse dŽterminer sa rŽponse. Si le plugin accepte la ressource, il remplit une structure de type RezPlugInfo que Rezilla lui a Žgalement passŽe en argument et dans laquelle il peut faire un certain nombre de requtes: il peut par exemple demander ˆ Rezilla d'insŽrer un ou plusieurs menus dans la barre de menus. C'est ˆ ce moment que le plugin peut attribuer une rŽfŽrence RezPlugRef, c'est-ˆ-dire un pointeur sur des donnŽes clientes, ˆ cette nouvelle session d'Ždition et la transmettre ˆ Rezilla qui, par la suite, la fera figurer dans toutes les autres fonctions de l'interface.

  2. aprs que Rezilla aura reu l'acceptation du plugin et les requtes de celui-ci, la fonction EditResource sera invoquŽe. Par le biais de cette fonction, le plugin reoit une structure de type RezHostInfo contenant des informations fournies par Rezilla: un pointeur WindowRef pour identifier la fentre d'Ždition fournie par l'application, des pointeurs MenuRef si jamais le plugin avait demandŽ l'insertion de menus, etc.

Ë ce stade, le plugin peut maintenant faire son travail d'Ždition de la ressource. Il peut par exemple crŽer des contr™les et des ŽlŽments graphiques, installer au besoin des CarbonEvents sur ces contr™les, etc. La fentre est construite en mode composite (compositing mode) ce qui permet d'utiliser pleinement le modle HIViews de dessin de l'interface graphique.

On notera dependant qu'il n'y a aucune obligation ˆ utiliser des CarbonEvents; toutes les actions et ŽvŽnements dŽclenchŽs par l'utilisateur sont passŽs ˆ Rezilla sous la forme d'un EventRecord dans les fonctions suivantes dŽfinies dans l'interface: HandleMenu, HandleClick, HandleKeyDown et HandleCommand.

Lorsque vient le moment de sauvegarder les modifications faites dans la ressource (par exemple lorsque l'utilisateur clique sur le bouton Save ou tente de fermer la fentre), Rezilla invoque la fonction ReturnResource afin que le plugin renvoie les donnŽes modifiŽes, puis la fonction CleanUp afin qu'il puisse terminer sa session d'Ždition:

Si l'utilisateur clique sur le bouton Revert (lorsqu'il y en a un!), Rezilla invoque la fonction RevertResource: les donnŽes auxquelles il faut revenir sont fournies par l'intermŽdiaire de l'argument inDataH, ce qui signifie en particulier que le plugin n'a pas ˆ se prŽoccuper de garder une copie des donnŽes originales.

Les requtes du plugin

Cette section donne plus de dŽtails concernant les requtes formulŽes par le plugin dans la fonction AcceptResource. La structure RezPlugInfo est dŽfinie comme ceci:

typedef struct RezPlugInfo {
    RezPlugRef    plugref;
    UInt32        attributes;
    Rect          winbounds;
    UInt8         menucount;
    MenuID *      menuIDs;
    OSErr         error;
}

Le membre le plus important de cette structure est attributes: il s'agit d'une valeur additive de type UInt32, somme de diffŽrents drapeaux qui dŽterminent diverses caractŽristiques de la fentre d'Ždition qui sera fournie par Rezilla ainsi que les commandes de base de Rezilla qui seront supportŽes par le plugin. Les valeurs de ces drapeaux sont dŽfinies dans une ŽnumŽration RezillaPluginFlags dŽclarŽe dans le fichier d'en-tte RezillaPluginInterface.h:

enum RezillaPluginFlags {
    kPluginNoAttributes             = 0L,
    
    kPluginEditorHasSaveButton      = (1L << 0),
    kPluginEditorHasCancelButton    = (1L << 1),
    kPluginEditorHasRevertButton    = (1L << 2),
    kPluginEditorHasLockIcon        = (1L << 3),
    kPluginEditorHasNameField       = (1L << 4),
    kPluginEditorStandardControls   = (kPluginEditorHasSaveButton 
                                   | kPluginEditorHasCancelButton 
                                   | kPluginEditorHasRevertButton 
                                   | kPluginEditorHasLockIcon),
    
    kPluginWinHasCollapseBox        = (1L << 5),
    kPluginWinIsResizable           = (1L << 6),
    
    kPluginSupportCut               = (1L << 10),
    kPluginSupportCopy              = (1L << 11),
    kPluginSupportPaste             = (1L << 12),
    kPluginSupportClear             = (1L << 13),
    kPluginSupportSelectAll         = (1L << 14),
    kPluginSupportFind              = (1L << 15),
    kPluginSupportFindNext          = (1L << 16),
    kPluginSupportImport            = (1L << 17),
    kPluginSupportExport            = (1L << 18),
    kPluginSupportEditCommands      = (kPluginSupportCut 
                                   | kPluginSupportCopy 
                                   | kPluginSupportPaste 
                                   | kPluginSupportClear)
}

Le membre winbounds est une structure Rect indiquant la position et dimension de la fentre d'Ždition: elle est exprimŽe en coordonnŽes globales et correspond ˆ la structure entire de la fentre, autrement les dimensions qui sont habituellement passŽes ˆ la fonction CreateWindow. Rezilla fournit toujours une fentre d'Ždition qui peut tre ŽquipŽe d'un certain nombre d'ŽlŽments de base tels que des boutons Save et Cancel. Le plugin n'a pas ˆ se prŽoccuper de surveiller ces contr™les: ils sont gŽrŽs par l'application principale. Le plugin doit simplement dŽcider, au moyen des drapeaux adŽquats, lesquels de ces contr™les doivent figurer dans la fentre. Par exemple, on ajoutera le drapeau kPluginEditorHasRevertButton aux attributs si l'on souhaite avoir un bouton Revert dans la fentre.

Le membre menucount indique combien de menus sont nŽcessaires au plugin (Žventuellement 0). Si le plugin dŽfinit des menus, il doit passer un tableau des MenuIDs correspondants dans le membre menuIDs de la structure. Ces MenuIDs sont les numŽros de ressources de type 'MENU' qui devront se trouver dans le fichier de ressources du plugin: ce fichier de ressources se trouve dans le sous-dossier Resources ˆ l'intŽrieur du paquetage (bundle) dans lequel se trouve le plugin, comme il a ŽtŽ expliquŽ ˆ la section Anatomie du plugin ci-dessus.

Dans le cas o le plugin n'accepte pas d'Žditer une resource, il doit renvoyer la valeur boolŽenne false comme valeur de retour de la fonction AcceptResource et il n'est alors pas nŽcessaire de remplir la structure de requtes. Si une erreur se produit, le plugin peut toutefois passer un code d'erreur dans le membre error de la structure RezPlugInfo.

Les commandes supportŽes

Les menus de base de Rezilla (File, Edit, Resources) comportent des commandes qui ne sont pas toujours activŽes: cela dŽpend du contexte. Par exemple, la commande Export n'a de sens que dans certaines circonstances ou avec certains types de ressources: de mme, un plugin peut dŽcider de supporter cette commande ou pas. Les commandes ainsi supportŽes par le plugin sont dŽclarŽes dans les attributs lors de la transaction prŽliminaire comme il a ŽtŽ expliquŽ ˆ la section prŽcŽdente: si la commande Export est supportŽe par le plugin, le drapeau kPluginSupportExport doit tre ajoutŽ aux attributs. Si ce drapeau n'est pas mis, l'article de menu correspondant sera dŽsactivŽ.

Par la suite, lorsque la commande Export du menu File est invoquŽe par l'utilisateur, Rezilla appelle la fonction HandleCommand avec un numŽro de commande correspondant afin que le plugin puisse rŽagir en consŽquence. Les numŽros de commandes sont dŽfinis dans l'ŽnumŽration RezillaPluginCmdIDs dŽclarŽe dans le fichier d'en-tte RezillaPluginInterface.h:

enum RezillaPluginCmdIDs {
    kPluginCommandCut        = 1,
    kPluginCommandCopy,
    kPluginCommandPaste,
    kPluginCommandClear,
    kPluginCommandSelectAll,
    kPluginCommandFind,
    kPluginCommandFindNext,
    kPluginCommandImport,
    kPluginCommandExport
}

Informations de l'h™te

La structure RezHostInfo est dŽfinie comme ceci:

typedef struct RezHostInfo {
    CFBundleRef  bundleref;
    short        refnum;
    WindowRef    winref;
    UInt8        menucount;
    MenuRef *    menurefs;
    Rect         editrect;
    Boolean      readonly;
}

C'est lˆ que Rezilla passe des informations ˆ destination du plugin.

Le membre bundleref de la structure est passŽ ˆ titre de facilitŽ: c'est une rŽfŽrence de type CFBundleRef au paquetage du plugin pour le cas o le plugin aurait besoin de trouver quelque chose ˆ l'intŽrieur de sa propre structure (des fichiers de localisation par exemple).

Le membre refnum est le numŽro de rŽfŽrence de la table de ressources en mŽmoire ˆ laquelle appartient la ressource en cours d'Ždition. Cela donne accs pour le plugin ˆ diverses fonctions du Gestionnaire de ressources (Resource Manager): le plugin pourrait avoir besoin de trouver d'autres ressources dans la mme table par exemple.

Le membre winref est le pointeur de type WindowRef de la fentre d'Ždition crŽŽe par Rezilla.

Le membre menucount indique combien de menus ont ŽtŽ crŽŽs par Rezilla. Ce devrait tre le mme nombre que celui demandŽ par le plugin dans la structure RezillaPluginFlags. Le membre menurefs est un tableau de pointeurs de type MenuRef correspondant aux diffŽrents menus.

Le membre editrect est une structure de type Rect indiquant les coordonnŽes de la zone de contenu de la fentre d'Ždition qui peut tre utilisŽe par le plugin pour installer ses contr™les et ŽlŽments graphiques sans dessiner par dessus des parties installŽes par Rezilla (comme par exemple l'en-tte de la fentre ou la zone infŽrieure contenant les boutons Save et Cancel). Ce Rect est exprimŽ dans le systme de coordonnŽes de la fentre.

Le membre readonly indique si la table ˆ laquelle la ressource appartient est en lecture seule. Si elle l'est, il n'y a pas lieu d'autoriser des modifications: dans ce cas le plugin se comporte comme un simple visualisateur de contenu.

Codes d'erreur

Rezilla dŽfinit des codes pour les principales erreurs susceptibles de se produire au cours d'une session d'Ždition par plugin. Ces codes commencent ˆ la valeur 5000:

enum RezillaPluginErrors {
    plugErr_Generic                = 5000,    
    plugErr_InitializationFailed,
    plugErr_UnsupportedType,
    plugErr_UnsupportedID,
    plugErr_InvalidData,
    plugErr_UnsupportedResourceFormat,
    plugErr_UnsupportedResourceVersion,
    plugErr_EditResourceFailed,
    plugErr_ReturnResourceFailed,
    plugErr_RevertResourceFailed,
    plugErr_CantResizeWindow,
    plugErr_CantHandleMenuCommand,
	plugErr_CantEditEmptyResource,
    plugErr_LastError
}

Le plugin Sample

Cette section commente le code du plugin Sample fourni avec le kit de dŽveloppement de Rezilla (Rezilla SDK) pour servir ˆ la fois d'exemple et de modle pour la crŽation d'un plugin pour Rezilla. Un fichier de projet XCode est aussi fourni. Le plugin Sample met en relief les divers aspects des t‰ches de programmation nŽcessaires pour la crŽation d'un plugin Rezilla. Le code source lui-mme contient par ailleurs des commentaires dŽtaillŽs.

Pour en apprendre plus sur les plugins Rezilla, on pourra aussi consulter le code source du plugin RezImage qui est un vŽritable Žditeur prŽsent dans la version 1.1 de Rezilla. Il permet d'Žditer des ressources d'images ('jpeg', 'tiff', 'gif ', etc.). Son code source fait partie de la distribution du code source de Rezilla.

Le plugin Sample a pour but d'Žditer des resources de type 'PStr' ou 'STR '. Ce sont des ressources trs simples qui contiennent uniquement une cha”ne de Pascal. Le plugin affiche cette cha”ne dans un champ d'Ždition afin de permettre ˆ l'utilisateur de la lire et de la modifier. Par ailleurs le plugin installe un menu avec deux commandes qui peuvent aussi agir sur la cha”ne (elles ne font rien de vraiment utile, il s'agit simplement d'un exemple).

Les fichiers source de Sample

Le dossier SamplePlugin du kit de dŽveloppement de Rezilla contient les fichiers suivants:

L'UUID de Sample

Le plugin doit dŽfinir un seul UUID correspondant ˆ l'unique fonction de fabrication (factory). Cet UUID peut tre crŽŽ avec la commande genuuid fournie avec les Developer Tools d'Apple ou avec l'utilitaire mkuuid fourni dans le kit de dŽveloppement de Rezilla.

La dŽfinition de l'UUID se trouve au dŽbut du fichier source RezSamplePlugin.c:

	#define kRezillaSampleFactoryID (CFUUIDGetConstantUUIDWithBytes(NULL,0x30,0x6B,0x89,0xA8,0x20,0x6E,0x11,0xDA,0x83,0x20,0x00,0x0A,0x95,0xB1,0xFF,0x7C))

La liste de propriŽtŽs de Sample

L'UUID mentionnŽ au paragraphe prŽcŽdent s'Žcrit sous forme de cha”ne comme ceci:

	306B89A8-206E-11DA-8320-000A95B1FF7C 
Il est utilisŽ dans le fichier Info.plist ˆ deux endroits: en tant que clŽ dans le dictionnaire CFPlugInFactories et en tant que valeur dans le dictionnaire CFPlugInTypes:
	<key>CFPlugInFactories</key>
	<dict>
		<key>306B89A8-206E-11DA-8320-000A95B1FF7C</key>
		<string>RezSampleFactory</string>
	</dict>
	<key>CFPlugInTypes</key>
	<dict>
		<key>306A0EF3-206E-11DA-8320-000A95B1FF7C</key>
		<array>
			<string>306B89A8-206E-11DA-8320-000A95B1FF7C</string>
		</array>
	</dict>
On notera que la clŽ (et non la valeur) dans le dictionnaire CFPlugInTypes est l'UUID identifiant le type de service assurŽ par le plugin (c'est la constante kRezillaPluginEditorTypeID dŽfinie par Rezilla). La valeur dans le dictionnaire CFPlugInFactories est le nom de la fabrique, fonction de fabrication ˆ invoquer pour instancier l'interface du plugin: cette fonction (RezSampleFactory) est dŽfinie dans le fichier source RezSamplePlugin.c.

Le fichier Info.plist dŽclare par ailleurs les types de ressources supportŽs par le plugin ('PStr' et 'STR '):

	<key>RezillaPluginEditTypes</key>
	<array>
		<string>PStr</string>
		<string>STR </string>
	</array>
et le r™le du plugin:
	<key>RezillaPluginRole</key>
	<string>Editor</string>

Les autres paires clŽ/valeur sont tout ˆ fait usuelles et se comprennent sans difficultŽ.

Le fichier de projet

Le fichier de projet du plugin Sample RezSamplePlugin.xcode a ŽtŽ crŽŽ avec la version 1.5 de XCode afin d'assurer une compatibilitŽ maximale avec d'anciennes versions du systme OS X (Jaguar et Panther). Il fonctionnera aussi avec des versions plus rŽcentes de XCode: sous systme 10.4 (Tiger), les versions 2.0 ou plus de XCode convertiront le fichier en un fichier appelŽ RezSamplePlugin.xcodeproj.

Le code source

Le code C dŽfinissant les fonctions du plugin se trouve dans le fichier RezSamplePlugin.c.

Les structures de Sample

Le plugin Sample dŽfinit deux structures afin de contr™ler ses sessions d'Ždition:

Une table de fonctions est aussi crŽŽe: c'est une instance statique d'une structure SPluginEditorInterface (autrement dit la structure dŽfinie par Rezilla pour dŽcrire l'interface) et contient le nom de toutes les fonctions de l'interface dŽfinies dans le plugin:

	static SPluginEditorInterface sSamplePlugFuncTable = {
			NULL,
			sample_QueryInterface,
			sample_AddRef,
			sample_Release,
			sample_AcceptResource,
			sample_EditResource,
			sample_ReturnResource,
			sample_RevertResource,
			sample_IsModified,
			sample_CleanUp,
			sample_Refresh,
			sample_ResizeBy,
			sample_HandleMenu,
			sample_HandleClick,
			sample_HandleKeyDown,
			sample_HandleCommand
	};

Deux variables statiques contiennent l'identificateur et la rŽfŽrence du menu associŽ au plugin:

	static MenuID    sampleMenuID;
	static MenuRef   sampleMenuRef;

La fabrique Sample

La fonction RezSampleFactory() est appelŽe par Rezilla (par le biais de l'API CFPlugin) lorsque le plugin est chargŽ pour la premire fois. Cette fonction est connue gr‰ce au fichier de liste de propriŽtŽ Info.plist. Aprs avoir vŽrifiŽ que le type du plugin est le bon (kRezillaPluginEditorTypeID), elle alloue de la mŽmoire pour la structure SampleRec et l'initialise. Finalement elle dŽclare la fabrique par un appel ˆ la fonction CFPlugInAddInstanceForFactory().

Les fonctions COM
Le modle COM requiert la dŽfinition de trois fonctions: QueryInterface, AddRef et Release. Celles-ci sont reprŽsentŽes dans le plugin Sample par les fonctions sample_QueryInterface(), sample_AddRef(), et sample_Release().

La transaction prŽliminaire

La transaction prŽliminaire entre l'application principale et le plugin est rŽalisŽe par les fonctions sample_AcceptResource() et sample_EditResource().

Dans la fonction sample_AcceptResource(), le plugin vŽrifie en premier lieu que le type de la ressource est bien un des ceux qu'il attend et il alloue alors de la mŽmoire pour une structure SampleEditInfo qui restera associŽe ˆ la ressource ŽditŽe. Le pointeur sur cette structure sera utilisŽ comme rŽfŽrence RezPlugRef et passŽ comme premier argument de toutes les autres fonctions de l'interface par Rezilla. L'information stockŽe dans cette structure sera ainsi disponible pour toutes ces fonctions.

La fonction sample_AcceptResource() remplit par ailleurs une structure RezPlugInfo passŽe par Rezilla en dernier argument afin de faire quelques requtes auprs de l'application principale. En particulier, dans notre exemple, elle rgle le champ d'attribut comme ceci:

	outInfo->attributes = kPluginEditorStandardControls | kPluginSupportEditCommands;

Le symbole kPluginEditorStandardControls est une constante dŽfinie par Rezilla (dans RezillaPluginInterface.h) demandant que les contr™les standard soient prŽsents dans la fentre d'Ždition (bouton Save, bouton Cancel, etc.). Le symbole kPluginSupportEditCommands est une constante prŽdŽfinie destinŽe ˆ activer les commandes du menu Edit de Rezilla (Cut, Copy, Paste, etc.). La fonction Žmet Žgalement une requte pour obtenir un menu:

	outInfo->menucount = 1;
	outInfo->menuIDs   = &sampleMenuID;
Le menu est dŽfini au moyen d'une ressource 'MENU' dans le fichier de ressources RezSamplePlugin.rsrc. Il dŽfinit deux commandes (dont l'utilitŽ reste douteuse, mais aprs tout il ne s'agit que d'un exemple): Reverse string et Rotate string.

Les fonctions de l'interface

Les fonctions restantes, imposŽes par l'interface, sont:

sample_ReturnResource
sample_RevertResource
sample_IsModified
sample_CleanUp
sample_Refresh
sample_ResizeBy
sample_HandleMenu
sample_HandleClick
sample_HandleKeyDown
sample_HandleCommand

Leur dŽfinition ne pose pas de difficultŽs.

RŽfŽrence des plugins

Voici, pour la rŽfŽrence, un rŽsumŽ de l'interface de programmation (API) permettant d'Žcrire des extensions pour Rezilla.

Un RezPlugRef est un pointeur sur des donnŽes dŽfinies par l'extension. Une extension alloue habituellement de la mŽmoire pour une structure contenant les informations nŽcessaires concernant la resource ŽditŽe. Le RezPlugRef permet d'accŽder ˆ ces donnŽes dans n'importe quelle fonction de l'interface: toutes ces fonctions le passent comme premier argument.

	typedef void *	RezPlugRef;

Structures

SPluginEditorInterface
typedef struct SPluginEditorInterface {
    IUNKNOWN_C_GUTS;
    Boolean  (*AcceptResource)(void *myInstance, ResType inType, short inID, Handle inDataH, RezPlugInfo * outInfo);
    OSErr    (*EditResource)(RezPlugRef inPlugref, RezHostInfo inInfo);
    Handle   (*ReturnResource)(RezPlugRef inPlugref, Boolean * outRelease, OSErr * outError);
    OSErr    (*RevertResource)(RezPlugRef inPlugref, Handle inDataH);
    Boolean  (*IsModified)(RezPlugRef inPlugref);
    void     (*CleanUp)(RezPlugRef inPlugref);
    void     (*Refresh)(RezPlugRef inPlugref);
    OSErr    (*ResizeBy)(RezPlugRef inPlugref, SInt16 inWidthDelta, SInt16 inHeightDelta);
    void     (*HandleMenu)(RezPlugRef inPlugref, MenuRef menu, SInt16 inMenuItem);
    void     (*HandleClick)(RezPlugRef inPlugref, const EventRecord * inMacEvent, Point inPortCoords);
    void     (*HandleKeyDown)(RezPlugRef inPlugref, const EventRecord * inKeyEvent);
    Boolean  (*HandleCommand)(RezPlugRef inPlugref, SInt16 inCommand);
}

RezPlugInfo
typedef struct RezPlugInfo {
    RezPlugRef    plugref;
    UInt32        attributes;
    Rect          winbounds;
    UInt8         menucount;
    MenuID *      menuIDs;
    OSErr         error;
}

RezHostInfo
typedef struct RezHostInfo {
    CFBundleRef  bundleref;
    short        refnum;
    WindowRef    winref;
    UInt8        menucount;
    MenuRef *    menurefs;
    Rect         editrect;
    Boolean      readonly;
}

ƒnumŽrations

RezillaPluginFlags
enum RezillaPluginFlags {
    kPluginNoAttributes             = 0L,
    
    kPluginEditorHasSaveButton      = (1L << 0),
    kPluginEditorHasCancelButton    = (1L << 1),
    kPluginEditorHasRevertButton    = (1L << 2),
    kPluginEditorHasLockIcon        = (1L << 3),
    kPluginEditorHasNameField       = (1L << 4),
    kPluginEditorStandardControls   = (kPluginEditorHasSaveButton 
                                   | kPluginEditorHasCancelButton 
                                   | kPluginEditorHasRevertButton 
                                   | kPluginEditorHasLockIcon),
    
    kPluginWinHasCollapseBox        = (1L << 5),
    kPluginWinIsResizable           = (1L << 6),
    
    kPluginSupportCut               = (1L << 10),
    kPluginSupportCopy              = (1L << 11),
    kPluginSupportPaste             = (1L << 12),
    kPluginSupportClear             = (1L << 13),
    kPluginSupportSelectAll         = (1L << 14),
    kPluginSupportFind              = (1L << 15),
    kPluginSupportFindNext          = (1L << 16),
    kPluginSupportImport            = (1L << 17),
    kPluginSupportExport            = (1L << 18),
    kPluginSupportEditCommands      = (kPluginSupportCut 
                                   | kPluginSupportCopy 
                                   | kPluginSupportPaste 
                                   | kPluginSupportClear)
}

RezillaPluginCmdIDs
enum RezillaPluginCmdIDs {
    kPluginCommandCut        = 1,
    kPluginCommandCopy,
    kPluginCommandPaste,
    kPluginCommandClear,
    kPluginCommandSelectAll,
    kPluginCommandFind,
    kPluginCommandFindNext,
    kPluginCommandImport,
    kPluginCommandExport
}

RezillaPluginErrors
enum RezillaPluginErrors {
    plugErr_Generic                = 5000,    
    plugErr_InitializationFailed,
    plugErr_UnsupportedType,
    plugErr_UnsupportedID,
    plugErr_InvalidData,
    plugErr_UnsupportedResourceFormat,
    plugErr_UnsupportedResourceVersion,
    plugErr_EditResourceFailed,
    plugErr_ReturnResourceFailed,
    plugErr_RevertResourceFailed,
    plugErr_CantResizeWindow,
    plugErr_CantHandleMenuCommand,
	plugErr_CantEditEmptyResource,
    plugErr_LastError
}


Last updated 2006-11-25 12:19:47


Rezilla is hosted by

SourceForge.net Logo