You are on page 1of 44

BLAISE PASCAL

ENGLISH VERSION

ALL ABOUT DELPHI AND KYLIX, PASCAL AND RELATED LANGUAGES

Pascal

Representing graphics for math functions


by Peter Bijlsma

Client Dataset Toolkit


by Detlef Overbeek

Website name checking


by Henk Schreij

Database normalisation
by Herman Peeren

Delphi 2007 and VCL Component Building by Bob Swart


Wallpaper
by Detlef Overbeek

Coding for two or more types


by Henk Schreij

Recipe for creating a cookbook


by Tim Opsteeg

Exploring Rave Reports


by Rik Smit

Cover story: Drawing on a changing background


by David Dirkse

Mini application: Opening files


by Detlef Overbeek

Rotating images
by Detlef Overbeek

Publisher: Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep) Stichting Ondersteuning Programeertaal Pascal

Cover price Europe: 10.00 / UK 7.00 / US $ 15.00

March 2008

CONTENTS
Articles
Representing graphics for math functions by Peter Bijlsma Client Dataset Toolkit by Detlef Overbeek Website name checking by Henk Schreij Database normalisation by Herman Peeren

ALL ABOUT DELPHI AND KYLIX PASCAL AND RELATED LANGUAGES Volume 1, ISSN 1876-0589

BLAISE PASCAL 1
page 6 page 8 page 9
Contact Detlef Overbeek, Edelstenenbaan 21, 3402 XA IJsselstein, Netherlands Tel.: +31 (0)30 68.76.981 Mobile: +31 (0)6 21.23.62.68 E-mail: editor@blaisepascal.eu Editor D. D. Overbeek News and Press Releases email only to editor@blaisepascal.eu

page 11 page 13 page 16 page 17 page 21 page 25

Delphi 2007 and VCL component building


by Bob Swart Wallpaper by Detlef Overbeek Coding for two or more types by Henk Schreij Recipe for creating a cookbook by Tim Opsteeg Exploring Rave Reports by Rik Smit

Columnists Bob Swart Contributors Peter Bijlsma, David Dirkse, Frans Doove, Herman Peeren, Henk Schreij, Rik Smit, Bob Swart, Tim Opsteeg Assistants Rob van den Bogert, F.M.W. (Frans) Doove, J.B.M. (Jac) Janssen, A.W. (Bert) Jonker, W. (Wim) van Ingen Schenau, M. L. E. J.M. (Miguel) van de Laar, E.E. (Rik) Smit, M.J. (Marco) Roessen, W. (Wout) de Zeeuw e-mail: office@blaisepascal.eu Copyright See the notice at the bottom of this page.

Cover story:
Drawing on a changing background
by David Dirkse Mini application: Opening files by Detlef Overbeek Rotating images by Detlef Overbeek page 33
page 35 page 36

Trademarks All trademarks used are acknowledged as the property of their respective owners. Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions. If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.

Columns
Why Blaise Pascal by Detlef Overbeek Books etc. by Frans Doove page page
a a b c a a c now? bb b b b b a

Subscriptions 3 4
Subscription price 40.-- / UK 28.-- / $ 60. for 4 issues excl. postage. Subscriptions can taken out online at www.blaisepascal.eu or by written order, or by sending an email to office@blaisepascal.eu Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well. Cover price in Europe: 10.00 / UK 7.00 / US $ 15.00 plus postage. Subscriptions are parallel to the calender year. Subscriptions will be prolonged without notice unless the subscription department receives notice of cancellation by letter or email before 1 November. Receipt of payment will be sent by email. Invoices will be sent with the February issue. Subscription can be paid by sending the payment to: ABN AMRO Bank Account no. 44 19 60 863 Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) IBAN: NL48 ABNA 0441960863 VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal) Subscription department Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68 office@blaisepascal.eu

Reminder

Pointers how does that go b c c by Detlef Overbeek


a

c c
a

c c
b

b a

page 24 page 32

