Some changes to CustomContainerPack

 Delphi  Kommentare deaktiviert
Sep 202014
 

Today I made some small changes to the CustomContainerPack.

If you don’t know about the Custom Container Pack, see my previous blog post.

Apart from removing two with statements I changed the place where the wizard shows up in the File -> New -> Other dialog. Up to Delphi 7 it is still in the “New” category.

ccpack_delphi7

For newer Delphi versions, it now shows up in the “Delphi Files” category:

ccpack_delphi2005

It took me quite a while to figure out how to do it. Just returning “Delphi Files” from the IOTARepositoryWizard.GetPage function doesn’t work. The trick is to use the IOTARepositoryWizard80 interface that was introduced with Delphi 2005 (or, judging from the name, probably with Delphi 8). It added two new functions to IOTARepositoryWizard:

    function GetPersonality: string;
    function GetGalleryCategory: IOTAGalleryCategory;

GetPersonality is easy, you just return one of the pre-defined string constants sXxxxPersonality, in this case: sDelphiPersonality

function TCCWizard.GetPersonality: string;
begin
  Result := sDelphiPersonality;
end;

(If anybody wants to check if CustomContainerCack can be used with the C++Builder personality, please contact me through my Google+ page.)

GetGalleryCategory is a bit more tricky. I found the solution in Steve’s Blog:

function TCCWizard.GetGalleryCategory: IOTAGalleryCategory;
var
  cat: IOTAGalleryCategory;
  catMgr: IOTAGalleryCategoryManager;
begin
  catMgr := (BorlandIDEServices as IOTAGalleryCategoryManager);
  Assert(Assigned(catMgr));
  cat := catMgr.FindCategory(sCategoryDelphiNewFiles);
  Assert(Assigned(cat));
  Result := cat;
end;
Sep 142014
 

I just updated the Custom Container Pack sources to support Delphi XE2 to XE7. It was mostly a matter of creating the packages for the newer versions. I also had to adapt the registration code to changes in Delphi XE2.

It now compiles and installs. I have not tested it extensively. We use it at work with Delphi 2007 and I know of no bugs with it.

Custom Containers Pack (CCPack) is an integrated tool and component mini-library to produce and maintain composite controls (or simply “composites”) and other containers (forms, data modules and frames). The process of building composite components looks like ActiveForm and Frame creating, but the result is the native VCL component. You can create new composites just as usual forms.

Here is the original documentation in RTF format.

It was originally developed by Sergey Orlik who posted the source code to code central

Sep 132014
 

Today I spent some time to make dzlib compile with all Delphi versions from 2007 to XE6 (XE7 to come later). It didn’t take too long since it already supported 2007, XE2 and XE6.

It’s interesting to see, how the RTL evolved between these versions. Some examples:

  • The IsWhitespace function started out as a class method of TCharacter, then moved to TCharHelper and finally ended up as a method of the Char type itself (probably added through a class helper, I didn’t check).
  • The global DecimalSeparator variable was marked deprecated for a long time (replaced by a property of the global FormatSettings class) and has finally been removed from the RTL.

There is also a breaking change in the Delphi XE6 RTL:
You can no longer create a TThread suspended and then call Resume/Start from within its constructor. If you do that, you will get an exception. But since the thread no longer gets started until the constructor has run that is no longer necessary.

[German only] Fahrradbeleuchtung

 Delphi  Kommentare deaktiviert
Sep 132014
 

Es wird wieder dunkel, und so allmählich braucht man auch zu normalen Radfahrzeiten wieder Licht. Seit einiger Zeit sind laut STVZO batteriebetriebene Lampen auch für normale Fahrräder zugelassen, sofern sie einige Bedingungen erfüllen. Passend dazu hatte Aldi Nord am 11.8.2014 die Pedaluxx LED Fahrradbeleuchtung im Angebot. Ich habe zugegriffen, weil ich dachte, dass man für 9,99 Euro nicht allzuviel verliert, wenn sie nichts taugt.

Die Lampen an sich sind ganz OK, auch wenn es ziemlich verwirrend ist, dass die Lade-Kontrolleuchte der Frontlampe an geht, wenn die Batterien gewechselt werden sollen, die des Rücklichts hingegen geht dann aus.

