[Last Call] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 973
  • Last Modified:

Open Tools API information needed

Hi Experts,

I need to write an expert using the new OTA (Opentools API), but I can't seem to figure out how to start with it, nor can I seem to find a lot of information on the Web.  I've got the Delphi 5 Developers Guid which shows a very basic sample on how to create a MenuExpert, but actually I need to have more than that.

What I have to do is to be able to get notified when a component is dropped on a form or when it is renamed.  In that case I need to be able to check it's name according to our Company's Naming Conventions.

I have found a sample on the CodeCentral but the sample still uses the old way, and I should use the OTA (which is only for delphi 4 and 5).

I have been able to set up an IOTAIDENotifier which notifies me when a form / datamodule / report gets opened in the editer, but I can't seem to get any further than that.  I know that I should have something like a IOTAFormNotifier on which I could implement my code in the ComponentRenamed event, but I can't seem to figure out how to do this.

So, if anybody could help me out, or point me to some good resources concerning the OpenTools API, I would really appreciate it.  Anyone who can provide me with valuable information will be rewarded with some points.

Thanks a lot in advance,


Stefaan
0
Stefaan
Asked:
Stefaan
  • 15
  • 14
  • 2
  • +3
1 Solution
 
simonetCommented:
In the page below you'll find tons of info on the OpenTools API:

http://www.drbob42.com/articles/index.htm

BE sure to check the rest of Dr. Bob's site.

Yours,

Alex
0
 
LischkeCommented:
Hi Stefaan,

it's been quite a long time when I worked with IDE enhancements, so I'm for sure not up-to-date. Hence I shut up and listen...

Ciao, Mike
0
 
LischkeCommented:
Alex, curious to learn more I followed your link but I have not found anything related to OTA. Have you given the wrong link? Btw: Why have you proposed an answer? Are you sure one link is all what's necessary to answer this question? Wouldn't be a simple comment more appropriate?

Ciao, Mike
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
simonetCommented:
simonet changed the proposed answer to a comment
0
 
StefaanAuthor Commented:
Hi Alex,

Sorry to reject your answer, but Mike is right.  I knew about the DrBob site and the articles in there which explain how to write experts using the old way.  By the old way I mean using EXPTINTF.PAS, TOOLINTF.PAS, VIRTINTF.PAS which is in fact using TIExpert.

This isn't what I am looking for as I explained in my question.  I have a working version which uses the old method, but I need to have some more info on the New Opentools API which uses IOTANotifier objects and stuff like that.  This is the new way of doing it since Delphi 4 & 5 and that is what I am looking for.

I'm sorry if I wasn't very clear, but I'm looking for more info on the OTA which you can find in the ToolsAPI.pas file (not in the ExptIntfm ToolIntf, ...)

Best regards,


Stefaan
0
 
shenqwCommented:
Have you look at $(delphi)\Demos\ToolsAPI for some help?(D5)
0
 
hubdogCommented:
http://www.tempest-sw.com/opentools/
above is a good site about ota.maybe helpful for you

from the site you can get below info and etc.
>>IOTAFormNotifier
>>FormActivated and FormSaving are >>never called. Delphi calls >>ComponentRenamed when the user adds, >>renames, or deletes a component.

if you want to  make iotaformnotifier to work .please paste your source.then someone can get you out.

good luck
hubdog
0
 
StefaanAuthor Commented:
Hi,

I know I have to use the IOTAFormNotifier, but I can't seem to get it to work, nor do I know how to do it, and that is what I am looking for.

I got the IOTAIDENotifier working and I can intercept when a file has been opened in the IDE, but I think that from there I need to create my own IOTAFormNotifier, but I can't seem to figure out how to do it.

What I tried is this :

unit ComponentPrefixExpert;

interface

procedure Register;

implementation

uses Windows, SysUtils, Controls, Classes, ToolsAPI, Dialogs, Form_Test;

type
  TPPWIdeNotifier = class( TNotifierObject, IOTANotifier, IOTAIDENotifier)
    { IOTAIDENotifier }
    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;

  TPPWFormNotifier = class( TNotifierObject, IOTANotifier, IOTAFormNotifier)
    procedure FormActivated;
    procedure FormSaving;
    procedure ComponentRenamed(ComponentHandle: TOTAHandle;
      const OldName, NewName: string);
  end;
