If you compiled your own GExperts clear the Uses Clause Manager cache

 Delphi, GExperts  Comments Off on If you compiled your own GExperts clear the Uses Clause Manager cache
Nov 192019
 

This is only relevant, if you have recently compiled your own GExperts DLL and you use the Uses Clause Manager’s Identifier tab:

There were several bugs in the unit parsing code that have been fixed. But you won’t see the effect, because the buggy results from before that have been cached. In order to get the benefit of these bugfixes, you must clear the Uses Clause Manager’s cache.

To do that,

  1. Go to GExperts -> Configuration
  2. On the Experts tab, enter “uses” and press Alt+C
  3. Press the “Clear Cache” button

Yes, I know, that I should make a new release soon, but I keep finding old and sometimes new bugs and then there are people who submit patches. It’s difficult to determine when to make a release. But hey, you have got the source code and the compiler, so why not compile your own DLL?

 Posted by on 2019-11-19 at 17:48

Creating an array of controls in Delphi

 Delphi  Comments Off on Creating an array of controls in Delphi
Nov 172019
 

One frequently asked question that still gets asked today goes like this: “How do I create an array of [component] and fill it with existing [component] instances from the form?” Where [component] usually is TLabel, TCheckbox or TEdit.

I’m going to outline some solutions here.

Let’s start by defining some parameters:

  1. We have got a (VCL) form
  2. On that form there are several controls of the same type. Let’s make them CheckBoxes.
  3. We want to do something with all these controls
  4. In order to make this easier, we want to create an array that contains all these controls

So, this is the form:

The form declaratin looks like this:

type
  TForm1 = class(TForm)
    grp_1: TGroupBox;
    chk_1: TCheckBox;
    chk_2: TCheckBox;
    chk_3: TCheckBox;
    grp_2: TGroupBox;
    chk_4: TCheckBox;
    chk_5: TCheckBox;
    chk_6: TCheckBox;
    b_OK: TButton;
    b_Cancel: TButton;
  private
  public
  end;

As you can see, it contains six CheckBoxes grouped in two GroupBoxes.
Now, for some reason, we want to disable all those CheckBoxes.

Of course we could do it like this:

  chk_1.Enabled := False;
  chk_2.Enabled := False;
  chk_3.Enabled := False;
  chk_4.Enabled := False;
  chk_5.Enabled := False;
  chk_6.Enabled := False;

And for just 6 CheckBoxes we probably would. But lets assume there are 30 and we don’t only want to disable them all but also sometimes enable or check or uncheckm them all. That starts to get a bit tedious, so wouldn’t it be great if we could just create an array of TCheckbox and fill it with those pesky things? We could then just write:

for chk in CheckBoxArr do begin
  chk.Enabled := false;
end;

But how do we get this array? If you ever programmed in Visual Basic, you know the concept of control arrays, which you get by “simply” setting an Index property of the controls. They would then all have the same name and you could access them individually by TheCheckBox[i]. While that looks nice at first glance, just consider setting these indexes correctly for 30 CheckBoxes using the (crappy) property editor that VB had. I hated it. But I deviate …

We want a function which we can pass a TForm parameter that returns a TCheckBoxArray:

type
  TCheckBoxArray = array of TCheckBox;

function AllCheckboxes(_frm: TForm): TCheckBoxArray;

If you look closely at the form declaration above you will notice that all CheckBoxes follow a common naming pattern: They all start with ‘chk_’ followed by a number. So we could use FindComponent to get them:

function GetAllCheckboxes(_frm: TForm): TCheckBoxArray;
var
  i: Integer;
  cmp: TComponent;
begin
  SetLength(Result, _frm.ComponentCount);
  i := 1;
  repeat
    cmp := _frm.FindComponent('chk_' + IntToStr(i));
    if cmp <> nil then begin
      Result[i - 1] := cmp as TCheckBox;
      Inc(i);
    end;
  until cmp = nil;
  SetLength(Result, i - 1);
end;

But if you think about it this is quite a bit of work, because you must make sure that your CheckBoxes all conform to that name pattern and for the above code to work, there must not be any gaps in the numbering. We usually want those CheckBoxes to have meaningful names rather than numbers, so it’s chk_Green, chk_Blue, chk_LightOn rather than chk_1, chk_2 etc. Also, what if you accidentally add another control that conforms to the name pattern but is not a CheckBox?

