                        - February '96 Issue -

Delphi-Talk: delphi-talk@bridge.net the very best of delphi-talk.
Summary February 1996
Final Update.

These questions and answers are taken from the delphi-talk of
delphi-talk@bridge.net and was built up from:
Marko Tietz
tietz@mailserv.rz.fh-merseburg.de
tietz@wing.tgm.ac.at

You can read it or download it from:
http://www.fh-merseburg.de/~tietz/delphi.html, Delphi Superpages
(http://sunsite.icm.edu.pl/~robert/delphi/index.html) and others.

I can't guarantee that all answers are correctly! Please use all information
at your own risk!

For subscribing to Delphi-Talk:
Send a mail to Majordomo@bridge.net and write into body (subject line will
not be processed!) subscribe delphi-talk or have a look at http://www.qns.com
/html/~delphi.

I dedicate this file to Claudia Junge also known as wurli ;-)



                      -  DELPHI KNOWLEDGE BASE -

You can also download all dtXXXX.txt files as a Paradox database. It is called
Delphi Knowledge Base. Look at http://www.fh-merseburg.de/~tietz/delphi.html
dkb*.zip. * stands for the actual version.

Delphi Knowledge Base archive contains:
- all questions and answers from dtXXX.txt files in a Paradox database,
- a database reader, which allows you to read, search, copy questions and
  answers
- a Paradox for Windows form to handle database within Paradox for Windows 5.0
  or Paradox for Windows 5.0 Runtime.

Delphi Knowledge Base current version:
- Database with all questions and answers from September 1995 to November 1995
- Database reader version 1.6
- Paradox form version 1.0

Delphi Knowledge Base is free for all private programmers! I hope you enjoy it!

Delphi Knowledge Base 2.0 - The future watch:
- DKB will appear as 32-bit application for Windows 95 and Windows NT.
- DKB should be more than a database reader. Edit your own categories, answers
  and questions to paradox table within dkb.
- Uses BDE 32 bit.
- Will be out in May / June 1996.


                     - DELPHI KNOWLEDGEBASE WinHELP -

There is a nice WinHelp-File from Eric Miles (milese@usafe14.ramstein.af.mil)
on Delphi Superpages. It contains all questions and answers from dt0995.txt to
dt1295.txt. Good work, Eric!



Mail me:
- if you find this file useful
- if you want make some suggestions




=============================================================================

 Contents

 1. Defining a BDE Alias in Code
 2. Destroy dynamically-created components
 3. Which button on panel is the Sender?
 4. Reading a byte from the parallel port
 5. Existing instance of a program already running
 6. How to add a gruop to the program manager
 7. Publishing properties that are sets
 8. Selecting MDI children with TabSet
 9. Mouse Coordinates / Lost Focus
10. WinExec
11. Overriding Virtual Methods
12. DBGrid resize
13. Dialog box title
14. Right Text Alignment in edit box
15. Iterating thru subdirectories
16. Pointer to a function
17. How to drop down a combo box list
18. Drag and drop out of a TScrollBox
19. Path stored in BDE Alias
20. Copy one memo field to another
21. Pass a function as a parameter
22. Forms / Scaled
23. Find a record in an SQL dataset
24. Multi line Hints
25. Shortened Directory label
26. Most Efficient way to trim a String

=============================================================================

1. Defining a BDE Alias in Code
Q:
How to defining BDE aliases in code?

A:
This Technical Information document will help step thru concepts regarding
the creation and use of ALIASES within your Delphi Applications.

Typically, you use the BDE Configuration Utility BDECFG.EXE to create and
configure aliases outside of Delphi.  However, with the use of the TDatabase
component, you have the ability to create and use this ALIAS within your
application-- not pre-defined in the IDAPI.CFG.

The ability to create Aliases that are only available within your
application is important.  Aliases specify the location of database tables
and connection parameters for database servers.
Ultimately, you can gain the advantages of using ALIASES within your
applications-- without having to worry about the existance of a
configuration entry in the IDAPI.CFG when you deploy your 
application.  

Summary of Examples:
- ------- -- ---------
Example #1:  
	Example #1 creates and configures an Alias to use 
	STANDARD (.DB, .DBF) databases.  The Alias is
    	then used by a TTable component.
Example #2:
	Example #2 creates and configures an Alias to use
 	an INTERBASE database (.gdb).  The Alias is then
	used by a TQuery component to join two tables of
	the database.
Example #3:
	Example #3 creates and configures an Alias to use
                  STANDARD (.DB, .DBF) databases.  This example 
	demonstrates how user input can be used to
	configure the Alias during run-time.


Example #1:  Use of a .DB or .DBF database (STANDARD)

1.  Create a New Project.

2.  Place the following components on the form:  - TDatabase, TTable,
TDataSource, TDBGrid, and TButton.
 
3.  Double-click on the TDatabase component or choose Database Editor from
the TDatabase SpeedMenu to launch the Database Property editor.

4.  Set the Database Name to 'MyNewAlias'.  This name will serve as your
ALIAS name used in the DatabaseName Property for dataset components such as
TTable, TQuery, TStoredProc.

5.  Select STANDARD as the Driveer Name.

6.  Click on the Defaults Button.  This will automatically add  a PATH= in
the Parameter Overrides section.

7.  Set the PATH= to C:\DELPHI\DEMOS\DATA  (PATH=C:\DELPHI\DEMOS\DATA)

8.  Click the OK button to close the Database Dialog.

9.  Set the TTable DatabaseName Property to 'MyNewAlias'.

10.  Set the TDataSource's DataSet Property to 'Table1'.

11.  Set the DBGrid's DataSource Property to 'DataSource1'.

12.  Place the following code inside of the TButton's OnClick event.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
          Table1.Tablename:= 'CUSTOMER';
          Table1.Active:= True;
    end;

13.  Run the application.


