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

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:
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).
#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
#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-000A95B1FF7CDans le cas o il existe plusieurs interfaces, chaque interface (et donc chaque fabrique) doit avoir un UUID.
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"
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.
| ClŽ | Valeur |
| CFBundleIdentifier | net.sourceforge.rezilla.RezImagePlugin |
| CFBundleName | RezImagePlugin |
| CFBundleShortVersionString | RezImagePlugin 0.1 |
| CFBundlePackageType | BNDL |
| CFBundleSignature | Rzil |
| CFBundleExecutable | RezImagePlugin |
| CFBundleVersion | 0.1 |
| CFBundleIconFile | RezillaPlugin.icns |
| CFBundleDevelopmentRegion | English |
| CFBundleInfoDictionaryVersion | 6.0 |
| LSRequiresCarbon | yes |
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.
<key>CFPlugInDynamicRegistration</key> <string>NO</string>
<key>CFPluginNameString</key> <string>RezImagePlugin</string>
<key>CFPlugInUnloadFunction</key> <string></string>
306A0EF3-206E-11DA-8320-000A95B1FF7CLes fonctions qui instancient une interface particulire sont appelŽes des fabriques. Les UUIDs de ces fabriques sont dŽfinis par le plugin: un UUID de fabrique par interface. Dans le cas du plugin RezImagePlugin, le fichier Info.plist dŽfinit ainsi:
<key>CFPlugInTypes</key> <dict> <key>306A0EF3-206E-11DA-8320-000A95B1FF7C</key> <array> <string>0905F836-A20C-11DA-BC6C-000A95B1FF7C</string> </array> </dict>Cela signifie que le type identifiŽ par l'UUID 306A0EF3-206E-11DA-8320-000A95B1FF7C (c'est-ˆ-dire la constante kRezillaPluginEditorTypeID dŽfinie par Rezilla) ne possde actuellement qu'une seule interface, identifiŽe par l'UUID 0905F836-A20C-11DA-BC6C-000A95B1FF7C, attribuŽ par le plugin.
<key>CFPlugInFactories</key> <dict> <key>0905F836-A20C-11DA-BC6C-000A95B1FF7C</key> <string>RezillaImageFactory</string> </dict>On reconna”t ici l'UUID (0905F836-A20C-11DA-BC6C-000A95B1FF7C) vu au paragraphe prŽcŽdent et attribuŽ par le plugin pour dŽsigner la fabrique qui implŽmente le service d'Ždition. La valeur correspondante est RezillaImageFactory: c'est le nom de la fonction que Rezilla devra invoquer au moment de charger le plugin. Cette fonction doit tre dŽfinie dans la bibliothque dynamique.
<key>RezillaPluginEditTypes</key> <array> <string>JPEG</string> <string>jpeg</string> <string>JPG </string> <string>jpg </string> <string>TIFF</string> <string>tiff</string> <string>GIF </string> <string>gif </string> <string>PNG </string> <string>png </string> <string>BMP </string> <string>bmp </string> </array>
<key>RezillaPluginRole</key> <string>Editor</string>Cette clŽ est optionnelle et n'est actuellement pas utilisŽe par Rezilla.
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.
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.
Ë 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.
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.
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
}
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.
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
}
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).
Le dossier SamplePlugin du kit de dŽveloppement de Rezilla contient les fichiers suivants:
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))
L'UUID mentionnŽ au paragraphe prŽcŽdent s'Žcrit sous forme de cha”ne comme ceci:
306B89A8-206E-11DA-8320-000A95B1FF7CIl 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 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 plugin Sample dŽfinit deux structures afin de contr™ler ses sessions d'Ždition:
typedef struct _SampleRec {
SPluginEditorInterface * _rezillaPlugInterface;
CFUUIDRef _factoryID;
UInt32 _refCount;
} SampleRec;
typedef struct SampleEditInfo {
ResType type;
short id;
Handle data;
WindowRef winref;
ControlRef controlref;
Boolean modified;
Boolean readonly;
} SampleEditInfo;
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 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().
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 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 |
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;
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);
}
typedef struct RezPlugInfo {
RezPlugRef plugref;
UInt32 attributes;
Rect winbounds;
UInt8 menucount;
MenuID * menuIDs;
OSErr error;
}
typedef struct RezHostInfo {
CFBundleRef bundleref;
short refnum;
WindowRef winref;
UInt8 menucount;
MenuRef * menurefs;
Rect editrect;
Boolean readonly;
}
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)
}
enum RezillaPluginCmdIDs {
kPluginCommandCut = 1,
kPluginCommandCopy,
kPluginCommandPaste,
kPluginCommandClear,
kPluginCommandSelectAll,
kPluginCommandFind,
kPluginCommandFindNext,
kPluginCommandImport,
kPluginCommandExport
}
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