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.

Debugging helper for TDataSet

 Delphi, Uncategorized  Comments Off on Debugging helper for TDataSet
Feb 152017
 

I like programming in Delphi, but I don’t particularly like writing applications that work with database backends, but sometimes I just can’t avoid it (have been working on one for several weeks now, and it wasn’t too bad really). But one thing that can drive me nuts is when you have got an error, want to investigate the cause and it’s a bloody nuisance to try and get the value of the current record in a dataset. So, finally I wrote this little helper unit which I want to share with you.

All it does is export one public function TDataset_Dump which is meant to be called from the debugger’s “Evaluate and Modify” window:

TDataset_Dump(SomeDataset)

If called like this, it will magically open a text file in the associated application and display the content of the current record of the dataset you passed to it.

Alternatively you can let it dump the whole dataset like this:

TDataset_Dump(SomeDataset, -1)

Or all remaining records in the dataset (until EOF) like this:

TDataset_Dump(SomeDataset, 0)

Or you can pass it a number of records it should dump:

TDataset_Dump(SomeDataset, 5)

For this to work, you need to add the unit u_dzDatasetDump to the unit with the dataset you want to debug, otherwise the debugger won’t know about the function.

The unit is part of my dzlib library. It needs a few units from it (I’m a lazy bastard ™, so since I use the library in most of my programs I didn’t see the point of trying to avoid dependencies, but it should’t be too difficult to remove them.).

OK, so here goes the unit:

///<summary>
/// Add this unit only for debug purposes.
/// It exports TDataset_Dump for dumping a dataset from the evaluate and modify dialog
/// of the Delphi debugger. </summary>
unit u_dzDatasetDump;

interface

uses
  SysUtils,
  Classes,
  DB;

///<summary>
/// @param Count:  1 -> Dump the current record only (default)
///               -1 -> Dump the whole dataset
///                0 -> Dump from the current record until EOF
///               >1 -> Dump a maximum of n records starting from the current
/// @returns the file name the data was written to </summary>
function TDataset_Dump(_ds: TDataset; _Count: Integer = 1): string;

implementation

uses
  u_dzLineBuilder,
  u_dzVariantUtils,
  u_dzFileUtils,
  u_dzExecutor,
  u_dzOsUtils;

const
  DUMP_ALL = -1;

procedure TDataset_DumpHeaders(_ds: TDataset; _sl: TStrings);
var
  lb: TLineBuilder;
  i: Integer;
  fld: TField;
begin
  lb := TLineBuilder.Create;
  try
    for i := 0 to _ds.FieldCount - 1 do begin
      fld := _ds.Fields[i];
      lb.Add(fld.FieldName);
    end;
    _sl.Add(lb.Content)
  finally
    FreeAndNil(lb);
  end;
end;

procedure TDataset_DumpCurrent(_ds: TDataset; _sl: TStrings);
var
  lb: TLineBuilder;
  i: Integer;
  fld: TField;
begin
  lb := TLineBuilder.Create;
  try
    for i := 0 to _ds.FieldCount - 1 do begin
      fld := _ds.Fields[i];
      lb.Add(Var2Str(fld.Value));
    end;
    _sl.Add(lb.Content)
  finally
    FreeAndNil(lb);
  end;
end;

procedure TDataset_DumpToEof(_ds: TDataset; _sl: TStrings);
begin
  while not _ds.Eof do begin
    TDataset_DumpCurrent(_ds, _sl);
    _ds.Next;
  end;
end;

procedure TDataset_DumpAll(_ds: TDataset; _sl: TStrings);
begin
  _ds.First;
  TDataset_DumpToEof(_ds, _sl);
end;

function TDataset_Dump(_ds: TDataset; _Count: Integer = 1): string;
var
  sl: TStringList;
  bm: Pointer;
begin
  Result := '';
  sl := TStringList.Create;
  try
    if not Assigned(_ds) then begin
      sl.Add('<dataset not assigned>');
      if _Count = -1 then begin
        // do nothing this is a dummy call to fool the linker
        Exit; //==>
      end;
    end else if not _ds.Active then begin
      sl.Add('<dataset not active>');
    end else if _ds.IsEmpty then begin
      sl.Add('<dataset is empty>');
    end else begin
      bm := nil;
      _ds.DisableControls;
      try
        bm := _ds.GetBookmark;
        TDataset_DumpHeaders(_ds, sl);
        if _Count = DUMP_ALL then begin
          TDataset_DumpAll(_ds, sl);
        end else begin
          if _ds.Eof then begin
            sl.Add('<dataset is at eof>');
          end else if _Count = 0 then begin
            TDataset_DumpToEof(_ds, sl);
          end else begin
            while (not _ds.Eof) and (_Count > 0) do begin
              TDataset_DumpCurrent(_ds, sl);
              _ds.Next;
              Dec(_Count);
            end;
          end;
        end;
      finally
        _ds.GotoBookmark(bm);
        _ds.EnableControls;
      end;
    end;
    if Assigned(_ds) then
      Result := _ds.Name;
    if Result = '' then
      Result := 'NONAME';
    Result := itpd(TFileSystem.GetTempPath) + 'dump_of_' + Result + '.txt';
    sl.SaveToFile(Result);
    OpenFileWithAssociatedApp(Result, True);
  finally
    FreeAndNil(sl);
  end;