In the next issue`


a

b c a a c

b c c a c
2

a c c b b
page 41 page 42 a page 43 page 10 page 39 page 40 page 38

Advertisers a
Code Gear Elektor Hofstad Hosting Netgear Vogelaar Electronics

b c c bb b b b a
2 a + b 2

Illustration: Pythagorean theorem Copyright notice

All material published in Blaise Pascal is copyright SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial purposes. Commercial use of program listings and code is prohibited without the written permission of the author.

Page 2 / 1776

March 2008 BLAISE PASCAL 1

Foreword...
Why Blaise Pascal?
For some time now, we have noticed to our regret that the amount of information about Pascal and Delphi in magazines devoted to programming is dwindling rapidly. Our own magazine, Blaise (with the Dutch version of Blaise Pascal we already have over twenty years of experience - 83 issues were edited), appears to be the very last of the Mohicans. As a group of ambitious volunteers, we think that with the publication of an English version of our magazine we can offer others the same services we have been providing to our own community for more than twenty years already. Based on some research, it appears that there is interest in such a publication, especially among starters, hobby programmers, and programmers with a broader range of experience. We expect that it will take a lot of effort to acquire interested users at the highly professional level. We simply have to start with what we can. The aim of our magazine is to promote the Pascal language. In my opinion, the simplicity, readability and open structure of Pascal make it the most suitable programming language (more so than any other language) for anyone interested in programming , and especially for learning how to program. It gives considerable pleasure to every user because in addition the basic activity of programming, it enables them to incorporate aspects of electronics and needs arising from everyday life. All of us must occasionally mow the lawn, water the garden, reduce energy consumption, find ways to communicate for free, and baby-sit via the Internet. For all programming, with or without databases, and for such daily life activities, Delphi is a wonderful tool to help us do what we want. I am not saying that other languages aren't good, but the learning aspect of Pascal opens the door to a lot of exciting opportunities. If you already know Pascal, it is much easier to learn how to do objectoriented programming. As the saying goes, there's nothing like experience. We want to become a platform for Pascal and Delphi users, with easy access and free to everyone. Of course, there are some limitations: 'Money makes the world go round'. (Liza Minnelli) If you endorse our goals, you can support us financially. You can do this by becoming a donor or by downloading files and code from our website. The fees are quite reasonable, and if you wish you can even use credits. You also can subscribe to the printed version of Blaise Pascal (take advantage of our special limited- time offer: 25.-- euro for the first year!). Although we are a non-profit organisation, we try to keep the price low so anyone can enjoy the magazine. Among other things, this means that we welcome advertisers. So dear advertisers, please support us: it all helps to sustain your own products. Have fun! Detlef Overbeek
Downloads of code, applications etc. are available on our website: www.blaisepascal.eu

Helpful suggestions
for using your issue of Blaise Pascal
About using this PDF file One of the most attractive features of Adobe Acrobat PDF files is that you can incorporate hyperlinks in them. We do this wherever appropriate. The articles listed on page 2 can be accessed by clicking the title or the page number. This takes you directly to start of the article you want to read. All URLs listed in code and mentioned in adverts are also clickable.

Want to become an author?


Write your own article? Notify us: email editor@blaisepascal.eu Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68

2005 Win32

2005 .NET

Recommended version hints appear in some locacations. Mouseover The page numbers on the contents page are clickable. clickable. All URLs are

Readers
Please send your questions and comments to: email office@blaisepascal.eu or to: Edelstenenbaan 21, 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981, Mobile: + 31 (0) 6 21.23.62.68

March 2008 BLAISE PASCAL 1

Page 3 / 1777

Books
The world of programming, programming languages and the evolution of languages and IDEs is filled with questions, riddles and uncertainties. Sometimes it appears that Microsoft is the only constant in this world. The future direction of Pascal and the Delphi IDE is certainly our biggest concern. Identifying the reasons for all this remains speculation for the most part, even though causes for these problems can be pointed out. It sometimes seems that programming for the sheer fun of it is on the decline. This might be due to competition between the various languages, or to a change of interests such as to photography or creating websites (even though this also requires programming). All of this creates another problem, except for Microsoft products: there are very few books written about using, learning and understanding programming. There are lots of books published, but only on current (fashionable) topics. Publishers apparently no longer dare to invest in manuals.
It is a very positive sign that some authors have found a new way to publish their manuals on their own. Marco Cant has done this before. Marco Cant: Delphi 2007 Handbook 2007, published by the author, English, 258 pages, paper, no ISBN. Recommended price: Euro 36.50 plus shipping.

by Frans Doove
Everything is easy to find, thanks to the clear layout and use of suitable typefaces. An essential book for users of Delphi 2007. Order at: http://www.lulu.com/content/1069254 Marcos website: http://www.marcocantu.com/dh2007/

Bob Swart has published two new titles, and he will continue to work this way in the future. These books are also printed by LULU and sent from Spain by airfreight. delivery takes approximately ten days. Bob Swart: Delphi Win 32 / .NET Component Development 2007, English, A4 format, paper, 175 pages, no CD. No ISBN number. Self-published. Order from Lulu.com. Price: EUR 39.98 plus shipping.

This book has seven chapters. The introductory chapter (0) presents an introduction to objectoriented programming (or a refresher for some readers) and describes a few essential items, such as units, objects, classes, encapsulation, inheritance and polymorphism. All topics are illustrated by code examples. The following survey indicates what the book has to offer. The detailed table of content (important for a quick overview) is followed by twelve chapters that explain the new aspects of Delphi 2007. Chapter 1 discusses the Delphi 2007 IDE, with many code examples. Chapter 2 discusses code templates and refactoring. Chapter 3 covers project management and MSIBuild, followed by the debugger and recent changes to the language. Chapter 6 and 7 discuss the changes to RTL and VCL. Chapter 8 discusses memory management Chapter 9 is an extensive chapter devoted to Vista and the VCL. The final chapters address practical topics: database support and dbExpress4 (Chapter 10), InstallAware and other tools, including EDI (Chapter 11) and migration to Delphi 2007. A very useful book! As we are have come to expect from Marco Cant, the explanations are excellent and aimed at practical use. The book contains many code examples. The chapters are kept short; each one is devoted to a topic with code examples. Chapter 1, VCL Component Building' (44 pages), describes how to build components and has five very well elaborated code examples documented by screen shots. I think the strength of this book is that the discussed items are supported by code examples. There is no complete program or CD. Chapter 2, 'VCL Data-Aware Components' (22 pages), discusses the concept of data-aware components. Again this is supported by examples and screen shots. I think the author has not aimed the book at beginners, but instead at readers who are quite familiar with the IDE but do not have any experience with building components. Chapter 3 discusses VCL property and component editors (22 pages). Chapter 4 devotes 20 pages to a discussion of VCL for .NET & assemblies Chapter 5 (16 pages) discusses .NET components, while Chapter 6 addresses ASP.NET.

Page 4 / 1778

March 2008 BLAISE PASCAL 1

Books (2)
In summary this is an excellent book, but not for rank beginners. The reader is assumed to have experience with program construction and programming in Delphi and Pascal. The concepts are not defined in advance, but they are explained using applications, coding and development. Each chapter concludes with a summary. The book begins with a well-organised table of contents, which clearly presents its structure. However, I thought that some terms were missing from the glossary at the end. For anyone with a corresponding Delphi or Pascal IDE who is willing to train himself on this topic, this book is a must. Oder at: http://www.lulu.com/content/1156990 Bobs website: http://www.drbob42.com/ Chapter 4 (14 pages) is devoted to dbExpress, with a discussion of migration to dbExpress, CDS to DBX, and additional fields. The final chapter (Chapter 5, 7 pages) presents an introduction to DBX4. Conclusion: Like the first book, this one is a very clear and transparent introduction to the subject. It provides a wealth of information for anyone interested in the subject. All items are clearly explained, with a strong focus on practical use. There are many examples, illustrations and screen shots. To avoid potential misunderstanding, it should be noted that both books are very attractively produced and the quality of the printing is excellent. Absolutely recommended.

Bob Swart: (Turbo) Delphi for Win 32 VCL Database Development


2007, English, A4 format, paper, 84 pages, no CD. No ISBN number. Self-published. Order from Lulu.com. Price: EUR 16.98 plus shipping. Price: EUR 12.99 for the PDF download.

How to become a subscriber


First of all you need to decide whether you want the printed version or the free download version.

What's the difference?


The printed version contains 40 pages and entitles you to download all code free of charge. You will receive all issues for one year at the price stated on page 2.

Special limited-time offer:

subcribe
to the printed version

for the first year for only 25.00


including all downloads, tax and postage
Our readers often work with data-based applications. A handbook specifically devoted to this subject can come in very handy, especially if you plan to use the new version of Delphi or create another new database. It's certainly useful with the new versions of Delphi (2006 or even the latest version, 2007 for Win 32), and probably with older versions as well. The discussion of DBX 4 is specifically aimed at Delphi 2007. To give you an impression of the contents: The book is divided into six chapters. The introductory chapter, (Chapter 0, 17 pages) describes relational databases and SQL. The topics include relational databases, SQL, DML (data manipulation language), data explorer, SQL relational design and normal forms. The discussion is supported by lots of examples and illustrations. Chapter 1 (16 pages) discusses the Borland database engine (BDE). It includes a discussion of BDE data access components, data-aware controls, dynamic tables, fields, and data entry validation. Chapter 2 (14 pages) is about dbGo for ADO and migration to ADO. Chapter 3 (8 pages) discusses the TClientData Set. A subscription to the download version is free, but you must pay for each code download. Code downloads are easy to find at www.blaisepascal.eu

Readers
Please send your questions and comments to: email office@blaisepascal.eu or to: Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68 Page 5 / 1779

March 2008 BLAISE PASCAL 1

Representing graphics for math functions


In the computer world, graphical representations are built up from pixels. Sometimes the number of pixels available on our screen is not sufficient for the scale we want to use. This article aims to give the reader an understanding of the issues involved in graphical scaling and provide a possible solution for this problem.

2005 Win32

by Peter Bijlsma

Xtransl = Width / (Xmax Xmin) Ytransl = Height /(Ymax Ymin)


Ymax

ZIPFILE

Graphics window
*(X,Y)

Image1
*(Xim,Yim)

Ymin Xmin Xmax

Height 0 Width

Figure 1a

Figure 1b

Note that the translation in the Y direction is negative. An X position can now be displayed in our Image1 by subtracting the lowest possible value and converting the result by using the calculated translation factor. To display a Y position in our Image1, we must bear in mind that the highest Y value is the lowest value in Image1, thus: Xim = Xtransl * (X Xmin) Yim = Ytransl * (Y Ymax) OK, we now have a set of formulae ready to code. However, a few refinements must be added to make the program fool-proof. For instance, a check has to be made to ascertain that Xmax is always greater than Xmin (and the same for Y). This problem can be solved in two ways: either give the user/operator some kind of warning that an illegal action has taken place, or forgive the user/operator his mistake and solve it in your program. I take the latter approach and simply swap the minimum and maximum parameters if they are accidentally interchanged. If these parameters are the same, I add some space to prevent a division-by-zero error in the translation formulae. Another issue to take care of is that the coordinates Xim and Yim must be an integer type, and we also know that integers in a program are limited to a maximum value. So the final solution for converting the coordinates of our graphics window into standard TImage coordinates is as follows: First, we have a procedure to define (set) our graphics window. This procedure calculates the globally used translation factors and the lowest possible values to be used as references in Image1. The procedure has to be called only once for each graph: procedure SetGrWin; var Temp: real; begin if Xmax < Xmin then begin Temp := Xmin; Xmin := Xmax; Xmax := Temp; end; if Xmax = Xmin then Xmax := Xmax + 0.1; if Ymax < Ymin then begin Temp := Ymin; Ymin := Ymax; Ymax := Temp; end; if Ymax = Ymin then Ymax := Ymax + 0.1; Xtransl := Image1.ClientWidth /(Xmax Xmin); Ytransl := Image1.ClientHeight / (Ymax Ymin); Xref := Xmin; Yref := Ymax; end; Actually, the test for whether Max is greater than Min is shown here only as an example. For practical reasons this test is made elsewhere in the accompanying program. Second, the coordinates to be displayed in Image1 are calculated via functions:

Figure 1: The user interface The problem When we draw the graph of a mathematical function such as y = 2x + 3x - 5, we are used to a grid where positive X values go from left to right and positive Y values go from bottom to top. But what happens when we want to draw our graph in a TImage as provided by standard Delphi? If we draw a straight line from point (0,0) to point (100,100): with Image1.Canvas do begin Pen.Color := clBlack; MoveTo(0, 0); LineTo(100, 100); end; we see a line starting at the top left-hand corner and going down instead of up! So the Y direction is wrong in our view. How can we correct this? Other questions that arise are: what if we want to draw a line to point (10000,10000), and how can we display a graph with negative X or Y values? If we write LineTo(-100,10000), we don't get an error message, but still we cannot see point (-100,10000) on the screen. In order to display any kind of graph in the way we used to draw them (how long ago?) in math lessons at school, we must not only invert the Y axis but also provide some kind of scaling to fit the graph in our TImage. Maybe there is a ready-made solution for this problem, but we are programmers aren't we? We want to be able to solve this kind of problems ourselves! This article just might get you thinking in the right direction. I sincerely hope that it stimulates you to find solutions for similar programming challenges.

A possible solution
Suppose we want a graphics window bordered by a minimum X value Xmin, a maximum X value Xmax, a minimum Y value Ymin, and a maximum Y value Ymax (see Figure 1a). In our Form we define a TImage called Image1. The size of the image is irrelevant, the width is given in Delphi as Image1.ClientWidth, and the height is given as Image1.ClientHeight (see Figure 1b). The scaling factors required to translate X and Y values from our graphics window to coordinates in Image1 are directly apparent from Figures 1a and 1b:

Page 6 / 1780

March 2008 BLAISE PASCAL 1

Representing graphics for math functions (2)


function Xim(X: real): integer; var Xtemp: real; begin Xtemp := Xtransl * (X Xref); if Xtemp > Maxint then Xtemp := Maxint; if Xtemp < Maxint then Xtemp := Maxint; Xim := round(Xtemp); // must be integer end; The same function of is of course used for Yim. Fortunately, no check is necessary to determine whether the resulting coordinate position (Xim,Yim) is within Image1, as Delphi does not consider this as an error. Finally, in the program part where the graph is to be drawn on Image1.Canvas, the coordinates must be designated by e.g. LineTo(Xim(X), Yim(Y)). For each step, the Y value is calculated in the function CalcY, which has the function number (1, 2 or 3) and obviously the X value as parameters. This yields the following code: function CalcY(FuncNo: integer, X: real): real; begin case FuncNo of 1: CalcY := CoefA * X * X * X + CoefB * X * X + CoefC * X + CoefD; 2: CalcY := CoefA * Sin(CoefB * X + CoefC)+ CoefD; 3: CalcY := CoefA * exp(sqr(X + CoefC)/ CoefB) + CoefD; end; end; Here the variables CoefA etc. are of course the coefficients entered by the user (or, in the first instance, by the program). In the procedure that reads the coefficients from the Edit boxes, a warning is given if CoefB is 0 when FuncNo 3 is selected. procedure DrawGraph; var XStep, X, Y: real; begin with Image1.Canvas do begin Pen.Color := clBlack; XStep := (Xmax Xmin) / ClientWidth; X := Xmin; Y := CalcY(FuncNo, X); MoveTo(Xim(X), Yim(Y)); repeat X := X + Xstep; Y := CalcY(FuncNo, X); LineTo(Xim(X), Yim(Y)); until X >= Xmax; end; end;

The Program
As a demonstration, I wrote a simple program called MathGraphs that can display three different mathematical functions: 1. Y = AX + BX 2 + CX + D 2. Y = A*(sin(BX + C) + D 3. Y = A*e^[{(X + C)}/B] + D The coefficients A, B, C, and D and the borders of the graphics window are pre-set, but they can be changed. See Figure 2.

It should be noted that we (quite conveniently) chose mathematical functions that are continuous and always have one Y value for every possible X value. If another type of function is chosen, such as a hyperbolic function, (Y = C/X), an exception must be made for the case where X equals zero. This means that if you want to extend the program with your own functions, part of the DrawGraph procedure must be rewritten! The other procedures are not too difficult to understand by examining the listing (which is of course available on the website). p.t.o.A Figure 2: Graphical representation of a function The mathematical function to be shown by the graph is selected by pressing one of the buttons: Function 1, Function 2 or Function 3. The display parameters, i.e. the coefficients of the formula and the scale values for the graphics window, are set and the graph is displayed. The user may now play with the display parameters by altering the values in the Edit boxes. The program procedure for this is not described here, but as explained earlier in this article, entering a value for the minimum that is greater than the maximum is corrected automatically. If invalid data are entered (e.g. letters instead of figures), the box turns red and a warning is displayed. Pressing ReCalc results in a new presentation of the graph, using the new settings. This is done by the procedure DrawGraph. The graph is basically a line consisting of pixels. For every X coordinate of a pixel on this line in the graphics window, we want to calculate the value of the Y coordinate. We know that the number of pixels in the X direction is equal to ClientWidth, so we have to run through the procedure in (Xmax Xmin)/ClientWidth steps, starting with Xmin.

To advertise in

BLAISE PASCAL
email advert@blaisepascal.eu mob. + 31.6.21.23.62.68
Page 7 / 1781

March 2008 BLAISE PASCAL 1

Representing graphics for math functions (3)


Before DrawGraph is called, a ClearScreen procedure is executed which colours the graphics window white. If the graphics window contains the positions X = 0 or Y = 0 then a green line is displayed for visual reference. Three additional buttons have been provided on the program interface: Zoom In and Zoom Out to change the scale by a factor 2 and Aspect to correct the aspect ratio of the graphics window such that X and Y scales are the same. In this way the graph will have the same shape as if drawn on graphic paper. In other words, the function Y = X will display a line of 45 upwards. For the zoom functions it is necessary to calculate first the mid-position of the graphics window (e.g. Mid := (Xmax + Xmin)/2) to accomplish that he centre of the window remains the same. The new scale can be subsequently calculated with respect to this midposition.

Conclusion
This program is only intended to demonstrate that the suggested solution for the graphics window really works. It is only a basic example, and I leave it to your creativity to extend the functionality, in order to solve problems like: Adding a button to always show coordinate (0,0) in the centre. Displaying non-continuous functions (e.g. orthogonal hyperbola or tangent). Displaying functions with two Y values for each X value (e.g. ellipse or non-orthogonal hyperbola). Calculating and displaying a tangent line for an X value selected by the cursor. Using the program for curve fitting. A basic knowledge of mathematics is necessary for solving these problems. If you need help and you'd like to see another article that deals with these or similar problems, let me know.

Client Dataset Toolkit Update


Some time ago I developed the Client Dataset Toolkit. It has now been upgraded with a new version, some known bugs have been fixed, and the interface has been radically redesigned. A free download is available to interested subscribers of Blaise Pascal. Send your request to office@blaisepascal.eu

by Detlef Overbeek

Fixed bug: program could only be installed in C:\ Enhancements: New, simpler user interface. For revising the details of a Client Data Set, there is now a second form that is fully sizable in all directions. Now you can change the contents of the CDS. To start with, you can delete or insert records, etc. Next you can add or insert fields or change field properties, or even remove fields. There is no other dataset tool that lets you do this, not even in Delphi at design or run time.

Here you can see the scaling of the user interface clearly.

Page 8 / 1782

March 2008 BLAISE PASCAL 1

Website name checking


Using the new 'for in do' instruction, it's easy to check the characters in a website name. In this way it's possible to check the syntax of a website (but not if the website exists, of course). For those who can't use the new Delphi 2005 'for in do' instruction I'll show an alternative course. Another way to check the syntax is to use the Windows function PathIsUrl, as I will demonstrate. For in do" example.
Before checking the website name, I would like to show you another example of using the 'for in do' instruction. Suppose you have a large number of fields for data entry (Edits or DBEdits). How can you be sure that no false space characters are left? False space characters can be very annoying. For example on testing if a field is empty (on required input). Or on testing the maximum length of the input. The user doesn't see the space at the beginning or the end. But gets an error message which seems to have no meaning at all. The code to remove false space characters in all (DB)Edits is given below: procedure TForm1.BitBtnOKClick(Sender:TObject); var C: TComponent; begin for C in Self do if C is TEdit then TEdit(C).Text:= Trim(TEdit(C).Text); { CHECK required fields, etc } Close; end; By replacing the TEdit in the code with TDBEdit you can get the same functionality for DBEdits. Apart from that, you may replace Self by Form1, which is the same. For Delphi 7 or earlier versions, it's possible to avoid the "for in do" instruction. But such code is less elegant, as you can see: procedure TForm1.BitBtnOKClick(Sender:TObject); var C: TComponent; i: Integer; begin for i:= 0 to Self.ComponentCount - 1 do begin C:= Components[i]; if C is TEdit then TEdit(C).Text:= Trim(TEdit(C)).Text); end; { Check required fields, etc } Close; end;

2007

ZIPFILE

by Henk Schreij

You can find an example of how to use this function, in Figure 1 and the code below. There is an error shown of a dollar sign in the name of the Website. But, the most common error you probably will find, is a comma instead of a dot.

Figure 1: Example of finding an error (dollar) in a website name


procedure TForm1.BitBtnOKClick(Sender: TObject); var C: TComponent; begin for C in Self do if C is TEdit then TEdit(C).Text:= Trim(TEdit(C).Text); if not IsCorrectWebsite(Edit4.Text) then raise Exception.Create('Website error); Close; end;

Extending the sitename check The function you have


seen will find most of the errors. However, there are still some improvements to find. For instance, at least one dot must occur in the name (I accept sitenames without www. at the start, because most browsers insert this prefix automatically). But finishing or starting a sitename with a dot, or putting two dots side by side, is not allowed. Or someone might use the official naming convention, with http:// at the start. The code that takes these into account is given below: function IsCorrectWebsite (S: string):Boolean; var K: Char; begin Result:= False; if RightStr(S, 1) = '.' then Exit; if Pos('.', S) <= 1 // At least one dot, but not in front. then Exit; if AnsiStartsText('http://', S) then S:= Copy(S, 8, Length(S)); for K in S do if not (UpCase(K) in ['A'..'Z',0'..'9','-', '.']) then Exit; Result:= True; end; With this code you can trace 99 percent of all errors. Not all, because there are some additional requirements. To name a few: the length of the parts can be 63 characters at maximum and 3 characters at least, the parts must not exist of numbers only, etc.

Here too, you can write Form1 instead of Self. But you can simply omit the Self as well.

Checking the website name


Not all characters are allowed in a website name. The domain name must consist of alphanumerics or hyphen(s) and the parts must be separated by a dot. The following function does these checks (don't forget the use of brackets after the "not"): function IsCorrectWebsite(S: string): Boolean; var K: Char; begin Result:= False; for K in S do if not (UpCase(K) in ['A'..'Z', '0'..'9', '-', '.']) then Exit; Result:= True; end;

Using the Windows function PathIsURL


There is a possibility to test a website name with a level of 100 percent accuracy. This facility is provided by the Windows function PathIsUrl. But it comes with a disadvantage because the testing is rather tight, and the http:// and www. prefixes are always required. The following code uses this Windows function to reach this goal: Don't forget to add ShlwApi to the uses clause. uses Shlwapi; function IsCorrectWebsite (S: string):Boolean; begin Result:= PathIsUrl(PChar(S)) end; I leave it to the reader of this article, to test the given sitename for the existence of http:// or www and add these automatically when needed. By the way, take into account that this function also accepts https:// and ftp:// as prefix.

March 2008 BLAISE PASCAL 1

Page 9 / 1783

Page 10 / 1784

March 2008 BLAISE PASCAL 1

Practical database normalisation


When you generate a computer program, you often start by structuring the data in a database. For instance in Access, Paradox, Interbase, MySQL, SQL Server, Blackfish or Oracle all examples of relational databases. With these databases, the data are put into tables. Some thirty years ago a theory was developed about how to best arrange these tables: normalisation of relational databases.
Hierarchic or relational Data in a database can be structured in several ways. Two commonly used forms are hierarchic and relational structures. An example of a hierarchic structure is the ordering of plants and animals. For instance, the species lion and cat are in the cat family, which in turn are mammals. Using XML, this hierarchy could be represented by nesting the less abstract term in the more abstract ones: <mammals> <cat_family> <species>lion</species> <species>cat</species> </cat_family> </mammals> In a relational database, the data are put into tables that are related. The data above could be put into a table as follows:

by Herman Peeren

Table: courses
Course_Name
Programming Programming Programming Web Design Web Design Web Design Databases Databases

Names_and_Addresses_of_Students
Fatima Guermat, Streetway 16, Birmingham

Lucky Luck, Bingoroad10, Fortune City Chris... etc. John Shepard, Wooldrive 33-a, Heather Habiba Hadith, Beachpark 14, Bristol Simone etc And also here just one name and address Every student has her own row (record)

Depending on how the data will be used, you can split the structure into more fields. For instance: name, address, city. Or even further, for instance: first name, middle name, family name, street, number, number suffix, postal code, city. The degree of splitting depends entirely on the expected use of the data, because that is the only basis for deciding which value are atomic (indivisible). This kind of analysis is therefore not a mechanical matter; there will always be some interpretation involved. In the case of names and addresses, however, it is generally good practice to at least split the data into the first name or initials, family name, address, postal code, and city.

Table: mammals
species lion cat family cat cat

Avoid redundancy
Up to now we have split the data into different fields and rows (records) in a single table. Next lets look at how we can distribute the data over several tables. Suppose that a student in our example is taking several courses. In this case, his or her address will also appear in the table more than once. This could cause a lot of trouble: for instance, if by accident the address is only updated in one location but not in the others. Then we would be faced with two different addresses which one is correct? This kind of duplicate (and thus superfluous) data is called redundant. Another example: if more data will be recorded about the courses (for instance: the date, location and fee), the same data will be repeated over and over again. The solution is to create several different tables: one containing data about students and addresses, another one with course data, and yet another one that couples courses with students. In schematic form, this would be something like:

A big advantage of saving data in tables is that data retrieval is very easy and fast, without any need for the software to first traverse all kinds of tree structures. A disadvantage is that many sorts of intuitive, often hierarchic relationships are less clearly presented. The relational model is presently the most commonly used data model. But how can you best arrange the data in the various tables?

1st normal form: atomic fields


Suppose an institution organises courses in computer science. The data to be recorded are which students are taking which courses. You could create a table with two columns (fields): the course name and the names and addresses of the students. This would produce something like:

Table: courses
course name
Programming

Names_and_Addresses_of_Students
Fatima Guermat, Streetway 16, Birmingham; Lucky Luck, Bingoroad10, Fortune City; Chris... etc.

Web Design

John Shepard, Wooldrive 33-a, Heather; Habiba Hadith, Beachpark 14, Bristol; Simone etc.
Here again a list of names and addresses separated by semicolons.

Databases

Figure1: Relationship diagram in MS Access A scheme like this is called an entity relationship diagram (in this case generated in Access). The name of the table is put at the head of the box. The field names are listed below, with the key field in bold or underlined. Each record in the course table is uniquely identified by the cId field, and each record in the student table is identified by the sId field. This sort of unique record identifier is called a key, and

With this arrangement, however, it is difficult to select all students from a certain city or change the address of a particular person. This would be much easier if only indivisible values were put in the fields. For instance: the name and address of only one student in the student names and address field, instead of data for several students, separated by semicolons. This would give us something like this:

March 2008 BLAISE PASCAL 1

Page 11 / 1785

Practical database normalisation (2)


In this way a student can attend different courses and a course can have more than one student (we even hope that will be the case). This is called a many-to-many relationship between courses and students. This many-to-many relationship can be split into two one-to-many relationships: the new course_student table shows that a student can take more than one course AND that a course can have more than one student. In the relationship diagram, an arrow is drawn on the 1side. With this table, we can record which courses are given where and which students take which courses. If each course is always given in only one specific location and this is not expected to change, it is not necessary to state the location identification for every occurrence in this table. In this case, it would be better to move it to the course table. This again is a matter of estimating how things will develop in the future more a subtle art than a mechanical procedure. There is another normal form that combines 2nd and 3rd normal forms and takes into account that there can be more than one (candidate) key: Boyce/Codd normal form (BCNF) Ensure that all fields in a table are only determined by a candidate key. If a table is in 3NF (or BCNF), you can say that it is normalised. This means that the major redundancy traps will be avoided. There are also fourth and fifth normal forms, but they are simply extensions of the dependency concepts of the first three normal forms for use in relatively uncommon situations.

Splitting tables: 2nd and 3rd normal forms


The theory of database normalisation gives us formal rules on how and when tables should be split. Often an intuitive approach takes us quite a way in the right direction: Put distinct things (entities) in their own table. Repetition of the same data is often an indication that something should be split. The same holds for columns with many null entries. Look for one-to-many relationships in a table and split them into two tables. Look for many-to-many relationships in a table and split the table into three separate tables, as in the example above. Each record in a table should have a unique key to identify the record, and possibly a set of mutually independent fields (attributes) that further describe the record. This concept is formalised in the theory of normalisation (relational calculus), but in everyday practice the procedure described below will suffice for gradual transformation from the 1st normal form to further normalisation. 2nd normal form (2NF): Put all fields that are not dependent on the entire key in a separate table. Suppose we put the locations for our courses in a table with the following fields: building, room, and address. The key (unique record identifier) is a combination of the fields building and room. You can write this as follows: locations (building, room, address) However, if each building has only one address, then the address field is dependent on only part of the key. This could be split into two tables as follows: building (bId, bName, bAddress) locations (bId, room) Here we created a new unique key (bId) in the building table, so that we can also put different buidings with the same name in the table (Crossroads in Brighton and Lancashire, for instance). Usually an auto-increment field is used for such a key. A short key like this is easier to use as a reference in other tables. In the locations table, such an identification number will also always be used in practice, and it provides an easy way to reference a location in another table. As a result of using an extra field as key, there are now two possible keys to uniquely designate a record: the new identification number, which is in fact used as the key (the primairy key), and the combination of the fields bId (building identification) and room. All possible keys are called candidate keys, and the key that is actually used is called the primary key. 3rd normal form (3NF): Put transitive dependent attributes in another table. This means fields that are also dependent on a non-key field. This usually implies a one-to-one relationship. Suppose our example institution uses the following database table, consisting of three foreign keys, to reference records in other tables:
course_location_student (courseId, locationId, studentId)

Every advantage has its disadvantage


(lk voordeel heb zn nadeel) This is a literal translation of a well-known statement by the famous Dutch football player Johan Cruyff. Splitting the data into several tables has the advantage of reducing redundancy, but it also makes table management more difficult. For instance, tables have to be joined temporarily to obtain any usefull information from them a pile of identification numbers of courses and students doesn't say much. You also have to be careful not to discard necessary data. For instance, if a location identification number is used somewhere in a table, this information must remain available in the location table. The latter restriction is called referential integrity, and it can be enforced automatically in most database management systems.

Literature
Almost every book about using databases has something to say about about normalisation. Tons of information can also be found on the internet, e.g.: http://en.wikipedia.org/wiki/ Database_normalization , www.serverwatch.com/tutorials/ article.php/1549781 , http://dev.mysql.com/tech-resources/ articles/intro-to-normalization.html or www.databasedev.co.uk/ database_normalization_process.html A classic book, first published 1975 but still beingis: C. J. Date, An Introduction to Database Systems (Addison-Wesley, 8th edition, July 2003). There are more publications from the same author the relational model. Date worked together with Codd, the founder of normalisation theory. Herman Peeren, Rotterdam, December 2007. herman@yepr.nl

Readers
Please send your questions and comments to: email office@blaisepascal.eu or to: Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands
Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68

Page 12 / 1786

March 2008 BLAISE PASCAL 1

Delphi 2007 and VCL component building


In this article, Ill show you how to create and install new custom components and existing components in Delphi 2007 for Win32. This is something that has been possible since Delphi 1, of course, but the screens and techniques have changed a little bit recently. TShadingLabel
The main example here is a TShadingLabel. For this new custom component, we'll enhance a regular Tlabel component with the ability to paint itself using different kinds of shades: lowered, raised, or normal (no shade at all). Before we can create this new component or add an existing component we first need to create a new package to serve as a component container. In order to create a new package, we have to start Delphi and do File | New Other. In the Object Repository, we find the Package icon in the Delphi Projects category:

2007

by Bob Swart

In the next page of the New VCL Component wizard, we can specify the name of the new component as TShadingLabel, the palette page as eBob42, and the unit name as ShadingLabel.pas.

Figure 3: New VCL Component Wizard forTShadingLabel Click on Finish to generate the new unit for TShadingLabel. The initial source code is still quite empty, as follows: unit ShadingLabel; interface uses SysUtils, Classes, Controls, StdCtrls; type TShadingLabel = class(TLabel) private {Private declarations } protected {Protected declarations } public {Public declarations } published {Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('eBob42', [TShadingLabel]); end; end. Note the procedure Register, which is only used to register the TShadingLabel component in the Tool Palette. Next time, we'll extract this procedure from this unit and place it in a unit of its own, to separate the design-time behaviour from the runtime behaviour of the component. This can also be used to create two different kinds of packages: a runtime package and a design-time package. But that's a story for next time. For now, let's continue with the TShadingLabel control.

Figure 1: Object Repository and Package icon If we double-click on the Package icon, we get a new package with the name package1, which we should save as package eBob42 using File | Save Project As. The package is still empty, since it contains no units in the Contains node, but it already has the RTL package in its Requires node. With this new package, we can now create a new component using File | New Other by double-clicking on the Component icon in the Delphi Files subcategory of the Delphi Projects category in the Object Repository. The result is the New VCL Component Wizard, where we first need to specify that our new custom component is derived from the TLabel class:

Visual Properties
The TShadingLabel should support three styles: lowered, normal and raised. The idea is simple: apart from the current text itself, we also write the text using a black font and a white font. For a lowered style, the black text is positioned one pixel down and to the right, and the white text is positioned one pixel up and to the left. For a raised style, the positions are swapped, and for a normal style I don't write the white and black texts at all.

Figure 2 : New VCL Component Wizard

March 2008 BLAISE PASCAL 1

Page 13 / 1787

Delphi 2007 and VCL component building (2)


For big font sizes, a single pixel may not be enough, which is why I'm also using an Offset property by default set to a value of 1, but you may set it to 2 or higher for font sizes bigger than 16 for example. First, let's add the LabelStyle type and the new Offset and LabelStyle properties. The TLabelStyle is defined as follows: type TLabelStyle = (lsLowered, lsNormal,lsRaised); Note that we have to place this type definition before the TShadingLabel type definition (as otherwise we cannot define a property or field of type TLabelStyle). For the two new properties, we only need to add the following to the published section of the TShadingLabel class: property LabelStyle: TLabelStyle; property Offset: Integer; And then hit Ctrl+Shift+C to generate the property defnition and set method skeletons. The TShadingLabel class should now look as follows: type TShadingLabel = class(TLabel) private FLabelStyle: TLabelStyle; FOffset: Integer; procedure SetLabelStyle(const Value: TLabelStyle); procedure SetOffset(const Value: Integer); {Private declarations } protected {Protected declarations } public {Public declarations } published {Published declarations } property LabelStyle: TLabelStyle read FLabelStyle write SetLabelStyle; property Offset: Integer read FOffset write SetOffset; end; The implementation of the SetLabelStyle and SetOffset methods has been generated as well, assigning the Value argument to the FLabelStyle and FOffset fields respectively. However, we need to add a little bit of code to these implementations. Specifically, since we are building a visual control, changing the LabelStyle or Offset properties should force the control to repaint itself. This is done using a call to Invalidate. Of course, we shouldn't call Invalidate too often, so the correct implementation of the SetLabelStyle and SettOffset should be as follows: procedure TShadingLabel.SetLabelStyle (const Value: TLabelStyle); begin if FLabelStyle <> Value then begin FLabelStyle := Value; Invalidate end end; procedure TShadingLabel.SetOffset (const Value: Integer); begin if FOffset <> Value then begin FOffset := Value; Invalidate end end;

Constructor
We should now also override the constructor of the TShadingLabel, in order to initialise the two properties with their default values. The easiest way to override the constructor is to move the cursor inside the class definition for TShadingLabe, and press Ctrl+Space. This will show a list of all methods (procedures and functions) as well as the Create constructor. Select the Create constructor and press Enter. As a result of this action, the constructor Create will be added to the public section of the TShadingLabel component. Note that it doesn't matter where your cursor was positioned: the constructor (or any method you override this way) will be added to the correct section. public constructor Create(AOwner: TComponent); override; Press Ctrl+Shift+C to generate the skeleton implementation for the constructor, which we should finish off with three additional lines of code, as follows: constructor TShadingLabel.Create(AOwner: TComponent); begin inherited; FOffset := 1; FLabelStyle := lsLowered; Transparent := True end; Note that we need to set the Transparent property to True; otherwise the white and black text (representing light and shadow) will not be seen after the painting is done. Also note that we should use the default keyword to specify that 1 is the default value for the Offset property, as follows: property Offset: Integer read FOffset write SetOffset default 1; Note that we should also do this for the LabelStyle and a default value of lsLowered, although the assignment to FLabelStyle in the constructor might be unnecessary because the lsLowered value is the first item in the enumeration ahd has a default value of 0.

Paint Method
This brings us to the last method that we need to implement for the TShadingLabel component: the Paint method. Like the constructor, the easiest way to override this method is to enter the component definition and press Ctrl+Space. In the list of methods, select the Paint method and press Enter. This will produce the following definition of the TShadingLabel component: type TShadingLabel = class(TLabel) private FLabelStyle: TLabelStyle; FOffset: Integer; procedure SetLabelStyle(const Value: TLabelStyle); procedure SetOffset(const Value: Integer); protected procedure Paint; override; public constructor Create(AOwner: TComponent); override; published property LabelStyle: TLabelStyle read FLabelStyle write SetLabelStyle default lsLowered; property Offset: Integer read FOffset write SetOffset default 1; end;

Page 14 / 1788

March 2008 BLAISE PASCAL 1

Delphi 2007 and VCL component building (3)


As you may see, the Paint method is a protected method. It cannot be called from the outside, but it will be called automatically when needed (for example, when the Invalidate method is called). The Paint method writes the Caption string of the TShadingLabel two more times, first in white (the light) and then in black (the shadow), before calling the inherited Paint method to write the Caption in the original way (on top of the light and shadow). Note that we have to explicitly cast the Caption to a PChar when passing it to DrawText. procedure TShadingLabel.Paint; const Alignments: array[TAlignment] of Word =(DT_LEFT, DT_RIGHT, DT_CENTER); var Len: Integer; // Caption Length CalcOffset: Integer; OldColor: TColor; TempRect: TRect; begin Len := Length(Caption); CalcOffset := (Ord(FLabelStyle)-1) * Foffset; if CalcOffset <> 0 then try OldColor := Font.Color; Canvas.Brush.Style := bsClear; Canvas.Font := Self.Font; Canvas.Font.Color := clWhite; // white light TempRect := Rect (ClientRect.Left - CalcOffset, ClientRect.Top - CalcOffset, ClientRect.Right - CalcOffset, ClientRect.Bottom - CalcOffset); DrawText (Canvas.Handle, PChar(Caption), Len, TempRect,DT_EXPANDTABS or DT_WORDBREAK or Alignments[Alignment]); Canvas.Font.Color := clBlack; // black shadow TempRect := Rect(ClientRect.Left + CalcOffset, ClientRect.Top + CalcOffset, ClientRect.Right + CalcOffset, ClientRect.Bottom + CalcOffset); DrawText(Canvas.Handle, PChar(Caption), Len, TempRect, DT_EXPANDTABS or DT_WORDBREAK or Alignments[Alignment]) finally Canvas.Font.Color := OldColor end; inherited Paint // paint original Caption end; The DrawText function is part of the Win32 API, which is why the Windows unit needs to be added to the uses clause of the implementation section of the unit ShadingLabel.pas. And we should add the Graphics unit for the TColor type.

Figure 4 : eBob42 package in Project Manager

Figure 5 : Installation This confirms that the component TShadingLabel is now also installed in the Tool Palette of Delphi.

Using TShadingLabel
Now take a look at the Tool Palette and open the eBob42 category, which should show the TShadingLabel component. Double-click on the TShadingLabel component or drag it to the VCL Form. This will place the ShadingLabel, with the LabelStyle property set to lsLowered and the Offset set to 1 by default. Since the default font colour of a TLabel is black (the same colour as the shadow), at first the TShadingLabel doesn't look that good. If you change the font style, size and colour, however, the TShadingLabel will start to look better. With the Font set to Comic Sans MS, style Bold, size 18 and colour Red, the Offset can be set to 2 and the lsLowered and lsRaised label styles appear as follows:

Figure 6: TShadingLabel in action There's one thing you may have noticed in the Object Inspector if you displayed the properties by category: the new properties Offset and LabelStyle are only shown in the Miscellaneous category, but not in any of the other categories. That's something that we'll fix next time, as well as the component bitmap and the issue of the package, which now contains runtime as well as design-time code and should be split in two. All this and more will be covered next time, so stay tuned! Bob SwartBob Swart Training & Consultancy (eBob42) http://www.drbob42.com

Compilation and installation


When you've finished all this, you can compile the eBob42 package. The Project manager will now show the ShadingLabel.pas unit in the Contains node, alolng with the VCL and the RTL packages in the Requires node. Feel free to add any more new or existing custom components to the Contains node this is the easiest way to register and install them in Delphi. In order to install the eBob42 package and the component(s) inside it, we have to right-click on it and select Install. That will install the package and execute the Register procedures (if any) to register the TShadingLabel component, which results in the following message box:

Download the code for all issues from: www.blaisepascal.eu


Page 15 / 1789

March 2008 BLAISE PASCAL 1

Wallpaper

2005 Win32

ZIPFILE

by Detlef Overbeek

The main task of this program (Wallpaper) is to arrange a nice desktop for you by placing an image of your choice on the background. As this might not be much for a demonstration, I thought of giving it some extras that need some interesting code, which could also be reused in completely different applications. It shows how you can put together your own database without having to create one or even install it. You might also want to print out the beautiful picture on your desktop and send it as a postcard. Create some party-like text on the card and send it right away to all your friends (or enemies?). I'm not sure that theyll appreciate it ... Structure
The subject of this program is divided into several lower-level subjects, each with its own code. I think this makes it easier to reuse the individual parts of the program. 1. Opening files: page 35 2. Renaming files: page 35 3. Rotating images: page 36 4. AboutBox: next issue (Blaise Pascal 2) 5. Printing: next issue (Blaise Pascal 2) Whenever you are creating an application, it is nice to have some version information. Well cover this completely in the next issue: Blaise Pascal 2

It looks ugly!
But it shows quite clearly how easy it is for the end user to use it to creating his or her own text with lots of sizes, colours and fonts. Freedom after all!

Page 16 / 1790

March 2008 BLAISE PASCAL 1

Coding for two or more types


Say you want to make a function that has to work for an 'Edit', but it has to work for a 'DBEdit' as well. Or for an 'integer', as well as for a 'float'. When you use Delphi, with its strict type recognition, that's a problem. A problem that you can solve in a number of ways. I'll show a couple of examples, in the form of 'overload' and in the form of 'type recognition' (using the is operator or using 'ClassType'). After which I'll show you an introduction to the new way to solve this problem: Generics. probably available in Delphi 2008 (it's already there in Delphi2007.net). Example
The problem is, when you want to show results, you need data. And so the old Paradox DBDemos comes in handy. On the Form, use a Table from the tab BDE, a DataSource and a DBNavigator. Fill in the Table fields as: DatabaseName = DBDemos; Tablename = Custoly.db and Active = True. Fill in the DataSource with Table1 as DataSet. And the Navigator with DataSource1 as DataSource, and set a couple of the VisibleButtons to False so you're left with four, as shown in figure 1.

2007

ZIPFILE

by Henk Schreij

unit CodingtypeU1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DBCtrls, Mask, ExtCtrls, DB, DBTables, Buttons;

type TForm1 = class(TForm) DataSource1 : TDataSource; DBComboBox1 : TDBComboBox; DBEdit1 : TDBEdit; DBNavigator1: TDBNavigator; Edit1 : TEdit; Table1 : TTable; SpeedButton1: TSpeedButton; procedure DataSource1DataChange(Sender: TObject; Field: TField); private procedure HintAtNoFit(Edt: TDBEdit; OrgHint: string = ''); public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.HintAtNoFit(Edt: TDBEdit; OrgHint: string = ''); begin // ShowHint = True if Form1.Canvas.TextWidth(Edt.Text) > Edt.Width - 6 then Edt.Hint:= OrgHint + ' ' + Edt.Text else Edt.Hint:= OrgHint; end; procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last name'); end;

Figure 1: Example using DBDemos and Custoly.db

To show you something, I've added a DBEdit (linked to DataSource1 with DataField Last_Name) and a DBComboBox (with DataField VIP_Status). And I've set Table1 IndexFieldNames to Last_Name to sort the names alphabetically. We'll talk about the Edit and the SpeedButton later.

Some further explanation Coding for one type: Showing Hints


I'll use a piece of code that shows a Hint to a DBEdit if the text won't fit in the space, throughout this article. Calling this code for various types of controls is our goal. The code is in the procedure HintAtNoFit giving a Hint as in figure 2. To calculate the width of the text we use Form1.Canvas.TextWidth. Because Edits and DBEdits don't have a Canvas (to show the text), you have to use the underlying canvas of its Form. The minus six (-6) at the EditWidth is for the border (three pixels on both sides). The procedure declaration of HintAtNoFit has a parameter OrgHint: string. You can fill this in with a Hint "in case the name fits in the Edit". In this example as Last name. So you'll get "Last name" as Hint when there is a short name in the Edit. In the declaration of the parameter the string is set empty by default. In this way you can omit the parameter as in the next example:

Figure 2: Showing a Hint when the text won't fit. You have to call this procedure every time something changes in a DBEdit. The appropriate place is the procedure OnDataChange from the DataSource. Both procedures follow:

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last Name'); HintAtNoFit(DBEdit2); end;

March 2008 BLAISE PASCAL 1

Page 17 / 1791

Coding for two or more types (2)


Coding for more types
After developing such a nice procedure for a DBEdit, you probably want to use the same procedure for another type of component, i.e. a DBComboBox or an Edit. As a novice you're easily tempted to change the code for that component and call it with another name. For instance the following code for a ComboBox with the procedure HintAtNoFitCbx. (*Novice approach (discouraged) *) procedure DataSource1DataChange(Sender: TObject; Field: TField); private procedure HintAtNoFit(Edt: TDBEdit; OrgHint:string = ''); procedure HintAtNoFitCbx(Cbx: TDBComboBox; OrgHint: string = ''); public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.HintAtNoFit(Edt: TDBEdit; OrgHint: string); begin // ShowHint= True if Canvas.TextWidth(Edt.Text) > Edt.Width - 6 then Edt.Hint:= OrgHint +' '+ Edt.Text else Edt.Hint:= OrgHint; end; procedure TForm1.HintAtNoFitCbx(Cbx: TDBComboBox; OrgHint: string); begin if Canvas.TextWidth(Cbx.Text) > Cbx.Width - 24 then Cbx.Hint:= OrgHint +' '+ Cbx.Text else Cbx.Hint:= OrgHint; end; procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last name'); HintAtNoFitCbx(DBComboBox1, 'Status'); end; Figure 3. Error with the compiler note, to add 'overload With overload added to the declaration, the code will be like: (* using overload *) procedure DataSource1DataChange(Sender: TObject; Field: TField); private procedure HintAtNoFit(Edt: TDBEdit; OrgHint: string = ''); overload; procedure HintAtNoFit(Cbx: TDBComboBox; OrgHint: string = ''); overload; public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.HintAtNoFit(Edt: TDBEdit; OrgHint: string); begin // ShowHint= True if Canvas.TextWidth(Edt.Text) > Edt.Width - 6 then Edt.Hint:= OrgHint + ' ' + Edt.Text else Edt.Hint:= OrgHint; end; procedure TForm1.HintAtNoFit(Cbx: TDBComboBox; OrgHint: string); begin if Canvas.TextWidth(Cbx.Text) > Cbx.Width - 24 then Cbx.Hint:= OrgHint + ' ' + Cbx.Text else Cbx.Hint:= OrgHint; end; procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last name'); HintAtNoFit(DBComboBox1, 'Status'); end; Form1 is left out in front of Canvas.TextWidth. You can, because the procedure starts with TForm1, so the program "knows" it concerns Form1. The minus 24 pixels for the width of the text in the ComboBox, is 18 more than for the Edit because of the button.

Coding using 'overload' for more types


It is possible to declare HintAtNoFit for a DBEdit as well as for a DBComboBox, when followed by overload. If you forget, the compiler will recognize that the procedure is has been attached twice. And tell you to add 'overload', like in figure 3.

But chances are, that you'll need the same procedure for an Edit (not a DBEdit), a ComboBox, or something else. When using the procedure you'll encounter the problem that you have to remember all the exact procedure names. But there's another, better way: Your goal is to use HintAtNoFit for every component without distinction between an Edit, a ComboBox or whatever. You can accomplish this with an overload, or recognizing the type (using "is" or 'ClassType'). I'll show you some examples later on. But first a few notes about the former code: The default part ( = ' ' ) is not repeated in the Implementation section. On top it says: OrgHint: string = ' ', and at the bottom: OrgHint: string. The default value should be in the interface, and can (but doesn't have to) be repeated in the implementation part.

Page 18 / 1792

March 2008 BLAISE PASCAL 1

Coding for two or more types (3)


Attention: There should be a semicolon before and after the word 'overload'. And you have to add 'overload' behind every declaration. But in the implementation part you may not (!) add overload. If you do, you'll get an error. Coding with is for more types Delphi has an is operator to recognize the type. Is actually means is type. With this operator you can write a procedure, in which you recognize the type. To write a different piece of code for every type, you don't just declare the procedure for a DBEdit or a ComboBox, but for the ancestor for both: TControl. Because all the visible components are descendants of TControl. Then the code will be: (* using the "is" operator *) procedure DataSource1DataChange(Sender: TObject; Field: TField); private procedure HintAtNoFit(Ctr: TControl; OrgHint: string = ''); public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm}

Coding using 'ClassType' for more types


Delphi has another way to test the type, namely using the function ClassType, as you can see in the next code example: procedure TForm1.HintAtNoFit(Ctr: TControl; OrgHint: string); begin // ShowHint = True if Ctr.ClassType = TDBEdit then begin if Canvas.TextWidth(TDBEdit(Ctr).Text) > Ctr.Width - 6 then Ctr.Hint:= OrgHint + ' ' + TDBEdit(Ctr).Text else Ctr.Hint:= OrgHint; end; // if if Ctr.ClassType = TDBComboBox then begin if Canvas.TextWidth(TDBComboBox(Ctr).Text)> Ctr.Width - 24 then Ctr.Hint:= OrgHint + ' ' + TDBComboBox(Ctr).Text else Ctr.Hint:= OrgHint; end; // if end; The ClassType is much more strict than the is operator, because it won't take its ancestors into consideration. An example using Edit1 shows the difference: The result of "Edit1.ClassType = TEdit" will be True, but with "Edit1.ClassType = TControl" it will be False. Whilst "Edit1 is TControl" and "Edit1 is TEdit" will both be True. That's why the Delphi Help recommends using the is operator.

procedure TForm1.HintAtNoFit(Ctr: TControl; OrgHint: string); begin // ShowHint = True if Ctr is TDBEdit then begin if Canvas.TextWidth(TDBEdit(Ctr).Text) > Upgrading the code with error handling, etc. Ctr.Width - 6 then If you are going to re-use the code, for instance in a Library unit, it's Ctr.Hint:= OrgHint +' '+TDBEdit(Ctr).Text a good idea to upgrade the code so it can also be used for an Edit or a else ComboBox (no DB-'s). And make the code even more efficient (to Ctr.Hint:= OrgHint; calculate the TextWidth only once, among others). end; // if Because the procedure is now defined for a TControl, you don't get if Ctr is TDBComboBox then begin an error message when you use it for a Control without a Text if Canvas.TextWidth(TDBComboBox(Ctr).Text)> property, a SpeedButton for instance. It just gets stuck. That's why it Ctr.Width - 24 then is a good idea to show a warning at when it's used by an Ctr.Hint:= OrgHint +' '+ "unexpected" control. TDBComboBox(Ctr).Text A version with improvements follows. else (* improvements, error handling *) Ctr.Hint:= OrgHint; procedure DataSource1DataChange(Sender: end; // if TObject; Field: TField); end; private procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last name'); HintAtNoFit(DBComboBox1, 'Status'); end; Now the code inside HintAtNoFit will recognize if it is called by a DBEdit or a DBComboBox. And every type will be treated differently. For instance: with an extra subtraction for the button (24 in stead of 6). A few notes: Every control has a property Width and Hint. This means that you can simply use Ctr.Width and Ctr.Hint. When using the procedure with a DBEdit you will get the width and hint of the DBEdit, same with the ComboBox. Note that not every Control has a Text property, so you can't use Ctr.Text. To read the text, you have to use a typecast. That's why you see TDBEdit(Ctr).Text and TDBComboBox(Ctr).Text . procedure HintAtNoFit(Ctr: TControl; OrgHint: string = ''); end; var Form1: TForm1; implementation {$R *.dfm}

procedure TForm1.HintAtNoFit(Ctr: TControl; OrgHint: string); var S: string; i: Integer; NoFit: Boolean; begin // ShowHint = True S:= TEdit(Ctr).Text; i:= Canvas.TextWidth(S); if (Ctr is TDBEdit) or (Ctr is TEdit) then NoFit:= (i > Ctr.Width - 6) else if Ctr is TCustomComboBox then NoFit:= (i > Ctr.Width - 24) else raise Exception.Create( 'HintAtNoFit is not allowed with '+ Ctr.Name); if NoFit then Ctr.Hint:= OrgHint +' '+ S else Ctr.Hint:= OrgHint; end;

March 2008 BLAISE PASCAL 1

Page 19 / 1793

Coding for two or more types (4)


procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField); begin HintAtNoFit(DBEdit1, 'Last name'); HintAtNoFit(DBComboBox1, 'Status'); Edit1.Text:= Table1.FieldByName('Address1').AsString + ' ' + Table1.FieldByName('City').AsString + ' ' + Table1.FieldByName('Country').AsString; HintAtNoFit(Edit1); // HintAtNoFit(SpeedButton1); end; There are also a few extensions. E.g. the complete address, including town and country, is shown in Edit1 by a line of code in the OnDataChange of the DataSource. And if the text won't fit in the Edit, it'll be shown in a Hint, see figure 4. For instance when you use it for an integer, you fill in < Integer > (or Float, string, or whatever). On the internet there's a demo, with an example in which you switch two elements: procedure Swap < T > (var A, B: T) var Tmp: T; begin Tmp:= A; A := B; B := Tmp; end; Something you usually do when switching is, that you declare a temporary variable to recall the value. If you can't use generics, you have to program it separately for an Integer, a Double, a DateTime, a string, etc.. Now you can do it all in one go. After which you use it like: i:= 1; j:= 0; Swap <Integer>(i, j); Resulting in i = 0 and j = 1. But this example works just as well: S1:= 'John'; S2:= 'Peter'; Swap <string>(S1, S2); Figure 4. Address shown in Edit and Hint Functions like Swap, Count (using a List), etc. are available among others. But it'll become more difficult when the order is of importance. Think about the Max function on a string. Do you have to use the alphabetical order or the length? That's why you have to assign order criteria to each type (CompareTo), just like we're used to with a SortedList. And potentially what you have to do when you use a type without having criteria.

Some further explanation:


It doesn't matter if the text in a (DB)Edit or a (DB)ComboBox is shown, the text will be the same width. That's why the width (i) only has to be calculated once. A CustomComboBox, the ancestor of ComboBox as well as DBComboBox, is used. This works fine. It's too bad that an Edit and a DBEdit don't have a common ancestor we can use. That's why you have to do a double check for these two controls. If the procedure HintAtNoFit is not used with one of the other previously named components it will stop the compiling of the code with an error message from an Exception. For example, if you use it with a SpeedButton, see figure 5.

Conclusion
In the future coding for two or more types is solved by Generics. Until then we can just as well use 'overload', or type recognition with the is operator or 'ClassType'. An example that shows a Hint when the text won't fit in the space of an Edit, DBEdit, ComboBox or DBComboBox is used to demonstrate all solutions. But other functions and procedures, especially those you use in a Library unit, will benefit if you equip them for different types. Henk Schreij, translation Gert Schreij

Figure 5. Error when HintAtNoFit is used with a SpeedButton

Generics In the future for Delphi for Windows there will also be a new approach on code that has to work for more than one type. It will be possible to use a parameterized type in your code. It's an approach that works for all types. That's why this kind of code is called generics. The essence is that you insert the type just at the moment you're using it, and during the programming, you assign a placeholder for the type. As placeholder the expression < T > is used, a T for type parameter between a "less than" and "greater than" sign. Page 20 /1794

Special limited-time offer:

subcribe
to the printed version

for the first year for only 25.00


including all downloads, tax and postage.

March 2008 BLAISE PASCAL 1

Recipe for creating a cookbook


A paper cookbook is not always ideal: it's difficult to quickly give someone a copy of a recipe, and recipes you get from other people don't always end up in the right place. And how many times have you stood in front of the refri-gerator and asked yourself what you could make with the leftovers? After reading various articles in Blaise about the ClientDataSet (CDS) and trying a few things, I started to get a taste for it. The result is a computer cookbook.

2007

ZIPFILE

by Tim Opsteeg

A program that uses CDS is relatively easy to transfer to a different computer. In addition to the program, you only have to install the database file (in this case an XML file) and possibly a help file. If MidasLib is included in the Uses section of the program, no other files are necessary. See e.g. Blaise 65 for info on reading and writing these files and program deployment. You need the following ingredients for this program: A data module A main screen where users can search for recipes, add or delete recipes, and import or export recipes. A print screen where users can adapt the recipe printout to their personal taste (font and layout). A splash screen that is shown when the program starts up and contains the program version data, among other things. This is not discussed any further in this article. A help module. Of course, nobody reads program help files, but generating a help file does make you think: does the program have a good logical structure, and is it not too complex? The help file for this program is a compiled HTML file (Microsoft HTML Help Workshop). It is not discussed any further in this article. A copy of the consts.pas file. It's quite easy to create a different language version of the program by translating the resource strings (such as SMsgDlgWarning) in this file. Caution: the files of different versions of Delphi are not always interchangeable!

Datamodule
Figure 1: After the program is opened it is set to searchmode This module contains a ClientDataSet and a DataSource for the recipes. Among other things, opening and saving recipes is handled via this module. This brings up a tricky problem: this file must be present, and it must be possible to save it if necessary. It's easy to edit a read-only file after it has been loaded, but the user may be rather irritated if it cannot be saved afterward. The FindFirst function provides an easy way to determine whether the recipes file is present (see Listing 1). If the file is not present, the user is informed and the program closes. If the file cannot be saved, it is necessary to prevent the user from editing recipes. Finally, there are two other items that require attention: A variable of type TSearchRec is also loaded via the FindFirst function. Unfortunately, with this variable it is not always possible to determine whether a file has read-only access. For example, if the recipes file is placed in the shared documents folder with administrator privileges, a normal user cannot edit the file. Fortunately, this can be detected by using the very clear function CheckFilesReadOnly. See Listing 1 for more details. Do not set LogChanges of the recipes data file to false. During program testing, we found that with Delphi 2007 this can lead to irrecoverable damage to the XML file under certain conditions. Use MergeChangeLog before saving the file in order to prevent the file from becoming excessively large. Figure 2: Switch to the recipe tab, finally cooking?

Structure and needs


Before I started, I already had a few ideas about the program: Easy to install on other computers, and preferably suitable for running from a USB stick. The interface should be as simple as possible so the user doesn't have to wrestle with all sorts of menus (the recipes are what matter) In addition to finding recipes by name, it must be possible to search using criteria such as type of dish and ingredients. It must be possible to exchange recipes with other people.

function TDM.CheckFileIsReadOnly(AFileName: String): Boolean; var AHandle: THandle; begin Handle := CreateFile(PAnsiChar(AFileName), GENERIC_WRITE,FILE_SHARE_READ+FILE_SHARE_WRITE ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); Result := (AHandle = INVALID_HANDLE_VALUE); if (not result) then CloseHandle(AHandle) end;

March 2008 BLAISE PASCAL 1

Page 21 / 1795

Recipe for creating a cookbook (2)


procedure TDM.DataModuleCreate(Sender: TObject); with DM.ClientDataSet1, StringGrid1 var SrchRec: TSearchRec; do begin ... StringGridString := Value; try USelection := ''; if FindFirst(fPad,faAnyFile, SrchRec)<> 0 for CntCol := 0 to ColCount - 1 do begin then raise for CntRow := 1 to RowCount - 1 do begin EDataBaseError.Create('FileMist'); if Cells[CntCol,CntRow] > '' then begin { fReadOnly := SrchRec.Attr and SysUtils.faReadOnly <> 0; if USelection > '' then in this way it will not be observed whether it is allowed to write a file USelection := USelection + ' and '; in folder shared documents. For example: a normal user has no if CntCol > 0 then rights to change a file when it was added by an administrator. USelection := USelection + 'not '; However: this is not detected...} USelection := USelection +'(('+SelPref SysUtils.FindClose(SrchRec); + QuotedStr fReadOnly := CheckFileIsReadOnly(fPath); ('%'+Cells[CntCol,CntRow]+'%')+') ... or ('+ SelPref + QuotedStr except ('%'+#32+Cells[CntCol,CntRow]+'%')+'))' Listing 1. on EDatabaseError do ... end; end; Recipes end; The recipes database naturally includes the name of the recipe, the Filter := USelection; Filtered := true; preparation instructions, and a field for the ingredients. The ingredients ... Listing 2 have been intentionally placed together in a single field so that if the user wants to have a recipe that contains (or does not contain) a Filling in the tree view proved to be a rather demanding exercise. On particular ingredient, you only have to check the contents of one field. the Internet, I found a nice recursive method for filling the tree view. In addition, the user can enter information about the type of dish. For Sometimes you have to do something yourself in order to understand example: a soup can be an appetiser or a main dish, some soups are it better. vegetarian, some soups are fattening, and so on. The first item in the tree is hung on the nil node. When you are filling Searching the tree view, you have to pay attention to which TreeNode you place Category fields are therefore included in the recipe search function. something under. This means that each time you first look for the There are two unconstrained fields for categories, a Boolean field for node that corresponds to the recipe, and then you add the item to this warning that the dish is vegetarian (if relevant), and text fields for node. information about the cooker (cooking stove) and origin. Finally, the Now back to the soup. The user can classify a soup recipe as an database has fields for other information such a preparation time, the appetiser or (if appropriate) as a main dish. The user will probably calorie content, and the file name (including the path) of an associated enter 'soup' as the second category. Figure 1 shows an example. As image. you may have already guessed, if you aren't careful here, you'll Images quickly end up in the soup!

Illustrations can be quite large. To keep the database as small as possible, only references to images (if present) are included. NOTE: the AutoEdit property of DataSource is set to false, since you don't want to allow a recipe to be modified by accident while it is being viewed.

Main screen
This is the most used part of the program. The interface is a question of taste and limitations: some people can read really tiny letters on a large screen, while others configure their computer so only a few letters fit on the screen. For this reason, I decided on two tabs (PageControl) and a 10-point font. One tab is for searching for recipes, while the other tab is for displaying recipes. How do you search for a recipe? Sometimes you search by title, sometimes by the type of dish (e.g. main dish), sometimes by the type of cooking (e.g. a baked dish), sometimes for a non-vegetarian dish, and so on. This means that it must be possible to sort the recipes in various ways. In addition, you want to be able to specify which ingredients may or may not be included in the recipe. Finally, you want to be able to simply select the recipe from a list. Consequently, the Search tab has the following components: a TreeView, a RadioGroup, a StringGrid, and a Button. The idea is that the user can use the RadioGroup to specify the desired type of sorting, the StringGrid to specify which ingredients may or may not be present, and the TreeView to select the recipe. The method use to search the CDS is implemented using a filter, and it is essentially described at page 32(Rave Report). The user can indicate which ingredients must be present in the left column of the StringGrid and which ingredients must not be present in the right column (e.g. includes sugar but not salt). See Listing 2 for the program code. The user can use the Button to quickly remove the filter. A tree view resembles Windows Explorer, and here it allows the recipes to be show in clearly defined categories. The good news for

Figure 3: The inflatable tree-node comes in very handy

Figure
So how do you go about this? To start with, it's helpful to first sort the database by the requested categories. This way you only have to check whether the name of a category has changed when you are filling in the tree view, so you don't have to search for the nodes of the corresponding category every time. The sorting can thus be determined when the user marks the categories to be used for searching in the RadioGroup. The CDS can be sorted by using the IndexName or IndexFieldName properties. The various indices can be created in the IndexDefs property during the design. I assigned each index a name and a sequence number, such as 'ClientDataSetIndex1'. This works as follows in combination with the RadioGroup. Suppose the user selects the second item (ItemIndex = 1). The built-in Format function is then used to determine the desired index (ClientDataSetIndex1). The key to all this is that the items in the RadioGroup have the same sequence as the index names. After the database has been sorted, you can fill the tree. This is done by referencing the field names containing the categories. See Listing 3 for the details.

Page 22 / 1796

March 2008 BLAISE PASCAL 1

Recipe for creating a cookbook (3)


procedure TForm2.RadioGroup1Click(Sender:TObject); procedure TForm2.FillTree(Section1, Section2: string); begin var S1, S2, S1Prev, S2Prev: string; ... TrNode1, TrNode2: TTreeNode; with DM.ClientDataSet1 do begin // voor vullen boom eerst sorteren IndexName := format('ClientDataSet1Index%d', with TreeView1.Items, DM.ClientDataSet1 do begin [RadioGroup1.ItemIndex]); // clientdataset try DisableControls; case RadioGroup1.ItemIndex of Clear; 0: VulBoom('Titel',''); First; 1: Vulboom('Origin',''); while not Eof do 2: Vulboom('Vegetar',''); begin 3: Vulboom('Machine',''); S1 := FieldByName(Rubr1).AsString; 4: VulBoom('Section1','Section2'); if Bof 5: VulBoom('Section2','Section1'); then begin end; S1Prev := ... FieldByName(Section1).AsString; Listing 3 end; TrNode1 := Add(nil, S1Prev); if Section2 <>'' The code for filling the tree is shown in Listing 4. First the content of then S2Prev := the tree is deleted. After this the database is read out. The names of #20+FieldByName(Section2).AsString; the first (S1) and second (S2) categories are read each time. These // for 1st comparison names are compared with the names of the categories in the previous end record (S1Prev and S2Prev, respectively). A special situation arises else with the first record if not AnsiSameText(S1, S1Prev) No names from the previous record are known at this time. For then TrNode1 := Add(Item[0],S1); the reason, S1Prev is set equal to S1. if Section2 <>'' The name of the first category is linked to an empty tree by then begin means of Add(nil, ), which means that this is the first node. S2 := FieldByName(Rubr2).AsString; The name of the second category is stored in S2Prev with the if(not AnsiSameText(S2,S2Prev)) prefix #20:. The node for this category is not placed under the or(notAnsiSameText(S1, S1Prev)) first node until later on in the code. The nodes are added as then s2 <> s2prev, which means that s2 and s2Prev must have begin different values. TrNode2 := AddChild(TrNode1,S2); With each of the subsequent records, as test is made to see whether S2Prev := S2; the name of the category has changed. A difference in letter case end must not be regarded as a new category ('Soup' is the same as 'soup'). end For the first category add: else TrNode2 := TrNode1; if Rubr1 <> 'Titel' if not AnsiSameText(S1, S1Prev) then AddChild(TrNode2,FieldByName then TrNode1 := Add(Item[0],S1); ('Titel').AsString); (part of Listing 4) S1Prev := S1; // comparison was also required upon change of S2 For the next category, use AddChild(TrNode1,S2) to add a child. Next; Naturally, you only add a child if there is a category and the name of end; the first category or the name of the second category has changed. if Rubr1 = 'Vegetar' then begin TrNode1 := FindNode(Item[0],'True',0); if Rubr2 <> '' then TrNode1.Text := 'Vegetarian'; begin TrNode1 := FindNode(Item[0],'False',0); S2 := FieldByName(Rubr2).AsString; TrNode1.Text := 'Not vegetarian'; if (not AnsiSameText(S2, S2Prev)) end; or (not AnsiSameText(S1, S1Prev)) Groupbox5.Caption := then format(NumberRecipes,[RecordCount]); begin finally TrNode2 := AddChild(TrNode1,S2); EnableControls; AlphaSort(True); S2Prev := S2; end; // note: TrNode2 is allways initialised end; end; // ignore warning compiler ... end (part of Listing 4) Listing 4 end; As it is necessary to test whether the name of the first category has changed before testing the second category, S1Prev is not set equal to S1 until after this has been verified. Note: DisableControls is called during tree filling in order to improve the display speed of the program. After the tree has been filled, a check is made to see whether sorting by the 'vegetarian' criterion has been requested. As 'True/False' is not overly user-friendly here, the names of the nodes are modified in this case. Finally, tree is sorted in alphabetic order.

Finally, a recipe must be shown when the user clicks the name of a recipe. If there is a click in the tree view, a test is first made to see whether an item has been clicked (see Listing 5). p.t.o.A

Download the code for all issues from: www.blaisepascal.eu


Page 23 / 1797

March 2008 BLAISE PASCAL 1

Recipe for creating a cookbook (4)


procedure TForm2.TreeView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var HT: THitTests; begin if TreeView1.GetNodeAt(X,Y) = nil then begin fSelectionOK := false; Exit; end; HT := TreeView1.GetHitTestInfoAt(X, Y); fSelectionOK := (htOnItem in HT) Listing 5 end; However, there is a problem here, because the tree view can contain category names as well as recipe names. If the user clicks a category name, nothing should happen. To make things even more difficult, sometimes there are two categories, sometimes only one, and sometimes none. So what level is the name at? In the tree view, the recipe names are always under any category or categories that are present. This means that if the property has children is false, a recipe is selected, and otherwise a category is selected. If a recipe name is selected, the corresponding record is retrieved using the Locate method. The content of the record is shown in the second tab, which is why the order to display this tab is given via PageControl1.SelectNextPage(True). All of this is worked out in Listing 6. procedure TForm2.TreeView1Click(Sender: TObject); begin with TreeView1.Selected do if (not HasChildren) and fSelectionOK then begin DM.ClientDataSet1.Locate ('Titel',VarArrayOf([Text]), [loCaseInsensitive]); PageControl1.SelectNextPage(True); // forces PageControl1Change end; Listing 6 end; Recipes The Recipes tab shows a description of the recipe. This tab shows all the fields from the recipes database. A TDBNavigator is also included so the user can page through the recipes without having to use the Search tab. As already mentioned under 'Data module', the user must be prevented from adding recipes if the recipe file is read only. For this reason, the FormCreate event is used to cause only the buttons that cannot be used to modify records to be visible (see with DBNavigator1 do VisibleButtons := [nbFirst,nbPrior,nbNext,nbLast]; Listing 7

with PrintUnit.PrintForm.PrintEdit do begin Clear; Lines.Add(Label1.Caption + DBEdit1.Text + #10); ... Listing 8

Conclusion
Using the ClientDataSet, you can create a cookbook program that does not have to be installed by the user and that can even be run from a USB stick if so desired. This article presents a recipe for creating a cookbook that uses a tree view to sort recipes in a logical manner. If you find this recipe tasty, you can download the full program code from the PGG website and season it to your own taste.

Reminder

Advert

by Detlef Overbeek

Pointers how doe tehy work/ what are they? (Wikipedia) In computer science, a pointer is a programming language data type whose value refers directly to (or points to) another value stored elsewhere in the computer memory using its address. Obtaining the value to which a pointer refers is called dereferencing the pointer. A pointer is a simple implementation of the general reference data type (although it is quite different from the facility referred to as a reference in C++). Pointers are so commonly used as references that sometimes people use the word pointer to refer to references in general, but more properly it only applies to a data structure whose interface explicitly allows it to be manipulated as a memory address. The Delphi help offers a good example: You can declare a pointer to any type, using the syntax type pointerTypeName = ^type When you define a record or other data type, it's a common practice also to define a pointer to that type. This makes it easy to manipulate instances of the type without copying large blocks of memory. There are standard pointer types for many purposes. The most versatile is Pointer, which can point to data of any kind, but a Pointer variable cannot be dereferenced: placing the ^ symbol after a Pointer variable causes a compilation error. To access the data referenced by a Pointer variable, first cast it to another pointer type and then dereference it.

Maximum freedom for the end user Users do not have to choose from fixed, predefined categories. This has been done intentionally, because they have their own ideas about categories and must be able to enter them according to their own preferences. It is not even mandatory to fill in some of the fields, including the category fields. Naturally, if the user does not fill in a category for some reason, this impacts the search screen. In this case the tree view will have a node (category) without a name. Users that consider this to be a bit on the lean side will thus most likely ensure that the recipe screen is nicely filled.

Print screen
A RichEdit is used for printing out recipes. It is filled in a simple manner via the main screen before the print screen is shown to the user (see Listing 8). After this, the user can edit the font and layout of the entire text. This is done using standard actions of the ActionManager. The details are not described in this article.

Download the code for all issues from: www.blaisepascal.eu


March 2008 BLAISE PASCAL 1

Page 24 / 1798

Exploring Rave Reports


In this article well explore Rave Reports. It begins with an introduction and then describes how to create a report and how to link it to code generated using the Delphi IDE. In this article, we create a very simple application that you can use to manipulate the data you need for the report before you print it. The article is written so that you can use it hands-on while reading. At the end, you will be able create and print reports for your own applications.

2007

by Rik Smit
How to start? In Delphi you need the IDE to create an application. To design a
report, you need Rave Visual Designer. You can launch it from the Delphi IDE in two different ways: by double-clicking the RvProject component in the Form or by selecting it via Tools in the main menu.

A quick look at Rave Designer

Figure 1: The Rave logo at the top left of the form What is Rave Reports? Starting with Delphi 7, Rave Reports gradually came to replace Quick Report as the standard installed component group. It has been compatible since Delphi 4 and is an independent report generator, called Rave Visual Designer, which is a sort of IDE (integrated development environment) for creating reports. This independent approach has the advantage that you can develop and change things without affecting your Delphi application. You do not need to recompile your Delphi code. It works very well with Delphi and has a simple structure, although it has its own programming style. Documentation There is a lot of free documentation available, and the number of solutions is endless. Creating a simple report takes only two minutes. Installation You have to install Rave as a component group in Delphi. The program is on the Delphi CD and is one of the recommended installation components, just like Interbase. Simply do a standard installation, and then youll have a component group called Rave. The program can be accessed by right-clicking on a component or via the menu: Tools " Rave Reports Designer. If necessary, you can let the end user create his own reports, there is a module that enables you to sell this to your customer. As of version 7.5.2, there are thirteen components available. Here we only discuss two of them: RvProject and RvDataSetConnection. Where to start? Because we are going to use data from a database, we will place two data components in a data module. Open a new project and a DataModule form and link it to your main Form1 by using the menu: File"Use Unit. Add a TClientDataSet to connect with the database, and call it CDSRaveExample. Put another TDataSource near to it, name it DSRaveExample, and change its DataSet property name to CDSRaveExample. Find the Rave Tab and drag a TRvProject and a TRvDataSetConnection to your form. Change the DataSet property name to CDSRaveExample.

Figure 2: The Rave Report interface The main menu is at the top of the window, below the navigation bar. ) 1 ( b v y h T e m d l : f p u r g w s i a n o c t I They can be used undocked without a group name or docked under a tab. See the figures below. If they are undocked, all the icons of one tab are visual.

Figure 3: Group R icons undocked

Figure 4: Group R docked under the Project tab By right-clicking an icon, you can dock the group again by clicking on the popup item Dock. Note: if you place the each of the undocked bars under a tab, you will be able to see the tab name. For example, the tab with the red R as an icon has Project as its group name. See Figure 4. If you click the tab name and then click the menu item Undock, the icons are placed as a group back in the navigator space. Sometimes they will show a reference to the group name, but it is undocked and not visible. See Figures 3 and 4. To place components on the Page Designer page and adjust them, use the tool components for the font (Fonts tab), the colour (Colors tab), or alignment (Alignment tab). There are three important elements under the navigation space. Just as in the IDE, there is an Object Inspector (2) on the left side.

Why these two Rave components? RvDataSetConnection We use this to make a connection to the dataset of the application and the data we want to use in the report. As an example, we will use the field names First name, Name, Address, and City for mailing. RvProject This component looks after the connection between the Delphi application and the report. The property ProjectFile contains the file name of the report project file. It is used to hold the properties of the project file and has the extension.rav. Note: double-clicking opens the Rave Report Designer. See Figure 2

March 2008 BLAISE PASCAL 1

Page 25 / 1799

Exploring Rave Reports (2)


Next to it there are two tabs (3) for the Page Designer and the Event Editor. At the far right side there is the Project Tree Panel(4), (see Figure 2) which shows a full overview of the reports in the project. The Project Tree Panel shows the structure of each report: what components are used and database links that are present, and even which fields are present, all nicely arranged in a tree view. Very useful. The Page Designer is the heart of Rave Reports. Page1 is open by default. If the report consists of several pages, each page has its own tab. You can place any components you need on the form (page). Some components will not become visible immediately. Page1 has a property to change the appearance of the page. Clicking on the page in the Project Tree Panel shows the properties in the Object Inspector. For our example report, we configure Page1 to have PaperSize A5, the name Exampage, and the orientation poLandScape. Now run Rave by pressing F9. If you want to see something on the otherwise empty page here, youll have to place components from one of the four Rave component tabs. Lets look at how to construct a page and see what you have to use. The non-data-aware components are located in the Standard tab, and the data-aware components are located in the Report tab. There is a Text component (Rave Standard tab) as in the label of the Delphi Standard tab and a Data text component (Rave Report tab), similar to a TDBText component (Delphi Data Controls tab). We will use both. Figure 6: The Text property of the Text1 object has Sender as text

Using database data in a report


We dont want to use only static text; we want text to use database content as well. As in Delphi, there is a database-aware component. Youll find it in the Report tab, and of course its called DataText. But before you can use it, you have to do some coding in Delphi. In your Delphi application Print Rave Report, the name of the database (RaveExampleAdressing.xml) must be inserted on the Rave DataModule Form in the FileName property of the CDSRaveExample. The property Active must be set to True, and the Dataset property of the RvDataSetConnection becomes CDSRaveExample. Some of the settings must also be changed before you can use the Rave report data. For this, go back to the Visual Reports Designer by double-clicking on the RvProject component of the DataModule. Now look for the Tab Project in the navigation space. It probably will not be visible on the tab because it may be undocked. It might be at the upper left corner in the navigation area with a red capital R icon inside (see Figure 3). Then select New Data Object as below:

Figure 5: Right- clicking on the object, Text1, will show a popup Figure 7: Select the New Data Object icon from the Project group

The Text component


Lets start with a simple component. Its on the Standard tab and looks like a capital T (for Text). Place this Text component at the top left on your ExamPage in Page Designer. You will see the properties of Text1 in the Object Inspector. You type your text, such as Sender, in the Text property. The change does not appear immediately, but something has happened in the Project Tree Panel at the left of the Page Designer. Open the Report Library Tree until all branches are open. Under ExamPage you will see the capital T of the Text1 component that is used. Clicking on it causes a green check mark to appear, and on the ExamPage the object becomes visible by showing little green rectangles in a frame. See Figure 5.

In the Data Connections tab, select the direct data view icon and press next. In the next popup menu, select RvDataSet ConnectionExample and press Finish. See Figures 8 and 9

Continuing with report construction


Now drop five Text components below the other component at the left top. This is where you will type the data for the sender. See Figure 6, without text at the top and with text at the bottom.

Figures 8 and 9: Selecting the link to the external database

Page 26 / 1800

March 2008 BLAISE PASCAL 1

Exploring Rave Reports (3)


A a new button named DataView1 will appear in the Project Tree Panel below Data View Dictionary. The names of all the fields of the connected client dataset are shown here. Now drop a DataText component from the Report tab of Page Designer so you can select the DataView property for DataView1. All the fields will become available. See Figure 10. The link to the database in our Delphi Application is now set. Next, drop six DataText components onto ExamPage. Link the DataText1 component by setting its DataView property to DataView1 and its DataField property to the FirstName data field (selected from the pull-down menu). I will explain the three little dots next to it later on. Start linking the other five DataText and DataView properties to DataView1, again using the pull-down menu to select the items, so you end up with a complete address. See Figure 10.

Figure 11: Output options

Region and DataBand components


If you try to drop a band component direct on the ExamPage, you will find that it doesnt work. You cant just drop a band on a page you have to drop it on a region component. Now we place a region component (Report tab) at the top left of the ExamPage. By default, this is a little grey rectangle that partially covers the text components when it is selected (and the previously mentioned green shapes also appear then). Drop a DataBand on this Region1 rectangle. This will produce a grey band at the top of the Region1 area with the following text: Region1: Databand1 (Master 1PC). See Figure 12.

Figure 10: Pull-down menu If you press F9 or the Execute button, the report will be compiled and you will see a popup menu called Output Options (Figure 11). Select preview here and press OK. The report will be shown in a Print Preview window. Here youll see a page with the body text at the top left and the postal address at the lower right. If you press F9 again, you will see the same report with the same address. As we want to make address labels, we need all the records in the database. For this we need an iterative or mail-merge type of report that can print all of the addresses. Before we can do this, we have to examine some of the basic theory of Rave.

Figure 12: Moving or resizing a Region object: DataBand1 is the default name. You can change this by editing the Name property of the component. Click in the grey area and the little green features will appear again, and you can see that DataBand1 is inside the Region1 object. (See Figure 12). Now take a look at the Rave project Tree Panel. There you will see Region1 and DataBand1(in that order) below ExamPage. You now can pull the gray area by grabbing the green shape and stretching it over the ExamPage so that it covers the full area. DataBand1 stays at the top in the full width of the Region component. Select the DataBand1 object and pull the green shape in the middle by dragging the mouse downward over the Region1 component to cause the grey area to become white. See Figure 13.

Back to theory
To create your first report in Page Designer, you need some basic knowledge. The designers of Rave have tried to make report generation as flexible and agile as possible. This is done by using combinations of several components. You can divide a report into several areas by using region components (Report tab). A region component is a container component, like TPanel in Delphi. It is especially designed to hold band components (Report tab). There are two of these: Band and DataBand. The difference is that a Band shows the same content on each page of a report, while a DataBand always shows the content of the next record of one or more linked databases. A band component is also a container for all components belonging to a group. The simplest example of a band is the header or footer of a word-processing document. A band can also contain data-aware items, and this sort of band (a DataBand) can be used as a slave by the ControllerBand property and be linked to another DataBand. Nice if you want to create a report from a master - detail database. However, some caution is necessary when using band components.

March 2008 BLAISE PASCAL 1

Page 27 / 1801

Exploring Rave Reports (4)


Next step
Now drop a text component with the text On top of DataBand on top of DataBand1 at the right, close to the text Below Region. Use the Fonts tab to change it to bold and a larger font size. At first sight it seems that the Below Region and On top of DataBand text objects are both on the DataBand component. The Rave project tree shows that Region1 has two components: DataBand1 and the OnDataband text. The Below Region text is linked to the ExamPage. Even though the Below Regiontext is placed on the ExamPage and the Region1 and the Band components are covering it, it is directly accessible. Align it to the top at the same height. See Figure 16 of the bottom window.

Figure 13: Dragging the Region object over the DataBand.


Now the Region1 area is owned by the DataBand1 and the ExamPage is empty again. Press F9 and then OK. Now you will see that the area is not empty. The text components that were not visible at design tme (Figure 15: cursor with red dot) are in fact printed (Figure 14 left side). In design time they seem not to be there, yet they are. And now another difference:

Figure 14: All text of all objects is printed!

Figure 16: Upside down Press F9 to display a print preview (see Figure 15 right area). Now the text at the top of the region is no longer aligned with the text below the region. How come? Stop everything and use the Object Inspector to examine the value of the Top property of each of the two components. You will notice that the values are different. This happens because they are on top of two different objects and have different points of origin. The ExamPage starts counting immediately from the white are of the page in Page Designer, while the DataBand starts counting from the bottom of the grey band with the title Region1: DataBand1. Design time and runtime often show very different results. This time the problem is easy to solve: just drop both text components on the Band or on the page. Now that you know how, it is fairly easy to set the Region1 component for the first six text components to transparent. Pull the region component down again so that all text components are visible. Select the six text components by pressing and holding the left mouse button, drawing a rectangle around them, and then right-clicking on one of the text components. Select Cut from the appearing menu. SelectPaste in the Edit menu. Now everything is placed back on the Exam Page. Next, pull Region1 all the way back to the top over these six Text components. They will remain visible. See Figure17. In the Tree Panel, you will now see the Region1 component as the first component below ExamPage, with seven Text components shown below it. The position and the order are both important. Theres more to come about the Region and bands...

Page, region and band


Now we place a new Text component on the ExamPage with the text `Below Region1 in the Text component. You can do this with the little green shape in the middle of the Region1 object (you will have to resize it a bit). Note: if you cant find the Region1 object, click on it in the Tree Panel. Drop the new Text component next to the Editor Blaise text. Figure 16 top window. Select Region1 in the Tree Panel and drag it (by grabbing the green shape) back to the top. You will see that the white part of the DataBand neatly follows the area of Region1. What strikes you here? Even though the new Text component is also dropped on the Region1 component, it now is visible and clickable, but previously the placed text components were hidden. Figure 15. Push F9 and you will see that it is shown neatly together in the report. See Figure 14 left part. Now look at Figure15, and in particular the expanded tree. The new Text component can be seen below the Region1 component in the tree, but it is owned by the ExamPage. Evidently the region is transparent to everything that is dropped on the region component on the ExamPage.

