2004. május 31., hétfő
Select or find a TTreeView node by caption
Problem/Question/Abstract:
How to select or find a TTreeView node by caption
Answer:
Solve 1:
function GetNodeByCaption(NodeCaption: string): TTreeNode;
var
X: Integer;
begin
Result := nil;
for X := 0 to TreeView1.Items.Count - 1 do
begin
if (TreeView1.Items[X].Caption = NodeCaption) then
Result := TreeView1.Items[X];
Break;
end;
end;
Solve 2:
Returns a node based on the text property. Set AVisible to show the new node:
function GetNodeByText(ATree: TTreeView; AValue: string;
AVisible: Boolean): TTreeNode;
var
Node: TTreeNode;
begin
Result := nil;
if ATree.Count = 0 then
Exit;
Node := ATree.Items[0];
while Node <> nil do
begin
if UpperCase(Node.Text) = AValue then
begin
Result := Node;
if AVisible then
Result.MakeVisible;
Break;
end;
Node := Node.GetNext;
end;
end;
2004. május 30., vasárnap
How to copy a 2D array with picture greylevels to an image (2)
Problem/Question/Abstract:
I have been developing a program to display and manipulate medical images which consist of 2D arrays of greyscale values as described. As was observed, the Pixels property is way too slow. Here's what I discovered. I think you'll find it a big improvement.
Answer:
Assuming your data is stored in an array of bytes named TestArray, for example:
TestArray: array[0..127, 0..127] of byte { ... }
ArrayPtr := addr(TestArray); {ArrayPtr: pointer}
In this case we are going to display on the bitmap of a TImage component that has been dropped on the canvas and named Image1.
Image1.Picture.Bitmap.Width := 128;
Image1.Picture.Bitmap.Height := 128;
This is a Windows API function that will copy the bits in TestArray, pointed to by ArrayPtr, into an HBitmap structure, in this case Image1.Picture.Bitmap.Handle.
SetBitmapBits(Image1.Picture.Bitmap.Handle, sizeof(TestArray), ArrayPtr);
Image1.Refresh; {must refresh before changes are displayed}
You still have to deal with the palette, but this technique works great for me.
2004. május 29., szombat
How to set the DisplayFormat of a TDateTime field to time only at runtime
Problem/Question/Abstract:
I'm running a simple query that returns a variable amount of columns somtimes with a DateTime column. How can I set at runtime the Displayformat property or any other way to format the column as a time only field. In other words, I can't seem to find where to set the DisplayFormat property at runtime.
Answer:
Here's one way:
procedure FormatDateFieldsAsTime(DS: TDataSet; TimeFormat: string);
var
f: integer;
begin
for f := 0 to DS.FieldCount - 1 do
if DS.Fields[f] is TDateTimeField then
TDateTimeField(DS.Fields[f]).DisplayFormat := TimeFormat;
end;
Apply this to the query after it's been run, like:
FormatDateFieldsAsTime(Query1, 'hh:mm:ss');
The DisplayFormat is available (assuming you're not creating any persistant fields for this dynamic query) from the query's (or other TDataSet's) Fields property.
2004. május 28., péntek
How to specify a line break in a TRichEdit
Problem/Question/Abstract:
I need to be able to (preferrably dynamically as the user is typing text) to specify a line break of say 70 characters so that the cursor will go to a new line upon reaching the 70 character limit. Actually it would be best to break on the last word boundary but even a break at 70 characters would give me a start.
Answer:
I've had a play with this and the following is the best I could come up with quickly. At least it may give you a start:
Set a Variable called Backpace : Boolean = False ;
procedure GetCurrentRC(re1: TRichedit; var row, col: LongInt);
begin
{Get Current Row and Column Values for Richedit Control}
with re1 do
begin
Row := sendMessage(handle, EM_LINEFROMCHAR, Selstart, 0);
Col := selstart - sendmessage(handle, EM_LINEINDEX, row, 0);
end;
end;
procedure TForm1.re1SelectionChange(Sender: TObject);
var
RTRow, RTCol: LongInt;
begin
GetCurrentRC(re1, RTRow, RTCol);
if (rtCol = 70) and (not Backspace) then
re1.Lines[rtRow] := Memo1.Lines[rtRow] + #13#10;
end;
procedure TForm1.Re1KeyPress(Sender: TObject; var Key: Char);
begin
{If Backspacing we don't want it to jump down again}
if key = #8 then
backspace := True
else
backspace := False;
end;
I think that's about right. You would have to search on the position of any space if you wanted to break on a word boundary.
2004. május 27., csütörtök
How to paint an arc on a TCanvas
Problem/Question/Abstract:
How to paint an arc on a TCanvas
Answer:
procedure PlotArc(const Canvas: TCanvas; const Center: TPoint; const Radius: Integer;
const StartAngle: Single; const StopAngle: Single);
function GetPositionForAngle(const Angle: Single): TPoint;
var
CosAngle: Extended;
SinAngle: Extended;
begin
SinCos(DegToRad(Angle), SinAngle, CosAngle);
Result.X := Round(Center.X + Radius * SinAngle);
Result.Y := Round(Center.Y - Radius * CosAngle);
end;
var
Index: Integer;
begin
with GetPositionForAngle(StartAngle) do
Canvas.MoveTo(X, Y);
for Index := Ceil(StartAngle) to Floor(StopAngle) do
with GetPositionForAngle(Index) do
Canvas.LineTo(X, Y);
with GetPositionForAngle(StopAngle) do
Canvas.LineTo(X, Y);
end;
2004. május 26., szerda
Save a TImagelist with all its images to a file
Problem/Question/Abstract:
How to save a TImagelist with all its images to a file
Answer:
There are ready-made methods for saving any component including all its children to a file. For writing components use WriteComponentResFile(path + source filename , component name source)
WriteComponentResFile('C:\imagelist1.bin', imagelist1);
For reading the data back to a component: component := ReadComponentResFile(path + source filename , component name traget)
imagelist1 := ReadComponentResFile('c:\imagelist1.bin', nil) as TImagelist;
Tip 1 - Reading the component will give the same name of the component written so don't try to load it to another component, even if it was the same type. You will get a duplicate name and delphi will crash. But you can jump over this as a programmer
Tip 2 - Get benfit of storing the heavy components inside compressed files, so you can get smaller programs
2004. május 25., kedd
Floating toolbar
Problem/Question/Abstract:
Floating toolbar
Answer:
All you have to do is handle Windows' wm_NCHitTest message.
(Compare to the tip how to drag a window without a caption bar. It's the same technique.)
unit Dragmain;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCHitTest;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.WMNCHitTest(var M: TWMNCHitTest);
begin
inherited; { call the inherited message handler }
if M.Result = htClient then { is the click in the client area? }
M.Result := htCaption; { if so, make Windows think it's }
{ on the caption bar. }
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end;
end.
2004. május 24., hétfő
IDE harddisk serial number (Part 2)
Problem/Question/Abstract:
In my previous article I described the way to extract s/n through call DeviceIoControl with DFP_RECEIVE_DRIVE_DATA control code. But on NT it works only under account with administrative priveleges. Now I've found the way to do this under 'everyone' account.
Answer:
Warning! On Win9x smartvsd.vxd must be installed: simply copy it from
\windows\system\ to \windows\system\iosubsys\ and reboot.
// (c) Alex Konshin mailto:akonshin@earthlink.net 30 jul 2000
program IdeSN;
// PURPOSE: Simple console application that extract first IDE disk serial number.
{$APPTYPE CONSOLE}
uses
Windows,
SysUtils; // only for Win32Platform and SysErrorMessage
//-------------------------------------------------------------
function GetIdeDiskSerialNumber: string;
type
TSrbIoControl = packed record
HeaderLength: ULONG;
Signature: array[0..7] of Char;
Timeout: ULONG;
ControlCode: ULONG;
ReturnCode: ULONG;
Length: ULONG;
end;
SRB_IO_CONTROL = TSrbIoControl;
PSrbIoControl = ^TSrbIoControl;
TIDERegs = packed record
bFeaturesReg: Byte; // Used for specifying SMART "commands".
bSectorCountReg: Byte; // IDE sector count register
bSectorNumberReg: Byte; // IDE sector number register
bCylLowReg: Byte; // IDE low order cylinder value
bCylHighReg: Byte; // IDE high order cylinder value
bDriveHeadReg: Byte; // IDE drive/head register
bCommandReg: Byte; // Actual IDE command.
bReserved: Byte; // reserved. Must be zero.
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;
TSendCmdInParams = packed record
cBufferSize: DWORD;
irDriveRegs: TIDERegs;
bDriveNumber: Byte;
bReserved: array[0..2] of Byte;
dwReserved: array[0..3] of DWORD;
bBuffer: array[0..0] of Byte;
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;
TIdSector = packed record
wGenConfig: Word;
wNumCyls: Word;
wReserved: Word;
wNumHeads: Word;
wBytesPerTrack: Word;
wBytesPerSector: Word;
wSectorsPerTrack: Word;
wVendorUnique: array[0..2] of Word;
sSerialNumber: array[0..19] of Char;
wBufferType: Word;
wBufferSize: Word;
wECCSize: Word;
sFirmwareRev: array[0..7] of Char;
sModelNumber: array[0..39] of Char;
wMoreVendorUnique: Word;
wDoubleWordIO: Word;
wCapabilities: Word;
wReserved1: Word;
wPIOTiming: Word;
wDMATiming: Word;
wBS: Word;
wNumCurrentCyls: Word;
wNumCurrentHeads: Word;
wNumCurrentSectorsPerTrack: Word;
ulCurrentSectorCapacity: ULONG;
wMultSectorStuff: Word;
ulTotalAddressableSectors: ULONG;
wSingleWordDMA: Word;
wMultiWordDMA: Word;
bReserved: array[0..127] of Byte;
end;
PIdSector = ^TIdSector;
const
IDE_ID_FUNCTION = $EC;
IDENTIFY_BUFFER_SIZE = 512;
DFP_RECEIVE_DRIVE_DATA = $0007C088;
IOCTL_SCSI_MINIPORT = $0004D008;
IOCTL_SCSI_MINIPORT_IDENTIFY = $001B0501;
DataSize = sizeof(TSendCmdInParams) - 1 + IDENTIFY_BUFFER_SIZE;
BufferSize = SizeOf(SRB_IO_CONTROL) + DataSize;
W9xBufferSize = IDENTIFY_BUFFER_SIZE + 16;
var
hDevice: THandle;
cbBytesReturned: DWORD;
pInData: PSendCmdInParams;
pOutData: Pointer; // PSendCmdOutParams
Buffer: array[0..BufferSize - 1] of Byte;
srbControl: TSrbIoControl absolute Buffer;
procedure ChangeByteOrder(var Data; Size: Integer);
var
ptr: PChar;
i: Integer;
c: Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1) - 1 do
begin
c := ptr^;
ptr^ := (ptr + 1)^;
(ptr + 1)^ := c;
Inc(ptr, 2);
end;
end;
begin
Result := '';
FillChar(Buffer, BufferSize, #0);
if Win32Platform = VER_PLATFORM_WIN32_NT then
begin // Windows NT, Windows 2000
// Get SCSI port handle
hDevice := CreateFile(
'\\.\Scsi0:', // Note: '\\.\C:' requires administrative permissions.
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then
Exit;
try
srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
System.Move('SCSIDISK', srbControl.Signature, 8);
srbControl.Timeout := 2;
srbControl.Length := DataSize;
srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
pInData := PSendCmdInParams(PChar(@Buffer)
+ SizeOf(SRB_IO_CONTROL));
pOutData := pInData;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl(hDevice, IOCTL_SCSI_MINIPORT,
@Buffer, BufferSize, @Buffer, BufferSize,
cbBytesReturned, nil) then
Exit;
finally
CloseHandle(hDevice);
end;
end
else
begin // Windows 95 OSR2, Windows 98
hDevice := CreateFile('\\.\SMARTVSD', 0, 0, nil,
CREATE_NEW, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then
Exit;
try
pInData := PSendCmdInParams(@Buffer);
pOutData := @pInData^.bBuffer;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl(hDevice, DFP_RECEIVE_DRIVE_DATA,
pInData, SizeOf(TSendCmdInParams) - 1, pOutData,
W9xBufferSize, cbBytesReturned, nil) then
Exit;
finally
CloseHandle(hDevice);
end;
end;
with PIdSector(PChar(pOutData) + 16)^ do
begin
ChangeByteOrder(sSerialNumber, SizeOf(sSerialNumber));
SetString(Result, sSerialNumber, SizeOf(sSerialNumber));
end;
end;
//=============================================================
var
s: string;
rc: DWORD;
begin
s := GetIdeDiskSerialNumber;
if s = '' then
begin
rc := GetLastError;
if rc = 0 then
WriteLn('IDE drive is not support SMART feature')
else
WriteLn(SysErrorMessage(rc));
end
else
WriteLn('Disk serial number: ''', s, '''');
end.
See also IdeInfo2 on my homepage: http://home.earhlink.net/~akonshin/
Component Download: http://home.earthlink.net/~akonshin/files/IdeSN.zip
2004. május 23., vasárnap
Getting the icon of an application, library or document
Problem/Question/Abstract:
How can I get the icon of an application or the icons in a DLL?
Answer:
ExtractAssociatedIcon
To get the icon of an application or document we can use this API function (declared in the ShellAPI unit):
function ExtractAssociatedIcon(hInst: HINST; lpIconPath: PChar;
var lpiIcon: Word): HICON; stdcall;
hInst: The application handle. This value is contained in the predefined variable HInstance.
lpIconPath: A pointer to a character buffer that should contain a null terminated string with the full path name of the application, library (DLL) or document. If it is a document, the function will place there the full pathname of the associated application from where the icon was extracted, so we should allocate a buffer large enough.
lpiIcon: The icon index (the first icon in the file has an index of 0). If lpIconPath specifies a document, then lpiIcon is set by the function (that's why it is passed by reference) to the index position of the actual icon taken from the associated executable (defined in the file association).
Return value:
If the function fails, it returns 0. If it succeeds, it returns an icon handle, which is an integer value Windows uses to identify the allocated resource. It is not necessary to call the API DestroyIcon to release the icon since it'll be deallocated automatically when the application finishes, although you can do it if you want.
Sample call
Now, what do we do with the icon handle? Normally what we want is an icon, namely and instance of the TIcon class. All we have to do is create a TIcon object and assign this handle to its Handle property. If later we assign the Handle property to another value, the previous icon will be automatically be released. The same happens if the TIcon object is freed. Here is an example that changes the icon of the form:
procedure TForm1.Button1Click(Sender: TObject);
var
IconIndex: word;
Buffer: array[0..2048] of char;
IconHandle: HIcon;
begin
StrCopy(@Buffer, 'C:\Windows\Help\Windows.hlp');
IconIndex := 0;
IconHandle := ExtractAssociatedIcon(HInstance, Buffer, IconIndex);
if IconHandle <> 0 then
Icon.Handle := IconHandle;
end;
GetAssociatedIcon
Unfortunately, ExtractAssociatedIcon fails if the file does not exists on disk, so we defined a procedure that gets the icon of a file whether it exists or not, and can also get the small icon (ideal for a TListView that can be shown in vsIcon or vsReport view styles). The procedure receives three parameters: the filename and two pointers to HICON (integer) variables: one for the large icon (32x32) and another one for the small icon (16x16). Any of them can be nil if you don't need one of these icons. The icons "returned" by the procedure must be freed with the DestroyIcon API. This will be done automatically if you assign the icon handle (HICON) to the Handle property of a TIcon object
(the icon will be released when this object gets freed or a new value is assigned to it).
uses
Registry, ShellAPI;
type
PHICON = ^HICON;
procedure GetAssociatedIcon(FileName: TFilename;
PLargeIcon, PSmallIcon: PHICON);
// Gets the icons of a given file
var
IconIndex: word; // Position of the icon in the file
FileExt, FileType: string;
Reg: TRegistry;
p: integer;
p1, p2: pchar;
label
noassoc;
begin
IconIndex := 0;
// Get the extension of the file
FileExt := UpperCase(ExtractFileExt(FileName));
if ((FileExt <> '.EXE') and (FileExt <> '.ICO')) or
not FileExists(FileName) then
begin
// If the file is an EXE or ICO and it exists, then
// we will extract the icon from this file. Otherwise
// here we will try to find the associated icon in the
// Windows Registry...
Reg := nil;
try
Reg := TRegistry.Create(KEY_QUERY_VALUE);
Reg.RootKey := HKEY_CLASSES_ROOT;
if FileExt = '.EXE' then
FileExt := '.COM';
if Reg.OpenKeyReadOnly(FileExt) then
try
FileType := Reg.ReadString('');
finally
Reg.CloseKey;
end;
if (FileType <> '') and Reg.OpenKeyReadOnly(
FileType + '\DefaultIcon') then
try
FileName := Reg.ReadString('');
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
// If we couldn't find the association, we will
// try to get the default icons
if FileName = '' then
goto noassoc;
// Get the filename and icon index from the
// association (of form '"filaname",index')
p1 := PChar(FileName);
p2 := StrRScan(p1, ',');
if p2 <> nil then
begin
p := p2 - p1 + 1; // Position of the comma
IconIndex := StrToInt(Copy(FileName, p + 1,
Length(FileName) - p));
SetLength(FileName, p - 1);
end;
end;
// Attempt to get the icon
if ExtractIconEx(pchar(FileName), IconIndex,
PLargeIcon^, PSmallIcon^, 1) <> 1 then
begin
noassoc:
// The operation failed or the file had no associated
// icon. Try to get the default icons from SHELL32.DLL
try // to get the location of SHELL32.DLL
FileName := IncludeTrailingBackslash(GetSystemDir)
+ 'SHELL32.DLL';
except
FileName := 'C:\WINDOWS\SYSTEM\SHELL32.DLL';
end;
// Determine the default icon for the file extension
if (FileExt = '.DOC') then
IconIndex := 1
else if (FileExt = '.EXE')
or (FileExt = '.COM') then
IconIndex := 2
else if (FileExt = '.HLP') then
IconIndex := 23
else if (FileExt = '.INI')
or (FileExt = '.INF') then
IconIndex := 63
else if (FileExt = '.TXT') then
IconIndex := 64
else if (FileExt = '.BAT') then
IconIndex := 65
else if (FileExt = '.DLL')
or (FileExt = '.SYS')
or (FileExt = '.VBX')
or (FileExt = '.OCX')
or (FileExt = '.VXD') then
IconIndex := 66
else if (FileExt = '.FON') then
IconIndex := 67
else if (FileExt = '.TTF') then
IconIndex := 68
else if (FileExt = '.FOT') then
IconIndex := 69
else
IconIndex := 0;
// Attempt to get the icon.
if ExtractIconEx(pchar(FileName), IconIndex,
PLargeIcon^, PSmallIcon^, 1) <> 1 then
begin
// Failed to get the icon. Just "return" zeroes.
if PLargeIcon <> nil then
PLargeIcon^ := 0;
if PSmallIcon <> nil then
PSmallIcon^ := 0;
end;
end;
end;
Sample call
This example will change the icon of your form:
procedure TForm1.Button1Click(Sender: TObject);
var
SmallIcon: HICON;
begin
GetAssociatedIcon('file.doc', nil, @SmallIcon);
if SmallIcon <> 0 then
Icon.Handle := SmallIcon;
end;
Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php
2004. május 22., szombat
Delphi translation of the IAutoComplete interface
Problem/Question/Abstract:
I'm looking for a Delphi translation of the IAutoComplete interface in Microsofts shldisp.h. Can anyone point me in the right direction, please?
Answer:
Here is the translation and a TEdit decendant I wrote a while back:
unit uAutoComplete;
interface
uses
Windows, SysUtils, Controls, Classes, ActiveX, ComObj, stdctrls, Forms, Messages;
const
IID_IAutoComplete: TGUID = '{00bb2762-6a77-11d0-a535-00c04fd7d062}';
IID_IAutoComplete2: TGUID = '{EAC04BC0-3791-11d2-BB95-0060977B464C}';
CLSID_IAutoComplete: TGUID = '{00BB2763-6A77-11D0-A535-00C04FD7D062}';
IID_IACList: TGUID = '{77A130B0-94FD-11D0-A544-00C04FD7d062}';
IID_IACList2: TGUID = '{470141a0-5186-11d2-bbb6-0060977b464c}';
CLSID_ACLHistory: TGUID = '{00BB2764-6A77-11D0-A535-00C04FD7D062}';
CLSID_ACListISF: TGUID = '{03C036F1-A186-11D0-824A-00AA005B4383}';
CLSID_ACLMRU: TGUID = '{6756a641-de71-11d0-831b-00aa005b4383}';
type
IACList = interface(IUnknown)
['{77A130B0-94FD-11D0-A544-00C04FD7d062}']
function Expand(pszExpand: POLESTR): HResult; stdcall;
end;
const
{Options for IACList2}
ACLO_NONE = 0; {don't enumerate anything}
ACLO_CURRENTDIR = 1; {enumerate current directory}
ACLO_MYCOMPUTER = 2; {enumerate MyComputer}
ACLO_DESKTOP = 4; {enumerate Desktop Folder}
ACLO_FAVORITES = 8; {enumerate Favorites Folder}
ACLO_FILESYSONLY = 16; {enumerate only the file system}
type
IACList2 = interface(IACList)
['{470141a0-5186-11d2-bbb6-0060977b464c}']
function SetOptions(dwFlag: DWORD): HResult; stdcall;
function GetOptions(var pdwFlag: DWORD): HResult; stdcall;
end;
IAutoComplete = interface(IUnknown)
['{00bb2762-6a77-11d0-a535-00c04fd7d062}']
function Init(hwndEdit: HWND; const punkACL: IUnknown; pwszRegKeyPath,
pwszQuickComplete: POLESTR): HResult; stdcall;
function Enable(fEnable: BOOL): HResult; stdcall;
end;
const
{Options for IAutoComplete2}
ACO_NONE = 0;
ACO_AUTOSUGGEST = $1;
ACO_AUTOAPPEND = $2;
ACO_SEARCH = $4;
ACO_FILTERPREFIXES = $8;
ACO_USETAB = $10;
ACO_UPDOWNKEYDROPSLIST = $20;
ACO_RTLREADING = $40;
type
IAutoComplete2 = interface(IAutoComplete)
['{EAC04BC0-3791-11d2-BB95-0060977B464C}']
function SetOptions(dwFlag: DWORD): HResult; stdcall;
function GetOptions(out pdwFlag: DWORD): HResult; stdcall;
end;
TEnumString = class(TInterfacedObject, IEnumString)
private
FStrings: TStringList;
FCurrIndex: integer;
public
{IEnumString}
function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
function Skip(celt: Longint): HResult; stdcall;
function Reset: HResult; stdcall;
function Clone(out enm: IEnumString): HResult; stdcall;
{VCL}
constructor Create;
destructor Destroy; override;
end;
TACOption = (acAutoAppend, acAutoSuggest, acUseArrowKey);
TACOptions = set of TACOption;
TACSource = (acsList, acsHistory, acsMRU, acsShell);
TACEdit = class(TEdit)
private
FACList: TEnumString;
FAutoComplete: IAutoComplete;
FACEnabled: boolean;
FACOptions: TACOptions;
FACSource: TACSource;
function GetACStrings: TStringList;
procedure SetACEnabled(const Value: boolean);
procedure SetACOptions(const Value: TACOptions);
procedure SetACSource(const Value: TACSource);
protected
procedure CreateWnd; override;
procedure DestroyWnd; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property ACStrings: TStringList read GetACStrings;
property ACEnabled: boolean read FACEnabled write SetACEnabled;
property ACOptions: TACOptions read FACOptions write SetACOptions;
property ACSource: TACSource read FACSource write SetACSource;
end;
implementation
{ IUnknownInt }
function TEnumString.Clone(out enm: IEnumString): HResult;
begin
Result := E_NOTIMPL;
pointer(enm) := nil;
end;
constructor TEnumString.Create;
begin
inherited Create;
FStrings := TStringList.Create;
FCurrIndex := 0;
end;
destructor TEnumString.Destroy;
begin
FStrings.Free;
inherited;
end;
function TEnumString.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult;
var
I: Integer;
wStr: WideString;
begin
I := 0;
while (I < celt) and (FCurrIndex < FStrings.Count) do
begin
wStr := FStrings[FCurrIndex];
TPointerList(elt)[I] := CoTaskMemAlloc(2 * (Length(wStr) + 1));
StringToWideChar(wStr, TPointerList(elt)[I], 2 * (Length(wStr) + 1));
Inc(I);
Inc(FCurrIndex);
end;
if pceltFetched <> nil then
pceltFetched^ := I;
if I = celt then
Result := S_OK
else
Result := S_FALSE;
end;
function TEnumString.Reset: HResult;
begin
FCurrIndex := 0;
Result := S_OK;
end;
function TEnumString.Skip(celt: Integer): HResult;
begin
if (FCurrIndex + celt) <= FStrings.Count then
begin
Inc(FCurrIndex, celt);
Result := S_OK;
end
else
begin
FCurrIndex := FStrings.Count;
Result := S_FALSE;
end;
end;
{ TACEdit }
constructor TACEdit.Create(AOwner: TComponent);
begin
inherited;
FACList := TEnumString.Create;
FACEnabled := true;
FACOptions := [acAutoAppend, acAutoSuggest, acUseArrowKey];
end;
procedure TACEdit.CreateWnd;
var
Dummy: IUnknown;
Strings: IEnumString;
begin
inherited;
if HandleAllocated then
begin
try
Dummy := CreateComObject(CLSID_IAutoComplete);
if (Dummy <> nil) and (Dummy.QueryInterface(IID_IAutoComplete, FAutoComplete) =
S_OK) then
begin
case FACSource of
acsHistory:
Strings := CreateComObject(CLSID_ACLHistory) as IEnumString;
acsMRU:
Strings := CreateComObject(CLSID_ACLMRU) as IEnumString;
acsShell:
Strings := CreateComObject(CLSID_ACListISF) as IEnumString;
else
Strings := FACList as IEnumString;
end;
if S_OK = FAutoComplete.Init(Handle, Strings, nil, nil) then
begin
SetACEnabled(FACEnabled);
SetACOptions(FACOptions);
end;
end;
except
{CLSID_IAutoComplete is not available}
end;
end;
end;
destructor TACEdit.Destroy;
begin
FACList := nil;
inherited;
end;
procedure TACEdit.DestroyWnd;
begin
if (FAutoComplete <> nil) then
begin
FAutoComplete.Enable(false);
FAutoComplete := nil;
end;
inherited;
end;
function TACEdit.GetACStrings: TStringList;
begin
Result := FACList.FStrings;
end;
procedure TACEdit.SetACEnabled(const Value: boolean);
begin
if (FAutoComplete <> nil) then
begin
FAutoComplete.Enable(FACEnabled);
end;
FACEnabled := Value;
end;
procedure TACEdit.SetACOptions(const Value: TACOptions);
const
Options: array[TACOption] of integer = (ACO_AUTOAPPEND, ACO_AUTOSUGGEST,
ACO_UPDOWNKEYDROPSLIST);
var
Option: TACOption;
Opt: DWORD;
AC2: IAutoComplete2;
begin
if (FAutoComplete <> nil) then
begin
if S_OK = FAutoComplete.QueryInterface(IID_IAutoComplete2, AC2) then
begin
Opt := ACO_NONE;
for Option := Low(Options) to High(Options) do
begin
if (Option in FACOptions) then
Opt := Opt or DWORD(Options[Option]);
end;
AC2.SetOptions(Opt);
end;
end;
FACOptions := Value;
end;
procedure TACEdit.SetACSource(const Value: TACSource);
begin
if FACSource <> Value then
begin
FACSource := Value;
RecreateWnd;
end;
end;
initialization
finalization
end.
2004. május 21., péntek
Getting rid of the initial flash of a WS_MAXIMIZE child
Problem/Question/Abstract:
How can I open an MDI child form so that it's initially in a maximized state? Every time I try, it appears in its normal size, then maximizes visibly. I can't hide it because Delphi won't let me, and if I trick it by hiding it in CreateParams, it's maximized for a split second (just after OnShow()), then is reduced to normal size, then is re-maximized. This is all happening somewhere after OnShow() and I can't seem to stop it ... I just need it to open already maximized, and all ready to go. ... Help!
Answer:
One thing you might have noticed is that child forms set with the wsMaximized property have a visible flash when they're first created. First they're created in a normal state, then they maximize. This is more annoying than problematic.
For those of you who are experienced in mucking about with form properties, you might think that setting the form's window style to WS_MAXIMIZE in the CreateParams method would do the trick. Alas, that doesn't work either. But don't worry, there's a very simple solution.
One of the ways you can prevent the user from seeing background operations on a window is to prevent it from painting, then having it refresh after the changes have been made. To the user, it will appear as if the screen was automagically changed in the blink of an eye. With respect to opening up a maximized MDI child form in an MDI application, this is exactly the type of thing we're going to do.
The specific function that allows us to prevent screen painting is a WinAPI function called LockWindowUpdate. LockWindowUpdate takes a single parameter &mdash the handle of the window &mdash and prevents it from painting until LockWindowUpdate is called again with a parameter of '0.' So, with respect to our particular problem, to prevent a maximized MDI child from flashing at create, you enclose its create statement between two LockWindowUpdate calls like so:
LockWindowUpdate(MyMDIMainForm.Handle);
MyMDIChild := TMyMDIChild.Create(Application);
LockWindowUpdate(0);
Pretty simple, huh? Notice that I locked the screen painting with respect to the MDI form, not the MDI child. That's important, because if you tried to lock the update for the child, you'd get an error because the handle is invalid. In any case, use this technique for all your MDI applications to avoid the initial flash.
2004. május 20., csütörtök
How to display hints always under the mouse cursor
Problem/Question/Abstract:
How to display hints always under the mouse cursor
Answer:
This code snippet shows how to make your popup hint windows behave more like normal windows apps. Instead of always being square under the control they belong to, they are based on where the mouse is. This uses the GetIconInfo API, which is only available for Win32.
Add the following to your main form's OnCreate event handler:
procedure TMainForm.FormCreate(Sender: TObject);
begin
Application.OnShowHint := GetHintInfo;
end;
Add the following declaration to your main form's protected declartion:
procedure GetHintInfo(var HintStr: string; var CanShow: boolean; var HintInfo: THintInfo);
And, finally, add this procedure to your main form:
procedure TMainForm.GetHintInfo(var HintStr: string; var CanShow: boolean; var HintInfo: THintInfo);
var
II: TIconInfo;
Bmp: Windows.TBitmap;
begin
with HintInfo do
begin
{Make sure we have a control that fired the hint}
if HintControl = nil then
exit;
{Convert the cursor's coordinates from relative to hint to relative to screen}
HintPos := HintControl.ClientToScreen(CursorPos);
{Get some information about the cursor that is used for the hint control}
GetIconInfo(Screen.Cursors[HintControl.Cursor], II);
{Get some information about the bitmap representing the cursor}
GetObject(II.hbmMask, SizeOf(Windows.TBitmap), @Bmp);
{If the info did not include a color bitmap then the mask bitmap is really two bitmaps, an AND & XOR mask. Increment our Y position by the bitmap's height}
if II.hbmColor = 0 then
inc(HintPos.Y, Bmp.bmHeight div 2)
else
inc(HintPos.Y, Bmp.bmHeight);
{Subtract out the Y hotspot position}
dec(HintPos.Y, II.yHotSpot);
{We are responsible for cleaning up the bitmap handles returned by GetIconInfo}
DeleteObject(II.hbmMask);
DeleteObject(II.hbmColor);
end;
end;
2004. május 19., szerda
Delphi controls MS Office applications
Problem/Question/Abstract:
Delphi controls MS Office applications
Answer:
How can you remote control MS Office applications from your Delphi application? The Answer is to use a TOLEContainer.
It requires some interface knowledge to use the right object(s) and their properties. Some samples are added to Delphi demos, but all of them are targeted at MSWord. I have posted examples for Internet Explorer elsewhere and here is a sample for MSExcel:
// procedure is activated when OleOject activates user interface
// procedure copies TStringGrid content to an (OleObject) Excel sheet
procedure TForm1.OleContainer1Activate(Sender: TObject);
var
ExcelSheet: Variant;
Count,
Curent: Variant;
i,
j: Integer;
begin
// first we read how many sheets are open in a specified Excel document
Count := OleContainer1.OleObject.Application.Sheets.Count;
// then we read the number of a sheet to witch user wants to add StringGrid content
Curent := StrToInt(OKBottomDlg.Edit2.Text);
if Curent <> 0 then
begin
if Curent <= Count then
// if the sheet with index Curent exist then copy content
begin
// first we activate the desiered sheet object
OleContainer1.OleObject.Application.Sheets[Count].Activate;
// pass the object to a variant variable
ExcelSheet := OleContainer1.OleObject.Application.ActiveSheet;
// now we can do what ever we like with it
ExcelSheet.name := OKBottomDlg.Edit3.Text + IntToStr(Count);
for i := 0 to StringGrid1.RowCount do
begin
for j := 0 to StringGrid1.ColCount do
begin
ExcelSheet.Cells(i, j) := StringGrid1.Cells[j, i]
end
end;
// here we copy the content
end
else // else if the sheet we are trying to access doesn't exsist
begin
// we add new sheets untill the requested
// user's index is reached ( curent variable )
for i := Count + 1 to Curent do
begin
OleContainer1.OleObject.Application.Sheets.Add
end;
// again we do as above
OleContainer1.OleObject.Application.Sheets[Curent].Activate;
ExcelSheet := OleContainer1.OleObject.Application.ActiveSheet;
ExcelSheet.name := OKBottomDlg.Edit3.Text + IntToStr(Count);
for i := 0 to StringGrid1.RowCount do
begin
for j := 0 to StringGrid1.ColCount do
begin
ExcelSheet.Cells(i, j) := StringGrid1.Cells[j, i]
end
end;
end
end;
end;
2004. május 18., kedd
How to make the TJPEGImage component recognize the *.jpeg file extension
Problem/Question/Abstract:
How to make the TJPEGImage component recognize the *.jpeg file extension
Answer:
{ ... }
var
MyImage: TImage;
begin
JPEG := TJPEGImage.Create;
try
JPEG.LoadFromFile('C:\TEMP\SOMEIMAGE.JPEG');
MainImage.Picture.Assign(JPEG);
MainImage.Invalidate;
finally
JPEG.Free;
end;
2004. május 17., hétfő
Retrieve a file's "Last Accessed" attribute
Problem/Question/Abstract:
Retrieve a file's "Last Accessed" attribute
Answer:
In Windows 95, you can see when a file was last accessed by right-clicking the file and selecting properties. You can retrieve this date easily with the following ready-to-use function:
function LastAccess(const filename: string): string;
var
FileHandle: THandle;
LocalFileTime: TFileTime;
DosFileTime: DWORD;
LastAccessedTime: TDateTime;
FindData: TWin32FindData;
begin
Result := ''; { never :-) }
FileHandle := FindFirstFile(filename, FindData);
if FileHandle <> INVALID_HANDLE_VALUE then
begin
Windows.FindClose(Handle);
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
FileTimeToDosDateTime(LocalFileTime,
LongRec(DosFileTime).Hi, LongRec(DosFileTime).Lo);
LastAccessedTime := FileDateToDateTime(DosFileTime);
Result := DateTimeToStr(LastAccessedTime);
end;
end;
end;
2004. május 16., vasárnap
Set the resolution of your screen
Problem/Question/Abstract:
This article shows how to set the resolution of your screen I pasted my whole unit below.
Answer:
unit Unit4;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,
ExtCtrls, Buttons;
type
TForm4 = class(TForm)
ComboBox1: TComboBox;
BitBtn1: TBitBtn;
Bevel1: TBevel;
procedure ComboBox1Change(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure Label4Click(Sender: TObject);
procedure Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure Button1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure BitBtn1Click(Sender: TObject);
procedure BitBtn1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure BitBtn1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
Modes: array[0..255] of TDevMode;
implementation
uses cliprex2;
{$R *.DFM}
procedure TForm4.ComboBox1Change(Sender: TObject);
begin
bitbtn1.Enabled := combobox1.ItemIndex >= 0;
bitbtn1.enabled := true;
end;
procedure TForm4.FormCreate(Sender: TObject);
var
DC: THandle;
Bits: Integer;
HRes: Integer;
VRes: Integer;
DM: TDevMode;
ModeNum: LongInt;
Ok: Bool;
I: Byte;
begin
DC := Canvas.Handle;
Bits := GetDeviceCaps(DC, BITSPIXEL);
HRes := GetDeviceCaps(DC, HORZRES);
VRes := GetDeviceCaps(DC, VERTRES);
ModeNum := 0;
EnumDisplaySettings(nil, ModeNum, DM);
Modes[ModeNum] := DM;
Ok := True;
while Ok do
begin
Inc(ModeNum);
Ok := EnumDisplaySettings(nil, ModeNum, DM);
Modes[ModeNum] := DM;
end;
for I := 0 to ModeNum - 1 do
begin
ComboBox1.Items.Add(Format('%d x %d, %d bits',
[TDevMode(Modes[I]).dmPelsWidth,
TDevMode(Modes[I]).dmPelsHeight,
TDevMode(Modes[I]).dmBitsPerPel]));
ComboBox1.ItemIndex := 0;
end;
end;
procedure TForm4.FormActivate(Sender: TObject);
var
DC: THandle;
Bits: Integer;
HRes: Integer;
VRes: Integer;
begin
DC := Canvas.Handle;
Bits := GetDeviceCaps(DC, BITSPIXEL);
HRes := GetDeviceCaps(DC, HORZRES);
VRes := GetDeviceCaps(DC, VERTRES);
combobox1.text := Format('%d x %d, %d bits', [HRes, VRes, Bits]);
bitbtn1.enabled := false;
end;
procedure TForm4.Label4Click(Sender: TObject);
begin
form4.hide;
end;
procedure TForm4.Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
bitbtn1.Font.Color := clblue;
end;
procedure TForm4.Button1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
bitbtn1.Font.Color := clblack;
end;
procedure TForm4.BitBtn1Click(Sender: TObject);
var
NewMode: TDevMode;
ChResult: LongInt;
begin
NewMode := TDevMode(Modes[ComboBox1.ItemIndex]);
NewMode.dmDisplayFrequency := 0;
NewMode.dmDisplayFlags :=
DM_BITSPERPEL and
DM_PELSWIDTH and
DM_PELSHEIGHT and
DM_DISPLAYFLAGS;
ChResult := ChangeDisplaySettings(NewMode, CDS_UPDATEREGISTRY);
form4.hide;
end;
procedure TForm4.BitBtn1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
bitbtn1.font.color := clblue;
end;
procedure TForm4.BitBtn1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
bitbtn1.font.color := clblack;
end;
end.
2004. május 15., szombat
How to extract icons from a program or DLL
Problem/Question/Abstract:
How can I extract an icon from another executable or DLL through code?
Answer:
Use the Windows API function ExtractIcon(), passing it the instance handle of your application, the path name of the application you wish to extract the icon from, and the number of the icon you wish to extract:
var
TheIcon: TIcon;
begin
TheIcon := TIcon.Create;
TheIcon.Handle := ExtractIcon(hInstance, 'C:\PATH\SOMEPROG.EXE', 0);
{Do something with the icon}
TheIcon.Free;
end;
2004. május 14., péntek
Create a polygon-shaped form using regions
Problem/Question/Abstract:
How to create a polygon-shaped form using regions
Answer:
To start with, we need to make an array of points of all corners of the form (there can be as many as you want). Next we use the Windows API call CreatePolygonRgn to get a handle to the region we have just defined. Finally we need to set this region the window we want to be that shape using another API call SetWindowRgn. To see this in effect create a new project and in the forms onCreate event have:
procedure TForm1.FormCreate(Sender: TObject);
var
Region: HRgn;
Points: array[0..11] of TPoint;
begin
{Define the points of a W shape}
Points[0] := Point(0, 0);
Points[1] := Point(50, 0);
Points[2] := Point(180, 200);
Points[3] := Point(218, 100);
Points[4] := Point(256, 200);
Points[5] := Point(385, 0);
Points[6] := Point(435, 0);
Points[7] := Point(256, 300);
Points[8] := Point(218, 200);
Points[9] := Point(180, 300);
{Define the region}
Region := CreatePolygonRgn(Points, 10, ALTERNATE);
{Set the window to have the above defined region}
SetWindowRgn(Handle, Region, True);
end;
2004. május 13., csütörtök
How to implement a 'Lasso'
Problem/Question/Abstract:
How to implement a 'Lasso'
Answer:
Here's a possible approach:
1. In the OnMouseDown event for the form that you are 'lasso-ing' controls on:
bMarquee := True;
{set a boolean so that you can differentiate between decisions that might have to be made during other mouse events}
ptOrigin := Point(X, Y); { get the starting point of the marquee }
ptMove := Point(X, Y); { initialize the stopping point }
Set the pen and brush attributes here or by calling a common procedure that can be reused elsewhere in the Unit.
Pen.Color := clBlack;
Pen.Width := 1;
Pen.Style := psDash;
Brush.Style := bsClear;
Then draw the marquee rectangle
DrawMarquee(ptOrigin, ptMove, pmNotXor);
2. In the OnMouseMove event for the form...
if bMarquee = True then
begin
DrawMarquee(ptOrigin, ptMove, pmNotXor);
DrawMarquee(ptOrigin, Point(X, Y), pmNotXor);
ptMove := Point(X, Y);
Canvas.Pen.Mode := pmCopy;
end;
3. In the OnMouseUp event for the form...
if bMarquee = True then
begin
bMarquee := False;
DrawMarquee(ptOrigin, Point(X, Y), pmNotXor);
ptMove := Point(X, Y);
{check for any intersections between the marquee frame and controls}
{ call the procedure that will highlight ( focus ) the desired controls}
end;
The DrawMarquee procedure...
procedure myForm.DrawMarquee(mStart, mStop: TPoint; AMode: TPenMode);
begin
Canvas.Pen.Mode := AMode;
Canvas.Rectangle(mStart.X, mStart.Y, mStop.X, mStop.Y);
end;
2004. május 12., szerda
Get File Created, Modified and Accessed dates
Problem/Question/Abstract:
How to get File Created, Modified and Accessed dates
Answer:
This function will return Created,Modified and Accessed datetimes of a given file. The datetimes are returned as TDateTime variables passed by REFERENCE. The function returns true if the file was found, else false. The dates are the same as displayed by EXPLORER when file properties is selected
// ================================================================
// Return the three dates (Created,Modified,Accessed
// of a given filename. Returns FALSE if file cannot
// be found or permissions denied. Results are returned
// in TdateTime OUT parameters
// ================================================================
function GetFileTimes(FileName: string;
out Created: TDateTime;
out Modified: TDateTime;
out Accessed: TDateTime): boolean;
var
FileHandle: integer;
Cmd: boolean;
FTimeC, FTimeA, FTimeM: TFileTime;
LTime: TFileTime;
STime: TSystemTime;
begin
FileHandle := FileOpen(FileName, fmShareDenyNone);
Created := 0.0;
Modified := 0.0;
Accessed := 0.0;
if FileHandle < 0 then
Cmd := false
else
begin
Cmd := true;
GetFileTime(FileHandle, @FTimeC, @FTimeA, @FTimeM);
FileClose(FileHandle);
// Created
FileTimeToLocalFileTime(FTimeC, LTime);
if FileTimeToSystemTime(LTime, STime) then
begin
Created := EncodeDate(STime.wYear, STime.wMonth, STime.wDay);
Created := Created + EncodeTime(STime.wHour, STime.wMinute, STime.wSecond,
STime.wMilliSeconds);
end;
// Accessed
FileTimeToLocalFileTime(FTimeA, LTime);
if FileTimeToSystemTime(LTime, STime) then
begin
Accessed := EncodeDate(STime.wYear, STime.wMonth, STime.wDay);
Accessed := Accessed + EncodeTime(STime.wHour, STime.wMinute, STime.wSecond,
STime.wMilliSeconds);
end;
// Modified
FileTimeToLocalFileTime(FTimeM, LTime);
if FileTimeToSystemTime(LTime, STime) then
begin
Modified := EncodeDate(STime.wYear, STime.wMonth, STime.wDay);
Modified := Modified + EncodeTime(STime.wHour, STime.wMinute, STime.wSecond,
STime.wMilliSeconds);
end;
end;
Result := Cmd;
end;
2004. május 11., kedd
Filters for 256 color greyscale images
Problem/Question/Abstract:
Filters for 256 color greyscale images
Answer:
There are a lot of different filters that belong in different algorithm methods . A few things that are important to all methods:
Images must be 256 gray levels (I did not test them with color images)
We assume that image has the function RC (x, y) where x,y are the position of every pixel
0 < = X < = image_width - 1 and 0 < = Y < = Image_height - 1
Convolution filters
This is the most used method (also known as "moving window filters" and maybe you have already used it. For each pixel in range (1,1),(width-1,height-1) we calculate its new value using the following algorithm:
for j = 1 to height - 1
for i = 1 to width - 1
newcolor = a1 * RC(i, j - 1) + a2 * RC(i, j - 1) + a3 * RC(i + 1, j - 1) + b1 * RC(i - 1, j) + b2 * RC(i, j) + b3 * RC(i + 1, j) + c1 * RC(i - 1, j + 1) + c2 * RC(i, j + 1) + c3 * RC(i + 1, j + 1);
newcolor = newcolor / kl;
newcolor = abs(newcolor);
if (newcolor > 255)newcolor = 255; {not greater than 255}
{do whatever you want here eg put new pixel color in a buffer}
end i
end j
Unfortunately in this method we have strange results in first & last row & column. That's why we start from row-column 1 (which is the second ) and we stop 1 row & column before end.
Here follow the names of the filters that belong into this method and the parameters a1..c3, kl that be used :
LOW_PASSn are noise removal filters. Images are getting smoother.
LAPLACE_ORIGINAL is Edge ehnancement filter.
LAPLACE is a special effect filter (looks like you type the image in abnormal paper)
LAPLACE_EDGE is Edge detection filter.
FOCUS is a sharpen filter (looks like you have changed the focus of the camera when
snapping the picture)
case LOW_PASS1: {
kl=9;
a1=1; a2=1; a3=1;
b1=1; b2=1; b3=1;
c1=1; c2=1; c3=1; break;
}
case LOW_PASS2: {
kl=10;
a1=1; a2=1; a3=1;
b1=1; b2=2; b3=1;
c1=1; c2=1; c3=1; break;
}
case LOW_PASS3: {
kl=16;
a1=1; a2=2; a3=1;
b1=2; b2=4; b3=2;
c1=1; c2=2; c3=1; break;
}
case LOW_PASS4: {
kl=5;
a1=0; a2=1; a3=0;
b1=1; b2=1; b3=1;
c1=0; c2=1; c3=0; break;
}
case LAPLACE_ORIGINAL: {
kl=1;
a1=-1; a2=-1; a3=-1;
b1=-1; b2=9; b3=-1;
c1=-1; c2=-1; c3=-1; break;
}
case LAPLACE: {
kl=1;
a1=1; a2=-2; a3=1;
b1=-2; b2=5; b3=-2;
c1=1; c2=-2; c3=1; break;
}
case LAPLACE_EDGE: {
kl=1;
a1=-1; a2=-1; a3=-1;
b1=-1; b2=8; b3=-1;
c1=-1; c2=-1; c3=-1; break;
}
case FOCUS: {
kl=1;
a1= 0; a2=-1; a3= 0;
b1=-1; b2= 5; b3=-1;
c1= 0; c2=-1; c3= 0; break;
}
Relief filter
Maxcolor is the maximum greyscale of the image. This is a very fancy filter. All new pixels have values near the (maxcolor/2) and for better results in viewing it is good to create a histogram equalization for the new image.
for j = 0 to height - 1
for i = 0 to width - 1
newcolor = RC(i, j) + ((maxcolor / 2) - RC(i - 2, j - 2));
newcolor = abs(newcolor); {hate negative values !!!}
if (newcolor > 255)newcolor = 255; {not greater than 255}
{do whatever you want here eg put new pixel color in a buffer}
end i
end j
2004. május 10., hétfő
Panel showing Enabled/Disabled in Children
Problem/Question/Abstract:
Often you disable all Controls within a Panel by simply setting the Enabled Property of the Panel. It works, however the user does not get any visual feedback.
Answer:
The following component code simply extends the Delphi Panel to properly show the Enabled State (True/False) within its children.
Extending the control is very simple. All we need to do is to override and extend the default SetEnabled procedure. The new procedure will first call the original version and then rotate through all children and copy the state.
There is one drawback although, if there is a disabled control (XYZ) on the panel, you then disable the panel and enbale it again, the control (XYZ) will be enabled, too.
Anyway, often it is very useful. Here you go:
unit uRealPanel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TRealPanel = class(TPanel)
private
protected
procedure SetEnabled(Value: Boolean); override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('gate(n)etwork', [TRealPanel]);
end;
{ TRealPanel }
procedure TRealPanel.SetEnabled(Value: Boolean);
var
I: Integer;
begin
inherited;
if csDesigning in ComponentState then
Exit;
for I := 0 to Pred(ControlCount) do
if Controls[I] is TWinControl then
(Controls[I] as TWinControl).Enabled := Value;
end;
end.
2004. május 9., vasárnap
How to search for a pattern in a file
Problem/Question/Abstract:
I need to locate a pattern in a file (both text and binary) - just like the Pos function does with the strings. Preferably, it should deal with FileStream. Straightforward solution first seemed kind of expensive - that is to just plainly go through the stream comparing patterns on every step.
Answer:
Solve 1:
You can do it that way but it is much faster to load chunks of data into a sizeable buffer and do the search in the buffer. Here is an example:
function ScanFile(const filename: string; const forString: string; caseSensitive:
Boolean): LongInt;
{ returns position of string in file or -1, if not found }
const
BufferSize = $8001; { 32K + 1 bytes }
var
pBuf, pEnd, pScan, pPos: Pchar;
filesize: LongInt;
bytesRemaining: LongInt;
bytesToRead: Word;
F: file;
SearchFor: Pchar;
oldMode: Word;
begin
Result := -1; { assume failure }
if (Length(forString) = 0) or (Length(filename) = 0) then
Exit;
SearchFor := nil;
pBuf := nil;
{ open file as binary, 1 byte recordsize }
AssignFile(F, filename);
oldMode := FileMode;
FileMode := 0; { read-only access }
Reset(F, 1);
FileMode := oldMode;
try { allocate memory for buffer and pchar search string }
SearchFor := StrAlloc(Length(forString) + 1);
StrPCopy(SearchFor, forString);
if not caseSensitive then { convert to upper case }
AnsiUpper(SearchFor);
GetMem(pBuf, BufferSize);
filesize := System.Filesize(F);
bytesRemaining := filesize;
pPos := nil;
while bytesRemaining > 0 do
begin
{ calc how many bytes to read this round }
if bytesRemaining >= BufferSize then
bytesToRead := Pred(BufferSize)
else
bytesToRead := bytesRemaining;
{ read a buffer full and zero-terminate the buffer }
BlockRead(F, pBuf^, bytesToRead, bytesToRead);
pEnd := @pBuf[bytesToRead];
pEnd^ := #0;
{ scan the buffer. Problem: buffer may contain #0 chars! So we
treat it as a concatenation of zero-terminated strings. }
pScan := pBuf;
while pScan < pEnd do
begin
if not caseSensitive then { convert to upper case }
AnsiUpper(pScan);
pPos := StrPos(pScan, SearchFor); { search for substring }
if pPos <> nil then
begin { Found it! }
Result := FileSize - bytesRemaining + LongInt(pPos) - LongInt(pBuf);
Break;
end;
pScan := StrEnd(pScan);
Inc(pScan);
end;
if pPos <> nil then
Break;
bytesRemaining := bytesRemaining - bytesToRead;
if bytesRemaining > 0 then
begin
{ no luck in this buffers load. We need to handle the case of the
search string spanning two chunks of file now. We simply go back a bit in
the file and read from there, thus inspecting some characters twice }
Seek(F, FilePos(F) - Length(forString));
bytesRemaining := bytesRemaining + Length(forString);
end;
end;
finally
CloseFile(F);
if SearchFor <> nil then
StrDispose(SearchFor);
if pBuf <> nil then
FreeMem(pBuf, BufferSize);
end;
end;
Solve 2:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
hFile: THandle;
hFileMapObj: THandle;
pSharedBuf: Pointer;
Time0: Integer;
p: PChar;
begin
if not OpenDialog1.Execute then
Exit;
s := InputBox('Find', 'Match', '');
Time0 := GetTickCount;
hfile := 0;
hFileMapObj := 0;
pSharedBuf := nil;
try
hFile := FileOpen(OpenDialog1.FileName, fmOpenRead);
Win32Check(hFileMapObj <> INVALID_HANDLE_VALUE);
hFileMapObj := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);
Win32Check(hFileMapObj <> 0);
pSharedBuf := MapViewOfFile(hFileMapObj, FILE_MAP_READ, 0, 0, 0);
Win32Check(pSharedBuf <> nil);
P := StrPos(PChar(pSharedBuf), PChar(s));
finally
if pSharedBuf <> nil then
UnMapViewOfFile(pSharedBuf);
if hFileMapObj <> 0 then
CloseHandle(hFileMapObj);
if hFile <> 0 then
CloseHandle(hFile);
end;
if P = nil then
Caption := Format('Not found, ticks=%d', [GetTickCount - Time0])
else
Caption := Format('Found it at pos %d, ticks=%d', [Integer(P - PChar(pSharedBuf)),
GetTickCount - Time0]);
end;
2004. május 8., szombat
How to centre a MessageBox on a form
Problem/Question/Abstract:
How to centre a MessageBox on a form
Answer:
{ ... }
msgCaption: PChar; {var to hold caption}
{ ... }
procedure pmChangeMessageBox(var Msg: TMessage); message WM_USER + 1024;
procedure TForm1.pmChangeMessageBox(var Msg: TMessage);
var
MBHwnd: THandle;
MBRect: TRect;
x, y, w, h: integer;
begin
MBHwnd := FindWindow(MAKEINTRESOURCE(WC_DIALOG), msgCaption);
if (MBHwnd <> 0) then
begin
GetWindowRect(MBHWnd, MBRect);
w := MBRect.Right - MBRect.Left;
h := MBRect.Bottom - MBRect.Top;
{center horizontal}
x := Form1.Left + ((Form1.Width - w) div 2);
{keep on screen}
if x < 0 then
x := 0
else if x + w > Screen.Width then
x := Screen.Width - w;
{center vertical}
y := Form1.Top + ((Form1.Height - h) div 2);
{keep on screen}
if y < 0 then
y := 0
else if y + h > Screen.Height then
y := Screen.Height - h;
SetWindowPos(MBHWnd, 0, x, y, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or
SWP_NOZORDER);
end;
end;
Example use:
PostMessage(Handle, WM_USER + 1024, 0, 0);
msgCaption := 'Confirm';
MessageBox(Handle, 'Save changes?', msgCaption, MB_ICONQUESTION or MB_YESNOCANCEL);
2004. május 7., péntek
How to select all rows in a TDBGrid programmatically
Problem/Question/Abstract:
Can someone tell me how I can select all rows in a DBGrid in code by clicking a button?
Answer:
Something like this should work (untested):
procedure SelectAllinGrid(grid: TDBGrid);
var
saveBK: TBookmark;
i: integer;
begin
with grid.DataSource.Dataset do
begin
for i := 0 to SelectedList.Count - 1 do
FreeBookmark(SelectedList.Items[i]);
SelectedList.Clear;
saveBK := GetBookmark; { Save current record position }
DisableControls;
try
First;
while (not Eof) do
begin
SelectedList.Add(GetBookmark);
Next;
end;
finally
GotoBookmark(saveBK); { Restore original record position}
Freebookmark(saveBK);
EnableControls;
end;
end
end;
2004. május 6., csütörtök
Implement a Win32 look and feel "Browse for Folder" directory picker
Problem/Question/Abstract:
I'm looking for code that will let me implement a Win32 look and feel "Browse for Folder" directory picker. Like the one used in Project Options - > Directories/ Conditionals interface.
Answer:
procedure TMainForm.BrowseFolderActionExecute(Sender: TObject);
var
pidl, pidlSelected: PItemIDList;
bi: TBrowseInfo;
szDirName: array[0..260] of AnsiChar;
begin
{Get the root PIDL of the network neighborhood tree}
if SHGetSpecialFolderLocation(Handle, CSIDL_DESKTOP, pidl) = NOERROR then
begin
{Populate a BROWSEINFO structure}
bi.hwndOwner := Handle;
bi.pidlRoot := pidl;
bi.pszDisplayName := szDirName;
bi.lpszTitle := 'Select directory';
bi.ulFlags := BIF_RETURNONLYFSDIRS;
bi.lpfn := nil;
bi.lParam := 0;
bi.iImage := -1;
{Display the "Browse For Folder" dialog box}
pidlSelected := SHBrowseForFolder(bi);
{NULL indicates that Cancel was selected from the dialog box}
if pidlSelected < > nil then
begin
SHGetPathFromIDList(pidlSelected, szDirName);
ShowMessage(szDirName);
{Release the PIDL of the computer name}
CoTaskMemFree(pidlSelected);
end;
{Release the PIDL of the network neighborhood tree}
CoTaskMemFree(pidl);
end;
end;
2004. május 5., szerda
Code Insight
Problem/Question/Abstract:
Code Insight
Answer:
Here's a list of the new features in Delphi 3 which are collected under the name 'Code Insight':
Code Insight
Shortcut
Cancel Key
Extra
Code Completion
Ctrl Space
Esc
Can be sorted using right click in popup list.
Incremental search, up and down arrow, home and end keys for navigation.
Respects the visibility of the declared members of the class.
Code Parameters
Ctrl-Shift-Space
Esc
Argument Value List
Ctrl Space
Can be sorted using right click in popup list.
Must be specifically requested.
Displays constants, functions, and variables that are consistent with the argument required by the expression.
Code Templates
Ctrl J
if you type the short cut for the template and then Ctrl J you can skip the popup window.
The Code templates are stored in a ASCII file delphi32.dci which is stored in the Delphi\Bin Folder if you want to enter your templates in manually.
2004. május 4., kedd
How to load a DLL from a resource file and save it to disk
Problem/Question/Abstract:
Is it possible to copy a DLL into my own executable using the IDE only for exporting it after in my program? For example, I create an executable Test.exe and in the IDE I want to attach a resource that contains the DLL. And when my program is running I can export this resource to put a file on my hard disk. Is there a way to do that?
Answer:
Create a file called "mydllres.rc". Edit it and insert the line MYDLL RT_RCDATA "Mydll.dll". Then call the Borland compiler for resouces (you may need to adjust the paths) with BRCC32 mydllres.rc .
This will produce a output file called mydllres.res. Under the implementation header in the main unit add the following: {$R mydllres.res}. This will add the resource to the project.
The following procedure will save it to a file:
procedure savedll;
var
myres: TResourceStream;
begin
myres := TResourceStream.Create(hInstance, PChar('MYDLL'), RT_RCDATA);
myres.SaveToFile(ExtractFilepath(Application.exename) + 'mydll.dll');
myres.destroy;
end;
2004. május 3., hétfő
Add Interfaces to a List
Problem/Question/Abstract:
It's more efficient to control Interfaces in a List and ask with QueryInterface() which objects support an Interface
Answer:
First we need some Interfaces (the same goes also in Kylix, pure Interfaces are independent from COM, it's a feature of ObjectPascal):
type
IKiss = interface(IUnknown)
['{19A231B1-269F-45A2-85F1-6D8A629CC53F}']
procedure kiss; stdcall;
end;
ISpeak = interface(IUnknown)
['{B7F6F015-88A6-47AC-9176-87B6E313962D}']
procedure sayHello; stdcall;
end;
Second the interfaces must be implemented:
TDog = class(TInterfacedObject, ISpeak)
public
procedure sayHello; stdcall;
end;
TFrench = class(TInterfacedObject, ISpeak, IKiss)
public
procedure kiss; stdcall;
procedure sayHello; stdcall;
end;
TEnglish = class(TInterfacedObject, ISpeak)
public
procedure sayHello; stdcall;
end;
e.g. the dog with
procedure TDog.sayHello;
begin
showmessage('dog is barking wauwau');
end;
Now we add the instances of the interface in the list, using the defined type TInterfaceList so we are able to ask with QueryInterface if an object supports an Interface, in our example if a dog as an object can kiss or just sayhello:
procedure TForm1.btnCollectClick(Sender: TObject);
var
collection: TInterfaceList;
i: Integer;
aObjspeak: ISpeak;
aObjKiss: IKiss;
begin
collection := TinterfaceList.create;
try
with collection do
begin
add(TEnglish.create);
add(TFrench.create);
add(TDog.create);
end;
for i := 0 to collection.count - 1 do
begin
aObjSpeak := collection[i] as ISpeak; //TFrench, TEnglish, TDog
if aObjSpeak <> nil then
aObjSpeak.sayHello;
collection[i].queryInterface(IKiss, aObjKiss); //only TFrench
if aObjKiss <> nil then
aObjKiss.kiss;
end;
finally
collection.free;
end;
end;
2004. május 2., vasárnap
RGB and HSV conversions
Problem/Question/Abstract:
Sometimes it is best to deal with colors as HSV rather than RGB. The artical explains a little bit what HSV is and includes source for converting between the two.
Answer:
HSV is Hue, Saturation, and Value.
HSV:
Hard to explain without a picture so you will have to use your imagination...
Hue:
Draw a circle in your head, the circle is 0 to 360 degrees (or 359 :-) ). On the outer edge of the circle, place a red dot at 0 degrees, a green dot at 120 degrees and a blue dot at 240 degrees. Those are the main points. The other points between these 3 colors are interpolated... for example yellow is between red and green at 60 degrees (equal red + green = yellow), cyan is between green and blue at 180 degrees, magenta is between blue and red at 300 degrees. Then between yellow and red is another and you keep breaking it down until your circle is full. The outer edge from 0 to 360 degrees is the hue.
Saturation:
The center of the circle is white. The color blends with the other colors to white as you go from the outside of the circle to the center. The outer egde is saturation of 1 and the center is 0 (white).
Value:
Value is simply the intensity of the color.
You already know RGB I assume since you are a programmer.
I played around with my digital camera and took a picture of my brown computer chair. I created an algorithm that turned my chair green. It was easy with HSV, I simply used photoshop to see what the hue was of the chair. Then I rotated the hue so that the chair was in the green range. Boom, the chair was green.
Here they are:
http://www.eggcentric.com/eimages/greenchair.jpg
http://www.eggcentric.com/eimages/brownchair.jpg
Here is the source of procedures to convert between RGB and HSV and back again. You can also download it from the link.
unit RGBHSV;
{
William Egge, public@eggcentric.com
http://www.eggcentric.com
This unit converts between RGB and HSV color models.
procedure HSVToRGB(const H, S, V: Single; out R, G, B: Single);
in
H = Hue. Range is from 0..1. 0.5 = 180 degrees, 1 = 360. or H < 0 for gray
S = Satration. Range is 0..1 where 0 is white and 1 is no saturation.
V = Value. Range is 0..255
out
R = 0..255
G = 0..255
B = 0..255
If H < 0 then the result is a gray value R=V, G=V, B=V
procedure RGBToHSV(const R, G, B: Single; out H, S, V: Single);
in
R = 0..255
G = 0..255
B = 0..255
out
H = Hue. -1 for grey scale or range 0..1. 0..1 represents 0..360 degrees
S = Saturation. Range = 0..1. 0 = white, 1 = no saturation.
V = Value or intensity. Range 0..255
}
interface
uses
Math;
procedure HSVToRGB(const H, S, V: Single; out R, G, B: Single);
procedure RGBToHSV(const R, G, B: Single; out H, S, V: Single);
implementation
procedure HSVToRGB(const H, S, V: Single; out R, G, B: Single);
const
SectionSize = 60 / 360;
var
Section: Single;
SectionIndex: Integer;
f: single;
p, q, t: Single;
begin
if H < 0 then
begin
R := V;
G := R;
B := R;
end
else
begin
Section := H / SectionSize;
SectionIndex := Floor(Section);
f := Section - SectionIndex;
p := V * (1 - S);
q := V * (1 - S * f);
t := V * (1 - S * (1 - f));
case SectionIndex of
0:
begin
R := V;
G := t;
B := p;
end;
1:
begin
R := q;
G := V;
B := p;
end;
2:
begin
R := p;
G := V;
B := t;
end;
3:
begin
R := p;
G := q;
B := V;
end;
4:
begin
R := t;
G := p;
B := V;
end;
else
R := V;
G := p;
B := q;
end;
end;
end;
procedure RGBToHSV(const R, G, B: Single; out H, S, V: Single);
var
RGB: array[0..2] of Single;
MinIndex, MaxIndex: Integer;
Range: Single;
begin
RGB[0] := R;
RGB[1] := G;
RGB[2] := B;
MinIndex := 0;
if G < R then
MinIndex := 1;
if B < RGB[MinIndex] then
MinIndex := 2;
MaxIndex := 0;
if G > R then
MaxIndex := 1;
if B > RGB[MaxIndex] then
MaxIndex := 2;
Range := RGB[MaxIndex] - RGB[MinIndex];
// Check for a gray level
if Range = 0 then
begin
H := -1; // Can't determine on greys, so set to -1
S := 0; // Gray is at the center;
V := R; // could choose R, G, or B because they are all the same value.
end
else
begin
case MaxIndex of
0: H := (G - B) / Range;
1: H := 2 + (B - R) / Range;
2: H := 4 + (R - G) / Range;
end;
S := Range / RGB[MaxIndex];
V := RGB[MaxIndex];
H := H * (1 / 6);
if H < 0 then
H := 1 + H;
end;
end;
end.
Component Download: http://www.eggcentric.com/download/rgbhsv.zip
2004. május 1., szombat
Insert text at bookmark positions of a Word document
Problem/Question/Abstract:
How to insert text at bookmark positions of a Word document
Answer:
procedure WordBookInsert(v: OleVariant; sgoto, sdata: string);
begin
try
{make sure we have passed a word.application level variant}
if not varisempty(v) then
begin
V.Selection.goto(What := wdGoToBookmark, Name := sgoto);
V.Selection.TypeText(Text := SDATA);
end;
except
{trap OLE errors and display message if it fails}
on E: sysutils.exception do
begin
showmessage(e.message);
end;
end;
end;
Feliratkozás:
Bejegyzések (Atom)