Open Tools API その2:メニューの差し替えとノーティファイアインターフェース
IDEのメニューを差し替えるには、IDEそのもののサービスであるINTAServicesのMainMenuプロパティを取得する。
これはTMainMenu型なので、通常のメニューにアクセスのと全く変わりなし。
procedure TFilterWizard.DataModuleCreate(Sender: TObject); var i: Integer; ItemIndex: Integer; InsertPosition: Integer; IDEMainMenu: TMainMenu; ToolsMenu: TMenuItem; begin // メインメニューの[ツール|オプション]を探す IDEMainMenu := (BorlandIDEServices as INTAServices).MainMenu; ToolsMenu := nil; with IDEMainMenu do begin for I := 0 to Items.Count - 1 do begin if AnsiSameText(Items[I].Name, 'ToolsMenu') then ToolsMenu := Items[I]; end; end; InsertPosition := ToolsMenu.Count; for I := 0 to ToolsMenu.Count-1 do begin if AnsiSameText(ToolsMenu.Items[I].Name, 'ToolsOptionsItem') then begin InsertPosition := I; Break; end; end; // PopUpMenuからIDEの[ツール|オプション]へMenuItemを移動 ItemIndex := PopupMenu1.Items.IndexOf(miOption); PopupMenu1.Items.Delete(ItemIndex); ToolsMenu.Insert(InsertPosition, miOption); end;
ソースエディタのメニューを差し替えるには、ソースエディタがアクティブになった段階で行うのだけど、これを検知するノーティファイアの登録は二段階で行わないと行けない。
- IDEに(何らかの)ファイルがロードされたことを通知するノーティファイア
- ソースエディタがアクティブになったことを通知するノーティファイア
まず、IDEのノーティファイアとソースエディタのノーティファイアの定義。
// IDEのノーティファイア TIDENotifier = class(TNotifierObject, IOTANotifier, IOTAIDENotifier) private procedure FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean); procedure BeforeCompile(const Project: IOTAProject; var Cancel: Boolean); overload; procedure AfterCompile(Succeeded: Boolean); overload; end; // ソースエディタのノーティファイア TSourceEditorNotifier = class(TNotifierObject, IOTANotifier, IOTAEditorNotifier) private FEditor: IOTASourceEditor; FIndex: Integer; procedure Destroyed; procedure ViewActivated(const View: IOTAEditView); procedure ViewNotification(const View: IOTAEditView; Operation: TOperation); public constructor Create(AEditor: IOTASourceEditor); destructor Destroy; override; end;
Registerでまず、IDEにノーティファイアを登録する。
procedure Register; var Services: IOTAServices; begin Services := BorlandIDEServices as IOTAServices; Assert(Assigned(Services), 'IOTAServices not available'); IDENotifierIndex := Services.AddNotifier(TIdeNotifier.Create()); end;
IDEでファイルに何らかの操作が発生するとIDEのノーティファイアのFileNotificationが呼ばれる。ファイルがオープンされるとイベントの種別としてofnFileOpenedが渡されるので、ここでソースエディタにノーティファイアを登録する。
procedure TIdeNotifier.FileNotification(NotifyCode: TOTAFileNotification; const FileName: string; var Cancel: Boolean); var ModuleServices: IOTAModuleServices; Module: IOTAModule; begin if NotifyCode = ofnFileOpened then begin ModuleServices := BorlandIDEServices as IOTAModuleServices; Module := ModuleServices.FindModule(FileName); if Assigned(Module) then begin InstallSourceEditorNotifiers(Module); end; end; end; // ソースエディタにノーティファイアを登録 procedure InstallSourceEditorNotifiers(Module: IOTAModule); var I: Integer; SourceEditor: IOTASourceEditor; begin for I := 0 to Module.ModuleFileCount - 1 do if Supports(Module.ModuleFileEditors[I], IOTASourceEditor, SourceEditor) then begin SourceEditorNotifiers.Add(TSourceEditorNotifier.Create(SourceEditor)); SourceEditor := nil; end; end;
エディタがアクティブになるとノーティファイアのViewActivatedが呼ばれる。ここでソースエディタのメニューを取得しメニューアイテムを登録。注意しなければならないのはメニューアイテムの「親」は一つだけなので、アクティブで無くなったソースエディタからメニューアイテムを除去し、アクティブになったソースエディタのメニューにメニューアイテムを登録し直す。
procedure TSourceEditorNotifier.ViewActivated(const View: IOTAEditView); var EditWindow: INTAEditWindow; EditWindowForm: TCustomForm; EditorLocalMenu: TComponent; ParentMenu: TMenu; begin EditWindow := View.GetEditWindow; if not Assigned(EditWindow) then Exit; EditWindowForm := EditWindow.Form; if not Assigned(EditWindowForm) then Exit; // エディタのメニューを取得 EditorLocalMenu := EditWindowForm.FindComponent('EditorLocalMenu'); if not Assigned(EditorLocalMenu) then Exit; try if (EditorLocalMenu is TMenu) then begin with FilterWizard do begin // アクティブで無くなったコードエディタからメニューアイテムを削除 ParentMenu := miExecFormatter.GetParentMenu; if Assigned(ParentMenu) then ParentMenu.Items.Remove(miExecFormatter); // アクティブになったコードエディタのメニューにメニューアイテムを登録 TMenu(EditorLocalMenu).Items.Add(miExecFormatter); end; end; except raise; end; end;
ノーティファイアを登録したら、最後に後始末をしなければならないので、finalizationで後始末。
// IDEに登録してあるノーティファイアを削除する procedure RemoveNotifier; var Services: IOTAServices; begin if IDENotifierIndex <> -1 then begin Services := BorlandIDEServices as IOTAServices; Assert(Assigned(Services), 'IOTAServices not available'); Services.RemoveNotifier(IDENotifierIndex); end; end; // ソースエディタに登録してあるノーティファイアを削除 procedure ClearSourceEditorNotifiers; var I: Integer; begin if Assigned(SourceEditorNotifiers) then for I := SourceEditorNotifiers.Count - 1 downto 0 do TSourceEditorNotifier(SourceEditorNotifiers[I]).Destroyed; end; finalization RemoveNotifier; ClearSourceEditorNotifiers; FreeAndNil(SourceEditorNotifiers);