Figure 15: Text objects below the region component are not visible.

Page 28 / 1802

March 2008 BLAISE PASCAL 1

Exploring Rave Reports (5)

Figure 21: Region and databand set to 2 columns To make things even more complex, you can paste several region components on a single page, each with several bands or databands. The order in which the regions are dropped on the form determines the order of their appearance on the form. This order cannot be change after they have been pasted! If you wish, you can even paste a region in another region. This shows how flexible Rave is. Lets get back to the report. Remove the Below Region and On top of the Region text components. This can be done in two ways: either click on the object directly in Page Designer to select it and then press Delete (keyboard), or right-click in the Project Tree panel and select Delete from the menu. Now drop six DataText components right below on DataBand1. Set the DataView properties of the DataText component to link them to DataView1, and then set the DataField property to thesurname of the datafield (selected from the pull-down menu).

Figure 17: Cut and past shows a remarkable result. Pages, regions, bands and databands how they all relate What happens if the region does not cover the entire area of the page object or if the DataBand object doesn't cover the entire area of the region object? See Figure 18. The Region area is the printing area. For example, if the height of the Region object is half the height of the Page object and the PaperSize property of the page is A5, only half of the A5 page will be printed when a Print command is executed. Note: if the DataBand object area is only half the height of the Region area, only two records will be printed. The Region has enough space to print the content of the Databand twice, even though there is only one DataBand object. If you want to print a list, you can set the height of the DataBand object according to how many items will be printed. If you want to have two or more columns be printed, there are two possibilities that you can use separately. The Region object and the DataBand object are both aware of the Columns property. Try changing both column settings to 2. See Figure 21. Four columns will be printed. See Figure 20.

