扩展DelphiXE IDE白皮书

简介: 扩展DelphiXE IDE白皮书    原著:Bruno Fierens翻译: 帅宏军(shuaihj@163.com) 前言Delphi提供了丰富的API,使开发人员能够在许多方面自定义和扩展IDE。

扩展DelphiXE IDE白皮书

 

 

 

 

原著:Bruno Fierens

翻译: 帅宏军(shuaihj@163.com


 

前言

Delphi提供了丰富的API,使开发人员能够在许多方面自定义和扩展IDE。本白皮书的目的是介绍这些API,并提供了很多关于如何使用它们的例子。在这些例子中有一些是TMS软件所提供的免费IDE扩展。这些用来扩展IDE的API受OTAPI的保护,OTAPI是Open Tools API的缩写。本白皮书中的例子已用Delphi XE编码和测试通过。

附加信息

除本白皮书以外, 在Delphi XE提供的源代码中,可以找到很多关于OTAPI的资料。这些API在TOOLSAPI.PAS单元中被声明。这个单元默认安装在DelphiXE的C:\Program Files\Embarcadero\RADStudio\8.0\source\ToolsAPI文件夹中。在源代码中,不仅包含接口的定义,也有很多有用的意见,,可以帮助学习这些接口。你应该经常参考这些宝贵的信息来源。

另一个有用的信息来源是GExperts源代码和OTAPI的常见问题页面,你可以在这里找到http://www.gexperts.org/otafaq.html

基本架构

      这些API很大程度上是建立在接口的基础上。接口通常使用前缀IOTA或INTA。IDE暴露了很多可以被插件调用的接口,反之,在IDE中触发一个具体的行动时,IDE本身也可以从插件调用代码,为了通知IDE插件如何响应具体的行动,大多数情况下,我们都需要声明并实现一个从TNotifierObject继承的类,并注册到IDE中。作为一个插件编码人员,你会发现,你的主要工作就是编写代码,以便调用IDE接口,编写类来实现接口,以便被IDE调用。

IDE的哪些方面可以被扩展

Delphi的 IDE在许多方面可以得到扩展。下面是最常见的IDE扩展方面的简要概述:

创建和添加自定义的停靠面板:可以在Object Inspector等面板上, 添加自定义的停靠面板,就像组件调色面板。

与Code Editor(代码编辑器) 交互:提供了接口,以编程方式操作Delphi IDE的代码编辑器。例如,代码片段插入,替换文本,处理特殊键序列,添加自定义语法高亮等。

与Code Insight(代码识别) 交互:在编辑器的代码识别也可定制,可以为代码中的特定结构自定义帮助文本。

与Project Manager(工程管理器)交互:允许您为工程管理器面板中的工程和文件自定义上下文菜单

添加自定义向导或项目到(Repository)仓库:允许添加自定义向导或项目到(Repository)仓库,通过自定义向导,可以创建新类型的工程、窗体或数据模块。

和ToDo items交互:提供了通过代码和ToDo items交互的API。

和Debugger(调试器)交互:在新的Delphi版本中,可以在调试时,对特定的数据类型增加自定义显示。

和Form Designer(窗体设计器)交互:通过扩展,可以和窗体设计器进行交互。

和启动时的闪屏界面交互:提供了接口,可以在启动Delphi时,在闪屏界面中增加自定义的文字。

在本白皮书中,涵盖了OTAPI的各个部分,如创建自定义的停靠模板,访问代码编辑器、工程管理器、仓库、各种菜单等。

OPEN TOOLS API的历史

      因为有了Delphi的多年发展,才有了用于Delphi IDE扩展的API。在DelphiXE中,原有的用于定制IDE的API得到扩展,很多新的API被增加进来。由于OTAPI主要是以接口为基础,新功能一般都是通过新接口提供。为了有调理有组织,Delphi团队采取了给那些从老接口继承下来的新接口,在老接口名字后面加上IDE版本号作为后缀的约定,来给新的接口命名。请注意IDE版本和Complier版本是不同的。例如,在老的IDE中,提供Repository扩展的接口是IOTARepositoryWizard,新的版本中,陆续改为IOTARepositoryWizard60、IOTARepositoryWizard80。

      如果一个插件想使用更高版本的IDE中提供的一些新功能,它必须增加一些类,来实现新的接口。同时,在老的IDE版本中实现的插件,如继承并实现IOTARepositoryWizard的插件,在新的IDE中能仍然能正常使用。

下面用于OTAPI接口的各种IDE版本的列表:

l  60 = Delphi 7

l  80 = Delphi 8

l  90 = Delphi 2005

l  100 = Delphi 2006

l  110 = Delphi 2007

l  120 = Delphi 2009

l  140 = Delphi 2010

l  145 = Delphi XE

访问IDE

      虽然Delphi提供了多种在访问Delphi的特定方面时被调用的接口的实现,在许多情况下,还是有必要通过插件直接调用IDE的一些功能。为此Delphi公开了很多接口。当IDE启动时,它会创建一个全局的的变量BorlandIDEServices来实现各种接口。当单元ToolsAPI被包含在引用列表中时,这个变量BorlandIDEServices可以用于访问和查询所需的接口。

 

例如:

// 检查BorlandIDEServices全局变量的合法性

if Assigned(BorlandIDEServices) then

begin

// 访问BorlandIDEServices中实现的IOTAModuleServices接口,并调用l CloseALL来关闭所有模块。

(BorlandIDEServices as IOTAModuleServices).CloseAll;

end;

 

以下是DelphiXE的IDE公开的接口:

- INTAServices

用于toolbars, menu, imagelists, actions的接口

- IOTAActionServices

用于open,close, save files的接口

- IOTACodeInsightServices ,IOTACodeInsightManage

用于thecode insight managers的接口

- IOTADebuggerServices

用于Debugger的接口,如断点、事件日志等

- IOTAEditorServices

用于IDEeditor, the edit buffer, edit view, options的接口

- IOTAEditorViewServices

用于IDEeditor view 的接口

- INTAEnvironmentOptionsServices

用于在IDETools, Options menu 中增加选项的接口

- IOTAKeyBindingServices

用于快捷键绑定的接口

- IOTAKeyboardServices

用于宏录制和回放的接口

- IOTAMessageServices

用于messageview的接口

- IOTAModuleServices

用于访问IDE已经打开的模块的接口,(如projects, source files, formfiles, 等.)

 -IOTAPackageServices

用于访问安装IDE中的组件包的接口

- IOTAServices

用于暴露IDE信息的接口,如产品的标识信息,应用程序文件夹,bin文件夹,安装的语言

- IOTAToDoServices

用于访问ToDo items的接口

- IOTAWizardServices

用于访问IDErepository 的接口

- IOTAHighlightServices 、IOTAHighlighter

用于语法高亮的接口

- IOTAPersonalityServices

用于IDE可识别的文件扩展名

- IOTACompileServices

用于访问compiler的接口,可以开始和结束应用程序的调试,以及在调试工程中发送通知等。

IDE插件快速入门

IDE插件是被编译安装到IDE中的包。也就是说,Delphi启动时,将加载这些包。包可以在单元的initialization段中通过代码实现初始化,通过Register方法将类注册到IDE中。为了安装IDE插件或扩展,至少需要做的是创建一个TNotifierObject类型的子类,并实现IOTAWizard接口,并注册(如下代码),IDE就可以在需要的时候调用相应的接口方法。

unit MyIDEPlugin;

interface

 

uses

Classes, ToolsAPI;

 

TMyIDEWizard = class(TNotifierObject, IOTAWizard)

// 在这里写实现代码

end;

 

implementation

 

//注册到IDE

procedure Register;

begin

RegisterPackageWizard(TMyIDEWizard.Create);

end;

扩展IDE REPOSITORY自定义向导

Our first indepth look at extending the IDE is about creating custom repository wizards. Tocreate a plugin that will extend the repository, it is necessary to write aclass that implements the IOTAWizard interface and the IOTARepositoryWizardinterface. The IOTARepositoryWizard interface has 3 versions. It was extendedin Delphi 7 to enable differentiating between repository items for VCL or CLXprojects. It was extended again in Delphi 2005 where hierarchical categories ofrepository items were introduced. Note that it is only necessary to implementthe latest IOTARepositoryWizard80 interface if you want to take advantage ofthe new features. The original IOTARepositoryWizard interface works fine inDelphi XE. For this example, we will take advantage of the categories exposedin the IOTARepositoryWizard80 interface. As such, we will write a classdescending from TNotifierObject that implements the IOTAWizard interface andthe IOTARepositoryWizard.

TMyProjectWizard = class(TNotifierObject,IOTAWizard, IOTARepositoryWizard80)

// implement interfaces here

end;

This class willthen be registered with the IDE:

procedure Register;

begin

RegisterPackageWizard(TMyProjectWizard.Create);

end;

The next stepis to implement the interfaces. The IOTAWizard interface informs the IDE of theplugin name, ID and provide a method to execute the plugin. TheIOTARepositoryWizard interface provides the IDE with information about therepository item, the author, its glyph and also the category and personality forwhich the repository item is provided.

The classdefinition becomes:

TMyProjectWizard = class(TNotifierObject, IOTAWizard,IOTARepositoryWizard80)

public

// IOTAWizard

function GetIDString:string;

function GetName:string;

function GetState:TWizardState;

procedure Execute;

// IOTARepositoryWizard

function GetAuthor: string;

function GetComment: string;

function GetPage: string;

function GetGlyph:Cardinal;

// IOTARepositoryWizard80

function GetGalleryCategory:IOTAGalleryCategory;

function GetPersonality:string;

function GetDesigner:string;

end;

The fullimplementation of the wizard class methods can be found in sample 1.

One part of theinterface informs the IDE about the new repository item, the Execute methodwill be called when the user clicks that repository item in the IDE. It is inthis Execute method that the plugin needs to take the necessary steps to createthe source files for the selected repository item. In the Execute method, wegrab the BorlandIDEServices global variable and use the IOTAModuleServicesinterface to query the default unit, class, and filename that the IDE proposesfor creating a new instance of the repository item. With this unit, class andfilename, the IOTAModuleServices CreateModule method is then used to create theactual new module that is added to the active project.

The Executemethod

procedure TTMSFormWizard.Execute;

var

LProj: IOTAProject;

begin

if Assigned(BorlandIDEServices)then

begin

// use the IOTAModuleServices interface to query a new defaultunit, class & filename

(BorlandIDEServices as IOTAModuleServices).GetNewModuleAndClassName('',FUnitIdent, FClassName, FFileName);

FClassName := SFormName + Copy(FUnitIdent, 5,Length(FUnitIdent));

LProj := GetActiveProject;

if LProj<> nil then

begin

(BorlandIDEServices as IOTAModuleServices).CreateModule(TMyUnitCreator.Create(LProj,FUnitIdent, FClassName, FFileName));

end;

end;

end;

Themodule creator is a class that implements the IOTACreator, IOTAModuleCreatorinterfaces. Via these interfaces, the actual source code file and form file canbe created.

TMyUnitCreator = class (TNotifierObject, IOTACreator,IOTAModuleCreator)

public

// IOTACreator

function GetCreatorType:string;

function GetExisting:Boolean;

function GetFileSystem:string;

function GetOwner:IOTAModule;

function GetUnnamed:Boolean;

// IOTAModuleCreator

function GetAncestorName:string;

function GetImplFileName:string;

function GetIntfFileName:string;

function GetFormName:string;

function GetMainForm:Boolean;

function GetShowForm:Boolean;

function GetShowSource:Boolean;

function NewFormFile(constFormIdent, AncestorIdent: string): IOTAFile;

function NewImplSource(constModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;

function NewIntfSource(constModuleIdent, FormIdent, AncestorIdent: string): IOTAFile;

procedure FormCreated(constFormEditor: IOTAFormEditor);

The mostimportant methods in this interface are the NewFormFile and the NewImplSourcemethods. These functions should return a class implementing the IOTAFileinterface that will return the actual code for the .DFM form file and the .PASsource code file. The NewIntfSource function is not used for Delphi forms orprojects, only for C++ header files.

The IOTAFileinterface is an interface that simply returns the content of the .DFM or .PASfile as a string and the file age as TDateTime.

TCodeFile = class(TInterfacedObject, IOTAFile)

protected

function GetSource:string;

function GetAge: TDateTime;

end;

Note that it isimportant that the .DFM and .PAS file returned contain valid code. The IDE willtry to parse the form file and source code and should the parsing fail, the IDEwill not create the file.

In the sample,the form unit & DFM file are stored in a resource file created with BRCC32from the .RC file:

TCompanyFormSRC10 " Unit1.pas" TCompanyFormFRM 10 " Unit1.DFM" and theIOTAFile implementing class retrieves the source code + DFM file from theresource, replaces the form name, form class, unit name with the new name andreturns it via the GetSource function:

function TUnitFile.GetSource: string; varText, ResName:AnsiString; ResInstance: THandle; HRes: HRSRC; begin Resname := AnsiString(SCodeResName);ResInstance := FindResourceHInstance(HInstance); HRes := FindResourceA(ResInstance,PAnsiChar(ResName), PAnsiChar(10)); Text := PAnsiChar(LockResource(LoadResource(ResInstance,HRes))); SetLength(Text, SizeOfResource(ResInstance, HRes)); Result := Format(string(Text),[FModuleName, FFormName, FAncestorName]); end;

EXTENDINGTHE IDEWITHCUSTOM DOCKING PANELS

Creating acustom dock panel for the IDE is a different type of IDE extension and we donot need to register a IOTAWizard interface implementing class with the IDE.Instead, the

pluginshould create an instance of a form, provide a way for the user to show or hidethe docking panel, persist its state with the IDE desktop, and, finally,destroy the docking panel upon exiting the IDE. The class used for the IDEdocking panel is implemented in the units DockForm, DockToolForm that are partof the DesignIDE package. Two different types exist: TDockableForm andTDockableToolBarForm. We will need to write a Register procedure in a unit ofthe plugin package. This Register procedure will be called by the IDE afterloading the package. From this Register procedure, we can create the dockingpanel and insert a new menu item in the IDE to show/hide the panel. The code inthe finalization section of the unit will be called when the IDE is closed andthus, the panel can be destroyed in this phase.

The skeletonfor the unit to create the custom docking panel and destroying it is as such:

unit MyIDEDockPanel;

interface

procedure Register;

implementation

uses

MyDockForm;

var

MyIDEDockForm: TMyDockForm;

procedure Register;

begin

if MyIDEDockForm= nil then

begin

MyIDEDockForm := TMyIDEDockForm.Create(nil);

end;

end;

finalization

MyIDEDockForm.Free;

end.

TMyDockFormis a class that descends either from TDockableForm and TDockableToolBarForm. Toplay well with the IDE desktop persisting functionality, it is required toinitialize the DeskSection, AutoSave and SaveStateNecessary properties of thebase class:

constructor TMyIDEDockForm.Create(AOwner:TComponent);

begin

inherited;

DeskSection := Name;

AutoSave := True;

SaveStateNecessary := True;

end;

destructor TMyIDEDockForm.Destroy;

begin

SaveStateNecessary := True;

inherited;

end;

In addition,the custom docking panel class should be registered with the IDE desktop withthe code:

RegisterDesktopFormClass(TMyIDEDockForm,MyIDEDockForm.Name,MyIDEDockForm.Name);

if @RegisterFieldAddress<> nil then

RegisterFieldAddress(MyIDEDockForm.Name, @MyIDEDockForm);

Theresult of the docking panel plugin is:

Full code ofthe docking panel plugin can be found in the code download in the folderDockForm.

ACCESSINGTHE DELPHICODE EDITOR

Access to theIDE code editor is made possible via IOTAEditorServices interface that isimplemented in the BorlandIDEServices global variable. The IOTAEditorServicesprovides access to the active editor view via IOTAEditorServices.TopView:IOTAEditView. The IOTAEditView interface provides access to editor bookmarks,cursor position, scrolling, etc. In turn, this IOTAEditView provides access tothe editor buffer. The buffer exposes the interface IOTAEditBuffer. ThisIOTAEditBuffer interface allows manipulation of text, for example insertion anddeletion.

Thiscode snippet will grab the IOTAEditorServices from BorlandIDEServices, get theIOTAEditView interface and access the IOTAEditBuffer to insert text at the topof the source code file in the active editor window:

var

EditorServices: IOTAEditorServices;

EditView: IOTAEditView;

copyright: string;

copyright := ‘Copyright © 2011 by tmssoftware.com’;

EditorServices := BorlandIDEServices as IOTAEditorServices;

EditView := EditorServices.TopView;

if Assigned(EditView)then

begin

// position cursor at 1,1

EditView.Buffer.EditPosition.Move(1,1);

// insert copyright notice on top

EditView.Buffer.EditPosition.InsertText(copyright);

end;

EXTENDINGTHE DELPHIIDEMENU

The Delphi IDEprovides the IOTAMenuWizard interface to add items the Delphi IDE main menu.The limitation of this interface is that all menu items added this way will beorganized under the help menu. We will offer an alternative method to insertnew menu items anywhere in the existing Delphi IDE main menu.

To add a newmenu item via IOTAMenuWizard, create a class that descends from TNotifierObjectand implements IOTAWizard, IOTAMenuWizard:

TMyMenuItem = class(TNotifierObject, IOTAWizard,IOTAMenuWizard)

function GetIDString:string;

function GetName:string;

function GetState:TWizardState;

procedure Execute;

function GetMenuText:string;

end;

andregister this class with the IDE via the unit Register procedure:

RegisterPackageWizard(TMyMenuItem.Create);

When the menuitem is clicked in the IDE, Delphi will call the Execute method from where yourcustom action can be performed.

Alternatively,it is possible to get access to the Delphi IDE main menu as a TMainMenu classand use the common TMainMenu methods to insert a TMenuItem instance in thismenu.

To do this, usethe INTAServices40 interface implemented in BorlandIDEServices and call itsfunction MainMenu that returns a TMainMenu instance.

var

NTAServices : INTAServices40;

mnuitem: TMenuItem;

mnuitem := TMenuItem.Create(nil);

mnuitem.Caption := 'New item';

NTAServices := BorlandIDEServices as INTAServices40;

NTAServices.MainMenu.Items.Add(mnuitem);

When the firsttechnique with the IOTAMenuWizard is used, the IDE will automatically removethe menu item when the plugin is uninstalled. Using the second technique, we’llneed to remove the menu item in code. As the interface is based on TMainMenu,call Items.Remove( ) to remove the item from the menu and destroy it as well asthe object that handles the menu click. The full code to add and remove themenu item becomes:

procedure AddIDEMenu;Extendingthe Delphi IDE

var NTAServices: INTAServices40;

begin NTAServices:= BorlandIDEServices as INTAServices40;

// avoid inserting twice if NTAServices.MainMenu.Items[5].Find('INTAServices40Menu')= nil then begin CustomMenuHandler := TCustomMenuHandler.Create; mnuitem:= TMenuItem.Create(nil); mnuitem.Caption := 'INTAServices40Menu'; mnuitem.OnClick:= CustomMenuHandler.HandleClick; NTAServices.MainMenu.Items[5].Add(mnuitem) end;end;

procedure RemoveIDEMenu;var NTAServices: INTAServices40;

begin if Assigned(mnuitem)then begin NTAServices := BorlandIDEServices as INTAServices40;NTAServices.MainMenu.Items[5].Remove(mnuitem); mnuitem.Free; if Assigned(CustomMenuHandler)then CustomMenuHandler.Free; end; end;

The full samplefor extending the IDE main menu can be found in the code download in theIDEMenu folder.

EXTENDING THE DELPHIPROJECT MANAGER CONTEXT MENU

In thissection, we will access to the IDE project manager, its context menu, and theprojects & files opened in the project manager. Furthermore, we will extendthe context menu with custom actions. To start, we will make use of theIOTAProjectManager interface, available in the BorlandIDEServices globalvariable. The IOTAProjectManager interface exposes the functionAddMenuItemCreatorNotifier that needs to be called to pass an instance of aclass descending from TNotifierObject and implementing theIOTAProjectMenuItemCreatorNotifier interface. Basically, this informs the IDEthat before it shows the project manager context menu, that it should query ourplugin if one or more custom context menu items should be added.

The class forthe context menu item creator is:

TMyProjectContextMenu = class(TNotifierObject,IOTAProjectMenuItemCreatorNotifier)

procedure AddMenu(constProject: IOTAProject; const IdentList: TStrings; const ProjectManagerMenuList:IInterfaceList; ltiSelect: Boolean);

IsMuend;

Via theparameter IOTAProject, the code can determine for which project the contextmenu is shown and via the parameter ProjectManagerMenuList, instances of aTMyProjectContextMenuLocal class can be added. In this sample code snippet, acontext menu item is unconditionally added, regardless of of which item isright-clicked in the project manager:

procedure TMyProjectContextMenu.AddMenu(constProject: IOTAProject; const IdentList: TStrings; const ProjectManagerMenuList:IInterfaceList; IsMultiSelect: Boolean);

var

Item: TMyProjectContextMenuLocal;

Mnubegin

MnuItem := TMyProjectContextMenuLocal.Create;

ProjectManagerMenuList.Add(MnuItem)

end;

TheIdentList is a stringlist holding string identifiers of what item type is rightclicked. This is declared in TOOLSAPI.PAS and can be:

sBaseContainer = 'BaseContainer';

sFileContainer = 'FileContainer';

sProjectContainer = 'ProjectContainer';

sProjectGroupContainer = 'ProjectGroupContainer';

sCategoryContainer = 'CategoryContainer';

sDirectoryContainer = 'DirectoryContainer';

sReferencesContainer = 'References';

sContainsContainer = 'Contains';

sRequiresContainer = 'Requires';

If the contextmenu item should only appear when the project group is right-clicked, the codewould be:

procedure TMyProjectContextMenu.AddMenu(constProject: IOTAProject; const IdentList: TStrings; const jectManagerMenuList:IInterfaceList; IsMultiSelect: Boolean);

Provar

MnuItem: TMyProjectContextMenuLocal;

begin

if (IdentList.IndexOf(sProjectGroupContainer)<> -1) then

begin

MnuItem := TMyProjectContextMenuLocal.Create;

// Set menu item properties here

MnuItem.OnExecute := MenuClickHandler;

ProjectManagerMenuList.Add(MnuItem)

end;

end;

TheTMyProjectContextMenuLocal is a class descending from TProjectContextMenuLocaland should implement the interfaces IOTALocalMenu and IOTAProjectManagerMenu.This interface consists of methods:

TMyProjectContextMenuItem = class(TProjectContextMenuLocal,IOTALocalMenu, IOTAProjectManagerMenu)

public

// IOTALocalMenu

function GetCaption: string;

function GetChecked:Boolean;

function GetEnabled:Boolean;

// IOTAProjectManagerMenu interface

function GetIsMultiSelectable:Boolean;

procedure SetIsMultiSelectable(Value:Boolean);

procedure Execute(constMenuContextList: IInterfaceList); overload;

function PreExecute(constMenuContextList: IInterfaceList): Boolean;

function PostExecute(constMenuContextList: IInterfaceList): Bool

ean; property IsMultiSelectable: Boolean readGetIsMultiSelectable write SetIsMultiSelectable;

end;

With theIOTALocalMenu, we can set the caption, checked state, and enabled state of themenu item. With the IOTAProjectManagerMenu interface, we can define whether ornot the context menu item supports executing on multiple selected items in theproject manager. The methods PreExecute, Execute, PostExecute are called,respectively, before actual execution of the Execute, when the item isselected, and after the actual execution of Execute. The parameter of theExecute methods is the MenuContextList that is a list of items selected in theproject manager.

From theExecute method, the project opened for which the context menu item was selectedis retrieved with following code:

procedure TMyProjectContextMenuLocal.Execute(constuContextList: IInterfaceList);

Menvar

MenuContext: IOTAProjectMenuContext;

Project: IOTAProject;

begin

MenuContext := MenuContextList.Items[0] as IOTAProjectMenuContext;

oject := MenuContext.Project;

Prend;

Thefull sample for adding a project manager context menu item can be found in thecode download in the ProjMenu folder.

ADDINGAN IDEEDITORCONTEXT MENU ITEM AND GRABBING THE SELECTED TEXT

If your pluginwants to offer some processing on selected text in the editor, you may want toadd a custom item in the IDE editor context menu that, when clicked, grabs theselected text and processes it. To add such a context menu, we need to getaccess to the menu, which, in turn, first need access to the editor. Pleasenote that the editor is not immediately available upon startup of the IDE,therefore we can’t get access to the editor in the plugin initialization code.What we needed is to register a class that implements theINTAEditServicesNotifier interface. The IDE calls this interface when theeditor is activated in the IDE. At that point, the plugin code can be sure thatthe editor instance exists. The INTAEditServicesNotifier interface offersseveral methods of which only EditorViewActivated is of interest. The interfaceof this INTAEditServicesNotifier implementing class is:

TEditNotifierHelper = class(TNotifierObject,IOTANotifier, INTAEditServicesNotifier)

procedure WindowShow(constEditWindow: INTAEditWindow; Show, LoadedFromDesktop: Boolean);

procedure WindowNotification(constEditWindow: INTAEditWindow; Operation: TOperation);

procedure WindowActivated(constEditWindow: INTAEditWindow);

procedure WindowCommand(constEditWindow: INTAEditWindow; Command, Param: Integer; var Handled:Boolean);

procedure EditorViewActivated(constEditWindow: INTAEditWindow; const EditView: IOTAEditView);

procedure EditorViewModified(constEditWindow: INTAEditWindow; const EditView: IOTAEditView);

procedure DockFormVisibleChanged(constEditWindow: INTAEditWindow; DockForm: TDockableForm);

procedure DockFormUpdated(constEditWindow: INTAEditWindow; DockForm: TDockableForm);

procedure DockFormRefresh(const EditWindow: INTAEditWindow;DockForm: TDockableForm);

end;

This notifierclass is registered with the IDE via the code:

procedure Register;var Services: IOTAEditorServices; begin Services :=BorlandIDEServices as IOTAEditorServices; NotifierIndex :=Services.AddNotifier(TEditNotifierHelper.Create); end;

The AddNotifierfunction returns a unique index with which the class is registered. ThisNotifierIndex variable in the unit needs to be used to unregister the notifierclass again when the plugin is uninstalled. As such, we need to perform thisunregister in the finalization section of the unit:

procedure RemoveNotifier;var Services: IOTAEditorServices;

begin if NotifierIndex<> -1 then begin Services := BorlandIDEServices as IOTAEditorServices;Services.RemoveNotifier(NotifierIndex); NotifierIndex := -1; end; end;

finalization RemoveNotifier;end.

With thisnotifier installed, it is now possible to get access to the IDE editor contextmenu when the editor first becomes active and install our custom menu item. Thecode used to do this is: Extending the Delphi IDE

var custommenu: TMenuItem;

procedure TEditNotifierHelper.EditorViewActivated(constEditWindow: INTAEditWindow; const EditView: IOTAEditView); begin if not Assigned(custommenu)then begin AddEditContextMenu; end; end;

procedure AddEditContextMenu;var editview: IOTAEditView140; popupmenu: TPopupMenu;

begin editview:= (BorlandIDEServices as IOTAEditorServices).TopView; popupmenu :=editview.GetEditWindow.Form.FindComponent('EditorLocalMenu') as TPopupMenu;custommenu := TMenuItem.Create(nil); custommenu.Caption := 'Customcontext menu item'; custommenu.OnClick := mnuHandler.MenuHandler;popupmenu.Items.Add(custommenu); end;

initialization custommenu:= nil; finalization custommenu.Free; end. Tohandle the click on the editor context menu item, a class with the methodMenuHandler is created: TMenuHandler= class(TComponent) procedure MenuHandler(Sender: TObject); end;

andthis MenuHandler() method can get access to the editor view and get theselected text with:

procedure TMenuHandler.MenuHandler(Sender:TObject); var editview: IOTAEditView140; editblock: IOTAEditBlock;

begin editview:= (BorlandIDEServices as IOTAEditorServices).TopView; // get theselected text in the edit view editblock := editview.GetBlock;

ShowMessage('Context menu click: ' +inttostr(editblock.StartingColumn)+':'+inttostr(editblock.StartingRow) + ' - '+ inttostr(editblock.EndingColumn)+':'+inttostr(editblock.EndingRow));

// if there is a selection of text, get it via editblock.Text

if (editblock.StartingColumn<> editblock.EndingColumn) or (editblock.StartingRow <>editblock.EndingRow) then ShowMessage('Selected text: ' +editblock.Text); end;

The full samplefor adding an editor context menu item can be found in the code download in theEditorContextMenu folder.

ADDINGINFORMATION ON THE DELPHI SPLASHSCREEN

To give the IDEplugin we create a finishing touch and to inform the user it is properlyinstalled in the IDE, we can add some information to the splash screen duringthe startup of the IDE. The TOOLSAPI unit exposes the SplashScreenServicesIOTASplashScreenServices interface. This interface offers the methodAddPluginBitmap:

procedure AddPluginBitmap(constACaption: string; ABitmap: HBITMAP; AIsUnRegistered: Boolean = False; const ALicenseStatus:string = ''; const ASKUName: string = '');

Theparameters are:

         ACaption : text to appear on the splashscreen

         ABItmap: bitmap handle to show in thesplash screen associated with the plugin for a 24x24 bitmap

         AIsUnRegisterd: this is a booleanparameter indicating whether the text should appear in red font forunregistered products or regular white text for registered products.

         ALicenseStatus: this is a text that candisplay for example 'Trial' or 'Registered'.

         ASKUName: this text can show the nameof the SKU if different SKUs exist for the plugin.

 

In theinitialization section of a unit within a package loaded by the IDE, we can usethis interface to add custom information to the splash screen while the IDE isloading:

procedure AddSplashText;

var

bmp: TBitmap;

begin

bmp := TBitmap.Create;

bmp.LoadFromResourceName(HInstance, 'PLUGINBITMAPRESOURCE');

SplashScreenServices.AddPluginBitmap('Plugin product XYZ © 2011by MyCompany',bmp.Handle,false,'Registered','');

bmp.Free;

end;

initialization

AddSplashText;

end.

The full samplefor adding an entry in the IDE splash screen can be found in the code downloadin the SplashScreen folder.

FREE TMSIDE PLUGINSFOR DELPHIXE

Based on thetechniques presented in this whitepaper, we offer a couple of free IDE pluginsfor Delphi XE. The free plugins can be found in the folder “TMS plugins forDelphi XE” in the code download.

TMS PROJECT MANAGERPLUGIN

This pluginuses the project manager context menu extension and the IDE menu extension tooffer access to the plugin options. It adds a context menu to the Project Managerwith 3 new options: 1) ZIP project: this allows to create a ZIP file containingthe project files. In the plugin options, it can be configured what file typesto include in a project. 2) ZIP & Email project: this option will try touse the default email client to send the project ZIP file by email. 3) ZIP& Upload project: this option will try to upload the project ZIP file to anFTP server defined in the plugin options

