Pascal Newsletter #22 - 18-MAY-2001
INDEX
1. A FEW WORDS FROM THE EDITOR
2. DELPHI 6
3. VALIDATING EMAIL ADDRESSES IN DELPHI
4. OLD TIMES (II) - Opinion - By H.R Quiroga
- A real programmer
- Going back to the early eighties
- The Holy War
- Basic and Java
- Present times
- Where are we going
5. SEARCHING TEXT IN A MEMO FIELD
6. GREATIS PRINT SUITE
- What is Greatis Print Suite?
- Components
- Download
- License
- More information
7. MISCELANEOUS STRING HANDLING FUNCTIONS
- Counting occurrences in a string
- Splitting a string in an array
- Splitting a string in a string list
- Converting MySql TimeStamps
8. COPY, INHERIT OR USE?
9. DELPHI ON THE NET
________________________________________________________________________
1. A FEW WORDS FROM THE EDITOR
AND THE LICENSE GOES TO...
Roberto Martinez Olvera, 31 years old, Systems Executive of IEQSA,
Querétaro (Mexico), is the winner of the Help & Manual Drawing, for
having the number 059 (corresponding to the second prize of the
Lottery of Cordoba, since no participant had the number 347
corresponding to the first prize). Congratulations!
Help & Manual: http://www.ec-software.com/hmpage.htm
ERRATA
The routine presented in the article "Getting the BIOS serial number" in
the last issue doesn't work on Windows NT/2000. You can see the comments
at http://www.delphi3000.com/articles/article_2194.asp#Comments
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. DELPHI 6
If -like me- you though that Delphi 6 was just going to introduce some
improvements over Delphi 5 and that Kylix portability was going to be
the star in this new version of Delphi, you were totally wrong...
While Microsoft's Visual Studio .NET is still in its first beta, Borland
announced the availability of Delphi for June 8 and is already taking
preorders. This way Delphi 6 will be the first RAD tool for Windows with
full support for the new industry standard Web Services, enabling
immediate and ongoing integration with emerging Web Services based
vendor platforms like for example Microsoft's .Net and BizTalk, and Sun
Microsystems's ONE.
More information:
* Borland unveils industry's first RAD Web Services development platform
http://www.borland.com/about/press/2001/del6released.html
* Borland enters Web services fray
http://www.infoworld.com/articles/hn/xml/01/05/07/010507hnborland.xml
* Borland aims to make Web services a "Snap"
http://www.zdnet.com/eweek/stories/general/0,11011,2712635,00.html
* Delphi gets corporate
http://www.comp-buyer.co.uk/index71/newnews/newsarticle.php3?id=2124
* Screenshots and articles on Delphi 6 in the Delphi community
http://community.borland.com/delphi/0,1419,1,00.html
* What's new in Delphi 6
http://www.borland.com/delphi/del6/whatsnew.html
* Delphi 6 feature matrix
http://www.borland.com/delphi/del6/featurematrix/
* Delphi 6 system requirements
http://www.borland.com/delphi/del6/sysreq.html
* Delphi 6 editions and prices
http://shop.borland.com/Category/0,1257,3-15-983,00.html
________________________________________________________________________
3. VALIDATING EMAIL ADDRESSES IN DELPHI
Nowadays it's very common that our programs store email addresses in
databases as part of the data of personnel, customers, providers, etc.
When prompting the user for an email address, how do we know if the
entered value is formally correct? In this article I'll show you how to
validate email addresses using a variation of the RFC #822.
The RFC #822 rules the "STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT
MESSAGES". You can find it at: http://www.isi.edu/in-notes/rfc822.txt
According to this rule, the following are valid email addresses:
John Doe johndoe@server.com
John Doe <johndoe@server.com>
"John Doe" johndoe@server.com
"John Doe" <johndoe@server.com>
The purpose of my code is not to validate such things, but strictly what
is necessary to reach a single recipient (like "johndoe@server.com"),
that in the specification is referred as an "addr-spec", which has the
form:
local-part@domain
local-part = one "word" or more, separated by periods
domain = one "sub-domain" or more, separated by periods
A "word" can be an "atom" or a "quoted-string":
atom = one or more chars in the range #33..#126 except ()<>@,;:\/".[]
quoted-string = A text enclosed in double quotes that can contain 0 or
more characters (#0..#127) except '"' and #13. A backslash ('\')
quotes the next character.
A "sub-domain" can be a "domain-ref" (an "atom") or a "domain-literal":
domain-literal = A text enclosed in brackets that can contain 0 or
more characters (#0..#127) except '[', ']' and #13. A backslash ('\')
quotes the next character.
According to the RFC 822, extended characters (#128..#255) cannot be
part of an email address, however many mail servers accept them and
people use them, so I'm going to take them into account.
The RFC 822 is very open about domain names. For a real Internet email
address maybe we should restrict the domain part. You can read more
about domain names in the RFC #1034 and RFC #1035 that you can find at:
http://www.ietf.org/rfc/rfc1034.txt
http://www.ietf.org/rfc/rfc1035.txt
For the RFC 1034 and the RFC 1035, a domain name is formed by "sub-
domains" separated by periods, and each subdomain starts with a letter
('a'..'z', 'A'..'Z') and should be followed by zero or more letters,
digits and hyphens, but cannot end with a hyphen. We are going to
consider that a valid domain should have at least two "sub-domains"
(like "host.com").
Now that we have the rules clear, let's get to the work. The algorithm
for the function resembles a states-transition machine. Characters of
the string are processed in a loop, and for each character first we
determine in which state the machine is and then we process the
character accordingly, to determine if the machine should continue in
that state, switch to a different state or produce an error (breaking
the loop). These kind of algorithms are extensively treated in
programming-algorithms textbooks, so let's get right to the code:
function ValidEmail(email: string): boolean;
// Returns True if the email address is valid
// Author: Ernesto De Spirito <eds2008 @ latiumsoftware.com>
const
// Valid characters in an "atom"
atom_chars = [#33..#255] - ['(', ')', '<', '>', '@', ',', ';', ':',
'\', '/', '"', '.', '[', ']', #127];
// Valid characters in a "quoted-string"
quoted_string_chars = [#0..#255] - ['"', #13, '\'];
// Valid characters in a subdomain
letters = ['A'..'Z', 'a'..'z'];
letters_digits = ['0'..'9', 'A'..'Z', 'a'..'z'];
subdomain_chars = ['-', '0'..'9', 'A'..'Z', 'a'..'z'];
type
States = (STATE_BEGIN, STATE_ATOM, STATE_QTEXT, STATE_QCHAR,
STATE_QUOTE, STATE_LOCAL_PERIOD, STATE_EXPECTING_SUBDOMAIN,
STATE_SUBDOMAIN, STATE_HYPHEN);
var
State: States;
i, n, subdomains: integer;
c: char;
begin
State := STATE_BEGIN;
n := Length(email);
i := 1;
subdomains := 1;
while (i <= n) do begin
c := email[i];
case State of
STATE_BEGIN:
if c in atom_chars then
State := STATE_ATOM
else if c = '"' then
State := STATE_QTEXT
else
break;
STATE_ATOM:
if c = '@' then
State := STATE_EXPECTING_SUBDOMAIN
else if c = '.' then
State := STATE_LOCAL_PERIOD
else if not (c in atom_chars) then
break;
STATE_QTEXT:
if c = '\' then
State := STATE_QCHAR
else if c = '"' then
State := STATE_QUOTE
else if not (c in quoted_string_chars) then
break;
STATE_QCHAR:
State := STATE_QTEXT;
STATE_QUOTE:
if c = '@' then
State := STATE_EXPECTING_SUBDOMAIN
else if c = '.' then
State := STATE_LOCAL_PERIOD
else
break;
STATE_LOCAL_PERIOD:
if c in atom_chars then
State := STATE_ATOM
else if c = '"' then
State := STATE_QTEXT
else
break;
STATE_EXPECTING_SUBDOMAIN:
if c in letters then
State := STATE_SUBDOMAIN
else
break;
STATE_SUBDOMAIN:
if c = '.' then begin
inc(subdomains);
State := STATE_EXPECTING_SUBDOMAIN
end else if c = '-' then
State := STATE_HYPHEN
else if not (c in letters_digits) then
break;
STATE_HYPHEN:
if c in letters_digits then
State := STATE_SUBDOMAIN
else if c <> '-' then
break;
end;
inc(i);
end;
if i <= n then
Result := False
else
Result := (State = STATE_SUBDOMAIN) and (subdomains >= 2);
end;
Any collaboration to improve this function will be welcome.
________________________________________________________________________
4. OLD TIMES (II) - Opinion - By H.R Quiroga
A real programmer
-----------------
Perhaps many of you have read the phrase "Real Programmers Don't Use
Pascal", which belongs to an article published in 1983 in "Datamation"
by Ed Post. Things have changed since then to the degree that many of
the utilities of "PCMagazine" are made with Delphi, and if those
programmers aren't "real", there's not much left for me. I don't think
that my knowledge of C, Assembler, Fortran, Cobol, Clipper and other
languages make me more "Real".
Going back to the early eighties
--------------------------------
When Borland's Turbo Pascal appeared, Microsoft was selling a Pascal
compiler (I have cloudy memories of it, so please excuse if I am not
right). It required three diskettes, compilation and many-steps linking,
and several hundreds dollars per license, in addition to some text
editor like Wordstar. On the other hand, "Turbo Pascal" needed 32K, had
an integrated editor, generated 30K executables that were much smaller
than those of the competition and got to cost less than 100 dollars.
You'll understand why Microsoft eventually retired from that competition
(it is possible that they have eliminated any historical record of this
failure like the main character of Orwell's novel in 1984). One tried
Turbo Pascal thinking about the ridiculous of the name. Soon it stopped
from sounding ridiculous.
There were some important losses in the way. Peace to the rests of
"Topspeed Modula-2".
The Holy War
------------
In computing, "Holy Wars" are based in nontechnical positions (almost
religious) taken by means of syllogisms to technical positions. They are
very funny or at least ingenious. There has always been wars of C versus
something else. Obviously "C" versus "Pascal". The war didn't made much
damage to either of them and both languages survive today. The Object
Pascal compiler is extremely far from Pascal like perhaps the one of C++
to C.
Paradoxically, this article looks very much like the artillery of a holy
war ;-> and given the forum where I'm presenting this, it's almost like
trying to convince the convinced. Anyway, the defects pointed to each
language during the wars were very certain in their majority. Many
changes in these languages are only for covering these terrible
weaknesses.
Basic and Java
--------------
There has always been something very annoying in the persistence of
BASIC, and it isn't only its origin, but also its poor evolution. BASIC
survives because Microsoft maintains it; something we could also say of
Pascal with respect to Borland (I believe that at some moment it's been
the other way round). Perhaps C, derivates and Java are safe from this.
Microsoft has tried in vain to spoil Java. Borland in a more discreet
way contributed important things to its specification (read JavaBeans)
without trying to appropriate it, and in this discreet way they maintain
one of the best Java development tools. It will be interesting to see
the future of Java and Borland.
BASIC, with the terrible thing it is, still exists derived in Visual
Basic. I hate to confront Visual Basic programmers who insist in that I
should use it, without caring that I in fact I had used it and left it.
Currently I'm thinking of giving some effort to Java. It seems to me
that this will contribute something useful or at least it will give me
the opportunity to reject it properly.
Present times
-------------
We already know that Pascal has presence nowadays, and significantly
applied through Delphi. The interesting thing is to see where it is
used and if we can recognize its maturity thru this. We know the NASA
uses it. We also know that there are some commercial applications made
with Delphi. What I would like to see is a complete Office Suite done
with Delphi; nevertheless, I know this is a very difficult market, so
it's only a dream so far. I must add that this dream has a Linux
environment.
Where are we going
------------------
Yes, first person in plural. Marco Cantù said "Dephi is not only a
product. It's a community". This community seems impelled more and more
by means of the Internet and it seems to grow more and more every day.
It pleases to me to belong to the Delphi Community and I believe that we
are many and enthusiastic, and for that reason we must make us notice.
It doesn't matter that Visual Something sells more copies if the quality
of our work is superior and fills us with more pleasure.
It's inevitable to mention Kylix. Any prediction is yet a little
ventured. I hope that eventually we hear of Delphi for Mac or something
like this. Unlike the Mac, I believe that Linux won't diminish its
impetus.
-------------
To remember the history I recommend a look at (many precise data
indicated here came from this source):
"THE JARGON FILE, VERSION 2.9.12", known as "The Hacker's Dictionary".
Other data were taken from articles by Marco Cantú (www.marcocantu.com)
------------------------------------
Copyright (c) 2001 H.R Quiroga
________________________________________________________________________
5. SEARCHING TEXT IN A MEMO FIELD
If you need to search for text in a memo field, you can do it by
scanning the dataset moving record by record to see if the search text
is present in the memo field or not:
procedure TForm1.btnFindClick(Sender: TObject);
var
SearchStr: string;
begin
SearchStr := UpperCase(Edit1.Text);
Table1.DisableControls;
if Sender = btnFindFirst then
Table1.First // Find First button
else
if not Table1.Eof then Table1.Next; // Find Next button
while not Table1.Eof and (AnsiPos(SearchStr,
UpperCase(Table1Notes.AsString)) = 0) do
Table1.Next;
Table1.EnableControls;
if Table1.Eof then ShowMessage('Not found')
end;
The full source code of this example can be found in the archive
attached to this newsletter.
________________________________________________________________________
6. GREATIS PRINT SUITE
What is Greatis Print Suite?
----------------------------
Greatis Print Suite is an extremely convenient set of components which
provides advanced print and preview features into Delphi and C++ Builder
applications.
Components
----------
The suite contains:
* TPrintJob: Main non-visual component of the suite which provides easy
multipage printing
* TPreview: Control which provides easy print preview
* TPreviewWindow: Ready-to-use preview window
* TPreviewToolbar: Ready-to-use toolbar which provides control of preview
* TPreviewStatusBar: Ready-to-use status bar which provides display
preview parameters
* TPreviewComboBox: Combo box which provides display and control of
preview scale
* TPreviewLabel: Label to display preview parameters
Greatis Print Suite Pro contains additional Print Jobs package - set of
ready-to-use components to print grids, databases and tables with many
abstract classes to create custom print jobs.
* TSimpleGridPrintJob: Print job for easy print any graphic grid
* TSimpleTextGridPrintJob: Print job for easy print any text grid
* TDBGridPrintJob: Print job for easy print database grid
* TStringGridPrintJob: Print job for easy print TStringGrid contents
* TListViewPrintJob: Print job for easy print TListView contents
* TStringsPrintJob: Print job for easy print text files and TStrings
contents
* TMultiPrintJob: Component for integrate other print jobs
* TDraftPrintJob: Component for draft (thumbnail) printing
Forget BeginDoc, EndDoc, NewPage and other low-level printing
procedures, just draw your print job and Print Suite will handle the
rest.
Download
--------
Compiled EXE-demo, printable documentation in PDF-format and trial
versions of all Print Suite Pro components are included in demo kit,
available from the following address:
http://www.greatis.com/printsuitedemo.zip (~735K)
An evaluation copy is available upon request.
License
-------
Print Suite costs US$ 29.95-39.95 for a single-user license.
More information
----------------
See more information on Print Suite home page
http://www.greatis.com/delphicb/printsuite/
For more information, contact Greatis Software <b-team@greatis.com>
________________________________________________________________________
7. MISCELANEOUS STRING HANDLING FUNCTIONS - By Ernesto De Spirito
Just in case someone finds them useful, here go a few string handling
functions I've been asked for in my "Delphi Help Desk".
Counting occurrences in a string
================================
The following functions return the number of occurrences of a char or
a substring within a string or ANSI string:
interface
function Occurs(const str: string; c: char): integer; overload;
function Occurs(const str: string; const substr: string): integer;
overload;
function AnsiOccurs(const str: string; const substr: string): integer;
implementation
uses sysutils;
function Occurs(const str: string; c: char): integer;
// Returns the number of times a character occurs in a string
var
p: PChar;
begin
Result := 0;
p := PChar(Pointer(str));
while p <> nil do begin
p := StrScan(p, c);
if p <> nil then begin
inc(Result);
inc(p);
end;
end;
end;
function Occurs(const str: string; const substr: string): integer;
// Returns the number of times a substring occurs in a string
var
p, q: PChar;
n: integer;
begin
Result := 0;
n := Length(substr);
if n = 0 then exit;
q := PChar(Pointer(substr));
p := PChar(Pointer(str));
while p <> nil do begin
p := StrPos(p, q);
if p <> nil then begin
inc(Result);
inc(p, n);
end;
end;
end;
function AnsiOccurs(const str: string; const substr: string): integer;
// Returns the number of times a substring occurs in a string
// ANSI version
var
p, q: PChar;
n: integer;
begin
Result := 0;
n := Length(substr);
if n = 0 then exit;
q := PChar(Pointer(substr));
p := PChar(Pointer(str));
while p <> nil do begin
p := AnsiStrPos(p, q);
if p <> nil then begin
inc(Result);
inc(p, n);
end;
end;
end;
Splitting a string in an array
==============================
The following functions split a string in parts separated by a substring
and return the parts in a dynamic string array:
interface
type
TStringArray = array of string;
function Split(const str: string;
const separator: string = ','): TStringArray;
function AnsiSplit(const str: string;
const separator: string = ','): TStringArray;
implementation
uses sysutils;
function Split(const str: string;
const separator: string): TStringArray;
// Returns an array with the parts of "str" separated by "separator"
var
i, n: integer;
p, q, s: PChar;
begin
SetLength(Result, Occurs(str, separator)+1);
p := PChar(str);
s := PChar(separator);
n := Length(separator);
i := 0;
repeat
q := StrPos(p, s);
if q = nil then q := StrScan(p, #0);
SetString(Result[i], p, q - p);
p := q + n;
inc(i);
until q^ = #0;
end;
function AnsiSplit(const str: string;
const separator: string): TStringArray;
// Returns an array with the parts of "str" separated by "separator"
// ANSI version
var
i, n: integer;
p, q, s: PChar;
begin
SetLength(Result, AnsiOccurs(str, separator)+1);
p := PChar(str);
s := PChar(separator);
n := Length(separator);
i := 0;
repeat
q := AnsiStrPos(p, s);
if q = nil then q := AnsiStrScan(p, #0);
SetString(Result[i], p, q - p);
p := q + n;
inc(i);
until q^ = #0;
end;
Example:
procedure TForm1.Button1Click(Sender: TObject);
var
a: TStringArray;
i: integer;
begin
a := Split('part1,part2,part3');
for i := 0 to Length(a) - 1 do begin // Will show three dialogs
ShowMessage(a[i]); // 'part1', 'part2', 'part3'
end;
end;
Splitting a string in a string list
===================================
The following functions split a string in parts separated by a substring
and return the parts in a string list that may be passed as third
parameter or created by the function (and in this latter case it must be
freed by the caller):
interface
uses classes;
function SplitStrings(const str: string;
const separator: string = ',';
Strings: TStrings = nil): TStrings;
function AnsiSplitStrings(const str: string;
const separator: string = ',';
Strings: TStrings = nil): TStrings;
implementation
uses sysutils;
function SplitStrings(const str: string; const separator: string;
Strings: TStrings): TStrings;
// Fills a string list with the parts of "str" separated by
// "separator". If Nil is passed instead of a string list,
// the function creates a TStringList object which has to
// be freed by the caller
var
n: integer;
p, q, s: PChar;
item: string;
begin
if Strings = nil then
Result := TStringList.Create
else
Result := Strings;
try
p := PChar(str);
s := PChar(separator);
n := Length(separator);
repeat
q := StrPos(p, s);
if q = nil then q := StrScan(p, #0);
SetString(item, p, q - p);
Result.Add(item);
p := q + n;
until q^ = #0;
except
item := '';
if Strings = nil then Result.Free;
raise;
end;
end;
function AnsiSplitStrings(const str: string; const separator: string;
Strings: TStrings): TStrings;
// Fills a string list with the parts of "str" separated by
// "separator". If Nil is passed instead of a string list,
// the function creates a TStringList object which has to
// be freed by the caller
// ANSI version
var
n: integer;
p, q, s: PChar;
item: string;
begin
if Strings = nil then
Result := TStringList.Create
else
Result := Strings;
try
p := PChar(str);
s := PChar(separator);
n := Length(separator);
repeat
q := AnsiStrPos(p, s);
if q = nil then q := AnsiStrScan(p, #0);
SetString(item, p, q - p);
Result.Add(item);
p := q + n;
until q^ = #0;
except
item := '';
if Strings = nil then Result.Free;
raise;
end;
end;
Examples:
procedure TForm1.Button1Click(Sender: TObject);
begin
SplitStrings(Edit1.Text, ', ', ListBox1.Items);
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Parts: TStrings;
begin
Parts := nil;
try
Parts := SplitStrings(Edit1.Text, ', ');
ShowMessage('First part is "' + Parts[0] + '"');
finally
Parts.Free;
end;
end;
Converting MySql TimeStamps
===========================
The following functions convert MySql full timestamps (strings in the
format 'YYYYMMDDHHMMSS') to a Variant in TDateTime format and viceversa.
uses sysutils;
function MySqlTimeStampToDateTime(const TimeStamp: string): variant;
// Converts a MySql TimeStamp to a Delphi TDateTime
begin
if TimeStamp = '' then
Result := Null
else
Result := EncodeDate(StrToInt(Copy(TimeStamp, 1, 4)),
StrToInt(Copy(TimeStamp, 5, 2)),
StrToInt(Copy(TimeStamp, 7, 2))) +
EncodeTime(StrToInt(Copy(TimeStamp, 9, 2)),
StrToInt(Copy(TimeStamp, 11, 2)),
StrToInt(Copy(TimeStamp, 13, 2)), 0);
end;
function DateTimeToMySqlTimeStamp(DateTime: variant): string;
// Converts a Delphi TDateTime in a variant to a MySql TimeStamp
begin
if VarType(DateTime) in [varNull, varEmpty] then
Result := ''
else
Result := FormatDateTime('yyyymmddhhnnss', DateTime);
end;
Example:
procedure TForm1.Button1Click(Sender: TObject);
var
timestamp: string;
begin
timestamp := DateTimeToMySqlTimeStamp(Now);
ShowMessage(timestamp + #13#13 +
DateTimeToStr(MySqlTimeStampToDateTime(timestamp)));
end;
________________________________________________________________________
8. COPY, INHERIT OR USE?
Code reusability saves us time and effort, increasing our productivity.
Object-oriented programming has something to do with that, and in the
case of Delphi we can reuse forms and even entire projects. For example
if we have a form with a table, a dbgrid, a navigator and several
buttons, we can save it like a model in the Object Repository to reuse
it in several parts of our application or other applications. The same
for a standard form of the type "Save, Don't save, Cancel". To add a
form to the repository you have to right-click it and select "Add to
Repository..." in the context menu. To save a project in the repository
choose "Add to Repository..." from the Project menu.
To use a form of the repository in our application, in the File menu we
chose New and in the New Items dialog we click the Forms tab to see the
forms available in the repository. Then we select the form we want, the
method of use (Copy, Inherit, or Use) and click the OK button. The
differences between these three methods of use are described briefly
here:
COPY: Creates a form that is copy of the form that is in repository. The
changes you make to the copy won't affect the form in the repository
(nor other projects that use it), and changes made to the form in the
repository won't affect forms previously copied from it. This option is
used when the form in the repository is just a base to work, with a very
low level of standardization. Full-adaptation is possible.
INHERIT: It creates a form that derives from the form in the
repository. Changes made to this derived form (inherited) won't affect
the form in the repository, but the inverse is not true. This option is
used when the form in the repository is well standardized but it is
desired to allow some adaptation. It's the most powerful way to use a
form.
USE: It adds the form of the repository to your project. It's not a
copy, but the form of the repository itself, and thus any modification
that you make to it will apply to other projects that USE or INHERIT it.
This option is used when the form of the repository is a standard and is
defined in itself (it doesn't require particular adaptations for each
case/application).
________________________________________________________________________
9. DELPHI ON THE NET
* Delphi Database Programming Course - by Zarko Gajic
Free online database programming course for beginner Delphi developers
focused on ADO techniques. A new chapter have been added in the last
two weeks (Chapter 8 "Data filtering").
http://delphi.about.com/compute/delphi/library/weekly/aa010101a.htm
* Learning Assembler with Delphi - by Ian Hodger
http://www.delphi3000.com/articles/article_2245.asp
* Starting an application as an icon in the system tray - by D. Souchard
http://www.delphi3000.com/articles/article_1606.asp
* QuickReports Manuals and Tutorials - by QuSoft
http://www.quickreport.co.uk/QR5_Downloaddoc.html
________________________________________________________________________
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=p22
________________________________________________________________________
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.
________________________________________________________________________
|