Figure 18: A half-page region with the databand half as large as the region

Figure 22: Grouping DataText objects to create a single address You can activate this by clicking the Down arrow at the end of the DataField input field. If there are no fields in the menu, you probably forgot to enter a value for the DataView property of DataView1 (located directly below it). The three little dots are explained further on in this article. You have to repeat this for all five DataText components so a complete address will appear. See Figure 22. Run (F9) and examine the result. Nothing to see. This is because I forgot to enter DataView1 in the DataView property of the DataBand. If there is nothing to see at runtime, you will have to check whether the desired data view is selected for the DataView property of each of the DataText(s) in the DataBand(s). Enter DataView1 and run again (F9). Now the address will appear. Next, click the next page arrow in the report preview. Is what you see actually the next page? Yes, it is the next address in the database. This means that to have all records of a database be printed on one page, you must drop DataText components on a DataBand component on top of a Region component. Only the DataBand can automatically cause all records to be printed. See Figure 23ad. If you only want a selection, you will have to filter the data before printing the report.

Figure 19: Print preview of Fig. 18 Figure 20: Print preview of Fig. 21

March 2008 BLAISE PASCAL 1

Page 29 / 1803

Exploring Rave Reports (6)


If you use these join tokens in the date field, the database field names will remain recognizable. Select &. It will immediately be inserted at the cursor position. The insert rectangle below the text Data Text is a memo field. Here you can add or delete any text. If you select the wrong data field, simply delete it. Now it will work. See Figure 25.