So FindComponent is out. What alternatives are there?

There is the Component array of the form, that contains every component on the form whose Owner is the form (this is usually the case for all components on a form when the form has been created using the form designer). So we enumerate all components and check whether they are CheckBoxes:

function GetAllCheckboxes(_frm: TForm): TCheckBoxArray;
var
  i: Integer;
  cmp: TComponent;
  cnt: Integer;
begin
  SetLength(Result, _frm.ComponentCount);
  cnt := 0;

  for i := 0 to _frm.ComponentCount - 1 do begin
    cmp := _frm.Components[i];
    if cmp is TCheckBox then begin
      Result[cnt] := TCheckBox(cmp);
      Inc(cnt);
    end;
  end;
  SetLength(Result, cnt);
end;

Much better. We don’t need any naming pattern and we only get CheckBoxes.

I mentioned above, that the form’s Components[] property contains all components placed on the form using the form designer. But components can also be created in code and there you can pass any other control as the Owner:

  chk := TCheckbox.Create(grp_1);
  chk.Name := '';
  chk.Parent := grp_1;
  chk.Top := 8;
  chk.Left := 8;
  chk.Caption := 'CheckBox created in code.';

The function will not find such a CheckBox because by passing grp_1 to its constructor we add it the Components[] property of the GroupBox rather than the form. That’s perfectly OK. We also clear the name, which would prevent it from being found by FindComponent too. But if you write code like this, you probably know what you are doing and won’t have read this article up to here.

I could stop now, but there is another use case: What if we want to get an array of only those CheckBoxes in one of the GroupBoxes? We could pass that GroupBox as a second parameter to the function and check that the Parent property of the CheckBoxes matches the GroupBox. But there is a simpler way: Each WinControl (that is: Control that itself can contain other controls, which includes GroupBoxes, Panels etc. and of course Forms or Frames) has got a Controls[] property that contains those controls. So we make a simple change to the function:

function GetAllCheckboxes(_Parent: TWinControl): TCheckBoxArray;
var
  i: Integer;
  ctrl: TControl;
  cnt: Integer;
begin
  SetLength(Result, _Parent.ControlCount);
  cnt := 0;
  for i := 0 to _Parent.ControlCount - 1 do begin
    ctrl := _Parent.Controls[i];
    if ctrl is TCheckBox then begin
      Result[cnt] := TCheckBox(ctrl);
      Inc(cnt);
    end;
  end;
  SetLength(Result, cnt);
end;

// [...]
begin
  CheckBoxes := GetAllCheckboxes(grp_1);

This will return all CheckBoxes placed on the GroupBox grp_1.

But note that …

  CheckBoxes := GetAllCheckboxes(Form1);

… will now return an empty array! None of the CheckBoxes have been placed on the form itself, so they will not be in the form’s Controls[] property.

If you haven’t been bored out of your skull by now and want to discuss this article, you can do so here in this post in the international DelphiPraxis forum.

 Posted by on 2019-11-17 at 12:52

dzPrepBuild for Delphi 1.3.3 released

 Delphi, dzPrepBuild  Comments Off on dzPrepBuild for Delphi 1.3.3 released
Nov 022019
 

I just released version 1.3.3 of dzPrepBuild

The reason for this release was that I wanted to allow automatically updating the ProductVersion when the FileVersion changes. In order to do that, the tool now supports two additional placeholders that an be used in any version string: {MajorVer} and {MinorVer} which, as you might have guessed, resolve to the major and minor version number.

e.g.

[Version Info Keys]
CompanyName=www.dummzeuch.de
FileDescription=This is a Testproject for dzPrepBuild
FileVersion=1.0.0.2
InternalName={ProjectName}
LegalCopyright=Copyright 2002-{ThisYear} by Thomas Mueller
LegalTrademarks=
OriginalFilename={ProjectName}.exe
ProductName=Testproduct
ProductVersion={MajorVer}.{MinorVer}
BuildDateTime={today}

I also added {ProjectName} which resolves into the name of the Delphi project and is used in the example above for the InternalName and OriginalFilename entries.

