Pascal

Pascal

Checking the number of components at runtime
Override of existing identical methods
Change location of hints
Set the mouse cursor at the focused button automatically
Cut, copy and paste: one method for different TEdit-controls
Enterkey instead of Tabkey
Alt-? key combination in an aboutbox
Bit-wise manipulation
Array of controls
Delay, pause, wait
Process Messages
Tab key handling
Changing font style problem
Testing event handler for existence
Toggle NumLock CapsLock Keys
Is a Bit set
Inherited free
Attach procedures to components created
Simulating a pause in a loop
Copying from a TEdit to an integer field
Using menu options to cut, copy, and paste
Overriding the Create method
Check whether mouse is over client area
Name / caption property & emulating the caption
Override vs Redefine
Why no free
Mouse Coordinates / Lost Focus
Overriding Virtual Methods
Copy one memo field to another
Sort a TStringList by Numerical Value
Canceling The Key Press
GetKeyBoardState
Obtain last digits in a number
Callback functions
Dynamically assigning event handlers
Sendkey function
Case Of Statement
Pointer Arithmetic in Delphi
Edit Mask for decimals
How to tell who Sender is
String property values
Object has a property
TList freeing it's items
Pionter arithmetic
Array of const
ReadLn longer than 255 characters
Dynamic memory allocation
Arrays, dynamically allocating
Object Pascal - Typecasting
Object Pascal - Typecasting (2)
multi-dimensional dynamic arrays
Dynamic array of records


Checking the number of components at runtime

Question


Is there a way of checking how many components exist (at runtime) for the

entire application without having to run through every other components

ComponentCount property.



Included in the above question is to find components that are on forms

that are only created at runtime.  (This can exclude any additional

components created on these created forms at runtime.).



I would have thought that TApplications ComponentCount property would

indicate this but it does not.



Answer


A:

You can find out how many Components your application uses (which

have already been created...)



Every form that is in existence is stored in Screen.Forms, which is an array of Forms.

Every form has a ComponentCount.

So, you would use a routine like this:



function GetTotalComponents : Integer;

var

  TotalComps,

  CurForm : Integer;

begin

  TotalComps := 0;



  for CurForm := 0 to (Screen.FormCount - 1) do begin

    TotalComps := TotalComps + Screen.Forms[CurForm].ComponentCount;

  end;



  Result := TotalComps;

end;



In fact, I just ran and compiled it and it worked... although I

didn't check it for components that are made at runtime.  I do know

that it doesn't give an accurate count for Forms that are in the .dpr

file, but which aren't autocreated. So I'll have to work on that.



Also, I did a check of what Application.ComponentCount gives me, and

it gives you the # of forms/windows that it has. This includes one

invisible window which is the application window.




Override of existing identical methods

Question


I need to add a couple of properties, methods and events to each of the standard components and override a couple of the existing methods. Since all of these except the overriden ones are identical for all or most of the components, it makes sense to write a class which handles these.



Answer


A:

You could write a unit in which you override each of the standard

components that you would like to modify.  However, instead of

copying virtually identical code into every component, just add

calls to a few standard procedures that all of the components

share.



E.g.:



procedure SharedProcedure(Sender : TComponent);

begin

  {do standard processing to manipulate Sender component}

end;



procedure TMyLabel.Whatever; {overridden method of TLabel descendant}

begin

  SharedProcedure(Self);

end;



procedure TMyListBox.Whatever; {overridden method of TListBox descendant}

begin

  SharedProcedure(Self);

end;



This way, all you duplicate are the procedure calls.  The bulk of

the code just resides in a single place.  This is almost as good

as having the multiple inheritance...almost.




Change location of hints

Question


I have been looking for the property that decides where the

popup hint window should locate itself. Is there a way

to change the location?



Answer


A:

There is a neat trick to popup hints.  You can bypass the popup altogether and control in code.  For example: have a panel with the visible property set to false.  Then have a routine at the form level that checks the item under the pointer.  If it passes over something you want then set the panel to be visible.  You can vary the size and location of the panel if you want to.  I think that should be done at

the form level.



You could also set up a status bar to show all hints.  I use a

combination of the popup hint and status bar.  To show both hint messages make the hint something like this:

This is the popup part of the hint|This is the status bar hint.




Set the mouse cursor at the focused button automatically

Question


I am trying to programmatically move the mouse to the currently focused button



Answer


A:

In the OnEnter event for the button(s)...



	cntl : TControl;

	...

	cntl := TControl( Sender );



1.  Calculate the center of the button ( x, y )



	Ex: if the button is Height 24 and Width 24 then the center

	    is 12 and 12.



	xCenter := cntl.Left + ( cntl.width / 2 );

	yCenter := cntl.Top + ( cntl.height / 2 );



2.  Place this value into a TPoint.



	ptBtn : TPoint;

	...

	ptBtn := Point( xCenter, yCenter );



3.  Obtain the Screen coordinates for the center of the button



	ptBtn := cntl.Parent.ScreenToClient( cntl.ClientToScreen( ptBtn ) );



4.  Set the mouse  ( cursor ) pos to ptBtn and it should move the cursor to

the center of the currently focused button.



	SetCursorPos( ptBtn.X, ptBtn.Y );




Cut, copy and paste: one method for different TEdit-controls

Question


The Delphi manual illustrates calling the cut, copy or paste methods of an

edit control using either a menu choice or the usual hotkeys. The example

goes like

  If Sender = Control then

  Control.CutToClipboard;

For forms with several edit controls, the suggestion is to use a case

statement to determine the sender and then access the method of the sender

as above. Is there another way that can be used to access any control in

one statement?



Answer


A:

The trouble is that sender is TObject. TObject does not have a cut to

clipboar method. If you think that TObject is a type which does have

such a method, you can convert it. For example,



procedure objectCutToClipboard(sender as TObject);

var

  myEdit : TEdit;

begin

  if sender is TEdit then myEdit := sender as TEdit;

  myEdit.cutToClipboard;

end;



You can also do this:



procedure objectCutToClipboard(sender as TObject);

begin

  if sender is TEdit then 

  (myEdit as TEdit).cutToClipboard;

end;



The first method is better if you will be using sender several times; 

that way you do not need to convert it to TEdit each time.



Try this:

(1) On a form place several edit boxes; names can be anything.  Put some 

text in each edit box.

(2) Add two more edit boxes named nEdit and rEdit.

(3) Add a list box named ListBox1 (default);



(4) Put this in the MOUSEUP routine for the first Edit box.



procedure TForm1.Edit1MouseUp(sender: TObject; Button: TMouseButton; 

Shift: TShiftState; x,y: integer);

var

  astr,aName: string;

  anEdit: TEdit;

begin

  if button <> mbRight then exit;

  if sender is TEdit then

    begin

      anEdit := sender as TEdit;

      clipboard.clear;

      anEdit.selectAll;

      anEdit.cutToClipboard;

      rEdit.text := '';

      rEdit.pasteFromClipboard;

      nEdit.text := anEdit.name;

      ListBox1.items.add(nEdit.text + ' : ' + rEdit.text);

    end;

end;



(5) Set the MOUSEUP routine for ALL of the edit boxes except for rEdit 

and nEdit to the routine for the first Edit box. (Just click on the down 

Arrow and choose it.)



Now a right click in any one of those Edit boxes will be processed by the 

same routine. The right click will select all of the text in the listbox, 

paste it into rEdit, get the name of the Edit that you clicked in, place 

the name into nEdit, and then place a carefully constructed combination 

of the two into the List Box.



(6) Now, add this routine to the MOUSEUP routine ( or click ) of List Box

1.  This routine shows you how to reverse the process and put the text 

back into the correct box.



procedure TForm1.ListBox1MouseUp(sender: TObject; Button: TMouseButton; 

Shift: TShiftState; x,y: integer);

var

  astr,lStr, cmptStr: string;

  anEdit: TObject;  {Must be TObject, not TEdit!}

  ii,aNo,l: integer;

begin

  {get the selected string }

  ii := ListBox1.itemIndex;

  lstr := ListBox1.items[ii];

  {split the string into a control name and the text }

  l := length(lstr);

  aNo := pos(':', lstr);

  cmptStr := copy(lstr,0,aNo-2);

  aStr := copy(lstr,aNo+2, l - (aNo + 1));

  nEdit.text := cmptStr;

  rEdit.text := astr;

  { These two lines are the really important ones! }

  anEdit := findComponent(cmptStr);

  (anEdit as TEdit).text := astr;

end;



If I have typed this all correctly, a click on any listBox line should 

put the text back into the listbox from whence it came.



Some notes:



You need to use MouseUp and NOT click in the Edit routine because you 

need to filter for the right button and click does not give you the info.

Unfortunately, a right click in a list box does not select a line.  Item 

index remains -1 and will give you an error.



FindComponent(aStr) returns a tObject; hence the need to type anEdit as 

TObject and cast it as TEdit later.



If you are going to use the (tObject as TEdit).something method, make 

sure that you use the parentheses.



I hope that this helps you out.  I really had to spend a lot of time 

struggling with it some time ago to figure it out.




Enterkey instead of Tabkey

Question


I would like the Enter key to work as the Tab key when an Entryfield is focused.



Answer


A:

First, make sure the Form's KeyPreview property is set to True, then attach

the following procedure to the form's OnKeyPress event (easiest way is go to

the form's events tab, then double-click in the OnKeyPress event, Delphi will

then create the procedure header and body for you):



  procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

  begin

     if (Key = #13) then

     begin

	Key := #0;                       { Eat the enter key }

	Perform(WM_NEXTDLGCTL, 0, 0);

     end;

  end;




Alt-? key combination in an aboutbox

Question


I'm trying to obtain the same that Delphi developers done in Delphi About

Box...I'd like to trap the ALT-? key pressing to show hidden information

such revision number etc...how can I do this?



Answer


A:

Anyway here's some code which does similar. However if you have the

shift=[ssalt] condition it means that the keypresses are intepreted by the

default handlers and every keypress generates a beep.

You need to set the previewkey option on the form.



In the key down event:



procedure TAboutBox.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

var

   i:integer;

   working:integer;

begin

     if (shift=[ssalt]) and (key>=$41) and (key<=$5A) then begin

     s:=s+chr(key);

     working:=0;

     for i:=1 to 4 do begin

         if (s=copy(strings[i],1,length(s))) then working:=-i;

         if (s=strings[i]) then working:=i;

         end;

     if working=0 then s:='';

     if working>0 then showmessage(strings[working]);

     end;

end;



In the form create event: {to ensure that the string is initially empty}



procedure TAboutBox.FormCreate(Sender: TObject);

begin

     s:='';

end;



At the top of the form to define the various messages:

type

    Tst=array[1..4] of string;

const

     strings:Tst= ('HELLO','BYE','VERSION','PROGRAMMER');





In the public section of the form:

  public

        s:string;



A:

Place a button off-screen that has a shortcut of Alt-?. Since it's an about

box, I assume that it's not resizeable and thus the user will never see this

button. Trigger your version and revision info off of the OnClick event of

this button.




Bit-wise manipulation

Question


Can some one explain how to do bit-wise manipulation?

Answer


A:

Ok I will give it a 4 bit example.

Bit masks are usually used for things like properties.



For a 4 bit number the max value is 1+2+4+8 = 15 (or 1111). If one

property is attached to each bit ( 1 = True, 0 = False) the resultant

number is always unique.



Now we wish to find the status of a particular bit. It would be

fairly tedious to convert to binary or write an algorithm to do this

in decimal. Lets assume we we have a number say 7 (0111) and  want the

setting of the third bit (from the right). The value we wish to check

for is 0100 (4 in decimal).



So we are comparing two values    0100 and 0111    literally bit by

bit. Here are some sample results which should show how to use

bitwise operators



0100               0111



4          AND       7          =  0100   (4)

                             (=4 so the bit we want is set to 1)

4           OR          7          = 0111   (7)

                             (not much use in this example)

4           XOR       7          =  0011  (3)



If you are following the thread I think you are AND is what you are

looking for. As you know exactly what you are checking for this is

the only bit that you set in your comparison value which means that

the only possible result is 0 or (in this example) 4



So your code would be

If  (YourCheckVar AND 4) = 4 Then

     3rd bit is set

Else

     3rd bit is not set;




Array of controls

Question


In VB when I make a Copy and Paste of a control, VB proposes to build an array

of controls. Do you know how to do something similar in Delphi? Warning: I

don't want to share a methode but to access to several controls in a loop.

Answer


A:

You can have an array of objects, but the visual designer won't build it for

you automatically. IMO, this is a "good thing", not a limitation. In every

case where I wanted to do this the array was more complex than a single

control (in one real world example it was a dynamic array of up to 26 bevels

that each had an image, a tick box, a label and a gauge within it) and the

layout on the form is typically more complex than a vector layout (in that

example it was a two-dimensional array which depended on the size of the

client area of the window at the moment and had to feedback adjustments to the

window size based on the existent suite of hardware and share devices).



My suggestion is to build the single prototype of the control complex that you

need on the form, but make it invisible and not enabled. Then define a

descendent of TObject that holds all of the various components involved (this

step may be omitted if the component structure has its own top level

hierarchical object like a TPanel). Finally, define an array of that object

and put a variable of this type on the form.



At execution time you can instantiate components to build each array entry

(copying the prototype to preset most properties) and set its attributes for

position. Remember to insert these dynamic components into their owner so that

they'll appear (the owners of the prototype are where to insert the new

controls). It may sound complex, but it is really simple and takes little code.

