This is an update to the second article Notify Me of Everything… – Part 2 . While I was fixing some performance bugs in the plug-in I thought it would be good to fix the issue of renamed modules at the same time.

One of the things I’ve been doing recently when revisiting code and refactoring it, is to use interface s to decouple the code. So I looked at the code I had written for the management of the notifier indexes against filenames and decided that that code should be separated out into a class of its own so that it can be reused. With that in mind I delcared the following interface for the class.

Type IDINModuleNotifierList = Interface [ '{60E0D688-F529-4798-A06C-C283F800B7FE}' ] Procedure Add ( Const strFileName : String ; Const iIndex : Integer ) ; Function Remove ( Const strFileName : String ) : Integer ; Procedure Rename ( Const strOldFileName , strNewFileName : String ) ; End ;

With the above interface I can then create a new class which encapsulates the generic collection I was using before. I’ve also added to the class the record type which described the data to be stored as the methods of the interface expose all the data required. I have also moved across the Find method. Although it uses a sequential search, the dataset should be small and its not called too often to require a binary search function. The below also allows me to have one of these collection for Modules and another for Projects. This also provides me a useful class to reuse later on when I try and implement other module based notifiers.

Type TDINModuleNotifierList = Class ( TInterfacedObject , IDINModuleNotifierList ) Strict Private Type TModuleNotifierRec = Record Strict Private FFileName : String ; FNotifierIndex : Integer ; Public Constructor Create ( Const strFileName : String ; Const iIndex : Integer ) ; Property FileName : String Read FFileName Write FFileName ; Property NotifierIndex : Integer Read FNotifierIndex ; End ; Strict Private FModuleNotifierList : TList < TModuleNotifierRec > ; {$IFDEF D2010} Strict {$ENDIF} Protected Procedure Add ( Const strFileName : String ; Const iIndex : Integer ) ; Function Remove ( Const strFileName : String ) : Integer ; Procedure Rename ( Const strOldFileName : String ; Const strNewFileName : String ) ; Function Find ( Const strFileName : String ; Var iIndex : Integer ) : Boolean ; Public Constructor Create ; Destructor Destroy ; Override ; End ;

The record has the following simple constructor just to simplify the creaton and initialise the record.

Constructor TDINModuleNotifierList . TModuleNotifierRec . Create ( Const strFileName : String ; Const iIndex : Integer ) ; Begin FFileName := strFileName ; FNotifierIndex := iIndex ; End ;

The Add method of the class constructs a new record with the filename of the module and the notifier index and adds it to the end of the generic collection.

Procedure TDINModuleNotifierList . Add ( Const strFileName : String ; Const iIndex : Integer ) ; Begin FModuleNotifierList . Add ( TModuleNotifierRec . Create ( strFileName , iIndex ) ) ; End ;

The constructor for the class simply creates the generic collection to hold the filename index pairs.

Constructor TDINModuleNotifierList . Create ; Begin FModuleNotifierList := TList < TModuleNotifierRec > . Create ; End ;

The destructor deletes any remaining record in the collection before freeing the memory used by the collection.

Destructor TDINModuleNotifierList . Destroy ; Var iModule : Integer ; Begin For iModule := FModuleNotifierList . Count - 1 DownTo 0 Do Begin FModuleNotifierList . Delete ( iModule ) ; End ; FModuleNotifierList . Free ; Inherited Destroy ; End ;

The Find method, used to search for a record with a specific filename, uses a simple linear search. The collections shouldn’t be too large and the method should only be called for closing modules and renaming modules, so infrequently.

Function TDINModuleNotifierList . Find ( Const strFileName : String ; Var iIndex : Integer ) : Boolean ; Var iModNotIdx : Integer ; R : TModuleNotifierRec ; Begin Result := False ; iIndex := - 1 ; For iModNotIdx := 0 To FModuleNotifierList . Count - 1 Do Begin R := FModuleNotifierList . Items [ iModNotIdx ] ; If CompareText ( R . FileName , strFileName ) = 0 Then Begin iIndex := iModNotIdx ; Result := True ; Break ; End ; End ; End ;

The Remove method searches for the corresponding record and if found deletes the record from the collection and returns the notifier index which should be used to remove the notifier from the IDE.

Function TDINModuleNotifierList . Remove ( Const strFileName : String ) : Integer ; Var iModuleIndex : Integer ; R : TModuleNotifierRec ; Begin Result := - 1 ; If Find ( strFileName , iModuleIndex ) Then Begin R := FModuleNotifierList [ iModuleIndex ] ; Result := R . NotifierIndex ; FModuleNotifierList . Delete ( iModuleIndex ) ; End ; End ;

The Rename method is very similar to the remove method however after finding the corresponding record it updates the notifier filename.

Procedure TDINModuleNotifierList . Rename ( Const strOldFileName , strNewFileName : String ) ; Var iIndex : Integer ; R : TModuleNotifierRec ; Begin If Find ( strOldFileName , iIndex ) Then Begin R := FModuleNotifierList [ iIndex ] ; R . FileName := strNewFileName ; FModuleNotifierList [ iIndex ] := R ; End ; End ;

In order for the module and project notifiers to be able to update the filename when they change they need a reference to the collection containing their filenames and notifier indexes. Here is where the interface comes into play and which is passed to the notifier in a modified constructor.

