Delphi tools: Ide menu expert

Erik Stok (aka Baldo)

In de vorige twee artikelen over Delphi’s Open Tools Api (OTA) behandelde ik al praktische toepassingen van de property editor en de component editor. Dit derde artikel beschrijft het bouwen van een uitbreiding van het Delphi IDE menu door gebruik te maken van een expert.

Het idee

De uitbreidingen op de Delphi IDE die in eerdere delen van deze serie artikelen gemaakt zijn, waren te benaderen via de object inspector en de form designer. Dit was mogelijk omdat deze uitbreidingen in een bepaalde context vielen, bijvoorbeeld een specifieke property van een component (de name property editor) of een component op zich (de align component editor). Er zijn echter uitbreidingen die moeten kunnen worden opgestart zonder deze contextgevoelige IDE onderdelen, bijvoorbeeld een speciale compile opdracht of het tonen van een about box. Het uitbreiden van Delphi’s menu is dan de ideale oplossing.

Het mooiste is een oplossing die eenvoudig uit te breiden is. Het toevoegen van functionaliteit in het menu moet gemakkelijk zijn, want er staan natuurlijk nog heel wat uitbreidingen van de Delphi IDE in de planning.

Experts

Een uitbreiding op de Delphi IDE zal in ieder geval in een designtime package geplaatst moeten worden. De vraag is alleen hoe er zorg voor gedragen kan worden dat tijdens het builden van dit design time package ook de menu items tijdelijk worden uitgeschakeld en dat er na de build bijgewerkte menu items verschijnen. Een expert is dan een uitkomst, want als een expert geregistreerd is bij Delphi en de unload van het package met de implementatie vindt plaats, dan zorgt Delphi voor het opruimen van de instance van de expert. Er zijn natuurlijk meerdere constructies denkbaar die een soortgelijk effect hebben, maar de optie voor een expert is naar mijn mening de meeste elegante.

Delphi’s main menu

De expert zal dus menu items moeten gaan toevoegen aan Delphi’s main menu. Maar hoe komt een expert bij Delphi’s main menu? De open tools api verschaft toegang tot het main menu (en diens actionlist) via het INTAServices interface uit de ToolsAPI unit (terug te vinden in de Delphi map onder Source\ToolsAPI). Door dit interface op te vragen van de BorlandIDEServices (tevens uit de ToolsAPI unit) kan een directe referentie worden verkregen naar zowel het menu als de bijbehorende actionlist.

Een menuitem aanmaken

Het mooiste is om een action toe te voegen in de Delphi action list en een menu item aan te maken dat aan deze action gekoppeld is. Het is dan mogelijk gebruik te maken van een shortcut die op elk moment reageert. Ook tools als GExperts zijn dan in staan om de action via hun shortcut instellingen en toolbars direct te benaderen.

Een van de regels bij het aanmaken van menu items (en actions) is dat de aanmaker in de gaten moet houden dat het uitvoeren van de aan het menu item gekoppelde code mogelijk is op het juiste moment. Een handig uitgangspunt hierbij is het aanmaken van menu items en actions zelf te regelen, zodat je precies in de gaten kunt houden dat een menu item wordt opgeruimd als de expert die de uit te voeren code implementeert uit de lucht gaat.

Uitbreidbaarheid

Om de uitbreidbaarheid van de expert te verhogen wordt gebruik gemaakt van een IdeMenuExpert en een IdeMenuObject. De IdeMenuExpert is de class die de toevoeging aan het Delphi menu verzorgd. IdeMenuObject afgeleiden implementeren de toe te voegen functionaliteit en kunnen worden aangemeld bij de IdeMenuExpert.

Het IdeMenuObject ziet er als volgt uit:

TIdeMenuObject = class(TObject)
public
function ItemName: String; virtual; abstract;
function Caption: String; virtual; abstract;
function ShortCut: String; virtual; abstract;
function Order: Integer; virtual; abstract;
procedure Execute(Sender: TObject); virtual; abstract;
end;

De ItemName function geeft een unieke naam terug voor het toe te voegen item. Deze naam wordt gebruikt bij het aanmaken van menu items en actions.

De Caption function geeft de caption terug die in het nieuw toe te voegen menu item moet worden gebruikt.

De ShortCut function spreekt voor zich: de te gebruiken shortcut voor de nieuw toegevoegde action.

Via de Order function kan een volgorde worden aangehouden binnen de toe te voegen menu items. Indien de volgorde van een menu item niet expliciet is opgegeven (of op een bestaande volgorde valt) dan wordt een alfabetische volgorde van menu items aangehouden.

De Execute method bevat de implementatie van de functionaliteit van het toegevoegde menu item.