TMS WHAT’SNEW PLUGIN

This pluginbuilds on the technique to create a custom IDE docking form. This docking formhas three tabs. It displays the latest component releases from TMS software,the blog feed as well as the tweets from TMS software.

TMS PRESENTATIONTOOL PLUGIN

The TMSPresentation tool plugin is also based on the custom IDE docking formcapability as well as interface with the IDE editor. It offers two tab. In thefirst tab is a clipboard monitor. This shows all text that was put on theclipboard. Direct drag & drop from the docking form to the IDE editor ispossible. The second tab is a list of saved code snippets. These code snippetscan be used for example when giving presentation.

ABOUT THE AUTHOR

Bruno studiedcivil electronic engineering at university of Ghent and started a career asR&D digital hardware engineer at Barco Graphics in Belgium. He founded TMSsoftware in 1996, developing VCL components starting with Delphi 1. TMSsoftware became Borland Technology Partner in 1998 and developed the DelphiInformant award-winning grid & scheduling components. In 2001, he starteddevelopment on IntraWeb component and in 2003, ASP.NET components. Currently,Bruno is developing and managing VCL, Silverlight, ASP.NET and IntraWebcomponent development projects, as well as consulting and custom projectdevelopment and management on Windows, Web and iPad with Delphi and XCode. Hisspecial areas of interest are user interfaces & hardware.