***  If you want an alternative way to steps 3 - 11, place the following
code inside of the TButton's OnClick event.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
          Database1.DatabaseName:= 'MyNewAlias';
          Database1.DriverName:= 'STANDARD';
          Database1.Params.Clear;
          Database1.Params.Add('PATH=C:\DELPHI\DEMOS\DATA');
          Table1.DatabaseName:= 'MyNewAlias';
          Table1.TableName:= 'CUSTOMER';
          Table1.Active:= True;
          DataSource1.DataSet:= Table1;
          DBGrid1.DataSource:= DataSource1;
    end;

*****

Example #2: Use of a INTERBASE database

1.  Create a New Project.

2.  Place the following components on the form: - TDatabase, TQuery,
TDataSource, TDBGrid, and TButton.

3.  Double-click on the TDatabase component or choose Database Editor from
the TDatabase SpeedMenu to launch the Database  Property editor.

4.  Set the Database Name to 'MyNewAlias'.  This name will serve as your
ALIAS name used in the DatabaseName Property for dataset components such as
TTable, TQuery, TStoredProc.

5.  Select INTRBASE as the Driver Name.

6.  Click on the Defaults Button.  This will automatically add  the
following entries in the Parameter Overrides section.

	SERVER NAME=IB_SERVEER:/PATH/DATABASE.GDB
	USER NAME=MYNAME
	OPEN MODE=READ/WRITE
	SCHEMA CACHE SIZE=8
	LANGDRIVER=
	SQLQRYMODE=
	SQLPASSTHRU MODE=NOT SHARED
	SCHEMA CACHE TIME=-1
	PASSWORD=

7.  Set the following parameters

	SERVER NAME=C:\IBLOCAL\EXAMPLES\EMPLOYEE.GDB
	USER NAME=SYSDBA
	OPEN MODE=READ/WRITE
	SCHEMA CACHE SIZE=8
	LANGDRIVER=
	SQLQRYMODE=
	SQLPASSTHRU MODE=NOT SHARED
	SCHEMA CACHE TIME=-1
	PASSWORD=masterkey

8.  Set the TDatabase LoginPrompt Property to 'False'.  If you  supply the
PASSWORD in the Parameter Overrides section and set the LoginPrompt to
'False', you will not be prompted for the 
password when connecting to the database.  WARNING:  If an incorrect
password in entered in the Parameter Overrides  section and LoginPrompt is
set to 'False', you are not prompted by the Password dialog to re-enter a
valid password.

9.  Click the OK button to close the Database Dialog.

10.  Set the TQuery DatabaseName Property to 'MyNewAliias'.

11.  Set the TDataSource's DataSet Property to 'Query1'.

12.  Set the DBGrid's DataSource Property to 'DataSource1'.