Als er een eenvoudige registratiemogelijkheid in de IdeMenuExpert komt die IdeMenuObject afgeleiden kan registreren om in het menu opgenomen te worden dan zit het met de uitbreidbaarheid wel goed.

De IdeMenuExpert ziet er dan ook als volgt uit:

TIdeMenuExpert = class(TIExpert)
private
...
protected
function IdeMenuItem: TMenuItem; virtual;
procedure SortMenu; virtual;
procedure RegisterMenuItem(IdeMenuObject: TIdeMenuObject); virtual;
procedure UnRegisterMenuItem(Index: Integer); virtual; procedure RegisterShortcuts(Sender: TObject); virtual;
public
constructor Create;
destructor Destroy; override;
function GetName: string; override;
function GetStyle: TExpertStyle; override;
function GetIDString: string; override;
function GetAuthor: string; override;
function GetComment: string; override;
function GetPage: string; override;
function GetGlyph: HICON; override;
function GetState: TExpertState; override;
function GetMenuText: string; override;
procedure Execute; override;
end;

Naast wat private fields en wat helper functies zijn dit de methods en properties van de IdeMenuExpert.

In de constructor zal zorg worden gedragen dat alle nodige IdeMenuObject afgeleiden worden aangemaakt en in het menu worden gehangen. In de destructor wordt alles weer netjes uit het menu gehaald en opgeruimd.

De methods GetName, GetStyle, GetIDString, GetAuthor, GetComment, GetPage, GetGlyph, GetState, GetMenuText en Execute moeten worden geïmplementeerd omdat dit abstract methods zijn van TIExpert, Delphi’s basis class voor Experts. In het commentaar van de ToolsAPI unit staat uitgebreid beschreven wat deze methods dienen te doen dus daar ga ik niet verder op in. Voor deze expert zijn deze methods slechts bijzaak.

Waar alles om te doen is zijn de methods RegisterMenuItem en UnRegisterMenuItem. Aan RegisterMenuItem wordt een IdeMenuObject meegegeven dat geregistreerd dient te worden in het menu. RegisterMenuItem zal via de methods die het IdeMenuObject biedt alles te weten kunnen komen wat nodig is om een nieuw menu item aan te maken.

Een nieuw toe te voegen menu item zal worden toegevoegd onder een hoofdmenu item; het NLDelphi hoofdmenu item. De method IdeMenuItem zorgt ervoor dat dit hoofdmenu item aangemaakt wordt. Het gebruik van deze method zorgt er tevens voor dat er alleen een hoofdmenu item wordt aangemaakt als er ook daadwerkelijk IdeMenuObjects geregistreerd zijn.

Na het toevoegen van het nieuwe item zal het menu natuurlijk opnieuw gesorteerd moeten worden op basis van de eerder genoemde regels. De method SortMenu implementeert het sorteren van het menu.

De RegisterShortCuts method wordt gebruikt om met behulp van een timer de shortcuts van de menu items te zetten. Delphi biedt faciliteiten in de open tools api om zelf shortcuts te zetten op menu-items, maar in Delphi 6 werken deze nog niet zonder problemen (lees: vele access violations). De enige manier om dan shortcuts te kunnen zetten op actions in de Delphi actionlist is handmatig in een timer, waardoor het zetten van de shortcuts gebeurd nadat Delphi via zijn keyboard bindings heeft gezet op alle actions.

Het About IdeMenuObject

Een expert bouwen zonder te bewijzen dat hij werkt kan natuurlijk niet. Daarom is het about IdeMenuObject toegevoegd. Dit geeft tevens een voorbeeld van hoe een nieuwe toevoeging aan het menu gebouwd dient te worden. Dit bestaat altijd uit drie stappen:

1. Bouw de IdeMenuObject afgeleide (in dit geval de class TIdeMenuAbout). Implementeer in de Execute wat het menu item moet doen.
2. Voeg de in stap 1 aangemaakte unit toe in de uses clause van de unit uIdeMenuExpert (dus: uses uIdeMenuAbout).
3. Voeg in de constructor van de IdeMenuExpert de regel RegisterMenuItem(<MijnIdeMenuAfgeleide>.Create) toe (in dit geval dus de RegisterMenuItem(TIdeMenuAbout.Create) regel).

Dat is alles. Het package toont de code die nodig is om dit alles te implementeren. In toekomstige artikelen zal ik nieuwe IdeMenuObject afgeleiden maken die volgens deze drie stappen kunnen worden toegevoegd in de expert.

Conclusie

Ook hier is weer de eenvoud en toepasbaarheid van de Delphi open tools API terug te vinden. En weer geldt: alles kan worden geprogrammeerd in de bekende Delphi syntax. De IDE menu expert is een goede basis voor uitbreidingen op de Delphi IDE die via een menu item opgeroepen moeten kunnen worden.