Delphi: RichEdit Text Background Color

Something I needed in a project recently was to put some makeup into a richedit control. This is pretty easy with the SelAttributes field in the class. However, to my disappointment I found that the TTextAttributes class (SelAttributes) does not contain a field to set the background color of your text even though it is supported by the control. So I set out to find a solution to the problem on Google, but everything I could find required you to externally use the windows api to force this property onto the window.

I didn’t quite like this solution, so I came up with a cleaner one. I created a class helper for the TTextAttributes class. I first had a look at this class and noticed that the way the Color property is set and read does not require much work. Since I “can’t” access any of the class’ private methods I simply copied the few ones I needed to my class helper, being SetAttributes and GetAttributes. However, I couldn’t do the same with the 2 private fields in the class which I needed, so I made a small workaround for this. Note that this workaround will only work as long as VCL is changed. I created a small class with the same private fields. When casting the original object to this class, you can use it to access those private fields. If the name of the fields is changed in VCL, this small workaround has to be updated as well.

Here’s the header code:

uses
  ComCtrls, Graphics, RichEdit;

type
  __TTextAttributes = class
  private
    RichEdit: TCustomRichEdit;
    FType: TAttributeType;
  end;

  TTextAttributesH = class helper for TTextAttributes
  private
    function GetBackColor: TColor;
    procedure SetBackColor(const Value: TColor);
    procedure GetAttributes(var Format: TCharFormat2);
    procedure SetAttributes(var Format: TCharFormat2);
  public
    property BackColor: TColor read GetBackColor write SetBackColor;
  end;

And the body:

{ TTextAttributesH }

procedure TTextAttributesH.GetAttributes(var Format: TCharFormat2);
var
  RichEdit: TCustomRichEdit;
begin
  RichEdit := __TTextAttributes(Self).RichEdit;
  InitFormat(Format);
  if RichEdit.HandleAllocated then
    SendGetStructMessage(RichEdit.Handle, EM_GETCHARFORMAT,
      WPARAM(__TTextAttributes(Self).FType = atSelected), Format, True);
end;

function TTextAttributesH.GetBackColor: TColor;
var
  Format: TCharFormat2;
begin
  GetAttributes(Format);
  with Format do
    if (dwEffects and CFE_AUTOBACKCOLOR) <> 0 then
      Result := clWindow
    else
      Result := crBackColor;
end;

procedure TTextAttributesH.SetAttributes(var Format: TCharFormat2);
var
  Flag: Longint;
  RichEdit: TCustomRichEdit;
begin
  RichEdit := __TTextAttributes(Self).RichEdit;
  if __TTextAttributes(Self).FType = atSelected then 
    Flag := SCF_SELECTION
  else
    Flag := 0;
  if RichEdit.HandleAllocated then
    SendStructMessage(RichEdit.Handle, EM_SETCHARFORMAT, Flag, Format);
end;

procedure TTextAttributesH.SetBackColor(const Value: TColor);
var
  Format: TCharFormat2;
begin
  InitFormat(Format);
  with Format do
  begin
    dwMask := CFM_BACKCOLOR;
    if Value = clWindowText then
      dwEffects := CFE_AUTOBACKCOLOR
    else
      crBackColor := ColorToRGB(Value);
  end;
  SetAttributes(Format);
end;

Note that this only works in versions of Delphi that support class helpers. You can also use this method to add other missing properties.

Read More

Parsing Pascal Language Syntax

On occasion people will want to parse the syntax of a programming language, to create manuals, syntax highlighters, etc… This however isn’t always easy to achieve. The easiest way to do this, is to use regular expressions. I’ve written up an expression to handle the syntax of the pascal language. In this expression I will be using capture groups for each part of the syntax, because of this, you will be able to determine what part of the syntax was matched by checking what group has a content. Note that the order of matching the syntax is very important.

(?:(//(?:(?!$).)*)|(\{\$[^\}]*\})|(\{(?:.*?\}|.*))|(\(\*(?:.*?\*\)|.*))|(‘(?:(?!$|’).)*’?)|(#\d+)|(\d+(?:\.\d+)?|\$[0-9A-F]+)|([A-Z_][A-Z0-9_]*))

Capture groups:

  • 1: // One line comments
  • 2: {$COMPILER DIRECTIVES}
  • 3: {Multi line comments}
  • 4: (*Multi line comments*)
  • 5: ‘Strings’
  • 6: Characters (#125)
  • 7: Numbers (268 or 569.56)
  • 8: Words (compare with list of keywords afterwards)

You can easily create similar regexes for other languages which work the same way. Note that you have to set the regex engine to have dot match newlines and match caseless for this regex to work.

Read More

C++: System-Wide Font Smoothing

The Win32 API contains a method called SystemParametersInfo, it is used to get and set system-wide parameters. In this case we will be using it to to get and set system-wide font smoothing. In the following piece of code, font smoothing is toggled and restored after 5 seconds.

#include <windows.h>

int main(int argc, char* argv[])
{
	int i;
	SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &i, 0);
	SystemParametersInfo(SPI_SETFONTSMOOTHING, !i, 0, 0);
	Sleep(5000);
	SystemParametersInfo(SPI_SETFONTSMOOTHING, i, 0, 0);
	return 0;
}

Read More

Delphi: Required package “rtl” not found

A while back I got this interesting error when Installing a package into a newly installed copy of Delphi 2010 saying “required package rtl not found”. I recall having had this problem before once when I had Delphi 2009 and 2010 installed at the same time, it just so happens that now I had 2010 and XE installed. Here’s an easy solution to the problem:

Add “$(BDS)\lib” and “$(BDSCOMMONDIR)\dcp” to your library path. This solution will probably only work for later versions of Delphi, though for all versions it should simply be a matter of adding your lib path and dcp output path to the library path.

In my case the cause of this problem is fairly simple. Whenever you install Delphi, the installer will set up all paths like the library path in the windows registry. However, if a path is already present, it will use that, since the user might just be reinstalling the IDE and wouldn’t want to loose their settings. Some component installers allow you to install the components for IDE’s that aren’t actually installed on your computer yet, they will append their library paths to the non-existing path for the IDE which means they will simply create it. This results in a library path that does not contain any of the paths to the standard VCL library and components included with Delphi.

A less delicate solution to this problem would be to just erase all registry content for the version of Delphi you’re trying to install and then install it. This will ensure you have all paths set up correctly, because the previous solution will only ensure VCL works correctly, not the components that come with the IDE like the Indy library. The registry values for the IDEs are located in HKEY_CURRENT_USER\Software\Borland for old releases, HKEY_CURRENT_USER\Software\CodeGear for all releases up to Delphi 2010 and HKEY_CURRENT_USER\Software\Embarcadero for XE and newer.

Read More