While working on this enhancement unfortunately I also found a major bug that was introduced by a patch I accepted nearly years ago. It added support for private and special build flags but used the wrong names for these entries in the .dof, .bdsproj and .dproj files which resulted in –ReadDof, -ReadBdsProj and -ReadDproj failing. This is now also fixed. I never noticed this problem because I don’t use the built in Delphi version information any more. All my projects have an external [projectname]_version.ini file for maintaining the version information.

And since I was at it, I moved the project from SourceForge to OSDN. The new version can be downloaded from there.

 Posted by on 2019-11-02 at 14:55

Schedule GMail mails to be sent later

 Google  Comments Off on Schedule GMail mails to be sent later
Nov 012019
 

From the “how could I have missed this feature for so long?” department:

GMail can schedule the sending of emails to a freely selectable future date and time. (I have no idea when that feature was added, probably a long time ago.). In the browser, just drop down the menu in the Send button:

According to cnet the GMail app has a similar feature, but I don’t use that app, so I don’t know.

Scheduled mails turn up in the scheduled folder and sending them can be cancelled there which moves the message back to the drafts folder, so you can edit it and possibly schedule it again.

 Posted by on 2019-11-01 at 12:28

PascalMagick – MagickDistortImage in Delphi

 Delphi  Comments Off on PascalMagick – MagickDistortImage in Delphi
Oct 222019
 

Playing around with ImageMagick I found that somebody already wrote a Pascal/Delphi import unit for it called PascalMagick. Unfortunately it has been integrated into freepascal which means Delphi support no longer has any priority.

So, the first thing to do was getting it to compile with Delphi.
It requires the ctypes unit from freepascal which did not compile because of an {$if …} that was terminated with an ${endif} rather than ${ifend} (line 107, newer versions fo Delphi won’t complain about this any more).

Then I opened one of the examples. There is a DPR file with an associated DPROJ file, but unfortunately that file was not a Delphi project file but some XML stuff. I renamed the LPR file (Lazarus Project) to DPR and it loaded into Delphi 2007.

Compiling worked too, but when I started it, I immediately got a System Exception with some cryptic error message. Half an hour later I knew the cause: The DLL could not be loaded, because there is an {$IFDEF Windows} around the MagickExport and WandExport constants (ImagageMagick.pas line 45). Delphi does not by default define this symbol. So I added it and the DLL still could not be found. WTF? Turned out that the DLL name was wrong. It’s now CORE_RL_MagickWand_.dll rather than CORE_RL_Wand_.dll. After that change the example program actually worked.

But I did not want to do the simple stuff but was interested in writing code that does what ‘magick -distort Perspective’ does. And I could not find anything in those units and include files that looked promising. Google finally turned up the MagickDistortImage function which is simply missing from import unit. So I added it:

type
  TMagickDistortImage = function(wand: PMagickWand; Method: DistortMethod; NumOfArgs: Integer;
        arguments: PDouble; bestfit: MagickBooleanType): MagickBooleanType; cdecl;

var
    MagickDistortImage: TMagickDistortImage;

  MagickDistortImage := GetProcAddress('MagickDistortImage');

But which values can be passed to the Method parameter? Again, it took me a while to find that enum declaration in C, so I converted it to Delphi:

type
  DistortMethod = (
    UndefinedDistortion,
    AffineDistortion, AffineProjectionDistortion, ScaleRotateTranslateDistortion,
    PerspectiveDistortion, PerspectiveProjectionDistortion,
    BilinearForwardDistortion,
    BilinearDistortion = BilinearForwardDistortion,
    BilinearReverseDistortion, PolynomialDistortion, ArcDistortion, PolarDistortion,
    DePolarDistortion, Cylinder2PlaneDistortion, Plane2CylinderDistortion, BarrelDistortion,
    BarrelInverseDistortion, ShepardsDistortion, ResizeDistortion, SentinelDistortion);

That’s how far I have come by now. I’ll put this here in case somebody else is as desparately looking for these declarations as I was for the last few hours.

 Posted by on 2019-10-22 at 16:00

efg’s Computer Lab and Reference Library is being restructured

 Delphi  Comments Off on efg’s Computer Lab and Reference Library is being restructured
Oct 212019
 