Type TDNModuleNotifier = Class ( TDGHNotifierObject , IOTAModuleNotifier , IOTAModuleNotifier80 , IOTAModuleNotifier90 ) Strict Private FModuleNotiferList : IDINModuleNotifierList ; {$IFDEF D2010} Strict {$ENDIF} Protected Function CheckOverwrite : Boolean ; Procedure ModuleRenamed ( Const NewName : String ) ; Function AllowSave : Boolean ; Function GetOverwriteFileNameCount : Integer ; Function GetOverwriteFileName ( Index : Integer ) : String ; Procedure SetSaveFileName ( Const FileName : String ) ; Procedure BeforeRename ( Const OldFileName , NewFileName : String ) ; Procedure AfterRename ( Const OldFileName , NewFileName : String ) ; Property RenameModule : IDINModuleNotifierList Read FModuleNotiferList ; Public Constructor Create ( Const strNotifier , strFileName : String ; Const iNotification : TDGHIDENotification ; Const RenameModule : IDINModuleNotifierList ) ; Reintroduce ; Overload ; End ;

The revised constructor is as follows:

Constructor TDNModuleNotifier . Create ( Const strNotifier , strFileName : String ; Const iNotification : TDGHIDENotification ; Const RenameModule : IDINModuleNotifierList ) ; Begin Inherited Create ( strNotifier , strFileName , iNotification ) ; FModuleNotiferList := RenameModule ; End ;

Finally back in the IDE notifier the original references to the generic collection are replaced with two references: one for the modules notifiers and another for the project notifiers.

Type TDGHNotificationsIDENotifier = Class ( TDGHNotifierObject , IOTAIDENotifier , IOTAIDENotifier50 , IOTAIDENotifier80 ) Strict Private FModuleNotifiers : IDINModuleNotifierList ; FProjectNotifiers : IDINModuleNotifierList ; {$IFDEF D2010} Strict {$ENDIF} Protected Procedure FileNotification ( NotifyCode : TOTAFileNotification ; Const FileName : String ; Var Cancel : Boolean ) ; Procedure BeforeCompile ( Const Project : IOTAProject ; Var Cancel : Boolean ) ; Overload ; Procedure AfterCompile ( Succeeded : Boolean ) ; Overload ; Procedure BeforeCompile ( Const Project : IOTAProject ; IsCodeInsight : Boolean ; Var Cancel : Boolean ) ; Overload ; Procedure AfterCompile ( Succeeded : Boolean ; IsCodeInsight : Boolean ) ; Overload ; Procedure AfterCompile ( Const Project : IOTAProject ; Succeeded : Boolean ; IsCodeInsight : Boolean ) ; Overload ; Public Constructor Create ( Const strNotifier , strFileName : String ; Const iNotification : TDGHIDENotification ) ; Override ; Destructor Destroy ; Override ; End ;

The FileNotification() method is now a little more straightward as follows:

Procedure TDGHNotificationsIDENotifier . FileNotification ( NotifyCode : TOTAFileNotification ; Const FileName : String ; Var Cancel : Boolean ) ; Const strNotifyCode : Array [ Low ( TOTAFileNotification ) .. High ( TOTAFileNotification ) ] Of String = ( 'ofnFileOpening' , 'ofnFileOpened' , 'ofnFileClosing' , 'ofnDefaultDesktopLoad' , 'ofnDefaultDesktopSave' , 'ofnProjectDesktopLoad' , 'ofnProjectDesktopSave' , 'ofnPackageInstalled' , 'ofnPackageUninstalled' , 'ofnActiveProjectChanged' {$IFDEF DXE80} , 'ofnProjectOpenedFromTemplate' {$ENDIF} ) ; Var MS : IOTAModuleServices ; M : IOTAModule ; P : IOTAProject ; MN : TDNModuleNotifier ; C : IDINModuleNotifierList ; iIndex : Integer ; Begin DoNotification ( Format ( '.FileNotification = NotifyCode: %s, FileName: %s, Cancel: %s' , [ strNotifyCode [ NotifyCode ] , ExtractFileName ( FileName ) , strBoolean [ Cancel ] ] ) ) ; If Not Cancel And Supports ( BorlandIDEServices , IOTAModuleServices , MS ) Then Case NotifyCode Of ofnFileOpened : Begin M := MS . OpenModule ( FileName ) ; If Supports ( M , IOTAProject , P ) Then Begin MN := TDNProjectNotifier . Create ( 'IOTAProjectNotifier' , FileName , dinProjectNotifier , FProjectNotifiers ) ; FProjectNotifiers . Add ( FileName , M . AddNotifier ( MN ) ) ; End Else Begin MN := TDNModuleNotifier . Create ( 'IOTAModuleNotifier' , FileName , dinModuleNotifier , FModuleNotifiers ) ; FModuleNotifiers . Add ( FileName , M . AddNotifier ( MN ) ) ; End ; End ; ofnFileClosing : Begin M := MS . OpenModule ( FileName ) ; If Supports ( M , IOTAProject , P ) Then C := FProjectNotifiers Else C := FModuleNotifiers ; iIndex := C . Remove ( FileName ) ; If iIndex > - 1 Then M . RemoveNotifier ( iIndex ) ; End ; End ; End ;

Hopefully all of the the above is straightforward. The new code can be found with the updated plug-in on its page IDE Notifications.