Die Halter allerdings sind ziemlich wacklig. Ich bin mir sicher, dass sie nicht lange halten werden. Schon bei der Montage der Halterung für die Frontlampe habe ich bemerkt, dass sich das dünne Plastik, in dem die Schraube halten soll, nach außen verbiegt.

Ich hatte vorher (bevor mir das Fahrrad komplett geklaut wurde) eine andere (aber auch deutlich teurere) Frontlampe, damals noch ohne STVZO-Zulassung, deren Halter bombenfest sass. Leider gibt es die nicht mehr zu kaufen.

Batterien (8x AAA) waren dabei, aber ich werde sie, sobald sie leer sind, durch Akkus ersetzen.

Man sollte vielleicht erwähnen, dass die neue Fassung der STVZO zwar batteriebetriebene Lampen erlaubt, man diese allerdings nach STVO bei der Benutzung des Fahrrads (also auch tagsüber) montiert haben muss. Mitführen alleine reicht nicht. Genaugenommen ist es sogar noch schlimmer: Die Leuchten müssen “fest” am Rad montiert sein, danach wären Ansteckleuchten gar nicht zulässig. Ob das so ist, ist noch nicht endgültig geklärt.

Mir persönlich ist es ziemlich egal, was diese Vorschriften zur Fahrradbeleuchtung sagen. Mir ist wichtig, dass ich im Straßenverkehr gesehen werden, denn ich möchte gerne noch ein paar Jährchen leben und ich weiß, wie schlecht Radfahrer ohne Licht in der Dämmerung zu sehen sind. Deshalb werde ich diese Beleuchtung testen und verwenden, wenn sie ihren Zweck erfüllt. Wenn nicht, muss eine andere her, Zulassung oder nicht.

Vielleicht noch ein Hinweis: Anders als früher sind Fahrradlampen heute so hell, dass sie entgegenkommende Verkehrsteilnehmer blenden, wenn sie falsch eingestellt sind. Die Frontlampe muss so eingestellt sein, dass ihr Leuchtkegel die Straße max. 15 m vor dem Fahrrad beleuchtet. Mir kommen in letzter Zeit leider immer wieder andere Radfahrer entgegen, deren Licht viel zu hoch gestellt ist.

(STVO = Straßenverkehrsordnung, STVZO = Straßenverkehrs-Zulassungs-Ordnung)

Autosave Wizard for Delphi 6 to XE7

 Delphi  Kommentare deaktiviert
Sep 062014
 

Since the stability of some Delphi IDE versions leave something to desire, people have asked for an autosave feature for a long time.

Delphi 2010 eventually got “Save on compile” while older versions only had “Save on run”.

Ray Lischner originally wrote an AutoSave wizard for Delphi 5 (if I remember correctly) and ported it to Delphi 6 and 7 later. I have taken his code, extended it and ported it to all later Delphi versions.

The wizard does the following:
Whenever triggered it checks for files that have not been saved yet. It saves these files as ~filename.ext. When the file is saved, it deletes these ~files again. In the event of the IDE crashing, it will on restart ask the user whether he wants to load the regular file or the last autosave file.
The wizard can be configured to trigger

  • every x minutes
  • whenever the IDE “compiles”

I recommend to activate the latter setting. You will be surprised how often the IDE “compiles” stuff.

There are no precompiled packages. Just get the sources, open the package that corresponds to your Delphi version, compile and install it.

You can find the current sources on the dzAutoSave page on sourceforge.

Autosave on compile in Delphi?

 Delphi  Kommentare deaktiviert
Sep 062014
 

Did you know that the Delphi IDE automatically saves all files when you compile a project? I didn’t.

In order to activate it, go to the Tools/Options dialog and there on the “Environment Options” page. There, you will find an “Autosave options” group. Set the checkmark for “Editor files” (and close the dialog with “OK” of course).

What, you knew that option was there but you thought that it only saved when running the program in the debugger not when just compiling? Welcome to the club! So did I.

I did some tests today and found that this behaviour changed with Delphi 2010. Up to Delphi 2009 autosave took place when running (in the debugger), since Delph 2010 it is done when compiling.

So, now I know that my upcoming new autosave wizard release only needs to support Delphi up to 2009…

Sep 042014
 

The latest version is the first version that supports Delphi XE7.

There is nothing really new about the formatter code. But the new release can be installed even if you don’t have the official GExperts installer (yet). This is an extract from the readme file:

** Installing without an official installer **
   
With two Delphi Releases per year and Erik Berry being busy otherwise,
new GExperts releases have been lagging behind. So in case there is no
official GExperts installer yet, these are the steps to install the
experimental version by hand:

1. Create a suitable GExperts directory. It doesn't have to be a
   subdirectory of %ProgramFiles% but may be anywhere, even on a network
   share if you are sure this share will always be available.
2. Extract all files from the ZIP somewhere
3. Copy all files from the extracted directory to the GExperts directory.
   (Do *NOT* copy the subdirectories!)
4. Copy the appropriate GExperts DLL from one of the subdirectories
   EditorExpert or RegularExpert to the GExperts directory.
5. Copy the files from the subdirectory FromRegularRelease to the
   GExperts directory.
6. Copy the appropriate cmd from the install subdirectory. To the GExperts
   directory.
7. Make sure that the Delphi IDE is not running
8. Run the cmd file. It will register the GExperts dll with the Delphi IDE.
9. Start Delphi and check that the new GExperts DLL has been loaded by
   opening the GExperts About dialog.

In theory it is possible to install GExperts for all Delphi versions into
the same GExperts directory. But be warned: This has not been tested
thoroughly.

Head over to the Experimental GExperts page to download the new version.

Aug 312014
 

A rather common question on StackOverflow, the Delphi newsgroups and elsewhere is how to display a drop down menu when the user presses a button.

ButtonWithDropdown

There are many proposed solutions and even something built into newer versions of Delphi (Which doesn’t work for me for some reason.)

Here is mine (which is based on this answer on StackOverflow):
First, I create a helper class based on TComponent. It links the button (which can be TButton or TBitBtn or anything else derived from TCustomButton) to the popup menu and hooks its OnClick event. To access the OnClick event, which is protected in TCustomButton, we need to cast it to TCustomButtonHack. The OnClick event then displays the popup menu. For convenience I set the helper class’s parent to the button, so it automatically gets freed when the button does get freed.

type
  TButtonPopupMenuLink = class(TComponent)
  private
    FBtn: TCustomButton;
    FMenu: TPopupMenu;
    FLastClose: DWORD;
  public
    constructor Create(_btn: TCustomButton; _pm: TPopupMenu);
    procedure doOnButtonClick(_Sender: TObject);
  end;

{ TButtonPopupMenuLink }

type
  TCustomButtonHack = class(TCustomButton)
  end;

constructor TButtonPopupMenuLink.Create(_btn: TCustomButton; _pm: TPopupMenu);
begin
  inherited Create(_btn);
  FBtn := _btn;
  FMenu := _pm;
  FMenu.PopupComponent := FBtn;
  FBtn.OnClick := Self.doOnButtonClick;
end;

procedure TButtonPopupMenuLink.doOnButtonClick(_Sender: TObject);
var
  Pt: TPoint;
begin
  if GetTickCount - FLastClose > 100 then begin
    Pt := FBtn.ClientToScreen(Point(0, FBtn.ClientHeight));
    FMenu.Popup(Pt.X, Pt.Y);
    { Note: PopupMenu.Popup does not return until the menu is closed }
    FLastClose := GetTickCount;
  end;
end;

And just for some more convenience I add a procedure that just creates that helper class, so I don’t have to expose the class through the unit’s interface but only that procedure:

procedure TButton_AddDropdownMenu(_btn: TCustomButton; _pm: TPopupMenu);
begin
  TButtonPopupMenuLink.Create(_btn, _pm);
end;

To use it I call that procedure from the form’s constructor:

constructor TMyForm.Create(_Owner: TComponent);
begin
  inherited Create;
  // other stuff
  TButton_AddDropdownMenu(b_MenuButton, pm_MenuButton);
end;

I have put that code into u_dzVclUtils, which is part of my dzlib library.

Translating Windows messages to strings

 Delphi  Kommentare deaktiviert
Aug 242014
 

