Création d'experts fiches et projets

De RAD Studio
Aller à : navigation, rechercher

Remonter à Utilisation d'interfaces spéciales pour étendre l'EDI


Delphi propose de nombreux experts fiche et projet préinstallés et vous pouvez également écrire les vôtres. Le référentiel d'objets vous permet de créer des modèles statiques que vous pouvez utiliser dans un projet, mais un expert est beaucoup plus puissant car il est dynamique. L'expert peut interroger l'utilisateur et créer différents types de fichiers selon les réponses de l'utilisateur.

Généralement, un expert fiche ou projet crée un ou plusieurs nouveaux fichiers. En fait, au lieu de fichiers réels, mieux vaut créer des modules sans nom et non enregistrés. Lorsque l'utilisateur les enregistre, l'EDI lui demande le nom de fichier. Un expert utilise un objet créateur pour créer de tels modules.

Une classe créateur implémente une interface de création qui hérite de IOTACreator. L'expert transmet l'objet créateur à la méthode CreateModule du service de module, et l'EDI rappelle l'objet créateur pour obtenir les paramètres nécessaires à la création du module.

Ainsi, un expert fiche qui crée une nouvelle fiche implémente généralement GetExisting pour renvoyer false et GetUnnamed pour renvoyer true. Cela crée un module sans nom (l'utilisateur doit donc spécifier un nom avant de pouvoir enregistrer le fichier) et sans fichier existant correspondant (l'utilisateur doit donc enregistrer le fichier même s'il ne l'a pas modifié). D'autres méthodes du créateur indiquent à l'EDI le type de fichier créé (un projet, une unité ou une fiche), spécifient le contenu du fichier ou renvoient le nom de la fiche, le nom de l'ancêtre ou d'autres informations importantes. D'autres rappels permettent à un expert d'ajouter des modules à un projet qui vient d'être créé, ou d'ajouter des composants à une fiche qui vient d'être créée.

Pour créer un nouveau fichier, ce qui est généralement nécessaire avec un expert fiche ou projet, il faut spécifier le contenu du nouveau fichier. Pour ce faire, écrivez une nouvelle classe implémentant l'interface IOTAFile. Si votre expert se contente du contenu de fichier par défaut, vous pouvez renvoyer nil depuis toute fonction qui renvoie IOTAFile.

Par exemple, votre société peut utiliser un bloc de commentaire standardisé devant apparaître au début de chaque fichier source. Vous pouvez obtenir ceci avec un modèle statique du référentiel d'objets, mais il faut alors modifier manuellement le bloc de commentaire pour indiquer l'auteur et la date de création. Vous pouvez utiliser à la place un créateur pour remplir dynamiquement le bloc de commentaire lors de la création du fichier.

Il faut tout d'abord écrire un expert qui crée de nouvelles fiches et unités. La plupart des fonctions d'un créateur renvoient zéro, des chaînes vides ou d'autres valeurs par défaut, ce qui indique à l'API Tools d'utiliser le comportement par défaut pour créer une nouvelle unité ou une fiche. Redéfinissez GetCreatorType pour indiquer à l'API Tools le type de module à créer : une unité ou une fiche. Pour créer une unité, renvoyez sUnit. Pour créer une fiche, renvoyez sForm. Pour simplifier le code, utilisez une seule classe utilisant le type de créateur comme argument du constructeur. Enregistrez le type du créateur dans une donnée membre afin que GetCreatorType puisse renvoyer sa valeur. Implémentez NewImplSource et NewIntfSource pour renvoyer le contenu souhaité dans le fichier.