Figure 23a: Print preview page 1 Figure 23b: Print preview page 2 Figure 25: The ampersand (&) couples two data fields correctly. In the same way, you can combine the street name and house number in the street name DataText, and the same again for the postcode and city. You can easily make room between field names by inserting spaces. This is thus the way to insert static text between dynamic data fields. The choice of + or & is the only thing that determines whether there will be an intervening space. As a reminder, you could put this into the memo field; it will not be printed See Figure 26. Figure 23c: Print preview page 3 Figure 23d: Print preview page 7

Three dots (ellipsis)


The way the address is printed In the previous example is not especially nice. To improve this, the first name, surname, street and house number should be in the same field. To put several data fields in a single DataText container, you have to use the three little dots (called an ellipsis) located to the right of the down arrow of the DataField property. If you click on it, a Data Text Editor window pops up. Double-clicking in the DataField has the same effect. See Figure 24.

Data Text Editor


Data Text Editor gives you lots of choices for the DataText content. By default you can select DataView1or something else. This lets you combine data from several databases in a single DataText field. Use Data Field to select the data field. Press Insert Field to display the Data Text below the Data Fields form. Repeat this to see more field text items, placed in a row. In DataBand1, delete the DataText component named Surname and select the DataField property of First Name. Press the ellipsis button and examine the input field below Data Field. It already contains: Surname. Press Insert Field to place them all in a row. See Figure 24.

