2004. október 6., szerda
First/Last changed File in Folder
Problem/Question/Abstract:
Sometimes its nessessary to know which File of an Folder was changed at the last.
Answer:
Sometimes its nessessary to know which File of an Folder was changed at the last. I wrote this function below. It can give you the File with the oldesd content too, if you set the parameter first := True. For the comparing I've used the API - Function CompareFileTime. If you wants to know the oldes or youngest created file or the last/first accessed file see the comments in the function.
function GetLastOrFirstChangedFileOfFolder(First: Boolean; Folder: string): string;
var
Ft1, Ft2: TFileTime;
sr: TSearchrec;
what, Res: Integer;
begin
if not DirectoryExists(Folder) then
exit;
if Folder[Length(Folder)] <> '\' then
Folder := Folder + '\';
if First then
What := 1
else
What := -1;
res := FindFirst(Folder + '*.*', faAnyFile, sr);
ft1 := sr.FindData.ftLastWriteTime;
//ft1 := sr.FindData.ftCreationTime; for the first/last created
//ft1 := sr.FindData.ftLastAccessTime; for the first/last access
Result := sr.Name;
while res = 0 do
begin
Ft2 := sr.FindData.ftLastWriteTime;
//ft1 := sr.FindData.ftCreationTime; for the first/last createt
//ft1 := sr.FindData.ftLastAccessTime; for the first/last access
if CompareFileTime(ft1, ft2) = What then
begin
ft1 := Ft2;
Result := sr.Name;
end;
res := FindNext(sr);
end;
FindClose(sr);
end;
2004. október 5., kedd
How to convert extended characters into their HTML character entities
Problem/Question/Abstract:
I just need a routine that scans an ordinary string and the replaces all occurrences of '<', '&' and all other illegal characters by the correct HTML symbol.
Answer:
In Delphi 7 (HTTPApp.pas) you have:
function HTMLEncode(const AStr: string): string;
const
Convert = ['&', '<', '>', '"'];
var
Sp, Rp: PChar;
begin
SetLength(Result, Length(AStr) * 10);
Sp := PChar(AStr);
Rp := PChar(Result);
while Sp^ <> #0 do
begin
case Sp^ of
'&':
begin
FormatBuf(Rp^, 5, '&', 5, []);
Inc(Rp, 4);
end;
'<', '>':
begin
if Sp^ = '<' then
FormatBuf(Rp^, 4, '<', 4, [])
else
FormatBuf(Rp^, 4, '>', 4, []);
Inc(Rp, 3);
end;
'"':
begin
FormatBuf(Rp^, 6, '"', 6, []);
Inc(Rp, 5);
end;
else
Rp^ := Sp^
end;
Inc(Rp);
Inc(Sp);
end;
SetLength(Result, Rp - PChar(Result));
end;
which is pretty good. It will use quite a bit of memory on long input strings, though. For some reason it sets the result buffer to 10 times the length of the input buffer when 6 times would have been enough (for the worst case, all quote chars (").
I rolled my own before D7 was released (part of my WOS framework - found on the D7 Companion CD):
function HTMLEncode(const S: string): string;
const
ConversionSet = ['&', '<', '>', '"', '+']; {The '+' is because of a IE bug}
ConversionChars: PChar = '&<>"+';
Entities: array[1..5] of string = ('&', '<', '>', '"', '+');
var
Sp, Rp: PChar;
P: integer;
begin
SetLength(Result, Length(S) * 6); {Ouch... ( worst case is all "'s )}
Sp := PChar(S);
Rp := PChar(Result);
while Sp^ <> #0 do
begin
if not (Sp^ in ConversionSet) then
begin
Rp^ := Sp^;
Inc(Rp);
end
else
begin
P := StrScan(ConversionChars, Sp^) - ConversionChars + 1;
StrCopy(RP, PChar(Entities[P]));
Inc(Rp, Length(Entities[P]));
end;
Inc(Sp);
end;
SetLength(Result, Rp - PChar(Result));
end;
2004. október 4., hétfő
Send characters to another control (in any application)
Problem/Question/Abstract:
How can I e.g. "type" programmatically in another application?
Answer:
There are several methods to send keystrokes or characters to a WinControl. The SetKeyboardState requires the control to have the focus but can send more (esp. special) keys. The use of WM_CHAR message enables you to send characters even to a hidden control unless you have found out the handle of it (there are a couple of ways to find out a controls handle). Once you've got it, you can send messages to it.
I'm using this method for a little tool to "type" certain frequently used phrases while posting to newsgroups.
I hardcoded the handle of the edit control of my favorite newsreader and now I have a small and handy tool to type messages faster than ever.
procedure SendMsg(const h: HWND; const s: string);
var
i: integer;
begin
if h = 0 then
Exit;
if Length(s) = 0 then
Exit;
for i := 1 to Length(s) do
begin
if Ord(s[i]) in [9, 13, 32..254] then
SendMessage(h, WM_CHAR, Ord(s[i]), 0);
end;
end;
2004. október 3., vasárnap
Including Components into a StatusBar
Problem/Question/Abstract:
The TStatusbar usually does not allow to place components on itself. But sometimes it would be very fine to add -for example- a TProgressBar on a Statusbar.
This Article shows how to add components to a TStatusbar and how to fit it into one of the Statusbar-Panels.
Answer:
There are (at least) two ways to add Components on your Statusbar:
1. Create an own Statusbar-Object
Create your own Statusbar and allow to add components on it. This is possible in overriding the Create-Constructor:
type
TMyStatusBar = class(TStatusBar)
public
constructor Create(AOwner: TComponent); override;
end;
implementation
constructor TMyStatusBar.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csAcceptsControls];
//that’s all !!
end;
That’s all! Now this component accept other components as “Children” and you can put them at design-time onto the statusbar!
But I don’t like this way very much because you have to use this new component. I prefer to use the “old” components and manipulating them a little bit. So lets have a look to my favourite way:
2. “Adopt” the other component
The simplest way to include components to a statusbar is to adopt the component! Place the TStatusbar on your Form, also place the Progressbar (or other component you wish to include on your Statusbar) on the form (!). Then do this in the “OnShow” Event of the Form:
Progressbar1.Parent := statusbar1;
Progressbar1.top := 1;
Progressbar1.left := 1;
Now the Progressbar is “adopted” by the Statusbar.
But unfortunatley it doesn’t look very nice because the Progressbar is larger than the panel and the position is not correct. So we have to determine the exact position of the Progresbar by using the Statubar’s border, width and height. (We have to add this code to the “OnShow” Event of the form, because in the “OnCreate” event still no Handles are avalible.)
procedure TForm1.FormShow(Sender: TObject);
var
r: TRect;
begin
Statusbar1.perform(SB_GETRECT, 0, integer(@R));
//determine the size of panel 1
//SB_GETRECT needs Unit commctrl
// 0 = first Panel of Statusbar; 1 = the second and so on.
progressbar1.parent := Statusbar1; //adopt the Progressbar
progressbar1.top := r.top; //set size of
progressbar1.left := r.left; //Progressbar to
progressbar1.width := r.right - r.left; //fit with panel
progressbar1.height := r.bottom - r.top;
end;
Now the Progressbar fits exactly into the first panel of the statusbar! If you want to use the second or another panel, you only have to change the parameter of the “perform” command.
2004. október 2., szombat
Great Looking Transparent Forms, Strange Shaped !
Problem/Question/Abstract:
Using This Code, You Can Make Some Unique Looking Forms, Transparent, Multi-shaped and more !!
Answer:
////////////////////////////////////////////////////////////////////////
/////// TRIED ON WINDOWS 2000 PROFESSIONAL AND SERVER WITH DELPHI 5
/////// PLEASE DO NOT TRY TO DEFINE ANY VALUES< MAY CAUSE ERROS
////////////////////////////////////////////////////////////////////////
Hi Everybody,
in a late time in the evening, i started digging in this code, and here is the result, to try it and see the results your self, please do the following......
1- Start A New Project In Delphi 5
2- Make Sure JPEG Is In Your Windows Clause
3- Add These Lines
const
WS_EX_LAYERED = THE - SECRET - CODE - HERE - - -LOOK - COMMENT;
LWA_COLORKEY = 1; //1
LWA_ALPHA = 2; //2
type
TSetLayeredWindowAttributes = function(
hwnd: HWND; // handle to the layered window
crKey: TColor; // specifies the color key
bAlpha: byte; // value for the blend function
dwFlags: DWORD // action
): BOOL; stdcall;
You Saw (THE-SECRET-CODE-HERE---LOOK-COMMENT) Above A Minute Ago, In This Place Add Any Of The Following Values
Const
$81111 Form Caption Flipped
$82222 Form Visible But Can Never Get Focused
$83333 Form Caption Flipped And Never Gets Focus
$80FFF Form is (bsToolWindow), Never Maximized, Never Gets Focused
$FFFFF Form is (bsToolWindow), Form Flipped, Never Gets Focus, Never Maximizes
$222222 Very Strange Behaviour On Form Move, Try It !!
$222000 Form Looks As In Win 3.11 Applications
$333333 Captionless Window, Never Moves
#333000 Form Like In Win 3.11 Applications, Caption Flipped
$444444 The Whole Form With Everything On It Is Flipped
$444888 The Whole Form With Everything On It Is Flipped, It Is Converted To (bsToolWindow)
$555555 Form And All Contents Are Flipped, But Caption Still In Place ( Not Flipped )
$555888 Form And All Contents Are Flipped, But Caption Still In Place ( Not Flipped ), It Is
Converted To (bsToolWindow)
$666666 Form And All Contents Are Flipped, Win 3.11 GUI
$666888 Form And All Contents Are Flipped, Win 3.11 GUI, (bsTooWindow)
$777000 This Is Strange, Looks Like A 16bit Application, Everything Flipped, Caption In Place ***BUT***, Windows Accesses Your Form As If It Was Not A Part Of Your Application, I Mean, When Minimizing Application, You Will See One Entry In The Taskbar, When Restoring It, Every Form Will Have Its Own Entry, Well, It Makes An MDI Like Application !!
$777888 The Same But The MDI Form Will Have No Icon At All.
$888888 (bsToolWindows) Transparent Form
$888000 Normal Transparent Form
$888FFF (bsToolWindows) Transparent Form, Cant Get Focused
$8F8F8F (bsToolWindows) Transparent Form, MDI Alike
$999999 Transparent (bsToolWindow) Form, Caption Flipped
$999000 Transparent Form, Caption Flipped
$999FFF Transparent (bsToolWindow) Form, Caption Flipped, Never Focused
4- In Your FormCreate Event, Place This Code
var
Info: TOSVersionInfo;
F: TSetLayeredWindowAttributes;
begin
inherited;
Info.dwOSVersionInfoSize := SizeOf(Info);
GetVersionEx(Info);
if (Info.dwPlatformId = VER_PLATFORM_WIN32_NT) and
(Info.dwMajorVersion >= 5) then
begin
F := GetProcAddress(GetModulehandle(user32),
'SetLayeredWindowAttributes');
if Assigned(F) then
begin
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle,
GWL_EXSTYLE) or WS_EX_LAYERED);
F(Handle, 1, Round(255 * 75 / 100), LWA_ALPHA);
end;
end;
end;
2004. október 1., péntek
How to store an RTF text in a database blob field without using a TDBRichEdit
Problem/Question/Abstract:
How to store an RTF text in a database blob field without using a TDBRichEdit
Answer:
{ ... }
var
blobS: TBlobStream;
begin
blobS := TBlobStream.Create(TBlobField(table1.FieldByName('YourField')), bmWrite);
try
table1.Edit;
try
RichEdit1.Lines.SaveToStream(blobS);
table1.Post;
except
table1.Cancel;
raise;
end;
finally
blobS.Free;
end;
end;
2004. szeptember 30., csütörtök
Reconnecting to network shares with the help of a Component.
Problem/Question/Abstract:
Ever lost a networked share and didn't know how to connect to it? Well with this component you can search the network for a specific share containing a file or a directory and automatically reconnect to it.
Answer:
NOTE: IF YOU ALLREADY KNOW THE LOCATION OF THE SHARE YOU SHOULDN'T USE THIS COMPONENT AS IN LARGE NETWORKS WILL BE SLOW. THIS IS ONLY IF YOU DON'T KNOW THE EXACT LOCATION BUT CAN LOCATE IT BY USING A MARKER SUCH AS A SPECIFIC FILE OR FOLDER.
TIP: Use the BeforeConnect Event to specify whether a connection should be made.
unit Reconnect;
interface
uses
Windows, Messages, StdCtrls, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
FileCtrl;
type
TSIsType = (itDir, itIniFile, itApp, itOther);
TBeforeConnectEvent = procedure(Owner: TObject; AssignPath: string; var Accept:
boolean) of object;
TAfterConnectEvent = procedure(Owner: TObject; AssignedPath: string) of object;
TOnFail = procedure(Owner: TObject; FailMessage: string) of object;
TReconnect = class(TComponent)
private
{ Private declarations }
DidAssign: boolean;
FItemToLookFor: string;
FUserName: string;
FPassword: string;
FLetterToAssign: Char;
FIsType: TSIsType;
FOutputLabel: TLabel;
FFailMessage: string;
FBeforeConnect: TBeforeConnectEvent;
FAfterConnect: TAfterConnectEvent;
FOnFail: TOnFail;
function DoEnum(NetResT: PNetResourceA): integer;
function addbs(g: string): string; overload;
function addbs(g: string; SLASH: CHAR): string; overload;
function SearchFor(NetResT: NETRESOURCE; Path, param: string): boolean;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
function SearchAndAssign: boolean;
property ItemToLookFor: string read FItemToLookFor write FItemToLookFor;
property LetterToAssign: Char read FLetterToAssign write FLetterToAssign;
property IsType: TSIsType read FIsType write FIsType default itDir;
property OutputLabel: TLabel read FOutputLabel write FOutputLabel;
property UserName: string read FUserName write FUserName;
property Password: string read FPassword write FPassword;
property BeforeConnect: TBeforeConnectEvent read FBeforeConnect write
FBeforeConnect;
property AfterConnect: TAfterConnectEvent read FAfterConnect write FAfterConnect;
property OnFail: TOnFail read FOnFail write FOnFail;
end;
procedure Register;
implementation
function TReconnect.addbs(g: string; SLASH: CHAR): string;
begin
g := trim(g);
if g <> '' then
begin
if g[length(g)] <> SLASH then
result := g + SLASH
else
result := g;
end
else
result := g;
end;
function TReconnect.addbs(g: string): string;
begin
result := addbs(g, '\');
end;
function TReconnect.SearchFor(NetResT: NETRESOURCE; Path, param: string): boolean;
var
cont: boolean;
Exists: boolean;
begin
Exists := false;
path := addbs(path);
SearchFor := false;
if IsType = itDir then
Exists := directoryExists(path + param);
if IsType = itIniFile then
Exists := FileExists(path + param);
if IsType = itApp then
Exists := FileExists(path + param);
if IsType = itOther then
Exists := FileExists(path + param);
if Exists then
begin
cont := true;
try
if assigned(FBeforeConnect) then
BeforeConnect(self, path, cont);
except
showmessage('Failed to call BeforeConnect.');
end;
if cont then
begin
try
NetResT.lpLocalName := pchar(string(FLetterToAssign) + ':');
WNetAddConnection2A(NetResT, pchar(UserName), pchar(Password),
CONNECT_UPDATE_PROFILE);
DidAssign := true;
try
if assigned(FAfterConnect) then
AfterConnect(self, path);
except
showmessage('Failed to call AfterConnect.');
end;
except on E: Exception do
Showmessage(E.Message);
end;
SearchFor := true;
end;
end;
end;
function TReconnect.DoEnum(NetResT: PNetResourceA): integer;
var
EnumH: THandle;
cnt: cardinal;
buffsize: cardinal;
NetResBuf: array[0..200] of NETRESOURCE;
res: word;
i: integer;
begin
if DidAssign then
exit;
try
cnt := 255;
WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, NetResT, EnumH);
res := 0;
while (res = NO_ERROR) do
begin
buffsize := sizeof(NetResBuf);
res := WNetEnumResource(EnumH, cnt, @NetResBuf, buffsize);
for i := 0 to cnt - 1 do
begin
if Assigned(OutputLabel) then
begin
OutputLabel.Caption := NetResBuf[i].lpRemoteName;
OutputLabel.Refresh;
end;
if NetResBuf[i].dwDisplayType = RESOURCEDISPLAYTYPE_SHARE then
begin
if not DidAssign then
if SearchFor(NetResBuf[i], string(NetResBuf[i].lpRemoteName),
ItemToLookFor) then
begin
result := 0;
exit;
end;
end;
if (NetResBuf[i].dwScope = RESOURCEUSAGE_CONTAINER) then
doEnum(@NetResBuf[i]);
end;
end;
WNetCloseEnum(EnumH);
result := 1;
except on E: Exception do
begin
FFailMessage := E.Message;
if Assigned(FOnFail) then
OnFail(Owner, FFailMessage);
result := 0;
end;
end;
end;
function TReconnect.SearchAndAssign: boolean;
begin
DidAssign := false;
DoEnum(nil);
result := true;
end;
procedure Register;
begin
RegisterComponents('VNPVcls', [TReconnect]);
end;
end.
2004. szeptember 29., szerda
Parse the lines of a text file and import them into a Paradox table
Problem/Question/Abstract:
I have a text file with a certain format where only the first line is of type year and month. The rest is always the same: Integer, String, String, Integer, Integer, Integer. Example:
2001,10
000368,"The Name","Category",000671000,0724690,009421
000701,"The Name","Category",000398500,0398500,005181
What's the best way to import this into Paradox tables?
Answer:
Solve 1:
I would read it one line at a time and parse it with something like the following parser. The variable ofs needs to be set to zero to start the parsing at the beginning of the line.
{ ... }
ReadLn(f, line);
ofs := 0;
if GetNextSepValueOK(line, ofs, YrStr, ', ', '"') and
GetNextSepValueOK(line, ofs, MoStr, ', ', '"') then
{prep date}
else
raise Exception.Create('Cannot find year and month');
while not EOF(f) do
begin
ReadLn(f, line);
ofs := 0;
{Do Append and try, etc. }
while GetNextSepValueOK(line, ofs, value, ', ', '"') do
{Do Post}
end;
end;
{ ... }
function GetNextSepValueOK(const line: string; var ofs: integer; out value: string;
const Separator, Grouper: char): Boolean;
var
i, oc, lnb, GrouperCount: integer;
c: char;
temp: ShortString;
begin
oc := 0;
lnb := 0;
GrouperCount := 0;
i := ofs;
while (ofs < length(line)) do
begin
c := line[ofs + 1];
if not Odd(GrouperCount) and (c = Separator) then
break
else if c = Grouper then
begin
inc(GrouperCount);
if odd(GrouperCount) and (ofs > i) and (line[ofs] = Grouper) then
begin
inc(oc);
temp[oc] := Grouper;
end;
end
else if (c > ' ') or (lnb > 0) or odd(GrouperCount) then
begin
inc(oc);
temp[oc] := c;
end;
if (c > ' ') or odd(GrouperCount) then
lnb := oc;
inc(ofs);
end;
if (ofs < length(line)) and (line[ofs + 1] = Separator) then
begin
inc(ofs);
Result := true;
end
else
Result := (i < length(line)) and not Odd(GrouperCount);
if Result then
begin
temp[0] := char(lnb);
value := temp;
end;
end;
Solve 2:
procedure TForm1.ImportFile(const filename: string);
var
F: Textfile;
year, month: Integer;
line: string;
sl: Tstringlist;
begin
Assignfile(F, filename);
Reset(F);
try
ReadLn(F, line);
sl := TStringlist.Create;
try
sl.QuoteChar := '"';
sl.Commatext := line;
year := StrToInt(sl[0]);
month := StrToInt(sl[1]);
while not EOF(F) do
begin
Readln(line);
sl.Commatext := line;
SaveRecord(sl);
end;
finally
sl.free
end;
finally
Closefile(f)
end;
end;
The Saverecord method would be something like:
procedure Tform1.SaveRecord(sl: TStringlist);
begin
if sl.Count <> 6 then
raise Exception.Create('Invalid record');
table1.Append;
table1['ID'] := sl[0];
table2['Name'] := sl[1];
{ ... }
table1.Post;
end;
Solve 3:
You can use the CommaText property of a TStringList to parse the lines. Something like this:
procedure ReadFile(FileName: string);
var
F: TextFile;
S: string;
List: TStringList;
i: integer;
begin
AssignFile(F, FileName);
Reset(F);
List := TStringList.Create;
try
Readln(F, S);
List.CommaText := S;
{do whatever you want with first line}
while not EOF(F) do
begin
List.Clear;
ReadLn(F, S);
List.CommaText := S;
{List now contains the integers and strings as separate strings}
MyTable.Append;
for i := 0 to 5 do
MyTable.Fields[i].AsString := List.Strings[i];
MyTable.Post;
end;
finally
List.Free;
end;
closefile(f);
end;
2004. szeptember 28., kedd
Find files with FindFirst and FindNext
Problem/Question/Abstract:
Find files with FindFirst and FindNext
Answer:
The procedure FindFiles locates files (by a given "filemask") and adds their complete path to a stringlist. Note that recursion is used: FindFiles calls itself at the end of the procedure!
Before calling FindFiles, the stringlist has to be created; afterwards, you must free the stringlist.
In StartDir you pass the starting directory, including the disk drive. In FileMask you pass the name of the file to find, or a file mask. Examples:
FindFiles('c:\', 'letter01.doc')
FindFiles('d:\', 'euroen??.dpr')
FindFiles('d:\projects', '*.dpr')
If you want to test this procedure, start a new project and add some components to the form: two Edits (one for the starting directory, one for the mask), a Button, a TLabel and a ListBox.
implementation
....
var
FilesList: TStringList;
...
procedure FindFiles(StartDir, FileMask: string);
var
SR: TSearchRec;
DirList: TStringList;
IsFound: Boolean;
i: integer;
begin
if StartDir[length(StartDir)] <> '\' then
StartDir := StartDir + '\';
{ Build a list of the files in directory StartDir
(not the directories!) }
IsFound :=
FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0;
while IsFound do
begin
FilesList.Add(StartDir + SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
// Build a list of subdirectories
DirList := TStringList.Create;
IsFound := FindFirst(StartDir + '*.*', faAnyFile, SR) = 0;
while IsFound do
begin
if ((SR.Attr and faDirectory) <> 0) and
(SR.Name[1] <> '.') then
DirList.Add(StartDir + SR.Name);
IsFound := FindNext(SR) = 0;
end;
FindClose(SR);
// Scan the list of subdirectories
for i := 0 to DirList.Count - 1 do
FindFiles(DirList[i], FileMask);
DirList.Free;
end;
procedure TForm1.ButtonFindClick(Sender: TObject);
begin
FilesList := TStringList.Create;
FindFiles(EditStartDir.Text, EditFileMask.Text);
ListBox1.Items.Assign(FilesList);
LabelCount.Caption := 'Files found: ' + IntToStr(FilesList.Count);
FilesList.Free;
end;
2004. szeptember 27., hétfő
Image can show preview-image in dwg file (autocad file name)
Problem/Question/Abstract:
I have writen a component from image which can show the preview-image in dwg file
Answer:
unit DWGView;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
BITMAPINFO256 = record
bmiHeader: BITMAPINFOHEADER;
bmiColors: array[0..255] of RGBQUAD;
end;
type
TNoPreviewEvent = procedure(Sender: TOBject) of object;
TFileErrorEvent = procedure(Sender: TOBject; DWGName: string) of object;
TDWGView = class(TImage)
private
FDWGVersion: string;
FDWGFile: string;
FNoPreviewEvent: TNoPreviewEvent;
FOnFileError: TFileErrorEvent;
FImage: TImage;
procedure SetDWGFile(const Value: string);
procedure SetFImage(const Value: TImage);
{ Private declarations }
protected
procedure ReadDWG;
constructor TDWGView;
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property Image: TImage read FImage write SetFImage;
property DWGFile: string read FDWGFile write SetDWGFile;
property DWGVersion: string read FDWGVersion;
property OnNoPreview: TNoPreviewEvent read FNoPreviewEvent write FNoPreviewEvent;
property OnFileError: TFileErrorEvent read FOnFileError write FOnFileError;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Voice', [TDWGView]);
end;
procedure TDWGView.ReadDWG;
var
DWGF: TFileStream; // ?�???ġ?
MemF: TMemoryStream; // ??µ????�?�
BMPF: TMemoryStream; // ?»?�?ġ?
SentinelF: TMemoryStream; //?�����¶? 16�ֽ?
bif: BITMAPINFO256; // ?»?�?ġ??�?�
bfh: BITMAPFILEHEADER; // ?»?�?ġ??ġ??· 14�ֽ?
PosSentinel: LongInt; // ?�����¶??»�?
LenPreview: Integer; // �??�����¶?�®??�?¬�?�???��¬??µ?µij�¶?
RasterPreview: ShortInt; // ?µ?�?�???��¬????µ�?»¬�µ?�ֽ?�???
// 0 �»±�???�???��¬ 1 ±�??BMP?��¬
// 2 ±�??WMF?��¬ 3 ?¬?±±�??BMP??WMF?��¬
PosBMP: Integer; // ?��¬µ�տ?µ?»�?�¬�»?»¶«???»?�
LenBMP: Integer; // ?��¬��¶?�¬�»?¬BITMAPFILEHEADER??µ?�¬�»?»¶«???»?�
IndexPreview: Integer;
TypePreview: Shortint; // ?��¬????
begin
if Assigned(FOnFileError) then
FOnFileError(Self, FDWGFile);
DWGF := TFileStream.Create(FDWGFile, fmOpenRead);
BMPF := TMemoryStream.Create;
MemF := TMemoryStream.Create;
SentinelF := TMemoryStream.Create;
try
SetLength(FDWGVersion, 6);
DWGF.ReadBuffer(FDWGVersion[1], 6);
DWGF.Position := 13; // ?ġ?�??�13�¦�¬???�����¶?
DWGF.Read(PosSentinel, 4);
DWGF.Position := PosSentinel;
SentinelF.CopyFrom(DWGF, 16); // ¶????�����¶?
DWGF.Read(LenPreview, 4); // ¶???
DWGF.Read(RasterPreview, 1); // ¶????��¬????
for IndexPreview := RasterPreview - 1 downto 0 do
begin
MemF.Position := 0;
MemF.CopyFrom(DWGF, 9); // ?��¬???�?� 9�ֽ?
MemF.Position := 0;
MemF.Read(TypePreview, 1); // TypePreview ?��¬????
case TypePreview of
1: ; // ?�??µ�???��???
2:
begin
// BMP?��¬,??DWG?ġ?�?±���µ�BMP?��¬?�????????�?BMP±�·¶µ�
// ?�?�?ġ???µ?�¬µ«??�»±���BITMAPFILEHEADER??µ?
MemF.Position := 1;
MemF.Read(PosBMP, 4); // 2,5
MemF.Read(LenBMP, 4); // 6,9
DWGF.Position := PosBMP;
DWGF.ReadBuffer(bif, sizeof(bif));
with bif do
begin
bmiColors[0].rgbBlue := 0;
bmiColors[0].rgbGreen := 0;
bmiColors[0].rgbRed := 0;
bmiColors[225].rgbBlue := 255;
bmiColors[225].rgbGreen := 255;
bmiColors[225].rgbRed := 255;
end;
bfh.bfType := $4D42;
bfh.bfSize := LenBMP + sizeof(bfh); //
bfh.bfReserved1 := 0;
bfh.bfReserved2 := 0;
bfh.bfOffBits := 14 + $28 + 1024;
BMPF.Position := 0;
BMPF.Write(bfh, sizeof(bfh));
BMPF.WriteBuffer(bif, sizeof(bif));
BMPF.CopyFrom(DWGF, LenBMP - 1064);
BMPF.Position := 0;
Picture.Bitmap.LoadFromStream(BMPF);
end;
3: ; // WMF?ġ?�¬?��?22�ֽ?��µ�Aldus?ġ??·
end;
end;
finally
SentinelF.Free;
MemF.Free;
DWGF.Free;
BMPF.Free;
end;
end;
procedure TDWGView.SetDWGFile(const Value: string);
begin
FDWGFile := Value;
ReadDWG;
end;
procedure TDWGView.SetFImage(const Value: TImage);
begin
FImage := Value;
end;
constructor TDWGView.TDWGView;
begin
//TODO: Add your source code here
FDWGFile := '';
FDWGVersion := '';
end;
end.
2004. szeptember 26., vasárnap
Creating a descendant of a component to enhance functionality
Problem/Question/Abstract:
Adding an accelerator key to a TPageControl
Answer:
This tip is an example of extending the functionality of a component by creating a descendant. While implicit to the discussion at hand, here's where the power of an object-oriented language such as Delphi lays. As you'll see in the code below, it doesn't take much to create new functionality of an object by creating a descendant. The point of this is that had I not been using an object-oriented language, I would have had to re-write the original code of the TPageControl, then add the extended functionality. Fortunately, the VCL, which is really an object hierarchy, allows me to transparently inherit and retain the ancestral functionality and concentrate on the new functionality. You gotta love it!
For those of you new to Delphi, an accelerator key is a key that is pressed in combination with the Alt key to execute a command. They're sometimes called keyboard shortcuts or hotkeys, and you'll typically see them in menus as the underlined letter of a menu item. For instance, the "F" in the File menu selection is an accelerator key for that item. So to open up the File menu, you'd press Alt-F.
Accelerator keys aren't limited to just menu items. In fact, for almost any Caption property or a Caption-like property (e.g. Radio Group items) of a component, you can define an accelerator key. All you need to do is place an "&" before a letter to designate it as an accelerator key. This is useful with VCL components like a TRadioGroup's Items, which allow the user to quickly select the radio button choice with the touch of a key. However, not all VCL components will respond to accelerator keystrokes if you define them. TPageControl in Delphi 2.0, which replaces TTabbedNotebook, is one of those components. And with it, accelerator key functionality would be particularly useful.
The only method I know for implementing accelerator key functionality in a TPageControl is to create a new component. There's another way, but you have to create menu and define hotkeys for menu items with equivalent functionality (they'll turn your pages for you), and that's a pretty kludgy way of doing things. Besides, the code to accomplish what we want is actually very simple.
Below is the unit code for a descendant of TPageControl that adds accelerator key functionality. We'll discuss the particulars after the listing:
unit accel;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls;
type
TAccelPageCtrl = class(TPageControl)
private
{ Private declarations }
procedure CMDialogChar(var Msg: TCMDialogChar); message CM_DIALOGCHAR;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure TAccelPageCtrl.CMDialogChar(var Msg: TCMDialogChar);
var
I: Integer;
Okay: Boolean;
begin
Okay := False;
inherited; //call the inherited message handler.
//Now with our own component, start at Page 1 (Item 0) and work to the end.
for I := 0 to PageCount - 1 do
begin
//Is key pressed accelerator key in Caption?
Okay := IsAccel(Msg.CharCode, Pages[I].Caption) and CanChange(I);
//this is the fix
//It is, so change the page and break out of the loop.
if Okay then
begin
Msg.Result := 1; //you can set this to anything, but by convention it's 1
ActivePage := Pages[I];
Change;
Break;
end;
end;
end;
procedure Register;
begin
RegisterComponents('BD', [TAccelPageCtrl]);
end;
end.
As you can see from the above. all that's required to add accelerator key response is a simple message handler procedure. The message we're interested in is CM_DialogChar, a Delphi custom message type encapsulated by TCMDialogChar, which is a wrapper type for the Windows WM_SYSCHAR message. WM_SYSCHAR is the Windows message that is used to trap accelerator keys; you can find a good discussion of it in the online help. The most important thing to note is what happens when the TAccelPageCtrl component detects that a CM_DialogChar message has fired.
Take a look at the CMDialogChar procedure, and note that all that's going in the code is a simple for loop that starts at the first page of the descendant object and goes to the last page, unless the key that was pressed happened to be an accelerator key. We can easily determine if a key is an accelerator key with the IsAccel function, which takes the key code pressed and a string (we passed the Caption property of the current TabSheet). IsAccel searches through the string and looks for a matching accelerator key. If it finds one, it returns True. If so, we set the message result value and change the page of TAccelPageCtrl to the page where the accelerator was found by setting the ActivePage property and calling the inherited Change procedure from TPageControl.
I haven't used TPageControl since I created this component because of how easy TAccelPageCtrl makes switching from TabSheet to TabSheet. It's far easier to do a Alt-<key> combination than use the mouse when you're at the keyboard. Play around with this and you'll be convinced not to use the standard VCL TPageControl.
2004. szeptember 25., szombat
How to prevent the cursor from jumping to the start of a TDBMemo after setting the charcase property
Problem/Question/Abstract:
I published the Charcase property of a TDBMemo, but have had a couple of problems. If I set the case to Upper or Lower sometimes, depending on what text is in the memo, no matter where I place the cursor and start typing (dataset in browse mode before this) the cursor jumps to the start of the memo and types there instead. Setting Charcase to normal corrects this. This only happens on a memo that is fairly full with text (the display area that is!). Any ideas why this is happening and how to stop it?
Answer:
This happens even in a normal TDBMemo. My solution is to use the OnEnter event of the TDBMemo:
{ ... }
x := TDBMemo(Sender).SelStart;
if not (TDBMemo(Sender).DataSource.DataSet.State in dsEditModes) then
TDBMemo(Sender).DataSource.DataSet.Edit;
TDBMemo(Sender).SelStart := x;
TDBMemo(Sender).SelLength := 0;
{ ... }
This seems to solve that problem entirely. First it stores the clicked on location of the memo, and you can see what the rest does.
2004. szeptember 24., péntek
How to create a random list of numbers
Problem/Question/Abstract:
I should give an example of what I'm trying to do. The NewTrackList procedure is supposed to create a list of 14 numbers from 1 to 14, with no numbers repeated. The list is supposed to be random, that is, a different sequence of numbers is created every time the procedure runs.
Answer:
procedure NewTrackList;
var
TrackNumbersList: array[1..14] of Integer;
I, II: Integer;
SameTracks: Boolean;
S: string;
begin
for I := 1 to 14 do
TrackNumbersList[I] := 0;
for I := 1 to 14 do
begin
TrackNumbersList[I] := Random(14) + 1;
repeat
SameTracks := False;
for II := 1 to I - 1 do
begin
if I = 1 then
Break;
if TrackNumbersList[I] = TrackNumbersList[II] then
begin
SameTracks := True;
TrackNumbersList[I] := Random(14) + 1;
Break;
end;
end;
until
not SameTracks;
end;
S := '';
for I := 1 to 14 do
S := S + ' ' + IntToStr(TrackNumbersList[I]);
Form1.Label1.Caption := S;
end;
procedure TTunesMain.FormCreate(Sender: TObject);
begin
Randomize;
NewTrackList;
end;
S is a local variable of type String. I obviously added a TLabel to the form, as well.
2004. szeptember 23., csütörtök
Master passwords for password protected Paradox tables
Problem/Question/Abstract:
Master passwords for password protected Paradox tables
Answer:
The password protection for Paradox tables is really weak. Here are two of the master passwords which you can use to open any protected Paradox table:
For Paradox 5 and 7 / BDE 3.0:
jIGGAe
cupcdvum
For Paradox 4 DOS:
nx66ppx
Feliratkozás:
Bejegyzések (Atom)