  ==================================================
                  RSD Tips&Tricks
  ==================================================
  Authors:                             Supported by:
  Andrey Telnov and Roman Eremin        RSD software
  rsd@tibc.tula.ru              http://www.rsd.pp.ru
  ..................................................
  June 3, 1997                  RSD Tips&Tricks  #03
  ..................................................

Contents 
1. When ControlAtPos method is working and is not? 
2. How to see what (win)control is under mouse cursor? (Demo + TControlAtCursorPos component) 
3. Macro Substitution in Delphi. Create indexed property. (Demo + TMacroSubstitution object)
4. Make a joined (sorted) Query LIVING (Demo)
5. Write component and property editors (Demo + 2 components)
6. Reindex all indexes in the alias in Delphi 1.0 (Demo)

1. When ControlAtPos method is workingand and is not? 
Q:
I have been trying to use ControlAtPos on a group box that has a Label and a Memo Field.  The function returns the label fine but will not return the Memo.
A:
TLabel is inherited from TControl = non windowed control (TLabel <- TGraphicControl <- TControl)
TMemo is inherited from TWincontrol = windowed control (TMemo <- TCustomMemo <- TCustomEdit <- TWinControl <-TControl).
ControlAtPos is the TWinControl method and it returns fine only the non-windowed objects (TControls in Delphi).

To return the TWinControl you have to use  FindVCLWindow global function (unit Controls, it isn't documented). 
function FindVCLWindow(point : TPoint) : TWinConrol;
The point has to be in the global screen coordinates.

Don't try to use this method in WM_MOUSEMOVE callback function of the TForm (or any another TWinControl). WinControl object disturbs its parent to send the mouse messages for treating Mouse Messages by itself.  So you have to rewrite the WndProc of the TForm (TWinControl).


2. How to see what (win)control is under mouse cursor ?
Q:
I'd like to have the next component:
If a mouse pointer enters a (win)control on a form then that component have to recognize it and act upon this action ?

A: I've done the TControlAtCursorPos component, which does such a thing.
It is inherited from TComponent and using Windows Subclussing. It has 
1) public property Control : TControl - mouse focused control
2) published property OnChange - event - happens when Control property is near to change.