end;

initialization
  // Make sure the linker doesn't eliminate the function
  TDataset_Dump(nil, -1);
end.

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.

Enhanced Goto dialog enhancement

 Delphi, GExperts, Uncategorized  Comments Off on Enhanced Goto dialog enhancement
Feb 042017
 

The Goto dialog IDE enhancement of GExperts got itself an enhancement:

It now also displays “Package”, “Requires” and “Contains” in the list.

Also, all IDE dialog enhancement classes now derive from a new TIdeDialogEnhancer class which provides some basic functionality that previously was duplicated.

New IDE enhancement for the Application settings

 Delphi, GExperts, Uncategorized  Comments Off on New IDE enhancement for the Application settings
Feb 042017
 

GExperts now has got a very small enhancement for the Application tab in the project settings dialog of the Delphi IDE:

A new button that sets the lib suffix to the default value corresponding to the IDE version which is showing this dialog (example: Delphi 2007 -> 110, Delphi 10.1 -> 240).

This is only really useful for those who develop packages for multiple Delphi versions and like me always forget, which suffix to use for which Delphi version.

Of course it would be nice to have that set automatically for new package projects. Anybody who wants to contribute code for that?

WordPress update broke ssl

 blog, Uncategorized  Comments Off on WordPress update broke ssl
Feb 032017
 

My hoster has updated my WordPress installation to the latest version (and broke it for several days). What they also did was disable my option to set the site address and wordpress address to https rather than http. So, now even though the site is still available through https://blog.dummzeuch.de it now longer automatically forces https connections. Thanks a lot. 🙁

What’s even worse: It reverses to plain http sometimes for no reason I can determine. e.g. I have been writing this post through http because I didn’t notice that until now. Thanks even more. :-((

GExperts Sort Selected Lines Expert now uses “natural” sort order

 Delphi, GExperts, Uncategorized  Comments Off on GExperts Sort Selected Lines Expert now uses “natural” sort order
Feb 032017
 

Achim Kalwa, who has been steadily contributing to GExperts, submitted a patch that changes the sort order used by the “Sort Selected Lines” Expert in GExperts to use the natural sort order, similar to the way Windows Explorer sorts files. E.g. Assume you want to sort the following:

  Label20
  Label1
  Label10
  Label2
  Label100
  Label3
  Label11

The old code sorted it like this:

  Label1
  Label10
  Label100
  Label11
  Label2
  Label20
  Label3

The new code does this:

  Label1
  Label2
  Label3
  Label10
  Label11
  Label20
  Label100

I have accepted this patch as is. Not sure whether there should be an option to switch between the new and the old sort order.

Using the adler32 checksum in Delphi 2007

 Delphi, Uncategorized  Comments Off on Using the adler32 checksum in Delphi 2007
Feb 012017
 

Delphi 2007 comes with the RTL unit zlib.pas which links to adler32.obj. adler32.obj is a precompiled object file implementing the Adler-32 checksum (most likely implemented in c).

Delphi apparently doesn’t actually use that function since it is simply declared as …

procedure adler32; external;

… which is plain wrong but gets the job done of forcing the compiler / linker to actually include that object file so it is available for the zlib implementation which itself is another .obj file.

Later Delphi versions declare this function as …

function adler32(adler: LongWord; buf: PByte; len: Cardinal): LongWord; cdecl;

… which matches the declaration in the official zlib documentation.

Trying to simply use that object file in Delphi 2007 like this …

Unit u_Adler32;

function adler32(adler: LongWord; buf: PByte; len: Cardinal): LongWord; cdecl;

implementation

{$L adler32.obj} // you need to copy the obj file to the same directory as this unit

function adler32; external;

… results in a compiler error

[DCC Error] u_Adler32.pas(72): E2065 Unsatisfied forward or external declaration: '_llmod'

Fortunately I am not the first one to stumble upon this problem. Arnaud Bouchez with this answer on StackOverflow helped me, to declare the missing functions:

procedure _llmod;
asm
  jmp System.@_llmod
end;

procedure free(P: Pointer); cdecl; { always cdecl }
begin
  FreeMem(P);
end;

function malloc(size: cardinal): Pointer; cdecl; { always cdecl }
begin
  GetMem(Result, size);
end;

So, now it compiles. But does it actually work?

Let’s try the Wikipedia example:

var
  s: AnsiString;
  adler: LongWord;
begin
  s := 'Wikipedia';
  adler := adler32(0, nil, 0);
  adler := adler32(adler, PByte(@s[1]), Length(s));
  WriteLn(adler);

This prints 300286872 which is exactly what the example states, so it can’t be too wrong.

Now, we need some adler-32 checksums for known files to do some more tests or an implementation that is known to work.

I found Adler 32 Hash at conversion-tool.com and let it create checksums for a few texts and files and the result always matched, so now I am fairly certain the above works.

Finding a Windows tool for calculating Adler-32 turned out to be a challenge. I downloaded several, let them virus check on VirusTotal and they came up as containting viruses or trojans. Even though only one virus scanner out of 44 claimed that, I didn’t want to take any risk. I ended up with rehash by Dominik Reichl, which passed the virus scan. The Adler-32 checksums calculated by that tool also matched the above results.

But don’t just take my word for it, do your own tests!

%d bloggers like this: