Apr 232017
 

Two weeks ago I added the Donations for GExperts page. A few days later I received the first money through PayPal. Thanks for that!

I have now used that credit for donations to other open source software that I use daily:

I had always wanted to donate to those projects but never came around actually doing that. So, having some ready to use credits on PayPal made me finally do it.

Apr 152017
 

If you want to say thank you for my work on GExperts, there are the following options:

Contribute to the GExperts quality
You can either contribute good bug reports or – even better – bug fixes to GExperts. I’d actually prefer that over getting money. Good bug reports include the Delphi version (and language), the Windows version, the GExperts version, the settings used and steps to reliably reproduce the problem. Screen shots might also help. Use the bug report form on Sourceforge for that.

You can do even more by taking over the maintenance of GExperts for a particular Delphi version. I am currently using Delphi 2007 for most of my work, with a bit of Delphi XE2 and the latest version sprinkled in. So most of the other versions don’t really get tested. If you regularly use e.g. Delphi 6, 7, 2005, 2006, 2009 or 2010, why not take over testing and possibly bug fixing GExperts for these versions? For now, I still have working installations of these Delphi versions, but I am not sure that I will go to much effort to keep them working in the future. If one of them breaks, I might decide to drop GExperts support for them. By taking on the responsibility for them, you ensure they will still be around in a few years.

Contribute new features to GExperts
Contributing features to GExperts involves more than just suggestions (even though I am open to these too, please use the feature requests form page on SourceForge for that.)

It’s also not only about implementing them and sending me patches. It’s about thinking about how to integrate them into the existing code and UI. If you want to contribute code, please feel free to contact me.

Donate money
If you can’t do any of the above, you can donate money. I suggest the following amounts:

  • 50 Euros per year for private use via Flattr this or Paypal donate
  • 100 Euros per developer per year for commercial use via Flattr this or Paypal donate

It’s up to you of course. If you think it’s worth more, I definitely won’t complain, but if you can’t afford these, smaller amounts are also OK.

Mar 192017
 

A while ago I stopped using the formatter branch and officially took over the trunk of the GExperts repository. When I announced that on G+, Stefan Glienke asked me about my plans for GExperts.

My answer today is still the same as back then:

I will release new GExperts versions when I feel like it. And I will continue to call them experimental. There will be no rigorous testing, since nobody has volunteered to take on that responsibility. I will be using the latest development version in my daily work, so for now you can assume that at least the Delphi 2007 and XE2 versions are reasonably stable. This will change once I move on to newer versions, but unfortunately I don’t see that in the near future.

So, if you don’t like this, I would welcome volunteers to step forward and assume the responsibility of testing GExperts preferably with the Delphi versions they use every day. Your reward will be a place in the contributor list and possibly the pain of working with a not quite stable IDE.

Mar 192017
 

Some people have started pestering me about making a new release (you know how you are!). Don’t think that this has done anything to actually make me do it, I simply thought it to be the right time with Delphi 10.2 Tokyo apparently right around the corner.

I have created installers for all supported Delphi versions.

I don’t remember all the new features and bug fixes that went into this release, but I am pretty sure it is more stable and has more features than the previous one.

But anyway:

Please be aware that I mostly work with Delphi 2007, so this version can be regarded as tested quite well, followed by Delphi XE2. The others are only known to compile and new features are usually tested superficially with all versions. This is particularly true for Delphi 6/7 and 2005/2006.

Head over to the Experimental GExperts page to download the latest release it.

Display and edit keyboard macros with GExperts

 Delphi, GExperts  Comments Off on Display and edit keyboard macros with GExperts
Mar 112017
 

The GExperts Macro Library expert can now display and edit keyboard macros that have been recorded in the Delphi IDE.

It uses the information I described in my article on Interpreting Delphi IDE Keyboard Macros.

You get to this dialog via the context menu of a macro.

It allows you not only to display and edit text.

But also special keys.

And, on top of that, it works with Unicode, in Unicode aware IDEs (>= Delphi 2005).

(The above are Arabic characters, but I have no idea whether it means anything. I just typed a few keys.)

Just in case you are interested in these two keyboard macros:

ReadOnly Property
Read Write Property

Save these files anywhere and load them into the Macro Library expert.

They are supposed to generate a property for a class’ field: Copy the field definition from the private section to the public section. Position the cursor on the first character and start the macro.

Interpreting Delphi IDE Keyboard Macros (Updated)

 Delphi, GExperts, Uncategorized  Comments Off on Interpreting Delphi IDE Keyboard Macros (Updated)