Here is the most interest code.  The rewritten WndProc procedure of the TForm control
 ...
      {Get Cursor position}
      if(MSG =  WM_MOUSEMOVE) Or (MSG = WM_NCMOUSEMOVE) then begin
        {if it is mouse messages}
        p.X := LoWord(lParam);
        p.Y := HiWord(lParam);
      end else begin
        {if it isn't mouse messages}
        GetCursorPos(p);
        p := FForm.ScreenToClient(p)
      end;
      {Get the Non windowed Components}
      vControl := FForm.ControlAtPos(p, False);
      if(vControl = Nil) then begin
        {Get the TWinControl Components}
         p := FForm.ClientToScreen(p);
         vControl := FindVCLWindow(p);
         if(vControl <> Nil) And (vControl <> FForm) then begin
           {Does it the TControl}
           p := vControl.ScreenToClient(p);
           ChildvControl := TWinControl(vControl).ControlAtPos(p, False);
           if(ChildvControl <> Nil) then
              vControl := ChildvControl;
          end;
      end;
      if(vControl = Nil) then
        FControl := FForm;
      if(FControl <> vControl)then begin
         if Assigned(FOnChange) then
           FOnChange(Nil, FControl, vControl);
         FControl := vControl;
      end;
 ...

Install component in mousefcs.pas and see Demo (\009)

If you want to find more about Window Subclussing see:
RSD Tips&Tricks # 02 (rsdtip02.zip)
...
4. Use Subclussing to add new features to Delphi wincontrols  (demo + component, TTextMacros)
...

and TDBGridSearch component (16/32 versions). It is also free of charge and you can find it at our web site: 
http://www.rsd.pp.ru

3. Macro Substitution in Delphi. Create indexed property.
Q:
In X-Base language, there is a very useful method call 'Macro Substitution'.  By using this method, I can store my entire Modules/Functions name into the database, and let users customize their own menu system accordingly in different levels.
A:
Delphi hasn't Macro Substitution but it is a high level language, the Object Pascal with new features + VCL library and you can create this feature by yourself. You can do it easily by using Index specifies for property.
I've done the TMacroSubstitution object which do almost the same thing, you've just have to improve it to make it work with DB.

unit macrsubs;

interface
uses classes, SysUtils;
type

{Declare our procedure type}
TDataProcedure = procedure of object;

{Declare Item}
TMacroSubstitutionItem = class
public
  dataprocedure : TDataProcedure;
  name : String;
end;

{Declare TMacroSubstitution}
TMacroSubstitution = class
private
  {Here is the item is stored}
  List : TList;
  {Here is the item is retreive}
  function GetMacroSubstitutionItem(value : Integer) : TMacroSubstitutionItem;
public
  constructor Create;
  destructor Destroy; override;
  {Add item}
  procedure Add(Name : String; dataprocedure : TDataProcedure);
  {Clear Item}
  procedure Clear;
  function Count : Integer;
  {Clear Item by its name}
  function GetByName(value : String) : TMacroSubstitutionItem;
  function IndexOf(value : TMacroSubstitutionItem) : Integer;
  {Array of items}
  property Items[Index : Integer] : TMacroSubstitutionItem   read GetMacroSubstitutionItem;
end;

implementation
{TMacroSubstitution}
constructor TMacroSubstitution.Create;
begin
  inherited;
  List := TList.Create;
end;

destructor TMacroSubstitution.Destroy;
begin
  Clear;
  List.Free;
  inherited;
end;

procedure TMacroSubstitution.Add(Name : String; dataprocedure : TDataProcedure);
begin
  List.Add(TMacroSubstitutionItem.Create);
  Items[Count - 1].Name := Name;
  Items[Count - 1].dataprocedure := dataprocedure;
end;

procedure TMacroSubstitution.Clear;
Var
  i : Integer;
begin
  for i := 0 to Count -1 do
    Items[i].Free;
  List.Clear;  
end;

function TMacroSubstitution.Count : Integer;
begin
  Result := List.Count;
end;

function TMacroSubstitution.GetByName(value : String) : TMacroSubstitutionItem;
var
  i : Integer;
begin
  Result := Nil;
  for i := 0 to Count -1 do
    if(CompareText(value, Items[i].Name) = 0) then begin
      Result := Items[i];
      Break;
    end;
end;

function TMacroSubstitution.IndexOf(value : TMacroSubstitutionItem) : Integer;
begin
  Result := List.IndexOf(Value);
end;

function TMacroSubstitution.GetMacroSubstitutionItem(value : Integer) : TMacroSubstitutionItem;
begin
  result := Nil;
  if(Value > -1) and (value < Count) then
    result := TMacroSubstitutionItem(List[Value]);
end;

See Demo (\010)

4. Make a joined (sorted) Query LIVING
Q:
I have to use joined Query and else I'd like to edit some fields in it. I try to create the Table, which contains some field from that joined Query to edit the records, but it doesn't work well. Can I do something ?
A:
You've created two DB Cursors. It is not good any way.

You have to Set RequestLive property  to True, to create your own TUpdateSQL and assign it to UpdateObject property of the Query. In this case your joined Query will be LIVING.

How to do it see demo (\011).

5. Write component and property editors (Demo + 2 components)
Q:
I'd like to write component which on clicking right mouse button show a menu with version, about, etc.
And also another one which has the next string property:
When user clicks on this property a form with version will be shown.
A:
No problem you have to write property and component editors.

{Create Component Editor}
type
  TComponentVersionEditor = class(TComponentEditor)
    procedure ExecuteVerb(Index: Integer); override;
    function GetVerb(Index: Integer): string; override;
    function GetVerbCount: Integer; override;
  end;

procedure TComponentVersionEditor.ExecuteVerb(Index: Integer);
begin
  case Index of
    0 : ShowComponentVersion(Component.ClassName, DefComponentVersion);
  end;
end;

function TComponentVersionEditor.GetVerb(Index: Integer): string;
begin
  case Index of
    0: Result := 'Show version';
  end;
end;

function TComponentVersionEditor.GetVerbCount: Integer;
begin
  Result := 1;
end;

{Create property Editor}
type
TComponentVersionProperty = class(TStringProperty)
public
  procedure Edit; override;
  function GetAttributes: TPropertyAttributes; override;
end;

procedure TComponentVersionProperty.Edit;
begin
  ShowComponentVersion(GetComponent(0).ClassName, DefComponentVersion);
end;

function TComponentVersionProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog, paReadOnly];
end;


procedure Register;
begin
  RegisterComponentEditor(TAboutComponent1, TComponentVersionEditor);
  RegisterComponentEditor(TAboutComponent2, TComponentVersionEditor);
  RegisterPropertyEditor(TypeInfo(String), TAboutComponent1, 'About', TComponentVersionProperty);
  RegisterPropertyEditor(TypeInfo(String), TAboutComponent2, 'About', TComponentVersionProperty);
end;


For more information install component in aboutcom.pas and see Demo (\012)

6. Reindex all indexes in the alias in Delphi 1.0 (Demo)
Q:
I saw then example to reindex a table, but I am developing a project with
Delphi 1.0, and the method check is not compatible with that Delphi version.
Could you help me by showing me how to reindex tables in Delphi 1.0?
A:
There is only one difference between version of that app for Delphi 1.0 and Delphi 2.0 (3.0 and C++ Builder).
All necessary functions are declared in one unit in Ver 2.0 - BDE.int.
For Delphi 1.0 you have to declare:
dbierrs.int - for BDE constants
dbiprocs.int - for BDE  functions
dbitypes.int - for BDE types.
In other words: BDE.int (32)  = dbierrs.int + dbiprocs.int + dbitypes.int (16).
Like: Windows.pas(32) = WinProcs.pas + WindTypes.pas (16)

See Demo (\002.d16)


Any comments and suggestions
please e-mail: rsd@tibc.tula.ru
or use feedback: http://www.rsd.pp.ru/