var
  Index : Integer;

procedure Register;
begin
  Index := (BorlandIDEServices as IOTAServices).AddNotifier(TPPWIdeNotifier.Create);
end;


{ TPPWIdeNotifier }

procedure TPPWIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
{}
end;

procedure TPPWIdeNotifier.BeforeCompile(const Project: IOTAProject;
  var Cancel: Boolean);
begin
{}
end;

procedure TPPWIdeNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
begin
  Case NotifyCode of
    ofnFileOpened :
      begin
        { I think that in here I will need to create an IOTAFormNotifier
          but I can't seem to get it working }
        ( BorlandIDEServices as IOTAEditor ). AddNotifier( TPPWFormNotifier.Create );
      end;
    ofnFileClosing :
      begin
        With TForm1.Create(TPPWIdeNotifier) do
        begin
          show;
        end;
      end;
  end;
  ShowMessage( 'File Opened : ' + FileName );
end;

{ TPPWFormNotifier }

procedure TPPWFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  const OldName, NewName: string);
begin
  { In here I will have to put my
    ComponentPrefix Code }
end;

procedure TPPWFormNotifier.FormActivated;
begin
{}
end;

procedure TPPWFormNotifier.FormSaving;
begin
{}
end;

initialization

finalization
  (BorlandIDEServices as IOTAServices).RemoveNotifier(Index);
end.


As you can see, I don't have pretty much yet, but I can't seem to find more information on this.

0
 
mscatenaCommented:
The best book for the Open Tools API is:

Hidden Paths of Delphi 3
by Ray Lischner

0
 
StefaanAuthor Commented:
Hi mscatena,

I know about the Hidden Paths of Delphi 3 book, but this book doens't cover the new Open Tools API which came with Delphi 4 and Delphi 5.

It seems to be very hard to find information about OTA and even harder to find some samples :-(
0
 
LischkeCommented:
But I have been successful! Look into $(Delphi)\Source\Property Editors\stfilsys.pas. They show there how to retrieve other interfaces from BorlandIDEServices (via QueryInterface or simply using "as"). This way you can get a module services interface (see also line 323 ff.) which you can use to access a form module interface. This interface returns then a form editor interface which you can use to register your form module notifier.

I hope this is finally what you are after :-)

Ciao, Mike
0
 
hubdogCommented:
yes,it can do
0
 
StefaanAuthor Commented:
Hi Folks,

Well thanks to Lischke's hints I was able to get my notifier working.  Now I have one more problem.  In the FileNotification I successfully created my FormNotifier if the opened file was a Form.  It works beatifully, but now I neet to be able to clean up things too, so I am looking for a way to remove my notifier in the FileNotification when the form is closed.

So what I am looking for is a way to find my notifier.  If anyone has any clues as to how I can achieve it.

I'll paste my source code so maybe it will be easier to help me.

unit ComponentPrefixExpert;

interface

procedure Register;

implementation

uses Windows, SysUtils, Controls, Classes, ToolsAPI, Dialogs;

type
  TPPWIdeNotifier = class( TNotifierObject, IOTANotifier, IOTAIDENotifier)
  private
    FFormNotifiers : TList;
  public
    constructor Create;
    destructor Destroy; override;
    { IOTAIDENotifier }
    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;

  TPPWFormNotifier = class( TNotifierObject, IOTANotifier, IOTAFormNotifier)
    constructor create;
    destructor destroy; override;
    procedure FormActivated;
    procedure FormSaving;
    procedure ComponentRenamed(ComponentHandle: TOTAHandle;
      const OldName, NewName: string);
  end;
var
  Index : Integer;

procedure Register;
begin
  Index := (BorlandIDEServices as IOTAServices).AddNotifier(TPPWIdeNotifier.Create);
end;


{ TPPWIdeNotifier }

procedure TPPWIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
{}
end;

procedure TPPWIdeNotifier.BeforeCompile(const Project: IOTAProject;
  var Cancel: Boolean);
begin
{}
end;