Yet it offers much more control than the VB array does -- that's the difference

between a real language like ObjectPascal and a RAP tool like VB.


Delay, pause, wait

Question


How do you get your Delphi app to do nothing for a period of time?

Answer


A:

Uses

     ....

     Winprocs

     ....;



Procedure delay(millisecs : longint);   { delay for given milliseconds }

var

     endtime   : longint;

begin

     endtime := gettickcount + millisecs;

     while endtime - gettickcount < 0 do

          Application.ProcessMessages;

     end; { delay }



A:

procedure Wait;

var oldTime: LongInt;

begin

  oldTime := GetCurrentTime;

  repeat

   { whatever}

  until GetCurrentTime - oldTime >= yourDelay;

end;



To which I would add:



As current Windows does not have pre-emptive multi-tasking,

it's normally good event-handling manners to allow Windows

to jump in to the gap, so the { whatever } above should

usually read:



          Application.ProcessMessages;





A:

Procedure Delay(DTime : LongInt);

Var

  L : LongInt;

Begin

  L := GetTickCount;

  While (Abs(L-GetTickCount) < DTime) do;

End;



The usage is "Delay(1000'th of a second)", for example

Delay(5000) will wait 5 seconds.


Process Messages

Question


Can anyone tell me is there an exact situation that

Application.ProcessMessages should be called, or do I just add

it in when I start getting some weird behaviour from my app.?

Answer


A:

In the normal course of events (unintentionally appropriate opening phrase)

we don't really need to call Application.ProcessMessages because the built-in

event loop provided by the Delphi framework hands control back to Windows

often enough anyway.



However, when coding a time-consuming loop it is sometimes advisable (and good

Windows manners) to insert a call to Application.ProcessMessages into the loop.

Without it everything will work, but the user will be prevented from

interacting with Windows (e.g. to switch to Program Manager and launch another

application) until the time-intensive activity is finished.



Inserting a call to Application.ProcessMessages into a time-consuming loop is

a particularly good idea if you want the user to be able to cancel the

operation. Without it your Cancel button won't get a look in.



A:

You would use it when some deep processing is occurring in your

app, like a deep loop, calculations, etc. This command say to Delphi to

let Windows manages his pending messages on his stack, so giving control

to other applications running simultaneously (remember, Windows 3.11 is

a using a cooperative multitasking, so your app must give control back

to Windows).


Tab key handling

Question


I am not able to trap the Tab key in my OnKeyDown Event handler.

Answer


A:

procedure YourFormName.FormKeyPress(Sender: TObject; var Key: Char);

begin

    {If Key = #13 Then

    {Begin

      if (ActiveControl is TEdit)

      or (ActiveControl is TDBEdit)

      or (ActiveControl is TDBListBox)

      or (ActiveControl is TDBComboBox)

      or (ActiveControl is TDBLookupList)

      or (ActiveControl is TDBLookupCombo) then

        begin

          SelectNext(ActiveControl as tWinControl, True, True );

          Key := #0;

        end;

    end;}

end;


Changing font style problem

Question


I have a routine for changing the font style of a TPanel caption to

Strike Out...BUT it doesn't work - I get a type mismatch error:



procedure PanelIgnore(cp : TPanel)

begin

  with cp do begin



     {various attributes set}

     Font.Style := fsStrikeOut;

  end;

  cp.Update;

end;

Answer


A:

Try the following...it should work:



with cp do begin

   { various attributes set }

   Font.Style := Font.Style + [fsStrikeOut];  { to add strike out }

   Font.Style := Font.Style - [fsStrikeOut];   [ to remove the strike out }

   end;



Note: this method will work for Bold, Italic and Underline, as well.


Testing event handler for existence

Question


I want to test (at runtime) whether an event has a handler attached to it.

However, if I try to write something like:



   if bitbtn1.onClick = nil then



it tries to execute the event (asks me for an argument).  I've also tried



   if not assigned(bitbtn1.onClick) then



but this gives me "Invalid Variable Reference".  I've tried assigning things

to pointers and trying to cast to a longint, all to no avail.  Is there any

way to do this?

Answer


A:

I wonder if the contraption



     if @bitbtn1.onClick = nil then



might work. This is what Borland recommends when you are dealing with a

procedural variable. After all, events are just procedural variables, although

8 bytes long.



A:

"bitbtn1.onClick" is defined as a _property_, not an actual pointer to a

method.  You can assign values to it (e.g., "bitbtn1.onClick :=

BitBtn1Click"), but I don't believe there is a simple way to read the

assigned value back again -- at least not that I'm aware of.  If there IS a

way, I hope someone will post it, as I'd like to know, too!



Anyway, I started screwing around with the stuff in the TypInfo unit, and I

came up with the following solution, which seems to work for me:



1.  Add the "TypInfo" unit to your "uses" clause.



2.  Try the following code on for size:



var

  MyPropInfoPtr:  PPropInfo;

  MyMethod:       TMethod;

begin

  MyPropInfoPtr := GetPropInfo(BitBtn1.ClassInfo, 'OnClick');



{    hopefully the following "if" statement will ALWAYS be TRUE! }

  if MyPropInfoPtr^.PropType^.Kind = tkMethod then begin

    MyMethod := GetMethodProp(BitBtn1, MyPropInfoPtr);

    if MyMethod.Code = @TForm1.BitBtn1Click

       then ShowMessage('equal') else ShowMessage('not equal');

    if MyMethod.Code = nil

       then ShowMessage('nil') else ShowMessage('not nil');

  end;

end;



A:

Try the following:

var

   OnClickAddr : TNotifyEvent;

begin

   OnClickAddr := bitbtn1.OnClick;

     { this is pointer to pointer assignment }



  if not Assigned(OnClickAddr) then

      { your stuff }

  {

   or

  if OnClickAddr = nil then

  }

end;


Toggle NumLock CapsLock Keys

Question


Has anyone programatically toggled the NumLock, CapsLock, etc. keys?

Answer


A:

const

     inserton     = 1 shl 7;

     capslockon   = 1 shl 6;

     numlockon    = 1 shl 5;

     scrolllocon  = 1 shl 4;

     



procedure chgkeys(whatkey : byte; want_on : boolean);

var

     keyflags  : byte absolute $40:$17;

begin

     if want_on then

          keyflags := keyflags or whatkey

     else

          keyflags := keyflags and not whatkey

     end;







Call with whatkeys set to one or more of the constants (added together) and

want_on true

or false depending of wheter you want to set or reset the state.


Is a Bit set

Question


Is a Bit set?

What is the most efficient way to see if a bit is set in an integer?

My main problem is finding out if the File Dialog box Readonly flag is set.

Answer


A:

AND is probably the most efficient.



   IF (filelistbox1.filetype AND ftreadonly)<>0 THEN read_only_flag_is_set;



A:

The expression after the 'IF' will always resolve to a boolean.

'A AND B' yields a boolean result (any non-zero value is 'TRUE') - so does '(A

AND B) <> 0'.  However in certain instances with range checking on I have had

to use a boolean() typecast.



A:

  IF (ftReadonly in filelistbox1.filetype) THEN read_only_flag_is_set;



According to the Object Pascal Language Guide, "Small set operations...are

generated inline using AND, OR, NOT, and TEST machine code instructions", so

chances are the code generated will be as efficient as the AND.


Inherited free

Question


If I derive a class from TObject and I define my own constructor and destructor

IE:



    MyClass = class(TObject)

      public

         constructor Create;

         destructor  Free;

    end;



Should the code in either my constructor or destructor call the inherited

create and free.  IE:



   destructor MyClass.Free;

   begin

      ... free my stuff ...



     { Call low-level free ???? }

      inherited Free;

   end;

Answer


A:

When deriving from TObject, you should have a Create and Destroy, not Create and

Free. Free is NOT a virtual method, and therefore cannot be overridden. Free

simply calls Destroy if the object is actually in existance. And yes, you

should *always* call the inherited Create and Destroy methods in your descendant

classes. So to clarify, use:



MyClass = class(TObject)

  public

    constructor Create; override;

    destructor  Destroy; override;

end;



constructor MyClass.Create;

begin

  inherited Create;

{do initialization}

end;



destructor MyClass.Destroy;

begin

{free up resources}

  inherited Destroy;

end;



A:

Yes, you should call the inherited Create method.

Free is a different story. You should not Override the free method, you should

override the destroy method, and also call the inherited destroy method. The

reson for this is the Free method automatically calls the Destroy method, IF

AND ONLY IF THE OBJECT IS NOT NIL.  Someone told me the Free method is some

kind of black-magic assembler function.



A:

You might want to note that you can't use the 'override' keyword with the

'constructor Create' or the 'destructor Free' as indicated below - or so

the compiler says when I attempt to do so. You can however use the

'virtual' keyword.  In fact you can use the 'virtual' keyword with both

'Create' and 'Free'.

The only hokey thing about using 'destructor Free' is that when your

procedure executes it automatically calls 'Destroy' on exit - so don't

explicitly call 'Destroy' when using 'destructor Free'. Maybe that is a

function of the 'destructor' keyword?


Attach procedures to components created

Question


I've created some components dynamically.  The problem is that I don't have

any idea on how to attach some common events such as "OnClick", "OnEnter"

to these components.

Answer


A:

I would recommend that you consider overriding the default KeyPress, Click,

DblClick (etc) events.

Here's a brief example of an override Click procedure:



(Declaration in protected section of class type definition)



     procedure Click ; override ;



(Code below, in implementation section)



procedure GOKButton.Click ;

begin

   inherited Click ;

   (Owner as TForm).Close ;

end ;


Simulating a pause in a loop

Question


I'm looking for some help in simulating a pause within a loop. This

is my scenario :

Form1.Procedure initiates a while..do loop which displays data on

Form2. When the Form2 is filled (part way through the Form1 loop) I

want to Show Form2. On Form2 are two buttons  and ,

which if the user selects  passes control back to the Form1

loop for continuation of processing.. but I DO NOT want to Close

Form2.. hence I cannot use ShowModal.

My attempts to date have been to pass control to a function in Form2

which will return true/false depending on the button selected, but I

still can't work out how, on a button click, to return info back to

the original function called in Form2 so it can return the

appropriate result ie I still need a 'wait until a button is pressed

on this form' loop (or equivalent) within the function.

Answer


A:

in response to the following:

what about this:

pass control to you funtion, and in you function,



buttonpressed:=0;

repeat

  Application.processmessages;

until buttonpressed<>0;

result:=buttonpressed;



in your on click methods, you could set the buttonpressed variable to a value

other than 0 to indicate which was pressed.


Copying from a TEdit to an integer field

Question


Can someone help me. I am trying to copy a Tedit (stringfield) to an

integerfield.

I am getting an error " Type mismatch"

Answer


A:

Try the follwing code :



     function IVal( Str : string ) : LongInt;

     var ErrCode : integer;

     begin

     

       result := 0;

       if ( Str = '' ) then

         exit;

       Val( Str, result, ErrCode );

     end;





     ....



     YourIntegwrField := IVal( YourStringField );





     if you are reading from a table or query component, You can also use:





        YourIntegwrField := YourStringField.AsInteger;



A:

MyIntField.Value := StrToInt(MyEditField.Text);



A:

That's because Tedit contains a string, not an integer, so you have to do a

conversion. Try something like:



    val(Tedit1.text, myinteger, code);



You need to trap an error if Tedit1.text is non-numeric, "code" will be set

if this is so, look it up in Help.



A:

integerfield.value := StrToInt(stringfield.value);

or

integerfield.AsString := stringfield.value;

or

integerfield.Value := stringfield.AsInteger;


Using menu options to cut, copy, and paste

Question


It's easy in Delphi to use the standard Windows keystrokes to cut, copy, and

paste in any component that holds text.  You don't even have to write code

to do it!

But if I want to add Cut, Copy, and Paste to my app's menu and have it work

no matter what component I'm in, is there an easy one-liner way to do this?

Answer


A:

If the component "knows" how to copy, cut and paste it's contents

to clipboard you can just send a message to it.



For example WM_COPY message:



if GetFocus <> 0 then  { if any window has a focus }

SendMessage( GetFocus, WM_COPY, 0, 0);   { send WM_COPY to it }



Note:

I prefer to use API's GetFocus insteed of ActiveControl property,

because ActiveControl not always points to a windows having a focus.


Overriding the Create method

Question


When creating a new TWindow descendant, overriding the create method to add

the object initilization is very easy, just:



Procedure TMyWinControl.create;

begin



inherited

...additional stuff



end;



But, when creating a TOBject descendant, the create method is not virtual, so...

How can I do the same to initialize my new object?

Is the following code rigtht?



Procedure TMyTojectDescendant.create;

begin

inherited; {is this right??}

{Initilization code}



end;

Answer


A:

Yes, but you cannot use inherited without a procedure name in these

places. You should write 'inherited Create. The difference is when

someone else (Delphi for example) calls your Create constructor.

Simplifying, caller is not able to create the correct instance of

TObject, because TObject's constructor is not virtual. More precisly

constructor is not a method of the object but of its' class.



Oh, following code will explaine it better:



type

  TComponentClass = class of TComponent;



procedure CreateInstanceExample(AClass : TComponentClass);

begin

  AClass.Create(nil).Show;

end;



begin

  CreateInstanceExample(TForm);

    { Here the procedure creates and shows a from }

end.


Check whether mouse is over client area

Question


I want my application to know when the mouse cursor is no longer

hovering over the Client area of my application window.

Answer


A:

On the Form's OnMouseMove do:



procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,

  Y: Integer);

