Delphi, GExpertsComments Off on Enhancement for the Sort editor expert
Apr232016
One of the editor experts in GExperts that come in handy once in a while is the Sort Expert. All it does is take the selected lines and sort them alphabetically.
Now, it can sort ascending, as before, descending (not sure when you might want to do that, but since I was at it, why not implement it?) and it can reverse the current line order. The latter is useful, if you want to undo changes in reverse order.
In addition, a new option allows to sort procedures / functions by name, ignoring the procedure / function prefix of the line, so
function c: integer;
procedure b;
function a: integer;
can be sorted like this:
function a: integer;
procedure b;
function c: integer;
rather than this:
function a: integer;
function c: integer;
procedure b;
The result is not too bad actually. All included there are 235 warnings, optimization and convention messages. Most messages are about
Fields, that are not prefixed with F
Local Variables that hide class fields/methods/properties
const missing for unmodified string parameters
empty then blocks and empty procedures
for variables that are not used in the loop (all of them legit)
There are some warnings about variables assigned twice successively, but these are actually assignments to properties with side effects (e.g. TTimer.Enable being set to false and then to true to restart it).
The overall code quality of GExperts is very good compared to other source code I have seen.
I’ll go through all those messages and clean them up if possible.
Delphi, DelphiIdeExplorerComments Off on Enabling a form while another form is being shown modally
Apr092016
There was one shortcoming in my Delphi IDE explorer that has irked me since the beginning: It was disabled while a modal dialog was shown in the IDE, so there was no way to inspect the controls on the current dialog. The option to follow the focus ameliorated this a bit because it was now possible to click on the various controls in the dialog and see their properties, but still: There was no way to inspect non-clickable controls and how they were related to other controls on the form or to switch the different pages showing the properties, events, hierarchy or parents of the current control.
(Click on the picture to see an animation how it works.)
In his answer David Heffernan mentioned the Windows API function EnableWindow and that it is being used by the VCL to disable all windows of an application when one of them is shown modally (and re-enable them afterwards). He explicitly warned me to meddle with that “The consequences are somewhat dire.”.
But being who I am, could of course not resist trying it anyway.
When I asked that question it was about a program at work that was showing a modal progress form which I wanted to allow to minimize the application and restore it when the user clicks on the task bar button. It did not work because the WM_SYSCOMMAND message never reached the program while the modal form was active. I worked around this by re-enabling the main window when the application was minimized (so it would receive the WM_SYSCOMMAND message again), and disabling it again once it got restored. It worked, and so far I have not seen any problems with that approach (but that might still be in store for me).
Today, I used a similar approach to enable the IDE explorer window while a modal form is active in the IDE. My first try was a TTimer that every second called EnabledWindow(Self.Handle, True). While this worked, I thought it might be a bit excessive to call that API every second, so I looked for a better place to put this code. How can a plugin be notified when a modal dialog is being shown? It turned out to be quite easy: Hook Screen.OnActiveFormChange, something I have done before.
And lo and behold: It works and again I have not yet experienced any problems with that approach.
Watching for WM_ENABLE with wParam = 0 in your explorer form, checking if Application.ModalLevel > 0 (meaning a ShowModal call is currently executing), and re-enabling yourself might work, too, and you could avoid the Screen.OnActiveFormChange event hook. (Just an idea.)
He even provided an implementation for this:
/// [...]
interface
/// [...]
type
TForm1 = class(TForm)
/// [...]
private
procedure WMEnable(var _Msg: TWMEnable); message WM_ENABLE;
/// [...]
end;
/// [...]
implementation
/// [...]
procedure TExplorerForm.WMEnable(var _Msg: TWMEnable);
begin
inherited;
if not _Msg.Enabled and (Application.ModalLevel > 0) then
EnableWindow(Self.Handle, True);
end;
And yes, it works. One hook less is good. Thanks Ondrey!
DelphiComments Off on Do not enumerate on TTreeNode.Item
Apr092016
Note to self: Do not enumerate on TTreeNode.Item, it’s highly inefficient.
Consider this code:
procedure SelectFocusedControl(_ActCtrl: TWinControl; _Parent: TTreeNode);
var
j: Integer;
CtrlItem: TTreeNode;
begin
for j := 0 to _Parent.Count - 1 do begin
CtrlItem := _Parent.Item[j];
if CtrlItem.Data = _ActCtrl then begin
CtrlItem.Selected := true;
break;
end else
SelectFocusedControl(_ActCtrl, CtrlItem);
end;
end;
This code is from my Delphi IDE Explorer expert (a bit simplified for readability).
It does something very simple: It goes through all child nodes of the given Parent TTreeNode and looks for one whose Data property matches the given ActCrl parameter. This node then gets selected.
I always wondered why this takes so long until I had a look at the implementation of TTreeNode.GetItem today:
function TTreeNode.GetItem(Index: Integer): TTreeNode;
begin
Result := GetFirstChild;
while (Result <> nil) and (Index > 0) do
begin
Result := GetNextChild(Result);
Dec(Index);
end;
if Result = nil then TreeViewError(Format(SListIndexError, [Index]));
end;
This is the current implementation in Delphi 10 Seattle.
As you can see, in order to get the Index’ed item, it enumerates through all child items counting them until reaching the given Index. And since my code calls it for each item, this is highly inefficient.
Now, consider this alternative code:
procedure SelectFocusedControl(_ActCtrl: TWinControl; _Parent: TTreeNode);
var
CtrlItem: TTreeNode;
begin
CtrlItem := _Parent.getFirstChild;
while Assigned(CtrlItem) do begin
if CtrlItem.Data = _ActCtrl then begin
CtrlItem.Selected := true;
break;
end else
SelectFocusedControl(_ActCtrl, CtrlItem);
CtrlItem := CtrlItem.getNextSibling;
end;
end;
This uses basically the same code as TTreeNode.GetItem, so it only loops through the items once. Much faster.
EDIT:
I deleted the rest of this post regarding TTreeNodesEnumerator. It was clearly wrong.
Delphi, GExpertsComments Off on More enhancements for the search path dialog
Apr022016
After I found and fixed the problem with the Grep results dialog I returned to enhancing the search path dialog. Last time I added an option to replace the ListBox on that dialog with a Memo. Many people liked that change but of course, people being people, they started to complain about missing features. In particular the option to easily spot invalid paths and remove them with a single button click was sorely missed. I even found myself missing that ListBox sometimes so I thought: Why not have both, the ListBox and the Memo on the form? It turned out to be both, easier and more complex than I thought, but here it is, the new enhanced search path dialog:
(Click on the image to get an animated introduction to the new features.)
So, what’s new?
There is now a tab below the list that switches between the ListBox and the Memo.
When switching between them, the current line is preserved.
When the ListBox is active, all buttons work as they used to.
The Up and Down buttons now have a keyboard shortcut (Ctrl+Up / Ctrl+Down) and work in both, the ListBox and the Memo.
Dropping directories now works on all three, the ListBox, the Memo and the Edit control. It’s also now possible to drop multiple directories
The OK Button now has a hotkey (Something that irked my since forever, but I always forgot to add it.)
There isn’t a new release yet, so in order to get these goodies, you’ll have to check out the sources and compile GExperts yourself.