constructor TPPWIdeNotifier.Create;
begin
  Inherited Create;
  FFormNotifiers := TList.Create;
end;

destructor TPPWIdeNotifier.Destroy;
var
  I : Integer;
  P : TPPWFormNotifier;
begin
  ShowMessage (' Destroying TPPWIdeNotifier ');
  fFormNotifiers.Pack;
  for I := 0 to FFormNotifiers.count - 1 do
  begin
    p := TPPWFormNotifier(fFormNotifiers.Items[i]);
    if p <> nil then
    begin
      p.free;
    end;
  end;
  inherited Destroy;
end;

procedure TPPWIdeNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
var
  FormModule: IOTAModule;
  Editor    : IOTAEditor;
  FormEditor: IOTAFormEditor;

  tmpFormNotifier, FormNotifier : TPPWFormNotifier;
  FormNotifierI, I, J : Integer;
begin
  Case NotifyCode of
    ofnFileOpened :
      begin
        FormModule := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
        for i := 0 to FormModule.GetModuleFileCount - 1 do
        begin
          Editor := FormModule.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            FormNotifier := TPPWFormNotifier.Create;
            FFormNotifiers.Add( FormNotifier );
            FormNotifierI := FormEditor.AddNotifier ( FormNotifier );
            if FormNotifierI < 0 then
            begin
              FFormNotifiers.Delete( FFormNotifiers.IndexOf( FormNotifier ) );
              FormNotifier := nil;
            end
          end
          else
          begin
            ShowMessage (' No Form Notifier Found ');
          end;
        end;
      end;
    ofnFileClosing :
      begin
        FormModule := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
        for i := 0 to FormModule.GetModuleFileCount - 1 do
        begin
          Editor := FormModule.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            { Now I need to remove the notifier I added when the form was
              opened }
          end;
        end;
      end;
  end;
end;

{ TPPWFormNotifier }

procedure TPPWFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  const OldName, NewName: string);
begin
  Showmessage( 'Old Name : ' + OldName + chr( 13 ) +
               'New Name : ' + NewName );
  { In here I will have to put my
    ComponentPrefix Code }
end;

constructor TPPWFormNotifier.create;
begin
  inherited create;
  ShowMessage ('Form Notifier Created');
end;

destructor TPPWFormNotifier.destroy;
begin
  ShowMessage ('Form Notifier Destroyed');
  inherited Destroy;
end;

procedure TPPWFormNotifier.FormActivated;
begin
{}
end;

procedure TPPWFormNotifier.FormSaving;
begin
{}
end;

initialization

finalization
  (BorlandIDEServices as IOTAServices).RemoveNotifier(Index);
end.


Apparently the FormNotifiers arn't freed automatically when the form is closed, so that is why I'll need to do it myself.

If I get this one working, I can continue with the other stuff.

Thanks a lot in advance,


Stefaan
0
 
StefaanAuthor Commented:
Adjusted points from 100 to 200
0
 
LischkeCommented:
Hi Stefaan,

well the code looks quite good now. You implemented the installation exactly as I had done it. For removal, I don't see any other way than to store the file name along with the notifier index into a local list (a string list would be perfect here instead of the TList variable you already have). So the code is then:

procedure TPPWIdeNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
var
  FormModule: IOTAModule;
  Editor    : IOTAEditor;
  FormEditor: IOTAFormEditor;

  tmpFormNotifier, FormNotifier : TPPWFormNotifier;
  FormNotifierI, I, J : Integer;
begin
  Case NotifyCode of
    ofnFileOpened :
      begin
        FormModule := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
        for i := 0 to FormModule.GetModuleFileCount - 1 do
        begin
          Editor := FormModule.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            FormNotifier := TPPWFormNotifier.Create;
            FormNotifierI := FormEditor.AddNotifier ( FormNotifier );
            if FormNotifierI = -1 then FormNotifier := nil // decrement reference count to release interface
                                  else FFormNotifiers.AddObject(FormNotifier, Pointer(FormNotifierI);
          end;
        end;
      end;
    ofnFileClosing :
      // remove only if we have installed a notifier
      if FFormNotifiers.Find(FileName, FormNotifierI);
      begin
        FormModule := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);

        for i := 0 to FormModule.GetModuleFileCount - 1 do
        begin
          Editor := FormModule.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            FormEditor.RemoveNotifier ( FormNotifierI );
          end;
        end;
      end;
  end;