TCreator = class(TInterfacedObject, IOTAModuleCreator)
public
constructor Create(const CreatorType: string);
  { IOTAModuleCreator }
  function GetAncestorName: string;
  function GetImplFileName: string;
  function GetIntfFileName: string;
  function GetFormName: string;
  function GetMainForm: Boolean;
  function GetShowForm: Boolean;
  function GetShowSource: Boolean;
  function NewFormFile(const FormIdent, AncestorIdent: string): IOTAFile;
  function NewImplSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
  function NewIntfSource(const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
  procedure FormCreated(const FormEditor: IOTAFormEditor);
  { IOTACreator }
  function GetCreatorType: string;
  function GetExisting: Boolean;
  function GetFileSystem: string;
  function GetOwner: IOTAModule;
  function GetUnnamed: Boolean;
private
  FCreatorType: string;
end;
class PACKAGE Creator : public IOTAModuleCreator {
public:
__fastcall Creator(const AnsiString creator_type)
: ref_count(0), creator_type(creator_type) {}
virtual __fastcall ~Creator();
// IOTAModuleCreator
virtual AnsiString __fastcall GetAncestorName();
virtual AnsiString __fastcall GetImplFileName();
virtual AnsiString __fastcall GetIntfFileName();
virtual AnsiString __fastcall GetFormName();
virtual bool __fastcall GetMainForm();
virtual bool __fastcall GetShowForm();
virtual bool __fastcall GetShowSource();
virtual _di_IOTAFile __fastcall NewFormFile(
const AnsiString FormIdent, const AnsiString AncestorIdent);
virtual _di_IOTAFile __fastcall NewImplSource(
const AnsiString ModuleIdent, const AnsiString FormIdent,
const AnsiString AncestorIdent);
virtual _di_IOTAFile __fastcall NewIntfSource(
const AnsiString ModuleIdent, const AnsiString FormIdent,
const AnsiString AncestorIdent);
virtual void __fastcall FormCreated(
const _di_IOTAFormEditor FormEditor);
// IOTACreator
virtual AnsiString __fastcall GetCreatorType();
 virtual bool __fastcall GetExisting();
virtual AnsiString __fastcall GetFileSystem();
virtual _di_IOTAModule __fastcall GetOwner();
virtual bool __fastcall GetUnnamed();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
private:
long ref_count;
const AnsiString creator_type;
};

La plupart des membres de TCreator renvoient zéro, nil ou des chaînes vides. Les méthodes booléennes renvoient true, sauf GetExisting qui renvoie false. La méthode la plus intéressante est GetOwner qui renvoie un pointeur sur le module du projet en cours ou nil s'il n'y a pas de projet. Il n'y a pas de moyen simple de déterminer le projet ou le groupe de projets en cours. Il faut à la place utiliser GetOwner pour parcourir tous les modules ouverts. Si un groupe de projets est trouvé, ce doit être le seul groupe de projets ouvert, donc GetOwner renvoie son projet en cours. Sinon la fonction renvoie le premier module de projet trouvé ou nil s'il n'y a pas de projet ouvert.

function TCreator.GetOwner: IOTAModule;
var
  I: Integer;
  Svc: IOTAModuleServices;
  Module: IOTAModule;
  Project: IOTAProject;
  Group: IOTAProjectGroup;
begin
  { Renvoyer le projet en cours. }
  Supports(BorlandIDEServices, IOTAModuleServices, Svc);
  Result := nil;
  for I := 0 to Svc.ModuleCount - 1 do
  begin
    Module := Svc.Modules[I];
    if Supports(Module, IOTAProject, Project) then
    begin
      { Mémoriser le premier module de projet}
      if Result = nil then
        Result := Project;
    end
    else if Supports(Module, IOTAProjectGroup, Group) then
    begin
      { Trouver le groupe de projets et renvoyer son projet actif}
      Result := Group.ActiveProject;
      Exit;
    end;
  end;
end;
_di_IOTAModule __fastcall Creator::GetOwner()
{
// Renvoie le projet en cours.
_di_IOTAProject result = 0;
  _di_IOTAModuleServices svc = interface_cast<IOTAModuleServices>(BorlandIDEServices);
  for (int i = 0; i < svc->ModuleCount; ++i)
  begin
    _di_IOTAModule module = svc->Modules[i];
_di_IOTAProject project;
_di_IOTAProjectGroup group;
if (module->Supports(project)) {
// Mémoriser le premier module de projet.
if (result == 0)
result = project;
} else if (module->Supports(group)) {
// Trouve le groupe de projets et renvoie son projet actif.
result = group->ActiveProject;
break;
}
}
return result;
}

Le créateur renvoie nil depuis NewFormSource pour générer un fichier fiche par défaut. Les méthodes intéressantes sont NewImplSource et NewIntfSource qui créent une instance de IOTAFile renvoyant le contenu du fichier.