13.  Place the following code inside of the TButton's OnClick event.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
          Query1.SQL.Clear;
          Query1.SQL.ADD(
		'SELECT DISTINCT * FROM CUSTOMER C, SALES S
		WHERE (S.CUST_NO = C.CUST_NO)
		ORDER BY C.CUST_NO, C.CUSTOMER');
          Query1.Active:= True;
    end;

14.  Run the application.


Example #3: User-defined Alias Configuration

This example brings up a input dialog and prompts the user to enter the
directory to which the ALIAS is to be configured to.  

The directory, servername, path, database name, and other neccessary Alias
parameters can be read into the application from use of an input dialog or
.INI file.

1.  Follow the steps (1-11) in Example #1.

2.  Place the following code inside of the TButton's  OnClick event.

procedure TForm1.Buttton1Click(Sender: TObject);
var
  NewString: string;
  ClickedOK: Boolean;
begin
  NewString := 'C:\';
  ClickedOK := InputQuery('Database Path', 
	'Path: --> C:\DELPHI\DEMOS\DATA', NewString);
  if ClickedOK then
  begin
    Database1.DatabaseName:= 'MyNewAlias';
    Database1.DriverName:= 'STANDARD';
    Database1.Params.Clear;
    Database1.Params.Add('Path=' + NewString);
     Table1.DatabaseName:= 'MyNewAlias';
    Table1.TableName:= 'CUSTOMER';
    Table1.Active:= True;
    DataSource1.DataSet:= Table1;
    DBGrid1.DataSource:= DataSource1;
  end;
end;

3.  Run the Application.


- -------------------------------------
See Also:

Delphi On-line help -->
	Database Properties Editor
	TDatabase



DISCLAIMER: You have the right to use this technical information
subject to the terms of the No-Nonsense License Statement that
you received with the Borland product to which this information
pertains.

[BORLAND (TM)]

-----------------------------------------------------------------------------

2. How to detect when form is minimized
Q:
How can I detect when a form is minimized. I tried to use form's
WindowState (or something like that) -property... It didn't change when I
minimized the form from the upper right corner of the form or from the
control menu, but It worked alright when I added a button that changed
the form's WindowState. Even the WinAPI call thought that the form
wasen't minimized when it actually was - Is this a bug in delphi and can
it be fixed?

A:


-----------------------------------------------------------------------------

2. Destroy dynamically-created components
Q:
I use a few dynamically-created buttons:
I destroy them using this code since I don't have any names assigned
to them at runtime.

for i := 0 to ComponentCount-1 do
  if (Components[i] is TButton) then TButton(Components[i]).Free;

However, it gives an "Index out of bounds" error.  this is really
surprising since I thought by using "if (Components[i] is....",
I destroy the dynamic button ONLY when the specific component
is confirmed to be a "TButton" type.
Also, this error seems to appear only when I call the "Free" method
for this dynamic button.
If I use something such as showing its name:

for i := 0 to ComponentCount-1 do
  if (Components[i] is TButton) then
      ShowMessage( TButton(Components[i]).NAME );

The above will work fine!  so I thought there may be some caveats
when it comes to freeing dynamically-created buttons.

A:
You have to think about the Components as a List, so when you cancel out
an item at a certain position, the position left vacant WON'T REMAIN EMPTY,
but it will be replace with the item immediately after and the ComponentCount
property, to which you index base the upper bound of your loop, should be
decreased as well!. A safer (and working way) is to walk thru lists beginning
from the bottom as in:

for i := Pred(ComponentCount) downto 0 do
  if (Components[i] is TButton) then TButton(Components[i]).Free;

[Emanuele Cipolloni, Leviathan@dlep1.itg.ti.com]

A:
I don't have an answer to the question "Why?" but I have had the same
problem when destroying all MDI child windows in a program I've been
working on.  The solution that I came up with, which may also
work for you is:

{ Remove current MDI child windows }
;	WHILE MDIChildCount > 0
DO	BEGIN
	;	MDIChildren[0].Close
	;	Application.ProcessMessages
        END

In the MDI child window OnClose event it sets the action to "caFree" which
will free the window.

The problem I had was that the windows weren't being freed. 

*FLASH* I just thought of this: Is the freeing of components tied
to Application.ProcessMessages?  I know that there are events triggered
when they get free'd, etc. My guess is that the messages need to be 
processed before the free is completed.  I think that this may be the
case because the following code produced an infinite loop:

{ Remove current MDI child windows }
;	WHILE MDIChildCount > 0
DO	MDIChildren[0].Close

When I added Application.ProcessMessages it worked and I moved on to
finish the section of code around it. I didn't question why it worked,
but was glad that it did.

[James Knowles, jamesk@spillman.com]

A:
There's a very subtle mistake here...

for i := 0 to ComponentCount-1 do
  if (Components[i] is TButton) then TButton(Components[i]).Free;

While you are freeing your components, the component list is updated, but the
upperbound of your for loop isn't. This means your freeing components that
don't exist any longer. Use:

for i := 0 to ComponentCount-1 do
  if (Components[0] is TButton) then TButton(Components[0]).Free;
***  Index i changed to 0 ! ***

To delete them all.

[Robert Cram, rcram@knoware.nl]

-----------------------------------------------------------------------------

3. Which button on panel is the Sender?
Q:
I've a panel on my application that has about 5 buttons.
A popup menu is attached to the Panel
When I right-click on the panel, how do I know which
button the right click fell on, and triggered the popup?
It seems that the Sender of the PopupMenu is the Panel.
But I need to know which button the click fell on.
I tried to put this in the button's OnMouseDown code but
it didn't work.

procedure TTBar2.ToolBarSpdBtn1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Shift = [ssRight] then ShowMessage('Right button!');
end;

A:
I think you might have to get rid of the PopupMenu property in the panel and
use the mouse down event of the button to do what you did above.  Also, I
think you could try to turn off the Auto property.  Then put this in your
OnMouseDown:

procedure TForm1.BitBtn1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  P: TPoint;
begin
  with Sender as TBitBtn do
    if Button = mbRight then
      begin
        P := Self.ClientToScreen (
           Point (Left + (Width div 2), Top + (Height div 2)));
        PopupMenu1.Popup (P.X,P.Y);
      end;
end;

[Matthew A. Gessner, mgessner@aristar.com]

-----------------------------------------------------------------------------

4. Reading a byte from the parallel port
Q:
I needed to read a byte from the parallel port (0x379).  I did it using inline
assemble language.  I noticed their is no way of doing it using the Windows API.
I have a sensor attached to this port.  It works fine but, is it safe to address
hardware directly in windows.  Windows is intercepting the call anyway (I
think).

Var
    BytesRead : BYTE;
begin
              asm                { Read port (LPT1) via Inline asm  }
                MOV dx,$379;
                IN  al,dx;
                MOV BytesRead,al;
              end;
BytesRead:=(BytesRead OR $07);   { OR and then XOR the data }
BytesRead:=(BytesRead XOR $80);  { to mask the unused bits  }

A:
It's no problem, use the Turbo Pascal command ...

 value:=port[$379]; { read from port }

and

 port[$379]:=value; { write to port }

The port command doesn't seem to be documented in online help, but it
certainly works!

[Andy Clark, andyc@rmpd-ngh.demon.co.uk]

-----------------------------------------------------------------------------

5. Existing instance of a program already running
Q:
How can a Delphi program check if an existing instance of it is already
running?

A:
Check the "hPrevInst" variable in the application startup. It should be
equal to 0, if the application is running for the first time.
This code seems to work for me.

MessageID := RegisterWindowMessage('Check for Prev');
if hPrevInst <> 0 then
begin
  PostMessage(hwnd_Broadcast,MessageId,0,0);
end
else
begin
  normal startup code.
....
end;

Then you can restore the original instance by adding this code.

procedure TForm.FormCreate(Sender: TObject);
begin
  Application.OnMessage := OnAppMessage;
end; 

procedure TForm.OnAppMessage(var Msg : TMsg;var Handled : Boolean);
begin
  if Msg.Message = MessageId then
  begin
    Application.Restore;
    TForm.SetFocus;
  end;{if Msg.Message = MessageId then}
end;

[-]

-----------------------------------------------------------------------------

6. How to add a gruop to the program manager
Q:
I am writing an installation utility , but i don't know how to add a group
and icon to the program manager.

A:
interface
procedure CreateGroup;

implementation

procedure TSetupForm.CreateGroup;
{ Uses the TProgMan component to install Program Manager group }
var
  ItemList: TStringList;
  GroupName: String;
  ItemName: String;
  i: word;
begin
  { Get the GroupName string from the INI file }
  GroupName := IniFile.ReadString('General', 'PMGroup', '');
  { If there is one, then install group }
  if GroupName <> '' then begin
    ItemList := TStringList.Create;
    try
      { read items to be installed }
      IniFile.ReadSectionValues('PMGroup', ItemList);
      with TProgMan.Create(Self) do
      try
        CreateGroup(GroupName);
        for i := 0 to ItemList.Count - 1 do begin
          { get file name }
          ItemName := Copy(ItemList.Strings[i], 1, Pos('=',
                           ItemList.Strings[i]) - 1);
          { append file name to path and add the item }
          AddItem(GetTarget(ItemList.Values[ItemName][1]) + ItemName, ItemName);
        end;
      finally
        Free;
      end;
    finally
      ItemList.Free;
    end;
  end;
end;

[Rick Roberts, rroberts@mnsinc.com]

-----------------------------------------------------------------------------

7. Publishing properties that are sets
Q:
I need to publish a property that is a set such as

 type
   myset =  set of (msThis, msThat, msThese, msEtc);

   myClass = class(TComponent)
     fMySetProperty : myset;
   published
     MySetProperty : myset read fMySetProperty write fMySetProperty;
   end;

the compiler lets me put this guy in the public portion of the
declaration, but not published. Says that it can't be published.

A:
I think your problem is that any field or method that is not explicitly
put under another kind of protection is assumed to be published, I
suspect what you meant to do was:

myClass = class(TComponent)
private { !! you omitted this protection directive }
  fMySetProperty : myset;
published
  MySetProperty : myset read fMySetProperty write fMySetProperty;
end;

[Stephen Posey, SLP@uno.edu]

-----------------------------------------------------------------------------

8. Selecting MDI children with TabSet
Q:
I have a TabSet on a MDI application, and I want to have the
ability to select any of the open MDI children using this tabset.

A:
It seems that MDI children don't respond to the
same Windows messages that other windows do. This is what
I had to do in order to select a specific MDI child
and make it active. Here I'm reading from an TINIFile
component and activating the specific MDI child:

{
Set active MDI child window. We have to post a message
to the Windows API because MDI child windows respond
to an abnormal set of window messages.
}
;	i := ReadInteger( 'Main', 'ActiveMDIChild', -1 )
;	IF (i>=0) AND (i<MDIChildCount)
THEN	POSTMESSAGE( Self.Handle, WM_MDIACTIVATE,
		MDIChildren[i].Handle, 0 )

[James Knowles, jamesk@spillman.com]

A:
MDI forms are handle by a different default Handler (defMDIProc).  Also,
there is always more than one way to skin a cat (sorry :>).  Mdi children
are part of an array called (oddly enough) MDIChildren.  You can do
something like this for instance.....

For i := 0 To Form1.MDIChildCount - 1 Do
begin
        If Form1.MDIChildren[i].Caption = 'This One!' Then
                {Activate the child form or perform operation here}
end;

The MDIChildren[x] is a pointer to an instance of an MDI child.  Using IS
and AS will allow you to use the methods and properties of any variety of
child forms.

[Donald A. Daugherty, bigd@raintown.com]

A:
1. When a file is opened a new Tab is added to the Tabset, and its
"caption" is set to the filename of that file . I then add the form as
an object to the tabset using the Tabset.AddObject command.

2. When I click on a tabset, I use the following to access the
correct file:
  TForm(TabsSet.Items.Objects[TabSet.TabIndex]).Show;

[Bruce W. Hearder, bwh@iinet.net.au]

-----------------------------------------------------------------------------

9. Mouse Coordinates / Lost Focus
Q:
I need to write an application in which I collect the absolute mouse
coordinates anywhere from the screen, on a mouse click, but my (always on
top) application does not cover the total screen. As soon as I click outside
the application I loose focus and can't update the new position.
Does anyone know an answer to one of these questions:
- how to prevent loosing focus or...
- how to get control over the mouse click events in windows or...
- how to make a application transparent, so I can maximise my window and see
the application below, but my application is in charge.

A:
Try to use:
  SetCapture(Form1.Handle);
  &
  ReleaseCapture;

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetCapture(Handle);
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  Caption:=Caption+'.';
  if length(Caption)>40 then
    ReleaseCapture;
{ You should return the power back to system in the right time, or ...}
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  SetCapture(Handle);
end;

[Henry Kowk, guoxy@public.szptt.net.cn]

-----------------------------------------------------------------------------

10. WinExec
Q:
   wProg := WinExec('thingy.exe', SW_SHOW)
   while (still_running(wProg))
      Application.ProcessMessage;
   ...

I need this 'still_running' piece, any one have any ideas.

A:
still_running(wprog) == (GetModuleUsage(wprog) > 0)

But your own process could be terminated, so better use
  repeat Application.ProcessMessages
  until (GetModuleUsage(wProg) = 0) or Application.Terminated;

[Tommy Ericson, teit@pi.se]

A:
     Try this code:

  var
    ProgramHandle: Word;
    PathName: PChar;

  ...

    ProgramHandle := WinExec(PathName, SW_SHOWNORMAL);
    While GetModuleUsage(ProgramHandle) <> 0 then
      Application.ProcessMessage;

  ...

[Kerry W. Podolsky, Kerry_Podolsky@alarmnet.com]

-----------------------------------------------------------------------------

11. Overriding Virtual Methods
Q:
Anybody know what the difference is between OVERRIDING a virtual
method and REPLACING it? I'm confused on this point.

A:
Say you have a class
  TMyObject = class (TObject)
and a subclass 
  TOverrideObject = class (TMyObject)

Further, TMyObject has a Wiggle method:
  procedure Wiggle; virtual;
and TOverrideObject overrides Wiggle
  procedure Wiggle; override;
and you've written the implementations for both.

Now, you create a TList containing a whole bunch of MyObjects and 
OverrideObjects in the TList.Items[n] property.  The Items property is a 
pointer so to call your Wiggle method you have to cast Items.  Now you 
could do this:

  if TObject(Items[1]) is TMyObject then
    TMyObject(Items[1]).Wiggle
  else if TObject(Items[1]) is TOverrideObject then
    TOverrideObject(Items[1]).Wiggle;

but the power of polymorphism (and the override directive) allows you to 
do this:

  TMyObject(Items[1]).Wiggle;

your application will look at the specific object instance pointed to by 
Items[1] and say "yes this is a TMyObject, but, more specifically, it is 
a TOverrideObject; and since the Wiggle method is a virtual method and 
since TOverrideObject has an overridden Wiggle method I'm going to 
execute the TOverrideObject.Wiggle method NOT the TMyObject.Wiggle 
method."

Now, say you left out the override directive in the declaration of the 
TOverrideObject.Wiggle method and then tried 

  TMyObject(Items[1]).Wiggle;

The application would look and see that even though Items[1] is really a 
TOverrideObject, it has no overridden version of the Wiggle method so 
the application will execute TMyObject.Wiggle NOT TOverrideObject.Wiggle
(which may or may not be what you want).

So, overriding a method means declaring the method with the virtual (or 
dynamic) directive in a base class and then declaring it with the 
override directive in a sub class. Replacing a method means declaring it 
in the subclass without the override directive.  Overriden methods of a
subclass can be executed even when a specific instance of the subclass
is cast as its base class.  Replaced methods can only be executed if the
specific instance is cast as the specific class.

[Brian Murray, murray@uansv3.vanderbilt.edu]

-----------------------------------------------------------------------------

12. DBGrid resize
Q:
I have a form. In that an Edit field, an SQL Query, a DBGrid and a Button.
I can write into the edit, and the Query result will put into the grid.
How can I resize the grid and the form to the fields size which appears in
the grid. The fields Which I select with the query does not fill the full
size of the grid or does not fit into it.

A:
You can change the size of a column at run-time by changing the DisplayWidth
property of the underlying field object...

        MyTableMyField.DisplayWidth := Length(MyTableMyField.value);

If you need to actually calculate the width of the entire grid, use the
following (from a tips library)...

function NewTextWidth(fntFont : TFont; const sString : OpenString) :

  integer;
var
  fntSave : TFont;
begin
  result := 0;
  fntSave := Application.MainForm.Font;
  Application.MainForm.Font := fntFont;
  try
    result := Application.MainForm.Canvas.TextWidth(sString);
  finally
    Application.MainForm.Font := fntSave;
  end;
end;


{ calculate the width of the grid needed to exactly display with no   }
{ horizontal scrollbar and with no extra space between the last       }
{ column and the vertical scrollbar.  The grid's datasource must be   }

{ properly set and the datasource's dataset must be properly set,     }
{ though it need not be open.  Note:  this width includes the width   }
{ of the vertical scrollbar, which changes based on screen            }
{ resolution.  These changes are compensated for.                     }

function iCalcGridWidth
  (
  dbg : TDBGrid { the grid to meaure }
  )
  : integer; { the "exact" width }

const
  cMEASURE_CHAR   = '0';
  iEXTRA_COL_PIX  = 4;
  iINDICATOR_WIDE = 11;

var
  i, iColumns, iColWidth, iTitleWidth, iCharWidth : integer;
begin
  iColumns := 0;
  result := GetSystemMetrics(SM_CXVSCROLL);
  iCharWidth := NewTextWidth(dbg.Font, cMEASURE_CHAR);
  with dbg.dataSource.dataSet do
    for i := 0 to FieldCount - 1 do with Fields[i] do
      if visible then
      begin
        iColWidth := iCharWidth * DisplayWidth;
        if dgTitles in dbg.Options then
        begin
          iTitleWidth := NewTextWidth(dbg.TitleFont, DisplayLabel);

          if iColWidth < iTitleWidth then iColWidth := iTitleWidth;
        end;
        inc(iColumns, 1);
        inc(result, iColWidth + iEXTRA_COL_PIX);
      end;
  if dgIndicator in dbg.Options then
  begin
    inc(iColumns, 1);
    inc(result, iINDICATOR_WIDE);
  end;
  if dgColLines in dbg.Options
    then inc(result, iColumns)
    else inc(result, 1);
end;

-----

I had to use the function NewTextWidth, rather than the Grid's
Canvas.TextWith as the Canvas of the Grid may not initialized when you need
to call iCalcGridWidth.

[code from LLoyd's Helpfile]

-----------------------------------------------------------------------------

13. Dialog box title
Q:
Is there any way to set the title for a dialogbox created with
MessageDlg(). In standard (not using CTL3D.DLL) dialogbox it can be
setted (in API-function).

A:
The dialog-caption gets set from inside the
CreateMessageDialog inside Dialogs.pas. It does a LoadStr to get
the Warningcaption, Cautioncaption and so on so you have two
choices: Either you modify Dialogs.pas or you change the strings
in the .res file.

[Leif Hellstroem, Leif@aivo.se]

-----------------------------------------------------------------------------

14. Right Text Alignment in edit box
Q:
One of these involved a form with a dbEdit box (TdbEdit) to
display the value of a float field) together with a standard
edit box (TEdit) to display the value of a float variable
(formatted as a text string).
The dbEdit box displayed the numerical value right-aligned,
which was most suitable indeed. I therefore also wanted the
standard edit box to do the same. However, I was unable to
get its text string to align itself in any other way than to
the left.
Can any of you Delphi gurus point me in the right direction
please. (How, for example, does Borland pull this stunt off
for dbEdit).

A:
TEdit1  = class(TEdit)
  public
    procedure CreateParams(var Params: TCreateParams); Override;
  end;

procedure TEdit1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style or ES_MULTILINE or ES_RIGHT;
end;

[Nitsan Kovalsky, nitsanko@netvision.net.il]

-----------------------------------------------------------------------------

15. Iterating thru subdirectories
Q:
I know how to iterate through the files in a particular directory. But, how
do you iterate through the subdirectories of that directory?

A:
procedure TFormList.RecurseDir(PathInicial: string);
var
  SearchRec: TSearchRec;
  Result: integer;
  tmpName: string;
begin
     DirectoryListBox1.Directory:=PathInicial;
     Result:=FindFirst(PathInicial+'\*.*', faAnyFile, SearchRec);
     While Result = 0 do begin
          if ExtOk(SearchRec.Name) then
             { if directory... }
             if SearchRec.Attr and faDirectory > 0 then
                { recurse in... }
                RecurseDir(PathInicial+'\'+SearchRec.Name)
             else begin
                tmpName:=PathInicial+'\'+SearchRec.Name;
                tmpName:=Copy(tmpName,
                        Pos(PathOrigen,tmpName)+Length(PathOrigen),
                        Length(tmpName)-Length(PathOrigen));
                ListBox1.Items.Add(LowerCase(tmpName));
             end;
          Application.ProcessMessages;
          Result:=FindNext(SearchRec);
     end;
     DirectoryListBox1.Directory:=PathInicial;
end;

[Javier Esteban Acuna, jacuna@datamar.com.ar]

-----------------------------------------------------------------------------

16. Pointer to a function
Q:
I passed a pointer pointing to a procedure to my DLL.  How do I call
the procedure if all I have is the pointer.  I did use makeprocinstance to
get the pointer.

A:
This is what I normally use to call some functions from a DLL:

1. Declare a type:

type
  TYourDLLFunc = function(Parm1: TParm1; Parm2: TParm2): TParm3;

2. Declare a variable of that type:

var
  YourDllFunc: TYourDLLFunc;

3. Get the DLL handle:

  DLLHandle := LoadLibrary('YourDLL.DLL');

4. Get the address of the function:

  @YourDLLFunc := GetProcAddress(DLLHandle, 'YourDLLFuncName');

5. Use the YourDLLFunc variable as you would normally use the function, e.g.:

  Parm3 := YourDLLFunc(Parm1, Parm2);

[Robert Cram, rcram@knoware.nl]

-----------------------------------------------------------------------------

17. How to drop down a combo box list
Q:
I have a drop down combo box on a form. I need to be able to drop the
list down programmatically when (if) the user types in to the edit part
of the combo box. I am aware that there is a Windows message to do this,
but how do I use it?

A:
The message is CB_SHOWDROPDOWN and to use it:

	SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, 1, 0) {Opens list}
	SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, 0, 0) {Closes list}

[Ron Manske, nova@q.continuum.net]

A:
Procedure THebEnhancedDBComboBox.ShowList;
begin
  if GetWindowLong(Handle, gwl_Style) and cbs_DropDown = cbs_DropDown then
    SendMessage(Handle, cb_ShowDropDown, 1, 0);
end;

{ Hides the list of an associated drop-down combobox. }

procedure THebEnhancedDBComboBox.HideList;
begin
  if GetWindowLong(Handle, gwl_Style) and cbs_DropDown = cbs_DropDown then
    SendMessage(Handle, cb_ShowDropDown, 0, 0);
end;

[Dov Landau, dlandau@actcom.co.il]

-----------------------------------------------------------------------------

18. Drag and drop out of a TScrollBox
Q:
I have a TScrollBox in which I load (in runtime) a variable quantity of TImage.
-> to drag a picture from the ScrollBox and drop it in an other frame (Form2)
1. I can't access to TImage because I don't know in which picture the user
clicks.
2. I don't know where I could write my methods dragdrop dragover etc ...
because my pictures are created at runtime.

A:
You can write a generic function for a single TImage, and assign that method
to each dynamically created TImage, thus:

procedure TForm1.GenericMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
    TImage(Sender).BeginDrag(False);
    {other stuff you might want to do}
end;

{....}


UmpteenthDynImage := TImage.Create(dummyImage);
UmpteenthDynImage.MouseDown := TForm1.GenericMouseDown;

This should be syntactically close. You can just assign each dynamic object
the GenericMouseDown method, and they'll all use it. The dummyImage owner
allows all dynamic objects to easily be destroyed by just destroying the
dummyImage.

[Dustin Caldwell, dustin@eddy.media.utah.edu]

-----------------------------------------------------------------------------

19. Path stored in BDE Alias
Q:
Does anyone know how to get the path stored in a BDE Alias without using
dummy components ?

A:
Use Session.GetAliasParams. You will get a Tstrings object from where you
can extract the value for 'PATH". Look in the help for TSession. The Session
object is
declared in the DB unit.

This code may work:


uses
     db;

var
     aliaspath : string[128];

begin
     aliaspath := Session.GetAliasParams['MyAlias'].values['PATH'];
end;

[George Blat, georgeb@brd.com]

A:
uses SysUtils,DbiProcs, DBiTypes;
...

function GetDataBaseDir(const Alias : string): String;
(* Will return the directory of the database given the alias
  (without trailing backslash) *)
var
  sp  : PChar;
  Res : pDBDesc;
begin
  try
    New(Res);
    sp := StrAlloc(length(Alias)+1);
    StrPCopy(sp,Alias);
    if DbiGetDatabaseDesc(sp,Res) =  0
    then Result := StrPas(Res^.szPhyName)
    else Result := '';
  finally
    StrDispose(sp);
    Dispose(Res);
  end;
end;

[Sven Lindhardt, svenlind@inet.uni-c.dk]

-----------------------------------------------------------------------------

20. Copy one memo field to another
Q:
How do I copy data in one memo field in Table1 to another memo field in
Table2?

A:
Table2.<MemoFieldName>.Text.Clear;
Table2.<MemoFieldName>.Text.Add(Table1.<MemoFieldName>.Text);

[Luis Hernandez, LUIS@ris.risinc.com]

A:
MemoField1.Assign(MemoField2);
Make sure the dataset is in Edit mode.

[Ryan Peterson, rpetersn@usit.net]

A:
One of the most reliable ways we have found to implement this transfer is to
use a TMemoryStream for the actaul transfer:

Var
   T : TMemoryStream
Begin
     T := TMemoryStream.Create;
     Table1<MemoField>.SavetoStream(T);
     Table2<MemoField>.LoadFromStream(T);
     T.Destroy;
End;

Now Transfering Between 2 TMemo Fields is another Story.  There you can rely
on using the Text property.

[The Frishbergs, frish@aracnet.com]

A:
Table2.Memo1.Lines.Assign(Table1.Memo1.Lines);

[Achim Kaluza, bprf41@mbag.sifi.daimlerbenz.com]

-----------------------------------------------------------------------------

21. Pass a function as a parameter
Q:
Does anyone know how can I typecast a function's address into a
LongInt? Or is there a way to pass a function as a parameter?
Here is what I'm trying to do:

 function DllFunction(p: Pointer): Integer;
 far; external 'mydll';

 function foo: integer;
 begin
 result := 1;
 end;

 procedure test;
 var
 l: LongInt;
 begin
 l := Addr(foo);  { Compile Error!!! I tried @foo and }
                  { LongInt(foo), and they won't work neither. }
 { This is what I need. }
 DllFunction(foo);  { Compile Error!!! It can't take a function}
                    { as the parameter. }
 end;

A:
Sounds like what you need is a procedural type. Assuming that
DllFunction() wants functions that look like what you've described
above, something like the following should work: 

type
  TMyFuncType = function : integer ;

var
  MyFunc : TMyFuncType ;

function foo: integer;
begin
  result := 1;
end;

begin
  MyFunc := foo ;
  DllFunction( longint( MyFunc )) ;

You may also be able to get away with
  DllFunction( longint( @foo )) ;

though I'm not sure about all of the memory issues that may be involved 
with a .DLL calling a routine in another code segment like this, you may 
need to declare foo as far for this to work, or export it from the unit 
or something.

Also, depending on how DllFunction() is written, you might be able 
to declare it thusly, so that it does the typecast implicitly:

function DllFunction( p: TMyFuncType ): Integer; 
  far; external 'mydll';

so you wouldn't need the dummy MyFunc variable or the @ operator.

[Stephen Posey, SLP@uno.edu]

A:
You can pass functions as parameters in Delphi/Pascal. However you have to
set up a type for the compiler to use. Try the following (I actually
compiled and tested this):

unit Unit1;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

type
	IntFunc = function: integer;

function DllFunction(iFunc: IntFunc): integer; far;
begin
    DllFunction := iFunc; {Notice this is a function call}
end;

function iFoo: integer; far;
begin
	iFoo := 1;
end;

procedure TestIFunc;
var
	i: integer;
begin
	i := DllFunction(iFoo);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
	TestIFunc;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
	Close;
end;

end.

[Jeffrey McArthur, j_mcarthur@bix.com]

A:
Two things you can do.  First, if you want to use longint to pass it, you
can use
   i := longint(@foo).

The other thing you can do is bypass all this longint stuff and call the dll
function with
   DLLfunction (@foo);

Note that if you're planning to call foo from within the DLL, you're
probably going to need a thunk to resolve DS; look at MakeProcInstance for
more info.

[Sid Gudes, cougar@roadrunner.com]

-----------------------------------------------------------------------------

22. Forms / Scaled
Q:
We're curious about what the relationship is among Scaled, PixelsPerInch
and dynamically creating forms...

A:
I think the documentation for "PixelsPerInch" is confusing, because in my
experience, this refers to the size in pixels/inch of the system's FONTS, 
not of the form.

For instance, Delphi's default PixelsPerInch property will typically be 96 
for any forms you create, REGARDLESS OF YOUR SYSTEM'S SCREEN RESOLUTION.  
On my system, this seems to be true whether I'm running at 640x480, 
800x600, 1024x768, etc.

However, when changing your screen's resolution, if you specify "Large 
Fonts" instead of "Small Fonts", Delphi's default PixelPerInch will now 
typically be 120 for any forms you create, again regardless of the 
resolution you're running at.

I think Delphi does this for the following reason:  Delphi needs to know 
both the PixelsPerInch of the form as it was designed and at runtime.  With 
this information, Delphi can determine if the form needs to be scaled in
order for all the text (i.e., fonts) to fit as it was designed.


Example:
-------

- You create a form using "Small Fonts".  You leave the PixelsPerInch
   setting at its default of 96.

- Your user tries to run your app using "Large Fonts".  This setup means
  that all the form's text would now be larger relative to the rest of the
  form, because of the larger font sizes.  The PixelsPerInch property of
  this user's setup would be 120.

If your form's "Scaled" property is set to False, Delphi will not do 
anything to compensate.  Your text will probably be larger than when you 
designed it, and some of it my be clipped because it won't fit.

However, if your form's "Scaled" property is set to True, Delphi will 
automatically scale the form by a factor of 120/96.  This way, the form has 
now "grown" to accomodate the larger fonts.  Everything will look as you
designed it.
 

NOTE:  This scaling will only take place if the user's PixelsPerInch 
property is different from the designer's.  As far as I know, this only 
happens when you choose a different font size when changing your screen's 
resolution.

In my case, my forms NEVER scaled themselves, whether at 800x600, 1024x768, 
etc., because I was always using "Small Fonts".  This really had me going 
for awhile.  My solution was to scale the forms manually, using 
ScaleBy(...).  

I suppose another solution would be to change the form's PixelsPerInch 
property at runtime, when the form is about to be created.  This would 
"trick" Delphi into thinking that the form was designed using a different 
PixelsPerInch, so it would adjust accordingly.  For example, changing a 
form's PixelsPerInch property from 96 to 80 during creation would cause 
Delphi to scale the form by a factor of 96/80.

[Bernie Mondschein, mondschein.bernie@ehccgate.sandoz.com]

-----------------------------------------------------------------------------

23. Find a record in an SQL dataset
Q:
Now, i want the user to be able to enter a letter or letters in an edit =
box and have the record pointer jump to the first occurance of a record =
whose index begins with that letter (partial find).  Something like =
this:

 ModelQuery.FindNearest([SpeedEdit.Text]);

Although this works perfectly with TTables, I can't get it to work with =
TQueries.

A:
In the edit boxes change event, do:

Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add('SELECT * FROM <table> WHERE <field> Like ''' +
SpeedEdit.Text + '*''');

Query1.Open;

All records that begin with whatever is entered into the edit box will be
returned.

[Christopher Matheny, cmatheny@greenapple.com]

-----------------------------------------------------------------------------

24. Multi line Hints
Q:
Is there a way to format the HINT property such that it spans
multiple lines versus a single long line?

A:
Not in the Object Inspector, but you can do it programmatically:

   edit1.hint := 'This is line 1'#13#10'This is line 2';

You could also do this as a hybrid by making a convention that a certain
character sequence represents a new line and replacing that at form create
time.  Eg., you could have the Hint property for Edit1 set to 'This is
line1::This is line 2' then in your FormCreate you can do:

   while true do begin
      i := pos ('::', edit1.hint);
      if (i = 0) then break;
      edit1.hint[i] := #13;
      edit1.hint[i+1] := #10;
   end;

Note that even if there are multiple lines the limit for a Hint is 255
characters.  You can also make the above a procedure and do it for several
components.

[Sid Gudes, cougar@roadrunner.com]

-----------------------------------------------------------------------------

25. Shortened Directory label
Q:
If the directory label is:
c:\windows\media\temp\abc\sound\chime.wav
I would like the label to appear as:
c:\windows\..\sound\chime.wav
and not the whole chunk of filename.
Is there any way to accomplish this easily?

A:
I developed a procedure, that does something like that.
It shortens the path, when it and the current path have
the same drive and/or directory in parts.
It's really useful for making the pathname easier to read
and understand.
I've written it for a hex-editor in Borland Pascal and I haven't been
using it for a while, but it should work flawlessly.

function shortenfilename(s : string) : string;
var drive,curdrive : string[2];
    dir,curdir : string[80];
    name : string[20];
    ext : string[5];
    i : byte;
begin
  for i:=1 to length(s) do s[i]:=upcase(s[i]);
  s:=fexpand(s);
  fsplit(s,dir,name,ext);
  drive:=copy(dir,1,2);
  dir:=copy(dir,4,length(dir)-3);
  getdir(0,curdir);
  curdrive:=copy(curdir,1,2);
  curdir:=copy(curdir,4,length(curdir)-3)+'\';
  if drive=curdrive then begin
    if copy(dir,1,length(curdir))=curdir then begin
      i:=length(curdir);
      if length(dir)<>i then dir:=dir+'\';
      shortenfilename:=copy(dir,i+1,length(dir)-i-1)+name+ext;
    end else shortenfilename:=copy(s,3,length(s)-2);
  end else shortenfilename:=s;
end;

[Stephan Meyer, sm006ns@munich.netsurf.de]

-----------------------------------------------------------------------------

26. Most Efficient way to trim a String
Q:
Anyone has a _very_ fast algorithm to Right-trim, left-trim,
and All-Trim a string?

A:
function PTrim(PS: PChar): Integer;
var ArCh: PByteArray absolute PS;
    i,f: Integer;
begin
  if ArCh^[0] = 0 then
  begin
    PTrim := 0;
    Exit;
  end;
  i:=0;
  f := StrLen(PS);
  while (f > 0) and (ArCh^[f-1] = 32) do Dec(f);
  while (i < f) and (ArCh^[i] = 32) do Inc(i);
  StrLCopy(PS, PS+i, f-i);
  PTrim := f-i;
end;
===============================================
PROCEDURE Trim(VAR S : String; C : Char);
BEGIN
  if S = '' then Exit;
  WHILE S[length(S)] = C DO Dec(S[0]);
END;
===============================================
PROCEDURE TrimLead(VAR S : String; C : Char);
VAR P : Byte;
BEGIN
  if S = '' then Exit;
  P := 1;
  WHILE (S[P] = C) AND (P <= length(S)) DO Inc(P);
  CASE P OF
    0 : S[0] := #0; {la stringa era 255 di C!}
    1 : ; {non trovato}
    ELSE
      Move(S[P], S[1], succ(length(S) - P));
      Dec(S[0], pred(P));
  END;
END;
=====================================================
function Trim(S : string) : string;
var I : Word;
    SLen : Byte absolute S;
begin
  while (SLen > 0) and (S[SLen] <= ' ') do Dec(SLen);
  I := 1;
  while (S[I] <= ' ') and (I <= SLen) do Inc(I);

  CASE I OF
    0 : SLen := 0;  {was of 255 blanks}
    1 : ;
    ELSE Move(S[I], S[1], succ(SLen - I));
         Dec(SLen, pred(I));
  END;
  Trim := S;
end;

[-]

A:
procedure Trim(var s: String);
var
  i: Integer;
begin
{ s > '' ? }
  if Length(s)>0 then begin
{ Trim leading }
    i:=1;
    While (Length(s)>=i) and (s[i]<=' ') do
      Inc(i);
    if i>Length(s) then begin
{ Trap the empty string, remove all and exit }
      s:='';
      Exit;
    end;
    if i>1 then begin
      s[0]:=Chr(Length(s)-i+1);
      Move(s[i],s[1],Length(s));
    end;
{ Trim Trail }
    i:=Length(s);
    While (i>0) and (s[i]<=' ') do
      Dec(i);
    s[0]:=Chr(i);
  end;
end;

[Leif Hellstroem, Leif@aivo.se]

-----------------------------------------------------------------------------