var

  P : TPoint;

begin

  P.X := X;

  P.Y := Y;

  if PtInRect (ClientRect,P) then {or boundsrect for whole window rect}

    MouseCapture := True

  else

    begin

      MouseCapture := False;

      ShowMessage ('It''s not over me anymore');

    end;

end;


Name / caption property & emulating the caption

Question


I need to use the name property to fill another property (like caption is

set by changing the name of a label) how is this done? I have to use a name

to create a link to another app for simplicity's sake I would like to

automatically use the component name. I would like to create the link at

create or load time but when I try, I get a blank for the name property

Answer


A:

Is something like this what you are looking for?



type

  TJJJ = class(TLabel)

  public

    constructor Create(AOwner: TComponent); override;

  end;



implementation



constructor TJJJ.Create(AOwner: TComponent);

begin

   inherited Create(AOwner);

   Caption := Name;

end;



A:

1. Override the virtual SetName method that is inherited from TComponent

2. Make sure you call inherited SetName( NewValue ) to reuse the deafualt

name creation logic from Tcomponent

3. Augment the SetName Method with whatever code you need.





Example

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

unit edit;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, StdCtrls, Mask, DBCtrls;



type

  TkeEdit = class(TDBEdit)

  private

    { Private declarations }

    FName : String;

  protected

    { Protected declarations }



 { override the virtual method }

    procedure SetName( const NewName : TComponentName ); override;



  public

    { Public declarations }

  published

    { Published declarations }

  end;



procedure Register;



implementation



procedure Register;

begin

  RegisterComponents('Keen Edge', [TkeEdit]);

end;



procedure TkeEdit.SetName( const NewName : TComponentName );

BEGIN

        { reuse default TComponent SetName Logic }

     inherited SetName( NewName );



        ( 3. Augment SetNAme Logic }

        { always add the string 'Test' to the name whenever it is changed. }

    Text := 'Test' + Name;

END;


Override vs Redefine

Question


On Compuserve, someone brought up that you cannot override a static

method, however, you can redefine it.  Functionally, what is the difference.

I can see that you do not have access to the overriden method, but are there

any other differences?

Answer


A:

Yes, there is one other important difference.  The difference is that if the

ancestor object calls the method then it won't be calling your method, it

will be calling its own method (or higher up the tree if not defined in that

object).  A common practice in OOP is to define a base class that has has

virtual methods that have no definition, that are meant to be overridden in

descendant objects.  The base class will actually call these methods even

though they have no code in them,  and if you did not create a descendant

class you would get a run-time error if that method was ever called.  That is

what polymorphism is all about.


Why no free

Question


My Pascal Ref manual states that calling Free is the correct (or better)

way to destroy these objects.



 TStage = class(TObject)

   constructor Create;

   destructor Destroy;

   ..

   ..

   ..

 end;



 *** NOTE that I did not override free!



So here is the question.  At program shutdown I call:  Stage.Free.  Stage

was created by  Stage := new TStage; I placed some debug output calls in

TStage.Destroy but I never see them!

If I call Stage.Destroy I do see them! What is the deal?

Answer


A:

You should declare your desctructor as

  destructor Destroy; override;



The Destroy method of TObject is a virtual method and can be overriden.  That

way when you call Free (even though you have not defined a Free method in

your class) it will, through the miracle of polymorphism, call the correct

Destroy method.



So, Stage.Free really executes TObject.Free.  Since you did not override the

definition of Destroy in TStage, TObject.Free calls TObject.Destroy.  If you

override Destroy in TStage then when TObject.Free calls Destroy, Delphi is

able to figure out that you wanted TStage.Destroy not TObject.Destroy.


Mouse Coordinates / Lost Focus

Question


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.

Answer


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;


Overriding Virtual Methods

Question


Anybody know what the difference is between OVERRIDING a virtual

method and REPLACING it? I'm confused on this point.

Answer


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.


Copy one memo field to another

Question


How do I copy data in one memo field in Table1 to another memo field in

Table2?

Answer


A:

Table2..Text.Clear;

Table2..Text.Add(Table1..Text);



A:

MemoField1.Assign(MemoField2);

Make sure the dataset is in Edit mode.



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.SavetoStream(T);

     Table2.LoadFromStream(T);

     T.Destroy;

End;



Now Transfering Between 2 TMemo Fields is another Story.  There you can rely

on using the Text property.



A:

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


Sort a TStringList by Numerical Value

Question


I cannot use the 'Sort' method in TStringList as I would like

to sort by Integer.

My TStringList is filled with numbers such as:

20

12

1

23

54

32

(of course, they're converted to string before being added to TStringList)

What is a fast algorithm to achieve this sort?  Thanks.

I normally have less than 50 items in my TStringList, if that is a factor.

Answer


A:

I think the implementation that I mailed out to a few people sorts an array

of integers, and so there would be no problem using it to sort a a string list

with integers. You'd end up doing a *lot* of conversions using StrToInt, which

is wasteful, so I would recommend that you create a



	type

		PInteger = ^Integer type,



store all of the StrToInt values in the TStringList.Objects array, and then when

you use the sort, do your comparisons based on



		PInteger(SL.Objects[Idx])^



The quicksort that TStringList uses (see CLASSES.PAS) uses a very simple

partition function, which is completely unaware of the data it's sorting.

It's using the midpoint index to begin to decide where to start partitioning,

which is just as reliable as picking a random number when deciding how to sort.

If, for example, you had a BIG list of items that was already sorted in the

reverse direction, and you used this quicksort on it, and would call itself

recursively once for every element in the list!  Now, when you take into account

that you're pushing a few items on the stack (the return address as well as the

parameters as well as the registers you are saving) it might not take too long

for your 16K of stack space to get eaten up (16,384 bytes divided by about maybe

32 bytes (and that's being pretty optimistic!) is about 2048 items before you

run the risk of killing the stack!).  The MaxListSize in CLASSES is 16380 (65520

div sizeof (Pointer)), so it's certainly possible to cause this problem.



I do want you guys to know that TStringList.Sort is declared as virtual, so if

you wanted to override it, you certainly could in a class derived from

TStringList.



I also want you to know that the odds of anyone having to sort this much data

(2000 items) seems pretty remote (correct me, anyone, if you've ever sorted more

than 2000 strings in an application).  The most reliable sort with the same

running time as QuickSort is a HeapSort.  They both run in O(N lg N) time,

whereas sorts like the InsertionSort (which someone mentioned) and BubbleSort

(which someone else mentioned) run in O(N^2) time, on the average.



The biggest differences between HeapSort and QuickSort, in terms of their run

time and storage are:



1) HeapSort only calls itself recursively at most lg N times, where as QuickSort

could call itself recursively N times (big difference, like 10 vs 1024, or 32 vs

2^32)



2) The worst case upper bound time on HeapSort is only O(N lg N), whereas in the

worst case for QuickSort, the running time is O(N^2).



Program follows:



{***********************************************************}



program H;