La classe TFile implémente l'interface IOTAFile. Elle renvoie -1 comme âge du fichier (cela signifie que le fichier n'existe pas) et renvoie le contenu du fichier dans une chaîne. Pour garder la classe TFile simple, le créateur génère la chaîne, la classe TFile se contenant de la transmettre..

TFile = class(TInterfacedObject, IOTAFile)
public
constructor Create(const Source: string);
  function GetSource: string;
  function GetAge: TDateTime;
private
  FSource: string;
end;
constructor TFile.Create(const Source: string);
begin
  FSource := Source;
end;
  function TFile.GetSource: string;
begin
  Result := FSource;
end;
  function TFile.GetAge: TDateTime;
begin
  Result := TDateTime(-1);
end;
class File : public IOTAFile {
public:
__fastcall File(const AnsiString source);
virtual __fastcall ~File();
AnsiString __fastcall GetSource();
System::TDateTime __fastcall GetAge();
protected:
// IInterface
virtual HRESULT __stdcall QueryInterface(const GUID&, void**);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
private:
long ref_count;
AnsiString source;
};
__fastcall File::File(const AnsiString source)
: ref_count(0), source(source)
{}
AnsiString __fastcall File::GetSource()
{
return source;
}
System::TDateTime __fastcall File::GetAge()
{
return -1;
}

Vous pouvez stocker le texte du contenu du fichier dans une ressource afin d'en simplifier la modification, mais dans un souci de simplicité, dans cet exemple le texte est codé directement dans l'expert. L'exemple suivant génère le code source en supposant l'existence d'une fiche. Vous pouvez facilement gérer le cas encore plus simple d'une unité de base. Testez FormIdent, et, si elle est vide, créez une unité de base, sinon créez une unité de fiche. Le squelette de base du code est le même que celui par défaut de l'EDI (avec bien entendu l'ajout des commentaires au début), mais vous pouvez le modifier à votre guise.

function TCreator.NewImplSource(
                    const ModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;
var
  FormSource: string;
begin
  FormSource :=
  '{ ----------------------------------------------------------------- ' +   #13#10 +
  '%s - description'+   #13#10 +
  'Copyright ©  %y Votre société, inc.'+   #13#10 +
  'Créé le %d'+   #13#10 +
  'Par %u'+   #13#10 +
  ' ----------------------------------------------------------------- }' +   #13#10 +   #13#10;
  return TFile.Create(Format(FormSource, ModuleIdent, FormIdent,
AncestorIdent));
}
_di_IOTAFile __fastcall Creator::NewImplSource(
const AnsiString ModuleIdent,
const AnsiString FormIdent,
const AnsiString AncestorIdent)
{
const AnsiString form_source =
"/*-----------------------------------------------------------------\n"
" %m - description\n"
" Copyright \xa9  %y Votre société, inc.\n"
" Créée le %d\n"
" Par %u\n"
"  ---------------------------------------------------------------*/\n"
"\n"
"#include <vcl.h>\n"
"#pragma hdrstop\n"
"\n"
"#include \"%m.h\"\n"
"//-----------------------------------------------------------------\n"
"#pragma package(smart_init)\n"
"#pragma resource \"*.dfm\"\n"
"T%f *%f;\n"
"//-----------------------------------------------------------------\n"
"__fastcall T%m::T%m(TComponent* Owner)\n"
"        : T%a(Owner)\n"
"{\n"
"}\n"
"//----------------------------------------------------------------\n";
  return new File(expand(form_source, ModuleIdent, FormIdent,
AncestorIdent));
}

La dernière étape consiste à créer deux experts fiche : un qui utilise sUnit comme type de créateur et l'autre utilisant sForm. Pour proposer un avantage supplémentaire à l'utilisateur, vous pouvez utiliser INTAServices pour ajouter un élément au menu Fichier > Nouveau permettant d'appeler chaque expert. Le gestionnaire d'événement OnClick de l'élément de menu peut appeler la fonction Execute de l'expert.

Certains experts ont besoin d'activer ou de désactiver des éléments de menu en fonction de ce qui se passe dans l'EDI. Ainsi, un expert qui archive un projet dans un système de contrôle du code source doit désactiver son élément de menu Archiver s'il n'y a pas de fichier ouvert dans l'EDI. Vous pouvez ajouter cette fonctionnalité à votre expert en Utilisant les notificateurs.

Voir aussi