I could not find anything like this so I wrote it myself:

This class translates most Windows message ids into their symbolic name.

type
  TWmMessageToString = class
    function MsgToString(const _WmMsg: Cardinal): string; overload;
    function MsgToString(const _Msg: TMessage): string; overload;
  end;

The names are taken from

  • Delphi 2010’s messages.pas
  • Delphi 2010’s controls.pas
  • Wine

It seems pretty complete, but if a message cannot be found, the MsgToString methods return its hexadecimal and decimal representation.

The code is part of my dzlib its the u_dzWmMessageToString unit.

Aug 242014
 

In an older blog post I wrote about AutoComplete for TEdits using SHAutoComplete.

I just actually tried to use that function in one of my applications and found that there is a quite annoying problem with it: If you have set the OK button’s Default property to true (so it gets “clicked” when you press return), selecting an entry from the autocomplete list with the return key also closes the form, which is usually not what the user wants.

I turns out that I am not the first to stumble upon that problem.

The suggestion posted there by mghie is a bit ugly because it hooks the Application.OnMessage event which might conflict with other code that uses it.

I had another problem anyway (see below) so I extended a class that hooks a TEdit’s WindowProc method instead. Here is the code:

procedure TAutoCompleteActivator.NewWindowProc(var _Msg: TMessage);
begin
  if (_Msg.Msg = CM_WANTSPECIALKEY) then begin
    if (_Msg.wParam = VK_RETURN) or (_Msg.wParam = VK_ESCAPE) then begin
      if IsAutoSuggestDropdownVisible then begin
        _Msg.Result := 1;
        Exit; //==>
      end;
    end;
  end;
  inherited NewWindowProc(_Msg);
end;

The IsAutoSuggestDropdownVisible function is directly taken from mghie’s answer:

function EnumThreadWindowsProc(AWnd: HWnd; AParam: LParam): BOOL; stdcall;
var
  WndClassName: string;
  FoundAndVisiblePtr: PInteger;
begin
  SetLength(WndClassName, 1024);
  GetClassName(AWnd, PChar(WndClassName), Length(WndClassName));
  WndClassName := PChar(WndClassName);
  if WndClassName = 'Auto-Suggest Dropdown' then begin // do not translate
    FoundAndVisiblePtr := PInteger(AParam);
    FoundAndVisiblePtr^ := Ord(IsWindowVisible(AWnd));
    Result := False;
  end else
    Result := True;
end;

function IsAutoSuggestDropdownVisible: Boolean;
var
  FoundAndVisible: Integer;
begin
  FoundAndVisible := 0;
  EnumThreadWindows(GetCurrentThreadId, @EnumThreadWindowsProc,
    LParam(@FoundAndVisible));
  Result := FoundAndVisible > 0;
end;

This works fine in my program compiled with Delphi 2010 and running on Windows 8.1 (your mileage may vary).

Now to the other problem mentioned above:
In the old blog post I published a TEdit_SetAutocomplete function that activates autocomplete for a TEdit control. This function works fine as as long as you don’t try to call it in the form’s constructor. If you do, it does nothing. The reason is that the TEdit’s handle gets destroyed and recreated after the form’s constructor was called, which results in autocomplete being turned off again. One option would have been to put the function call into the form’s OnShow handler, but I am no fan of distributing code that in my opinion belongs into the constructor to these event handlers, so I wanted a different solution.

It turned out that I already had one in my dzlib.u_dzVclUtils unit: TWinControl_ActivateDropFiles returns a TObject that hooks the TWinControl’s WindowProc and handles the WM_NCCREATE and WM_NCDESTROY messages. I refactored that class a bit to create a generic TWindowProcHook ancestor and derived TAutoCompleteActivator from it. Its WmNcCreate method now looks like this:

procedure TAutoCompleteActivator.WmNcCreate;
begin
  inherited;
  SetAutoComplete;
end;

procedure TAutoCompleteActivator.SetAutoComplete;
begin
  TEdit_SetAutocomplete(FCtrl as TCustomEdit, FSource, FType);
end;

So every time the window handle gets created anew, it activates autocomplete for it again.

The full code can be found in my dzlib library on SourceForge. It’s in the u_dzVclUtils unit.