uses WinCrt, SysUtils;



  const

    min = 10;

    max = 13;

    maxHeap = 1 shl max;



  type

    heap = array [1..maxHeap] of integer;

    heapBase = ^heap;



  var

    currentSize, heapSize: integer;

    A: heapBase;



  procedure SwapInts (var a, b: integer);

  var

    t: integer;

  begin

    t := a;

    a := b;

    b := t

  end;



  procedure InitHeap (size: integer);

  var

    i: integer;

  begin

    heapSize := size;

    currentSize := size;

    Randomize;

    for i := 1 to size do

      A^[i] := Random(size) + 1; 

  end;



  procedure Heapify (i: integer);

  var

    left, right, largest: integer;

  begin

    largest := i;

    left := 2 * i;

    right := left + 1;

    if left <= heapSize then

      if A^[left] > A^[i] then

        largest := left;

    if right <= heapSize then

      if A^[right] > A^[largest] then

        largest := right;

    if largest <> i then

      begin

        SwapInts (A^[largest], A^[i]);

        Heapify (largest)

      end

  end;



  procedure BuildHeap;

  var

    i: integer;

  begin

    for i := heapSize div 2 downto 1 do

      Heapify (i)

  end;



  procedure HeapSort;

  var

    i: integer;

  begin

    BuildHeap;

    for i := currentSize downto 2 do

      begin

        SwapInts (A^[i], A^[1]);

        dec (heapSize);

        Heapify (1)

      end

  end;



type

  TAvgTimes = array [min..max] of TDateTime;

var

  sTime, eTime, tTime: TDateTime;

  i, idx, size: integer;

  avgTimes: TAvgTimes;

  



begin

  tTime := 0;

  i := min;

  size := 1 shl min;

  new (A);

  while i <= max do

    begin

      for idx := 1 to 10 do

        begin

          InitHeap (size);

          sTime := Time;

          HeapSort;

          eTime := Time;

          tTime := tTime + (eTime - sTime)

        end;

      avgTimes[i] := tTime / 10.0;

      inc (i);

      size := size shl 1;

    end;

end.


Canceling The Key Press

Question


I'm using the OnKeyDown event (NOT OnKeyPress) of a DBEdit to trap the

Num Key Pad's Plus Minus pressing, and that means that the user wants

to increace or decrease the value in the field.

My problem is that after performing the operation - the control adds

