Professional Documents
Culture Documents
ENGLISH VERSION
Pascal
Database normalisation
by Herman Peeren
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
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
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
c c
a
c c
b
b a
page 24 page 32
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
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
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.
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
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
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.
subcribe
to the printed version
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
2005 Win32
by Peter Bijlsma
ZIPFILE
Graphics window
*(X,Y)
Image1
*(Xim,Yim)
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
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
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.
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
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.
Here too, you can write Form1 instead of Self. But you can simply omit the Self as well.
Page 9 / 1783
Page 10 / 1784
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?
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:
Page 11 / 1785
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
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.
Page 13 / 1787
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
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
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
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;
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.
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;
Page 17 / 1791
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
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;
Page 19 / 1793
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
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
subcribe
to the printed version
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?
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;
Page 21 / 1795
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
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
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
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.
Page 24 / 1798
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.
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 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
Page 25 / 1799
Figure 5: Right- clicking on the object, Text1, will show a popup Figure 7: Select the New Data Object icon from the Project group
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
Page 26 / 1800
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.
Page 27 / 1801
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...
Figure 15: Text objects below the region component are not visible.
Page 28 / 1802
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
Page 29 / 1803
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
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
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.
Page 31 / 1805
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
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!
subcribe
to the printed version
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 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}
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.
Page 33 / 1807
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.
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/
Page 34 / 1808
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.}
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.
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;
Page 36 / 1810
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!
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.
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.
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
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
Page 39 / 1813
Special offer:
Buy 1 ReadyNAS and get a free HD 300GB: Order at http://www.blaisepascal.eu
Page 40 / 1814
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.
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.
Defined JavaBeans
Industry
Structured Programming
OO Programming Windows
Components
JavaBeans
Linux
.NET
J2EE
Web Services
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.
Industry
PHP
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)
http://www.codegear.com/shop
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
Barnsten Business Solutions, Binderij 7 B NL-1185 Amstelveen tel. +31 20 347 9020 www.barnsten.com