Mar 042017
 

Keyboard macros have been part of the Delphi IDE since basically forever (I remember using them in Delphi 5 but I wouldn’t rule it out that they already existed in Delphi 1 which I never used.)

GExperts also has had the Macro Library Expert since I know about it to overcome the Delphi IDE’s shortcoming of having only one keyboard macro.

This expert handles the macros as a black box, storing them in a binary stream which is used to get them from and pass them to the IDE and to save and load them from files.

So far I haven’t seen any documentation of the format these macros are stored in. That’s a real shame because it prevented the Macro Library Expert to become more useful, by allowing to edit existing macros.

I have had a look at data stored in its configuration files and tried to interpret them. Here is what I found:

A keyboard macro is a stream of bytes. It always seems to start with the four bytes 54 50 45 52 (ASCII “TPOR”, no idea what it means. TP looks suspiciously like Turbo Pascal). This seems to be a kind of signature. I have not tried to modify it to see what the IDE makes of it, I just want to understand the actual macros. This signature is followed by a single byte that is either 00 or 01. 00 seems to mark the end of the macro. 01 is followed by 4 bytes, which seem to belong together as two separate words, e.g. 61 00 00 00, and describe a key press. Each such 4 byte sequence is again followed by a single byte that is either 00 or 01. 00 again, marks the end of the macro, 01 means that another another key press follows etc.

54 50 54 52
01  61 00  00 00
00

would be a keyboard macro that simply types the lower case letter “a”.

54 50 54 52
01  61 00  00 00
01  62 00  00 00
00

would be “ab”.

Let’s have a closer look at the four bytes describing a key press:

The first word describes the main key, the second word describes modifier keys. The modifier word can have the following values:

  • 00 00 – meaning the main key is a normal character
  • 88 00 – meaning the main key is a special key like the arrow keys or insert / del / page up/down etc.
  • 10 00 added to it means that the Ctrl key is being pressed
  • 20 00 added to it means that the Shift key is being pressed
  • 40 00 added to it means that the Alt key is being pressed

I have not seen any other values.

Please keep in mind that I represent the bytes in the order they show up in the stream. When interpreting two consecutive bytes as words, you have to switch them around according to Intel convention, so the bytes 10 00 actually mean the the number $0010, and 88 00 is the number $0088.

If the modifier word is $0000, the main key is in its simplest form the ASCII code of a character, e.g. like you can see in GExperts ASCII Chart:

I have yet to check whether the value is actually a WideChar.
update: It definitely is a WideChar, so the code below won’t work correctly for input that uses the high byte of the character.

If the modifier value is <> $0000 it describes a special key, e.g.
$0020 is the Space key, $0021 is PgUp, $0022 is PgDn, $002D is Ins key, $002E is Del etc.

update: The above applies to all Delphi versions that I checked. That includes Delphi 6, 7, 2005, 2006, 2007 and 10.1. Note that Delphi 6 and 7 apparently do not support Unicode characters in keyboard macros. I tried to enter some Arabic characters (Unicode $30xx) and they were converted to characters in $00xx range.

Based on my observations I have so far written the following code to interpret a keyboard macro:

type
  TMenuKeyCap = (
    mkcBkSp, mkcTab, mkcEsc, mkcEnter, mkcSpace, mkcPgUp,
    mkcPgDn, mkcEnd, mkcHome, mkcLeft, mkcUp, mkcRight,
    mkcDown, mkcIns, mkcDel, mkcShift, mkcCtrl, mkcAlt);

var // these resource strings are declared in unit Consts
  MenuKeyCaps: array[TMenuKeyCap] of string = (
    SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp,
    SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight,
    SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt);

function KeyCodeToText(Code: Word; Modifier: Word): string;
var
  LoByte: Byte;