end;

Ciao, Mike
0
 
LischkeCommented:
Oops, sorry, there is one step missing:

ofnFileClosing :
      // remove only if we have installed a notifier
      if FFormNotifiers.Find(FileName, FormNotifierI);
      begin
        // here we only have the index into our local list, we need to retrive the notifier index now
        FormNotifierI := Integer(FFormNotifiers.Objects[FormNotifierI]);
        :


Ciao, Mike
0
 
StefaanAuthor Commented:
Mike,

I must thank you for your input on this matter.  I didn't have time to check the code yet, but when I find some free time I will check it and tell you if it works, and of course reward you for your effort ;-)

Or maybe if you find the time could you mail me the source code you have, since 4 eyes will discover more than 2 ;-)

If you want and have the time to do it, here is my mail Adress : Stefaan_Lesage@peopleware.be
0
 
LischkeCommented:
Stefaan, just take the time it needs to verify the code. Aside from this, I don't have any more sources here regarding your problem.

Ciao, Mike
0
 
StefaanAuthor Commented:
Mike,

Well, there is one more problem with the StringList option you suggested.  When a new form is created or an old form is opened, it's name will be stored in the stringlist.  But when the file is saved under a different name and then closed, the stringlist will contain an invalid filename.

I'll have to check if I can catch this too.

If it is OK for you, I will leave the question open till after the weekend.  You never know someone else has some more input on this matter.  You can be sure that you will be rewarded with an excellent grade for you input on the subject.

Best regards,


Stefaan
0
 
LischkeCommented:
Mmh, renaming the module is indeed an additonal problem. To handle this you need to add also an IOTAModuleNotifier which has a method ModuleRenamed. But this notifier needs access to the old name too to replace it in the string list. You will need to store the name in the module notifier. Btw: I have not explicitely mentioned it but the string list to hold the indices of the notifiers must of course be sorted.

Ciao, Mike
0
 
StefaanAuthor Commented:
Hi Mike,

Do you mean I'll have to add an extra notifier ? So I need to add an IOTAFormNotifier and an IOTAModuleNotifier, store both in the StringList, rename both in the ModuleRenamed and remove both when the form gets closed ? Or do I get something wrong here ?

Best regards,

Stefaan
0
 
LischkeCommented:
Well, I may be wrong, but that's the way I see it. You can at least save one rename operation by storing both notifiers into the same list. But you can also do it the other way around. Make a class which supports both interfaces (both notifiers types) and store the module name it belongs to with the class. Put all classes, as you did before, into a normal TList and do an iteration over all those stored notifiers for a particular name whenever you need it. Of course you need to store the indices of both notifiers in those classes too, in order to remove them later.

Ciao, Mike
0
 
StefaanAuthor Commented:
Hi,

Well, I think I almost got it working now.  I see that my notifiers get freed, but aparently sometimes they don't (can't seem to figure out why).

Also some times they don't get freed when closing a project, but they do when opening another project.

Well, I'll have a look at it during the weekend and I'll get back to you on monday.

Anyway, thanks for you input so far, you'll get rewarded soon ;-)

Greetings,


Stefaan


P.S. : Did you already do something with the OTA ? Since you know so much about it, I guess you do ;-0
0
 
LischkeCommented:
:-)) I don't need to write all code and test it. Often simply viewing it or thinking about it a while enlightens me. That happens when you work 10, 12 or sometimes 14 hours per day as programmer :-) And as I said, I dealt already with the old tools API...

Ciao, Mike
0
 
StefaanAuthor Commented:
Hi Mike,

As I told you in the previous comment, it is almost working perfectly, though the notifiers sometimes get freed and sometimes they don't.

I'll paste the full source code in here, and if you have the time to look at it, maybe you can tell me if I'm doing something wrong.  If you don't see anything wrong, then I'll consider this question closed and grade your comments.

Thanks a lot for your help,

Stefaan


unit ComponentPrefixExpert;