'+' or '-' to the edit box. I tried Key := 0; (because I know that in

OnKeyPress you do something similiar: Key := #0) but no results.

Answer


A:

To stop the + or - from appearing in your Tedit window:



Tformz.EditzKeyDown(Sender:Tobject;Var key:word;Shift:TshiftState);

var

    save_key:byte;

begin

save_key := key;

KEY := $0;

If ((save_key = VK_ADD)or(save_key=VK_SUBTRACT)) then do whatever...

else key := save_key;

..

..

end;



I am not sure if the VK_ADD, VK_SUBTRACT are the VK values for the

keypad, thier definition shows up just after the VK_NUMPAD values.



These VK key values are listed in APT help, Virtual Key Codes. Not all

are supported by DELPHI.



..

implementation

const

    proof:integer = 0;   {Just for testing}

var

    key_sig:integer;     {Was the key the Numeric KeyPad + or - ?}

                         {A simple way for KeyDown and KeyPress to}     

                     {communicate}



{**************** Sense the key*******************}

procedure Tformxyz.EditzzzKeyDown(...var key:word...);

var

    save_key:byte;

begin

key_sig := 0;             {default value]

save_key := key;          {save key press if needed later}

if (key = VK_ADD) then key_sig := +1;

if (key + VY_SUBTRACT) then key_sig := -1;

end;



procedure Tformxyz.EditzzzKeyPress(...var key:char...);

var

    save_key:char;

begin

save_key := key;

key := #0;                                {Suppress printing...for now}

if key_sig = 0 then key := char(save_key) {Print the character}



{I just noticed the cast to char..left over from earlier trials,}

{should not be necessary}



else

    begin

    proof := proof + key_sig;         {Demonstration that it works}

    edityyy.text := inttostr(proof);

    end;

end;

..

..

end.


GetKeyBoardState

Question


How do I get the VK_INSERT state?

Answer


A:

Hope the following code helps. It shows that indeed, VK_INSERT can be used.

Your program will need to track the status and adjust your program to overwrite

or insert text.



  TFormInstructor = class(TForm)

{   Must never absolutely change position! Code depends on this!

    PanelScrollLock, PanelINS, PanelCAPS, PanelNUM }

    PanelScrollLock: TPanel;

    PanelINS: TPanel;

    PanelCAPS: TPanel;

    PanelNUM: TPanel;

    procedure Timer1Timer(Sender: TObject);

  private

    stToggles: array[0..3] of Bool;

  end;



{ Check keystate @ every timer tick.

  Won't receive any Windows messages informing that keystate has changed! }

procedure TFormInstructor.Timer1Timer(Sender: TObject);

const

 vkconsts: array[0..3] of Word=(vk_Scroll, vk_Insert, vk_Capital, vk_NumLock);

 PanelColor: array[Boolean] of TColor=(clGray, clBlack);

var

{ tmScrollLock, tmNumLock, tmCapital, tmInsert: Bool; }

 Toggles: array[0..3] of Bool; { Maybe able to use

[Low(vkconsts)..High(vkconsts)] }

 Panels: array[0..3] of TPanel absolute PanelScrollLock;

 I: Integer;

begin

 for I := Low(vkconsts) to High(vkconsts) do

  begin

   Toggles[I] := Bool(GetKeyState(vkconsts[I]) and 1);

   if stToggles[I]<>Toggles[I] then

    begin

     stToggles[I] := Toggles[I];

     Panels[I].Font.Color := PanelColor[Toggles[I]];

    end;

  end;

end;


Obtain last digits in a number

Question


I want to obtain the last 3 digits of a number

This is the code:



procedure TForm1.Button1Click(Sender: TObject);

var

  MyNumber: Word;

  TempNumber: LongInt;

begin

  TempNumber := 555444;

  MyNumber := Trunc(Frac(TempNumber / 1000) * 1000);

  ShowMessage(IntToStr(MyNumber));

end;



1- Why ShowMessage tell me 443 while 444 is the correct number?

2- Is there an easy way to obtain the last n digits?

Answer


A:

Convert your number to a string (IntToStr - if number is an integer)

then:

Assuming converted number is held in StringValue

Var

	tmpStr := String;

	LastThree := Integer;

Begin

	tmpStr := Copy(StringValue, len(StringValue)-4,3);

	LastThree := StrToInt(tmpStr);

End;



This should work with integers only.



A:

The 443 occurs because you're doing floting point operations, the

easy way to obtain the last three digits is:



MyNumber := MyNumber MOD 1000;



A:

1. The fraction is not safe due to the limited accuracy of the intermediate

real number. If you get, e.g. 999999/1000 = 999.99899, your algorithm

will result in : .9989 * 1000 = 998.9 which truncates to 998. It would be

better to calculate:

MyNumber := Trunc(0.5 + Frac(TempNumber / 1000.0 ) * 1000)

or simply the equivalent:

MyNumber := Round(Frac(TempNumber / 1000.0 ) * 1000)

Alternatively, calculate:

MyNumber := TempNumber - Trunc(TempNumber / 1000.0 ) * 1000;



A:

procedure TForm1.Button1Click(Sender: TObject);

var

  MyNumber: Word;

  TempNumber: LongInt;

  tString : array[0..15] of Char;

  iLen : word;

begin

  TempNumber := 555444;

  StrPCopy(tString, IntToStr(TempNumber));

  iLen := Length(StrPas(tString));

  MyNumber := StrToInt(StrPas(@tString[iLen-3]));

  ShowMessage(IntToStr(MyNumber));

end;



A:

function ReadDigits(TheNumber:LongInt;NumDigits:byte):longint;

{Reads the last NumDigits of TheNumber}

var

   TempStr : string;

   TempInt : longint;

   ErrorCode : integer;

begin

  TempStr := IntToStr(TheNumber);

   val(copy(TempStr,length(TempStr)-(NumDigits-1),length(TempStr)),

          TempInt,ErrorCode);

  if ErrorCode = 0 then

    ReadDigits := TempInt

  else

    {do something about the error here}

end;



{as an example, I created this OnClick method to show the results in a 

label}



procedure TForm1.Button1Click(Sender: TObject);

begin

   {Read the last 2 digits of the number 555444}

   Label1.Caption := IntToStr(ReadDigits(555444,2));

end;


Callback functions

Question


Could someone explain to me how to setup callback functions?

Answer


A:

{function FindWindowHandle (HuntFor: string): HWnd;}

{----------------------------------------------------------------}

{ Hunts for a parent window with title containing the HuntFor    }

{ string.  Returns the window handle or 0 if none found.         }

{----------------------------------------------------------------}

    { The following indented code is logically a part of  }

    { FindWindowHandle but is placed here above the real  }

    { FindWindowHandle function heading as Borland do not }

    { allow nesting of callback functions.                }

    {-----------------------------------------------------}

     type

       PHuntRec = ^THuntRec;

       THuntRec = record

         HuntingFor: string;

         WindowFound: HWnd;

       end;



     function EnumWindowsFunc (WindowHandle: HWnd;

                         lParam: Longint): WordBool; export;

    {-----------------------------------------------------}

    { Callback function used by FindWindowHandle.         }

    {-----------------------------------------------------}

     var

       ATitle: array[0..255] of Char;

     begin

       GetWindowText(WindowHandle, PChar(@ATitle), 255);

       if StrContains(StrPas(PChar(@ATitle)),

         PHuntRec(lParam)^.HuntingFor, CaseInsensitive) then

       begin

         PHuntRec(lParam)^.WindowFound := WindowHandle;

         EnumWindowsFunc := false;     {stop looking}

       end

       else

         EnumWindowsFunc := true   {continue looking}

     end; {EnumWindowsFunc}



 function FindWindowHandle (HuntFor: string): HWnd;

 var

   Proc: TFarProc;

   HuntRec: PHuntRec;

 begin

   GetMem(HuntRec, SizeOf(THuntRec));

   HuntRec^.HuntingFor := HuntFor;

   HuntRec^.WindowFound := 0;

   Proc := MakeProcInstance(@EnumWindowsFunc, HInstance);

   EnumWindows(Proc, Longint(HuntRec));

   FreeProcInstance(Proc);

   FindWindowHandle := HuntRec^.WindowFound;

   FreeMem(HuntRec, SizeOf(THuntRec));

 end; {FindWindowHandle}


Dynamically assigning event handlers

Question


If I dynamically create a decendant of a PopUp Menu how do I designate the

OnClick event handler?

Answer


create a procedure that handles the event (must have the same

signature as the default event handler).  Then assign it to the

OnClick property:



procedure MyPopUpClick(Sender : TObject);

begin

 {Handle the event}

end;



then assign it with:



MyPopUp.OnClick = MyPopUpClick;



A:

It is not difficult to assign event handler on the fly. For example, to assign

the OnClick event of a PopUp menu to a event handler, as below:



procedure TForm1.PopupMenusClickHandler(Sender: TObject);

begin

  :

  :

end;



procedure TForm1.TestButtonClick(Sender: TObject);

begin

  :

  PopupMenu1.OnClick := PopupMenusClickHandler;

  :

  :

end;


Sendkey function

Question


Is there anybody have information on the function especially how to program

it in Delphi ?

Answer


It's attached one freeware sendkeys component from Makoto Muramatsu

( and )



unit Sendkey;

{

      This is a procedure named "SendKeys".

      This function like the same named statment of Visual Basic.

      It provide the like features by VB's function.

      This version not use "wait" flg.



                     CopyRights 1995, Makoto Muramatsu



      Para ejecuatar la funcion:

        SendKeys(VentDestino,Teclas,False);

              VentDestino: 0: Ventana acticava actualmente,

              si el destino es una pantalla Delphi se puede activar

              la ventana de destino y con la funci=F3n GetActiveWindow

              recoger el valor de la ventana, tambien se puede usar

              FindWindow(NombreVentana,nil) para buscar el numero de

              ventana de un determinado programa.

              Teclas:  Son las teclas tal cual, para mandar un Control seel

                       antepone ^ (es decir Ctrl+ C seria ^C), para simular

                       Alt+Tecla es %Tecla ; para Mayusculas+Tecla es +Tecla,

                       adem=E1s se pueden simular las teclas especiales

                       poniendo los siguientes textos entre llaves:

                       BS, BACKSPACE, BKSP'

                           BREAK

                           CAPSLOCK

                           CLEAR

                           DEL, DELETE

                           DOWN

                           END

                           ENTER

                           ESC, ESCAPE

                           HELP

                           HOME

                           INSERT

                           LEFT

                           NUMLOCK

                           PGDN

                           PGUP

                           PRTSC

                           RIGHT

                           SCROLLLOCK

                           TAB

                           UP

                           F1

                           F2

                           F3

                           F4

                           F5

                           F6

                           F7

                           F8

                           F9

                           F10

                           F11

                           F12

                           F13

                           F14

                           F15

                           F16

                           F17

                           F18

                           F19

                           F20

                           F21

                           F22

                           F23

                           F24



         1.996 Juan Davi Evora H=E4nggi

}



interface



uses  WinTypes;



procedure SendKeys( h: HWND; const keys: string; wait: boolean );



implementation



uses WinProcs, Messages, SysUtils, Forms, Dialogs ;



type

  TWindowObj = class( TObject )

  private

    windowHandle : HWND;

    TargetClass : PChar;

    NameLength : Integer;

    Buffer : PChar;

  public

    constructor Create;

    destructor Destroy;

    procedure SetTargetClass( className : string );

    procedure SetWindowHandle( hWnd: HWND );

    function GetWindowHandle: hWnd;

    function Equal( handle: HWND ): boolean;



  end;



const

     OPENBRACE = '{';

     CLOSEBRACE = '}';

     PLUS = '+';

     CARET = '^';

     PERCENT = '%';

     SPACE = ' ';

     TILDE = '~';

     SHIFTKEY = $10;

     CTRLKEY = $11;

     ALTKEY = $12;

     ENTERKEY = $13;

     OPENPARENTHESES  = '(';

     CLOSEPARENTHESES = ')';

     NULL = #0;

     TargetControlClass = 'Edit';



{================ GetTextWindow ===============================}

function  EnumChildProc( hWnd: HWND; lParam: LongInt ):Bool;export;

var

   continueFlg : boolean;

   HObj : TWindowObj;

begin

   HObj := TWindowObj( lParam );

   if HObj.Equal( hWnd ) then begin

      HObj.SetWindowHandle( hWnd );

      continueFlg := false;

   end;

   result := continueFlg;   { Stop Enumerate}

end;





function GetFocusWindow( h: HWnd ): hWnd;

{ GetFocus and if return 0 then search Edit Control in Children of the window}

var

   EnumFunc : TFarProc;

   Param : LongInt;

   proc: TFarProc;

   ok : Boolean;

   hObj :  TWindowObj;

   targetWindow : HWnd;



begin

   targetWindow := GetFocus;

   if targetWindow <> 0 then begin

      result := targetWindow;

      exit;

   end;

   h := GetActiveWindow;

   Proc := @EnumChildProc;

    EnumFunc := MakeProcInstance( proc, HInstance );

    If Not Assigned(EnumFunc ) then begin

       MessageDlg( 'MakeprocInstanceFail', mtError, [mbOK],0 );

       exit;

    end;

    hObj := TWindowObj.Create;

    hObj.SetTargetClass(TargetControlClass);

    Param := LongInt( hObj );

    result := 0;

    try

       ok := EnumChildWindows(h, EnumFunc, Param );

       targetWindow := hObj.GetWindowHandle;

    finally

      FreeProcInstance( EnumFunc );

      hObj.Free;

    end;

    result := h;

    if targetWindow <> 0 then begin

        if IsWindowEnabled( targetWindow ) then begin

            result := targetWindow;

        end;

    end;

end;



{================ TWindowObj ===============================}

{transfer User Data from EnumChildWindow to EnumChildProc }

constructor TWindowObj.Create;

begin

     TargetClass := nil;

end;



destructor TWindowObj.Destroy;

begin

     if Assigned( TargetClass ) then begin

        StrDispose( TargetClass ) ;

     end;

     if Assigned( Buffer ) then begin

        StrDispose( Buffer ) ;

     end;

end;



function TWindowObj.Equal(handle: HWND ): boolean;

var

   classNameLength : integer;

begin

   result := false;

   classNameLength := GetClassname( handle, Buffer, NameLength + 1 );

   if classNameLength = 0 then exit;

   if StrLIComp( TargetClass, Buffer, NameLength ) = 0 then begin

      result := true;

   end;

end;



procedure  TWindowObj.SetTargetClass( ClassName: String );

begin

     if Assigned( TargetClass ) then begin

        StrDispose( TargetClass ) ;

     end;

     if Assigned( Buffer ) then begin

        StrDispose( Buffer ) ;

     end;

     NameLength := Length( ClassName );

     TargetClass := StrAlloc( NameLength + 1 );

     StrPCopy( TargetClass, ClassName );

     Buffer := StrAlloc( NameLength + 1 );

end;



procedure TWindowObj.SetWindowHandle( hWnd: HWND );

begin

     windowHandle := hWnd;

end;



function TWindowObj.GetWindowHandle: hWnd;

begin

     result := windowHandle;

end;



{=============  SendKeys =============================}

procedure SendOneKey( window: HWND; virtualKey: WORD; repeatCounter: Integer;

          shift: BOOLEAN; ctrl: BOOLEAN; menu: BOOLEAN; wait: BOOLEAN);

{ Send One VirtualKey, to other Window }

var

    lparam: LongInt;

    counter: integer;

    keyboardState: TKeyBoardState;

    test: BYTE;

begin

    window := GetFocusWindow( window );

    for counter := 0 to repeatCounter - 1 do begin

          lparam := $00000001;

          if menu then begin

             lparam := lparam or $20000000;

          end;

          if shift or ctrl or menu then begin

             { Set KeyboardState }

             GetKeyBoardState( keyboardState );

             if menu then begin

                if VirtualKey = 220 then { Si es '\' no es SYSKEY}

                 PostMessage( window, WM_KEYDOWN, ALTKEY, lparam )

                else

                 PostMessage( window, WM_SYSKEYDOWN, ALTKEY, lparam );

                keyboardState[ALTKEY] := $81;

             end;

             if shift then begin

                PostMessage( window, WM_KEYDOWN, SHIFTKEY, lparam );

                keyboardState[SHIFTKEY] := $81;

             end;

             if ctrl then begin

                PostMessage( window, WM_KEYDOWN, CTRLKEY, lparam );

                keyboardState[CTRLKEY] := $81;

             end;

             SetKeyBoardState( keyboardState );

          end;

          if menu and (VirtualKey <> 220) then begin

              PostMessage( window, WM_SYSKEYDOWN, virtualKey, lparam );

          end

          else begin

              PostMessage( window, WM_KEYDOWN, virtualKey, lparam );

          end;

          Application.ProcessMessages;

          lparam := lparam or $D0000000;

          if menu and (VirtualKey <> 220) then begin

              PostMessage( window, WM_SYSKEYUP, virtualKey, lparam );

          end

          else begin

              PostMessage( window, WM_KEYUP, virtualKey, lparam );

          end;

          if shift or ctrl or menu then begin

             {unSet KeyBoardState }

             GetKeyBoardState( keyboardState );

             if ctrl then begin

                PostMessage( window, WM_KEYUP, CTRLKEY, lparam );

                keyboardState[CTRLKEY] := $00;

            end;

             if shift then begin

                PostMessage( window, WM_KEYUP, SHIFTKEY, lparam );

                keyboardState[SHIFTKEY] := $00;

             end;

             if menu then begin

                lparam := lparam and $DFFFFFFF;

                if (VirtualKey = 220) then

                  PostMessage( window, WM_KEYUP, ALTKEY, lparam )

                else

                 PostMessage( window, WM_SYSKEYUP, ALTKEY, lparam );

                keyboardState[ALTKEY] := $00;

             end;

             SetKeyBoardState( keyboardState );

          end;

    end;

end;



procedure SendOneChar( window: HWND; oneChar: Char; wait: BOOLEAN);

{ Send One Character to target Window }

var

    lparam: LongInt;

    counter: integer;

    key : WORD;

begin

    window := GetFocusWindow( window );

    lparam := $00000001;

    key := Word( oneChar );

    PostMessage( window, WM_CHAR, key, lparam );

    Application.ProcessMessages;

end;



function RecognizeChar( s : string ): BYTE;

{ Recognize Virtual Key by KEYWORD }

begin

     if (CompareText( s, 'BS') = 0) OR

        (CompareText(s, 'BACKSPACE') = 0) or

        ( CompareText(s,'BKSP') = 0 ) then begin

          result := $08;

     end

     else if CompareText(s, 'BREAK') = 0 then begin

          result := $13;

     end

     else if CompareText(s, 'CAPSLOCK') = 0 then begin

          result := $14;

     end

     else if CompareText(s,  'CLEAR') = 0 then begin

          result := $0C;

     end

     else if (CompareText(s, 'DEL') = 0 ) or

             (CompareText(s ,'DELETE') = 0) then begin

          result := $2E;

     end

     else if CompareText(s, 'DOWN') = 0 then begin

          result := $28;

     end

     else if CompareText(s, 'END') = 0 then begin

          result := $23;

     end

     else if CompareText(s,  'ENTER') = 0 then begin

          result := $0D;

     end

     else if (CompareText(s, 'ESC') = 0) OR

            ( CompareText(s, 'ESCAPE') = 0 ) then begin

          result := $1B;

     end

     else if CompareText(s, 'HELP') = 0 then begin

          result := $2F;

     end

     else if CompareText(s, 'HOME') = 0 then begin

          result := $24;

     end

     else if CompareText(s, 'INSERT') = 0 then begin

          result := $2D;

     end

     else if CompareText(s, 'LEFT') = 0 then begin

          result := $25;

     end

     else if CompareText(s, 'NUMLOCK') = 0 then begin

          result := $90;

     end

     else if CompareText(s, 'PGDN') = 0 then begin

          result := $22;

     end

     else if CompareText(s, 'PGUP') = 0 then begin

          result := $21;

     end

     else if CompareText(s, 'PRTSC') = 0 then begin

          result := $2C;

     end

     else if CompareText(s,  'RIGHT') = 0 then begin

          result := $27;

     end

     else if CompareText(s, 'SCROLLLOCK') = 0 then begin

          result := $91;

     end

     else if CompareText(s, 'TAB') = 0 then begin

          result := $09;

     end

     else if CompareText(s, 'UP') = 0 then begin

          result := $26;

     end

     else if CompareText(s, 'F1') = 0 then begin

          result := $70;

     end

     else if CompareText(s, 'F2') = 0 then begin

          result := $71;

     end

     else if CompareText(s, 'F3') = 0 then begin

          result := $72;

     end

     else if CompareText(s, 'F4') = 0 then begin

          result := $73;

     end

     else if CompareText(s, 'F5') = 0 then begin

          result := $74;

     end

     else if CompareText(s, 'F6') = 0 then begin

          result := $75;

     end

     else if CompareText(s, 'F7') = 0 then begin

          result := $76;

     end

     else if CompareText(s, 'F8') = 0 then begin

          result := $77;

     end

     else if CompareText(s, 'F9') = 0 then begin

          result := $78;

     end

     else if CompareText(s, 'F10') = 0 then begin

          result := $79;

     end

     else if CompareText(s,  'F11') = 0 then begin

          result := $7A;

     end

     else if CompareText(s, 'F12') = 0 then begin

          result := $7B;

     end

     else if CompareText(s, 'F13') = 0 then begin

          result := $7C;

     end

     else if CompareText(s, 'F14') = 0 then begin

          result := $7D;

     end

     else if CompareText(s, 'F15') = 0 then begin

          result := $7E;

     end

     else if CompareText(s, 'F16') = 0 then begin

          result := $7F;

     end

     else if CompareText(s, 'F17') = 0 then begin

          result := $80;

     end

     else if CompareText(s, 'F18') = 0 then begin

          result := $81;

     end

     else if CompareText(s, 'F19' ) = 0 then begin

          result := $82;

     end

     else if CompareText(s, 'F20') = 0 then begin

          result := $83;

     end

     else if CompareText(s,  'F21') = 0 then begin

          result := $84;

     end

     else if CompareText(s, 'F22') = 0 then begin

          result := $85;

     end

     else if CompareText(s, 'F23') = 0 then begin

          result := $86;

     end

     else if CompareText(s, 'F24') = 0 then begin

          result := $87;

     end

     else begin

         result := 0;

     end;

end;



function CharToVirtualKey( source: Char; var shift: boolean; var ctrl:

boolean; var menu: boolean): WORD;

var

    resultCode: WORD;

    upperWord : WORD;

begin

    resultCode := VkKeyScan( Word(source) );

    upperWord := resultCode shr 8;

    case upperWord of

       1,3,4,5: shift := true;

       6 : begin

             ctrl := true;

             menu := true;

           end;

       7 : begin

             shift := true;

             ctrl := true;

             menu := true;

           end;

    end;

    result := resultCode and $00ff;

end;



function GetSpecialChar(specialChar: PChar; var repeatCount: Integer;

         var shift: boolean; var ctrl: boolean; var menu: boolean ): WORD;

{ In Brace String Parser}

var

    p : PChar;

    s : string;

    virtualKey : BYTE;

begin

    p := StrScan( specialChar, SPACE );

    if p <> nil then begin

       p^ := NULL;

       Inc(p);

       s := StrPas( p );

       repeatCount := StrtoInt( s );

    end

    else begin

       repeatCount := 1;

    end;

    s := StrPas( specialChar );

    virtualKey := RecognizeChar( s );

    if virtualKey = 0 then begin

       result := CharToVirtualKey(specialChar^, shift, ctrl, menu);

    end

    else begin

       result := virtualKey;

    end;

end;



procedure Parser( window: HWND; chars: PChar; wait:Boolean);

{Parse String Line and Send keys }

var

     p : PChar;

     specialChar: PChar;

     shift, ctrl, menu: Boolean;

     parenthese : Boolean;

     repeatCounter : Integer;

     oneChar : Char;

     vertualKey : Word;



     procedure ClearAddKey;

     begin

          shift := false;

          ctrl := false;

          menu := false;

     end;

begin

     p := chars;

     ClearAddKey;

     parenthese := false;

     while p^ <> NULL do begin

           if p^ = OPENBRACE then begin

               {Control Code }

               Inc( p );

               specialChar := p;

               while p^ <> NULL do begin

                   if p^ = CLOSEBRACE then begin

                      if (p + 1)^ = CLOSEBRACE then begin

                         Inc(p);

                      end;

                      break;

                   end;

                   Inc(p);

               end;

               if p^ = NULL then begin

                  MessageDlg('Illegal string ', mtError, [mbOK], 0 );

                   break;

               end;

               p^ := NULL;

               vertualKey := GetSpecialChar(specialChar, repeatCounter,

               shift, ctrl, menu);

               SendOneKey(window, vertualKey, repeatCounter, shift, ctrl,

               menu, wait);

               if not parenthese then begin

                     ClearAddKey;

               end;

           end

           else if p^ = PLUS then begin

                shift := true;

           end

           else if p^ = CARET then begin

                ctrl := true;

           end

           else if p^ = PERCENT then begin

                menu := true;

           end

           else if p^ = TILDE then begin

               SendOneKey( window, ENTERKEY, 1, shift, ctrl, menu, wait);

               if not parenthese then begin

                  ClearAddKey;

               end;

           end

           else if (shift or ctrl or menu ) and ( p^ =  OPENPARENTHESES)

           then begin

                parenthese := true;

           end

           else if parenthese and ( p^ = CLOSEPARENTHESES ) then begin

                parenthese := false;

           end

           else begin

               if  ($80 and BYTE(p^)) > 0 then begin

                   { 2 Bytes Char}

                   SendOneChar(window, p^, wait);

                   Inc(p);

                   SendOneChar(window, p^, wait );

               end

               else begin

                   vertualKey := CharToVirtualKey( p^,shift,ctrl,menu);

                   SendOneKey(window, vertualKey, 1, shift, ctrl, menu, wait);

               end;

               if not parenthese then begin

                  ClearAddKey;

               end;

           end;

           Inc(p);

     end;

end;



procedure SendKeys( h: HWND; const keys: string; wait:Boolean );

{ SendKeys send strings to Window by specific HWND.

  Before sending keys,  activate the window.

  if h = 0 then send string to current activate Window

  sorry, this version not use wait.}

var

     window: HWND;

     focusControl: HWND;

     chars: PChar;

begin

     { handle check}

     if h = 0 then begin

        window := GetActiveWindow;

     end

     else begin

        window := h;

        SetActiveWindow( window );

     end;



     chars := StrAlloc( length( keys ) + 1 );

     StrPCopy( chars, keys );

     Parser( window, chars, wait );

     StrDispose( chars );

end;





end.


Case Of Statement

Question


Can someone help me with "CASE" function like this:

The user have an inputbox where they can type digits

from 1 to 100, now I would like to check what digit the

typed and start some action depending on what the typed.

Somthing like this:



 Case I OF

   '10'..'20' : Showmessage('Test 10-20');

   '21'..'30' : Showmessage('Test 21-30');

 and so on...



What should "I" be and integer or string or what...

Answer


A:

Longint is out in Borlandish Pascal; according to the Delphi on-line

help for "Case":

  The selector must be a byte-sized or word-sized ordinal type, so

  strings and the integer type Longint are invalid selector types.



Note that this DOES permit "user defined" enumerated types to be case

selectors (booleans will also work). So the following is valid:



type

  TMyType = ( mt1, mt2, mt3 ) ;



var

  MyType : TMyType ;



begin

  { ... code assigning a value to MyType }

  case MyType of

    mt1 : DoMT1Stuff ;

    mt2 : DoMT2Stuff ;

    mt3 : DoMT3Stuff ;

  end ;



This fact, along with Delphi's Run-Time Type Information facilities,

actually allows you to effectively use strings as case selectors as

well.  If anyone's interested in that technique, they can see how it's

done in my entry for the "Tricks and Tips" column in the May issue of

_The Delphi Magazine_; or (if there's sufficient interest) I can post a

write-up to the list.



Anyhoo, the original poster has a couple of options for doing what he

described; probably the most straightforward way is to convert the

numeric string into an integer value:



var

  I : string ;

begin

  { ... code that assigns I a string representation of a numeric value }

  Case StrToInt( I ) OF

    10..20 : Showmessage('Test 10-20');

    21..30 : Showmessage('Test 21-30');

  end ;



Or, alternatively:



var

  I : integer ;

begin

  I := StrToInt( { ...code that gets the string value from the user } ) ;

  Case I of

    10..20 : Showmessage('Test 10-20');

    21..30 : Showmessage('Test 21-30');

  end ;



The individual cases in the body of the CASE must be INTEGRAL type

constants.

Right, or constants of an enumerated type (boolean or user defined);

from the on-line help again:

  All case constants must be unique and of an ordinal type compatible

  with the selector type.



They cannot be variables (unfortunately).  If you want to compare strings,

you have to do this with IF..THEN..ELSE statements.

AFAIK, this is a limitation of "case" type control structures in all

modern procedural languages (e.g. C, Basic, Ada), and is not special to

Pascal.  If the expected strings are fairly well defined beforehand, the

RTTI method mentioned above could serve as an alternative to multiple

if-then-else statements.



A:

How to use Delphi's Run Time type information to use strings as case

selectors:



Delphi's RTTI facilities aren't well documented, sometimes you have to 

dig a bit; but I have found that it's possible to convert a string into 

an enumerated type constant via the GetEnumValue() function that is 

(briefly) documented in the TYPINFO.INT file which is found in the 

\DELPHI\DOCS directory/folder of a standard install of Delphi 1.0 (you 

2.0 folks will have to look around if it's not in the same place there, 

as I don't have 2.0 yet to check; and this technique should be used in 

2.0 with the caveat that I haven't tried it to know if it works).



Anyhoo, let's suppose we've defined a enumerated type like so:



....

type

  TMyEnumType = ( metItem1, metItem2, metItem3 ) ;

....



I can use string input of some kind (from an editbox, a listbox, a file, 

wherever) to select on this type in a case statement like so:



....

{ be sure to add the TypInfo unit to your uses clause! }



var

  S : string ;

  MyType : TMyEnumType ;

begin

{ 

  first get a string that duplicates an enumerated constant; 

  e.g. 'metItem1' from someplace 

}

  GetString( S ) ; 



{ 

  now convert that string into a constant of TMyEnumType 

}

  

  MyType := TMyEnumType( GetEnumValue( TypeInfo( TMyEnumType ), S ) ;



{

  Now use MyType as a case selector

}



  case MyType of

    metItem1 : DoItem1Stuff ;

    metItem2 : DoItem2Stuff ;

    metItem3 : DoItem3Stuff ;

  end ;

....



So what's going on here?  I'll break down the GetEnumValue() expression 

by parts:



  TMyEnumType( GetEnumValue( TypeInfo( TMyEnumType ), S ) ;



The GetEnumValue() call takes two parameters, the first is a pointer to 

the RTTI record for the type in question, the second is merely a Pascal 

string.  The TypeInfo() function is a system routine (along the lines of 

TypeOf() and SizeOf()) that expressly returns the RTTI pointer for a 

type.  GetEnumValue() returns an integer that is the ordinal value of 

the particular constant of the set; it returns -1 if it can't resolve

the string to a constant of that type (nice! often low level routines 

like this throw an exception or something instead).  Finally, the 

returned integer is converted to an enumerated constant by typecasting 

it to a TMyEnumType.  



Of course, you can manipulate the string before passing it to 

GetEnumValue(), so the strings don't have to start out LOOKING like 

Enumeration constants; say you had a list box with items like so:



Item 1

Item 2

Item 3



Then (assuming you have a routine Strip() that removes spaces from a 

string) you could do something like this:



GetString( S ) ;

S := 'met' + Strip( S ) ;



then pass it to GetEnumValue.





BTW, here's some New Orleans Style Lagniappe ("something extra"):

there's an inverse RTTI function GetEnumName() turns an Enumeration 

constant into its string representation, you use it like so:



var

  S : string ;



begin

  S := GetEnumName( TypeInfo( TMyEnumType ), Ord( metItem1 ))^ ;



which should return 'metItem1' into S.  Note that the pointer

dereference operator at the end of the GetEnumName() call is NOT a typo,

GetEnumName() returns a PString, which has to be dereferenced to assign

it into a string. 



Hope this is useful for someone, and not too annoying a waste of

bandwidth for everyone else; I'd appreciate it if someone would try

these under 2.0 and let me know how it works.


Pointer Arithmetic in Delphi

Question


How does one manipulate pointer values in Delphi?  For Example, suppose I

have a pointer defined by Pt:^Integer and I want to offset it by 4, say Pt

:= Pt + 4*sizeof(Integer). How can I do that?

Answer


type

PReal = ^Real;

TMoment = CLASS(TOBject)

PUBLIC

  PROCEDURE Append( Datum : Real);

  PROCEDURE Get(index : integer) : Real;

  PROCEDURE Replace( index: integer; Datum : Real);

PRIVATE

   nextItem : integer;

   Data : PReal;

END;



PROCEDURE TMoment.Append( Datum : Real);

VAR

  aPointer : PReal;

BEGIN

  inc(nextItem);

  ReAllocMem(Data,sizeof(REAL)*nextItem);

  aPointer := PReal(LongInt(Data) +  sizeof(REAL)*(nextItem - 1));

  aPointer^ := Datum;

  END; { Append }



FUNCTION  TMoment.Get( index : integer) : Real;

VAR

  aPointer : PReal;

BEGIN

  aPointer := PReal(LongInt(Data) +  sizeof(REAL)*(index - 1));

  Result := aPointer^;

  END; { Get }



PROCEDURE TMoment.Replace( index: integer; Datum : Real);

VAR

  aPointer : PReal;

  i        : INTEGER;

BEGIN

 aPointer := PReal(LongInt(Data) + ( index - 1));

 aPointer^ := Datum;

 END;



Ofcourse you probably don't need the TYPED pointer ...


Edit Mask for decimals

Question


Could someone explain how to include decimal places in an edit mask ?

Answer


You can try; # ###.##;0;_


How to tell who Sender is

Question


If I have this procedure defined and assigned to, say, 20 TEditBoxes,

How do I tell which one activated the procedure?

Answer


You can use the Tag property - set up your TEdits with a unique Tag for each

one, say 1..20 then in the proc they all call you can do something like



with Sender as TEdit do

    begin

    case Tag of

        1: do something

        2: do something else

    end; {case}

    end;


String property values

Question


I am developing a component with a property Name of type String. I need to

provide the user with a dynamic list of possible values for Name (this list

will vary at design time).

The TTable component has a property DatabaseName (of type string) that

lists the available databases when the user opens the drop down list. The

TMediaPlayer component has a property FileName (of type string) that

displays the file open dialog.

I cannot see anything special in the VCL source code to activate the list

or the file open dialog.

How do I create and display the list of possible string values for the

string property?

Answer


Just set up your "Names" property the same as the "Lines" property and it

should work fine.

Delphi will automatically use the default Property Editor for a TStrings

component.  This will allow you to enter lines, load/save to/from files,

etc. at design time.



{Begin Source Code}

Unit Memodlg;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

  Forms, Dialogs, MemoForm;



type

  TGumpMemoDlg = class(TComponent)

  private

    { Private declarations }

    FLines : TStrings;

    procedure SetLines(Value : TStrings);

    function GetLines : TStrings;

  protected

    { Protected declarations }

  public

    { Public declarations }

    constructor Create(AOwner : TComponent); override;

    destructor Destroy; override;

  published

    { Published declarations }

    property Lines : TStrings read GetLines write SetLines;

  end;



procedure Register;



implementation



procedure Register;

begin

  RegisterComponents('STUFF', [TGumpMemoDlg]);

end;



Constructor TGumpMemoDlg.Create(AOwner : TComponent);

begin

  Inherited Create(AOwner);

  FLines := TStringList.Create;

end;





Destructor TGumpMemoDlg.Destroy;

begin

  FLines.Free;

  Inherited Destroy;

end;





function TGumpMemoDlg.GetLines : TStrings;

begin

  Result := FLines;

end;



Procedure TGumpMemoDlg.SetLines(Value : TStrings);

begin

  FLines.Assign(Value);

end;



end.


Object has a property

Question


Is there a function to determine if an object has a particular property? I

would prefer not to use this way:



	if MyObject is TEdit then ... else ... ;



but would like to use:



	if MyObject has Caption then Myobject.caption:='hello';

Answer


A:

Yes but only for published properties.



Here are a few functions I've written using RTTI.

The first hasprop will return True if a property of name prop exists.



eg. hasprop(MyLabel,'Caption') will return true

while hasprop(MyEdit,'Caption') will return false



The second one will set property prop to string value s if it exists and is a string type property



function hasprop(comp : TComponent;const prop: string) : boolean;

var proplist : PPropList;

    numprops,i : Integer;

begin

        result:=false;

  getmem(proplist,getTypeData(comp.classinfo)^.propcount*Sizeof(Pointer));

  try

    NumProps:=getproplist(comp.classInfo,tkProperties,proplist);

    for i:= 0 to pred (NumProps) do

    begin

      if comparetext(proplist[i]^.Name,prop)=0 then

      begin

        result:=true;

        break;

      end;

    end;

  finally

    freemem(proplist,getTypeData(comp.classinfo)^.propcount*Sizeof(Pointer));

  end;

end;



procedure setcomppropstring(comp : TComponent;const prop,s : string);

var proplist : PPropList;

    numprops,i : Integer;

begin

  getmem(proplist,getTypeData(comp.classinfo)^.propcount*Sizeof(Pointer));

  try

    NumProps:=getproplist(comp.classInfo,tkProperties,proplist);

    for i:= 0 to pred (NumProps) do

    begin

      if (comparetext(proplist[i]^.Name,prop)=0) and (comparetext(proplist[i]^.proptype^.name,'string')=0 then

      begin

        setStrProp(comp,proplist[i],s);

        break;

      end;

    end;

  finally

    freemem(proplist,getTypeData(comp.classinfo)^.propcount*Sizeof(Pointer));

  end;

end;


TList freeing it's items

Question


I read in Delphi docs that when we use the FREE method for a TList object,

the TList "destroys the object and frees its associated memory". Does it

means that it FREEs every item left in it when we free the TList, or that

it frees the memory of the TList object itself?

Answer


A:

It just frees the tList object itself.  You have to free the items in the

tList yourself.



A:

You must free each element of the TList.



The same is true if you use addobject for any list control (TListBox,

TComboBox, etc.)


Pionter arithmetic

Question


How do I do pointer arithmetic in Delphi?

Answer


First a brief explanation of pointer arithmetic.  When you 

are dealing with dynamic memory locations and all you have is a 

pointer to where it all begins, you want to have the ability to 

traverse that line of memory to be able to perform whatever 

functions you have in mind for that data.  This can be 

accomplished by changing the place in memory where the pointer 

points.  This is called pointer arithmetic.



The main idea that must be kept in mind when doing your pointer 

arithmetic is that you must increment the pointer's value by 

the correct amount.  (The correct amount is determined by the 

size of the object receiving the pointer.  e.g.  char = 1 byte; 

integer = 2 bytes; double = 8 bytes;  etc.)  The Inc() and 

Dec() functions will alter the amount by the correct amount.  

(The compiler knows what the correct size is.)  



For an example of the practical application of pointer 

arithmetic, download the BreakAApart() TI2905.



If you are doing dynamic memory allocation, it is done like this:



uses WinCRT;



procedure TForm1.Button1Click(Sender: TObject);

var

  MyArray: array[0..30] of char;

  b: ^char;

  i: integer;

begin

  StrCopy(MyArray, 'Lloyd is the greatest!'); {get something to point to}

  b := @MyArray; { assign the pointer to the memory location }

  for i := StrLen(MyArray) downto 0 do

  begin

    write(b^);   { write out the char at the current pointer location. }

    inc(b);      { point to the next byte in memory }

  end;

end;



The following code demonstrates that the Inc() and Dec() functions 

will increment or decrement accordingly by size of the type the pointer

points to:



var

  P1, P2 : ^LongInt;

  L : LongInt;

begin

  P1 := @L; { assign both pointers to the same place }

  P2 := @L;

  Inc(P2);  { Increment one }



{ Here we get the difference between the offset values of the 

two pointers.  Since we originally pointed to the same place in 

memmory, the result will tell us how much of a change occured 

when we called Inc(). }



  L := Ofs(P2^) - Ofs(P1^); { L = 4; i.e. sizeof(longInt) }

end;



You can change the type to which P1 and P2 point to something other than a 

longint to see that the change is always the correct value (SizeOf(P1^)).




Array of const

Question


How do I use "array of const"?

Answer


An array of const is in fact an open array of TVarRec (a 

predeclared Delphi type you can look up in the online help). So 

the following is Object Pascal psuedocode for the general battle

plan:



procedure AddStuff( Const A: Array of Const );

Var i: Integer;

Begin

  For i:= Low(A) to High(A) Do

  With A[i] Do

    Case VType of

    vtExtended: Begin

       { add real number, all real formats are converted to 

         extended automatically }

      End;

    vtInteger: Begin



       { add integer number, all integer formats are converted 

         to LongInt automatically }

      End;

    vtObject: Begin

        If VObject Is DArray Then

          With DArray( VObject ) Do Begin

            { add array of doubles }

          End

        Else If VObject Is IArray Then

          With IArray( VObject ) Do Begin

            { add array of integers }

          End;

      End;

    End; { Case }

End; { AddStuff }



For further information see "open arrays" in the on-line help.





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.




ReadLn longer than 255 characters

Question


How can I readln() from a file when the lines are longer than 255 bytes?

Answer


ReadLn will accept an array [0..something] of Char as 

buffer to put the read characters in and it will make a proper 

zero-terminated char out of them. The only limitation is this: 

the compiler needs to be able to figure out the size of the 

buffer at compile time, which makes the use of a variable 

declared as PChar and allocated at run-time impossible.



Workaround:



 Type

   {use longest line you may encounter here}

   TLine = Array [0..1024] of Char; 



   PLine = ^TLine;



 Var

   pBuf: PLine;

 ...

   New( pBuf );



 ...

   ReadLn( F, pBuf^ );



To pass pBuf to functions that take a parameter of type Pchar, 

use a typecast like PChar( pBuf ).



Note:  you can use a variable declared as of type TLine or an 

equivalent array of char directly, of course, but I tend to 

allocate anything larger than 4 bytes on the heap...







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.




Dynamic memory allocation

Question


How do I reduce the amount of memory taken from the data segment?  (or How do I allocate memory dynamically?)

Answer


Let's say your data structure looks like this:



 type

   TMyStructure = record

     Name: String[40];

     Data: array[0..4095] of Integer;

   end;



That's too large to be allocated globally, so instead of 

declaring a global variable,



 var

   MyData: TMyStructure;



you declare a pointer type,



 type

   PMyStructure = ^TMyStructure;



and a variable of that type,



 var

   MyDataPtr: PMyStructure;



Such a pointer consumes only four bytes of the data segment.



Before you can use the data structure, you have to allocate it 

on the heap:



 New(MyDataPtr);



and now you can access it just like you would global data. The 

only difference is that you have to use the caret operator to 

dereference the pointer:



 MyDataPtr^.Name := 'Lloyd Linklater';

 MyDataPtr^.Data[0] := 12345;



Finally, after you're done using the memory, you deallocate it:



 Dispose(MyDataPtr);





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.




Arrays, dynamically allocating

Question


Is it possible to create a dynamically-sized array in Delphi?

Answer


First, you need to create an array type using the largest

size you might possibly need.  When creating a type, no memory

is actually allocated.  If you created a variable of that type,

then the compiler will attempt to allocate the necessary memory

for you.  Instead, create a variable which is a pointer to that

type.  This causes the compiler to only allocate the four bytes

needed for the pointer.



Before you can use the array, you need to allocate memory for

it.  By using AllocMem, you will be able to control exactly how

many bytes are allocated.  To determine the number of bytes

you'll need to allocate, simply multiply the array size you

want by the size of the individual array element.  Keep in mind

that the largest block that can be allocated at one time in a

16-bit environment is 64KB.  The largest block that can be

allocated at one time in a 32-bit environment is 4GB.  To

determine the maximum number of elements you can have in your

particular array (in a 16-bit environment), divide 65,520 by

the size of the individual element.

Example:  65520 div SizeOf(LongInt)



Example of declaring an array type and pointer:



type

  ElementType = LongInt;



const

  MaxArraySize = (65520 div SizeOf(ElementType));

    (* under a 16-bit environment *)



type

  MyArrayType = array[1..MaxArraySize] of ElementType;



var

  P: ^MyArrayType;



const

  ArraySizeIWant: Integer = 1500;



Then when you wish to allocate memory for the array, you could

use the following procedure:



procedure AllocateArray;

begin

  if ArraySizeIWant <= MaxArraySize then

    P := AllocMem(ArraySizeIWant * SizeOf(LongInt));

end;



Remember to make sure that the value of ArraySizeIWant is less

than or equal to MaxArraySize.



Here is a procedure that will loop through the array and set a

value for each member:



procedure AssignValues;

var

  I: Integer;

begin

  for I := 1 to ArraySizeIWant do

    P^[I] := I;

end;



Keep in mind that you must do your own range checking.  If you

have allocated an array with five members and you try to assign

a value to the sixth member of the array, you will not receive

an error message.  However, you will get memory corruption.



Remember that you must always free up any memory that you

allocate.  Here is an example of how to dispose of this array:



procedure DeallocateArray;

begin

  P := AllocMem(ArraySizeIWant * SizeOf(LongInt));

end;



Below is an example of a dynamic array:



}



unit Unit1;



interface



uses

  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,

  Controls, Forms, Dialogs, StdCtrls;



type

  ElementType = Integer;



const

  MaxArraySize = (65520 div SizeOf(ElementType));

    { in a 16-bit environment }



type

  { Create the array type.  Make sure that you set the range to

    be the largest number you would possibly need. }

  TDynamicArray = array[1..MaxArraySize] of ElementType;

  TForm1 = class(TForm)

    Button1: TButton;

    procedure FormCreate(Sender: TObject);

    procedure Button1Click(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;



var

  Form1: TForm1;

  { Create a variable of type pointer to your array type. }

  P: ^TDynamicArray;



const

  { This is a typed constant.  They are actually static

    variables hat are initialized at runtime to the value taken

    from the source code.  This means that you can use a typed

    constant just like you would use any other variable.  Plus

    you get the added bonus of being able to automatically

    initialize it's value. }

  DynamicArraySizeNeeded: Integer = 10;



implementation



{$R *.DFM}



procedure TForm1.FormCreate(Sender: TObject);

begin

  { Allocate memory for your array.  Be very careful that you

    allocate the amount that you need.  If you try to write

    beyond the amount that you've allocated, the compiler will

    let you do it.  You'll just get data corruption. }

  DynamicArraySizeNeeded := 500;

  P := AllocMem(DynamicArraySizeNeeded * SizeOf(Integer));

  { How to assign a value to the fifth member of the array. }

  P^[5] := 68;

end;



procedure TForm1.Button1Click(Sender: TObject);

begin

  { Displaying the data. }

  Button1.Caption := IntToStr(P^[5]);

end;



procedure TForm1.FormDestroy(Sender: TObject);

begin

  { Free the memory you allocated for the array. }

  FreeMem(P, DynamicArraySizeNeeded * SizeOf(Integer));

end;



end.





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.




Object Pascal - Typecasting

Question


1.  (Sender as TButton).Enabled := True;

2.  TButton(Sender).Enabled := True;

Answer


A:

My understanding is that the difference between these is that 1. uses RTTI

(Run Time Type Information)  to check the validity of the cast before

carrying on and that 2. is a hard cast (there is no overhead of checking

the RTTI.



I rarely use the style in 1. because I am usually doing something like

this:



if (Sender is TButton) then

  TButton(Sender).Enabled := TRUE;



A:

There is a very important difference between the two forms which on the

surface both appear to be perfroming a type cast. Taking the second form

first, this form is exactly identical to a C/C++ type cast. It will

perform the type cast even if the result type is not an appropriate type

for the object or data structure.



The first form applies only to objects, and uses Run Time Type

Information (RTTI) to verify that the object is capable of being cast to

the result type. If it is not, an exception will be generated.

Additionally, no temporary variable is required, as the result with any

object is still simply a pointer. The difference is a difference in

syntax between the two languages, Delphi allows and expects implicit

pointer references, while C/C++ still requires explicit pointer

references.


Object Pascal - Typecasting (2)

Question


Can I assign the TField returned by FindField to my own variable?  For

instance:



Procedure TestFind(MyTable: TTable);

begin

  MyField: TField;

  MyField := MyTable.FindField('Customer Name');

  if MyField <> nil then begin

    MyTable.Edit;

    MyField.AsString := 'Jon Robertson';

    MyTable.Post;

  end;

end;

Answer


This example shows the same confusion you where having with type casts.

You can do what you show in the example, however I do not think you will

get the expected results. Effectively what you have done is to assign

MyField to point at the result of FindField which is also a pointer. You

have not created a new instance of TField. To create a new instance you

must use the Create constructor as follows:



MyField := TMyField.Create(Self)



You could now copy the result object to the newly created object, but

again to probably unexpected results. In fact the example shown would

not have the desired results, as the field value, as far as MyTable is

concerned has not changed. 



What you probably want to do is to double-click your table object

(MyTable), then right click the dialog box and select add. This will

create a variable such as:



  MyTableCustomerName



Then you simply modify your code as follows:



  MyTable.Edit;

  MyTableCustomerName.AsString := 'Jon Robertson';

  MyTable.Post;



If this does not do what you had in mind, you are probably better off

copying those fields or attributes of interest to you into simple types,

or a simple record, as most attributes will become meaningless when

disassociated with the table object.



All of this is covered both in the Delphi help system and the associated

manuals (the confusion is common among people coming from C++ so I am

not implying RTFM).


multi-dimensional dynamic arrays

Question


(1)  How do you use multi-dimensional arrays that are dynamic?

(2)  How do you use a multi-dimensional array for a user-defined type?

Answer


A:

  Try declaring an array of ^byte with the dimenstions [0..0] and using

getmem to get her to work...ie:



type

  pfun  = ^tfun;

  tfun  = record

            a,b,c       : integer;

            d           : string;

          end;

  afun  = array[0..0] of pfun;



var

  fun   : afun;



begin

  getmem (, sizeof(tfun));

  ..

  freemem (, sizeof(tfun));

end;



  be sure range checking is off (default anyhow), and it should work.



A:

To use dynamic arrays define a BIG array (to avoid range checking) and

a pointer to it and then declare a variable that is a pointer to this

array and use GetMem to allocate memory for it. This works for pre-defined

types, user-defined types, classes, etc.



   For instance:



type

  TMyType = class;



  TMyBigArray = array[0..MaxInt div SizeOf(TMyType) - 1] of TMyType;

  PMyBigArray = ^TMyBigArray;



var

  ary: TMyBigArray;

  t: TMyType;



begin

  GetMem(ary, 100 * SizeOf(TMyType));



  ary^[12] := TMyType.Create;  // writing array position

  t := ary^[12];               // reading array position

end.



 

   In Delphi 2.0 you can access the array without the pointer operator, 

like a static array:



  ary[12] := TMyType.Create;  // writing array position

  t := ary[12];               // reading array position





It works very, very well for single-dimensional arrays. I tried to

use multi-dimensional arrays this way but I had no success.


Dynamic array of records

Question


Using the following code:



 type

   TDetailRec = Record

     value1       : Integer;

     value2       : Integer;

   end;



    PTDetailRec = ^DetailRec;



  var

    detailArray: PTDetailRec;



How would I allocate and reference a dynamic array of TDetailRec?



Answer


Unless memory usage and speed are paramount, I would recommend that you

use a TList as follows:



var

  MyList: TList;



procedure AllocateIt(HowMany: Integer);



  var

    PMyRec: PDetailRec;

    I: Integer;



  begin

    MyList := TList.Create;

    for I := 1 to HowMany do

      begin

      New(PMyRec);

      with PMyRec^ do

        {assign initial values} 

      MyList.Add(PMyRec);

      end;

  end;



Then when you want to reference an item use for example



  with PMyList(TList.Items[n]) do

    {reference the individual fields}



If you really want to maintain your own array of pointers, you can do

that as follows:



type

  TDetailArray = array[0..1] of PDetailRec;

  PDetailArray = ^TDetailArray;



var

  DetailArray: PDetailArray;



procedure AllocateIt(HowMany: Integer);



  var

    I: Integer;



  begin

    GetMem(DetailArray, SizeOf(TDetailRec) * HowMany);

    for I := 0 to (HowMany - 1) do

      begin

      New(DetailArray^[I]);

      with DetailArray^[I] do

        {Assign initial values}

      end;

  end;



Then to reference an item use:



  with DetailArray^[n]

    {reference the individual fields}



I do not believe the run time will catch the subrange of 0..1 even with

range checking on. And certainly will not if you turn range checking

{$R-} off around the references. Alternatively, you could make the upper

range an arbitrarily large number, larger than would ever be allocated.

No storage is ever generated for a type declaration. I have seen a few

ways to represent this more elegantly syntactically, but this is a the

basic concept.





A:

This is where typecasting comes in and where you will sooner or

later find the need to derive a custom class from tlist that will hide that

task from you.



but lets start at the beginning.



the 3rd item in a tlist is at index 2 and you get a pointer to it through



var

  l:tlist;

  o:tobject;



o:=l[2];



though typecasting you can let the compiler assign that tobject to a TDetailRec:



var

  l:tlist;

  t:TDetailRec;



t:=TDetailRec(l[2]);



and so you can get the answer..



var 

  i:integer



i:=TDetailRec(l[2]).value2;





.... to make matters just a little more formal, you would now look at how to

add properties to an object and declare a custom layer that derived from

Tlist wich would be TMyList and introduce



private

  GetMyRev

  SetMyRec   code that would basically call the 'built-in' Get/Set methods

through one line of code each to just recast the pointers.



and 

published

  property myrec: TDetailRec read GetMyRec write GetMyrec





some headscratching later you'll have a simple way of putting any type of

custom data into a list.



BUT caveat.



internally TList uses a procedure 'Grow' that is not the very most efficient

thing at allocating room and you need to be aware of that. If you know HOW

MANY items you may need, then TELL THE TLIST ahead of time by setting it's

Capacity property in advance. This will allocate room without reshuffling

things as you go on adding items. It's not really a that big of a deal, but

it's great practice to think these things through.


Close    To Top
  • Prev Article-Programming:
  • Next Article-Programming:
  • Now: Tutorial for Web and Software Design > Programming > delphi > Programming Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles