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
eds2008 @ 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-2006 & 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/en/file.php?id=p05 (source code)
Pascal Newsletter #6
http://www.latiumsoftware.com/en/pascal/0006.php
http://www.latiumsoftware.com/en/file.php?id=p06 (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/en/file.php?id=p16
________________________________________________________________________
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.programmingpages.com/?r=latiumsoftwarecomenpascal
http://top100borland.com/in.php?who=20
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/en/file.php?id=p16
________________________________________________________________________
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? eds2008 @ latiumsoftware.com
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/en/index.php
Copyright (c) 2001 by Ernesto De Spirito. All rights reserved.
________________________________________________________________________
|