interface

procedure Register;

implementation

uses Windows, SysUtils, Controls, Classes, ToolsAPI, Dialogs;

type
  TPPWIdeNotifier = class( TNotifierObject, IOTANotifier, IOTAIDENotifier)
  public
    constructor Create;
    destructor Destroy; override;
    { IOTAIDENotifier }
    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;

  TPPWFormNotifier = class( TNotifierObject, IOTANotifier, IOTAFormNotifier )
  private
    FFileName : String;
  public
    constructor Create( FileName : String );
    destructor Destroy; override;
    procedure FormActivated;
    procedure FormSaving;
    procedure ComponentRenamed(ComponentHandle: TOTAHandle;
      const OldName, NewName: string);
    { IOTAModuleNotifier }
  end;

  TPPWModuleNotifier = class( TNotifierObject, IOTANotifier, IOTAModuleNotifier )
  private
    FOldFileName : String;
    FFileName    : String;
  public
    constructor Create (Const FileName : String);
    destructor destroy; override;
    { IOTAModuleNotifier }
    function CheckOverwrite : Boolean;
    procedure ModuleRenamed(const NewName: string);
  end;

var
  Index : Integer;
  FNotifierList : TStringList;

procedure Register;
begin
  Index := (BorlandIDEServices as IOTAServices).AddNotifier(TPPWIdeNotifier.Create);
end;


{ TPPWIdeNotifier }

procedure TPPWIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
end;

procedure TPPWIdeNotifier.BeforeCompile(const Project: IOTAProject;
  var Cancel: Boolean);
begin
end;

constructor TPPWIdeNotifier.Create;
begin
  Inherited Create;
  FNotifierList := TStringList.Create;
  FNotifierList.Sorted := True;
end;

destructor TPPWIdeNotifier.Destroy;
begin
  ShowMessage (' Destroying TPPWIdeNotifier ');
  ShowMessage (' Notifiers Left : ' + inttostr(FNotifierList.Count));
  inherited Destroy;
end;

procedure TPPWIdeNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
var
  Module         : IOTAModule;
  Editor         : IOTAEditor;
  FormEditor     : IOTAFormEditor;
  ModuleNotifier : TPPWModuleNotifier;
  FormNotifier   : TPPWFormNotifier;

  IModuleNotifier : Integer;
  FormNotifierI, ListI, I : Integer;
begin
  Case NotifyCode of
    ofnFileOpened :
    begin
      { Get the IOTAModule associated to this File }
      Module := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
      { Loop over the number of associated File }
      for i := 0 to Module.GetModuleFileCount - 1 do
      begin
        { Get the FileEditor }
        Editor := Module.GetModuleFileEditor(i);
        if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
        begin
          { If the File Editor is a FormEditor then Add Our Notifier }
          FormNotifier := TPPWFormNotifier.Create( FileName );
          FormNotifierI := FormEditor.AddNotifier ( FormNotifier );
          if FormNotifierI < 0 then
          begin
            FormNotifier.Free;
          end
          else
          begin
            ShowMessage( 'Notifier Index' + inttostr( FormNotifierI ));
            FNotifierList.AddObject(FileName, Pointer(FormNotifierI));
          end;
          { Also Add a module Notifier }
          ModuleNotifier := TPPWModuleNotifier.Create( FileName );
          IModuleNotifier := Module.AddNotifier( ModuleNotifier );
          if IModuleNotifier < 0 then
          begin
            ModuleNotifier.Free;
          end;
        end
      end;
    end;
    ofnFileClosing :
    begin
      if FNotifierList.Find(FileName, ListI) then
      begin
        Module := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
        FormNotifierI := Integer(FNotifierList.Objects[ListI]);
        for i := 0 to Module.GetModuleFileCount - 1 do
        begin
          Editor := Module.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            FormEditor.RemoveNotifier ( FormNotifierI );
            FNotifierList.Delete(ListI);
          end;
        end;
      end;
    end;
  end;
end;

{ TPPWFormNotifier }

procedure TPPWFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  const OldName, NewName: string);
begin
  Showmessage( 'Old Name : ' + OldName + chr( 13 ) +
               'New Name : ' + NewName );
  { In here I will have to put my
    ComponentPrefix Code }