Figure 26: Two join tokens containing a string of spaces. Back to the Data Text Editor. In addition to data fields, you can add other variables to the DataField. The first option is Report Variables. Press the Down button at the end of the field to see a list of all available variables. If you want to insert a date or page number or the total number of pages, you will find a suitable variable in this list. If you want to use report parameters, youll find them under Project Parameters. IF the list is empty, there are no know variables. In any case, be sure to always place a + or & join token between each pair of items. This enables you to extend DataField for your own needs with variables or parameters that do not have to belong to the same database (anotherDataView). Now we can finish the report. We will use a DataMemo component, two Bitmap components, and three FontMaster components. At the top left of the page you will find the static text. As it comes from the editor of Blaise Pascal, we will drop a Bitmap component (Standard tab). We link an image of Blaise to the Bitmap component via its Image property. As we will use this report for distribution of Blaise we also drop a Post Paid (post paid) bitmap here. Now we can select s specific font for each text component via its Fontproperty. If you later decide to change the appearance of a group of text components, you will have to do this one by one.

Figure 24: Data Text Editor At runtime you will see that the report is generated, but where you expect to see the text you will see the message invalid. Whats wrong? The DataField property expects consistent content. It sees two data fields in a row as the name of a data field, which of course does not exist. You must return to the Data Text Editor and edit the First Name DataText. Navigate to the Data Text panel and place the cursor between the two data fields. Next to it there are two buttons: a button with a + sign and a button with an & sign. The + means join without space, while the & means join with one space in between. Figure 27: Finished address page

Page 30 / 1804

March 2008 BLAISE PASCAL 1

Exploring Rave Reports (7)


