Pascal Newsletter #16
The full source code examples of this issue are available for download.
![]() |
![]() |
Pascal Newsletter #16 - 09-FEB-2001 INDEX 1. A FEW WORDS FROM THE EDITOR 2. NON-RECTANGULAR SHAPES - Part II - INTRODUCTION - BASIC REALIZATION OF "SKINS" - THE BACKGROUND - THE SHAPE - STEP BY STEP - THE CONTROLS - GOING FURTHER - WHAT OTHERS DO 3. INDISTINGUISHABLE GRAY BLOBS IN DISABLED MENU ITEMS 4. COLOR BUTTON 5. WORKSHOP 6. KYLIX LICENSE 7. LINKS ________________________________________________________________________ 1. A FEW WORDS FROM THE EDITOR In this issue I'm glad to present the long awaited second part of the article NON-RECTANGULAR SHAPES, by Alirio Gavidia, whose first part was published in the 13th issue: http://www.latiumsoftware.com/en/pascal/0013.php I'd like to remind you that the JfControls Library Drawing (or "raffle" if you like it) is still going on. If you haven't done it so far, take a look at the demo and you'll see that the prize is not any trinket, but rather a valuable product that implements an innovative concept and that can help you create visually appealing applications in a snap. Participation is free. All you have to do is download and install the trial version of the library (available for Delphi and C++ Builder, both in versions 3, 4 and 5) to get the Key Number (a unique number that identifies each installation of the library) that you need in order to be able to fill in the registration form. If you don't want to install the trial version of the library, the latest version of JfSetup will give you a Key Number if you install only the demo (it's worth seing it and doesn't require the library to be installed for the executable to run). For more information about the drawing: http://www.latiumsoftware.com/jfcontrols/index.php?lang=en Starting from the next issue, this newsletter will be published with the source code examples attached (usually below 30K). The past issues of this newsletter and their examples can be found in our web site and are available for download: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php#past Best regards, Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Library. Multi-language. Multi-appearance. Skins. Privileges. More than 40 integrated and customizable components. Impressive GUI. Centralized resources administration. Multiple programming problems solved. For Delphi 3-7 and C++ Builder 3-6. http://www.jfactivesoft.com/ ________________________________________________________________________ 2. NON-RECTANGULAR SHAPES - Part II Copyright (c) 2001 Alirio Gavidia <alirio@gavidia.org> To see more of my articles visit: Programación Orientada en Delphi (site in Spanish) http://www.gavidia.org/pod/ Translated by Ernesto De Spirito INTRODUCTION ------------ This is the second part of an article oriented to the use of non- rectangular shapes with Delphi under Windows. In the previous part we gave a non-rectangular shape to a form creating a hands watch as an example. In this part the intention is to be able to create forms whose shape are defined from some file external to the application. This behavior corresponds to the widely spread use of "Skins". For example "Windows Media Player". BASIC REALIZATION OF "SKINS" ---------------------------- The first approach that we introduce here not only allows us to define the background, but also the shape of the form. In the market there is a VCL component that does this job and more. The referred component makes use of two BMP files, the first one with the figure in black and white and the second with the background in full color. In the present essay both characteristics are combined in one single file simply taking the color of the first pixel as the transparent color, actually like the color the that defines the limits of the shape (there is a little difference). The rest of the image, inside the shape, is taken as the background of the form. Note: The use of a BMP has many cons. The major ones: size and scalability. For the first maybe the alternative would be a JPG, but the compression process alters the frontier of the shape generating intermediate colors, resulting in a blurred frontier. On the other hand we have the GIF format, but they have been banished from my routines, libraries and functions because certain company intends to get paid royalties for the use of the compression algorithms used in GIF files. Regarding scalability, one alternative is the use of a Metafile, better yet I suspect that the metafiles are definitions of regions completely analogous to those used here. However, I'll leave the study of Metafiles for another occasion. A complete version of a "Skinner" requires to be able to define the background image of the form, its shape and also the one of each present control. We'll concentrate in the form leaving the controls aside. THE BACKGROUND -------------- The problem of drawing the background is addressable in two ways as far as I know. One is intercepting some Windows message to draw on the canvas of the background (like some VCL controls do to place backgrounds in MDI and non-MDI forms) and the other is placing a TImage control as the last control in the form's client area. I'll choose the latter way because it'll make possible the use of some TImage events. THE SHAPE --------- The problem of the shape basically resides in creating a region of a given shape. The determinant shape I have assumed is given thru a bitmap, so that for example a bitmap consisting of a blue circle over a white background must generate a form visible like a circle. The shape can be anything like the face of Mickey Mouse or the silhouette of a spider. For this purpose the bitmap has to be read to generate a polygonal shape out of it. This will give us freedom, but eliminates much the possibilities of scalability. STEP BY STEP ------------ In general terms, the algorithm goes like this: 1.- We adjust the form to the dimensions of the BMP file. This is simple. For the purpose, the following two lines will do it: // Ajustar el área / to Ajust the area ClientWidth := Image1.Width; ClientHeight:= Image1.Height; 2.- We read the first pixel of the form and take it as "transparent". 3.- We search for the first "non-transparent" pixel and from this point we generate a polygon with the silhouette of the "non-transparent" part (see GenRegion in the file ffigura.pas). 4.- We get rid of the title bar (if it's still bothering us). To do it: BorderStyle := bsNone; // Titulo estorba / no Title 5.- We change the shape of the form according to the polygon. To do it we make use of the "CreatePolygonRgn" and "SetWindowRgn" API functions (see ActiveSkin in ffigura.pas). Unlike the example of the first part of this article, in this one we don't define an elliptical (circular) region. 6.- We set from the TImage the necessary events for dragging the form like we did in the example of the first article. Except for the third step, nothing seems too difficult. To generate the polygonal region we just look for the frontier of the figure, then we advance thru all the outline of the figure until we reach the starting point (or run out of memory, in this case 1024 points). This series of points define a polygon used to generate a region with the function "CreatePolygonRgn". Then we use "SetWindowRgn" y we get the form with the given shape. It works but it has limitations: our figure must have only one frontier, therefore it can't be a figure with a hole in the middle (in any case the algorithm will ignore the hole in the middle). As usual this article is accompanied with an example to illustrate the points referred here. Note: The algorithm that scans the silhouette given in the bitmap is relatively slow and coarse, but is maintainable and performs a certain optimization with shapes that aren't very complex. There is a given limit since an array in memory is generated when we still don't know how many points will define the figure. It's up to the user to vary this limit to his/her convenience. THE CONTROLS ------------ The example corresponding to this article also contains buttons with shapes. In design mode they are square because they are of TBitBtn type, but in the OnCreate event of the form each button is applied the shape of the graphics it contains in the same way as we did with the form in the ActiveSkin procedure: procedure TForm1.FormCreate(Sender: TObject); Var hRegion : Thandle; Par : Array [0..1024] of TPoint; // Data -> region Cnt : Integer; begin Cnt := GenRegion(Par, BitBtn1.Glyph, (BitBtn1.Width - BitBtn1.Glyph.Width+1) div 2, (BitBtn1.Height - BitBtn1.Glyph.Height+1) div 2); hRegion := CreatePolygonRgn(Par, Cnt, ALTERNATE); SetWindowRgn(BitBtn1.handle, hRegion, true); ... This shows the definition of regions for controls other than forms. GOING FURTHER ------------- The problem of the BMP size remains latent, but we could pack the BMP and use it packed like Winamp does. There are two ways for this approach: we can use some almost-proprietary, semi-secret or little- common algorithm (inevitably I think of the Windows Media Player) or something like the zip. This last choice has the advantage that we are not limited regarding its use. You can find more information to know how to handle in the web site of Latium Software: Pascal Newsletter #5 http://www.latiumsoftware.com/en/pascal/0005.php http://www.latiumsoftware.com/download/p0005.zip (source code) Pascal Newsletter #6 http://www.latiumsoftware.com/en/pascal/0006.php http://www.latiumsoftware.com/download/p0006.zip (source code) If the images are simple, like the ones I used in the example, you can save them using 16 colors and RLE 4 encoding (this can be done for example with the old but beloved Borland Resource Workshop that comes in the Delphi installation CD). WHAT OTHERS DO -------------- We should separate the areas of the form in client and non-client, and also considerer the captions. Eventually we could use semi- transparencies, what will become more common every day (thanks to Steve Jobs for the received favors, although not all of us use a Mac ;) There's a library out there that considers shadows, animation, morphing and even a scripting language, but they go beyond the purpose of this article. -------------------------- The source code that accompanies this article can be downloaded from: http://www.latiumsoftware.com/download/p0016.zip ________________________________________________________________________ 3. INDISTINGUISHABLE GRAY BLOBS IN DISABLED MENU ITEMS By Vladimir S. <shvetadvipa@mtu-net.ru> If you want to see more interesting Delphi tips and tricks you can go to http://www.webmachine.ru/delphi where you can find the file KULIBA.CHM by Valentin Ozerov that contains more than 1,500 tips and tricks (in Russian language). Ozerov collected solutions from Russian and other countries' programmers. There are some of my solutions too. In one of them I describe how to conquer a glitch for certain disabled menu items, which is the subject of this article. Probably you have noticed that when you use images in menu items and toolbar buttons, they look like indistinguishable gray blobs when these components are disabled (see bmf.gif and btf.gif in the archive that accompanies this newsletter). One workaround for this problem is modifying the VCL sources (you need to have the Enterprise edition for this). First of all, find the file ImgList.pas, usually located in the folder ${Delphi}\Source\Vcl where ${Delphi} is the path where you installed Delphi, like for example C:\Program Files\Borland\Delphi5 Then locate the implementation of the TCustomImageList.DoDraw method. Comment it out (to keep the old version just in case) and copy the following code: procedure TCustomImageList.DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean); procedure ScrambleBitmap(var BMP: TBitmap); const RMask = $0000FF; RAMask = $FFFF00; GMask = $00FF00; GAMask = $FF00FF; BMask = $FF0000; BAMask = $00FFFF; var R,C: integer; Color: LongWord; begin with Bmp.Canvas do begin for C:= 0 to Bmp.Height - 1 do for R:= 0 to Bmp.Width - 1 do begin Color:= Pixels[R,C]; if (Color = 0) or (Color = $FFFFFF) then Continue; if (Color and RMask > $7F) and (Color and RAMask > $0) or (Color and GMask > $7F00) and (Color and GAMask > $0) or (Color and BMask > $7F000) and (Color and BAMask > $0) then Pixels[R,C]:= $FFFFFF else Pixels[R,C]:= 0; end; end; end; const ROP_DSPDxax = $00E20746; var R: TRect; DestDC, SrcDC: HDC; begin if HandleAllocated then begin if Enabled then ImageList_DrawEx(Handle, Index, Canvas.Handle, X, Y, 0, 0, GetRGBColor(BkColor), GetRGBColor(BlendColor), Style) else begin if FMonoBitmap = nil then begin FMonoBitmap:= TBitmap.Create; with FMonoBitmap do begin // Monochrome:= True; commented!!! Width:= Self.Width; Height:= Self.Height; end; end; { Store masked version of image temporarily in FBitmap } FMonoBitmap.Canvas.Brush.Color:= clWhite; FMonoBitmap.Canvas.FillRect(Rect(0, 0, Self.Width, Self.Height)); ImageList_DrawEx(Handle, Index, FMonoBitmap.Canvas.Handle, 0, 0, 0, 0, CLR_DEFAULT, 0, ILD_NORMAL); ScrambleBitmap(FMonoBitmap); // call patch R:= Rect(X, Y, X+Width, Y+Height); SrcDC:= FMonoBitmap.Canvas.Handle; BitBlt(SrcDC, 0, 0, Width, Height, SrcDC, 0, 0, DSTINVERT); // add!!! { Convert Black to clBtnHighlight } Canvas.Brush.Color:= clBtnHighlight; DestDC:= Canvas.Handle; Windows.SetTextColor(DestDC, clWhite); Windows.SetBkColor(DestDC, clBlack); BitBlt(DestDC, X+1, Y+1, Width, Height, SrcDC, 0, 0, ROP_DSPDxax); { Convert Black to clBtnShadow } Canvas.Brush.Color:= clBtnShadow; DestDC:= Canvas.Handle; Windows.SetTextColor(DestDC, clWhite); Windows.SetBkColor(DestDC, clBlack); BitBlt(DestDC, X, Y, Width, Height, SrcDC, 0, 0, ROP_DSPDxax); end; end; end; Now you need to compile this unit. This is normally done by placing the ImgList.pas file in the same directory where your project resides and then compiling the project. This will produce the file ImgList.dcu that you have to copy for example in the ${Delphi}\Lib folder, so that it applies to all applications that you compile in the future. The graphics that result with this patch look a lot better (see bmr.gif and btr.gif in the archive that accompanies this newsletter). ________________________________________________________________________ 4. COLOR BUTTON By Vladimir S. <shvetadvipa@mtu-net.ru> When you read books by Charlie Calvert, Tom Swan and others you can find something like this "...you can't change the color of a Button or BitBtn because Windows draws it". Well... you can't do it, but if you wish to do it very much, you can! I developed a ColorBtn component. It can use any color you want (see colorbtn.gif in the zip archive that accompanies this newsletter). Here's the code: unit ColorBtn; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TColorBtn = class(TButton) private IsFocused: boolean; FCanvas: TCanvas; F3DFrame: boolean; FButtonColor: TColor; procedure Set3DFrame(Value: boolean); procedure SetButtonColor(Value: TColor); procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM; procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK; procedure DrawButtonText(const Caption: string; TRC: TRect; State: TButtonState; BiDiFlags: Longint); procedure CalcuateTextPosition(const Caption: string; var TRC: TRect; BiDiFlags: Longint); protected procedure CreateParams(var Params: TCreateParams); override; procedure SetButtonStyle(ADefault: boolean); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property ButtonColor: TColor read FButtonColor write SetButtonColor default clBtnFace; property Frame3D: boolean read F3DFrame write Set3DFrame default False; end; procedure Register; implementation { TColorBtn } constructor TColorBtn.Create(AOwner: TComponent); begin Inherited Create(AOwner); FCanvas:= TCanvas.Create; FButtonColor:= clBtnFace; F3DFrame:= False; end; destructor TColorBtn.Destroy; begin FCanvas.Free; Inherited Destroy; end; procedure TColorBtn.CreateParams(var Params: TCreateParams); begin Inherited CreateParams(Params); with Params do Style:= Style or BS_OWNERDRAW; end; procedure TColorBtn.Set3DFrame(Value: boolean); begin if F3DFrame <> Value then F3DFrame:= Value; end; procedure TColorBtn.SetButtonColor(Value: TColor); begin if FButtonColor <> Value then begin FButtonColor:= Value; Invalidate; end; end; procedure TColorBtn.WMLButtonDblClk(var Message: TWMLButtonDblClk); begin Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos)); end; procedure TColorBtn.SetButtonStyle(ADefault: Boolean); begin if IsFocused <> ADefault then IsFocused:= ADefault; end; procedure TColorBtn.CNDrawItem(var Message: TWMDrawItem); var RC: TRect; Flags: Longint; State: TButtonState; IsDown, IsDefault: Boolean; DrawItemStruct: TDrawItemStruct; begin DrawItemStruct:= Message.DrawItemStruct^; FCanvas.Handle:= DrawItemStruct.HDC; RC:= ClientRect; with DrawItemStruct do begin IsDown:= ItemState and ODS_SELECTED <> 0; IsDefault:= ItemState and ODS_FOCUS <> 0; if not Enabled then State:= bsDisabled else if IsDown then State:= bsDown else State:= bsUp; end; Flags:= DFCS_BUTTONPUSH or DFCS_ADJUSTRECT; if IsDown then Flags:= Flags or DFCS_PUSHED; if DrawItemStruct.ItemState and ODS_DISABLED <> 0 then Flags:= Flags or DFCS_INACTIVE; if IsFocused or IsDefault then begin FCanvas.Pen.Color:= clWindowFrame; FCanvas.Pen.Width:= 1; FCanvas.Brush.Style:= bsClear; FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom); InflateRect(RC, -1, -1); end; if IsDown then begin FCanvas.Pen.Color:= clBtnShadow; FCanvas.Pen.Width:= 1; FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom); InflateRect(RC, -1, -1); if F3DFrame then begin FCanvas.Pen.Color:= FButtonColor; FCanvas.Pen.Width:= 1; DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags); end; end else DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags); FCanvas.Brush.Color:= FButtonColor; FCanvas.FillRect(RC); InflateRect(RC, 1, 1); if IsFocused then begin RC:= ClientRect; InflateRect(RC, -1, -1); end; if IsDown then OffsetRect(RC, 1, 1); FCanvas.Font:= Self.Font; DrawButtonText(Caption, RC, State, 0); if IsFocused and IsDefault then begin RC:= ClientRect; InflateRect(RC, -4, -4); FCanvas.Pen.Color:= clWindowFrame; Windows.DrawFocusRect(FCanvas.Handle, RC); end; FCanvas.Handle:= 0; end; procedure TColorBtn.CalcuateTextPosition(const Caption: string; var TRC: TRect; BiDiFlags: Integer); var TB: TRect; TS, TP: TPoint; begin with FCanvas do begin TB:= Rect(0, 0, TRC.Right + TRC.Left, TRC.Top + TRC.Bottom); DrawText(Handle, PChar(Caption), Length(Caption), TB, DT_CALCRECT or BiDiFlags); TS := Point(TB.Right - TB.Left, TB.Bottom - TB.Top); TP.X := ((TRC.Right - TRC.Left) - TS.X + 1) div 2; TP.Y := ((TRC.Bottom - TRC.Top) - TS.Y + 1) div 2; OffsetRect(TB, TP.X + TRC.Left, TP.Y + TRC.Top); TRC:= TB; end; end; procedure TColorBtn.DrawButtonText(const Caption: string; TRC: TRect; State: TButtonState; BiDiFlags: Integer); begin with FCanvas do begin CalcuateTextPosition(Caption, TRC, BiDiFlags); Brush.Style:= bsClear; if State = bsDisabled then begin OffsetRect(TRC, 1, 1); Font.Color:= clBtnHighlight; DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags); OffsetRect(TRC, -1, -1); Font.Color:= clBtnShadow; DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags); end else DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags); end; end; procedure Register; begin RegisterComponents('Samples', [TColorBtn]); end; end. You can see that it's not very difficult. Note: Windows draws the button and ColorBtn paints it. ________________________________________________________________________ 5. WORKSHOP In the last issue I showed a simple way to put checkboxes for boolean fields in a DBGrid without creating a new component. Now, to those of you who are learning about component creation and have some time for this, I'd like to propose as an excercise that you write a TDBGrid descendant that handles boolean fields. I'll include all the solutions I receive in the attached archive of the first issue of March of this newsletter. ________________________________________________________________________ 6. KYLIX LICENSE I'm writing this article because some of you expressed concerns about the dual license of CLX, thinking that to deploy application or component commercially one has to pay a CLX commercial license apart from the Kylix license. Before continuing, I'd like to thank Thomas J. Theobald (Product Manager - RAD Tools - Borland Software Corporation) for very kindly answering my questions regarding this issue. According to the EULA (End-User License Agreement) of the Open Edition of Kylix, all applications, libraries, packages, components, etc. that you make with this edition will have to be distributed under the terms of the General Public License (GPL). The CLX components that will come with this edition of Kylix are also GPL. You can find more information about this license in the web site of the Free Software Foundation: http://www.fsf.org/copyleft/gpl.html On the other hand, the CLX that come with Kylix Desktop Developer Edition and Kylix Server Developer Edition are distributed under a dual license. With these editions you can distribute your applications, libraries, packages, components, etc. under the GPL or under your own licensing terms (commercial, shareware, beerware, freeware, adware, etc.) without having to pay any additional licenses besides the license of the Desktop or the Server edition (U$S 999 and U$S 1,999 respectively) since they already include the price of the commercial license of CLX. Kylix will be licensed on a per-developer basis, meaning that you can have it installed in many computers for as long as you are the only one who uses Kylix. If for example two developers intend to use Kylix, even if it is installed in the same machine and even if they don't make simultaneous use, they have to purchase two licenses. I heard many people arguing that the prices of the commercial Kylix editions are quite high, and I believe they are (for me at least), but we have to consider three things: 1) We have to understand that Borland has to cover the cost of Kylix development that took a lot of more work and time than expected, or -better said- than originally estimated, because the delay can't be said to be totally unexpected considering that we are talking of almost a whole new development in a new environment like Linux... 2) The price is still very good considering that with this RAD tool one can enter a whole new market. 3) The Open Edition will be available for free download (or for only U$S 99 for the CD and printed documentation) and it's quite suitable for independent contractors that develop tailor-made solutions. The fact that you have to give the source code and allow free use of the software (copying, modifying, etc.) doesn't preclude you from charging for it. Free software refers to freedom, not pricing. If someone asks you to develop or make changes to a GPL application, this doesn't mean that you have to do it gratis! You can charge for your work as much as you can. You can also charge for packaging and delivering GPL software, even if it's not yours (for example Linux distributions do this). Besides, distributing your software as GPL can help you sell your services as an independent developer, because companies see GPL software with good eyes since they don't want to be tied to maintenance fees or to be forced to turn to the original developer if they need changes or additions to the program. What would happen if you don't have time for these things anymore? Companies want the freedom to choose who will maintain and do further developments of the software they buy. Quite likely, they will still request these services from you since nobody will know an application better than the one who developed it. Mainly what they want is the assurance that they don't depend on you, and that eventually they can replace you if you ever decide to move to other things or something happens to you or if they feel you are charging too much for your services or whatever... Personally I believe there's going to be a before and after the release of Kylix... My first thought is that very soon Linux will be populated with very good GUI applications distributed under the terms of the GPL. Mainly system configuration and management utilities and basic tools that will make Linux more user-friendly. Commercial software will probably take more time to appear, but I guess that sooner or later will be there too. The phrase "there aren't applications for Linux" will be part of the past. My second though is that since Linux is free, and Kylix Open Edition will also be free, and since the GPL is suitable for educational purposes, I can see right now the pressure of students and faculty members in the next year to include the Linux+Kylix combination in the study programs of schools, colleges and universities. I belive that if Kylix doesn't help take Linux to the next level and doesn't get new fans of the Pascal language, nothing will. Well, time will tell... ________________________________________________________________________ 7. LINKS * Scalabium A very well designed site with lots of Delphi tips, code samples, components, articles and tutorials. http://www.scalabium.com/ ________________________________________________________________________ YOU CAN HELP US We need your help to keep this newsletter going and growing. You can help by referring the newsletter to your colleagues: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Or you can help by voting for us in some or all of these rankings to give more visibility to our web site and thus increase the number of subscriptions to this newsletter: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 It's just a few seconds for you that REALLY mean a lot to us. ________________________________________________________________________ If you haven't received the full source code examples for this issue, you can get them from http://www.latiumsoftware.com/download/p0016.zip ________________________________________________________________________ This newsletter is provided "AS IS" without warranty of any kind. Its use implies the acceptance of our licensing terms and disclaimer of warranty you can read at http://www.latiumsoftware.com/en/legal.php where you will also find a note about legal trademarks. Articles are copyright of their respective authors and they are reproduced here with their permission. You can redistribute this newsletter as long as you do it in full (including copyright notices), without changes, and gratis. ________________________________________________________________________ Main page: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Group home page: http://groups.yahoo.com/group/pascal-newsletter/ Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com Problems with your subscription? eds2004 @ latiumsoftware.com ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/en/index.php Copyright (c) 2001 by Ernesto De Spirito. All rights reserved. ________________________________________________________________________ |
The full source code examples of this issue are available for download.
![]() bmf.gif |
![]() bmr.gif |
![]() btf.gif ![]() btr.gif |
||
![]() colorbtn.gif |
||||
![]() |
Errors? Omissions? Comments? Please contact us!











