Overview
So in my last post I said there were a few things to fixed and / or enhance. In this post I’ll explain how I fixed two issues.
The two issues I’ve fixed are stoppping the timer which closes the Message View if a re-compilation occurs (very simple and I should have done this in the first place) and only closing the Message View if the RAD Studio Desktop remains the same as when the timer was started.
The Fixes
Stopping the Timer on Recompilation
This fix couldn’t be simpler and I really should have done this in the first place. To stop the timer on a recompilation I’ve added a call to disable the timer in the Compile Notifier’s ProjectGroupCompilationStarted
method as below.
Procedure TMVHCompileNotifier.ProjectGroupCompileStarted(Mode: TOTACompileMode); Begin FTimer.Enabled := False; End;
Finding the Name of the Active Desktop
The next bug was a little more involved. What was happening before I fixed this is that if you debug an application your RAD Studio Desktop changes (usually) and the Message View in the debugging Desktop was being hidden NOT the one in your main Desktop. The behaviour I wanted is that the main RAD Studio Desktop is hidden but to do that I needed to track which Desktop was active. A search of the Open Tools API files didn’t provide any interfaces I could use so I was back to manipulating the IDE directly as before. After a quick look around the IDE I found what I was lookng for.
In the above image (from my Delphi IDE Explorer) I found a custom combo box in the main application form called cbDesktop
which as shown above contained my main RAD Studio Desktop name in the Text
property. So my first thoughts on solving this problem were to access the combo box directly. Now you will notice above that the combo box is a TDesktopComboBox
, a component we don’t have access to therefore I cannot cast or assign the reference in order to access the Text
property. So I thought I would access the selected item using the TCustomComboBox
reference and the components Items
and ItemIndex
properties.
Before I can do that I needed another function in my application to find a component on a form by name. Below is a simple function that takes a form and a name and returns the component reference if the component with the given name is found on the given form.
Function FindComponent(Form : TForm; strComponentName : String) : TComponent; Var iComponent: Integer; Begin Result := Nil; For iComponent := 0 To Form.ComponentCount - 1 Do If CompareText(Form.Components[iComponent].Name, strComponentName) = 0 Then Begin Result := Form.Components[iComponent]; Break; End; End;
Using the above function I tried the following code to get the current desktop name.
Function CurrentDesktopName : String; Var F: TForm; C: TComponent; CB: TCustomComboBox; Begin Result := '(not found)'; F := FindForm('AppBuilder'); If Assigned(F) Then Begin C := FindComponent(F, 'cbDesktop'); If Assigned(C) And (C Is TCustomComboBox) Then Begin CB := C As TCustomComboBox; Result := CB.Items[CB.ItemIndex]; End; End; End;
You would think that all is well? Well no. I found during testing (outputting the desktop name to the CodeSite Live Viewer) that in some instances the ItemIndex
of the combo box was -1
yet on checking the properties of the desktop dropdown with my Delphi IDE Explorer the Text
property was correct. So I needed another solution.
Since I was targetting RAD Studio 2010 and above and my Delphi IDE Explorer had already found the information I needed I though that I would use the new RTTI capabilities in RAD Studio 2010 and above. If you’ve not used RTTI before then I would suggested you create a test project and have a little play as its very powerful and really quite easy to use.
So I redefined my function as below (I’ll walk you thought it below):
Function CurrentDesktopName : String; Var F: TForm; C: TComponent; CB: TCustomComboBox; Ctx: TRttiContext; T: TRttiType; P: TRttiProperty; V: TValue; Begin Result := '(not found)'; F := FindForm('AppBuilder'); If Assigned(F) Then Begin C := FindComponent(F, 'cbDesktop'); If Assigned(C) And (C Is TCustomComboBox) Then Begin CB := C As TCustomComboBox; Ctx := TRTTIContext.Create; // Create context Try T := Ctx.GetType(CB.ClassType); // Get a type P := T.GetProperty('Text'); // Get the Text Property If Assigned(P) Then Begin V := P.GetValue(CB); // Get the value of the Text property Result := V.AsString; // Return the text value a a string End; Finally Ctx.Free; // Free context End; End; End; End;
The first thing to do if you want to use RTTI is you must create a TRTTIContext
in which you can access the RTTI attributes of an object. Next we need to get the type of the object we are referring to with a call to the context’s GetType
method which takes the objects class type. Once we have this we can access the object’s fields, methods and properties. In this case we just need a specific property so I call the type’s GetProperty
method with the name of the property I want. Now that I have the property reference I can call the property’s GetValue
method passing the instance of the object to get the value of the property in a TValue
record. All we need to do now is call the value’s AsString
method to get the property’s string information and return it from the function. You need to make sure that you free the context you’ve created else you will have a memory leak.
So with the above function in place my compile notifier method ProjectGroupCompileFinished
gets an additional line of code to store the name of the RAD Studio Desktop in a field at the time of compilation as follows:
Procedure TMVHCompileNotifier.ProjectGroupCompileFinished(Result: TOTACompileResult); Begin If (Result = crOTASucceeded) And (mvhoEnabled In TMVHOptions.MVHOptions.Options) Then Begin FTimer.Interval := TMVHOptions.MVHOptions.HideMessageViewDelay; FDesktopName := CurrentDesktopName; FTimer.Enabled := True; End; End;
Also the timer event changes to ensure the Message View is only closed when the desktop is the same as when the compilation started.
Procedure TMVHCompileNotifier.CloseMessageView(Sender: TObject); Begin If CompareText(FDesktopName, CurrentDesktopName) = 0 Then Begin FTimer.Enabled := False; HideMessageView; End; End;
I hope the above gives people further ideas on how to achieve interesting stuff in the RAD Studio IDE but just remember that you’re mucking about with the actual IDE and the names and locations of element of the IDE might change in different releases so check all the versions of RAD Studio you are going to support.
Downloads
The compiled BPLs and source code for this plug-in can be found on the web page Message View Helper which contains links to a downloadable ZIP file with the BPLs and source code or a GitHub link to the source code.