begin
  Result := Format('unkown (%.4x)', [Code]);
  if (Modifier and $88) <> 0 then begin
    // special keys
    Result := '';
    if (Modifier and $10) <> 0 then
      Result := Result + MenuKeyCaps[mkcCtrl];
    if (Modifier and $40) <> 0 then
      Result := Result + MenuKeyCaps[mkcShift];
    if (Modifier and $20) <> 0 then
      Result := Result + MenuKeyCaps[mkcAlt];
    LoByte := (Code and $FF);
    case LoByte of
    // $00..$07
      $08, $09: // backspace / tab
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte - $08)];
      $0D: Result := Result + MenuKeyCaps[mkcEnter];
      $1B: Result := Result + MenuKeyCaps[mkcEsc];
    // $1B..$1F ?
      $20..$28: // space and various special characters
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + LoByte - $20)];
    // $29..$2C ?
      $2D..$2E: // Ins, Del
        Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + LoByte - $2D)];
    // $2F ?
      $30..$39: // 0..9
        Result := Result + Chr(LoByte - $30 + Ord('0'));
      $41..$5A: // A..Z
        Result := Result + Chr(LoByte - $41 + Ord('A'));
      $60..$69: Result := Result + Chr(LoByte - $60 + Ord('0'));
      $70..$87: Result := Result + 'F' + IntToStr(LoByte - $6F);
    end;
  end else begin
    LoByte := (Code and $FF);
    case LoByte of
      // $00..$07
      $08, $09: // backspace / tab
        Result := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte - $08)];
      $0D: Result := MenuKeyCaps[mkcEnter];
      $1B: Result := MenuKeyCaps[mkcEsc];
      // $1B..$1F ?
      $20..$7E: Result := Chr(LoByte);
      $7F: Result := MenuKeyCaps[mkcDel];
      $80..$FF: Result := Chr(LoByte);
    end;
  end;
end;

function TMacroInfo.TryDecode(AStrings: TStrings): Boolean;

  function Read(var Buffer; Count: Longint): Boolean;
  begin
    Result := (FStream.Read(Buffer, Count) = Count);
  end;

var
  Magic: Longword;
  Flag: Byte;
  HiWord: Word;
  LoWord: Word;
  s: string;
begin
  Result := False;
  FStream.Position := 0;
  if not Read(Magic, SizeOf(Magic)) or (Magic <> $524F5054) then
    Exit;
  if not Read(Flag, SizeOf(Flag)) then
    Exit;
  while Flag = $01 do begin
    if not Read(LoWord, SizeOf(LoWord)) then
      Exit;
    if not Read(HiWord, SizeOf(HiWord)) then
      Exit;
      s := KeyCodeToText(LoWord, HiWord);
    AStrings.Add(s);
    if not Read(Flag, SizeOf(Flag)) then
      Exit;
  end;
  Result := True;
end;

It’s not finished and might never be. It’s also far from perfect. I am posting this here in the hope that it might be useful to somebody. I might use it to extend the Macro Library Expert in GExperts to allow limited editing of keyboard macros.

Fixed HideNavBar functionality in GExperts

 Delphi, GExperts, Uncategorized  Comments Off on Fixed HideNavBar functionality in GExperts
Feb 262017
 

When Embarcadero added the Navigation Toolbar to the Delphi code editor in Delphi 10 there were a few people who didn’t like it because it took up some more of the vertical screen space. But there was no option to disable it.

Achim Kalwa wrote an expert to hide this toolbar and contributed the code, which I integrated into GExperts. Unfortunately it didn’t work reliably. The Navigation Toolbar came back whenever one opened a new edit window and went away again when switching between tabs, creating an annoying flicker.

Today I investigated the issue and found that apparently the IDE sets the control’s visible property to true whenever it opens a new editor tab. The fix was actually quite simple: Create a new panel, set its Parent to the toolbar’s original parent control and set the toolbar’s parent to this new panel. So it ends up between the toolbar and its parent. Then set this panel to be invisible instead of the toolbar. Since the IDE doesn’t know about this new panel, it does not change its visibility. Voila, problem solved.

Of course it was a bit more involved because I had to size the panel correctly and also make sure that I don’t insert a new panel every time I check for the visibility. But once I had the general principle it was just a matter of fine tuning the solution.

  if TryFindComponentByName(Ctrl, 'TEditorNavigationToolbar', C) then begin
    Ctrl := TWinControl(C);
    ParentCtrl := Ctrl.Parent;
    if Assigned(ParentCtrl) and (ParentCtrl is TPanel) and (ParentCtrl.Name = GX_HideNavbarPanel) then
      pnl := TPanel(ParentCtrl)
    else begin
      pnl := TPanel.Create(ParentCtrl);
      pnl.Parent := ParentCtrl;
      pnl.Name := GX_HideNavbarPanel;
      pnl.Align := alTop;
      pnl.BevelOuter := bvNone;
      pnl.Height := ctrl.Height;
      Ctrl.Parent := pnl;
    end;
    pnl.Visible := FIsNavbarVisible;
    pnl.Enabled := FIsNavbarVisible;
    if FIsNavbarVisible then
      Ctrl.Visible := True;
    Result := True;
  end;

