Solved

Open Tools API information needed

Posted on 2000-05-03
35
952 Views
Last Modified: 2011-10-03
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
Comment
Question by:Stefaan
  • 15
  • 14
  • 2
  • +3
35 Comments
 
LVL 15

Expert Comment

by:simonet
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 15

Expert Comment

by:simonet
Comment Utility
simonet changed the proposed answer to a comment
0
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 3

Expert Comment

by:shenqw
Comment Utility
Have you look at $(delphi)\Demos\ToolsAPI for some help?(D5)
0
 
LVL 2

Expert Comment

by:hubdog
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 1

Expert Comment

by:mscatena
Comment Utility
The best book for the Open Tools API is:

Hidden Paths of Delphi 3
by Ray Lischner

0
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 2

Expert Comment

by:hubdog
Comment Utility
yes,it can do
0
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
Adjusted points from 100 to 200
0
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
:-)) 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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Accepted Solution

by:
Lischke earned 300 total points
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
Adjusted points from 200 to 300
0
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
I surely will.  If i get my Expert up and running, I'll send you the source code.
0
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
Okay.
0
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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
 
LVL 10

Expert Comment

by:Lischke
Comment Utility
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
 
LVL 3

Author Comment

by:Stefaan
Comment Utility
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

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now