end;

constructor TPPWFormNotifier.Create( FileName : String );
begin
  inherited Create;
  FFileName := FileName;
  ShowMessage ('Form Notifier Created for File : ' + FileName );
end;

destructor TPPWFormNotifier.destroy;
begin
  ShowMessage ('Form Notifier Destroyed for File : ' + FFileName );
  inherited Destroy;
end;

procedure TPPWFormNotifier.FormActivated;
begin
{}
end;

procedure TPPWFormNotifier.FormSaving;
begin
{}
end;

{ TPPWModuleNotifier }

function TPPWModuleNotifier.CheckOverwrite: Boolean;
begin
  Result := True;
end;

constructor TPPWModuleNotifier.Create(const FileName: String);
begin
  inherited Create;
  FOldFileName := FileName;
  FFileName := FileName;
  ShowMessage ('Module Notifier Created for file : ' + FOldFileName);
end;

destructor TPPWModuleNotifier.destroy;
begin
  ShowMessage ('Module Notifier Destroyed for file : ' + FFileName);
  inherited;
end;

procedure TPPWModuleNotifier.ModuleRenamed(const NewName: string);
var
  ListI         : Integer;
  FormNotifierI : Integer;
begin
  if FNotifierList.Find(FOldFileName, ListI) then
  begin
    FormNotifierI := Integer(FNotifierList.Objects[ListI]);
    FNotifierList.Delete(ListI);
    FNotifierList.AddObject(NewName, Pointer(FormNotifierI));
  end;
  FOldFileName := NewName;
  FFileName := NewName;
  inherited ;
end;

initialization

finalization
  (BorlandIDEServices as IOTAServices).RemoveNotifier(Index);
end.
0
 
LischkeCommented:
Stefaan,

oooooh, you made a big mistake :-) Never use global variables and create them in classes which can be instantiated several times! Create the notifier list only once and let the notifiers work on it, like:

unit ComponentPrefixExpert;

interface

procedure Register;

implementation

uses Windows, SysUtils, Controls, Classes, ToolsAPI, Dialogs;

type
  TPPWIdeNotifier = class( TNotifierObject, IOTANotifier, IOTAIDENotifier)
  public
    constructor Create;
    destructor Destroy; override;
    { IOTAIDENotifier }
    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;

  TPPWFormNotifier = class( TNotifierObject, IOTANotifier, IOTAFormNotifier )
  private
    FFileName : String;
  public
    constructor Create( FileName : String );
    destructor Destroy; override;
    procedure FormActivated;
    procedure FormSaving;
    procedure ComponentRenamed(ComponentHandle: TOTAHandle;
      const OldName, NewName: string);
    { IOTAModuleNotifier }
  end;

  TPPWModuleNotifier = class( TNotifierObject, IOTANotifier, IOTAModuleNotifier )
  private
    FOldFileName : String;
    FFileName    : String;
  public
    constructor Create (Const FileName : String);
    destructor destroy; override;
    { IOTAModuleNotifier }
    function CheckOverwrite : Boolean;
    procedure ModuleRenamed(const NewName: string);
  end;

var
  Index : Integer;
  NotifierList : TStringList;

procedure Register;
begin
  Index := (BorlandIDEServices as IOTAServices).AddNotifier(TPPWIdeNotifier.Create);
end;


{ TPPWIdeNotifier }

procedure TPPWIdeNotifier.AfterCompile(Succeeded: Boolean);
begin
end;

procedure TPPWIdeNotifier.BeforeCompile(const Project: IOTAProject;
  var Cancel: Boolean);
begin
end;

constructor TPPWIdeNotifier.Create;
begin
  Inherited Create;
  ShowMessage ('TPPWIdeNotifier created');
end;

destructor TPPWIdeNotifier.Destroy;
begin
  ShowMessage (' Destroying TPPWIdeNotifier ');
  ShowMessage (' Notifiers Left : ' + inttostr(NotifierList.Count));
  inherited Destroy;
end;

procedure TPPWIdeNotifier.FileNotification(
  NotifyCode: TOTAFileNotification; const FileName: string;
  var Cancel: Boolean);
var
  Module         : IOTAModule;
  Editor         : IOTAEditor;
  FormEditor     : IOTAFormEditor;
  ModuleNotifier : TPPWModuleNotifier;
  FormNotifier   : TPPWFormNotifier;

  IModuleNotifier : Integer;
  FormNotifierI, ListI, I : Integer;
begin
  Case NotifyCode of
    ofnFileOpened :
    begin
      { Get the IOTAModule associated to this File }
      Module := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
      { Loop over the number of associated File }
      for i := 0 to Module.GetModuleFileCount - 1 do
      begin
        { Get the FileEditor }
        Editor := Module.GetModuleFileEditor(i);
        if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
        begin
          { If the File Editor is a FormEditor then Add Our Notifier }
          FormNotifier := TPPWFormNotifier.Create( FileName );
          FormNotifierI := FormEditor.AddNotifier ( FormNotifier );
          if FormNotifierI < 0 then
          begin
            FormNotifier.Free;
          end
          else
          begin
            ShowMessage( 'Notifier Index' + inttostr( FormNotifierI ));
            NotifierList.AddObject(FileName, Pointer(FormNotifierI));
          end;
          { Also Add a module Notifier }
          ModuleNotifier := TPPWModuleNotifier.Create( FileName );
          IModuleNotifier := Module.AddNotifier( ModuleNotifier );
          if IModuleNotifier < 0 then
          begin
            ModuleNotifier.Free;
          end;
        end
      end;
    end;
    ofnFileClosing :
    begin
      if NotifierList.Find(FileName, ListI) then
      begin
        Module := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FileName);
        FormNotifierI := Integer(NotifierList.Objects[ListI]);
        for i := 0 to Module.GetModuleFileCount - 1 do
        begin
          Editor := Module.GetModuleFileEditor(i);
          if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
          begin
            FormEditor.RemoveNotifier ( FormNotifierI );
            NotifierList.Delete(ListI);
          end;
        end;
      end;
    end;
  end;
end;

{ TPPWFormNotifier }

procedure TPPWFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  const OldName, NewName: string);
begin
  Showmessage( 'Old Name : ' + OldName + chr( 13 ) +
               'New Name : ' + NewName );
  { In here I will have to put my
    ComponentPrefix Code }
end;

constructor TPPWFormNotifier.Create( FileName : String );
begin
  inherited Create;
  FFileName := FileName;
  ShowMessage ('Form Notifier Created for File : ' + FileName );
end;

destructor TPPWFormNotifier.destroy;
begin
  ShowMessage ('Form Notifier Destroyed for File : ' + FFileName );
  inherited Destroy;
end;

procedure TPPWFormNotifier.FormActivated;
begin
{}
end;

procedure TPPWFormNotifier.FormSaving;
begin
{}
end;

{ TPPWModuleNotifier }

function TPPWModuleNotifier.CheckOverwrite: Boolean;
begin
  Result := True;
end;

constructor TPPWModuleNotifier.Create(const FileName: String);
begin
  inherited Create;
  FOldFileName := FileName;
  FFileName := FileName;
  ShowMessage ('Module Notifier Created for file : ' + FOldFileName);
end;

destructor TPPWModuleNotifier.destroy;
begin
  ShowMessage ('Module Notifier Destroyed for file : ' + FFileName);
  inherited;
end;

procedure TPPWModuleNotifier.ModuleRenamed(const NewName: string);
var
  ListI         : Integer;
  FormNotifierI : Integer;
begin
  if NotifierList.Find(FOldFileName, ListI) then
  begin
    FormNotifierI := Integer(NotifierList.Objects[ListI]);
    NotifierList.Delete(ListI);
    NotifierList.AddObject(NewName, Pointer(FormNotifierI));
  end;
  FOldFileName := NewName;
  FFileName := NewName;
  inherited ;
end;

initialization
  NotifierList := TStringList.Create;
  NotifierList.Sorted := True;

finalization
  (BorlandIDEServices as IOTAServices).RemoveNotifier(Index);
  NotifierList.Free;
end.


Ciao, Mike
0
 