I also found, that in Delphi 10.1 Berlin there is already an option to show or hide the Navigation Toolbar (Tools -> Options -> Editor Options -> Display), so I removed the functionality from GExperts for Delphi 10.1 again.

GExperts formatter branch is dead, long live the trunk

 Delphi, GExperts, Uncategorized  Comments Off on GExperts formatter branch is dead, long live the trunk
Feb 252017
 

As of today, I have stopped developing GExperts in the formatter branch and switched to the trunk of the repository.

If you want to get the current sources, take them from

svn co https://svn.code.sf.net/p/gexperts/code/trunk GExperts

I have updated the Compiling GExperts article accordingly.

Some more form enhancements in GExperts

 Delphi, GExperts, Uncategorized  Comments Off on Some more form enhancements in GExperts
Feb 122017
 

Prompted by a post from +Attila Kovacs I have added the menu designer form (TMenuBuilder) to the list of forms which GExperts enhances. In this case, it only stores the size and optionally the position of the form.

And since I was at it, I also added several other forms:

  • TActionListDesigner
  • TFieldsEditor – used for TDataset and descendants
  • TDBGridColumnsEditor
  • TConnEditForm – the one for TAdoConnection, required to modify some controls. There is a form with the same name used for TSqlConnection, that is already sizeable.
  • TDriverSettingsForm – I don’t remember where it is used
  • TPakComponentsDlg – this is the one which shows the component list for a package, required to modify some controls

The last one pointed out a problem with the GExperts code: For whatever reason, the Delphi IDE does not destroy the form when it closes, but the form’s window handle changes every time. This messed up the code which was supposed to keep track of forms that were already managed because it compared the form’s ClassName and window handle. Since the handle changed it ended up setting the OnDestroy handler every time, saving the old handler for calling and restoring later. Unfortunately the “old handler” was GExperts’ own replacement handler for the second and all further instances, so it called itself. Boom instant stack overflow.

So I ended up completely rewriting the code. It no longer maintains a list of all managed forms. Instead, the TManagedForm class now descends from TComponent and is added to the form it enhances. This makes the code much cleaner. It also allowed me to have descendants for forms, that require special handling to become sizeable. These now have got a MakeComponentsResizable method that looks up the components on the form and changes their Anchor property so they move and size sensibly when the form gets resized.

Today I finally finished that code and found some more forms that could benefit from being sizeable:

  • TImageListEditor – Delphi 6 only, it is already sizeable in Delphi 7 and later
  • TPictureEditDlg – which is surprisingly not sizeable even in Delphi 10.1 Berlin

And last but not least, I fixed a bug in the Environment Options form of Delphi 6 and 7 where the Environment page was not correctly modified.

Anybody interested in adding stuff from JEDI Experts?

 Delphi, GExperts, Uncategorized  Comments Off on Anybody interested in adding stuff from JEDI Experts?
Feb 052017
 

A feature request for GExperts mentions a tool called JEDI Experts which is a project on SourceForge which has been inactive since Delphi 6 times. The description reads as follows:

JEDI Experts is set of experts/wizards to be used in Delphi IDE. While they can be used directly in Delphi IDE, the main task will be convert them, and merge into GExperts – another Delphi project on SourceForge: http://sourceforge.net/projects/gexperts

It also links to a more descriptive page which unfortunately still doesn’t say much about the actual functionality.

I got curious, downloaded the sources and tried to compile them. It was quite difficult because it requires the JCL/JVCL and many of the components it used are have been deprecated. I ended up converting the dfm files to text (Yes, it is that old.) and replacing these components in a text editor.

JEDI experts consists of a DLL and a package, the latter is apparently supposed to load the DLL into the IDE. I got the DLL to compile but eventually gave up on the package. It references the deprecated ToolsAPI units from the Delphi 5 days and after deleting/replacing lots of ifdefs to get it to even compile in Delphi 2007 I gave up.

But I didn’t want to let those hours of work I already put into it got to waste. So I have put the sources into the GExperts repository, adding a new branch for it:

https://svn.code.sf.net/p/gexperts/code/branches/JExperts/trunk

If you check out this directory, it will automatically also get the parts of the GExperts sources it requires.

The actual JEDI Experts sources are in the subdirectory JExperts. The DLL project is in “Library”, the Package project in “Package”.

There is also the ZIP file containing the original sources as downloaded from the JEDI Experts SourceForge project page.

So, if anybody is interested to pick up where I gave up, you are welcome. There might be some functionality there which could be integrated into GExperts. Much of it is duplicated though.

%d bloggers like this: