2008. július 31., csütörtök
Exporting an TImage contents to WMF format
Problem/Question/Abstract:
How can I export the content of a TImage for an image in the format of Clip Galleyr of Microsoft Office?
Answer:
I used this function once and she worked.
procedure ExportBMPtoWMF(Imagem: TImage; Dest: Pchar);
//
// Export an TImage contents to WMF format (Microsoft Clippart format file)
//
var
Metafile: TMetafile;
MetafileCanvas: TMetafileCanvas;
DC: HDC;
ScreenLogPixels: Integer;
begin
Metafile := TMetafile.Create;
try
DC := GetDC(0);
ScreenLogPixels := GetDeviceCaps(DC, LOGPIXELSY);
Metafile.Inch := ScreenLogPixels;
Metafile.Width := Imagem.Picture.Bitmap.Width;
Metafile.Height := Imagem.Picture.Bitmap.Height;
MetafileCanvas := TMetafileCanvas.Create(Metafile, DC);
ReleaseDC(0, DC);
try
MetafileCanvas.Draw(0, 0, Imagem.Picture.Bitmap);
finally
MetafileCanvas.Free;
end;
Metafile.Enhanced := FALSE;
Metafile.SaveToFile(Dest);
finally
Metafile.Destroy;
end;
end;
2008. július 30., szerda
Create a locked file
Problem/Question/Abstract:
I was using temporary files on the root of the disk, but the user could try to modify it when my application was open and I didn't want that. Here's how to prevent it.
Answer:
Solve 1:
There are two ways of doing that, but one, with the use of Windows' APIs (LockFileEx and UnlockFileEx) using the parameter LOCKFILE_EXCLUSIVE_LOCK was not good for my case, so I found that:
Create the file with the OpenFile function and handle it:
hMyLockedFile := OpenFile('c:\variables.dat', ofStruct, OF_CREATE or OF_READWRITE or
OF_SHARE_EXCLUSIVE);
Now, you can work with your file, but users cannot change it!
A last comment:
I found that in Win32 SDK Reference, so if you need to know more (and there's more to know: believe me!) you should use it!
Solve 2:
var
SA: TSecurityAttributes;
MyText: array[0..500] of char;
BWritten: DWord;
OK: Boolean;
begin
MyText := 'Mark Halter' + chr(13) + Chr(10) + 'S�dstr. 6';
with SA do
begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
Hndl := CreateFile('d:\temp\testfile.txt', // filename
GENERIC_READ or GENERIC_WRITE, // read/write access
0, // do not share file
@SA, // Security Attributes
CREATE_ALWAYS, // create file everytime
FILE_ATTRIBUTE_NORMAL, // set normal attributs
0);
OK := WriteFile(Hndl,
MyText,
StrLen(MyText),
BWritten,
nil);
CloseHandle(Hndl);
end;
2008. július 29., kedd
Limit the form position to the screen's work area
Problem/Question/Abstract:
What's the best windows message to check if the Form's position is beyond the desktop area, when the user is moving it? How can I prevent the form/ mouse from moving when that happens?
Answer:
Solve 1:
You need a message that is send to the form before it moves and allows you to modfify the position it is about to move to before it actually does move. WM_MOVING or WM_WINDOWPOSCHANGING fit that bill. I would use the second, WM_MOVING will not be send if the user has switched off the "drag full window" option.
Limit a form to the screens workarea:
{ Private declarations }
procedure WMWINDOWPOSCHANGING(var msg: TWMWINDOWPOSCHANGING);
message WM_WINDOWPOSCHANGING;
procedure TForm1.WMWINDOWPOSCHANGING(var msg: TWMWINDOWPOSCHANGING);
var
r: TRect;
begin
if ((SWP_NOMOVE or SWP_NOSIZE) and msg.WindowPos^.flags) < > (SWP_NOMOVE
or SWP_NOSIZE) then
begin
{Window is moved or sized, get usable screen area}
SystemParametersInfo(SPI_GETWORKAREA, 0, @r, 0);
{Check if operation would move part of the window out of this area.
If so correct position and, if required, size, to keep window fully inside
the workarea. Note that simply adding the SWM_NOMOVE and SWP_NOSIZE flags
to the flags field does not work as intended if full dragging of windows is
disabled. In this case the window would snap back to the start position instead
of stopping at the edge of the workarea, and you could still move the
drag rectangle outside that area. }
with msg.WindowPos^ do
begin
if x < r.left then
x := r.left;
if y < r.top then
y := r.top;
if (x + cx) > r.right then
begin
x := r.right - cx;
if x < r.left then
begin
cx := cx - (r.left - x);
x := r.Left;
end;
end;
if (y + cy) > r.bottom then
begin
y := r.bottom - cy;
if y < r.top then
begin
cy := cy - (r.top - y);
y := r.top;
end;
end;
end;
end;
inherited;
end;
Delphi 4.03 does not recognize TWMMOVING, because there is no message record type declared for it for some reason. That is easily fixed, however:
type
TWmMoving = record
Msg: Cardinal;
fwSide: Cardinal;
lpRect: PRect;
Result: Integer;
end;
Solve 2:
You can get this behaviour by handing the WM_MOVING message in the form. The message is send to the form before it actually moves, so you can modify the rectangle with the new form position before you pass the message on to the inherited handler.
For some reason messages.pas declares no message record for this message.
type
TWmMoving = record
Msg: Cardinal;
fwSide: Cardinal;
lpRect: PRect;
Result: Integer;
end;
Add a handler to your forms private section:
procedure WMMoving(var msg: TWMMoving); message WM_MOVING;
Implement it as:
procedure TFormX.WMMoving(var msg: TWMMoving);
var
r: TRect;
begin
r := Screen.WorkareaRect;
{Compare the new form bounds in msg.lpRect^ with r and modify it if necessary}
if msg.lprect^.left < r.left then
OffsetRect(msg.lprect^, r.left - msg.lprect^.left, 0);
if msg.lprect^.top < r.top then
OffsetRect(msg.lprect^, 0, r.top - msg.lprect^.top);
if msg.lprect^.right > r.right then
OffsetRect(msg.lprect^, r.right - msg.lprect^.right, 0);
if msg.lprect^.bottom > r.bottom then
OffsetRect(msg.lprect^, 0, r.bottom - msg.lprect^.bottom);
inherited;
end;
2008. július 28., hétfő
Is font "X" installed?
Problem/Question/Abstract:
How to determine if a font is installed
Answer:
You can just use this function, or put the code directly wherever you need it
function FontInstalled(const FontName: string): Boolean;
begin
Result := Screen.Fonts.IndexOf(FontName) > 0
end;
then just call it
if not (FontInstalled('Pilobolus')) then
begin
ShowMessage('Pilobolus font is not installed!')
//you can do stuff here to install it, or whatever you need...
end;
2008. július 27., vasárnap
TStringGrid functions (Delete, Insert, Sort)
Problem/Question/Abstract:
How to insert, delete or sort columns in StringGrids
Answer:
Solve 1:
procedure GridRemoveColumn(StrGrid: TStringGrid; DelColumn: Integer);
var
Column: Integer;
begin
if DelColumn <= StrGrid.ColCount then
begin
for Column := DelColumn to StrGrid.ColCount - 1 do
StrGrid.Cols[Column - 1].Assign(StrGrid.Cols[Column]);
StrGrid.ColCount := StrGrid.ColCount - 1;
end;
end;
procedure GridAddColumn(StrGrid: TStringGrid; NewColumn: Integer);
var
Column: Integer;
begin
StrGrid.ColCount := StrGrid.ColCount + 1;
for Column := StrGrid.ColCount - 1 downto NewColumn do
StrGrid.Cols[Column].Assign(StrGrid.Cols[Column - 1]);
StrGrid.Cols[NewColumn - 1].Text := '';
end;
procedure GridSort(StrGrid: TStringGrid; NoColumn: Integer);
var
Line, PosActual: Integer;
Row: TStrings;
begin
Renglon := TStringList.Create;
for Line := 1 to StrGrid.RowCount - 1 do
begin
PosActual := Line;
Row.Assign(TStringlist(StrGrid.Rows[PosActual]));
while True do
begin
if (PosActual = 0) or (StrToInt(Row.Strings[NoColumn - 1]) >=
StrToInt(StrGrid.Cells[NoColumn - 1, PosActual - 1])) then
Break;
StrGrid.Rows[PosActual] := StrGrid.Rows[PosActual - 1];
Dec(PosActual);
end;
if StrToInt(Row.Strings[NoColumn - 1]) < StrToInt(StrGrid.Cells[NoColumn - 1,
PosActual]) then
StrGrid.Rows[PosActual] := Row;
end;
Renglon.Free;
end;
Solve 2:
Had a few problems with range errors with the algorythms. On Delete or Add columns it is desirable to keep the widths of the columns as they are moved. Add column could also take the width of the new column (or default to DefaultColWidth if zero). I also had range errors in the Grid sort. On a large grid a Quicksort routine would be more desirable.
The Quicksort routine could take various sort modes as a parameter eg. Alpha,Double,Integer etc. (have supported only these 3 in demo, but it's easy to see how to incorporate more). The quick sort should also take "from row - to row" as parameters as we normally would not want to sort the header, or just a sub range may be required to be
sorted.
All in all though, some nice ideas for an extended stringgrid class, couple with DeleteRow, AddRow, LoadFromQuery etc.
procedure RemoveColumn(SG: TStringGrid; ColNumber: integer);
var
Column: integer;
begin
ColNumber := abs(ColNumber);
if ColNumber <= SG.ColCount then
begin
for Column := ColNumber to SG.ColCount - 2 do
begin
SG.Cols[Column].Assign(SG.Cols[Column + 1]);
SG.Colwidths[Column] := SG.Colwidths[Column + 1];
end;
SG.ColCount := SG.ColCount - 1;
end;
end;
procedure AddColumn(SG: TStringGrid; AtColNumber: integer;
ColWidth: integer = 0);
var
Column: integer;
Wdth: integer;
begin
AtColNumber := abs(AtColNumber);
SG.ColCount := SG.ColCount + 1;
if abs(ColWidth) = 0 then
Wdth := SG.DefaultColWidth
else
Wdth := ColWidth;
if AtColNumber <= SG.ColCount then
begin
for Column := SG.ColCount - 1 downto AtColNumber + 1 do
begin
SG.Cols[Column].Assign(SG.Cols[Column - 1]);
SG.Colwidths[Column] := SG.Colwidths[Column - 1];
end;
SG.Cols[AtColNumber].Text := '';
SG.Colwidths[AtColNumber] := Wdth;
end;
end;
Solve 3:
type
TStringGridExSortType = (srtAlpha, srtInteger, srtDouble);
procedure GridSort(SG: TStringGrid; ByColNumber, FromRow, ToRow: integer;
SortType: TStringGridExSortType = srtAlpha);
var
Temp: TStringList;
function SortStr(Line: string): string;
var
RetVar: string;
begin
case SortType of
srtAlpha: Retvar := Line;
srtInteger: Retvar := FormatFloat('000000000', StrToIntDef(trim(Line), 0));
srtDouble:
try
Retvar := FormatFloat('000000000.000000', StrToFloat(trim(Line)));
except
RetVar := '0.00';
end;
end;
Result := RetVar;
end;
// Recursive QuickSort
procedure QuickSort(Lo, Hi: integer; CC: TStrings);
procedure Sort(l, r: integer);
var
i, j: integer;
x: string;
begin
i := l;
j := r;
x := SortStr(CC[(l + r) div 2]);
repeat
while SortStr(CC[i]) < x do
inc(i);
while x < SortStr(CC[j]) do
dec(j);
if i <= j then
begin
Temp.Assign(SG.Rows[j]); // Swap the 2 rows
SG.Rows[j].Assign(SG.Rows[i]);
SG.Rows[i].Assign(Temp);
inc(i);
dec(j);
end;
until i > j;
if l < j then
sort(l, j);
if i < r then
sort(i, r);
end;
begin {quicksort}
Sort(Lo, Hi);
end;
begin
Temp := TStringList.Create;
QuickSort(FromRow, ToRow, SG.Cols[ByColNumber]);
Temp.Free;
end;
2008. július 26., szombat
Speech Part 1 - How to Add "Text to Speech" (Speech Synthesis) to your Delphi Apps
Problem/Question/Abstract:
How can I get my application to read text?
Answer:
On Aug 11, 2001 Microsoft released the SAPI 5.1 SDK. This is significant because SAPI 5.1 is fully automated. That is you can use it from any language that supports OLE automation. These are not Active X controls and can be either early or late bound.
In this article I’m going to show you how to get and install the SAPI 5.1 SDK. Then I’m going to show how to use the SDK convert text to synthesized speech in a Delphi application. The synthesized speech is played over you computers speakers. I test this in Delphi 5 and 6.
To get SAPI 5.1 you need to go to Microsoft’s Speech.net Technologies web site at
http://www.microsoft.com/speech
and follow the link to the download. Right next to the download link is the release notes link. READ THE RELEASE NOTE! Especially if your development machine is using a default language other than US English.
If you are running a beta version of the XP operating system you might have some problems. This is because SAPI 5.1 is built into XP and the most recent public beta of XP as of this writing (RC 2) includes an earlier version of SAPI 5.1. Don’t try to install the release version of SAPI 5.1 into XP, it will not work.
Once you read the release notes follow the link to the Speech SDK 5.1 Download page. In most cases all you need to download is the link labeled “Speech SDK 5.1 (68 MB). This contains the SDK, the documentation and the free Microsoft English text to speech and speech recognition engines. The download is very large, 68 MB, so unless you have a high speed connection to the internet you might want to order the SDK CD from Microsoft.
…. Time passes while you download or wait for the postman ….
Ok, now you have the SAPI 5.1 SDK. Run the speechsdk51.exe to install it on your development system.
DELPHI 6 Users IMPORTANT
There is a bug in the type library import in Delphi 6 see article "Delphi 6 - Imported Automation Events Bug". This sample will still work with the unit created by the type libary import in Delphi 6 but only because none of the events for the component are used. If you want to use any of the SPVoice events you will need to read article "Delphi 6 - Imported Automation Events Bug".
What you need to do now is make Delphi aware of the new SAPI automation objects. To do this, start up Delphi 5 or 6 (I didn’t try earlier versions) and go to Project | Import Type Library. In the Import Type Library dialog highlight “Microsoft Speech Object Library (Version 5.1)”. If you don’t find this in the list then something’s wrong with the installation of SAPI 5.1.
Delphi is going to want to put the SAPI components on your ActiveX palette page. I recommend you put these on a new palette page called “SAPI 5” since the number of components installed is large (19). You may also want to choose a “Unit dir name” of something other than the default. Make sure the “Generate Component Wrapper” check box is checked and press the >Install< button.
In the Install dialog choose the “Into new package” tab and in the “File name:” field give a package name like “SAPI5.dpk” press the browse button and make sure the dpk is created in the same directory where you created the components. Actually this isn’t completely necessary it just helps keep things together. In the Install dialog’s Description field give some meaningful description like “SAPI 5 automation components”. Press OK
Press yes in the confirm dialog and the new components will be created and installed.
If you now look in the directory you specified for the components you should find SpeechLib_TLB.pas (and dcr) which contains all the component code as well as interface, const, type and other useful information. This is your most valuable piece of documentation on the SDK. I’ve found it even better than the Microsoft SAPI 5.1 documentation which is pretty good. This directory should also contain (if you followed the above instructions) the SAPI5.dpk which is your package source.
If you go to the far eastern end of your component palette you should find the new SAPI5 palette page with its 19 speech components.
Now for the fun part.
Let’s make an application that can synthesize speech. In Delphi start a new application and drop a button on the form. On the SAPI5 palette page find the SpVoice component and drop it on the form. On my machine this component is the 5th one reading from left to right.
Now create an onClick event for you button that looks something like this;
procedure TForm1.Button1Click(Sender: TObject);
begin
SpVoice1.Speak('Hello world!', SVSFDefault);
end;
Run the program and press the button. Cool hu?
At this level it’s amazingly simple. The SPVoice objects Speak method is very powerful. This power comes from the second parameter. For the above example I choose to use the default mode which causes the speak method to return only when the synthesis is complete, not to purge pending speech requests, to respond to special XML control tags embedded in the text.
The SDKs documentation is contained in sapi.chm which you will find in the \Program Files\Microsoft Speech SDK 5.1\Docs\Help directory.
Sapi.chm contains a lot of information. To go directly to the meat of the subject go to the last folder on the outlines 1st level titled Automation and go down to SPVoice and then to the Speak method read what’s there and also be sure to follow the link to the SpeechVoiceSpeakFlags info. You will find that in addition to just speaking passed in text that can also do much more some of the more interesting flags are;
Pass in a file name and speak the text in the file. (SVSFIsFilename)
Make the function either return immediately (asynchronously) or only after the synthesis is complete(synchronously). If you speak asynchronously there are events available to fire when the speech is done. (SVSFlagsAsync)
Embed flags in the text that can control various aspects of the synthesis like pitch, rate, emphasis, and much more (see the included White Paper titled “XML TTS Tutorial”). I found this feature a bit addicting as I attempted to make the synthesized voice sing.( SVSFIsXML)
One interesting thing I found (but not documented) was that you can speak a web sites title by setting the flag to SVSFIsFilenam and passing a URL. If you are connected to the internet, try replacing the speak line in the sample line with
SpVoice1.Speak('http://www.o2a.com', SVSFIsFilename);
And run it.
Even more bizarre is you can use the speak method to play wav files. Try
SpVoice1.Speak('C:\WINNT\MEDIA\Windows Logon Sound.wav', SVSFIsFilename);
There’s a lot more to SAPI then text to speech and there’s more to text to speech then what I’ve covered here. Hopefully this will be the first of a number of articles on SAPI but I’ll only do them if you’re interested so please be sure to comment. Also I’m completely open to suggestions on what you’d like to see next (if anything at all).
If you want to talk privately I’m at alecb@o2a.com.
2008. július 25., péntek
Debug Delphi 3 experts with Delphi 3
Problem/Question/Abstract:
Debug Delphi 3 experts with Delphi 3
Answer:
Delphi 3 has a new feature "debug DLLs". It can be used to debug experts with the internal debugger. Just follow these simple steps, and debugging an expert can be fun:
Make sure that the expert is not installed. If there is this entry \CURRENT_USER\software\Delphi\3.0\experts, myexpert=\projects\myexpert\expert.dll rename this entry to "expert.xxx". (don't delete it, you'll need it later). Otherwise, you cannot compile a new version.
Run Delphi, open your expert's project as used, compile it and set the break points you think you need.
Go to the menu item run | parameters. This is the new Delphi 3 feature mentioned above.
Surprise: the host application is Delphi itself! So, next to the field "host app", enter something like e:\Programs\delphi3\bin\delphi32.exe (with path)
Second trick: now we install the expert... If you have "expert.xxx" installed, rename that to "expert.dll". This will be used by any Delphi instance started from now on.
Run "your application" (= Delphi 3) using menu item run | run. If you have enough RAM, Delphi is loaded and this instance will have your expert installed. Activate the expert, you'll have the possibility to use the comfort of the first instance's internal debugger.
Close the right instance of Delphi - and you can modify/ recompile etc. your expert.
2008. július 24., csütörtök
Getting a page from a webserver and put it in a string variable
Problem/Question/Abstract:
How do I get a page from a webserver only using TClientSocket?
Answer:
Attach the following event-handlers to your TClientSocket. It gets the file from the server and puts it in the FText string variable. Btw, it doesn't remove the header that is also send by the webserver.
Don't forget to setup your Socket object with a correct server adress. Set port to 80. And open it with "Socket.Open;".
I wrote this as part of an AutoUpdate feature in one of my applications.
const
WebPage = '/index.html';
var
FText: string;
procedure TForm1.SocketWrite(Sender: TObject;
Socket: TCustomWinSocket);
begin
Socket.SendText('GET ' + Webpage + ' HTTP/1.0'#10#10);
end;
procedure TForm1.SocketRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
FText := FText + Socket.ReceiveText
end;
procedure TForm1.SocketConnecting(Sender: TObject;
Socket: TCustomWinSocket);
begin
FText := '';
end;
procedure TForm1.SocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
{ --- }
{ HERE YOU CAN PROCESS YOUR FText !!! }
{ --- }
end;
procedure TForm1.SocketError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode := 0; { Just ignore errors }
end;
2008. július 23., szerda
Get the screen coordinates of the rectangle in which web pages are rendered in IE
Problem/Question/Abstract:
I am writing a Delphi application that needs to know the screen coordinates (top, left) of where the IE 'browser section' starts. What I mean by 'browser section' is the rectangle where web pages are rendered - not where the IE window is. I can find out where the IE window is, and where the client coordinates start, but not the where the 'browser section' starts. I want to lay my Delphi window precisely on top of the where the browser section is. But depending on how many toolbars the users is displaying where this browser section may start is a mystery.
Answer:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Buttons, ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
SpeedButton1: TSpeedButton;
Label1: TLabel;
procedure SpeedButton1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure FindIEBrowserWindowHandle;
end;
var
Form1: TForm1;
IEBrowserWindowHandle: THandle;
implementation
{$R *.DFM}
function EnumIEChildProc(AHandle: hWnd; AnObject: TObject): BOOL; stdcall;
var
tmpS: string;
theClassName: string;
theWinText: string;
begin
Result := True;
SetLength(theClassName, 256);
GetClassName(AHandle, PChar(theClassName), 255);
SetLength(theWinText, 256);
GetWindowText(AHandle, PChar(theWinText), 255);
tmpS := StrPas(PChar(theClassName));
if theWinText <> EmptyStr then
tmpS := tmpS + ' "' + StrPas(PChar(theWinText)) + '"'
else
tmpS := tmpS + '""';
if Pos('Explorer_Server', tmpS) > 0 then
begin
IEBrowserWindowHandle := AHandle;
end;
end;
function IEWindowEnumProc(AHandle: hWnd; AnObject: TObject): BOOL; stdcall;
{callback for EnumWindows}
var
theClassName: string;
theWinText: string;
tmpS: string;
begin
Result := True;
SetLength(theClassName, 256);
GetClassName(AHandle, PChar(theClassName), 255);
SetLength(theWinText, 256);
GetWindowText(AHandle, PChar(theWinText), 255);
tmpS := StrPas(PChar(theClassName));
if theWinText <> EmptyStr then
tmpS := tmpS + ' "' + StrPas(PChar(theWinText)) + '"'
else
tmpS := tmpS + '""';
if Pos('IEFrame', tmpS) > 0 then
begin
EnumChildWindows(AHandle, @EnumIEChildProc, longInt(0));
end;
end;
procedure TForm1.FindIEBrowserWindowHandle;
begin
Screen.Cursor := crHourGlass;
try
EnumWindows(@IEWindowEnumProc, LongInt(0));
finally
Screen.Cursor := crDefault;
end;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
caption: array[0..127] of Char;
s: string;
R: TRect;
begin
FindIEBrowserWindowHandle;
if IEBrowserWindowHandle > 0 then
begin
GetWindowRect(IEBrowserWindowHandle, R);
s := 'IE Browser Window located at: ' + IntToStr(R.Top) + ', ' + IntToStr(R.Left)
+ ', ' + IntToStr(R.Bottom) + ', ' + IntTOStr(R.Right);
Label1.Caption := s;
end
else
label1.Caption := 'Not Found';
end;
end.
2008. július 22., kedd
Create a watermark
Problem/Question/Abstract:
How to create a watermark
Answer:
Solve 1:
How it works: On a hidden image, sized to suit the final text, print text in black. Then by using the Pixels[] property, interrogate the hidden image. Where a pixel is black, adjust the red/green/blue settings of a corresponding pixel in the photograph. The following procedure MakeWatermark will do just that, placing the text where you want it.
{ ... }
type
wmType = (wmTopLeft, wmTopCentre, wmTopRight, wmCentreLeft, wmCentre, wmCentreRight,
wmBottomLeft, wmBottomCentre, wmBottomRight, wmRndXRndY, wmSetXRndY,
wmRndXSetY, wmSetXSetY);
function Limit256(I: integer): integer;
begin
if I < 0 then
I := 0;
if > 255 then
I := 255;
Limit256 := I;
end;
function ColourAdjust(Z, R, G, B: integer): integer;
var
B1, G1, R1: integer;
begin
{shl 1 = Multiply by 2}{TColor}
R1 := Limit256((Z and $000000FF) + R);
G1 := Limit256(((Z and $0000FF00) shr 8) + G);
B1 := Limit256(((Z and $00FF0000) shr 16) + B);
ColourAdjust := (B1 shl 16) + (G1 shl 8) + R1;
end;
procedure TForm1.MakeWatermark(const wmMode: wmType; var vImage: TImage;
const Txt, FntName: string; FntStyle: TFontStyles; FntSize, X, Y, AdjRed, AdjGrn,
AdjBlu: integer);
var
I, J, IH, IW, TH, TW, Z: integer;
TmpImg: TImage;
begin
TmpImg := TImage.Create(Form1);
TmpImg.Picture := nil;
with TmpImg do
begin
Canvas.Pen.Color := clWhite;
Canvas.Pen.Style := psSolid;
Canvas.Font.Name := FntName;
Canvas.Font.Color := clBlack;
Canvas.Font.Style := FntStyle;
if FntSize > 0 then
begin
Canvas.Font.Size := FntSize;
TW := Canvas.TextWidth(Txt);
TH := Canvas.TextHeight(Txt);
end
else
begin
TW := vImage.Width;
TH := vImage.Height;
I := 7;
repeat
inc(I);
Canvas.Font.Size := I;
until
(Canvas.TextWidth(Txt) > TW) or (Canvas.TextHeight(Txt) > TH);
dec(I);
Canvas.Font.Size := I;
TW := Canvas.TextWidth(Txt);
TH := Canvas.TextHeight(Txt);
end;
end;
TmpImg.Width := TW;
TmpImg.Picture.Bitmap.Width := TW;
TmpImg.Height := TH;
TmpImg.Picture.Bitmap.Height := TH;
TmpImg.Repaint;
TmpImg.Canvas.TextOut(0, 0, Txt);
TmpImg.Refresh;
if TmpImg.Canvas.Pixels[0, 0] < 0 then
ShowMessage('TmpImg pixel error.');
if vImage.Picture.Bitmap.Canvas.Pixels[0, 0] < 0 then
ShowMessage('vImage pixel error.');
IW := vImage.Picture.Width;
IH := vImage.Picture.Height;
case wmMode of
wmTopLeft:
begin
X := 0;
Y := 0;
end;
wmTopCentre:
begin
X := IW div 2 - TW div 2;
Y := 0;
end;
wmTopRight:
begin
X := IW - TW;
Y := 0;
end;
wmCentreLeft:
begin
X := 0;
Y := IH div 2 - TH div 2;
end;
wmCentre:
begin
X := IW div 2 - TW div 2;
Y := IH div 2 - TH div 2;
end;
wmCentreRight:
begin
X := IW - TW;
Y := IH div 2 - TH div 2;
end;
wmBottomLeft:
begin
X := 0;
Y := IH - TH;
end;
wmBottomCentre:
begin
X := IW div 2 - TW div 2;
Y := IH - TH;
end;
wmBottomRight:
begin
X := IW - TW;
Y := IH - TH;
end;
wmRndXRndY:
begin
X := Random(IW - TW);
Y := Random(IH - TH);
end;
wmSetXRndY:
begin
{X passed}
Y := Random(IH - TH);
end;
wmRndXSetY:
begin
X := Random(IW - TW);
{Y passed}
end;
wmSetXSetY:
begin
{X passed}
{Y passed}
DoNothing;
end;
end;
for I := 0 to TW do
for J := 0 to TH do
if TmpImg.Canvas.Pixels[I, J] = clBlack then
begin
Z := vImage.Picture.Bitmap.Canvas.Pixels[I + X, J + Y];
Z := ColourAdjust(Z, AdjRed, AdjGrn, AdjBlu);
vImage.Picture.Bitmap.Canvas.Pixels[I + X, J + Y] := Z;
end;
TmpImg.Free;
end;
Call MakeWatermark with the following parameters:
watermark type eg wmTopLeft places the watermark in the top left of the image;
Image to be watermarked;
Text to be shown;
Name of the font to use;
style of the font eg [fsBold,fsItalic];
size of the text, use -1 for maximum size;
X and Y co-ordinates (used only in wmSetXRndY, wmRndXSetY and wmSetXSetY, X being only used where type contains "SetX";
adjustments for Red, Green, and Blue, negative = darken.
If the image you wish to watermark objects to reading the pixels then an error message will be given and the procedure exited.
Solve 2:
If you sport a company (or other) logo, you might want to display it on your program's GUI, but unobtrusively and matching the user's color preferences. This can be done by displaying it as a "watermark" with colors only slightly different from the background color. All you have to do is to add a Timage with a 2- or 3-color bitmap. One color (usually the one at the lower left corner of the bitmap) becomes the transparent color (be sure to set Image.Transparent := True), the other one or two are changed at runtime to the watermark colors:
procedure TAboutBox.FormCreate(Sender: TObject);
var
clWatermd: TColor;
clWaterml: TColor;
begin
{Set watermark colors slightly off parent}
clWatermd := ColorToRGB(Panel1.Color) - $101010;
clWaterml := ColorToRGB(Panel1.Color) + $080808;
with Image2 do
begin
{Modify moon color to watermark color}
Canvas.Brush.Color := clWaterml;
Canvas.FloodFill(20, 20, clSilver, fsSurface);
Canvas.FloodFill(51, 42, clSilver, fsSurface);
{Modify wolf color to watermark color}
Canvas.Brush.Color := clWatermd;
Canvas.FloodFill(60, 60, clBlack, fsSurface);
end;
end;
In the example above, clSilver in the original bitmap is displayed slightly brighter than the background, clBlack slightly darker. The point coordinates lie somewhere within the colored areas, separate areas must all be separately handled. On 256 color displays, the watermark colors may be 'slightly' off the desired effect, so use is only recommended on higher color resolutions.
2008. július 21., hétfő
Get the HTML code of the active document of a TWebBrowser component
Problem/Question/Abstract:
How to get the HTML code of the active document of a TWebBrowser component
Answer:
Solve 1:
procedure GetHtmlCode(WebBrowser: TWebBrowser; FileName: string);
var
htmlDoc: IHtmlDocument2;
PersistFile: IPersistFile;
begin
htmlDoc := WebBrowser.document as IHtmlDocument2;
PersistFile := HTMLDoc as IPersistFile;
PersistFile.save(StringToOleStr(FileName), true);
end;
Solve 2:
This function returns the body as a string, but, maybe, all you need is the InnerText from IHTMLDocument2.
function TFrameBook.GetFullHTMLBody(): string;
var
S: TStringStream;
begin
S := TStringStream.Create('');
try
(WebBrowser1.Document as IPersistStreamInit).Save(TStreamAdapter.Create(S), True);
Result := S.DataString;
finally
S.Free;
end;
end;
2008. július 20., vasárnap
Demo of file copying (TFileStream) in a thread (TThread)
Problem/Question/Abstract:
Demo of file coping (TFileStream) in a thread (TThread). It also resumes copying of files that have been partialy copied.
Answer:
unit copythread;
interface
uses
Classes, SysUtils;
const
KB1 = 1024;
MB1 = 1024 * KB1;
GB1 = 1024 * MB1;
type
TCopyFile = class(TThread)
public
Percent: Integer;
Done, ToDo: Integer;
Start: TDateTime;
constructor Create(Src, Dest: string);
private
{ Private declarations }
IName, OName: string;
protected
procedure Execute; override;
end;
implementation
{ TCopyFile }
constructor TCopyFile.Create(Src, Dest: string);
begin
IName := Src;
OName := Dest;
Percent := 0;
Start := Now;
FreeOnTerminate := True;
inherited Create(True);
end;
procedure TCopyFile.Execute;
var
fi, fo: TFileStream;
dod, did: Integer;
cnt, max: Integer;
begin
Start := Now;
try
{ Open existing destination }
fo := TFileStream.Create(OName, fmOpenReadWrite);
fo.Position := fo.size;
except
{ otherwise Create destination }
fo := TFileStream.Create(OName, fmCreate);
end;
try
{ open source }
fi := TFileStream.Create(IName, fmOpenRead);
try
{ synchronise dest en src }
cnt := fo.Position;
fi.Position := cnt;
max := fi.Size;
ToDo := Max - cnt;
Done := 0;
{ start copying }
repeat
dod := MB1; // Block size
if cnt + dod > max then
dod := max - cnt;
if dod > 0 then
did := fo.CopyFrom(fi, dod);
cnt := cnt + did;
Percent := Round(Cnt / Max * 100);
Done := Done + did;
ToDo := Max;
until (dod = 0) or (Terminated);
finally
fi.free;
end;
finally
fo.free;
end;
end;
end.
2008. július 19., szombat
What is the difference between Borland DataSnap and Microsof ADO.NET
Problem/Question/Abstract:
What is the difference between Borland DataSnap and Microsof ADO.NET
Answer:
// Torry's Delphi Tips - Database
// Author Pablo Reyes
// Listed 04.10.2003
Borland DataSnap vs Microsof ADO.NET
====================================
Delphi 7 and .NET Framework 1.1
A comparison between the tools provided by these two technologies for
building data aware applications.
Data access technologies:
=========================
Everybody knows that Borland DataSnap provides four data access technologies.
While ADO.NET is a data access technology, it provides four embedded data access technologies.
Third party companies provide data access technologies for both.
*** Borland DataSnap ***
- Borland Database Engine (BDE)
- dbExpress (DBX)
- InterBase Express (IBX)
- Activex Data Objects (ADO)
- Third party
*** Microsoft ADO.NET ***
- SQL for MS SQL Server
- OleDb
- ODBC
- Oracle
- Third party
Provide/Resolve
===============
Both uses a provide/resolve mechanism. You first provide data to a
component which holds data in memory in a disconnected fashion.
Changes to this data are hold in memory too.
Then you apply this changes to the underlying database.
So what you need is...
Components to establish a connection:
-------------------------------------
This components let you establish a connection with a
database and manage transactions.
*** Borland DataSnap ***
- Session and Database
- SQLConnection
- IBDatabase and IBTransaction
- ADOConnection
- Third party
*** Microsoft ADO.NET ***
- SQLConnection
- OleDbConnection
- ODBCConnection
- OracleConnection
- Third party
Borland DataSnap connection components have many similarities
between them but they all are different. All Microsoft ADO.NET
connection components implements the same interface.
Components to obtain data:
--------------------------
This components let you obtain a data set form a database
through one of the connection components.
*** Borland DataSnap ***
- Table, Query, StoredProc
- SQLTable, SQLQuery, SQLStoredProc, SQLDataSet
- IBTable, IBQuery, IBStoredProc, IBDataSet
- ADOTable, ADOQuery, ADOStoredProc, ADODataSet
- Third party
*** Microsoft ADO.NET ***
- SQLCommand, SQLDataReader
- OleDbCommand, OleDbDataReader
- ODBCCommand, OleDbDataReader
- OracleCommand, OracleDataReader
- Third party
Borland DataSnap components used to obtain data have many similarities between
them but they all are different. All Microsoft ADO.NET components used to obtain
data implements the same interface.
While some of the Borland DataSnap components used to obtain data let you obtain
a read-write, bi-directional data sets, all Microsoft components used to obtain
data provide read-only, forward-only data sets.
Components to provide data and resolve changes:
-----------------------------------------------
This components do two main things: provide data and resolve changes.
While Borland DataSnap component can perform it in a connected and disconnected fashion,
Microsoft ADO.NET component can perform it only in a disconnected fashion.
*** Borland DataSnap ***
- DataSetProvider
Provide:
- Connect it to a DataSet and it provides data to a ClientDataSet.
The DataSet must implements IProviderSupport interface.
- If the connected DataSet is a master DataSet it recognize the
master/detail relationship and provide data treating master record
and its detail records as a unit.
Resolve:
- It generates SQL statements on the fly using information form the
connected DataSet, even if you use JOINs.
- It treats master and details as a unit and generates transactions
for updating master and details in the same transaction and updates
data in the correct order (for inserts, first master and then details;
for deletes, first details and then master).
- It lets you configure how SQL statement should be generated.
- It manages concurrency.
*** Microsoft ADO.NET ***
- DataAdapter
Provide:
- You use a DataAdapter to fill a DataSet with records.
It could use embedded Command components or it could be connected
to your Command components.
- DataAdapter does not recognize master/detail relationships.
You need to use one DataAdapter for each table.
Resolve:
- It generates SQL statements on the fly using information from the SELECT statement,
but only for single tables.
- You must use one DataAdapter for each table so it updates only one table.
- It does not let you configure how SQL statement should be generated.
- It manages concurrency.
Components to hold data and changes in memory:
----------------------------------------------
This components hold data and changes to that data in memory.
Both can save its data to a file on disk and resolve updates later.
*** Borland DataSnap ***
- ClientDataSet
- It is aware of the DataSetProvider so they work together to apply
updates and reconcile update errors.
- Details are an extra field of the master. If you modify details
you also modify the master.
- It provides functionality to obtain details and BLOBs on demand.
*** Microsoft ADO.NET ***
- DataSet
- It doesn't aware of the DataAdapter so they don't work synchronized
and it doesn't know about update errors.
- Has a collection of tables and relations between this tables.
If you modify details you don't modify the master.
- It doesn't provide functionality to obtain details and BLOBs on demand.
Conclusions
===========
Borland DataSnap is a mature technology while Microsoft ADO.NET is an emerging,
new technology with an excellent starting point. You can accomplish the same task
with both technologies but you need to code a lot more with Microsoft ADO.NET.
With DataSetProvider and ClientDataSet from Borland DataSnap you get more than
with DataAdapter and DataSet from Microsoft ADO.NET.
Pablo Reyes
What is the difference between Borland DataSnap and Microsof ADO.NET
Answer:
// Torry's Delphi Tips - Database
// Author Pablo Reyes
// Listed 04.10.2003
Borland DataSnap vs Microsof ADO.NET
====================================
Delphi 7 and .NET Framework 1.1
A comparison between the tools provided by these two technologies for
building data aware applications.
Data access technologies:
=========================
Everybody knows that Borland DataSnap provides four data access technologies.
While ADO.NET is a data access technology, it provides four embedded data access technologies.
Third party companies provide data access technologies for both.
*** Borland DataSnap ***
- Borland Database Engine (BDE)
- dbExpress (DBX)
- InterBase Express (IBX)
- Activex Data Objects (ADO)
- Third party
*** Microsoft ADO.NET ***
- SQL for MS SQL Server
- OleDb
- ODBC
- Oracle
- Third party
Provide/Resolve
===============
Both uses a provide/resolve mechanism. You first provide data to a
component which holds data in memory in a disconnected fashion.
Changes to this data are hold in memory too.
Then you apply this changes to the underlying database.
So what you need is...
Components to establish a connection:
-------------------------------------
This components let you establish a connection with a
database and manage transactions.
*** Borland DataSnap ***
- Session and Database
- SQLConnection
- IBDatabase and IBTransaction
- ADOConnection
- Third party
*** Microsoft ADO.NET ***
- SQLConnection
- OleDbConnection
- ODBCConnection
- OracleConnection
- Third party
Borland DataSnap connection components have many similarities
between them but they all are different. All Microsoft ADO.NET
connection components implements the same interface.
Components to obtain data:
--------------------------
This components let you obtain a data set form a database
through one of the connection components.
*** Borland DataSnap ***
- Table, Query, StoredProc
- SQLTable, SQLQuery, SQLStoredProc, SQLDataSet
- IBTable, IBQuery, IBStoredProc, IBDataSet
- ADOTable, ADOQuery, ADOStoredProc, ADODataSet
- Third party
*** Microsoft ADO.NET ***
- SQLCommand, SQLDataReader
- OleDbCommand, OleDbDataReader
- ODBCCommand, OleDbDataReader
- OracleCommand, OracleDataReader
- Third party
Borland DataSnap components used to obtain data have many similarities between
them but they all are different. All Microsoft ADO.NET components used to obtain
data implements the same interface.
While some of the Borland DataSnap components used to obtain data let you obtain
a read-write, bi-directional data sets, all Microsoft components used to obtain
data provide read-only, forward-only data sets.
Components to provide data and resolve changes:
-----------------------------------------------
This components do two main things: provide data and resolve changes.
While Borland DataSnap component can perform it in a connected and disconnected fashion,
Microsoft ADO.NET component can perform it only in a disconnected fashion.
*** Borland DataSnap ***
- DataSetProvider
Provide:
- Connect it to a DataSet and it provides data to a ClientDataSet.
The DataSet must implements IProviderSupport interface.
- If the connected DataSet is a master DataSet it recognize the
master/detail relationship and provide data treating master record
and its detail records as a unit.
Resolve:
- It generates SQL statements on the fly using information form the
connected DataSet, even if you use JOINs.
- It treats master and details as a unit and generates transactions
for updating master and details in the same transaction and updates
data in the correct order (for inserts, first master and then details;
for deletes, first details and then master).
- It lets you configure how SQL statement should be generated.
- It manages concurrency.
*** Microsoft ADO.NET ***
- DataAdapter
Provide:
- You use a DataAdapter to fill a DataSet with records.
It could use embedded Command components or it could be connected
to your Command components.
- DataAdapter does not recognize master/detail relationships.
You need to use one DataAdapter for each table.
Resolve:
- It generates SQL statements on the fly using information from the SELECT statement,
but only for single tables.
- You must use one DataAdapter for each table so it updates only one table.
- It does not let you configure how SQL statement should be generated.
- It manages concurrency.
Components to hold data and changes in memory:
----------------------------------------------
This components hold data and changes to that data in memory.
Both can save its data to a file on disk and resolve updates later.
*** Borland DataSnap ***
- ClientDataSet
- It is aware of the DataSetProvider so they work together to apply
updates and reconcile update errors.
- Details are an extra field of the master. If you modify details
you also modify the master.
- It provides functionality to obtain details and BLOBs on demand.
*** Microsoft ADO.NET ***
- DataSet
- It doesn't aware of the DataAdapter so they don't work synchronized
and it doesn't know about update errors.
- Has a collection of tables and relations between this tables.
If you modify details you don't modify the master.
- It doesn't provide functionality to obtain details and BLOBs on demand.
Conclusions
===========
Borland DataSnap is a mature technology while Microsoft ADO.NET is an emerging,
new technology with an excellent starting point. You can accomplish the same task
with both technologies but you need to code a lot more with Microsoft ADO.NET.
With DataSetProvider and ClientDataSet from Borland DataSnap you get more than
with DataAdapter and DataSet from Microsoft ADO.NET.
Pablo Reyes
2008. július 18., péntek
Bitmap crossfade
Problem/Question/Abstract:
Well I have two pictures and I want to put the second on the first but with transparence. This could be usefull in many situations.
Answer:
The function combines two images in a crossfade image and returns it.
function returncross(srcbit, markbit: TBitmap; srcleft, srctop, markleft, marktop:
integer): TBitmap;
var
x, y: integer;
psrc, pmark: PByteArray;
begin
srcbit.PixelFormat := pf24bit;
markbit.PixelFormat := pf24bit;
for y := 0 to markbit.Height - 1 do
begin
if y + srctop psrc := srcbit.ScanLine[y + srctop];
pmark := markbit.ScanLine[y + marktop];
for x := 0 to srcbit.Width - 1 do
begin
if (y + srctop < srcbit.Height) and (x + srcleft < srcbit.Width) then
begin
psrc^[(x + srcleft) * 3] := (psrc^[(x + srcleft) * 3] + pmark^[(x + markleft)
* 3]) div 2;
psrc^[(x + srcleft) * 3 + 1] := (psrc^[(x + srcleft) * 3 + 1] + pmark^[(x +
markleft) * 3 + 1]) div 2;
psrc^[(x + srcleft) * 3 + 2] := (psrc^[(x + srcleft) * 3 + 2] + pmark^[(x +
markleft) * 3 + 2]) div 2;
end;
end;
end;
result := srcbit;
end;
srcbit - the first picture, on the foreground
markbit - the second picture, this picture is drawed transparently
srcleft - the left coordinate of the drawed markbit
srctop - the top coordinate ...
markleft and marktop are used if you want to take only a part of the picture (by default you can use 0)
Usage example:
Put on a form two tpicture objects (image1, image2). Load some bitmaps in them. In a button click event you can place the next line.
image1.Picture.Bitmap := putwatermark(image1.Picture.Bitmap, image2.Picture.Bitmap, 0,
0, 0, 0);
Well I have two pictures and I want to put the second on the first but with transparence. This could be usefull in many situations.
Answer:
The function combines two images in a crossfade image and returns it.
function returncross(srcbit, markbit: TBitmap; srcleft, srctop, markleft, marktop:
integer): TBitmap;
var
x, y: integer;
psrc, pmark: PByteArray;
begin
srcbit.PixelFormat := pf24bit;
markbit.PixelFormat := pf24bit;
for y := 0 to markbit.Height - 1 do
begin
if y + srctop psrc := srcbit.ScanLine[y + srctop];
pmark := markbit.ScanLine[y + marktop];
for x := 0 to srcbit.Width - 1 do
begin
if (y + srctop < srcbit.Height) and (x + srcleft < srcbit.Width) then
begin
psrc^[(x + srcleft) * 3] := (psrc^[(x + srcleft) * 3] + pmark^[(x + markleft)
* 3]) div 2;
psrc^[(x + srcleft) * 3 + 1] := (psrc^[(x + srcleft) * 3 + 1] + pmark^[(x +
markleft) * 3 + 1]) div 2;
psrc^[(x + srcleft) * 3 + 2] := (psrc^[(x + srcleft) * 3 + 2] + pmark^[(x +
markleft) * 3 + 2]) div 2;
end;
end;
end;
result := srcbit;
end;
srcbit - the first picture, on the foreground
markbit - the second picture, this picture is drawed transparently
srcleft - the left coordinate of the drawed markbit
srctop - the top coordinate ...
markleft and marktop are used if you want to take only a part of the picture (by default you can use 0)
Usage example:
Put on a form two tpicture objects (image1, image2). Load some bitmaps in them. In a button click event you can place the next line.
image1.Picture.Bitmap := putwatermark(image1.Picture.Bitmap, image2.Picture.Bitmap, 0,
0, 0, 0);
2008. július 17., csütörtök
How InteBase stores the passwords?
Problem/Question/Abstract:
How InteBase stores the passwords?
Answer:
InterBase stores all of it's user name information in a database called ISC4.gdb.
It is the "user" table that contains the "User_Name" and "Passwd" fields for each user.
To get a list of the valid user do a:
select user_name from users;
Just because a user is listed in the users table, doesn't mean that they have rights to access
any of the tables in InteBase. Access to each table is handled by sql grant and revokes and that data
is stored in the actual database not isc4.gdb.
The passwords for each user is stored in the passwd field and are encrypted.
The password is encrypted with the UNIX crypt routine (DES Salt).
That routine requires a salt which is always "9z". The resulting encrypted data
is striped of the "9z" (11 char. left) and crypted again with the same "9z" salt.
The result, once striped of the "9z" is the encrypted password as found in the ISC4.GDB database.
How InteBase stores the passwords?
Answer:
InterBase stores all of it's user name information in a database called ISC4.gdb.
It is the "user" table that contains the "User_Name" and "Passwd" fields for each user.
To get a list of the valid user do a:
select user_name from users;
Just because a user is listed in the users table, doesn't mean that they have rights to access
any of the tables in InteBase. Access to each table is handled by sql grant and revokes and that data
is stored in the actual database not isc4.gdb.
The passwords for each user is stored in the passwd field and are encrypted.
The password is encrypted with the UNIX crypt routine (DES Salt).
That routine requires a salt which is always "9z". The resulting encrypted data
is striped of the "9z" (11 char. left) and crypted again with the same "9z" salt.
The result, once striped of the "9z" is the encrypted password as found in the ISC4.GDB database.
2008. július 16., szerda
Load a JPG in a TImage, preserving the aspect ratio
Problem/Question/Abstract:
Load a JPG preservind the original aspect ratio of the JPG.
Answer:
procedure TForm1.Button1Click(Sender: TObject);
procedure CargaJPGProporcionado(Fichero: string;
const QueImage: TImage);
var
ElJPG: TJpegImage;
Rectangulo: TRect;
EscalaX,
EscalaY,
Escala: Single;
begin
ElJPG := TJPegImage.Create;
try
ElJPG.LoadFromFile(Fichero);
//Por defecto, escala 1:1
EscalaX := 1.0;
EscalaY := 1.0;
//Hallamos la escala de reducci�n Horizontal
if QueImage.Width < ElJPG.Width then
EscalaX := QueImage.Width / ElJPG.Width;
//La escala vertical
if QueImage.Height < ElJPG.Height then
EscalaY := QueImage.Height / ElJPG.Height;
//Escogemos la menor de las 2
if EscalaY < EscalaX then
Escala := EscalaY
else
Escala := EscalaX;
//Y la usamos para reducir el rectangulo destino
with Rectangulo do
begin
Right := Trunc(ElJPG.Width * Escala);
Bottom := Trunc(ElJPG.Height * Escala);
Left := 0;
Top := 0;
end;
//Dibujamos el bitmap con el nuevo tama?o en el TImage destino
with QueImage.Picture.Bitmap do
begin
Width := Rectangulo.Right;
Height := Rectangulo.Bottom;
Canvas.StretchDraw(Rectangulo, ElJPG);
end;
finally
ElJPG.Free;
end;
end; {De CargaJPGProporcionado}
begin
CargaJPGProporcionado('UnaFoto.jpg', Image1);
end;
Load a JPG preservind the original aspect ratio of the JPG.
Answer:
procedure TForm1.Button1Click(Sender: TObject);
procedure CargaJPGProporcionado(Fichero: string;
const QueImage: TImage);
var
ElJPG: TJpegImage;
Rectangulo: TRect;
EscalaX,
EscalaY,
Escala: Single;
begin
ElJPG := TJPegImage.Create;
try
ElJPG.LoadFromFile(Fichero);
//Por defecto, escala 1:1
EscalaX := 1.0;
EscalaY := 1.0;
//Hallamos la escala de reducci�n Horizontal
if QueImage.Width < ElJPG.Width then
EscalaX := QueImage.Width / ElJPG.Width;
//La escala vertical
if QueImage.Height < ElJPG.Height then
EscalaY := QueImage.Height / ElJPG.Height;
//Escogemos la menor de las 2
if EscalaY < EscalaX then
Escala := EscalaY
else
Escala := EscalaX;
//Y la usamos para reducir el rectangulo destino
with Rectangulo do
begin
Right := Trunc(ElJPG.Width * Escala);
Bottom := Trunc(ElJPG.Height * Escala);
Left := 0;
Top := 0;
end;
//Dibujamos el bitmap con el nuevo tama?o en el TImage destino
with QueImage.Picture.Bitmap do
begin
Width := Rectangulo.Right;
Height := Rectangulo.Bottom;
Canvas.StretchDraw(Rectangulo, ElJPG);
end;
finally
ElJPG.Free;
end;
end; {De CargaJPGProporcionado}
begin
CargaJPGProporcionado('UnaFoto.jpg', Image1);
end;
2008. július 15., kedd
Use a TPanel as a host for child windows (MDI simulation) (2)
Problem/Question/Abstract:
Does anyone know if it is possible to change the Parent of the Mainform's client window in such a way that MDI forms are correctly displayed within (for instance) a panel?
Answer:
Here is a class that we use to place forms in panels:
unit oSubForm;
interface
uses
Forms, Controls;
type
TSubForm = class(TForm)
public
procedure CreateParams(var Params: TCreateParams); override;
{ ... }
function SetUp: boolean; virtual;
function Activate: boolean; virtual;
function Deactivate: boolean; virtual;
function Validate: boolean; virtual;
function AddKnockOnChanged(Sender: TObject): boolean; virtual;
{ ... }
procedure SetAllChanged; virtual;
end;
type
TDfEE_Form = class(TForm)
private
protected
public
function PageValidate(nPage: integer): boolean; virtual;
published
end;
implementation
uses
WinTypes;
procedure TSubForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
WndParent := TWinControl(Owner).Handle;
Params.Style := WS_CHILD or WS_CLIPSIBLINGS or WS_CLIPCHILDREN;
end;
Align := alClient;
Parent := TWinControl(Owner);
end;
function TSubForm.SetUp: boolean;
begin
end;
function TSubForm.Activate: boolean;
begin
end;
function TSubForm.Deactivate: boolean;
begin
end;
procedure TSubForm.SetAllChanged;
begin
end;
function TSubForm.Validate: boolean;
begin
end;
function TSubForm.AddKnockOnChanged(Sender: TObject): boolean;
begin
end;
function TDfEE_Form.PageValidate(nPage: integer): boolean;
begin
result := True;
end;
end.
2008. július 14., hétfő
Read the properties of a Word document
Problem/Question/Abstract:
I want to read the properties of a MS Word document (the title). I use the function builtInDocumentProperties, but it returns a IDispatch Interface and I don't know how to get the value. If I write Document.BuiltInProperties[wdPropertyTitle].Value, I get an error.
Answer:
Variants are the easiest way of dealing with these properties.
{ ... }
var
Doc: OleVariant;
{ ... }
Doc := Word.ActiveDocument;
Doc.BuiltInDocumentProperties['Title'].Value := 'The title';
Doc.BuiltInDocumentProperties['Category'].Value := 'Category';
{ ... }
2008. július 13., vasárnap
How to fire the OnTitleClick event of a TDBGrid and discard the OnDblClick event at the same time
Problem/Question/Abstract:
How do I know if the user has double-clicked the titlebar or anywhere else on a DBGrid? I have a project that responds to both the OnTitleClick and the OnDblClick of the grid. Unfortunately if a user decides to double click on the title bar both events end up firing and that's not good. Any way to get the title bar OnTitleClick to fire and discard the double click? Or can I simply tell that it's pointing over the title bar?
Answer:
procedure TForm1.DBGrid1DblClick(Sender: TObject);
var
CurPos: TPoint;
begin
CurPos := DBGrid1.ScreenToClient(Mouse.CursorPos);
if (DBGrid1.MouseCoord(CurPos.X, CurPos.Y)).Y = 0 then
DBGrid1.Tag := 1;
end;
procedure TForm1.DBGrid1TitleClick(Column: TColumn);
begin
if DBGrid1.Tag = 0 then
beep;
DBGrid1.Tag := 0;
end;
2008. július 12., szombat
InterBase: "lock manager out of room"
Problem/Question/Abstract:
My application that runs against InterBase 5 throws an exception "sql code -104, lock manager out of room". How can I increase the lock space?
Answer:
Go to the interbase/bin directory (Windows) or /usr/interbase (Unix) and locate the configuration file isc_config. By default your configuration file will look like this:
#V4_LOCK_MEM_SIZE������� 98304
#ANY_LOCK_MEM_SIZE������ 98304
#V4_LOCK_SEM_COUNT������ 32
#ANY_LOCK_SEM_COUNT����32
#V4_LOCK_SIGNAL�������� 16
#ANY_LOCK_SIGNAL�������� 16
#V4_EVENT_MEM_SIZE������ 32768
#ANY_EVENT_MEM_SIZE����� 32768
I increased the V4_LOCK_MEM_SIZE entry from 98304 to 198304 and things were fine then.
!!! Important !!!
By default all lines in the config file are commented out with the leading # sign. Make sure to remove the # sign in any line that you change - the default config file just shows the default parameters.
2008. július 11., péntek
Karp-Rabin string searching
Problem/Question/Abstract:
Karp-Rabin string searching
Answer:
Do you need a fast routine that searches a string within a string? Try the Karp-Rabin algorithm:
function search(pat: PATTERN; Text: Text): integer;
const
b = 131;
var
hpat, htext, Bm, j, m, n: integer;
found: Boolean;
begin
found := False;
search := 0;
m := length(pat);
if m = 0 then
begin
search := 1;
found := true
end;
Bm := 1;
hpat := 0;
htext := 0;
n := length(Text);
if n >= m then
{*** preprocessing ***}
for j := 1 to m do
begin
Bm := Bm * b;
hpat := hpat * b + ord(pat[j]);
htext := htext * b + ord(Text[j])
end;
j := m;
{*** search ***}
while not found do
begin
if (hpat = htext) and (pat = substr(Text, j - m + 1, m)) then
begin
search := j - m + 1;
found := true
end;
if j < n then
begin
j := j + 1;
htext := htext * b - ord(Text[j - m]) * Bm + ord(Text[j])
end
else
found := true
end
end;
2008. július 10., csütörtök
How to detect if a TreeView has a scrollbar and how to change its position?
Problem/Question/Abstract:
How to detect if a TreeView has a scrollbar?
How to change the scrollbar in a TreeView (if it has one)?
Answer:
procedure TForm1.FormMouseWheelDown(Sender: TObject;
Shift: TShiftState;
MousePos: TPoint;
var Handled: Boolean);
var
iMin, iMax: Integer;
bTreeViewVertScrollBarVisible: Boolean;
begin
bTreeViewVertScrollBarVisible := True;
GetScrollRange(Form1.TreeView1.Handle, SB_VERT, iMin, iMax);
if iMin = iMax then
bTreeViewVertScrollBarVisible := False; // No scrollbar visible
if bTreeViewVertScrollBarVisible then
begin
iPos := GetScrollPos(Form1.TreeView1.Handle, SB_VERT);
SetScrollPos(Form1.TreeView1.Handle, SB_VERT, iPos + 1, True);
// Don't set Handled to True!
// If you do that then ONLY the scrollbar changes but NOT the
// content of the TreeView!
end;
end;
2008. július 9., szerda
How do I make transparent forms?
Problem/Question/Abstract:
How do I make transparent forms?
Answer:
You need to override the CreateParam function and there add WS_EX_TRANSPARENT
to the Params.ExStyle.
Set the form's canvas' Brush.Style to bsClear, as shown in this example:
type
TMyForm = class(TForm)
procedure FormCreate(Sender: TObject);
procedure CreateParams(var Params: TCreateParams); override;
end;
procedure TMyForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
// this is the important constant!
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;
procedure TMyForm.FormCreate(Sender: TObject);
begin
inherited;
Canvas.Brush.Style := bsClear;
end;
2008. július 8., kedd
Control the AutoPlay feature
Problem/Question/Abstract:
Control the AutoPlay feature
Answer:
You know how to stop Windows' [CD-ROM] AutoPlay from occurring by holding SHIFT or by changing Windows settings.
Here's how to detect whether an AutoPlay is about to occur from your application and then either allowing or stopping it.
We're going to ask Windows to send us a message when the AutoPlay is about to occur. In order to catch this message, first of all we have to override our default Windows message handler -- "WndProc()."
You can do this by inserting the following code in your form's (named "Form1" for example) public declarations section:
MsgID_QueryCancelAutoPlay: Word;
procedure WndProc(var Msg: TMessage); override;
Now, type in the following code in the "implementation" section (again, assuming that your form is named "Form1") to actually handle the Windows messages. As you can see, we're only interested in catching "QueryCancelAutoPlay" messages, so we'll let the default (or the inherited) "WndProc()" handle all other messages.
procedure TForm1.WndProc(var Msg: TMessage);
begin
if Msg.Msg = MsgID_QueryCancelAutoPlay then
begin
{ set Msg.Result
to 1 to stop AutoPlay or
to 0 to continue with AutoPlay }
Msg.Result := 1;
end
else
inherited WndProc(Msg);
end;
Finally, we have to ask Windows to actually send a "QueryCancelAutoPlay" message to our message handler by inserting the following code in the "FormCreate()" event (click on your form, go to the "events" tab in the "Object Inspector" and double click on "Create"):
MsgID_QueryCancelAutoPlay := RegisterWindowMessage('QueryCancelAutoPlay');
2008. július 7., hétfő
How to create transparent bitmaps
Problem/Question/Abstract:
What I'd like to see is an image component which can be positioned over other windows/ images and use their respective canvases to fill-in its transparent color. My question is, does this component use as its background the bitmap of the window(s)/ device context(s) which it covers?
Answer:
Here's an excerpt of code that draws a transparent bitmap. I still use a mask, but the program does it for me. And, yes it is easy to draw over other graphic controls, all you need to do is to grab the form's canvas before drawing.
{ ... }
if (FTmpComp.Transparent) and (FTmpComp.CellMask.Width = 0) then
{need to draw w/out cellmask}
try
{Setup temp bitmaps}
TmpBitmap := TBitmap.Create;
TmpBitmap.Height := FTmpComp.Height;
TmpBitmap.Width := FTmpComp.Width;
MskBitmap := TBitmap.Create;
MskBitmap.Height := FTmpComp.Height;
MskBitmap.Width := FTmpComp.Width;
MskBitmap.Monochrome := True;
ImgBitmap := TBitmap.Create;
ImgBitmap.Height := FTmpComp.Height;
ImgBitmap.Width := FTmpComp.Width;
{Create Mask}
MskBitmap.Canvas.Brush.Color := clWhite;
MskBitmap.Canvas.BrushCopy(DRect, FTmpComp.CellPicture, SRect,
FTmpComp.CellPicture.Canvas.Pixels[0, 0]);
MskBitmap.Canvas.CopyMode := cmSrcInvert;
MskBitmap.Canvas.CopyRect(DRect, FTmpComp.CellPicture.Canvas, SRect);
{Create 'blacked out' image}
ImgBitmap.Canvas.CopyMode := cmNotSrcCopy;
ImgBitmap.Canvas.CopyRect(Drect, MskBitmap.Canvas, DRect);
ImgBitmap.Canvas.CopyMode := cmSrcAnd;
ImgBitmap.Canvas.CopyRect(DRect, FTmpComp.CellPicture.Canvas, SRect);
{Copy background from FPicture into the temp bitmap}
TmpBitmap.Canvas.CopyMode := cmSrcCopy;
TmpBitmap.Canvas.CopyRect(DRect, FPicture.Canvas, FRect);
{AND the mask into the background to provide 'cut-out'}
TmpBitmap.Canvas.CopyMode := cmSrcAnd;
TmpBitmap.Canvas.CopyRect(DRect, MskBitmap.Canvas, DRect);
{PAINT the CellPicture into the hole}
TmpBitmap.Canvas.CopyMode := cmSrcPaint;
TmpBitmap.Canvas.CopyRect(DRect, ImgBitmap.Canvas, DRect);
{finally copy the temp bitmap onto the main canvas}
Canvas.CopyMode := cmSrcCopy;
Canvas.CopyRect(FRect, TmpBitmap.Canvas, DRect);
{mark the Cell as having been updated}
FTmpComp.IsDirty := False;
finally
{free the bitmaps}
TmpBitmap.Free;
MskBitmap.Free;
ImgBitmap.Free;
end;
2008. július 6., vasárnap
How to use TStrings.DelimitedText to separate a comma delimited string
Problem/Question/Abstract:
I am trying to use TStrings.DelimitedText to separate a comma delimited string. The trouble is (some of my strings contain spaces, for example a person's address), with TStrings.DelimitedText it seems to have the space character set as a delimiter as well as the character that I specify. How do I stop it from doing this?
Answer:
The substrings in the comma-separated list have to be enclosed in the TStrings.QuoteChar for this to work properly. The way TStrings.SetDelimitedText has been written it will not only break on the Delimiter character but also on any character in the range #1..' ' when they appear outside a quoted string. The SplitString routine below does not suffer from this problem but it does not handle delimiters inside quoted strings.
{Function IScan
Parameters:
ch: Character to scan for
S : String to scan
fromPos: first character to scan
Returns: position of next occurence of character ch, or 0, if none found
Description: Search for next occurence of a character in a string.
Error Conditions: none
Created: 11/27/96 by P. Below}
function IScan(ch: Char; const S: string; fromPos: Integer): Integer;
var
i: Integer;
begin
Result := 0;
for i := fromPos to Length(S) do
begin
if S[i] = ch then
begin
Result := i;
Break;
end;
end;
end;
{Procedure SplitString
Parameters:
S: String to split
separator: character to use as separator between substrings
substrings: list to take the substrings
Description:
Isolates the individual substrings and copies them into the passed stringlist. Note that we only add to the list, we do not clear it first! If two separators follow each other directly an empty string will be added to the list.
Error Conditions:
will do nothing if the stringlist is not assigned
Created: 08.07.97 by P. Below}
procedure SplitString(const S: string; separator: Char; substrings: TStringList);
var
i, n: Integer;
begin
if Assigned(substrings) and (Length(S) > 0) then
begin
i := 1;
repeat
n := IScan(separator, S, i);
if n = 0 then
n := Length(S) + 1;
substrings.Add(Copy(S, i, n - i));
i := n + 1;
until
i > Length(S);
end;
end;
2008. július 5., szombat
How to count words in a TRichEdit
Problem/Question/Abstract:
How to count words in a TRichEdit
Answer:
function GetWord: boolean;
var
s: string; {presume no word > 255 chars}
c: char;
begin
result := false;
s := ' ';
while not EOF(f) do
begin
read(f, c);
if not (c in ['a'..'z', 'A'..'Z' {,... etc.}]) then
break;
s := s + c;
end;
result := (s <> ' ');
end;
procedure GetWordCount(TextFile: string);
begin
Count := 0;
assignfile(f, TextFile);
reset(f);
while not EOF(f) do
if GetWord then
inc(Count);
closefile(f);
end;
2008. július 3., csütörtök
How to implement an OnMouseDown event for the buttons of a TRadioGroup
Problem/Question/Abstract:
I have created a decendant of TRadioGroup called TSuperRadioGroup. With this new control, I have surfaced the onmousedown event. But the event is only triggered when the mouse goes down on the border or caption of the Group, not the Radio Buttons themselves.
Answer:
The solution I use is to register a window procedure for the radiobuttons. You can trap the Windows messages there.
procedure TSuperRadioGroup.RegisterWndProc;
var
BtnHnd: hWnd;
ItrBtn: Integer;
begin
inherited;
HasWndProc := True;
BtnHnd := GetWindow(Handle, GW_CHILD);
ItrBtn := 0;
while BtnHnd > 0 do
begin
if GetWindowLong(BtnHnd, GWL_USERDATA) <> 0 then
raise Exception.Create('Userdata may not be used');
ButtonHandle[ItrBtn] := BtnHnd;
OrigWndProc[ItrBtn] := GetWindowLong(BtnHnd, GWL_WNDPROC);
SetWindowLong(BtnHnd, GWL_USERDATA, Longint(self));
SetWindowLong(BtnHnd, GWL_WNDPROC, Longint(@RadioBtnWndProc));
Inc(ItrBtn);
BtnHnd := GetWindow(BtnHnd, GW_HWNDNEXT);
end;
end;
In the RadioBtnWndProc window procedure you can use this code to get at the radiogroup object and the specific button:
Obj := TObject(GetWindowLong(WndHnd, GWL_USERDATA));
if Obj is TSuperRadioGroup then
begin
RadioGrp := TSuperRadioGroup(Obj);
for ItrBtn := 0 to RadioGrp.Items.Count - 1 do
begin
if WndHnd = RadioGrp.ButtonHandle[ItrBtn] then
begin
OrigWndProc := RadioGrp.OrigWndProc[ItrBtn];
break;
end;
end;
end;
If the message is not completely handled, you need to call the original wndproc at the end of your specialized wndproc:
Result := CallWindowProc(Pointer(OrigWndProc), WndHnd, Msg, WParam, LParam);
2008. július 2., szerda
How to check the timer progress
Problem/Question/Abstract:
I have a TTimer that is programmed to show a message box after 5 minutes. Is there a way to check how much of the timer interval has passed after a certain time?
Answer:
You will have to use a timer that occurs every second and increments a variable (even its own Tag property).
TForm1.Timer1Timer(Sender: ...);
begin
Timer1.tag := Timer1.tag + 1;
if Timer1.tag = 300 then
begin
Timer1.enabled := false;
{If you leave, it will show a message every 5 minutes, resulting in many windows;
suspend counting for the period showing the message}
Timer1.tag := 0;
showmessage('5 Minutes!!!');
Timer1.enabled := true;
end;
end;
TForm1.button1click(....);
begin
showmessage('Only ' + inttostr(Timer1.tag div 60) + ' minutes and ' +
inttostr(Timer1.tag mod 60) + ' seconds have passed');
end;
2008. július 1., kedd
Working with delphi menus
Problem/Question/Abstract:
How to hack delphi environment (IDE)?
Answer:
This is just a sample application for interacting with Delphi IDE.U can use it more extensively. For invoking other applications u just need to change the menuclick event handler.
Given below is the full code. Compile this unit into a package and install the same.
unit SubhaExp;
interface
uses Windows, Menus, ExtCtrls, SysUtils, Forms, ToolsApi;
type
TSubhaMenu = class
private
FMainMenu: TMainMenu;
FFileMenu: TMenuItem;
FGiriMenu: TMenuItem;
procedure OnMenuItemClick(Sender: TObject);
public
procedure AddMenuItem;
procedure RemoveMenuItem;
end;
var
FSubhaMenu: TSubhaMenu;
procedure Register;
implementation
procedure TSubhaMenu.AddMenuItem;
var
i: Integer;
begin
FMainMenu := (BorlandIDEServices as INTAServices).MainMenu;
for i := 0 to FMainMenu.Items.Count - 1 do
begin
if AnsiSameCaption(FmainMenu.items[i].Caption, 'File') then
begin
FFileMenu := FMainMenu.items[i];
Break;
end;
end;
FGiriMenu := TMenuItem.Create(FFileMenu);
FGiriMenu.Caption := 'Subha IDE Services';
FGiriMenu.OnClick := OnMenuItemClick;
for i := 0 to FFileMenu.count - 1 do
begin
if FFileMenu.Items[i].isLine then
begin
FFileMenu.Insert(i, FGiriMenu);
Break;
end;
end;
end;
procedure TSubhaMenu.RemoveMenuItem;
var
i: Integer;
begin
for i := 0 to FFileMenu.Count - 1 do
begin
if AnsiSameCaption(FFileMenu.Items[i].Caption, 'Subha IDE Services') then
begin
FFileMenu.Remove(FFileMenu.items[i]);
Break;
end;
end;
end;
procedure TSubhaMenu.OnMenuItemClick(Sender: TObject);
begin
Application.MessageBox(PChar('This Is only a Simple Example' +
' to Work With Delphi IDE ' + #13#10 + ' For Further Details On This Contact' +
#13#10#13#10 + ' Subha@botree.co.in'), PChar('Message From Subha'), MB_OK);
end;
procedure Register;
begin
FSubhaMenu.AddMenuItem;
Application.MessageBox('Subha Narayanan Has Hacked Your ' +
' Delphi Environment !!! ' + #13#10 +
' See You Soon With Lot More Goodies !!! ' +
#13#10 + ' CopyRight (c) 2001, Subha Narayanan. ',
' Welcome To Delphi ', MB_SYSTEMMODAL);
end;
initialization
FSubhaMenu := TSubhaMenu.Create;
finalization
FSubhaMenu.RemoveMenuItem;
FSubhaMenu.Free;
end.
Feliratkozás:
Bejegyzések (Atom)