LischkeCommented:
Additionally, while playing with the stuff: You should remove all remaining notifier entries when the IDE notifier is destroyed (e.g. when a package is compiled) otherwise you will get really ugly AVs with no point of return. The best way, though, is to let all notifiers remove itself if not already done on their destruction.

Ciao, Mike
0
 
StefaanAuthor Commented:
Adjusted points from 200 to 300
0
 
StefaanAuthor Commented:
Mike,

Since you've helped me quite a bit on this matter, I'm awarding you with 100 extra points and an Excellent grade.

It is almost working perfectly now.  There is still one little problem which occurs when you pick the Close All menu item from withing Delphi.  In the old tools API it was possible to react on the fnProjectClosing but apparently in the new OTA this has disappeared.  I'm trying to solve this now.

If you may find any references to the OTA or good samples, I would appreciate it if you could inform me about that.

Thanks a lot for you excellent input.

Stefaan
0
 
LischkeCommented:
Thank you for the extra points :-) I'm glad that I was of help. I learned some things too and refreshed my knowledge about the tools API... That's the best synergy effect I can imagine from E-E!

I'll let you know when I find online resources about OTA. Please do the same to me if you have gotten a link....

Ciao, Mike
0
 
StefaanAuthor Commented:
I surely will.  If i get my Expert up and running, I'll send you the source code.
0
 
LischkeCommented:
Okay.
0
 
StefaanAuthor Commented:
Hi Mike,

Well, I'm very close to my initial goal, but I seem to have an additional problem.  In my FormNotifier's ComponentRenamed method, I would like to prefix the component with our prefixes, but it doesn't seem to work very well.

If you have some time and are willing to give me some more hints, could you take a look at it and help me some more ? I'll give you some extra points if you want.

Best regards,


Stefaan
0
 
LischkeCommented:
Oh dear, while looking for the implementation we've lost the initial goal. Of course, with the code above you cannot change the name of the component (strings are passed as constant paremters in ComponentRenamed).

A new question for this problem would be fine, but to get you started I present you what I found:

procedure TPPWFormNotifier.ComponentRenamed(ComponentHandle: TOTAHandle;
  const OldName, NewName: string);
var
  Module         : IOTAModule;
  Editor         : IOTAEditor;
  FormEditor     : IOTAFormEditor;
  OTAComponent: IOTAComponent;
  Component: IComponent;
  I: Integer;
 
begin
  if not FRenaming then
  try
    FRenaming := True;
    Module := ( BorlandIDEServices as IOTAModuleServices ).FindModule(FFileName);
    for i := 0 to Module.GetModuleFileCount - 1 do
    begin
      Editor := Module.GetModuleFileEditor(i);
      if Editor.QueryInterface( IOTAFormEditor, FormEditor ) = S_OK then
      begin
        OTAComponent := FormEditor.FindComponent(OldName);
        if OTAComponent = nil then ShowMessage('OTAComponent not found.')
                              else
        begin
          Component := OTAComponent.GetIComponent;
          if Component = nil then ShowMessage('IComponent not found')
                             else Component.Name := 'Mike' + NewName;
        end;
      end
    end;
  finally
    FRenaming := False;
  end;
end;


This does not work as you want but shows the way to go. What it does is rather funny. Say you have a button Button1 and rename it to Button2 then the code above will create a second entry in the source code like:

type
  TMyForm = class(TForm)
    Button2: TButton;
    MikeButton2: TButton;
    :

without a true second component. While saving the form this additional entry is seen as having no corresponding component and will be removed. My assumption is to put the prefix code somewhere else, but no into the ComponentRenamed event. Perhaps a timer or separate thread with an event would do the job.

Ciao, Mike
0
 
StefaanAuthor Commented:
Hi Lischke,

Sorry to have disturbed you, you are right about the problems that occur, but I've already found the solution.

You shouldn't rename te component in the ComponentRenamed method.  Better is to create a procedure RenameComponent and then in the Modified method call the RenameComponent method.  Well, this works as a beauty.

The OTA part seems to be finished and working rather well, now I can concentrate on a property editor, prefixing routines ... (still some work as you see).

Thanks, and sorry for disturbing you.


Stefaan
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 15
  • 14
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now