If you have done graphics processing in Delphi, you know efg’s computer lab which contained lots of great documentation and source code for that purpose. Unfortunately today on www.efg2.com you can read the following:

This site will be reorganized in the near future.

Prior to July 2019, this site contained 180+ pages on a variety of technical topics.
Links below are to copies on the Wayback Machine.

And since I am afraid that even those links will go away in the near future, I want to preserve them here:

Computer Lab on Wayback Machine (last snapshot from 2019-04-29)
The Computer Lab contains Lab Reports and Technical Notes on topics including, image processing, color science, computer graphics, mathematics, fractals & chaos, science & engineering. Most projects involve programming and include complete Delphi source code.

Reference Library on Wayback Machine (last snapshot from 2019-05-02)
The Reference Library contains two major sections, Delphi and Technical, each of which is a compendium of information from a wide variety of Internet resources.

The Delphi section is divided into a number of subsections and contains a large number of Delphi programming examples and code fragments. The Delphi Reference Library is mostly about technical topics (other than databases, except for the ADO subsection). The Algorithms, Graphics, Printing, and Math pages are the most popular.

The Technical section contains a large number of links to technical information. The Algorithms, Color, and Image Processing Reference Library pages are the most popular.

 Posted by on 2019-10-21 at 11:15

Several bugs regarding the GExperts Uses Clause Manager fixed

 Delphi, GExperts  Comments Off on Several bugs regarding the GExperts Uses Clause Manager fixed
Oct 192019
 

A few months ago I added the “Identifiers” tab to the GExperts Uses Clause Manager. It allows you to search for a unit that exports a given identifier. It works a bit like the “Find Unit” refactoring in the IDE since Delphi 2005.

That feature starts a background thread that parses all units in the search path to get a list of identifiers these units export. It works well with Delphi 2007 but recently I discovered that in Delphi 10.3.2 it finds “FreeAndNil” in various units which I am sure don’t export it:

So something must have been wrong with the parsing code.

It turned out that there were several bugs in that code, all related to language features added to Delphi later than Delphi 2007 (You may remember that I stated several times that I still do most of my work with Delphi 2007 so that version is the one with which GExperts has been tested extensively.).

These bugs have now been fixed and “FreeAndNil” only gets me the expected short list of units:

The changes have been committed to the svn repository (revisions #2807 and #2808), so if you want a DLL without this bug, you can compile your own.

 Posted by on 2019-10-19 at 20:20

All but one known GExperts code formatter bugs fixed

 Delphi, GExperts  Comments Off on All but one known GExperts code formatter bugs fixed
Oct 182019
 

Some long standing bugs in the GExperts code formatter have been fixed.

It’s great to see nearly all unit tests finally succeed.

About 10 new test cases have been added too, so the improvement is significant.

The fixed bugs including some that have bugged (sorry 😉 ) me for years, e.g.:

  Msg := Format('%s %s'#13#10
    + '%s',
    [a, b
      c]);

where the last line used to be indented the same as the previous line like this:

  Msg := Format('%s %s'#13#10
    + '%s',
    [a, b
    c]);

There is one left:

begin
  { testcomment
}s := '';
end;

where I am not even sure how it should be formatted. Maybe like this:

begin
   { testcomment
   }s := '';
end;

Or maybe it shouldn’t be changed at all.

Anyway, I’m thinking about making another GExperts release this weekend, but haven’t decided yet. If you want a fixed code formatter, you will have to compile your own DLL which isn’t rocket science after all.

 Posted by on 2019-10-18 at 13:55

Yahoo is closing down Groups

 Delphi, GExperts  Comments Off on Yahoo is closing down Groups
Oct 182019
 

I just read that Yahoo will be closing down Groups and will delete all content there.

By the end of October no new content will be accepted, by mid December all content will be deleted.

Why am I posting this? The GExperts mailing lists are hosted at Yahoo Groups, so they will be gone shortly. If you want to stay up to date or communicate with other GExperts users, there is either this blog (there is a GExperts RSS feedWhat is an RSS feed?), or the GExperts sub forum on the international Delphi Praxis forum.

Since Groups was the only remaining Yahoo feature I was using, have also just deleted my Yahoo account.

 Posted by on 2019-10-18 at 10:44