Filtering or creating a selection is asking for trouble In a complicated report, especially if there are several pages involved. To overcome this, you can use groups of text components and a FontMaster component. The Font property triggers a popup menu that can be used to adjust almost anything. Each component with the Font property also has a FontMirror property that lets you select master font. This overrules the Font property. In our report, there are three TextData address objects that are linked to FontMasterAdres via FontMirror. Set the font to Bold and the size to 12. I also linked a FontMaster to the Memo field. Because the parMemo parameter is not initiated by the database, we use the Data Text Editor to link the parMemo parameter to the DataField property of the DataMemo component. The DataView property does not need a link unless you create a data field with a composite content, such as (for example) a record field together with a parameter as the field value for a DataText component. See Fgure 27 for the result. The report is now ready for use. Most of the work is done. We can print by pressing F9 in Rave Reports Designer or in Delphi. Printing from a Delphi application requires a bit of code.

What does this code do?


First it creates a filter for the database. We want to select all inhabitants of one city. We can do this by creating a search key as a string consisting of the field name City = and the name of a specific city. This is why it is placed between double quotes: a string inside a string. After this we activate the filter by setting the client database Filtered method to True. Now the database is ready and the relevant report will be addressed by the path and name of the report. Because we opened the Rave Project, we have to close it because the memory must be freed again. To ensure that nothing goes wrong, we create a Try Finally construction so the project finishes neatly regardless of what happens. So far nothing new, but here is something worthwhile: SetParam ('parMemo', Memo1.Lines.Text);

The SetParam method


Syntax procedure SetParam(ParamName: string; ParamValue: string); Description You can use SetParam to send parameters from Delphi to a report. The first part is a parameter name, and the second part is the content. In the code, the content of the memo is sent to the variable varMemo as a string. The content can be created inside the Delphi application and starts empty. The declaration of varMemo is not done in Delphi!

Triggering a report in Delphi


The basic ingredients for this are already present on the Data Module form: the RvDataSetConnectionExam and RvProjExam components. The first of these connects the database, and the second one connects to the Rave Report Generator. We will use the RvProject component in our Delphi code to trigger the Rave generator. You will need three methods to do this. The code for this looks like: With datamodule Do Begin RvProject. Open; RvProject. Execute; RvProject. Close; End; The Open method opens the report project file shown in the ProjectFile property of the RvProject1 component of the DataModule. This method makes it available for printing or editing such as inserting text into the DataMemo component. RvProject.Execute: starts the Rave report. RvProject.Close : closes the Report project and wipes all used memory. Never forget this! Execution of this piece of code does the same thing as F9 in the Rave Report Generator. Now that we know how to start a report by triggering an event inside Delphi, we can extend this code to suit our specific needs. As an example here, we use some code from the Rave Demo program. Procedure TfrmRaveMain.btnMakeReportClick (Sender: TObject); { Start Rave Report Generator with RaveExample.rav as the project file. As an extra we create a filter } Begin With DataMod Do Begin // filtering CDSRaveExample.Filter:= 'City =' + ''''+ DataMod. CDSRaveExampleCity.AsString+ ''''; CDSRaveExample.Filtered:= True; // Start the Rave report With RvProjExample Do Begin ProjectFile := 'RaveExample.rav'; Try Open; SetParam('parMemo',Memo1.Lines.Text); Execute; Finally Close; CDSRaveExample.Filtered:= False; End; End; End; End;

Declaring the parMemo variable


To understand this, we must open the Report Generator again. There we open the RaveExample.rav report. In the Tree Panel, we click on the Rave Project so that a green tick mark appears. At left in the Object Inspector, you will see the properties of the Rave project. Look for Parameters. Select its ellipsis. This causes a String Editor to pop up, where you can declare the parameter(s) by inserting its or their name(s). These are the names that must be used In the Delphi code. This means that in principle, you can use any desired name. When you are done with the parameters, you can close the editor, and you will see Parameters (Strings) property in the field. See Figure 28.

Figure 28: Defining the parameter variables. The Report and Page of the TreePanel have their own parameters. These parameters can be linked to the DataField property of a component by editing the properties in the Data Text Editor. Now that the parMemo parameter has been added to the report, you can use Delphi code by adding SetParam. Now the report is created by Execute and then closed by Close. The filter must be set to false.

March 2008 BLAISE PASCAL 1

Page 31 / 1805

Exploring Rave Reports (8)


Conclusion
In this article, we started with an overview of Rave Reports. We then created a sample report in order to unveil some of the mysteries of Rave. The program is very comprehensive, so we could only show some of its features here. The report takes its data from a database, and it can be launched by a simple Delphi program. See Figure 29.

In the next issue


Multiple filtering your Client Data Set by Martin De Bont
Client Datasets are notorious for being difficult to filter, but Martin shows you how to do it very easily even with multi-filtering. He has built a Filter class that does everything for you.

Extracting secrets from your bank account by Martin De Bont Bank accounts have their own way of refusing to provide answers to simple questions. Write your own application and use filtering to obtain all the results you ever wanted but never thought you could find. Martin has created a program that can steer you in the right direction. Make your bank account transparent!
Figure 29: The Delphi program that generates the report. This program has a memo field that makes it possible to include additional data in the report. For this purpose, it uses a variable that is declared in Rave Reports instead of in Delphi. A DBGrid has been added to simplify address entry from an XML database.

Bob Swart goes marching on... with even more components How about Lazarus ? A lot has changed since... The IDE is more compleet then ever. More components, better database connections... It works with the new Freepascal 2.
Compile your program once

(in Windows, Linux or Mac) just use it anywhere.

Figure 30: The address page as printed. I fell into a lot of traps while writing this article. Each time I thought I had it all figured out, Rave Reports came up with another surprise. It is a very versatile program, and in this article I was only able to demonstrate a small set of its features. The nice thing about this is that it nicely illustrates the value of learning by doing. So dont be afraid to get your feet wet. Simply start using the program, and dont give up too soon. With the features described here, you should soon be able to create your own customised reports. Have fun!

Special limited-time offer:

subcribe
to the printed version

Want to become an author?


Write your own article? Notify us: email editor@blaisepascal.eu Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68

for the first year for only 25.00


including all downloads, tax and postage. Page 32 / 1806

March 2008 BLAISE PASCAL 1

Cover Story:
drawing on a changing canvas
This article describes a program for drawing arrows on the computer screen. Here we use arrows as an arbitrary choice to show: a method for flicker free drawing a general approach such that other shapes (rectangle, circle) can be easily added. some possibilities of the Delphi canvas some basic mathematics to calculate the arrows

ZIPFILE

by David Dirkse

Figure 1: The arrow


General approach See Figure 1 for a result. The mouse button is pressed at point P. Holding the mouse button down, the mouse pointer is then moved to point Q where the button is released. The result is an arrow pointing from P to Q. Drawing takes place on a bitmap named 'paintmap'. To show the arrow, part of 'paintmap' is copied to a paintbox on the form of our application. After pressing the mouse button, point P is fixed, but Q will change position on the screen as we move the mouse pointer. A new arrow is painted for each mousemove event, but the old arrow must be erased before a new arrow is painted. This is accomplished by copying part of the 'Bgmap' bitmap to paintmap. BGmap is the bitmap that holds all arrows painted so far. BG stands for background.

Figure 3: Arrow details Drawing the arrow See Figure 3. The complete arrow is defined by 10 points. These points are entered in array p2, which is then passed to the Canvas method 'polygon' to draw the shape. The brush style is set to bsClear, so the area within the arrow is not shaded. Figure 3 also shows some variables used to calculate the outline of the arrow: ? the length of the arrowhead head : ?, headY1 etc.: the reference coordinates used to headX1 calculate points 2,3,4,5,7,8,9,and 10. ? : the width of the arrowhead. aWidth head and aWidth are, within limits, a percentage of the length of the arrow. See Figure 4. for details. We start with the coordinates of P(x1,y1) and Q(x2,y2). Then we calculate the horizontal and vertical distances: dx = x2 x1 dy = y2 y1 Using the Pythagorian lemma aLength = sqrt(dx * dx + dy * dy) then sinPhi = dy / aLength ...{sinPhi = sin() } cosPhi = dx / aLength ...{cosPhi = cos(f) } Note: we do not call any sine or cosine function, but instead use similarity (of triangles) to calculate the points. Now = aWidth * sin() widthX = awidth * cos() widthY = headX1 + widthX p2[8].x = headY1 widthY p2[8].y The other points are calculated in a similar way (see listing of unit2). Program description The only component on the form is a paintbox for showing the results on the screen. The paintbox dimensions are 601 * 461 pixels (horizontal * vertical). The create method of form1 also creates the bitmaps Bgmap and paintMap. Bgmap is also provided with a Note: the width and height of the bitmaps and paintbox are a multiple of this grid size + 1. Without this '+1', we would not be able to show the grid at the right and bottom edges. On the canvas of Form1, we paint a frame of 2 pixels wide around the paintbox. This frame is painted by the sequence 1..2..3..4 for i=0 (outside line) and i=1 (inside line);, see Figure 6. Moveto(x1,y1) places the pen at position (x1,y1) on the canvas. Lineto(x2,y2) draws a line from (x1,y1) to (x2-1,y2-1) and places the pen at (x2,y2). {assuming x2 > x1 and y2 > y1}

Figure 2: Flow of objects


Paintmap is a copy of Bgmap and also holds the temporary arrow. After the mouse button is released, the resulting arrow is painted in Bgmap and copied to paintmap and the paintbox, with the result displayed on the screen. See Figure 2. Two procedures do most of the work: drawproc : Receives the mouse events, selects the bitmap where painting takes place, and restores the bitmap before painting a new temporary arrow. Transforms event information to parameters for the Arrowproc procedure, which is called to draw the arrow. arrowproc: Draws the arrow according to supplied parameters.

Please note

If figures other than arrows are to be painted, a procedure to replace Arrowproc is needed. However, all figures defined by just 2 points can share drawproc. In this case, drawproc should contain a case statement to call the appropriate paint procedure.

March 2008 BLAISE PASCAL 1

Page 33 / 1807

Drawing on a changing canvas (2)


Please note that the paintbox or bitmap contains pixels 0..width-1 horizontally and 0..height-1 vertically. Unit1 is simple and straightforward. There are 3 mouse events, which supply w pMouseEvent w mouseX w mouseY to procedure drawProc in unit2.

Figure 5
DrawProc is called on each mouse event of the paintbox. To guide the drawing process, a control counter p_count is established. For p_count = 0 no painting takes place. After the mouse button is pressed, p_count is set to1 and a mousemove event sets p_count = 2. If p_count =1, no arrow has been painted yet. If p_count = 2 , the existing arrow must be removed before a new one is painted. Drawproc receives coordinates (mouseX,mouseY) and stores them in (p_x1, p_y1) at mousedown and in (p_x2, p_y2) at mouse-move. p_x1, p_y1, p_x2, p_y2 are then passed to procedure ArrowProc to draw the arrow, as previously described. Arrowproc calculates a rectangle, p_rect around the arrow to indicate the area of the bitmap that has been changed. Drawproc uses this rectangle to erase any temporary painted arrow while p_count = 2. Also, p_rect is used to update the paintbox, showing what we are doing. Variable p_bitmap is the bitmap used by ArrowProc.

Figure 6 Background information


The information in this article is derived from the 'Master Math editor', a program I have been working on for quite some time. It combines text editing and formula editing with drawing, geometric constructions and function plotting. The objective is to save time for writing papers on mathematics. In this description, I omitted the selection of colors, line styles and line width for the sake of clarity. In the Master Math editor, the paintbox height is less than the bitmap height, so Drawproc must handle a vertical scroll event as well. Several 'Drawprocs' are used because some constructions (like arcs) need more steps, which means a larger p_count .

David Dirkse
Figure 4 Appendix
Sine and cosine functions calculate the ratio of the edges in a rightangled triangle. See Figure 5. All right-angled triangles with one acute angle the same are similar, so an angle corresponds to a fixed ratio of the edges. By definition: sin() = Y / S ...., so.... Y = S*sin() cos(f) = X / S....., so.... X = S*cos(f) Note that we use similarity of triangles here. The sine and cosine functions are not actually used because the ratios are already known. Sine and cosine functions supply the ratios ofthe (unknown) sides when only the angle is known. Davids page is at: http://home.hccnet.nl/david.dirkse/

Download the code of all issues from: www.blaisepascal.eu

Page 34 / 1808

March 2008 BLAISE PASCAL 1

Mini-application: opening files


These small articles are intended to provide very simple examples of how easy it is for Delphi users to build a graphical user interface. This very small app shows how to get a file and copy it to another location of your own choice. The level is for beginners.

ZIPFILE

by Detlef Overbeek

procedure TMainF.Button1Click(Sender: TObject); { The CopyFile function copies an existing file to a new file.. CopyFile( = WindowsFile) lpExistingFileName : Pchar, // name of an existing file lpNewFileName : PChar, // name of the new file bFailIfExists : Boolean); // execute 'if file exists' bFailIfExists: shows how to proceed if there is another file with the same name as lpNewFileName If the parameter isTRUE and the new file exists, the function will fail. If the parameter is FALSE and the new file exists, the new file will be overridden.}

Figure 1: The application ingredients

Building the project


To build the project, drop the components on the form as shown in Figure 1. The button and the label are found on the Standard tab, and the two picture dialogs are of course of the Dialog tab. In the dialogs, you will have to tune some filter properties by using the Property Editor. Actually, this is not essential because if you choose pictures you can easily choose the picture dialog. In this case you are pampered, since Delphi has pre-arranged all sorts of pictures: All(*.gif;*.jpg;*.jpeg;*.bmp;*.ico;*.emf;*.wmf )|*.gif;*.jpg;*.jpeg;*.bmp;*.ico;*.emf;*.wmf|G IF Image (*.gif)|*.gif|JPEG Image File (*.jpg)|*.jpg|JPEG Image File (*.jpeg)|*.jpeg|Bitmaps (*.bmp)|*.bmp|Icons (*.ico)|*.ico|Enhanced Metafiles (*.emf)|*.emf|Metafiles (*.wmf)|*.wmf This is an enormous selection. You can of course make your own list, So it is worth getting it right. Dont forget the actual objective here: we want to fetch a list of files by clicking a button, choose a file, and save it in another location.

var FileOrigin, FileTarget: string; begin if OpenPictureDialog1.Execute then begin FileOrigin := (OpenPictureDialog1.FileName); SavePictureDialog1.FileName := (OpenPictureDialog1.FileName); end; if SavePictureDialog1.Execute then begin FileTarget := SavePictureDialog1.FileName; CopyFile(PChar(FileOrigin), Pchar(FileTarget), False); end; Label1.Visible := True; Label2.Visible := True; Label1.Caption := 'Origin: ' + FileOrigin; Label2.Caption := 'Destination:'+ FileTarget; end; end.

Figure 2: Click the button The procedure on the next page will be executed.

Figure 4: The Save Dialog appears. Indicate here where the file is to be saved.

Figure 3: The dialog window appears. You choose a file and click the Open button, and the dialog appears.

Figure 5: The application form indicates where the file originated and where it was copied to. Subscribers can download the code.

March 2008 BLAISE PASCAL 1

Page 35 / 1809

Rotating images

2007 Win32

by Detlef Overbeek

The aim of this set of Wallpaper application examples is to explain the principle image rotation. We start with a simple task: rotate a picture by 90 degrees. This is an easy task. After this, the idea is to do this faster, since it takes quite a while to rotate an image with a considerable size. Here we use a special Windows method called Scanline, which is complicated but fast.

Setup How do we solve this problem? An image, regardless of its origin, consists of pixels (with the exception of vector images). This means that the pixels have to be moved one by one an enormous task. In this example, they must be moved from a horizontal alignment to a vertical alignmentl. Because we have to copy each pixel to a new location, I use three bitmaps: BMPOrigin, BMPDestination, and a tmpBMP used as a temporary storage location for the rotated image. It is temporary because I have to reuse it again and again. When everything is done, I can save the image and use tmpBMP again. Image Until Vista, Windows only knew one image type: bitmap. And this is not an image. Delphi provides a component that directly handles most cases on its own. Ultimately, a bitmap is saved to this Image component. The Image component is actually just a container. Nowadays this seems a bit primitive, but it was a start, and in fact all other formats are derived from BMP'. The difference between this and other formats is the method used to contain the data. Algorithms with more or less advanced compression techniques create improvements: JPG, GIF, TIFF, and many others. The principle is still the same (and lets not argue about this now). If you keep in mind that a bitmap is composed of (coloured) pixels that are very small, it does not take a lot of imagination to realise that there are a lots of them in a single picture. We have to move each single pixel from X to Y. We also need height and width data for each bitmap. This is handled by the RotarAntiH procedure. See Listing 1. First I have to create the temporally bitmap called tmpBMP. Now we read in all given coordinates: Heigth := BMPOrigin.Width -1 and Width := BMPOrigin.Height-1 In the following code, the positions are swapped by the For ..To ..Do loop. The next task, processmessages, lets the rest of the pending Windows tasks that must be performed execute without any hitches. For B := 0 To Width Do Begin For H := 0 To Height Do Begin tmpBMP.Canvas.Pixels[ B, Height-H ]:= BMPOrigin.Canvas.Pixels[ H, B]; // original Application.processmessages; End; End;

Figure 1: The bitmap that we want to rotate Procedure TForm1.RotarAntiH(BMPOrigin, BMPDestination:TBitmap); B, H : Integer; Var : Integer; Width, Height : TBitmap; tmpBMP Begin tmpBMP := TBitmap.create; With tmpBMP Do Begin Width := BMPOrigin.height; Height := BMPOrigin.width; End; Height:= BMPOrigin.Width -1; Width := BMPOrigin.Height-1; For B := 0 To Width Do Begin For H := 0 To Height Do Begin tmpBMP.Canvas.Pixels[ B, Height-H ]:= BMPOrigin.Canvas.Pixels[ H, B]; // original Application.processmessages; End; End; BMPDestination.Assign(tmpBMP); tmpBMP.Free; Listing 1 End;

Figure 2: Rotated image

Page 36 / 1810

March 2008 BLAISE PASCAL 1

Rotating images with Scanline

by Detlef Overbeek
Procedure TForm1.Button1Click(Sender: TObject); Type TAInt = Array [0..30000] Of Integer; PAInt = ^TAInt; Var BMP1, BMP2 : TBitmap; : Integer; X, Y, A : Pointer; ScnLn Px : Record Case Integer Of 1 : (I : Integer); 2 : (A : Packed Array [0..3] Of Byte) End; Begin Button1.Enabled := False; // this to avoid unnecessary screen refreshes BMP1 := TBitmap.Create; BMP2 := TBitmap.Create; BMP1.Assign (Image1.Picture.Bitmap); Try BMP2.Width := BMP1.Height; BMP2.Height := BMP1.Width; For Y := 0 to BMP1.Height -1 Do Begin ScnLn := BMP1.ScanLine [Y]; For X := 0 to BMP1.width -1 Do Begin := PAInt (ScnLn) [X]; A Px.A [0] := A Shr 16; Px.A [1] := (A Shr 8) And $FF; Px.A [2] := A And $FF; Px.A [3] := 0; BMP2.Canvas.Pixels [Y, X] := Px.I End; End; BMP2.SaveTofile('C:\tmp.bmp'); Image1.Picture.LoadFromFile('C:\tmp.bmp'); Finally BMP1.Free; BMP2.Free; Button1.Enabled := True End; End; ShiftRight explanation: A Shr 16 means $00123456 Shr 16 --> $00000012 So for a shift of 16 bits (4 spaces) $00123456 Shr 8 --> $00001234 shift 8 bits (2 spaces) This method lets you determine the exact position of the colour and where the pixel has to be written. This code gives you a very fast way to rotate your bitmaps. Have fun, and don't get dizzy!

Figure 3: Immediately after opening the application


Rotating by scanline is a little more complex then what was shown before. The main objective, as already demonstrated, is to copy pixels from X to Y, but in this case we use a different method. With the first method, the pixels were moved one at a time. Scanline Scanline uses a very ingenious method. It actually copies a whole row of pixels and moves them. Because this happens in memory, the result is only visible when it is displayed on the screen. The CPU does it all for you, with the result that the method is much faster. Try it. The difference is enormous. If you're still not satisfied, you will have to resort to programming at the machine-language level. I envy the rather complicated rotation actions that Photopaint and similar programs seem to execute so easily. They do the task in almost no time. Lets search the help file: TBitmap.Scanline.Property Provides indexed access to each line of pixels. property ScanLine [Row: Integer]: Pointer; which means that an entire row of pixels must be placed in a single action for proper operation: View the array created in Listing 2 here.

Procedure TForm1.Button1Click(Sender: TObject); Type TAInt = Array [0..30000] Of Integer; PAInt = ^TAInt; Var BMP1, BMP2 : Tbitmap; : Integer; X, Y, A : Pointer; ScnLn Px : Record Case Integer Of 1 : (I : Integer); 2 : (A : Packed Array [0..3] Of Byte) Listing 2 End;

Scnln : Pointer. The concept of 'pointer' is explicitly explained in the Reminder text on page 24. packed array saves all addresses in consecutive order to keep everything compact. A is an integer of type packed array of 4 successive bytes, starting at 0. This lets you access the the colours and pixels precisely. Shr (shift right) and its companion function Shl (shift left) move bits to the right or to the left.

Figure 4: Push the button and youll be surprised!

March 2008 BLAISE PASCAL 1

Page 37 / 1811

Vogelaar Electronics
www.vogelaar-electronics.com
Your partner for Pascal / Delphi / FPC and Lazarus based industrial automation, measurement and control.
The Delphi Stamp n The Delphi Stamp is part of a development kit. This kit simplifies the design path for micro controller firmware n The DelphiStamp is a miniature plug-in controller sizing 52 x 20 x 20 mm. n Firmware can be developed in Pascal using the Delphi IDE including features like auto-completion etc. n is also used for testing, simulating and debugging. Delphi As the resulting code is cross compiled by the included PasAvr compiler, fast and compact code is generated for the Atmel ATMega128 RISC processor running at 14.7 MHz.

200-pin DDR Audio CF II and FDD SO-DIMM socket Interface on bottom

Main Power Input

Embedded controller
n Low power embedded controller (7,5 watt @ 500 Mhz). AMD CS5536 n on Compact Flash. Linux n Configuration of Linux using AUFS and ramdisk as LPT port filesystem to minimize writing to CF. n Configuration of servers SSH, FTP, HTTP, PC104 FIREBIRD / Interbase and more. n Development of application software in Kylix, FPC, Dual USB 2.0 Lazarus etc. n Real time applications (response time <40 micro sec). n to customized hardware (RS 485, I2C etc). Linking K/M

IDE

AMD LX800

TTL LCD LVDS

Dual USB 2.0 Dual 10/100 BaseT COM Fast Ethernet

SATA VGA

Air Gaging

Some typical characteristics include: n accuracy non- contact masurement High n Non contact measurement, no surface damage on soft materials n Self-cleaning due to the continuous outflow of air. n Suitable for optical applications no radiation of light n control, automatic report generating (Rave), data Remote export (Firebird) through the internet Our activities cover following aspects of air gaging: 1. Sensors nozzles and nozzle assemblies 2. Signal processing using two technologies BP = 1% and PE = 0.1%

Air gaging is a high accuracy non-contact technology, comparing dimensions of products against dimensions of master parts. Accuracies and repeatabilities better than 0.1 micro meter are easy achievable. Using concentric nozzles measuring ranges up to 3 mm are possible. All instrumentation is Pascal based and developed in Delphi.

Dorpsstraat 90 / 3751 ES Bunschoten / Netherlands Telephone +31 (0)33 2980727 / Fax +31 (0)847 115096

Page 38 / 1812

March 2008 BLAISE PASCAL 1

March 2008 BLAISE PASCAL 1

Page 39 / 1813

Special offer:
Buy 1 ReadyNAS and get a free HD 300GB: Order at http://www.blaisepascal.eu

Page 40 / 1814

March 2008 BLAISE PASCAL 1

Why CodeGear RAD Studio?

Rather than listing all the reasons why you will want CodeGear RAD Studio, we thought wed keep the list short and mention just 10: u and productivity with your all-in-one toolbox, covering Delphi for Win32, Choice C++Builder for Win32 and Delphi for .NET 2.0 v Seamlessly develop Vista applications in a RAD environment w Build eye-catching .NET applications with support for the .NET Framework 2.0 xdeploy your applications with the included Blackfish SQL embedded database Freely y Build rich, AJAX-enabled web-based business applications with VCL for the Web z C++. Boost performance and speed development with the latest updates and RAD for enhancements in C++Builder 2007, including up to 5x faster builds, Unit Testing support, { improved ANSI C++, Dinkumware and Boost support, and much more Streamline enterprise database connectivity with the new DBX 4 database architecture with support for the managed ADO.NET 2.0 data source | Numerous feature and quality improvements make Delphi for Win32 even more reliable and robust. } improved Help system, including code samples, enhanced cross-referencing and Greatly IDE integration ~ New build flexibility with MSBuild and custom build options to simplify build management.

Download a trial at http://cc.codegear.com/free/delphi

Events http://www.codegear-events.eu Sales http://www.codegear.com/shop/online-stores

Backgrounder
ABOUT CODEGEAR
CodeGear delivers innovative, high-productivity development tools for a wide spectrum of software developers, from individuals to enterprise teams. Formed from the Developer T ools Division of Borland to focus exclusively on technical innovation and support for software developers, CodeGear has more than 25 years of technology leadership and more than 3.2M users of its products worldwide. In November 2006, CodeGear became an independent organization within Borland Software Corporation with a separate management, R&D, sales, marketing and operations team. CodeGear is dedicated to meeting the needs of developers around the world a number that will increase to 13 million globally in 2007 and 17 million by 2011, according to International Data Corporation.

MARKET POSITION
With an active community of millions of developers in 29 countries, CodeGear is among the worlds top tools providers and the only one of the top vendors focused exclusively on developer tools. CodeGear products are used extensively by individual developers, enterprises, small-to-medium businesses, Independent Software Vendors and Value Added Resellers. Customers in the largest vertical market segments, including financial services, manufacturing, telecommunications, healthcare and government, use CodeGear products to develop applications critical to their businesses and operations.

CODEGEAR FIRSTS
CodeGear invented the first commercial Integrated Development Environment (IDE), which provides comprehensive capabilities for developers to efficiently produce software. CodeGear brought to market the first pure Java IDE, the first IDE for C++, the first J2EE-compliant IDE, the first Rapid Application Development IDE for Linux, and was the first company to have SOAP Web services support in the IDE, the language, and the runtime library across multiple popular programming languages.

Heritage of Industry Leadership


Re
1st Extensible Component Framework Redefined RAD 1st C++ Invented the IDE IDE

defining Software Engineering for Over 25 Years

Defined JavaBeans

1st RAD IDE for Linux

1st Enterprise Framework

1st Pure Java IDE

1st J2EE compliant IDE

1st IDE to support SOAP

From Inventing IDE & RAD to Enabling J2EE & .NET

Industry
Structured Programming

OO Programming Windows

Components

JavaBeans

Linux

.NET

J2EE

Web Services

CODEGEAR AND OPEN SOURCE


CodeGears mission is to help developers control and manage the open source environment. As open source has changed the nature of software development, developers can accumulate a variety of free tools and products, but then must make them all work together. That can be challenging and cost developers their most precious commodity time and productivity. CodeGear has become a top provider of tools that add value on top of Eclipse, which has become a huge force in software development but presents integration and other challenges for developers. CodeGear also has introduced groundbreaking products aimed at open source communities such as PHP and Ruby on Rails. And it has contributed key technologies to open source projects such as Mylyn and the Dynamic Languages T Kit (DLTK). ool

Where Developers Matter

http://www.codegear.com/shop

Backgrounder
PRODUCTS Java Development
JBuilder 2007, the first application server-independent, enterprise-class IDE built on Eclipse. It provides all the economic benefits of an open source platform, with the reliability of a trusted, turnkey solution provider. JGear, a set of specialized plug-ins for Eclipse that both augments and accelerates the open-source platform and tools in three key areas (Java application performance, visual development, and team collaboration) that arent addressed by Eclipse-based IDE solutions today.

Pioneering the Next Generation of Innovation


Delphi for PHP 3rdRail JBuilder 2007 JGear TeamInsight Delphi 64 C++Builder 64 Application Factories SMP VCL

Defining the Future of Software Development Tools


Eclipse Agile AJAX OSS Frameworks Ruby on Rails SOA Fractured Java Platform 64bit MultiCore

Industry
PHP

Windows and .NET Development


CodeGear RAD Studio 2007, a new version of the award-winning RAD environment for Microsoft Windows that combines Delphi for Win32, C++Builder and Delphi .NET The offering is designed to help developers . more quickly and cost-effectively build rich database-driven Windows and Web applications. C++ Builder 2007, the world's only native C++ Rapid Application Development IDE and framework for Windows. C++ Builder 2007 provides support for Microsoft Windows Vista and AJAX, and offers new features that allow developers to rapidly and visually build Web applications and rich AJAX interfaces. Delphi 2007 for Win32 speeds Win32 development by combining Delphi's visual Rapid Application Development approach with support for Windows Vista, AJAX, and streamlined database connectivity.

Ruby on Rails for the Enterprise

CODEGEAR DEVELOPER NETWORK


The CodeGear Developer Network (http://dn.codegear.com) is an online community of worldwide developers that enables developers to communicate, collaborate, and gain access to unique content. CodeGears Developer Relations and Evangelist teams work closely with this community, ensuring their needs and requirements are met and folded into CodeGear's strategic product plans. In addition, a group of customers known as T eamB manage and monitor CodeGear newsgroups user-supported areas where developers can exchange information, tips and techniques on CodeGear products. T eamB members, who come from a range of backgrounds and professionals, volunteer their time, expertise and advice to enhance the technical skills of other customers.

KEY PARTNERS
CodeGear products are available from more than 130 channel partners worldwide.

Dynamic Languages
Delphi for PHP is an integrated visual RAD environment for the popular PHP Web development language. 3rdRail is a powerful IDE built specifically for Ruby on Rails, the open-source framework written in the Ruby programming language and popular with next-generation Web developers. David Heinemeier Hansson, creator of Ruby on Rails, has said: "CodeGear's new 3rdRail IDE represents an important step in tooling for Ruby on Rails.

MANAGEMENT TEAM
CodeGear has a veteran, respected leadership team, including: Jim Douglas, CEO (former CEO of ReShape, sr. executive at Cadence) Allen Bauer, Chief Scientist (15+ years of Borland experience) David Intersimone, Chief Evangelist (an industry icon with more than 22 years of Borland tenure) Cynthia Mignogna, Chief Financial Officer (former CFO of Pharsight, Infoseek, Quiver) Peter Shambora, Sr. VP Worldwide Field Operations (former CEO of Data Center T , echnologies, Rasvia, StorageWay) Michael Swindell, VP of Products & Strategy (8+ years of Borland experience; sr. product positions at Imation/3M and Adobe) Steve T odd, Sr. Director, R&D (14 years at Borland, Ashton-T ate)

SERVICE, SUPPORT AND TRAINING Databases


InterBase 2007 is a leading SQL-compliant database for embedded and enterprise applications. Blackfish SQL is a high-performance, small footprint, SQL-92 compliant database for .NET and Java platforms. CodeGear developer support engineers are true experts in the CodeGear product lines and key related technologies. CodeGear Support can provide up-to-date service and installation information; answers to software usage and functionality questions; information on, and pointers to tools and fixes to prevent known software problems; and responses and resolutions for software inquiries and issues. CodeGear Education Services provides training solutions for developers of all skill levels throughout the world, leveraging a highly experienced community of partners and web-based technologies.

Where Developers Matter

http://www.codegear.com/shop

Out of the Box .NET Obfuscation


DeepSea Obfuscator makes Obfuscation of your .NET assemblies an intuitive and integrated part of your product development.

Zero configuration Declarative obfuscation through attributes All. NET versions All .NET languages Integrated with all popular build environments Fast. Optimized for multi-core Licensed per user, not per machine Silverlight 2.0 support

Special offer for Blaise Pascal subscribers:


Order your personal copy before April 30, 2008 and receive a discount of 10%

Go to Barnsten, official distributor of DeepSea Obfuscator, at www.barnsten.com

Barnsten Business Solutions, Binderij 7 B NL-1185 Amstelveen tel. +31 20 347 9020 www.barnsten.com

You might also like