EmbarcaderoTechnologies, Inc. is the leading provider of software tools that empowerapplication developers and data management professionals to design, build, andrun applications and databases more efficiently in heterogeneous ITenvironments. Over 90 of the Fortune 100 and an active community of more thanthree million users worldwide rely on Embarcadero’s award-winning products tooptimize costs, streamline compliance, and accelerate development andinnovation. Founded in 1993, Embarcadero is headquartered in San Francisco withoffices located around the world. Embarcadero is online at www.embarcadero.com.

Copyright © 2011 TMS software

 

相关文章
|
IDE 程序员 编译器
VS2019正确创建C++步骤以及扩展插件美化你的VS2019的IDE开发环境
VS2019正确创建C++步骤以及扩展插件美化你的VS2019的IDE开发环境
VS2019正确创建C++步骤以及扩展插件美化你的VS2019的IDE开发环境
|
Web App开发 JavaScript 前端开发
Chrome 扩展推荐:让你的 Chrome 变成 Python IDE
这是一款运行在Chrome上的 python shell ,可以让您不必安装python环境,即可运行python脚本。当然也不仅限于Python,这个二十几兆的扩展还支持Ruby,JavaScript等。
483 0
Chrome 扩展推荐:让你的 Chrome 变成 Python IDE
|
IDE 前端开发 JavaScript
KAITIAN IDE 是如何构建扩展能力极强的插件体系的?
本文简要阐述了 VS Code 插件模型,从实际场景出发在这套体系之上初步构建出开天特有的插件扩展能力。
KAITIAN IDE 是如何构建扩展能力极强的插件体系的?
|
IDE 开发工具 数据安全/隐私保护
|
6月前
|
网络协议 IDE 网络安全
GoLand远程开发IDE:使用SSH远程连接服务器进行云端编程
GoLand远程开发IDE:使用SSH远程连接服务器进行云端编程
681 0
|
IDE Go 开发工具
Go开发IDE全览:GoLand vs VSCode全面解析
Go开发IDE全览:GoLand vs VSCode全面解析
494 0
|
12天前
|
IDE 开发工具 C++
AvaloniaUI项目离线开发全攻略:IDE安装、模板应用与NuGet私有化部署一站式解决
本文详细介绍了在离线环境中开发Avalonia UI项目的完整解决方案,包括Visual Studio 2022和JetBrains Rider的离线安装、Avalonia UI模板的配置、私有NuGet服务的部署与使用,以及NuGet包的制作和上传。通过这些步骤,您可以在网络受限或完全离线的环境中顺利进行Avalonia UI项目的开发。
AvaloniaUI项目离线开发全攻略:IDE安装、模板应用与NuGet私有化部署一站式解决