Enhancement for the Sort editor expert

 Delphi, GExperts  Comments Off on Enhancement for the Sort editor expert
Apr 232016
 

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;

Sort-Selected-Lines-Expert

 Posted by on 2016-04-23 at 19:31

FixInsight vs. GExperts

 Delphi, GExperts  Comments Off on FixInsight vs. GExperts
Apr 232016
 

Roman Yankovsky has been so kind to donate a FixInsight license to my open source projects, in particular to GExperts.

And since he just blogged about running FixInsight against the latest FMX I did the same with GExperts.

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.

 Posted by on 2016-04-23 at 15:51

Enabling a form while another form is being shown modally

 Delphi  Comments Off on Enabling a form while another form is being shown modally
Apr 092016
 

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.

Delphi-Ide-Explorer-with-modal-form

(Click on the picture to see an animation how it works.)

Today, I remembered that I had asked a question on StackOverflow a few days ago: Why do modal Delphi forms not receive WM_SYSCOMMAND when the user clicks the task bar button?

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.

EDIT:
As Ondrej Kelle pointed out in his comment on my Google+ post:

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!

 Posted by on 2016-04-09 at 20:27

Do not enumerate on TTreeNode.Item

 Delphi  Comments Off on Do not enumerate on TTreeNode.Item
Apr 092016
 

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.

 Posted by on 2016-04-09 at 15:32

More enhancements for the search path dialog

 Delphi, GExperts  Comments Off on More enhancements for the search path dialog
Apr 022016
 

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:

GExperts-PageControl-in-SearchPathEdit-Small
(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.

 Posted by on 2016-04-02 at 20:12