Anda di halaman 1dari 126

BLAISE PASCAL MAGAZINE

ALL ABOUT DELPHI AND DELPHI PRISM(.Net) , LAZARUS & PASCAL AND RELATED LANGUAGES

Pascal

SPECIAL ISSUE 126 PAGES

18

irr

or ,m

irr

or

on

th

wa
Cylindrical anamorphosis / Chinese Nightingale
Book review James Duff Writing New Components in Lazarus, Part 2 Howard Page-Clark Delphi XE2 is the future - theme XE2 Fikret Hasovic Cylindrical anamorphosis Peter Bijlsma Web Service Toolkit as a rich type framework for Plug-in development Inoussa Ouedraogo Polygon colouring and area calculation David Dirkse Creating a Database program from scratch - part 1 Detlef Overbeek An Android client for a DataSnap Server Part 2 Daniele Teti Anti-freeze for the VCL Alexander Alexeev Delphi XE2 New Features - theme XE2 Bob Swart Learning to use FastReport 4 for VCL - Part 3 Sergey Lyubeznyy Question and Answers Henk Schreij Creating 64-bit applications with Delphi XE2 - theme XE2 Jeremy North kbmSQL Structured Query Language for your memory table Kim Madsen High Level Multithreading PrimozGabrijelcic TMS Scripter Studio Pro Wagner R. Landgraf & Bruno Fierens FreePascal vectorial Felipe Monteiro de Cavalho The tale of the Nigthtingale Detlef Overbeek Using the Advantage Database Client Engine Michal Van Canneyt Configuring OLE DB Providers and ODBC Drivers Cary Jensen Delphi XE2 LiveBinding - theme XE2 Bob Swart

ll,

wh

at

's the code I need to call?

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

BLAISE PASCAL MAGAZINE 18 ALL ABOUT DELPHI AND DELPHI PRISM(.Net), LAZARUS & PASCAL AND RELATED LANGUAGES
CONTENTS
Articles
Book review James Duff page 4 Writing New Components in Lazarus, Part 2 Howard Page-Clark page 6 Delphi XE2 is the future - theme XE2 Fikret Hasovic page 13 Cylindrical anamorphosis Peter Bijlsma page 17 Web Service Toolkit as a rich type framework for Plug-in development Inoussa Ouedraogo page 21 Polygon colouring and area calculation David Dirkse page 24 Creating a Database program from scratch - part 1 Detlef Overbeek page 29 An Android client for a DataSnap Server Part 2 Daniele Teti page 34 Anti-freeze for the VCL Alexander Alexeev page 39 Delphi XE2 New Features - theme XE2 Bob Swart page 46 Learning to use FastReport 4 for VCL - Part 3 Sergey Lyubeznyy page 53 Question and Answers Henk Schreij page 16 Creating 64-bit applications with Delphi XE2 - theme XE2 Jeremy North page 64 kbmSQL Structured Query Language for your memory table Kim Madsen page 72 High Level Multithreading PrimozGabrijelcic page 76 TMS Scripter Studio Pro Wagner R. Landgraf & Bruno Fierens page 81 FreePascal vectorial Felipe Monteiro de Cavalho page 90 The tale of the Nigthtingale Detlef Overbeek page 96 Using the Advantage Database Client Engine Michal Van Canneyt page 101 Configuring OLE DB Providers and ODBC Drivers Cary Jensen page 107 Delphi XE2 LiveBinding - theme XE2 Bob Swart page 113

Volume 18, ISSN 1876-0589


Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68

News and Press Releases email only to editor@blaisepascal.eu Authors


A B C D F G H J L M N O P R S Alexander Alexeev Peter Bijlsma, Michal Van Canneyt, Marco Cant, David Dirkse, Daniele Teti Bruno Fierens Primo Gabrijeli, Fikret Hasovic Cary Jensen Wagner R. Landgraf, Sergey Lyubeznyy KIm Madsen, Felipe Monteiro de Cavalho Jeremy North, Tim Opsteeg, Inoussa Ouedraogo Howard Page-Clark, Herman Peeren, Michael Rozlog, Henk Schreij, Rik Smit, Bob Swart,

Editors
Peter Bijlsma, Rob van den Bogert, W. (Wim) van Ingen Schenau,

Correctors
Howard Page-Clark, James D. Duff Copyright Page 118 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. Subscriptions ( 2011 prices ) 1: Printed version: subscription 60.-(including code, programs and printed magazine, 4 issues per year including postage). 2: Non printed subscription 35.-(including code, programs and download magazine) Subscriptions can be 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.

Advertisers
Advantage Database Server page 3 AnyDac page 70 Barnsten page 98 Be-Delphi page 124 BitTime page 36 Cary Jensens newest book page 106 Components for Developers page 71 Database Workbench Pro- Upscene Productions page 45 Embarcadero page 99, 100 FastCube page 63 FastReport page 62 LAZARUS Book page 60 TMS Software page 89 Subscription info page 61, 115

Subscriptions run per calendar year.


Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email. Invoices will be sent with the March issue. Subscriptions can be paid by sending the payment to: ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal or TakeTwo Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)

Subscription department
Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68

office@blaisepascal.eu

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Book Review James D (Jim) Duff


1: The Architecture of Lazarus The Pascal language and the tools available to enhance the writing and correction of source code; the meaning and descriptions of projects, components and packages 2: Installing Lazarus Installing Lazarus for Windows, Linux, FreeBSD and MacOS X, plus installation of version management systems: TortoiseSVN and Subversion 3: The IDE Using the Lazarus IDE (Integrated Development Environment) for rapid application development, explained in detail 4: Projects Developing different types of projects - GUI (Graphical User Interface), console and unit test applications, and using packages to develop new components
LAZARUS The Complete Guide Background

This book is an English translation of the German original, written by several authors who are part of the Lazarus design team. Following the conversion into English by several translators, a final overall grammatical review was undertaken by Howard Page-Clark, creating a uniform style throughout. The translation and production of this English version was managed by Detlef Overbeek, Editor-in-Chief of Blaise Pascal Magazine. Following a knowledgeable Foreword by Detlef, the opening chapter contains a very descriptive and competitive opening paragraph about this free, open source software development product:

5: Target Platforms Understanding the differences among the platforms Lazarus supports - Windows 32 and 64, Windows CE, Unix Systems (FreeBSD) and MacOS X 6: The Class Libraries The class libraries available: RTL, FCL and LCL, and a description of the functionality supplied with Lazarus in its two hundred components. 7: Porting Delphi Components How to port existing Delphi components to Lazarus (where possible)

8: Files and Devices Working with files, directories, serial and parallel ports, and "Lazarus is an integrated development environment (IDE) printers. for the Object Pascal Language. The Free Pascal Compiler provides the backend for Lazarus. This combination of 9: Graphics Programming backend engine and integrated development environment Lazarus graphics programming - working with images and is comparable to Visual Studio for C++ or Borland icons, the necessary basics for making attractive interfaces Delphi. It yields efficient, highly optimised native source in this day and age code. Whereas Java and C# development environments produce bytecode that must be interpreted in a runtime 10: Processes and Threads environment or converted into poorly optimised code by a Operating system processes, and programming with JIT compiler." multiple threads - functions that highlight the ability to make the compiled software operate efficiently Go Pascal! 11: Network Programming Review Web services, client/server and TCP/IP network This is a large book that contains 735 pages comprising 12 programming chapters. A CD containing the source code is also provided with the book, as well as a free USB stick that 12: Database Access contains an installed version of Lazarus - just plug it in Working with flat-file and client/server databases (via a and go. unified database interface to DBF files, Firebird, Interbase, MySQL, Oracle, PostGreSQL, SQLite and ODBC), SQL The book's chapters are listed here together with a small and reporting (based on FastReports) summary of each to give an idea of the book's coverage.
4
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Book Review
LAZARUS The Complete Guide (continuation)

There is also an additional Lazarus USB STICK. It contains a preinstalled copy of Lazarus on a free 4GB The chapters are presented in a logical courseware type layout and sequence for beginners to the Pascal language, USB stick. 50 additional sample projects one of the key target audiences of Lazarus. It also The book includes the folowing example programs: provides sufficient details for experienced Pascal/Delphi B: borderstyle\ borderwidth \ bouncer programmers wishing to utilise the benefits of Lazarus. D: dbftrack\ dbfviewer\ dialogdemo\ docking dragdrop\ dragdropobject\ dynamicforms Summary E: editordemo\ eventsdemo One has a feeling of the good old days when manuals F: filedragdrop\ formevents provided the reader with a logical and educational theme G: gentrackdata\ griddemo for learning about particular software products, such as I: ibtracker\ initialwindowposition\ inputquery Turbo Pascal or Delphi. L: lazreport\ listviewdemo M: menudemo\ messagdlg\ morecontrols This book has been generated by a group of highly skilled P: pacman \ paramsdemo \ pinguin contributors and, together with the other positive benefits Q: questiondlg mentioned above, is highly recommended for use by both S: showmessage\ sizing-samples\ sizingwindows beginners and experienced Pascal developers. splitterdemo\ splitterpairdemo\ statusbardemo T: toolbardemo\ traffic light \ treeviewdemo Am I going beyond the subject of reviewing the book W: windowmenu\ windowstate when I say many of its topics make the end product very attractive? Also, the ability to have an efficient development tool delivered on a USB stick, ready to go, is Additional preinstalled component suites on the USB Stick: another positive element. Hmmm, I think this book has Additional / done its job on me. Chart / Common / Controls / DataAccess / DataControls / Dialogs / The Book Indy Misc - Core / Intercepts- Core /IO Handlers / LAZARUS The Complete Guide Servers Core / Clients core / Misc Protocols Decoder / Including CD Misc Protocols Encoder / Misc Protocols / IO Handlers Protocols / IO SASL Protocols / Authors: IO Intercept Protocols / IO Servers Mapped Port / IO M. van Canneyt, M. Grtner, S. Heinig, F. Monteiro de Servers Protocols (nz) / IO Servers Protocols (am) / Cavalho, I. Ouedraogo Clients Protocols (am) / Clients Protocols (nz) Inet / Development Editor, Production Editor and Cover Ipro / LazControls/ Lazreport / Misc / RTTI / Designer: Detlef Overbeek Copyright 2010 by C&L Computer und Literaturverlag SQLDB / Standard / SynEdit System / VE Copyright 2011 All rights for the English version reserved by Blaise Pascal Magazine Update Service there is an update service available that ensures you have ISBN: 978-94-90968-02-1 for the paperback the latest version. It cost only 25, for a maximum of ISBN: 978-94-90968-03-8 for the hardcover four updates.. This is an affordable way to ensure you have a complete, fully updated Lazarus program. The Update service wil be available at the end of September 2011. You will be notified by web and email as soon as there is an update available.

Each chapter contains very well described topics along with relevant screen shots and source code. The level of detail is relevant and correct, given that the authors of the book were involved in the development of Lazarus.

USB STICK

To receive a printed copy of the magazine you need to go to our website to place your order

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

Writing New Components in Lazarus, Part 2 By Howard Page-Clark


A little bit of work up-front in creating and saving the package, setting the component's properties and recompiling the IDE The first article in this series looked at the Lazarus will save you a lot of repetitive work in the future (until you change component hierarchy, and some properties of TControl company, that is; although the component will continue to benefit other and TComponent that are important for a background employees). understanding of component development. This If you've never created a new component in Lazarus before, subsequent article focuses on the stepwise development this is a good way to familiarise yourself with the process, of a specific simple component. even if such a customised label appears to be rather a trivial component to you. Frankly, it is, because this is an early article in You write a new component to fill a gap in the functionality of a series. There's plenty of scope for component complexity the components available to you. You need some GUI (or other) ahead but let's begin simply. If you don't want this component functionality that you can't find on the Lazarus Component installed long-term, it is easy enough to remove it from the IDE Palette, so you wonder about creating a new component that later. You can walk through the construction stages simply as a does meet your need. The default Component Palette found on learning process. Subsequently you can remove the component a newly installed latest-release Lazarus (0.9.30) has 13 pages by recompiling Lazarus without the newly added package (and containing 186 components in total. But that is not enough! delete the CompanyLabel package directory altogether if desired). Note Perhaps you find a component that almost but not quite that in Lazarus (unlike Delphi) new components can be does what you want. introduced into a Component Palette page only by recompiling Say you are working primarily on Windows and want to use the Lazarus IDE, since the new component is linked statically buttons that can be shown in subtly different colours. Although with the rest of the IDE code. There is no provision (currently) a TButton's Color property can apparently be 'changed' for a dynamic package system of component dlls such as Delphi in the Object Inspector to something other than clDefault, uses (these are .dpl and .bpl files together with .dcp files, which in you discover that it always gets reset to clDefault! versions from Delphi 2 up to Delphi XE have all been Win32-specific, Checking the Object Inspector's Restricted page confirms that a and depend on compiler 'magic' to work dynamically). Windows widgetset restriction applies: the native Windows The basic steps of new component button-widget's colour cannot be changed. You search beyond the Standard page. But neither TBitBtn, TSpeedButton creation/installation We will illustrate the basic steps involved in installing a new (Additional page) nor TColorButton (Misc page), nor Lazarus component from scratch by creating the simple TTIButton or TTIColorButton (TTI page) suit your TCompanyLabel component suggested above, and then purpose, in spite of their hopeful button-containing names. consider how these basic stages need to be amplified when In fact in this case there is a custom drawn component that fits writing a more sophisticated, more widely useful component. If the bill exactly. Felipe Monteiro de Carvalho and JiXian Yang you don't want to have your component installed in the IDE have written several custom drawn Lazarus components. (employing its functionality merely by adding its unit to your These include a TCDButton, which provides button colourchanging functionality on all Lazarus-supported platforms inclu- application's uses clause and instantiating it through code) that is ding Windows. For more information on these components see certainly possible. We will look at that scenario in due course. http://wiki.lazarus.freepascal.org/Lazarus_Cus However, the full process of installing a visual component into the IDE that can be manipulated by both the Object Inspector tom_Drawn_Controls and the Form Designer requires registration of the component In a standard Lazarus Windows installation you'll find the with the IDE. To accomplish that, you need to follow these package from which you can install these components at steps: C:\lazarus\components\customdrawn\ Decide on your new component's ancestry and name customdrawn.lpk (Optionally) design an icon to identify your component on In this particular case you can take advantage of the work of the Palette other developers which they have made freely available. But Create a new folder for the new component package supposing we can't find the component functionality we need? Then we have to roll our own. Create a new component package Write (or adapt) the component unit as appropriate to Setting properties to make a 'new' provide new functionality component Test the new component's functionality before installation, The simplest way to customise an existing component to better debugging as required suit your purpose is to pre-set the default properties of that Check the new package's integrity by doing a test compilation component, and save this altered component as a 'new' Install the package in the Lazarus IDE by recompiling the component on the Lazarus Component Palette (with a slightly IDE altered name, of course, since component names must be unique). Test the component's IDE functionality after installation, If you find yourself time and again dropping a TLabel on a debugging if needed form and setting its Caption to your company's name, its Name to lblCompanyName, its Font, Color and Layout to match your company's livery, its Autosize, Anchors, and so on to the same regular values then it is time to create a TCompanyLabel as a descendant of TCustomLabel (or TLabel), set its properties accordingly, and save it in a new package. You can then install it by recompiling Lazarus to get it Figure 1: Two indistinguishable icon-less components added to the Common Controls page to appear as a new item in the IDE's Component Palette. 6
COMPONENTS
DEVELOPERS

starter

expert

Lazarus 9.30

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Writing New Components in Lazarus, Part 2 (continuation 1)


Creating TCompanyLabel by descending from TWhat? Which class should our new component descend from? This is always an important question in component development. Finding the best answer to this question requires familiarity with the LCL class hierarchy. Potential ancestors will usually include at least the following possibilities: TGraphicControl TWinControl TCustomControl TCustomxxxx TGraphicControl (or a TGraphicControl descendant) is the best ancestor for visual components that do not require user interaction, but just provide information to the UI; components such as labels, shapes, and progress indicators. On the other hand, components that must respond to mouseclicks or gestures or keystrokes (components that will receive and lose focus) will require a window handle (or its equivalent in non-Windows operating systems) and must descend from TWinControl or one of its many descendants. Often the best choice in either case is a TWinControl or TGraphicControl descendant named TCustomXXX where XXX might be Label, Edit, StringGrid and so on. Nearly all the standard LCL controls such as TLabel, TEdit, TStringGrid and so on have an immediate ancestor named TCustomLabel, TCustomEdit,or TCustomStringGrid.The immediate TCustom ancestor has all the designed functionality of its descendant, but has few, if any, published properties or events. This is a deliberate LCL (and VCL) architectural feature, which keeps controls as small and sparing of memory footprint as possible. The actual, everyday control such as TLabel is nothing more than a TCustomLabel with suitable properties and events published for availability in the Object Inspector. Our new TCompanyLabel component is just a customised TLabel. In this particular case there is little to be gained by descending from TCustomLabel, so we will descend directly from TLabel, thereby accepting all TLabel's published properties and events. It is often the case that a new component does not need all the published properties of its ancestor. Here there is a distinct advantage in descending from a TCustom class, since unneeded publishing of properties and/or events can thereby be avoided. Component Identity To identify the new component we need a name for it, and a unique icon (if it is to be distinguishable from nearly 200 other components on the Component Palette). Both name (text) and icon (picture) deserve careful consideration, and of the two, the name is more important because you must give a component a name, and it must be unique. If you do not provide an icon for a registered component Lazarus will supply a default icon for it in the Component Palette (see Figure 1). Provided there are no other icon-less components on that Palette page, this will suffice. However, if you have several such components on a single Palette page, the icons will indicate how many such components there are, but obviously won't identify which is which. Users who have enabled the Hints for component palette checkbox in the Hints section of the Environment Desktop page of the IDE Options dialog will see the component's name in a hint box if they move the mouse over the icon. Users who don't have this checkbox enabled will be shooting in the dark. (You reach the IDE Options dialog via the ToolsOptions menu). The requirement for a unique name needs careful consideration. A long-standing convention names new components with a twoor three-letter prefix to identify the component's author or owning company, such as the TkbmSQL component featured in this issue from Components4Developers (see page 60). A good name will be descriptive, unique, and preferably short. Of the 186 components that are installed in a default Lazarus installation the name lengths range from the shortest (TDbf, TEdit, TMemo) up to the longest, TDateTimeIntervalChartSource, a 28-character name, which is hardly short, though it is descriptive and unique. One of the beauties of a language such as Object Pascal is its self-documenting propensity, provided you choose identifier names carefully and intelligently. Two other related names need careful thought, as well. What will you call the Pascal unit where your component's code is stored, and what will be the name and location of the package used to register the component? There is the possibility of a name clash here if you are not careful, because if you name the package MyComponent.lpk, Lazarus will automatically generate a file in the package directory called MyComponent.pas which is used to compile the package. There is potential here for this file to unwittingly overwrite your component's source file if you have not thought about a possible name clash. So avoid giving the package and the component source file exactly the same name. Make them similar, but not identical. In addition to intelligent choice of names throughout the development process it is also a good idea to set up a component development directory structure along the following lines:
..\ComponentPackageDirectory\source\ \images\ \languages\ \tests\ \docs\

If you are not planning on internationalising your component, you won't need the \languages subdirectory, and many people manage without an \images or a \tests subdirectory. Sometimes a \lib\ or \bin\ subdirectory is useful. Obviously how you set up your development file storage structure is up to you. Lazarus will not enforce anything, except requiring a unique component name, and auto-generating the yourPackageName.pas file in your package directory. Nevertheless, a considered, tidy approach may help you avoid unnecessary and frustrating errors later in the development process. I can testify that the careless bung-everything-in-asingle-folder approach is a recipe for later name conflicts! Creating a new component package Lazarus' package system is quite different from Delphi's, and the identical term used by the two development tools can be misleading since the basic concept of a Lazarus package differs from the Delphi concept. Lazarus uses packages to modularize projects in a cross-platform way. Lazarus allows you to deal with only one project at a time, but you can have any number of packages open simultaneously. Each package has its own directory, its own compiler configuration and its own language files, and packages are versioned.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

Writing New Components in Lazarus, Part 2 (continuation 2)


Packages are not restricted to containing source code: you can add any type of files you wish to a package, such as a .wav or a .png file. A package developed in a Linux environment such as Ubuntu can be copied and recompiled in Mac OS X or Windows without needing any changes to paths or settings (the folder separators '\' and '/' are converted transparently as needed). In particular, Lazarus uses packages to implement components that are registered with the IDE and show up on the Component Palette. As ever, cross-platform considerations mean that you are best advised to keep all file names lowercase only (you can't know who might one day want to use your work, or what platform they will be working under assuming, that is, you'll not be proprietary about your code). Lazarus packages are created and maintained by the Package Editor. This is accessed either from the File New menu, selecting the Package item from the treeview on the left of the New dialog Figure 4: The newly created (empty) companylabel.lpk (Figure 2), or you can access it from the PackageNew package package open in the Package Editor menu (Figure 3). Notice that the package has already had the FCL added by Lazarus as a mandatory requirement, so we don't need to add this ourselves. To customise the package for our purposes we need to add other content and requirements, so we click on the [+ Add] toolbutton in the toolbar at the top of the Package Editor. This brings up the Add to package dialog (Figure 5):

Figure 2: The New dialog accessed from the FileNew menu

Figure 5: The Package Editor's Add to package dialog


The Add to package dialog has four pages: New File, New Component, New Requirement, and Add Files, and opens by default at the first page, New File. Our package is a component package, so we click on the New Component tab whose opening appearance is as follows (Figure 6):

Figure 3: The Package New package menu

Whichever method you use to access the Package Editor, Lazarus first presents you with a Save Package NewPackage 0.0 (*.lpk) dialog, waiting for you to name the new package (default name NewPackage.lpk) and save it. Although you can dismiss this dialog by pressing [Esc], it is far better to give the new package its long-term name now, and save it in a new directory created specifically for the creation of the new component. If you're following along, do that now, and you will see the Package Editor with the newly created, newly named package open. For me the initial view of the Package Editor dialog appeared as follows, entitled Package companylabel with the full name of the new package shown in the status bar at the bottom of the window (Figure 4). 8
COMPONENTS
DEVELOPERS

Figure 6: The New Component page of the Add to package dialog

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Writing New Components in Lazarus, Part 2 (continuation 3)


If you're familiar with Delphi's New Component wizard, you'll Lazarus then opens the newly created companylbl.pp file appreciate that analagous pages from that wizard are compressed in the Source Editor. You'll find that it contains the following here (sensibly) into a single page of required information. skeleton code written for you by Lazarus: The Lazarus version of its Delphi equivalent lacks the colourful unit CompanyLbl; designs that have been lovingly lavished on the Delphi wizard, {$mode objfpc}{$H+} but it also saves you from having to click through lots of pages of a wizard when one page suffices. There are five pieces of interface mandatory information that the component writer has to supply, uses and optionally you can specify a sixth, an icon. When you drop Classes, SysUtils, LResources, Forms, Controls, down the Ancestor Type combobox's list, you'll find that it lacks Graphics, Dialogs, StdCtrls; any TCustomXXX entries apart from TCustom Control. So so if you wanted to use say, TCustomLabel, you'd have to type TCompanyLabel = class(TLabel) type it in manually (you're not restricted to classes only found in the dropprivate down list), and if it exists Lazarus will find it without any { Private declarations } problems (unless you spelled it incorrectly). We want to use the protected standard TLabel, so can just select it in the drop-down list. { Protected declarations } public Lazarus then fills in the other fields with some defaults, which { Public declarations } you will probably want to change. If you type the name of a published non-existent Palette Page: entry, Lazarus will create that page for { Published declarations } you, and place your new component on the newly added page. end; To add an icon you click on the ellipsis[]button labelled Icon (maximum 24x24) which lets you locate a .png icon file. Note procedure Register; that this must be named with the new component's class name implementation (so tcompanylabel.png in this case). The completed New Component page looked as follows, before procedure Register; begin clicking the [OK] button:

{$I Companylbl_icon.lrs} RegisterComponents('Additional',[TCompanyLabel]); end; end.

You'll see that this is just a standard Object Pascal unit, including compiler directives for objfpc mode (rather than delphi mode) and for treating the keyword string as meaning ansistring ({H+}). Eight units which you are likely to need are included in the uses clause, the new component class declaration is written out as a skeleton descending from the ancestor you specified earlier, and a Register prodecure is declared and implemented for you. Notice that this two-line Register implementation associates the icon you specified with the new component via a {$I } Figure 7: The information needed to complete the New Component page directive which has inserted a Lazarus-generated resource file (based on the icon you specified) named Companylbl_icon.lrs, When specifying the Unit File Name: information there is an and places the registered component on the Palette page given intruigingly labelled [ <> ]button beside the ellipsis [ ]button earlier via the RegisterComponents()procedure. (that brings up a Save As dialog). The [<>] button is a Shorten or Of course you could write all this code yourself, but it is easier expand button which usefully shows either the short filename and less error-prone to use the New Component page of the (source\companylbl.pp here) or the expanded filename Package Editor to do it for you. If we look at the Package Editor with the full path. This affects the name display only. now (via the Window Package CompanyLabel* menu) we see Lazarus saves all files with fully qualified paths, however much that Lazarus has now added the LCL in the Required Packages of that path is actually displayed in the dialog. Clicking the treeview node (TLabel obviously needs the LCL) and the [OK] button creates a new companylbl.pp in the location component file \source\companylbl.pp as a package you have specified. If the package has not registered any of the file, as we would expec (see Figure 9). paths you've just entered an intermediate dialog will pop up If we highlight the companylbl.pp file you'll see in the File asking if you want the package to add the paths (to which you Properties section near the bottom of the Package Editor should agree). window that two new checkboxes have appeared (Register unit and Use unit) which are both now (correctly) ticked (Figure 9). The checked Register unit checkbox indicates that companylbl.pp contains a Register procedure in its interface which will be called by the IDE to place the component on the Component Palette. The checked Use unit checkbox indicates that this unit will be compiled automatically when the package is compiled, which is what we want to happen. The asterisk at the end of CompanyLabel* in the Package Editor's title indicates that the new package has not yet been saved. Figure 8: A confirmation dialog for changing the unitpath

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

Writing New Components in Lazarus, Part 2 (continuation 4)


It is time to click on the Save toolbar icon. This saves the package, and the asterisk appended to the package name in the Editor's title bar disappears (until you make some other change to the package). Notice also that the Registered plugins area at the bottom of the Package Editor is empty. This indicates that our new component is not yet registered with the Lazarus IDE. Customising the component code For this example we shall use the hypothetical company Timeless Ltd. If this is a useful component for you it would obviously be customised appropriately for your situation. We proceed to alter the skeleton code to fashion TCompanyLabel's appearance as Timeless Ltd. might require. For this simple customised TLabel component we don't need 3 of the 8 units Lazarus had added to companylbl.pp, so we remove SysUtils, Forms, and Dialogs from the uses clause. (You can leave them there, redundant, of course, but my preference is to issue only needed instructions to the compiler, even though it ignores unneeded ones). To set various default properties as the component is constructed we simply override the constructor, supplying our own, which first calls the inherited constructor and then calls a SetDefaults procedure we write, where the customisation takes place. The amended class declaration then looks like the listing at the left. Setting properties in code is obviously more tedious than clicking around in the Object Inspector, but once it's done and debugged, that property-setting code can be recalled anytime thereafter simply by dropping this new component on a form. It then comes with all the properties already set the way you want them. So we save the CompanyLbl.pp file, click on the Compile toolbutton in the Package Editor to check that the component compiles correctly, and if so click then on the [Use >>] toolbutton, to begin recompiling the IDE (Figure 10).

Figure 9: Highlighting the companylabel.pp file in the Package Editor


If we compile and install TCompanyLabel at this stage it will register a duplicate TLabel component, identical in every respect to a default TLabel, except for its name and alternative icon. So we next need to customise our component before use. Component writing is a non-visual task we have to write the customisations in code, since the component-to-be cannot be manipulated yet in the Form Designer nor can it be customised yet using the Object Inspector.
TCompanyLabel = class(TLabel) private procedure SetDefaults; public constructor Create(TheOwner: TComponent); override; end; The new constructor and SetDefaults procedure are as follows: Constructor TCompanyLabel.Create(TheOwner: TComponent); Begin Inherited Create(TheOwner); SetDefaults; End; Procedure TCompanyLabel.SetDefaults; Begin := 'Timeless Limited'; Caption Autosize := True; Alignment := taCenter; := clMoneyGreen; Color ParentColor := False; := clMaroon; Font.Color := False; ParentFont := 'Calibri'; Font.Name := [fsBold, fsItalic]; Font.Style Font.Height := -19; := 2; BorderSpacing.InnerBorder := 1; BorderSpacing.Around := 23; Constraints.MinHeight := 140; Constraints.MinWidth Hint:='Timeless can meet all your software needs'; := True; ShowHint Transparent := False; End;

Figure 10: Selecting the Package Editor's Install menu option This brings up a confirmation dialog, reminding you that to register and install a component in the Lazarus IDE requires recompiling the whole IDE (a somewhat time-consuming operation first time round, so it checks first that this is indeed what you want to do).

Figure 11: The Rebuild Lazarus? confirmation dialog

10

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Writing New Components in Lazarus, Part 2 (continuation 5)


Clicking the [Yes] button proceeds to recompile the IDE. The Messages window keeps you informed of progress until Lazarus closes and restarts, whereupon you should find a new icon in the Component Palette's Additional page (as specified earlier) which can be seen as the rightmost hourglass icon of Figure 12. Now a TCompanyLabel instance can be dropped straight onto a form, and it has all the properties that previously you would have had to set by hand, all of them pre-set ready for use. Finishing touches and removal We have done no testing of our component, except to iron out the one property-setting glitch encountered above. However, in the case of this simple, straightforward component that does nothing more than alter a few property values in code there is no point setting up a test program. Hundreds of thousands of Lazarus users 'test' the TLabel component every day, and we can be fairly sure it is thoroughly debugged! Our minor adaptations of the component are not likely to introduce errors. Of course a more complex component with major functionality we have coded ourselves will need much more thorough testing before it gets added to the Component Palette. Registering a buggy component with the IDE (even if it compiles and installs) will quite likely lead to a buggy IDE, and unexplained Lazarus Figure 12: The Additional page of the Component Palette crashes. sporting a new component icon If we open the component package now in the Package Editor Well, almost all the properties you want. Disappointingly, not and highlight the companylbl.pp source file, we see that every property we set via SetDefaults in the component's the previously empty Registered plugins region is now updated constructor has come through. The Caption, which we set to reflect the registration of this new component with the IDE. in code to be 'Timeless Limited' has been overridden by Lazarus The name of the registered plugin is shown to become 'CompanyLabel1' (see Figure 13). (TCompanyLabel)with its icon, as well as the Component Palette page where it is located (the Additional page).

Figure 13: The newly dropped component with the 'wrong' caption
What is going on here? Well it turns out that after construction Lazarus does some housekeeping, which includes setting the default name of new components if the component's ControlStyle property contains the csSetCaption flag (see the previous article for more detail). The Caption is set to match the name. Since all new components have the csSetCaption flag set by default, we have to override this Lazarus behaviour to get our constructor's Caption-setting code to persist. So we must add a line to the SetDefaults procedure given earlier as follows:
Procedure TCompanyLabel.SetDefaults; Begin ControlStyle := ControlStyle - [csSetCaption]; Caption := 'Timeless Limited'; // etc. End;

Figure 15: The Package Editor showing the newly registered plugin
Moreover, if we click the [Use >>] toolbutton when the component package is open in the Editor we now see an additional Uninstall menu option available. Lazarus recognises that not all registered plugins are there to stay. If we change our mind about TCompanyLabel, we now have the option to uninstall it. After putting up a confirmation dialog, Lazarus lets us uninstall the component.

If we recompile and install our TCompanyLabel component, we now get the behaviour we want.

Figure 16: Getting rid of an unwanted component plugin


As when the component was first installed, so now the uninstallation of a statically-linked package requires recompilation of the Lazarus IDE to remove it. So a further confirmation dialog intervenes before the recompilation is allowed to proceed.

Figure 14: TCompanyLabel at design time and runtime


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

11

Writing New Components in Lazarus, Part 2 (continuation 6)

Figure 17: Confirmation is required before component removal and IDE recompilation
The freshly compiled Lazarus IDE now has an Additional Component Palette page with no sign of the TCompanyLabel icon that was there previously. The ability to uninstall components is, of course, as important as the ability to install them. The Lazarus Package Editor The Package Editor is a sophisticated tool, whose functionality is worth fully exploring. There is not space to document it all here, but several pages of the Compiler Options for Package XXXX dialog are worth a brief mention. You access this from the [Options] toolbutton (sixth from the left) of the Package Editor's toolbar. The Description page allows you to briefly document your component and add copyright and licence information, as well as set the version number (see Figure 18): The IDE Integration page has radiobutton options set for you by Lazarus at installation, but the preferences can be changed if for some reason you want to override Lazarus' guess as to whether the component is runtime only or runtime and design time (Figure 19). If you have FPDoc documentation files (which for a component of any complexity you ought to take the trouble to provide, so users have local access to important details of usage and behaviour) you specify their location here too.

Figure 19: The Package Options's IDE Integration page


Next time we will write from scratch a more widely useful component to fill a gap in Lazarus' current provision. This will require much more extensive customisation of an existing component.
The author Howard Page-Clark lives near Poole, Dorset in the UK. He is a hobby programmer who first made use of Delphi to develop database programs when working as a book keeper for a disability charity. After a period as a teacher of science to teenagers, he now works as a volunteer with charities and church groups. He is married to Jodi, and they have four adopted children, now all grown-up.

Figure 18: The Description page of the package's Compiler Options


12
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 is the future by Fikret Hasovic


starter expert
DELPHI XE2

Since I first learned Basic, C and Pascal in the early 1990s I have always preferred Pascal's clear language combined with its low-level abilities (if needed). Object Pascal has been continuously developed since then through the introduction of the VCL framework to wrap Windows widgets and API calls, while Borland and then CodeGear adapted both the language and the Delphi IDE to keep pace with technological developments. Embarcadero's acquisition of CodeGear was a further important milestone. Now we have its upcoming star product: RAD Studio XE2, in which Pascal takes two further leaps forward: Delphi's ability to compile for non-Windows target platforms, and the introduction of a new cross-platform framework, FireMonkey (FMX) to complement the cross-platform compiler capabilities. This release brings other enhancements as well.

- Cross-platform application development provided by Macintosh OS X cross compilers: Delphi (DCCOSX.exe) and C++ (BCCOSX.EXE) and new cross-platform component libraries. - 64-bit application development for Windows with a new 64-bit Delphi compiler and a 64-bit Delphi RTL. We finally get 64-bit support in our favourite development tool! And for a first version, it is very impressive. - DataSnap Connectors for mobile devices - take aa look at Bob Swarts article on page 111 - (Android, Blackberry, iOS, and Windows Phone) have been introduced. - LiveBindings, A entirely new feature is LiveBindings, the new data binding feature that simplifies your programming work with both the VCL and the new FMX FireMonkey library. So, Delphi XE2's supported platforms are now: Mac OS X (Delphi and C++Builder), 32-bit Windows (Delphi and C++Builder), 64-bit Windows (Delphi only). What does Cross-Platform Mean? Cross-platform applications that you develop with the RAD Studio IDE will run on a remote target machine such as a Mac running OS X, or a PC running a Windows 64-bit OS. The VCL is not supported on the Mac OS X platform, so a VCL application has no direct migration path to the Mac or to FireMonkey. If you have a VCL application that you want to migrate to the Mac OS X platform, you start by creating either a crossplatform console application or a FireMonkey application. You install and run the Platform Assistant on the Mac, then create a remote profile on the development system to describe the target platform (Mac OS X), and redesign your Windows application with the requirements of the target platform in mind. For example, you cannot use any Windows function calls in an application for the Mac. If you want to reuse your original application's logic, you must refactor your Windows application and cut-and-paste sections of the code into the new application.

Figure 1 Th splash screen of Delphi XE2


For newcomers Delphi XE2 has everything to get you started. For experts, there are many new features to raise your coding experience to a higher level. This release provides key new features for developing applications using both Delphi and C++Builder.

To create Cross-Platform applications you follow these steps Compiling and building, but not running, cross-platform Key features of the XE2 release: applications is similar to the same operations for building native - The FireMonkey Application Platform for cross-platform Windows applications, with a few additions. Running and applications that run on both 32-bit and 64-bit Windows, Mac debugging a true cross-platform application requires that the OS X, and iOS. The VCL now supports 64-bit and 32-bit development system be connected to the target platform, where Windows, and the Delphi RTL now supports Mac OS X and the Platform Assistant (the remote application server) is running in both 32-bit and 64-bit Windows. listening mode.

Figure 2 RAD Studio now supports Windows 64-bit Cross-Platform applications


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

13

Delphi XE2 is the future (continuation 1)


RAD Studio now supports Windows 64-bit Cross-Platform applications! To create a Delphi 64-bit cross-platform application, you need to add the Win64 target platform to your project and then activate the Win64 target platform. For Win64 you can use either VCL or FireMonkey. Here are the steps: In the Project Manager, any project type that potentially supports cross-platform applications now has a Target Platforms node. When you create a new project, only the native platform (Windows 32-bit) is listed as a target platform: To use a specific platform, you need to activate your chosen target platform: Either double-click the platform (such as OS X), or right-click the platform and select Activate. Note: To add OS X as platform, you need to create an application using the FireMonkey Application Platform. A comparison of the VCL and FireMonkey When you create a new GUI project using either the VCL or FireMonkey, you'll notice a few differences from previous Delphi versions. XE2 introduces consistent use of unit scope prefixes. The Windows related units have the Winapi prefix, and the VCL units have the Vcl prefix. FireMonkey units have the FMX prefix. In what follows you can see that the two frameworks generate basically similar skeleton code. Apart from referring to different framework units, the resource include statements are also different: "*.dfm" for VCL applications as opposed to "*.fmx" for FireMonkey applications. An example VCL uses clause:
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

An example FireMonkey uses clause:

Figure 3
The name of the active platform is boldfaced in the Project Manager. The native Win32 platform is the default active platform. To make the current project a crossplatform project Add a target platform (right-click the Target Platforms node, and click Add Platform):

uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs;

The two framework's designers are fairly similar, with a few differences between them. The VCL designer (as we know and use it already in Delphi):

Figure 6 The FireMonkey designer (cross-platform):

Figure 4
On the Select Platform dialog box, select the platform you want to add, and click OK:

Figure 5
14
COMPONENTS
DEVELOPERS

The FMX designer looks gray at design time, but at runtime FMX adapts to the OS theme where it is running - so it looks like a Windows application on Windows, and like an OS X application on the Mac. FireMonkey (or FMX) is a completely new framework, not based on the VCL, which has been designed with cross-platform considerations in mind (unlike the VCL which was designed solely to wrap Windows controls and Windows API calls). The illustration below shows the appearance of a selection of FMX controls: SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 is the future (continuation 2)

Figure 7
DataSnap Mobile Connectors in RAD Studio XE2

A very interesting new feature in XE2 is the DataSnap extension called Mobile Connectors. The first version of XE allowed you to create ad hoc JSON messages and manually parse the returned JSON messages from an Android connection using a DataSnap REST service. With RAD Studio XE2 you no longer need to do it that way. If you have a DataSnap REST service, you can automatically generate a proxy connector for the major mobile platforms. DataSnap XE2 version now supports four mobile platforms: Android (Using Java) BlackBerry (Using Java) Windows Phone (Using C#) iOS 4.2 (Using ObjectiveC) To enable your DataSnap server for the Mobile Connectors you have to check that feature in the New DataSnap Server wizard.
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

15

Delphi XE2 is the future (continuation 3)


The same basic connectivity elements are associated with each type of deployment. To deploy applications, you use the Deployment Manager which is accessed from the Project Deployment menu. Your deployed Mac OS X application is not just the LiveBindings in RAD Studio XE2 executable, but a set of object files and shared libraries LiveBindings is a data-binding feature supported by both (dylibs). the VCL and FireMonkey in RAD Studio. LiveBindings is I am impressed with how stable and good this XE2 release expression-based, meaning that it uses expressions to feels... bind objects to each other, by means of their properties. Thumbs up! We have a very exciting time ahead of us! LiveBindings is based on relational expressions, called binding expressions, that can be either unidirectional or bidirectional. LiveBindings uses the concept of control objects and source objects. By means of binding expressions, any source object can be bound to itself (it becomes both a source and a control object) or to any other control object, simply by defining a binding expression involving one or more properties of the objects you want to bind together. For example, you can bind a TEdit control to a TLabel so that when the edit's text changes, the label's caption is automatically changed to the value your binding expression evaluates. Another example would be binding a tracker control to a progress bar so that the progress indicator rises or falls as you adjust the track bar. In the same way you can live-bind to a database, alter one or more properties of an onscreen object, and have these changes reflected in the connected database. Because QUESTIONS AND ANSWERS #4 How to #show a hint LiveBindings propagate, you can even alter properties of on an disabled button Q & A objects that are connected to other objects that are bound & USEFUL CODE SNIPPETS to a control object. This type of functionality is no longer Author: Henk Schreij restricted just to data-aware components. The generated proxies support all the standard Delphi types and map them to the native target language. The functionalities of the various Delphi classes are not-oneto-one with the Delphi version, but similar.
Delphi XE2 Compiler improvements

I develop principally in Delphi, so this area is very important to me. Eight new DEFINEs have been added: ALIGN_STACK CPUX86 CPUX64 MACOS (Mac operating system) MACOS32 PC_MAPPED_EXCEPTIONS PIC WIN64
Lastly, how do we deploy cross-platform applications?

We have changed the name of this column. It was UCOS USEFUL CODE SNIPPET ... Readers chose to change the name... If you change a BitButton's Enabled property to False, in Delphi the Hint is no longer shown (though it is in Lazarus). How can we get round this? It is often unclear to a user why a button should be disabled (grayed out). A working Hint (or tooltip) would be an excellent way to explain the reason for the button being unresponsive. For example, with a delete-button you could show a hint message: "You require administrator rights to remove the selected client." However, if this Hint will never be shown for a disabled button any such explanation via a Hint is useless. A matter of chicken and egg? A possible solution to this dilemma in Delphi is simply to use a SpeedButton, and format this to (75x25) to match the default size of a BitButton. The disadvantage is the SpeedButton cant get focus. If you need the button to get focus, there is a further solution. Take a Panel component, sized to match the BitButton (75x25) and drop the button on the Panel. Now if the button is disabled, it will show the Panel's Hint. So enter the required text into the Panel's Hint and your Chicken will have laid the egg.

Deployment can have two different meanings in RAD Studio: Deployment is a required step that the IDE automatically performs when you run your crossplatform application in the debugger. Deployment is the final step that you perform in successfully delivering any completed project.

For the code of the complete project see downloads:

www.blaisepascal.eu

16

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Cylindrical Anamorphosis by Peter Bijlsma


starter expert
DELPHI 3 and above (Win32)

Cylindrical anamorphosis is the technical term for using a cylinder as a mirror to reshape an image. When you look at Figure 1, you see a heavily distorted photograph. To discover what is depicted, you'll have to place a shiny cylinder (silver or chromium) on the circle in the centre. Now when you look at the reflection of the image on the cylinder, you will notice that the undistorted photograph is revealed on the shiny cylinder face. This article describes a method for constructing such anamorphic images and the program you need to produce them.

Figure 2: A possible construction method


So artists of earlier generations had a lot of geometrical work to do to construct their hidden images. Nowadays we have computers to perform this task. For my program I decided to wrap the original picture around the cylinder and calculate the reflected points on the image to be constructed by looking down at an angle of 45 degrees from the viewing point to the cylinder (see Figures 3 and 4).

Figure 1: An anamorphic image


Constructing anamorphic pictures Cylindrical anamorphosis was in use as early as the 16th century. Morphed images were produced from originals varying from painted landscapes to naughty pen drawings. Imagine the big grins on the faces of the lord of the manor and his guests when the secret of his mysterious drawing was revealed by placing a cylinder on top of it! If you are curious about the subject of my example in Figure 1, see the end of the article. Hint: it's happy and wears no clothes! There are several ways to construct anamorphically distorted images. It is possible for an artist to paint an anamorphic image directly by copying the picture details accurately onto paper while looking at the reflection in the cylinder. The image can also be constructed mathematically. One way to do this is to position the original picture inside the circle representing the cylinder base and, for each point on the drawing, to construct a corresponding point outside the circle (see Figure 2). Point P1 of the original picture is transformed to a point I1 on the resulting image by determining the mirror point using the tangent at the cylinder face. You have to use a great many points, since straight lines on the original picture are not straight lines on the anamorphic image. Because the lines of sight in this method are parallel, you can construct this type of morphed drawing fairly easily using a ruler and a pair of compasses.

Figure 3: Calculation of horizontal component Ihor

The vertical position of a point on the original picture to be displayed on the cylinder is called Pv, and the angular position of this point on the circle's circumference is called Pa. The horizontal projection of the reflected line on the image is called Ihor. You can calculate the x and y values of the image point [Ix,Iy] by applying a number of goniometric formulae. This is done in the procedure ConvCylToImg: (next page) COMPONENTS SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 17
DEVELOPERS

Cylindrical Anamorphosis (continuation 1)


procedure TForm1.ConvCylToImg; var Hdis, Vdis, A1, A3, A4, A5, A6, Ia, Ib: double; {convert cylinder coordinates to image coordinates on paper} begin if Pa<0 then Pa:= 360+Pa; if Pa<PaMin then Pa := PaMin; if Pa>PaMax then Pa := PaMax; Par := DegToRad(Pa); Cx := R * sin(Par); Cy := R * cos(Par); Hdis := Ed + Cy; Vdis := Eh - Pv; Ihor := Ed * Pv / Vdis; A1 := ArcTan(Cx/Hdis); if Cx =0 then begin A3 := Pi/2; A4 := Pi/2; end else begin A3 := Abs(ArcTan(Cy/Cx)); A4 := Abs(ArcTan(Hdis/Cx)); end; A5 := Pi - (A4+A3); if A1<0 then A5 := -A5; A6 := A5 - A3; if A1<0 then A6 := A3 + A5; Ihx := Ihor * Cos(A6); if A1<0 then Ihx := -Ihx; Ihy := Ihor * Sin(A6); if A1<0 then Ihy := -Ihy; Ix := Ihx + Cx; Iy := Ihy + Cy; end;

So the only thing we have to do is to map the pixels of the original picture to Pa/Pv coordinates on the cylinder and subsequently convert them to Ix/Iy coordinates on the anamorphic image. This brings us to:

Figure 4: Calculation of Image points Ix and Iy


The Program The program is designed as a demonstration, which means it is not really suitable for end users. Just to demonstrate how many points are necessary to make a drawing, I added a [Hello] button (see Figure 5 at the left). Once clicked, an anamorphic picture is produced which causes the word HELLO to be reflected on the cylinder. For this simple word 156 points were needed to get a reasonable image. They are contained in a separate unit, UnCoords.pas, and consist of an array (named MirrPnts) of record variables of type TCylPnt:

Figure 5: The user interface after Hello has been clicked


18
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Cylindrical Anamorphosis (continuation 2)


The loaded picture is shown in the TImage area. Since you can load high resolution photographs, the picture is stretched so you can verify that the correct picture is being used. Don't be alarmed by this stretching which distorts what you see. For the conversion process the unstretched bitmap (called BitmapOrg) is used, of course. Prior to the Each coordinate pair Pa/Pv is converted to a plotting point morphing process, a calculation is made to fit the picture (Ix/Iy) by calling the procedure ConvCylToImg. The efficiently on the available cylinder space. Using the tangents drawing is then made by connecting the points on the image from the eye to the cylinder (Ihor then forms a straight line from the using LineTo commands. viewing direction in Figure 4), When the Boolean MirrPnts.New has been set to True a maximum and minimum angle for Pa can be calculated and for any given point, a MoveTo command is executed to start a therefore also the maximum width (Wmax) of the picture new line. wrapped around the cylinder. Magazine subscribers will find the details in the source code These values are displayed on the interface. listing, which is available on the www.blaisepascal.eu The picture to be converted is centred on the available area in website. such a way that for portrait type pictures the height is decisive and for landscape type pictures the width is decisive (see Figure I added a [Demo] button to demonstrate the reflection 6 down left). process. It displays a simplified image of Figure 4, and clicking the [+] and [-] buttons enables you to see the position of the line Ihor at various angles Pa (Pv is kept constant). The procedure CheckFormat calculates the high and low values for the angular position Pa (Pahi and Palo), and for the The most interesting part of the program is its capability of vertical position Pv (Pvhi and Pvlo): automatically converting an existing picture (e. g. a photograph) into its anamorphic image. procedure TForm1.CheckFormat; To do this you load a picture (jpg or bmp) by clicking the [Load var Temp, Mid, HVRatio, BMRatio: double; Pict] button, and then convert it by clicking [Make begin HVRatio := Wmax/PvMax; Image]. BMRatio := BitmapOrg.Width/BitmapOrg.Height; You can alter several conversion parameters in the provided if HVRatio<BMRatio then begin EditBoxes: the Cylinder radius (R), Pahi := PaMax; the Cylinder height (PvMax) Palo := PaMin; and the Eye distance (Ed and Eh, equal values). Mid := PvMax/2; You save the converted image by clicking the [Save Image] Temp := PvMax*HVRatio/(BMRatio*2); button. Pvhi := Mid + Temp; To print the image you'll have to make use of a separate Pvlo := Mid - Temp; program. end else begin
TCylPnt = record Angle : double; //angular position Pa Height: double; //vertical position Pv : Boolean; //beginning of a new line New end; Pvhi := PvMax; Pvlo := 0; Mid := 180; Temp := (Pamax-PaMin)*BMRatio/(HVRatio*2); Pahi := Mid + Temp; Palo := Mid - Temp; end; end;

Figure 6: Fitting the picture in the available area


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

19

Cylindrical Anamorphosis (continuation 3)


When the conversion process is complete, the TImage area displays the anamorphic image and other UI elements display the original picture's file name, and its resolution on the cylinder (calculated in dots per inch, dpi). Using Scanlines The most complicated task has to be accomplished first, namely making a bitmap of the anamorphic image. I decided to use TBitmap's Scanline property, as I did in earlier articles (see Blaise Pascal Magazine #8). Remember that Bitmap.Scanline is a read-only array of pointers to a horizontal row of pixels. To manipulate the pixels in these rows we must copy the scanlines to an array where we can access the separate pixels. Each pixel needs a number of bytes, dependent upon the pixel format (or colour depth). In this instance I'm using a 24-bit pixel format, and therefore I've introduced the following types and variables:
type TPixCol = record //sequence is BGR in scanlines B : byte; G : byte; R : byte; end; TPixArray = array[0..6000] of TPixCol;
procedure TForm1.BtMakeImgClick(Sender: TObject); var I, J, NumbPix, NumbScl: integer; DegStep, VertStep : double; BitmapTmp: TBitmap; ScanlTmp: array of pPIxArray; begin if BitmapOrg.Empty then begin ShowMessage('First load a picture'); exit; end else begin ClearScreen; //makes Image1 white Checkformat; BitmapTmp := TBitmap.Create; BitmapTmp.PixelFormat := pf24bit; BitmapTmp.Height := BitmapImg.Height; BitmapTmp.Width := BitmapImg.Width; SetLength(ScanlTmp,BitmapTmp.Height); for I:= 0 to BitmapTmp.Height-1 do ScanlTmp[I] := BitmapTmp.ScanLine[I]; //copy scanline pointers NumbPix := BitmapOrg.Width; NumbScl := BitmapOrg.Height; SetLength(ScanlCyl,NumbScl); for I:= 0 to NumbScl-1 do ScanlCyl[I] := BitmapOrg.ScanLine[I]; //copy scanline pointers DegStep := (Pahi-Palo)/NumbPix; VertStep := (Pvhi-Pvlo)/NumbScl; Pv := Pvlo; try for J:=NumbScl-1 downto 0 do begin Pa := Palo; for I:=0 to NumbPix-1 do begin ConvCylToImg; //retrieve Ix, Iy ScanlTmp[Yim(Iy)][Xim(Ix)] := ScanlCyl[J][I]; Pa := Pa + DegStep; end; Pv := Pv + VertStep; end; except on Exception do begin Label6.Caption := 'error'; //further ignored end; end; end; Image1.Picture.Bitmap := BitmapTmp; with Image1.Canvas do begin Pen.Color := clBlack; {draw the Cylinder base} Ellipse(Xim(-R), Yim(R), Xim(R), Yim(-R)); TextOut(10,Image1.Height-20,'Cyl. diameter: ' + FloatToStr(2*R)); end; BitmapTmp.Free; Label6.Caption := 'resol. ' + IntToStr(round(2.54*BitmapOrg.Height/ (Pvhi-Pvlo))) + 'dpi'; end;

//a length of 6000 bits is sufficient


pPixArray = ^TPixArray; //scanlinepointers var ScanlCyl: array of pPixArray;

Now the following code snippet :


NumbScl := BitmapOrg.Height; SetLength(ScanlCyl,NumbScl); for I:=0 to NumbScl-1 do ScanlCyl[I] := BitmapOrg.ScanLine[I];

ensures that ScanlCyl[y][x].R points to the Red component of pixel number x in row number y of BitmapOrg. By the way, we don't need the colour component in this program, but it's there for free. Similarly, we must create pointers to access the pixels in the resulting anamorphic image. In the following procedure (called when the [Make Image] button is clicked) this is done by creating a temporary bitmap BitmapTmp. For each pixel in the original bitmap a new position is calculated in the temporary bitmap via procedure ConvCylToImg and the pointers of the temporary bitmap are assigned to the pointers of the original pixels. For high resolution pictures this can take several seconds. When all pixels have been converted, the temporary bitmap is copied to Image1's bitmap, and the result is instantly visible. In this listing the functions Xim and Yim translate the calculated positions of the pixels to the coordinates of Image1. The cylinder base circle is also drawn on the image together with the selected cylinder diameter (in cm). The resolution of the original picture on the cylinder is displayed on the user interface in dpi. If this resolution is too low (i.e. the number of pixels is insufficient to fully cover the image area) you see white interference lines in the image. If that happens you need to increase the resolution or size of the original in a photo editing program.

20

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Cylindrical Anamorphosis (continuation 4)


Conclusion I've not told you the identity of the image subject used in Figure 1. It's Lhamo, one of my Tibetan Mastiffs. See Figure 7. I must admit that for quite a long time I didn't know whether my program was functioning correctly because I couldn't find a shiny cylinder anywhere at home; but eventually one of my wife's lipsticks came to my rescue. So now you can surprise your guests by showing your homemade anamorphic pictures, provided you have a shiny cylinder (or lipstick). If you want to improve the program, feel free to do so and let me know. Maybe you can speed up the processing by using the SinCos function in the math unit (which works only with variables of type extended). I also didn't try whether the Canvas.Pixels property is faster or slower than the Scanline property. So download the source code and try it out! It's written in Delphi 7, and therefore usable by the many hobbyists among us. There's only one thing that annoys me: sometimes I get an Access violation in the try except loop. I couldn't discover the cause, but as the program appears to continue without problems, I just ignore the error. Any suggestions for a solution? See you next time.
peter @blaisepascal.eu

Figure 7: The image of Figure 1 revealed

Web Service Toolkit By Inoussa Ouedraogo


- a framework offering rich potential for Plug-in development
starter expert
Lazarus

The Web Service Toolkit [1] (WST for short) is a software package for FPC/Lazarus and Delphi developers which is designed to facilitate web services consumption (consumer side) and authoring (provider side). Its Web Service name reflects its design goal of offering a Pascal framework to integrate and simplify all aspects of providing internet-capable web services. However, it also has rich potential for defining both simple and complex types (through its built-in Type Library Editor) that can be used in non-web services settings such as the client-server communication required for developing application plug-ins. WST frees developers from struggling with the 'plumbing details' of message formats (SOAP, XML, RPC, JSON) and transport protocols (HTTP, TCP or as another possibility, a binary protocol specific to WST). Please refer to Chapter 11 of the book Lazarus, the Complete Guide[2] for extensive information about WST and Lazarus. The example presented here demonstrates how you can use a dynamic-library-based Application Server as a way of offering additional functionalities (plug-ins) to an application using the rich type of framework supported by WST.

A Library-based Application Server

Figure 1: Client and server modules in a client process WST web services can be hosted in a dynamic library either a Windows dynamic linked library (.dll) or a Unix shared object (.so). At runtime the library is loaded into the client process so that the client and server code are then running in the same process. Library-based application servers offer superior performance since the server and the client share the same address space and no remote protocol stack is needed. The library transport protocol uses the local process address space to share client request buffers and server response buffers. Every library-based application server exports a function wstHandleRequest located in the library_server_intf unit, that is used by the client library protocol implementation to invoke the server code.
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

21

Web Service Toolkit (continuation 1)


The following code shows the function signature. The ARequestBuffer parameter is the one used to share request and response buffers. Library-based application servers are well suited for plug-in development since the plug-in interface is defined in a high level language and the developer does not have to worry about low level library code. The example illustrated here is a program that lets the user choose a mathematical function to perform calculations on parameters the user has entered. The function list from which the user makes a choice is provided by the currently selected plug-in module. The plug-in list is built from the modules (dll/so) found in the program folder's modules sub-folder. This example contains : The main GUI program that is being extended with the plug-in modules. The program source code is located in the sources\web-services\ plugins folder. The application's only (main) form lets the user select a plug-in module. For each module a description is shown, along with a drop-down list of the functions available in that module. A module named defaultmodule. This module contains common mathematical functions such as exp, ln, and the inverse function (1/x). The module source code is located in the folder sources\web-services\ plugins\modules\default. A module named linearmodule. This module contains simple linear functions such as the Absolute function, the Identity function x, and the Double function 2x. The module source code is located in the sources\webservices\ plugins\modules\linear folder. The plug-in interface is specified by sources\web-services\ plugins\pluginservice.WSDL. The function that is used by the main application to invoke the plug-in calculation for a selected operation on given parameters. The Object Pascal translation of that file contains principally three classes together with the service interface as follows: TPluginDesc : This class describes a particular plug-in module implementation. It contains the module's caption, its description and the list of available functions. TFunctionDesc : A function description class which contains the function identifier (Name) and a human-readable caption. TFunctionDescList : This utility class is used by the plug-in description object class to specify the module's function list. IPluginService : The interface of the service provided by the plug-ins. This interface is the only way the main application can communicate with the plug-in implementation modules. The interface contains two methods : GetDescription : Every plug-in describes itself by returning a TPluginDesc instance filled with its identifying and informational items. ExecuteFunction : The function that is used by the main application to invoke the plug-in calculation for a selected operation on given parameters.

Figure 2: Plug-in Main Program


Plug-in example WST library-hosted services can be used as a framework offering rich potential for plug-in development. The main application defines its API interface through a WSDL (Web Services Description Language) schema that plug-in modules use to interact with the application. The main application (the consumer) consumes the services provided by the plug-in libraries (the service providers).
<Code unit=library_server_intf.pas> function wstHandleRequest(ARequestBuffer:IwstStream; AErrorBuffer : Pointer; var AErrorBufferLen : LongInt ):LongInt; </Code>

Figure 3: Architecture
22
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Web Service Toolkit (continuation 2)


<Code unit=pluginservice.pas> TPluginDesc = class(TBaseComplexRemotable) published property Identifier : string read FIdentifier write FIdentifier; property Description : string read FDescription write FDescription; property Caption : string read FCaption write FCaption; property Functions : TFunctionDescList read FFunctions write FFunctions; end; TFunctionDesc = class(TBaseComplexRemotable) published property Name : string read FName write FName; property Caption : string read FCaption write FCaption; end; TFunctionDescList = class(TObjectCollectionRemotable) public class function GetItemClass():TBaseRemotableClass;override; function Add(): TFunctionDesc; {$IFDEF USE_INLINE} inline;{$ENDIF} function AddAt(const APosition : Integer) : TFunctionDesc; property Item[AIndex:Integer] : TFunctionDesc Read GetItem;Default; end; IPluginService = interface(IInvokable) ['{26FEB177-55C3-4D59-A5C6-3CF7AD651E2A}'] function GetDescription():TPluginDesc; function ExecuteFunction( const AOperation: string; const AValue : Double ):Double; end; </Code>

When the application's main form is created, the LoadPlugins method builds the plug-in list (which is pointed to by the FPlugins field). This method iterates through the plug-in modules and for each module it creates the appropriate service and asks for its description using the function GetDescription. Note that the service's proxy is created using the proxy class and an unambiguous library file name. The actual calculation is invoked by the actCompute action's event handler actComputeExecute. Again, the proxy is manually created and the correct library must be specified. This example shows how easy and simple it is to transfer complex data structures via the plug-in API. In this regard there is no difference between an HTTP-based remote service and the library- based service. Notes : [1]
http://wiki.lazarus.freepascal.org/Web_Service _Toolkit,

SUMMER OFFER:

LAZARUS

the complete guide

Blaise Magazine is making a summer deal available to all our subscribers. If you purchase the newly published book (Lazarus the Complete Guide) we will include with it the following bonus items at no extra charge: - a Windows installer CD for Lazarus version 0.9.30 - a preinstalled copy Lazarus on a free 4GB USB stick. - 50 additional sample projects - Blaise Pascal Magazine Library 17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article - extra: additional preinstalled component suites

PAPER BACK 50,00 + postage HARDCOVER 60,00 + postage

WST can be checked out at


https://lazarus-ccr.svn.sourceforge.net/ svnroot/lazarus-ccr/wst/trunk

[2] Lazarus The Complete Guide, ISBN 978-94-90968-02-1, Blaise Pascal Magazine,
http://www.blaisepascal.eu/index.php?actie=./s ubscribers/lazarusbookinfoEnglish

COMPONENTS
DEVELOPERS

Pag 87

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

23

Polygon colouring and area calculation By David Dirkse


starter expert
DELPHI 3 and above (Win32)

Introduction This articles describes both how to colour a polygon and how to calculate its area. These two tasks are actually related since both require that you first divide the polygon into its constituent triangles. This triangulation is the hardest part of the task. We can distinguish three types of polygon:

Figure 2
In the case of two intersecting vectors (red and blue) there is a point S where they meet. Since S is on both the red and the blue vector, we may calculate the f values (f1 for red, f2 for blue) for S. f1 and f2 then give an indication of the relative positions of the vectors.

Figure1 1 is a convex polygon, where each internal angle is less than 180 degrees. 2 has an internal angle greater than 180 degrees. 3 has intersecting edges. In this application type 3 is illegal, and if encountered will cause the program to raise an error message. Types 1 and 2, however complex they might appear, can always be broken down into constituent triangles. These individual triangles can then be coloured, or their areas can be calculated and totalled. 'Outside' and 'inside' angles A polygon is made up of a sequence of points interconnected by lines. There is an area internal to the polygon, and an external area outside the polygon. When decomposing the polygon into triangles, the biggest problem is to classify an angle as being either 'outside' or 'inside'. In case 1 you can take any three consecutive points on the polygon's perimeter to construct a triangle, and that triangle will always be internal to the polygon. All the angles of this polygon are therefore 'inside' angles, giving rise to internal (inside) triangles. Triangulation may also be accomplished by drawing lines from one point to all the others. In the case of polygon 2 however, we can identify an angle formed by joining three consecutive points on which a triangle can be constructed which is external to the polygon. We must avoid colouring this triangle, which lies outside the polygon, and the angle giving rise to this external triangle will thus be termed an 'outside' angle. To differentiate between these angle types I use some vector-geometry mathematics. I will explain the general method here, and give its details further on in this article. A line (edge) has a starting and ending point. Such a line is called a vector, because more than one number is required to describe it (two numbers are required in 2D geometry). The figure below shows line l with vector AB. An arbitrary point on line l can be characterized by a single number, a factor which I call f. Point A, the starting point, corresponds to f = 0. Point B, the ending point, corresponds to f = 1. To the right of B (the forward extension of AB) are points that correspond to f > 1 To the left of A (the backward extension of AB) are points that correspond to f < 0. (You can refer to Appendix 1 at the end of the article for a fuller mathematical description.) 24
COMPONENTS
DEVELOPERS

Figure 3
The red and blue vectors may be parallel or may coincide. In that case there is no intersection. An fvalid flag is set to false if this is the case. Using this type of vector geometry we are able to distinguish between 'inside' and 'outside' angles in the case of simple polygons. Consider the following figure, and examine the angle at B.

Figure 4
In case (1) B is an internal angle of less than 180 degrees (an 'inside' angle) because the forward extension of AB does not intersect other polygon vectors. In case (2) the forward extension of AB intersects DE at point S, so B forms an internal angle greater than 180 degrees (an 'outside' angle). In case (1) we may colour triangle ABC. In case (2) with the 'outside' angle, we must not colour triangle ABC, since it is external to the polygon. However look at the following figure (5):

Figure 5
Diagram (1) shows polygon ABCD. At point B there is an internal angle of less than 180 degrees (apparently an 'inside' angle), but triangle ABC may not be coloured because there is a polygon vertex (D) inside triangle ABC. Diagram (2) shows how to recognize this situation. Construct vectors CA and BD and investigate if the forward extension of BD intersects CA.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Polygon colouring and area calculation(continuation 1)


Of course this must be repeated for all points (D) other than A, Vector start and end points may, or may not, lie on the extended B and C. For simple polygons like bars and arrows, this algorithm vector AB. In each case we compute a score, the crosscount (see suffices. the figure left bottom (8)) which indicates whether just one end, or whether both ends, of the crossing vector span the line, taking The complete algorithm direction into account as indicated, to give the sign of the Below is a more complex polygon for which the algorithm we crosscount. have worked out so far fails to distinguish 'outside' angles Now the simple rule for an 'inside' angle is: correctly. inside' angles have a crosscount sum of zero. Note: a parallel vector has a crosscount of zero, which adds nothing to the crosscount total. Please refer to the following illustration (of an F-shaped polygon tipped on its side) which shows crosscount values for AB.

Figure 6
The extended vector AB intersects FG at S1 (and HI at S2) so angle B (on the basis of the simple arrow-case algorithm above) is a forbidden 'outside' angle. However, B is less than 180 degrees and triangle ABC is internal to the polygon. So, the 'outside' angle test needs refining. We notice that the extension of AB intersects two other polygon vectors, not one. That is the important clue towards a correct solution. If there are an odd number of intersections then we have an 'outside' angle. If there are an even number of intersections then we have an 'inside' angle. But there still is a problem to be solved, which can be seen in the following figure:

Figure 9
The sum for all AB crosscounts is 1 -1 + 2 = 2. This is not equal to zero so B is not an 'inside' angle. All that remains is to design a method that will recognize a positive or negative crosscount. Clearly we should measure the angle between the vectors to do this. We shift the vectors parallel-wise until their starting points coincide. We notice
RIGHT...0 < angle < 180 degrees ...or -360 < angle < -180 degrees (relative to AB)

Figure 7
The extension of AB intersects two other vectors (CD and DE) exactly at their starting and ending point D. In case(1) B is an 'outside' angle, but in case (2) B is an 'intside' angle. In case (1) we should colour triangle ABC whereas in case (2) we should not colour ABC. What then should we do? I present the following solution, having evaluated all possibilities:

Figure 10
The angle between two vectors is the difference of their directions. Rather than using degrees (0..360) ,we calculate in radians (0..2*pi), where pi = 3.14, the ratio of a circle's circumference to its diameter. The directions are shown in the following diagram, in which clockwise movement is positive:

Figure 8
Position yourself at point A and look beyond B. Vectors intersect the extension of AB and they may cross to the right considered positive crossings (CD, EF, GH) or to the left considered negative crossings (IJ, KL, MN).

Figure 11
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

25

Polygon colouring and area calculation(continuation 2)


This completes discussion of the algorithm. Let's consider the implementation details. The following data structures are defined: // the maximum number of points allowed
const maxpolypoint = 100;

The trianglelist is the final table produced. Working from this array, individual triangles may be coloured or a triangle's area can be calculated. To limit the size of this article the code for triangle-colouring is available for subscriber download, but is not reproduced here in full. The area of a triangle, given the coordinates of the angles, is calculated using Heron's theorem:
area = sqrt(s*(s-a)*(s-b)*(s-c)).

Points are defined in a points array:


var points : array[1..maxpolypoint] of Tpoint; pcount : byte;

// the number of valid points in the points[ ]array

where a, b, c are the lengths of the sides, and s = 0.5*(a+b+c) // half the triangle perimeter.
a,b and c are simply calculated using Pythagoras' theorem. For a proof of Heron's theorem please look here: http://home.hccnet.nl/david.dirkse/math /geocalc/geocalc.html#area

The first and last points in the array must be the same so that the polygon is "closed". Using the points array, a vectorlist is generated.
type Tvector = record x,y : longInt; // the coordinates of starting point dx,dy : longInt; // the horizontal, vertical lengths dir : double; // the direction in radians end; var vectorlist : array[1..maxpolypoint] of TVector; vcount : byte; // the number of vectors

The structure of the polygon project All polygon type definitions, variables, functions and procedures are declared in the poly_unit unit. The routines provided include:
function polyArea(pts:pointer;n:byte):double;//area of polygon

From the vectorlist a triangle-list is generated.


type Ttriangle = record ax,ay,bx,by,cx,cy : longInt;// end;

pts points to points[1] and n is the number of valid points in points[ ].

(x,y) of A,B,C

This function returns the area and draws the polygon on the previously selected canvas.
procedure procedure procedure procedure setCanvas(cvs : Tcanvas); //select canvas setpencolor(pc : longInt); //pen color setBGcolor(bgc : longInt); //background setBG(BGmode : boolean); //BG true/false

var trianglelist : array[1..maxpolypoint] of Ttriangle; tcount : byte; //the number of entries in trianglelist

color

Coding a vector: (screen coordinates)

If BGmode is true the background is painted. If false, only the lines and points are painted.
procedure geoClear;

//debug purposes initializes all arrays.

//triangles on/off
procedure setShowTriangles(trMode : boolean);

If trMode = true then draw the triangles, which is used only for test purposes.
function polyResultMessage : string;

//message of code

Figure 12: The vectorlist becomes successively shorter


(by removing vectors) as the trianglelist is built.

The global polyResultCode variable measures processing success zero is OK, nonzero indicates an error. The PolyResultmessage function returns a string describing the polyResultCode. Global variables in poly_unit are as follows:
var polyresultcode : byte;

// polyResultcode = 0 if processing is error-free


polyTime : longInt;

// polyTime is the processing time in microseconds

Figure 13
In the figure above vector 1 gets replaced by the sum (AC) of vectors 1 and 2, and points A,B,C are entered in the trianglelist.

The Clock_unit unit contains procedures to measure execution times using the CPU's clock cycle. To design and test the algorithm a so-called exerciser is built using form1/unit1; This allows polygon generation and modification. The results are visualised.

26

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Polygon colouring and area calculation(continuation 2)


There are some low-level procedures which calculate the vectors. Most calculations are performed by vrsect, which calculates the f1 and f2 factors of the intersection of two vectors. (See Appendix 1 for the maths).
procedure vrsect(const v1,v2 : TVector); //calculate intersection of vectors v1,v2 //line1 = (v1.x1,v1.y1) +f1*(v1.dx,v1.dy) //line2 = (v2.x1,v2.y1) +f2*(v2.dx,v2.dy) //return f1,f2,fvalid var d,vx,vy : double; begin d := v1.dx*v2.dy - v1.dy*v2.dx;//discriminant if d = 0 then begin fvalid := false; exit; end; fvalid := true; vx := v2.x - v1.x; vy := v2.y - v1.y; f1 := (vx*v2.dy - vy*v2.dx)/d; f2 := (vx*v1.dy - vy*v1.dx)/d; if abs(f1) < if abs(f2) < if abs(f1-1) if abs(f2-1) end; frnd then f1 := 0; //round to 1e-6 frnd then f2 := 0; < frnd then f1 := 1; < frnd then f2 := 1;

Figure 14: The exerciser program The exerciser The exerciser operates in either draw or in modify mode. Draw mode: uses the mouse to draw lines. Undo or [Backspace] removes the last line. Modify mode: if you position the mouse pointer on an angle, you can click to move the point to a new position. Press [Shift] to increment the mouse pointer pixel by pixel. Otherwise it uses 10-pixel steps. The autoproc checkbox enables polygon drawing and recalculation after each modification. The fill checkbox paints a background inside the polygon. The show triangles checkbox draws each individual triangle within the polygon. Clicking the [area] button will calculate the polygon's area and draw it. The architecture of the exerciser is not discussed in this article. The poly_unit This unit only uses the clock_unit, to measure processing times. Therefore, you can simply add the poly_unit and the clock_unit to any Delphi project you choose to implement polygon area calculation. The function that calculates the area is given below:
function polyArea(pts:pointer; n:byte) : double; //calculate area, draw polygon var t1,t2 : Int64; begin //clock cycles since power on getCPUticks(t1); polyresultcode := 0; pp := Ppoints(pts); //pointer to points[1] pcount := n; //check closed, sufficient points checkpoints; if polyresultcode <> 0 then exit; buildvectorlist; //crossing edges, duplicate checkvectors; if polyresultcode <> 0 then exit; buildtrianglelist; if polyresultcode <> 0 then exit;

Other low-level procedures are:


function outward(vnr : word) : boolean;

//return true if vectorlist[vnr] points outward


function VDir(deltaX,deltaY : double) : double;

//return direction of vector in radians


function Empty3(i1,i2,i3 : word) : boolean;

//check for no point inside triangle i1,i2,i3 //no point : true

are sequential indices to the vectorlist[ ] For i3, only the vectorlist[i3] starting point is used. For more details please refer to the source code. There are various high-level procedures including:
i1,i2,i3 procedure checkpoints;

// tests for sufficient points and a closed polygon


procedure buildVectorlist;

// builds a vectorlist from array points[ ]


procedure checkvectors;

// tests for intersecting edges and duplicate points


procedure buildTriangleList;

// builds trianglelist from vectorlist


procedure TriangleSums;

// sums areas of all triangles

Note: the bitmap has 20 * 20 pixel squares. Areas are measured in squares (later versions). The following drawing procedures are included:
procedure geoTriColor(const tr:Ttriangle; brushcol: LongInt);

points

This draws the background of a triangle, using horizontal lines. Angles are first sorted by ascending y values.
procedure drawline(p1,p2 : Tpoint); // draws a line from point p1 to p2
procedure drawpoint(p : Tpoint);

//summarize area of triangles trianglesums; //later version: result = area / 400 result := area; getCPUticks(t2); polyTime := ProcTime(t2-t1); //draw polygon on selected canvas drawpoly; end;

// draws point p

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

27

Polygon colouring and area calculation(continuation 3)


Appendix 1

dx1 dx2 The notation of vector AB= dy1 BC= dy2 (see figure below) dx1+dx2 The sum of two vectors AB+BC = dy1+dy2

An introduction to the vector geometry used by this algorithm. In plane geometry, a vector is a line with length and direction.

( ) ( ) ( )

The intersection of two lines: x1 x dx1 line 1 y = y1 + f1 dy1 x2 x dx2 line 2 y = y2 + f2 dy2 To know the intersection point following set of equations must be solved:

()( ) ( ) ()( ) ( )

x1 + f1*dx1 = x2 + f2*dx2 y1 + f1*dy1 = y2 + f2*dy2

The only unknowns here are f1 and f2. As outlined earlier, these factors indicate the relative position of the vectors. Calculations are done by procedure vrsect( ) To solve the equations we multiply row 1 by dy2 and row 2 by dx2 :
dy2(x1 + f1*dx1) = x2.dy2 + f2*dx2.dy2 dx2(y1 + f1*dy1) = y2.dx2 + f2*dx2.dy2

Now subtract row 2. from row 1.

Figure 15: Multiplication of a vector (enlarge, reduce)


dx1 f d y1 =

)the vector is multiplied by f. ( ) ( f.dx1 f.dy1

Note : for negative f the vector changes direction.

dy2(x1+f1*dx1) - dx2(y1+f1*dy1) = x2.dy2 - y2.dx2 ...or: x1.dy2+f1*dx1.dy2 - y1.dx2 - f1*dx2.dy1 = x2.dy2 - y2.dx2 ...or: f1(dx1.dy2 - dx2.dy1) = (x2-x1)*dy2 - (y2-y1)*dx2

let
d = dx1.dy2 - dx2.dy1..and vx = x2 - x1 ...and vy = y2 - y1 ...then

f1=

vx.dy1-vy.dx1 vx.dy2-vy.dx2 and similar f2= d d

F or d = 0 the vectors coincide or are parallel. In this case flag fvalid = false indicating there is no
f .y d d1 y1

intersection point. Appendix 2

The direction of a vector:


Figure 16 : Before, vector AB is multiplied by 1.5 (yields A'B') and -0.5 (yields A''B'') The vector equation of a line (normal coordinates, not screen) x of vector d d y the arctangent function is used to get the direction. dy direction = arctan d x in radians. Two problems arise: 1. if dx = 0 then the direction is 0.5pi for dy >

() ()

and

1.5pi for dy < 0

division is not possible and would result in a floating point error. 2. the arctan function returns directions between -0.5pi (-90 degrees) and 0.5pi (+90 degrees) To trap all directions, sometimes a correction is necessary. if dx < 0 then increase direction by pi (180 degrees) if direction < 0 then increase by 2*pi (360 degrees) This concludes the description of this polygon project. And again we realise that nice applications can be made using only simple math. Figure 17 x1 x x dx1 A point y on the line has y = y1 + f1 dy 1
David Dirkse Born 1945 in Amsterdam, David joined Control Data Corporation in 1968, after studying electrical engineering. As a hardware engineer, he was responsible for the installation and maintenance of mainframes at scientific data centers in the Netherlands. With the decline of CDC around 1990, he studied mathematics and became a math teacher. His hobbies are programming, in particular educational software, math algorithms, and puzzle solving. http://home.hccnet.nl/david.dirkse

()

( )( ) ( )

Note: f=0 for point A, the starting point of the vector. f=1 for B, the end of the vector. For 0 < f < 1 a point is located between A and B. If f > 1 the point is situated on the forward extension of AB. If f < 0 then a point is on the backward extension (left of A in this case) of AB. 28
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating a Database program from scratch


starter expert
DELPHI XE and above (Win32)

by Detlef. D. Overbeek

1. What database? The database handling tool must be capable of creating the Firebird database. 2. Auto creation and visual design It should be able to automatically create tables and allow you to design them visually. 3. Reengineering We need reengineering since we already have many existing tables. 4. The ability to create and then visually alter the design We need a tool for SQL scripting that works in two directions: create and design eventually in visual alterations (the ones that show in your design overview). 5. Design updates change the underlying tables, and vice versa The first hurdle: the Database 6. Support printable overviews We already have an application in rudimentary form which Support printable overviews, at least A3 or bigger, works after a fashion. However, it is not adequate for our in colour. current and future needs. I think it's best to start from scratch, using only some ideas and 7. Create images in a variety of popular formats Be able to create images with several different details from our current application. So we have reusable parts and ideas, but we have not yet settled on a database. Since we are sorts of extensions. a user group, we need to use open source where possible to keep 8. Have export and import capabilities to the price down, which limits our choices. A further issue is other databases choice of the operating system on which the database server will 9. Have a data pump run. 10. Documentation Will it be Linux, Windows, Mac or Android? How can all our Last but not least: have very good quality users have easy access? Linux is cheap (almost free), but then you documentation. will need to use an extra helper language for creating a lot of internet-network functions in something like PHP. Since all the user group are familiar with Pascal, I suppose it would be best to And now the million dollar question: Can we as a poor user group get that free? Who might help us? keep as close as possible to Pascal. We are of course poor, but not short of fantasy. Why not ask So the next question is whether we use Delphi or Lazarus the manufacturers? And within no time we came up with the libraries? It doesn't really affect the choice of database, especially solution. There is a Dutch company that offers such a product, if we opt for the Interbase equivalent : Firebird. But... and as chauvinistic as I am - I love that. If we use some of the best component suites for database connection (from Components4Developers) as well as other So we gave it a try and they responded positively: we could have their tool at no cost. (I must admit that they're one of our advertisers: attractive offerings from TMS Software we should opt for Upscene). They have created a program called Database Delphi. (TMS surprises us time and again. At the recent Delphi Conference in Cologne (Kln), Germany they presented an astonishing new Workbench which meets all of our needs. So let's start testing product: Desktop - Touch Table. I was excited - like a little boy who had it just found the ultimate toy when I realised the potential impact it might have). Since the TMS Suite is not yet fully available for Lazarus (they are still working on it) I opted for the Delphi flavour of Pascal, where TMS provides a full set of reliable components, and To receive a printed copy of the magazine Firebird will be our database.
you need to go to our website to place your order

In this series of articles I will explain how we are addressing the magazine's need for a multi-functional customer service program. The functionality has to include registration and maintenance of subscriber accounts, ability to send and receive emails (respecting individual's privacy), administration of sales and promotion of a variety of products, and assistance in planning promotional campaigns. We need to have direct access to a web-served database, so any team member can gain access from their own location. Additionally we need the security of being able to work using our internal network (if the internet fails for a period) using direct server access. The server must make data backups automatically to enhance the security. We will be open about the choices we have to make, the tools potentially available to us, the initial thinking and discussion stages, the eventual plan and time scale, and how we implement it in code. This involves many interesting techniques. I think for some of our readers it may be a routine task to handle this type of challenge and to design software to meet such requirements. I would appreciate discussion about this, so please respond (to editor@blaisepascal.eu) with any helpful feedback. I estimate this will take at least a year of preparing, designing and coding.

The database connection components will be from Kim Madsen of Components4Developers (from now on C4D). A further - not very objective - reason for using Delphi would be exploring Delphi XE2's capabilities during our application development. Which program will we use for creating and handling the database? The next step is to decide on a database administration tool. What capabilities should it have? Gwan Tan, the owner of BetterOffice taught me a good lesson: Almost any tool at all can create projects of up to 100 tables. But if you might have over 100 tables you would be wise to choose ER Embarcadero's database tool. Our project is not big enough for that but we will start a series of articles about ER in October. First lets summarize the needs:

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

29

Creating a Database program from scratch (Continuation 1)

Figure 2 : The IDE opening screen

Figure 1: The Splash screen...

Testing Database Workbench We already have many Interbase tables and it would be very interesting to see the quality of Upscene's localized SQL (since Interbase is no longer identical to Firebird). It works most of the time. But Interbase has different SQL abilities compared to Firebird. So the tool needs to understand both. We have to design, create and connect about 100 tables, and we must be sure... The next image shows an overview of Database Workbench version 4's IDE. To research the Firebird support and the SQL dialect it uses I went straight to the help file, which can be seen in Figure 3:

Figure 3: Firebird's local SQL dialect


30
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating a Database program from scratch (Continuation 2)


To make sure that we have all the options available: The database visual designer supports bi-directional updating. If you change anything in the design it will be visible there, and it will also be reflected in updates directly in the table (see Figure 4).

Figure 4: The Database Workbench table editor

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

31

Creating a Database program from scratch (Continuation 3)


Very good, I like that. I must say I am always very curious about the user-friendliness of a new application's functions when I first use it. What I term my gut feeling, often based on whether I intuitively know where to find a feature, acting as if I already knew it were there. I was not disappointed. Within 30 minutes I found all the main items without even using the help once. This was a very gratifying experience. It is one of those applications where you can feel at home very quickly.

Figure 5: The IDE and the menu where you can process reverse engineering
The next thing to go for is the auto creation of the diagram. I know all database tools do this. But not all have a bi-directional updating ability. Through reengineering, this all works well in Database Workbench. And of course since I'm interested in the ability to create very good presentations (you need that if you want to sell your database project, but it also demonstrates the functionality and coherence of your database) it comes in very handy that you can use colour with the ability to edit your table design text and annotations as if they were a RichEditMemo. Great! As listed in the requirements above we can print in various colours, sizes etc. But what makes it feel like home is the little Exit icon. This tells you the Workbench was written in Delphi! (See Figure 9)

Figure 6: A part of the table and permitted alterations

Figure 7: You can create and edit annotations.

Figure 9: A sure sign that it was built in Delphi: the exit icon...
32
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating a Database program from scratch (Continuation 1)


Returning to the list of requirements: #7 Create images in various formats. We find bitmap, gif, jpeg, jpg, png, but alas no vector graphics. And then requirement #8, import and export capabilities: I would have liked the possiblility to import native Excel files, so I would not have to convert data into CSV files. Of course there should be better import capablilities... One always wants to dream of more...

Figure 10: The Draw menu Figure 12: Export capabilities

Figure 11: Import capabilities The requirement #9 to have a data pump is provided for. I haven't tested it yet. But I am quite confident after all I saw.

Figure 13: The Data Pump...Who ever thought of that name?


And then #10, good documentation. I always like to have a written file. Not only as a help file. With some ingenuity I tried to create a PDF file through printing to PDF which could be done per chapter. That is too difficult. We need to have a PDF help file, which I will request. It's not that much work to create one... In the next issue we will start creating the database, create the application schema and how it should be designed. That will be at the end of October. Any questions, you know how to find me... editor@blaisepascal.eu

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

33

An Android client for a DataSnap Server Part 2 By Daniele Teti


starter expert

Specifically, the graphical controls are called widgets and are defined in the package android.widget. Some of these In this second article we'll create an Android client widgets (buttons, edits and so on) will be used on the app that application that talks to the previously built DataSnap we'll create. server. Basic knowledge of Java syntax is assumed in One difference for a Delphi developer is the event handling what follows. mechanism is different from that to which a Delphi developer is used to, but it is very similar to that of the graphics libraries Introducing Android development available on Java SE, so a Java developer should not have As Google says Android is a software stack for mobile devices that includes an problems in understanding the mechanism. operating system, middleware and key applications. The Android The application we are going to build will allow us to SDK provides the tools and APIs necessary to begin developing understand how the listener mechanism works, and how to parse a string in JSON format. applications on the Android platform using the Java I have not structured this article as a step-by-step tutorial. programming language. (http://developer.android.com/ guide/basics/what-is-android.html) Rather I will focus on the specific issues involved while A lot has been written on the web about the pros and cons of interfacing Android with DataSnap. If you need a step-by-step Android, so we will not repeat that here. Android is a great tutorial on Android development, you can browse the amazing operating system and development platform. To get an Google documentation, or a white paper that I wrote for introduction to Android and the motivations that led the way for Embarcadero some months ago. You can download the Google to undertake the development of Android, you can whitepaper for free at http://www.embarcadero.com/rad-inconsult Wikipedia at the following URL action/php-android. The white paper talks about a PHP http://en.wikipedia.org/wiki/ REST service and Android. However the Android part is almost Android_%28operating_system%29. the same. This is the code for the main activity. Before starting to develop for Android, you must set up the development environment. Although it is not mandatory to use public class MainActivity extends Activity a specific development environment, Google has written a very implements OnItemClickListener { private ListView lv; useful plug-in for Eclipse called ADT (Android Development private List<ToDo> todo_list; Tools), and later in this article we will use Eclipse with ADT. private final int MENU_REFRESH = 1001; private final int MENU_CONFIG = 1002; Google provides an excellent guide on how to configure private final int MENU_ABOUT = 1003; everything you need to develop on Android. In Throughout this article I'll assume that your development environment is /** Called when the activity is first created. */ configured as recommended in this guide
(http://developer.android.com/sdk/installing.html)

DELPHI 2007 and above

and that you have successfully tried the HelloWorld tutorial


(http://developer.android.com/resources/tutorials/he llo-world.html). I am referring to various key components

of Android's architecture. To understand what I'm talking about, and avoiding a lengthy and boring copy-paste, I suggest you refer to Application Components. You can Help that you find more on this topic here:
http://developer.android.com/guide/topics/fundamenta ls.html#appcomp

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); setTitle(R.string.app_title); lv = (ListView) findViewById(R.id.lv_todo); lv.setOnItemClickListener(this); }

Layout The commonest way to define the appearance of an activity is using an XML layout file. The structure of the XML used by Android for layout it is a reminiscent of an HTML web page. Each element within the XML can be either a View or a ViewGroup (or one of their descendants). The name of the XML elements of the layout file correspond to the JAVA class that represents them. So a <Button/> element creates an instance of the Button within the GUI and a <LinearLayout/> element creates an instance of the LinearLayout ViewGroup. When loading resources, Android initialises the objects defined in the XML file layout, creating concrete functional objects. So you can imagine the layout xml file as a text representation of a complex object tree. For a Delphi or Lazarus programmer, what comes the closest in concept to an Android layout file is the DFM or the LFM file. View and Event Handling The activities are your application's screens. An activity is the Android counterpart to the Delphi/Lazarus Form. So, whatever the user can see, is necessarily contained by an Activity. Every visible element within Android is called a View and is drawn on an activity. 34
COMPONENTS
DEVELOPERS

@Override protected void onResume() { super.onResume(); refresh_data(); } private void refresh_data() { todo_list = new RESTProxy(this).getToDos(); lv.setAdapter(new ToDoListAdapter (this, todo_list)); } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent i = new Intent(this, EditToDoActivity.class);

// the selected item


ToDo todo = todo_list.get(position);

// fill the intent extra data. //Will be readed by the EditToDoActivity.


i.putExtra("id", todo.getId()); i.putExtra("completed", todo.getCompleted()); i.putExtra("description", todo.getDescription()); // start the other activity startActivity(i); } public void newTodoClick(View btn) { Intent i = new Intent(this, EditToDoActivity.class); // start the other activity startActivity(i); }

//Menu management code. Remove for brevity

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

An Android client for a DataSnap Server Part 2 (continuation 1)


The proxy uses the same approach. Running an application with this code on the emulator (or a device) produces the following error message at the call to the (http://developer.android.com/reference/androi execHttpRequest method: d/app/Activity.html#ActivityLifecycle), Permission denied (maybe missing INTERNET the onResume() method is called just before the activity runs, permission) or returns to running. So, in that method I've put the actual data This is because, in Android, every application that uses a loading from the server. This application is a demo. network connection, must inform the operating system. In a real world application you would put all the remote To inform Android that our application needs access to the invocation, or more generally, all the time-consuming operations internet, open the file AndroidManifest.xml and add just before in a secondary thread. the closing tag </manifest>, the following line The Android SDK provides strong multithreading support. If <uses-permission you need further information about threads in Android, you android:name="android.permission.INTERNET should read these articles: "></uses-permission> http://developer.android.com/guide/topics/fund Even in this case you can use the visual editor if you prefer. Our amentals/processes-and-threads.html, todo application requires this permission.
http://developer.android.com/reference/java/la ng/Thread.html, http://developer.android.com/reference/android /os/AsyncTask.html).

In the onCreate() method (called when the activity is first created) I do some initialization related to the activity. As the (famous) Activity LifeCycle says

The To Do List Proxy

A ToDo list is simply a list of things to do, without a specific date on which they must be done. We are using as The DataSnap proxy the server the service that we built in the previous The most interesting part of the application is the proxy. In this article. The interface is minimal but effective. The main application the DataSnap proxy is written from scratch (although activity just shows a list of all the 'todo' items, and in Delphi XE2 there is very cool automatic proxy generation for Android whether they have been completed or not yet completed. and other mobile platforms). However, if you have Delphi XE and Clicking on a TODO item, using an Intent, will open a you need to write an Android client, you will have to write the proxy by hand. The proxy maps each REST method to a normal new activity that will then allow us to edit it. In addition, you can also insert a new 'todo' items directly from the object method that uses a connection to a webserver. main activity using the "New ToDo" button. So the URL
/datasnap/rest/TSrvMethodsTODO/Todos is mapped to the a proxy instance method getTodos() and so on. Accessing the web server It is simple to send a request to a web server from an Android application. For these this purposes, Android provides the wellknown Java HttpClient library from the Apache Software Foundation . Precisely for this reason, the documentation on this is really great and well done. To understand how HttpClient works, it is useful to use the project's official documentation which you can find here:
http://hc.apache.org/httpcomponents-clientga/tutorial/html/index.html

As an example, here's the code to send an http request for a URL and gets its text representation in a String.
private String execHttpRequest(String url) { try { //Create default http client HttpClient httpclient = new DefaultHttpClient();

//We want send a GET request, so Create an HttpGet object


HttpGet httpget = new HttpGet(url);

Figure 1 : The main activity Studying the code, you will learn how to connect a ToDo list retrieved from the server to a ListView, how to open a secondary activity, how to create ing menus for the activity, how to manage user preferences, and how to use Toast. It is not the aim of this article to be an Android tutorial, but just understanding these simple mechanisms you will be able to develop a simple Android application that does something really functional. Let's having take a look at the most significant proxy methods, starting with in the method that returns the complete ToDo list:

//Execute GET request over the httpclient


HttpResponse response =httpclient.execute(httpget);

//Retrieve the response body or "entity"


HttpEntity entity = response.getEntity();

//Return the entity as String


return EntityUtils.toString(entity); } catch (Exception e) {

//If something goes wrong, print the stack trace //and return the Exception message
e.printStackTrace(); return e.getMessage(); } }

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

35

An Android client for a DataSnap Server Part 2 (continuation 2)

Daniele Teti
Figure 2: The code to get the ToDo list and to convert the JSON array ToDo items into an ArrayList As can be seen in Figure 2, The ToDo list is returned from the service as an array of JSON objects. I have written a method that takes care of converting the JSON representation of a ToDo into a real List<ToDo>. At this point the code shown should be clear. Creating and editing a ToDo item it is just a little bit more complex, because we have to handle the body of the request.

Figure 3: The create and update methods


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

37

An Android client for a DataSnap Server Part 2 (continuation 3)


Figure 3 shows more on the PUT and POST. In this case, in addition to sending the request with the appropriate method (PUT to create and POST to update) we also need to send the body of the request in the expected format. It is necessary to represent the JAVA object in JSON, and In this case the proxy method is responsible for the JSON creation. In a real world application it is correct to ensure that every object can serialize its internal state independently via Reflection, or by implementing an interface such as the following:
public interface JSONSerializable { public String toJSONString() throws JSONException; public void fromJSONString(String json) throws JSONException; }

Please notice at line 56 of Figure 3 the URL is built, to identify which ToDo item to edit, using parameters. The same criterion is used by the deleteToDo method. When you have to work on a single ToDo, another activity is launched. The code to start another activity, with or without extra Intent data, is shown in the Figure 4:

Figure 4: How to start a secondary activity. The secondary activity displays the ToDo data.

Summary In this quick introduction to the Android world, we have only scratched the surface of the subject. We but have nevertheless been introduced to several key concepts that allow readers to start developing DataSnap REST services in Delphi. You can also take full advantage of mobile device features in allowing an Android application to manage remote data. You can find the complete application in the sample folder. If you would like to learn more about it, I would recommend you read a good text on Android, and above all, I recommend that you study the samples in the SDK provided by Google. It They can be found under the directory samples \<your-platformnumber> of the SDK .

Figure 5. 38
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Anti-freeze for VCL applications By Alexander Alexeev


starter expert
DELPHI 3 and above (Win32)

Many of you who read this will have developed applications which perform time-consuming work. Tasks such as searching through files, loading huge data files, performing complex computations, accessing slow networks and so on. When your application executes such tasks and it starts thinking it doesn't look good to the user:

The wrong solution So what can you do? The most frequently encountered advice to deal with this problem is to interrupt your work from time to time to give responses to Windows' requests. This process is known as message pumping or message processing. For example, if you're copying a file then you are advised to abandon trying to accomplish this all at once, and rather to split the copying process into several steps in which the file gets copied in smaller, same-sized blocks. In between copying each file block you can do the Windows message processing. Incidentally, you carry out message pumping by calling the ProcessMessages method of the global Application object. In other words, you should divide your work into relatively small pieces, and mix the work and message processing in order to keep the user interface (UI) responsive. However, this solution has number of serious problems, which makes it far from optimal: This is not always a possible solution. If you've called an external function (such as copy file) then you can't interrupt it from time to time to perform message pumping. Heavy work may be performed by a DLL, which has no idea whether it's a console application which called it or whether it's a GUI application (which needs to pump messages). If you call Application.ProcessMessages, when there are no pending messages to process you're wasting time. If you call Application.ProcessMessages too often (unnecessarily) you'll slow down your program, since you'll frequently spend time doing nothing. In the worst case you may put more effort into keeping the UI responsive than in doing actual work! If you call Application.ProcessMessages too rarely then your application won't work smoothly. It will appear jagged. Note that you can't reliably pick the optimum frequency of calls to Application.ProcessMessages. Say you're copying a file from one hard disk to another. You might set the call frequency to one call per megabyte of file data copied. However, if the user runs your application and copies the file over a network your estimates will fail. For a slow network it's more appropriate to set the call frequency, say, to one call every for every 10 Kb of copied data. However, if you implement this slower estimate in your application, then you'll waste time when copying files between local hard disks. Calling too often is bad, as is calling too rarely. And what's worse it changes while you're running. What's just as bad is that you can't really assess how well your estimate fits until your users try the application out in their various situations! Inappropriate calling of Application.ProcessMessages may introduce recursive cycles and re-entrancy problems (for example, a user may click a button more than once while your application is still busy with that button's task which will lead to running a second task inside the first one. That's probably not what you're wanting!). Personally, I believe that correctly coded applications don't need to call Application.ProcessMessages in the first place. Anyway, it's good to have another, better solution. And, indeed, there is one.

There are several undesirable effects: Your Program (Not responding) appears in the program window's caption. The window doesn't respond (or responds poorly) to any user action such as window minimizing and dragging, or clicking on a control. The mouse cursor turns into an hourglass, when moving over your application's windows. Your window's client area does not get refreshed. The client area is drawn as shaded (in Vista and above). The client area is drawn as filled by a background colour (typically - white) in XP and earlier. A system dialog may appear after a timeout, informing you about the 'hung' application.

All these things underline to the user that your application has hung even if this might be not true. Your application may just be busy with processor-intensive work and will revert to full responsiveness after some delay. A specific case of this situation is the question (often asked by newbies): when you do time-consuming work, how can you implement an immediately effective Cancel button? The window is not responding so how can a user click on the button? OK, let's start with the basic question Why do these things happen? It is because Windows doesn't know what you want to show in your windows. It doesn't know how you want to react to a button-click. That's why Windows tells you Please, redraw your windows, and The user just clicked on your button please, do something. Typically, this conversation goes on under the hood of the VCL library and you deal only with the high level end result (for example, a Button1Click OnClick event handler). If your application is busily doing some work it can't answer these Windows requests, which leads to the appearance of a hung application. Threads? This happens because code running in a single thread can only do one thing at a time. Either it is working or it is responding The thing is that processes (applications) don't run any code at all. They are just containers for threads. to Windows' requests, but not both simultaneously. COMPONENTS SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 39
DEVELOPERS

Anti-freeze for VCL applications (continuation 1)


A thread is a sequence of code running in your application. When the application starts Windows creates one thread for it, the so-called primary (or main) thread, which executes your application's code. Threads are always created in the context of a particular process, they cannot stand alone. Their entire life is spent inside the borders of a process. That's why two or more threads running inside same process can run the same code, use the same data, and share common data and objects inside that process. Most applications have only one (the primary or main) thread. However, you can create more threads on demand, as needed. Creating additional threads allows you to perform more work at the same time. Additional threads are called secondary, worker, additional, helper or background threads. As you will have guessed by now, the correct solution to unfreezing an unresponsive UI lies in the use of additional worker threads, which will extract some work from main thread, and execute it simultaneously. One of the threads will perform constant message pumping, thus keeping the UI alive (refreshing the UI, reacting to the user's input, etc.). Another thread will do the actual work. That way your GUI application can run smoothly whilst doing its work.If you've already been involved in developing multi-threaded applications, then you will know that controlling threads and syncing access to shared, common data are particularly complex tasks. There are a large number of tools available to help deal with these tasks (critical sections, events, semaphores, mutexes, and so on). Even though they all are quite simple by themselves, they can quickly become a real headache when you develop a proper synchronization model for your application. This is an especially hard task for newcomers, who simply want a smoothly responsive UI. In real-world applications developers try to reduce coding complexity, minimizing the amount of synchronization needed, treating it as unavoidable evil only when they can't see a way to avoid it. The simplest synchronization is a sync which you don't need to do. In other words it's the lovely situation where threads do not share any data at all. In Delphi there is another overriding issue, namely that the VCL is single-threaded. This means that you can't access any VCL object (Form1, Label1, Button1, etc.) from another thread. UI work can be performed only by main thread and not by any other helper thread. This also implies that major non-VCL tasks should be offloaded to secondary worker threads, and all UI work should be running in the main thread, and not vice versa. That's why the main thread is often called a UI thread. Also, this means that you can't access, say, Edit1 from a secondary thread to discover the file name to process. And you can't access ProgressBar1 to update the status of the file's processing because both things mean accessing the VCL, which you can't safely do from another thread. All this leads us to unavoidable thread synchronization, which we were trying to avoid! Because of all these complex issues, many developers just avoid threading. They are too lazy to implement a really smoothly working application. Instead they just scatter calls to Application.ProcessMessages all over their code, and sometimes they don't even bother to do that (this is particularly true if the operation in question is performed quickly in 90% of cases, and there are only 10% of cases where the operation runs slowly producing the corner cases where the application hangs). So, what can you do? Should this simple wish to be so hard to implement? 40
COMPONENTS
DEVELOPERS

The multi-threaded solution Well, as it turns out, there is a really good solution to this conundrum. I've written a TasksEx unit (you can download it from the registered subscribers area of the Blaise Pascal Magazine website) which you can use to easily solve this problem, even if you have no knowledge of threads or synchronisation primitives. The TasksEx unit uses a bit of a magic (and by magic I mean the work of Andreas Hausladen the AsyncCalls unit: http://andy.jgknet.de/blog/?page_id=100 ). To use the TasksEx unit you just need to follow some simple rules. The unit offers you only two principal procedures:
procedure EnterWorkerThread; procedure LeaveWorkerThread;

Any code, which is put between calls to EnterWorkerThread and LeaveWorkerThread, will be performed as if it were put into a secondary thread. For example: // This code runs in the main thread (for example, a Button1Click)
EnterWorkerThread; try

// This code runs in a secondary thread. // Even if it's written in Button1Click, it'll still be executed // by a worker thread, as if it were written in TThread.Execute.
finally LeaveWorkerThread; end;

// This code runs in main thread You can use any global or local(!) variables in code between and LeaveWorkerThread this includes the routine's parameters, as well as the parent routine's parameters. Stated simply: you have access to all the data which you normally have access to without EnterWorkerThread and LeaveWorkerThread. And you can write your code as if there were just one thread in your application (well, almost you still need to follow a few rules see below). The typical multi-threaded approach involves creating and using a thread manager or thread pool, implementing a queue of tasks for background threads, synchronizing access to common data, and much else. The advantage of this method is that you don't need to do anything at all! To be sure, this can't be used for seriously complex multi-threaded applications, but EnterWorkerThread and LeaveWorkerThread have simpler aims. Most often you have a situation in which you just need to do something without freezing the UI (preferably also without having to put too much thought into how it will work under the hood). There are lots of examples out there that demonstrate the need for this. One application hangs when doing file searches, because its author didn't anticipate that there would ever be so many plugins for his application to search. Another application hangs during file loading, because the author despaired of doing this smoothly because the task was too complex. And that's where the TasksEx unit comes in. It's designed to solve this one programming problem. To make your application run smoothly you need to detect potentially time-consuming areas of code, and wrap them with calls to Enter/LeaveWorkerThread. That's all. There are no thread priorities to set, no Terminate, no Queue, no syncing. The unit is designed for transparent programming. You can write code without thinking about threads at all.
EnterWorkerThread

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Anti-freeze for VCL applications (continuation 2)


You just need to separate your code in two areas which don't overlap: a) Time-consuming code b) Code which works with the UI (and accesses the VCL). Then you wrap both areas in above mentioned calls and you get your really smooth application. That's all! One possible scenario for using this unit is when modifying existing code which was written without any thought of multi-threading. For example, you have a large routine or method already written. Later you find that you need to run part of it in a background worker thread. Instead of rewriting the code, separating different parts to run in different threads, preparing data to pass to a secondary thread, passing results back, establishing error handling and proper synchronization, and so on you can just put EnterWorkerThread/LeaveWorkerThread around the problematic area. In short this unit is a very convenient alternative to other multi-threaded approaches for cases where you don't want to put much work into applying beneficial threading to existing non-threaded code. Usage examples Here are several examples which show how the TasksEx unit can be used. Firstly, a simple example: file searching. Let's assume we have a form with an Edit1, a Memo1, a Button1 and the following code:
procedure TForm1.Button1Click(Sender: TObject); procedure EnumFiles(const AFolder: String; AFiles: TStrings); var SR: TSearchRec; S: String; begin if FindFirst(AFolder + '*.*', faAnyFile, SR) = 0 then try repeat if (SR.Name <> '.') and (SR.Name <> '..') then begin S := AFolder + SR.Name; if DirectoryExists(S) then EnumFiles(S + '\', AFiles) else AFiles.Add(S); end; until FindNext(SR) <> 0; finally FindClose(SR); end; end; begin Memo1.Lines.BeginUpdate; try EnumFiles(IncludeTrailingPathDelimiter(Edit1.Text), Memo1.Lines); finally Memo1.Lines.EndUpdate; end; end; uses TasksEx; procedure TForm1.Button1Click(Sender: TObject); procedure EnumFiles(const AFolder: String; AFiles: TStrings); begin // ... no changes ... end; var Str: TStringList; Folder: String;

begin Button1.Enabled := False; try//1 Str := TStringList.Create; try//2 Folder := IncludeTrailingPathDelimiter(Edit1.Text); EnterWorkerThread; try;//3 EnumFiles(Folder, Str); finally;//3 LeaveWorkerThread; end;//3 Memo1.Lines.BeginUpdate; try//4 Memo1.Lines.Assign(Str); finally//4 Memo1.Lines.EndUpdate; end;//4 finally //2 FreeAndNil(Str); end;//2 finally //1 Button1.Enabled := True; end;//1 end;

Okay, so the main searching code was left unchanged, but we've wrapped it between calls to EnterWorkerThread and LeaveWorkerThread. Run the application now, and notice how beautifully it behaves itself: the application doesn't hang, it allows you to drag it around, minimize, or restore it. It even accepts clicks on controls inside its window! (That's why we've locked Button1 for the search duration to prevent the user running the search again, while first search is still running). All this happens because code between EnterWorkerThread and LeaveWorkerThread (i.e. the call to EnumFiles) is now running in a separate background thread, so main thread is free to do message pumping and UI processing. Rules Even though this was a very simple example, it illustrates several important rules: 1. Calling EnterWorkerThread/LeaveWorkerThread must always to be like this:
EnterWorkerThread; try // do work finally LeaveWorkerThread; end;

Incidentally, notice how we're avoiding the magic button antipattern discussed in the previous issue of Blaise Pascal Magazine. The EnumFiles routine is fully isolated and can easily be moved somewhere else to allow external calls or even simultaneous multi-threading. Run this code, enter any folder with a huge number of files into Edit1 (say, C:\) and click on Button1. You'll see that the application hangs for some minutes, while it searches for files. Experiment by minimizing and restoring the application, dragging it over the desktop, or clicking on controls inside the form. You'll see how the application fails to react properly. Obviously, the reason is that EnumFiles takes far too long to complete. During the file searching no other work (such as UI repainting, or processing of user input) can be performed. Let's see how we can fix this:

Pay great attention to this template. It's very important. You must use the try/finally pattern. You must not insert any code in the finally block or before try, (apart from the calls to EnterWorkerThread and LeaveWorkerThread). It's quite easy to understand why: because these functions perform thread switching. If there is an exception in the secondary thread and there is no try/finally, then reversing the thread switch will be missed.
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

41

Anti-freeze for VCL applications (continuation 3)


Imagine the chaos which will result from that! If you're used to exception programming, then there is nothing new for you in this construct, and you'll set it up automatically. If you're not used to writing exception-catching code blocks, then you'd better learn quickly, otherwise 2 Since your application now has more than one thread (i.e. your application is now capable of doing two things at the same time), then you must design its UI to be resistant to re-entrance issues. That's why we've inserted the code which changes Button1's Enabled property. Remove these two lines and run the application again. Notice that you can click on Button1 twice, and the second click might occur while the first search is still running. This will really spawn multiple background threads, each simultaneously executing a file search. Probably this is not what you want. 3. The background thread is another thread. If you're using any thread-affinity objects or states then you must explicitly transfer them to the secondary thread. For example, any variable values of the main thread's threadvar block will be inaccessible in the secondary thread (and vice versa). In order to use them between threads you must pass them explicitly as parameters or variables. Another thread-affinity issue that may arise is with COM initialization. You must initialize COM in the secondary thread before using it. It's often done automatically for the main thread by Delphi itself. But this does not happen automatically for any additional threads, so you must do this manually as follows: 5. You must not access the VCL from any secondary thread. In our first example we passed Memo1.Lines to EnumFiles, so the AFiles.Add(S) line was adding a string directly to Memo1 therefore working with the VCL(since a memo is a VCL control). Because this is not allowed we must get rid of this code.

That's the most complex part of using secondary threads. Because Delphi's compiler will not warn you about attempts to use visual controls from another thread. What's worse if you miss such code overlaps, your application will work most of the time, but sometimes it will crash. So, if you have random hangs, crashes or access violations check your code for access to the VCL from a non-primary thread. So, what's the best approach to avoid these pitfalls? Well, there are many possibilities. Here are a few short examples As shown in second code example above move any VCL interaction out of the secondary thread to come either before or after it. In our example we replaced working with the lines of Memo1 with working with a TStringList instance instead. TStringList is a non-visual class. It's not part of the VCL, so we can easily use this class and share it across secondary threads. After our work is complete we simply copy the TStringList results back to Memo1. The common idea here is to collect data from your form into variables, work with those variables in the secondary thread (calculating results and so on), and finally publish the results in the form. Send a message to the main thread. The background thread can send a window message to the main window, asking it to EnterWorkerThread; perform VCL work (instead of the secondary thread itself try performing the VCL work). You can send messages both sync CoInitialize; and async (of which async has the least performance penalty). try // your-code This is a very good and flexible way to work. Unfortunately, finally there are too many potential issues to fully describe all its aspects CoUninitialize; in this article. If you have worked with window messages before end; finally you shouldn't have any problems with this approach. Since all LeaveWorkerThread; messaging work is done inside a single process, no inter-process end; communication (IPC) is necessary. You can see an example of this approach in the code examples accompanying this article The same holds true for similar mechanisms. (available at the Blaise magazine website). 4. This rule is similar to previous one: not all external Temporarily switch to the main thread. (For the benefit of readers functions allow simultaneous calls from several threads. who are familiar with Synchronize and TThread, this is analogous to calling Some external functions may not allow calls from a nonSynchronize in TThread). You can do this as follows: primary thread at all. The VCL is the best known example, procedure TForm1.Button1Click(Sender: TObject); but we're talking about all sorts of external functions here. procedure EnumFiles(const AFolder: String; For example, GDI objects typically have no thread-affinity, AFiles: TStrings); but you must guarantee single thread access at one time. // ... no changes ... Windows objects have thread-affinity: only the thread which begin // ... no changes ... created a window owns it. A further example would be calls if DirectoryExists(S) you make to loadLibrary and FreeLibrary. then EnumFiles(S+'\',AFiles)else begin EnterMainThread; Even though they don't formally have thread-limitations, the try AFiles.Add(S); DLL they load and free may have implicit expectations about finally LeaveMainThread; the loading thread being the application's primary thread. end; In such cases you must protect calls to these functions with end; critical sections (though you can ignore this if you're sure that you // ... no changes ... won't create more than one worker thread). For functions which end; don't like calls from other threads at all, you will need to begin Memo1.Lines.BeginUpdate; serialize calls to the main thread. You can either make these Button1.Enabled := False; calls before (or after) spawning the secondary thread, or you try EnterWorkerThread; can use the technique described in the next item (Rule 5). try EnumFiles(IncludeTrailingPathDelimiter A function's description does not always mention the (Edit1.Text), Memo1.Lines); finally LeaveWorkerThread; requirements for multi-threaded calls, which makes life end; difficult for inexperienced programmers. So sometimes you finally will need to do some study and research before writing code. Button1.Enabled := True; Of course, you can risk trial-and-error programming, but I Memo1.Lines.EndUpdate; strongly recommend against doing this. end;
end;

42

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Anti-freeze for VCL applications (continuation 4)


This is quite an involved construct for such a simple example, but it can come in handy for more complex cases. As you can see, a pair of EnterMainThread and LeaveMainThread calls must be enclosed in a try finally block, in just the same way as when using EnterWorkerThread and LeaveWorkerThread. Personally I don't recommend this approach. This is because it's easy to make a mistake here, it has less than optimal performance and, more important, it is not always possible, since you can't insert EnterMainThread / LeaveMainThread calls in external code. As you can see, the above examples do not all exhibit the same behaviour. For example, Memo1.Lines.Assign in the first approach may take a lot of time (if there is a vast number of files) and thus still give the appearance of a hung application after all our work. The solution? Don't pull large amounts of data into the UI in one go do it partially (as is done in second and third approaches). Better still use a virtual view. Implementing a Cancel button When your application reaches the Put the work into a secondary thread! level your first natural desire (and need) will be to implement a Cancel button. Clicking on the Cancel button lets a user stop and cancel the current processing. How can we implement it? Very easily! You don't need to write much code. Take any of three examples above (a: Pulling VCL code out of the secondary thread, b: Using messages, c: Serializing access), and add a Stop button to the form (set its Enabled property to False at design-time) and make the following changes to your code:
procedure TForm1.Button1Click(Sender: TObject); procedure EnumFiles(const AFolder: String; const AFiles: TStrings); // ... no changes ... begin if FindFirst(AFolder + '*.*', faAnyFile, SR) = 0 then try repeat CheckAbort; // <- added // ... no changes ... until FindNext(SR) <> 0; finally FindClose(SR); end; end; begin Button1.Enabled := False; // start button Button2.Enabled := True; // stop button try // ... no changes ... finally Button1.Enabled := True; Button2.Enabled := False; end; end; procedure TForm1.Button2Click(Sender: TObject); begin AbortAllWorkerThreads; end;

Run the application and start a long search. Pay attention to the Stop button and how its state changes during the search. Now, click it while the search is still running the search will be halted. How does this work? The first and most important thing is that you must call the CheckAbort routine in your secondary thread from time to time this is a mandatory condition. That's because there is no clean way to stop the thread from outside. You can, of course, do it in a dirty way (a forced Terminate), but doing so will leave a lot of trash in the process. The correct way to stop the thread's work is to let the thread itself do so. You let the thread check for a cancel condition and stop work, if it's appropriate. That's exactly what the CheckAbort routine does. If there is no cancel command it just exits without doing anything. If there is a cancel command then the routine will raise an EAbort exception. Raising this exception will stop current work in the thread and pass the exception up to the main thread, where it will be processed by the Application.HandleException method. This (by default) just ignores EAbort. So where is the command to exit issued? That's done by calling AbortAllWorkerThreads. As you will have guessed this method stops all running secondary tasks. Other issues and technical details A secondary (worker) thread is picked randomly from the system thread pool (by default via the QueueUserWorkItem function). If there are no free threads then code execution will be delayed (queued), until one of the worker threads completes and comes free. The number of threads in the pool is controlled by passing flags to the QueueUserWorkItem function (see the description of the function in MSDN). By default the limit is 512 threads. While secondary threads run your code, the main thread cycles the message pump constantly by calling Application.HandleMessage. The cycle ends with the end of all worker threads started via EnterWorkerThread. During this cycle code may be invoked which calls EnterWorkerThread / LeaveWorkerThread again (for example, when the user has clicked a button). Therefore at any moment, there might be multiple code blocks running (code blocks between EnterWorkerThread and LeaveWorkerThread), including the case of multiple instances of the same code running. That's why you may need to explicitly prevent such code starting again (as we've done in our examples by disabling the button that starts that code sequence running). Such calls are termed parallel calls. The first call to LeaveWorkerThread will continue to run only after all parallel calls have been completed. In other words the very first call to EnterWorkerThread will wait until all later calls to EnterWorkerThread have been completed. If all such calls have done their work before the first call does its work there will be no waiting at all. This is the same as in a single-threaded application, when you call Application.ProcessMessages and the message invokes another time-consuming message handler, so Application.ProcessMessages will not return control to you (the caller) until that handler has done its work. This case should not be confused with nested calls to EnterWorkerThread:

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

43

Anti-freeze for VCL applications (continuation 5)


... EnterWorkerThread; try ... P; ... finally LeaveWorkerThread; end; ... procedure P; begin EnterWorkerThread;// try ... finally LeaveWorkerThread;

A call to EnterMainThread/LeaveMainThread is like calling Synchronize. Because there can be only one code sequence executed in the main thread, EnterMainThread will block, if there is already a thread running inside EnterMainThread. Also, while code between EnterMainThread and LeaveMainThread is running the worker thread waits for completion and is not returned to the thread pool.

does nothing since we're already in worker thread

// does nothing, since nearest EnterWorkerThread // above didn't do anything too

end; end;

In other words, you can safely nest calls to Enter/Leave Worker/Main Thread and only the first call will work, all other calls will be ignored. Because of the wait for parallel calls to complete before returning control, exit from applications with parallel calls will be delayed until all called secondary threads have completed (or been cancelled via AbortAllWorkerThreads or a similar routine). Exceptions in a secondary thread will be caught and re-raised (and re-thrown) in the main thread. Delphi's debugger is not able to follow thread switching. So, if you're debugging an application and want to Step Over an EnterWorkerThread/LeaveWorkerThread, you must set an explicit breakpoint right after calls to these functions. A second (not nested) call to EnterWorkerThread may use another worker thread, not necessarily equal to the first worker thread. For example:

When NOT to use this solution It's good to know when to use this solution. But it's also important to know, when not to use it. I remind you that the primary aim of the TasksEx unit is to simplify development of smoothly responsive GUI applications. Period. End of story. This unit is not suitable for other tasks. If you need to do something like simultaneously download using ten threads or anything of that sort then this unit will not help you. To be sure, you could try to do that using this unit (and it's possible), but this would be the moment when a scary monster program is born. Solving such tasks is discussed in other Blaise Pascal Magazine articles and many Internet blogs and articles. There are various approaches at your disposal: BeginThread, TThread, QueueWorkItem, OTL (Omni Thread Library),
AsyncCalls,

There may be another article about the TasksEx unit and its additional features in a future issue of Blaise Pascal Magazine.

// This code runs in main thread


EnterWorkerThread; try

{ This code runs in worker thread #1 }


finally LeaveWorkerThread; end;

// This code runs in main thread


EnterWorkerThread; try { This code runs in

worker thread #2, which could be the very same worker thread, but also can be a completely different thread. }

finally LeaveWorkerThread; end;

To temporarily switch to the main thread use the EnterMainThread/LeaveMainThread functions. For example: // This code runs in main thread
EnterWorkerThread; try { This code runs in worker thread #1 EnterMainThread; try // This code runs in main thread finally LeaveMainThread; end;

{ This code runs in worker thread #1 } { (guaranteed to be the same worker thread) }
finally LeaveWorkerThread; end;

To receive a copy of the printed magazine you need to order a subscription from our website: www.blaisepascal.eu
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

// This code runs in main thread


44
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

Pag 45

Delphi XE2 New Features


starter expert
DELPHI XE2

By Bob Swart This can be used on just about any VCL application (just about, since some code cannot be migrated to 64-bit the BDE, for example, is a collection of 32-bit DLLs that will not ever (never!) be migrated to 64-bit yet another good reason to move away from the BDE today rather than tomorrow). Apart from 32- and 64-bit Windows, Delphi XE2 also offers native Mac OS X as a target platform. The IDE remains a 32-bit application, however, so running (launching) a 64-bit Windows or a Mac OS X application from the IDE requires some tinkering

Early in September 2011, Embarcadero's annual RAD Studio release cycle offered XE2 editions of Delphi, C++Builder, Prism (previously known as Delphi Prism) and RadPHP. This article examines some of the noteworthy new features in Delphi XE2. Some features (such as the DataSnap enhancements) are only available in Enterprise and higher editions, but other new features are available in all editions.

XE2 Editions Project Targets Speaking of Editions, there are now no less than five (5!) Where previous Delphi projects had a special node in the different Delphi XE2 editions available to buy. These are listed Project Manager for the Build Configuration, Delphi XE2 below with pricing details both for first-time buyers and for upgrading. All prices are shown without VAT, and these editions introduces the Target Platform node. This can have a value of 32-bit Windows, 64-bit Windows, and OS X (sometimes seen as are available from http://www.bobswart.com for EU OSX32, so most likely 32-bit). Since the Delphi XE2 IDE itself customers, together with a corresponding 12 month is still a 32-bit Windows application, by default a new project subscription price. will be created for the 32-bit Windows target. Depending on the Delphi XE2 New User Upgrade Subscription project type, we can add one or more Target Platforms. Starter 199 149 n/a A VCL project, for example, can be targeted for both 32-bit and 64-bit Windows, but not Mac OS X. The VCL is really tied to Professional 899 499 270 the Windows API, and is just about impossible to migrate to Enterprise 1999 1299 600 another platform. If you want to create an application for Mac Ultimate 2999 1999 900 OS X, you have to use either a console application (which can be Architect 3499 2299 1050 handy at times, but may not be exactly what you have in mind when you think of a Mac application), or you have to create an application You can upgrade from Delphi 2007 or later. However, after using a special cross-platform application framework called December 31st 2011, you will no longer be able to upgrade from FireMonkey. As you can read in Fikret Hasovic's article Delphi 2007, and the only eligible versions to upgrade from will elsewhere in this issue of Blaise Pascal Magazine, it's easy to add be Delphi 2009, 2010 or XE. Delphi XE2 Ultimate is the same a 64-bit VCL target to a project. Make sure you read Jeremy as Delphi XE2 Enterprise with the addition of DB North's detailed coverage of creating 64-bit applications with PowerStudio. Delphi XE2 Architect is the same as Delphi XE2 Delphi XE2 as well. But for Mac OS X we have to use a Enterprise with the addition of ER/Studio. These two database different framework, called FireMonkey. products are both from Embarcadero (now the proud owner of Delphi). To be honest, as a reseller I mostly sell Professional and FireMonkey Enterprise editions of Delphi. The Starter edition is rather In order for an application to look like a Windows application limited, so check out the conditions and constraints before on Windows, and a Mac application on Mac OS X, you purchase a Starter edition (especially if you plan to make some Embarcadero had to bring in (read: buy) a whole new set of money with it). Apart from these five commercial editions, you can visual components a new GUI framework. They actually call it also get special Academic versions of these editions, but only if the FireMonkey Application Platform, and it is specifically you are a registered student at a school or university (obviously, designed for cross-platform application development. Note that these Academic editions cannot be used for any commercial development). the VCL is not dead or being replaced. The VCL is still the best Finally, you can download a 30-day trial edition of Delphi XE2 solution for native Windows applications (32-bit and/or 64-bit). Architect to get a hands-on feeling of what the product is But for cross-platform applications that need to move to other capable of. platforms like Mac OS X (and in the future probably Linux), we Subscriptions are offered for the Professional and higher should use the FireMonkey Application Platform. editions, and are valid for a period of a year. You can optionally extend a subscription each year for a further year before the The FireMonkey Application Platform presents cross-platform subscription expires. With an active subscription, developers controls and elements such as forms, dialogs, buttons, menus, automatically get new editions of Delphi as soon as they are and so on. It supports 2D and 3D graphics and uses the GPU released by Embarcadero. They also have the right to three sofor the graphics, freeing the CPU itself for doing the real called incident support calls with Embarcadero (for example for work (like calculations or database operations). problems that even your reseller cannot solve for you). A subscription is The FireMonkey Designer may feel a bit awkward at first, since especially beneficial if you are a high-end Delphi user, but also it's not a clone of the VCL Designer. However, remember that for Professional users if they upgrade at least once every two FireMonkey is at version 1.0, and there will be many years. enhancements (and fixes) in the time ahead. Major New Feature: X-Platform The major new feature in Delphi XE2 is the capability for crossplatform development. First of all, the 32-bit Windows compiler has been extended with a 64-bit counterpart which produces 64-bit Windows applications. 46
COMPONENTS
DEVELOPERS

FireMonkey Example To demonstrate the FireMonkey Application Platform, let's create a new application using Delphi XE2. Using File | New Other, we get into the Object Repository and can see a number of different FireMonkey project targets for Delphi:

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 New Features (continuation 1)


The Standard category contains a number of familiar controls like TEdit, TListBox and TButton, which can be placed on the form. Warning: do not place them on top of each other (the effect that you get when you enter Edit in the Tool Palette search box) and type [Enter], followed by ListBox and another [Enter]. This places the TListBox as a child of the TEdit (unlike the way the VCL works), which is probably not what you want, and can lead to strange effects, so if you move the TEdit, the TListBox will move along! Speaking of positioning the FireMonkey controls, you encounter here another area of difference from the VCL. FireMonkey doesn't expose a Top or Left property, but uses a Position property with X and Y subproperties. There are no Anchors, but a Margin and a Padding property that appear to conflict with the way margins Figure 1: A FireMonkey 3D Application and paddings work in the VCL or CSS. A FireMonkey 3D Application is capable of doing more graphic Anyway, after you've placed a TListBox, TEdit and a TButton stuff than a FireMonkey HD Application. However, as a on the TForm (at least the control names are the same, although they consequence, a FireMonkey 3D Application requires more originate from different units), we can write the well-known event power from the GPU. The FireMonkey HD Application is the handler for the OnClick of the TButton: one that most closely resembles a VCL Forms Application, with procedure TForm1.Button1Click(Sender: TObject); a 2D Form Designer that can host FireMonkey controls that begin almost look and feel like good old VCL controls. ListBox1.Items.Add(Edit1.Text); end; As an example, let's create a FireMonkey HD Application for Delphi, which creates a new project starting with a new empty And then we can run the application, which by default will form with a black caption and border. show up as a 32-bit Windows application (the default target Save the project in FireMonkeyDemo.dpr and the form in platform of any project in Delphi XE2). MainForm.pas (in case you want to play along, for example using the trial-edition of Delphi XE2). One thing that you may immediately notice: if you click on the design area of a FireMonkey form, you cannot use the Alt+F shortcut to go to the File menu. Somehow, the Alt menu keystrokes are disabled here. Also, the nice IDE short- cut of Edit | Copy to copy the contents of the VCL Form Designer is missing in the FireMonkey Designer, so I had to make a screenshot the hard way, as can be seen in the following picture.

Figure 3
This sure looks and feels like a regular Windows application. In fact, it already contains some more-than-regular effects, like the glow around the TEdit that has the focus (visible in the screenshot). This effect can take up some processing power (GPU or CPU), and if the application becomes too slow, you can disable the effect as follows:
GlobalDisableFocusEffect := true;

Figure 2
As you can see, this looks like a platform-independent window, with placeholders for the border icons (on the upper right, probably because the IDE is still running on Windows), and a black border and caption. We can now look at the Tool Palette for a list of FireMonkey controls that can be used.

In order to run the same project, without any changes to the source code, on Mac OS X, we need to perform some additional steps.
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

47

Delphi XE2 New Features (continuation 2)


Mac OS X First of all, you need a Mac running Mac OS X version 10.6 (Snow Leopard) or later. In order to deploy the application from your Windows development machine to the Mac target machine, you also need to install a separate utility called the Platform Assistant (paserver) on the Mac. The paserver can be found on your development machine in the C:\Program Files\Embarcadero\RAD Studio\9.0\PAServer directory. There is a setup_paserver.zip to be used on Mac OS X, and a setup_paserver.exe to be used on a 64bit Windows machine. You need to copy the setup_paserver.zip and open it on the Mac where you need to unzip it and run the setup_paserver application to launch the installer of the Platform Assistant Server application.

Figure 4
You need to work through a number of screens (leaving the default settings intact), after which the Platform Assistant Server will be installed on the machine, in my case in the /Users/bob/Applications/Embarcadero/ PAServer directory:

Figure 5
Now, as soon as you start the paserver application itself, it will start a terminal window and ask for a password. Don't panic, this is not a password that you should have remembered or written down somewhere. Rather, it's the password that you can define here (at the deployment machine) and that you have to specify at the development machine to allow the development machine access to the deployment machine (and also to prevent any other visitors from accessing the Mac at port 64211).

Figure 6
48
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 New Features (continuation 3)


Once the password is entered, we can leave the paserver running on the Mac, and return to the Windows development machine to compile and run (with or without debugging) and/or deploy the project as a Mac OS X application. Mac OS X Development The next step back at Delphi XE2 involves the addition of a new target platform. In the Project Manager, open the Target Platforms node to verify that it only contains the default 32-bit Windows target. Then, right-click on the Target Platforms node and select Add Platform to add a new target platform. In the dialog that follows, select OS X as new target:

Figure 7
A Target Platform also needs a Remote Profile with the information to connect to the remote target machine. If a remote profile for the selected platform already exists, then Delphi XE2 will assign it as remote profile. Otherwise, you will be prompted to create a new remote profile as soon as you try to run the project for OS X.

Figure 8
If you click on [Yes], a dialog will show up with all available Remote Profiles for the OS X Platform. Initially, this list will be empty, so you need to click on the Add button to create a new Remote Profile, or you need to click on the Import button to import a Remote Profile (in case you saved and exported one from another development machine for example). The Add button will display the Create a Remote Profile wizard, were we can specify the name of the remote profile. I typically call the Mac profiles Mac followed by the last part of the IP-address, so I know which Mac I'm talking about. Mac164 is the Mac mini running OS X Snow Leopard on which we just installed the paserver:

Figure 9
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

49

Delphi XE2 New Features (continuation 4)


Note the checkbox to set this Remote Profile as the default remote profile for the OS X platform. Once checked, the next time you add a Target Platform for OS X, this Remote Profile will be assigned to it automatically. On the next page, we can specify the Host Name of the Mac OS X machine (or the IPaddress). The Port number is already specified using the default port number 64211. If you debug and deploy in your own local network, that port number is fine. However, If you plan to debug or deploy over the internet, I would change that port number to a slightly less obvious one, since only the password is keeping other visitors from connecting to your Mac and deploying applications to it. Finally, do not forget to specify the same password here as the one you specified in the paserver back on the Mac.

Figure 10
Click on the Test Connection button to ensure you can talk to the Mac. If the connection is refused, then you may have to configure the firewall on the Mac to allow the incoming connection.

Figure 11
If you close this confirmation dialog and go to the next page in the Remote Profile Wizard, a page is shown which is only relevant for C++Builder developers (who need to cache symbolic information from the remote machine on the local machine). Delphi developers can just skip that page and click on the Finish button to create the remote profile.

To receive a printed copy of the magazine you need to go to our website to place your order

50

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 New Features (continuation 5)

Figure 12
This will bring you back to the Select Remote Profile dialog, where you can now finally select the newly created Remote Profile.

Figure 13
As a result, the Project Manager will now show the Remote Profile next to the Target Platform in the Project Manager:

Figure 14
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

51

Delphi XE2 New Features (continuation 6)


Also, if the Remote Profile selection was shown as a result of the initiative to Run the application, the actual application will now be running on your Mac OS X machine! This may not be clear at first, especially if you did Run | Run without Debugging, but if you switch back to the Mac, you'll see the FireMonkey demo application running as a native Mac OS X application! If you copy it to a USB stick, you'll see a FireMonkeyDemo.app directory with a Contents subdirectory and a MacOS as well as a Resources directory inside plus the Info.plist file (see list of files above). The MacOS directory contains the FireMonkeyDemo, the FireMonkeyDemo.rsm and the libcgunwind.1.0.dylib library (only 20 KB), while the Resources directory contains the FireMonkeyDemo.icns file. I'm not sure if the . rsm file is still required after we've done a Release build, but that's something to figure out later. At least it's easy to deploy, and we can now even run it from the USB stick! Unit Scope Names Getting back on track, focusing on some of the new features of Delphi XE2: in order to prevent VCL and FMX (FireMonkey) classes and units from interfering with each other, and to enable both the VCL and FMX to use one RTL, Delphi XE2 introduces the concept of scoped unit names. Where Delphi 7 already introduced the capability of using dots in unit names (a functionality mainly used in the .NET flavour of Delphi before it became extinct), the dot is now being used again to produce scoped unit names. In summary: all Delphi XE2 units are grouped into logical categories, and these category names become the scope or prefix of the actual unit name (as well as the filename on disk). So SysUtils is now System.SysUtils and can be found in System.SysUtils.pas (or System.SysUtils.dcu if you have the trial-edition that doesn't include the source code). If you want to know the scoped unit name of a Delphi unit, you can use the little web application that I wrote at
http://www.bobswart.nl/cgibin/UnitScope.exe?unit=XXX

Figure 15
And this certainly looks like a native Mac OS X application to me. If you compare this screenshot to the Windows edition of the FireMonkey Demo, you will see the same ListBox, Edit and Button, but the actual look-and-feel is now totally different and Mac-like, while the Windows edition truly feels like a Windows application. Of course, these are just simple demo applications, but you should get the idea Deployment When it comes to deployment of your application, especially to the Mac running OS X, you need to perform a number of steps again. Using Project | Deployment you can get a Deployment tab in the IDE that shows the files that need to be deployed to the Mac OS X machine. There is one gotcha: if you compile a Release Build, then the project .rsm file will not end up in the OSX32\Release directory, so this file cannot be deployed. The Debug Build will produce the .rsm file, which causes it to be displayed in the list of deployment files: If you connect to the deployment machine, you can see the Remote Status. The green arrow will actually deploy the project files. On my Mac, they end up in the /Users/bob/Application/Embarcadero/PAServ er/scratch-dir/Bob-Mac164 where Bob is the name of the remote user (since the local user is called "bob") and Mac164 is the name of my Remote Profile. The FireMonkeyDemo is a 17.6 MB archive that contains all selected deployment files, and can be run as a stand-alone application on the Mac. We can also copy it to another Mac (running at least Mac OS X Snow Leopard) and run it from there.

where XXX is the name of the unit you want to resolve. In a follow-up article I discuss a new technique that allows FireMonkey applications to bind data, such as data-aware controls, without being limited to controls that communicate via datasources. In addition, I cover the DataSnap enhancements in Delphi XE2 Enterprise. You can find that article at page 108. Bob Swart (Bob@eBob42.com) Bob Swart Training & Consultancy
www.bobswart.com

Figure 16

52

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Learning to use FastReport 4 for VCL Part 3 Sergey Lyubeznyy


starter expert
DELPHI 6 and above

In previous articles (Blaise Magazine issues 16 and 17) I've talked about FastReport (FR) installation and localization. I also described the process of creating simple reports both with and without bands (including creating reports from Delphi code), and showed how to connect different data sources to the report. This article begins by showing you how to put images from files or database fields into a report. The next part describes FastReport's graphic objects. The final part shows you how to create a simple report containing a diagram. To follow along you will need some experience of working with FR, because I will not describe again typical simple operations like creating bands and placing objects on them, which have been covered previously. Putting images into a report To work with images in FastReport we need an image database. We will use a database of several photo images which I took recently on visits to St. Petersburg (Russia), with accompanying text which annotates the photos. Most of this information was taken from Wikipedia articles. This database is a specific Firebird file, created using Firebird SQL Server (version 1.5). We will connect to this database using the Firebird Embedded 1.5 DLL and the InterBase Express (IBX) components. However we don't need to place these components on the Delphi form. Instead we can simply use FastReport 4.0's built-in features to work directly with IBX in the report Designer. If you want to try to create this application yourself, you can download the example archive from the Blaise Pascal Magazine website. In this archive you will find a folder named Example1. This folder contains only the database file and the necessary Firebird Embedded components to connect to this database. You can completely unpack this folder to your computer and save your Delphi project there.

A further directory, called Example1_App, contains the same set of files and the binaries from the completed project that I created using Delphi 2010. If you want to create the project using Delphi 2009, it is important to know that the IBX components shipped with Delphi 2009 may contain a bug. When you try to open a dataset containing string fields, a division-by-zero exception is raised. You can correct this by editing and recompiling the ibsql.pas file. It is possible that this error might occur when you work with IBX for FastReport in Delphi 2009. Additional information on the bug can be found here: http://qc.embarcadero.com/wc/qcmain.aspx?d =68103 So, let's start. Create a new Delphi project and save it in the folder mentioned above, and then place the following components on your main form:
frxReport1: TfrxReport Button1: TButton CheckBox1: TCheckBox

Place them as shown in Figure 1, and set their Captions.

Figure 1: Layout of the example form


Then write the Button1.OnClick event hander, to be able to test the application during the report's creation.
procedure TForm1.Button1Click (Sender: TObject); begin with frxReport1 do begin PrepareReport; if CheckBox1.Checked then ShowPreparedReport else Print; end; Figure 2: The FastReport Data end;

page

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

53

Learning to use FastReport 4 for VCL Part 3 (continuation 1)


To work with FastReport's built-in IBX components, we also need to specify the frxIBXComponents module in the Uses section of the module that contains the TfrxReport component. Open the report Designer by double-clicking the frxReport1 component. Place the first band on the page (the Report Title band). Then place a Text object on this band and change its text to My PhotoReport. Centre the text, set the font size to 14 and make it bold. Set the Text object's Align property to baClient. Correct the band's height to place all this text inside the band's working area. Then you need to place a MasterData band on the page. We will not set a Datasource for this band right now - let's do that later. Set this band's height to approximately 5-6 cm Now look at the region above the Object Inspector. You will see three text tabs with the names Code, Data and Page1. Click the Data tab. You will see the report's Data page (Figure 2).(see page 46). The Data page is an object of type TfrxDataPage. Its published properties are not useful for us. But we can place components on this page, although they are not the same components that can be placed on the printable pages. The Data page is a container for the data access components. Take a look at the left vertical toolbar, and you will see the FR data access component icons. Three of them are IBX components: IBX Database, IBX Table and IBX Query. You can see these names in the pop-up hints as you move the mouse over them. Placing components on a data page is done exactly as for a nondata page. The only difference is that we do not need to adjust the size of the components. So, place the IBX Database (IBXDatabase1) and IBX Query (IBXQuery1) components on this data page. You can see these objects' types in the Object Inspector: TfrxIBXDatabase and TfrxIBXQuery. The TfrxIBXQuery.Database property must point to IBXDatabase1. Now you need to configure these components using the Object Inspector. First let's configure the IBXDatabase1 component. Set the Params property, using the Text editor, as shown in Figure 3.

Figure 4: Two lines of SQL text need to be entered


It is also useful to verify that the CloseDataSource property is set to True. That completes configuration of the IBX components. Next we look at the Designer's ReportData menu. The data source list should look like Figure 5

Figure 5: The data source list


Programmers familiar with IBX components may wonder which FR IBX component is responsible for working with transactions. I don't know the answer, because my FR Standard version has no source code included. I can only assume that a transaction is created and managed fully automatically. This process is hidden from the user. Now it's time to return to the Page1 tab. Double-click on the MasterData1 band and assign the IBXQuery1 as data source for this band. After that, look at the data tree (Figure 6).

Figure 6: The Data Tree dialog


When the database connection is established, near the dataset name a [+] icon is shown. If you click this icon, you can see a list of the dataset's field names. Unfortunately, we can't yet establish a connection with the database (during the design phase) because of the peculiarities of the Firebird Embedded engine. However, we can make our work little easier. Drag the IBXQuery1 dataset from the data tree to the Figure 3: Values for the Params property MasterData1 band. After that, a Text object will appear on To confirm these settings, click on the button with a green check the band containing the following text: mark. The text editor window will disappear. Then set the [IBXQuery1."IBXQuery1"] LoginPrompt property to False to avoid displaying a Instead of quoted IBXQuery1 text, we need to put the name Login dialog box, and set the DatabaseName property by of the field in quotes. Our text field is called TXT. locating the database file MYIMAGES.FDB in the file selection Insert this name in our object's text. dialog (Note: by default this dialog only displays files with a 'gdb' Similarly, when the dataset is active, you can drag a field name extension, so to find the MYIMAGES.FDB file, you must from data tree onto the data band to create the text object that switch the file type to 'All Files'). The SQLDialect property will display this field's text. Very convenient! This is what we will must be equal to 3. do later (see the last part of this article). Let's go back to our project. We need to configure this Text So, let's look at the properties of IBXQuery1. Its main object. Set the text alignment to left justify, the Arial font size to property is SQL of TStrings type. The SQL query text a value of 10, and turn off its bold property. should be entered via the Text editor (Figure 4). 54
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Learning to use FastReport 4 for VCL Part 3 (continuation 2)


Place the object in the top-right corner of the band and stretch its width to half of the page's working area. Set its height to match that of the band. Assign its stretch mode in the Object Inspector by setting the StretchMode property to smMaxHeight. Now find the Picture object icon in the Designer's left vertical toolbar. Click on this icon and then on the MasterData1 band. This shows the Picture dialog (Figure 7). If the dataset is active and correctly configured the desired field name can be selected in the Object Inspector from the combobox list at design time. If the dataset is inactive, you can enter the field name manually. To generate reports with dynamic image loading, it's very important to understand the properties of TfrxPictureView which relate to resizing the images and objects. These properties are all of type Boolean. If the Stretched property is True, the image will be resized to the object's size. The KeepAspectRatio property affects how the original image's proportions are considered during this resizing. If it is True, the image will occupy only as much of the object as is necessary to keep the aspect ratio unchanged. When you set the Center property to True, the image will be relocated to the object's centre. In addition, the object has an AutoSize property. When it's set to True, the TfrxPictureView object will change its width and height to the image sizes. Be careful when using this property, because if the picture is too large, it will be painted beyond the edges of the page. Another Boolean property, called HightQuality (not my spelling mistake - the property is written that way in FR4!) manages the picture painting with improved quality. This can be useful for example when printing a report on a high-quality printer. Once again, let's go back to our example. Set the DataSet property of Picture1 to "IBQuery1" by selecting that value from the combobox, and the DataField property to IMG (enter this text manually). Also set the Picture1 properties Stretched, KeepAspectRatio, HightQuality and Center to True, and the MasterData1 Stretched and AllowSplit properties to True, to make the band divide over two or more pages, depending on the height of the objects placed on it. I think it's necessary to consider one shortcoming of FastReport associated with the algorithm used to split objects across pages. The Text object can be divided across two pages, but the Picture object cannot be divided. When the band's AllowSplit property is True, if the image does not fit on the page it will be moved to the next page. The Text object will be divided: some lines will be placed on the first page, and the remaining lines will be moved to the next page. This dividing algorithm fails in the case of a large vertical image which gets placed on the next page while its accompanying small text stays on the original page. I hope that FastReport's developers will be able to find and implement a better solution in a future release. If the database connection is established, we can see how the report will look by opening it in the preview window. To do this, either use the corresponding icon on the Designer's top horizontal toolbar, or press the key combination [Ctrl]-[P]. But in our case, we must do it differently. Close the Designer, save the application, run it, check the preview checkbox and click the [Generate Report] button. The view of our report in the preview window is shown in Figure 9.
COMPONENTS
DEVELOPERS

Figure 7: An empty Picture dialog


The toolbar button's captions show as pop-up hints on mouseover. But this dialog's toolbuttons are only used for assigning a static image to the Picture component. This is not needed in our case, so we can simply close this window. This places a new empty Picture1 object of TfrxPictureView type on the band. Move it to the top left corner of the band's working area. Stretch it horizontally almost to the border of the Text object, and vertically to the bottom of the Text object. The Designer's report page should now look like Figure 8.

Figure 8: The Designer's Report page


Properties of the TfrxPictureView object First, I will discuss the properties of TfrxPictureView which define the image. The main property is Picture - it is a TPicture object, containing the object's image. When you open this property in the Object Inspector you see the dialog shown in Figure 7 (above). If you work with images that will be loaded by the object during the report preparation process, then this property must be left empty (unassigned). The published FileLink string property can contain the path and file name of the image to be loaded when you run the report. In addition, this property can contain a variable name in square brackets. The value of this variable (the path and filename) can be assigned, for example, in the OnGetValue event handler of the TfrxReport component. If you want to load images from a database field, you must use two string properties, DataSet and DataField.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

55

Learning to use FastReport 4 for VCL Part 3 (continuation 3)

Figure 9: The report preview window in action


We can see that if the height of a block of text is less than the image height, the images are placed right next to each other. To avoid this, we can slightly modify the report. Open the Designer. Increase the height of the MasterData1 band to about 1cm and set both of its objects' Top properties to 38 pixels (or 1 cm). As you probably know, the measurement units are configured via the Designer's Options menu (ViewOptions). Then you can again close the Designer, save project and run the program to view the report in the preview window. Now our report looks better. To show the association of text with a particular image more clearly, we could draw a line under our objects. Once again increase the height of the MasterData1 to about 1 cm (or 38 pixels), without changing the height and position of objects. Find in the Designer's left vertical toolbar the icon named Draw and click on it. A menu will appear. Select the Line object item. When you move the cursor to the page, the cursor changes its form to a pencil with a plus sign, whose centre indicates where the line will be drawn. Aim the crosshairs of this '+' at the left edge of the page workspace below the Picture object. Click the left mouse button and, while holding it down, draw a horizontal line to the right page margin. Then release the button. You will see a line object Line1 of type TfrxLineView. If it's necessary, you can move this line up or down, or change its length. 56
COMPONENTS
DEVELOPERS

Finally, you can run the program again and see how the altered report looks in the preview window. So, our first example is completed. Summing up, I want to emphasize that FastReport is able to cope well with the representation of static images, often used in reports (for example, a company logo), as well as loadable images with text in tabular form, provided there is no splitting of the data band. FastReport Graphic Objects This section about FastReport's graphic objects is theoretical, but I suggest you open the FR Designer with a blank report and play with dropping and modifying graphic objects directly as you read. This will help you to under-stand what you are reading better. One of the lower icons in the Designer's left vertical toolbar is named Draw. When you click this icon, the menu shown in Figure 10 appears.

Figure 10

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Learning to use FastReport 4 for VCL Part 3 (continuation 4)


In the previous example, we drew a line on the master data band. Here we will draw another line, this time directly on the page. Select the Line Object menu item and draw a line anywhere on the page, as you did in the previous example. Now look in the Object Inspector. Let's consider our line's properties. All lines available in the above menu are TfrxLineView objects with different settings. All these settings can be changed in the Object Inspector. The Left and Top coordinate properties, and Width and Height dimensions at this time require the single comment: this object's line always unfortunately has a fixed thickness. The boolean Diagonal property determines whether the line is diagonal. If it is set to False, the line will be strictly vertical or strictly horizontal, regardless of the Width and Height values. Otherwise, these properties define the line shift from the point [Left, Top] to the second point of the line. The point with [Left, Top] coordinates is the starting (initial) point of the line. Accordingly, the end point will be [Left+Width, Top+Height]. So, let's continue the practical exercises. Set the Diagonal property of the line to True, and then click on the toolbar's top icon (Select Tool, it has an arrow image). After that, move your mouse to any of the boundary points of the line. The cursor changes to a + sign. Click the left mouse button and, holding it, move the mouse cursor over the page's working area. The line will rotate around one of its boundary points. If you examine the changes in the Width and Height properties, you will see that these properties can have negative values. Now try to set either of these properties: ArrowStart or ArrowEnd to True. You'll see the arrow on the corresponding end of the line. You can configure the visual properties of the line arrows, using the ArrowSolid, ArrowWidth and ArrowLength properties. The ArrowSolid property, when true, fills the arrow. The ArrowWidth and ArrowLength properties determine the width and length of the arrows in pixels. You can try to manage these properties in the Object Inspector and note the appearance of the line - perhaps, it will be useful to you later. So, learning about the Line object is complete. You can remove it from the page by selecting it and pressing the [Delete] key. Now let's study the Shape objects. Similar to lines, all shape objects are represented by the TfrxShapeView class with different property values visible in the Object Inspector. Now we will investigate these settings. Using the Draw toolbar button and its menu, place a rectangle on the page and manually set its size, say, to 7x5 cm. OK, it's time to consider this object's properties. It might seem odd that this object has few properties. Its rectangular shape is determined by the Shape property, which is set to skRectangle. You can specify several other forms by changing the value of this property in the drop-down list in the Object Inspector. In addition to the rectangular shape, available shapes are: skDiagonal1, skDiagonal2 (different direction diagonal lines), skDiamond , skEllipse , skRoundRectangle (rounded rectangle) and skTriangle. The BrushStyle property of type TBrushStyle determines how all of these figures are filled (except skDiagonal1 and skDiagonal2). The Color property determines the fill colour (don't confuse this with the colour of the shape itself). If filling is not required, set the BrushStyle to bsSolid, and the Color to clNone. The Curve integer property determines the degree of rounding of the rounded rectangle's corners (Shape = skRoundRectangle). The triangle shape (Shape = skTriangle) has some peculiarities. This triangle is always isosceles, and its apex is always directed to the Top coordinate. You can't manually invert the triangle in the Designer. But this can be done in the Object Inspector, by setting a negative value for the object's Height property, and then moving the object to the desired position. I want to say that I have not tested the behaviour of this object on split bands, so I recommend you test out the triangle's behaviour on a split band before using it in this situation. Summing up, I would say that FastReport allows you to include only the simplest of shapes (with only basic characteristics) in a report. If you need to place a more complex shape in a report, you should design and save it as an image and place it in the report using a Picture component. The Chart This section gives you an example of placing a chart in a report. You will find the example project in the Example2 folder of the downloadable archive (produced using Delphi 2010). For this example we will use the dbdemos.mdb included with Delphi, connecting to it using ADO technology. This database has an EMPLOYEE table listing employees. Our aim is to print a short report from this table together with a diagram, constructed according to this report. Create a new Delphi project. Place the frxReport1 component on the form and, as usual, open the Designer by double clicking on it. Place the Report title band on the page, and then put a text object with any text on this band. Place a MasterData band on the page. The data source will be defined later. Open the Data page, as you did in the first example. Place ADODatabase1 and ADOQuery1 components on this page. Let's configure the DatabaseName property of ADODatabase1. In the first box you must set the OLE DB provider to Microsoft Jet 4.0 OLE DB Provider. Then click the [Next] button. In the next box specify the path and file name of the dbdemos.mdb database (in Delphi 2010 it's located by default in the directory C:\Program Files\Common Files\CodeGear Shared\Data), set the user name to Admin, then check the 'Empty password' checkbox and test the database connection by clicking the appropriate button. If successful, press the [OK] button. Then set the ADODatabase1.LoginPrompt property to False and its Connected property to True. If no error messages appear, you can continue. Now let's configure the ADOQuery1 component. The Database property must point to ADODatabase1, the property CloseDataSource is set to True. Specify the SQL property with this query text: SELECT LastName + " " + FirstName, Salary FROM employee WHERE Salary >= 40000; Go back to the Page1 tab. Double-click on the MasterData1 band and set its Dataset to ADOQuery1. In the data tree expand the ADOQuery1 component by clicking on the [+] icon near its name. You will see the dataset fields Expr1000 and Salary. Drag them on the MasterData1 band, to create two new text objects. Place them on the band as a table with two columns and give them all-side frames. As a result, the report should look like Figure 11.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

57

Learning to use FastReport 4 for VCL Part 3 (continuation 5)

Figure 11: The Designer with Expr1000 and Salary fields


Since we established the connection to the database, you can look at the report data in the preview window already at this stage. If all components have been set up correctly, the report will contain 9 records. Now create the Footer band ReportSummary1 and set to it a height of about 10 cm. We will now construct a diagram on this band. Find the Chart object button in Designer's left vertical toolbar and click on it. When you move the mouse cursor on the page, you will see a square cursor. Choose its location on the band's workspace and click the left mouse button. The chart setup window will appear (Figure 12).

Figure 12: The Chart Editor


Close this window. Manually stretch this object to be as wide as the borders of the page's workspace and about 8 cm. high. The top of object must be located about 1 cm below the top of the footer band. Then re-open the chart setup window by double-clicking this object. The first phase of the diagram's construction is adding the series. In our example, we need to add one series. First click on the button with a yellow + sign at the top of the window. A window will appear (Figure 13)

Figure 13: The Chart Gallery (initial view)


58
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Learning to use FastReport 4 for VCL Part 3 (continuation 6)


In the left panel select the Bar item. The window changes to look like Figure 14.

Figure 14: Selecting Bar chart styles in the Gallery

Select the Normal type and press [OK]. Then you can configure the data sources and fields in the chart setup window, as shown in Figure 15. Figure 15: Setting up the chart properties
After you set the fields and click [OK], the process of creating the chart will be completed. You can see how it will look in the preview window. Now we have to write the VCL application code. Here this code will be very easy. We are using the only command to show the report in the main form's OnShow event handler.
procedure TForm1.FormShow (Sender: TObject); begin frxReport1.ShowReport (true); end;

OK, we have considered an example of constructing a simple chart. In fact, FastReport in combination with the TChart library allows you to add many different chart types to a report. It's almost impossible to describe all these types. You can try them for yourself, either using Delphi's demos, or your own databases. This article has described working with images, graphic objects and simple diagrams in FastReport. I hope that these examples will help you develop high quality FastReport reports for your own applications. If you write to me by e-mail at slyubez@gmail.com with your ideas or questions about Delphi I may be able to discuss the matters you raise in a future article. See you again.
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

59

SUMMER OFFER: the complete guide


Blaise Magazine is making a summer deal available to all our subscribers. If you purchase the newly published book (Lazarus the Complete Guide) we will include with it the following bonus items at no extra charge: - a Windows installer CD for Lazarus version 0.9.30 - a preinstalled copy Lazarus on a free 4GB USB stick. - 50 additional sample projects -

LAZARUS

Blaise Pascal Magazine Library

17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article - extra: additional preinstalled component suites

PAPER BACK 50,00 + postage HARDCOVER 60,00 + postage

COMPONENTS
DEVELOPERS

Pag 87

For Android, iOS, Linux, Mac, Win CE, Windows XP / 7

Become

BLAISE PASCAL MAGAZINE


subscriber ...
A special offer for all Delphi users: Anyone who buys a new DELPHI product and takes out a new subscription for BLAISE PASCAL MAGAZINE (whether for a year or more) will receive the Blaise Pascal Library on a 4GB USB stick for free, including future free updates to include all issues to the end of 2011. The last update will be available in January 2012 through our web service. The free USB stick has approximately 2.8 GB unused capacity. Existing magazine subscribers renewing their subscriptions have two options: - You can buy the Blaise Pascal Library on a 4GB USB stick for the discounted price of 15 (the cost to non-subscribers is 50) - You can download the Blaise Pascal Library for free as a 1.2GB ISO file for burning to a DVD of your own. This also includes free access to future magazine issues updated to the end of 2011. The last update will be available in January 2012 through our web service. The Blaise Pascal Library is available to non-subscribers on a USB stick, priced at 50 The Blaise Pascal Library contains all issues both articles and code, including program examples, totalling 17 English issues (Issue 1 up to Issue 17), with all the code ever published in Blaise Pascal Magazine: 17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article Here are the new company subscription prices: Blaise Pasacal Magazine is now published 6 times a year (bimonthly). The magazine has a minimum of 60 full colour pages. Company subscription rates: single person annual subscription by download 35 5-person annual subscription by download 150 10 -person annual subscription by download 300 50 -person annual subscription by download 1125 100 - person annual subscription by download 2250 single person annual subscription for printed copies 50 (excluding postage) 5-person annual subscription for printed copies 225 (excluding postage) 10-person annual subscription for printed copies 425 (excluding postage) 50-person annual subscription for printed copies 2250 (excluding postage) 100-person annual subscription for printed copies 4250 (excluding postage)

Creating 64-bit applications with Delphi XE2 By Jeremy North


starter expert
DELPHI XE 2 ONLY

64-bit Windows-based computing has been around for over a decade, starting with Itanium and Windows XP 64-bit back in 2001. It wasn't until the release of Windows 7 in 2009 that installing the 64-bit version of Windows was the default. Before then most large computer retailers were still installing the 32-bit version of the latest operating system. Today many of these vendors no longer even offer installation of a 32-bit operating system as an option. With the release of Windows Server 2008 R2, Microsoft released the first version of an operating system that only supported 64-bit you cannot purchase a 32-bit version of Windows 2008 R2.
64-bit Windows Time Line 2009 Windows Server 2008 R2 (first 64-bit only OS release) 2009 Windows Win7 64-bit and 32-bit 2006 Windows Vista 64-bit and 32-bit 2005 Windows XP 64-bit for em64T/amd64 chips 2001 Windows XP 64-bit for the Itanium chip

Shell Extensions If you write Windows shell extensions then you have no doubt been waiting impatiently for a 64-bit Delphi compiler (or moved long ago to another product that compiled for 64-bit). Now you can use Delphi to create Windows shell extensions for 64-bit operating systems. Working with 64-bit in the IDE This article will just cover working with VCL 64-bit applications using XE2. The new FireMonkey framework also supports 32bit and 64-bit compilation, as well as being able to target OS X 32-bit (Snow Leopard and above) and iOS (using Free Pascal and XCode). Creating a new VCL application reveals the first hint of change. If you look at the Project Manager, you will see that there is a new node called Target Platforms under the Build Configuration node.

Figure 1

When creating a new VCL application the 32-bit platform is automatically added as a platform target. To add another platform, simply right click on the Target Platforms node and Do I actually need 64-bit? select the Add Platform command. This displays a dialog that Obviously there is no single answer that applies to every developer's applications. If you are manipulating large amounts allows you to select an additional target platform for your of data (such as large media files) then offering a 64-bit application application. For VCL applications, 32-bit Windows and 64-bit Windows are the only platforms available. For a FireMonkey will most likely provide benefits over a 32-bit application. The main reason for this is the ability to address more than 4GB application, OS X is also available. of memory (since 32-bit Windows always sees less than that amount). You might think it would be beneficial to throw 32GB of RAM into your Windows 7 Home Premium system. You can certainly do this, but you will only be able to access half of it! This is because Windows 64-bit editions have upper limits on their accessible memory. The different operating system version's maximum memory limits are shown in the following table. Figure 2
Reference: http://en.wikipedia.org/wiki/64-bit Edition Maximun Ram Starter Home Basic Home Premium Professional Enterprise Ultimate 8 8 16 GB GB GB

192 GB 192 GB 192 GB

It is not possible to add the same target platform more than once to a project. Target platforms can be removed from a project, so if your application just needs to target 64-bit systems, right click on the 32-bit Windows entry under Target Platforms and select the Remove Platform command.

Library Paths The Library options page (Tools | Options | Delphi Options | Library) has been updated to include a Selected Platform combo box at the top of the page. This lists all valid Target Platforms Access to gigantic memory spaces is not the only reason for wanting your application to compile as 64-bit. Being recognised for the IDE (not framework specific). This means that each as a first class citizen in the operating system has many benefits. platform's paths have to be maintained separately. When 32-bit applications are not able to store to and access some parts switching to the 64-bit Windows platform you will notice of the system as 64-bit applications usually would. instances of $(Platform) in the paths displayed. The IDE treats This includes access to the registry and file system areas. 32-bit this as a variable and whenever a path contains $(Platform) the applications are also installed to their own program files folder, appropriate platform abbreviation is substituted. called "Program Files (x86)".
Platform Name Abbreviation Win32 Win64

Shared resources are stored in a different folder from the System32 folder called "SysWoW64". Yes, the System32 folder contains proper 64-bit versions of the system files. 64

32-bit Windows 64-bit Windows

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating 64-bit applications with Delphi XE2 (continuation 1)

Figure 3

Figure 4
For those with a keen eye, another minor change to the dialog is the replacement of the "Namespace prefixes" field title with the title "Unit scope names". In XE2, all units have a prefix applied to them. These prefixes are not platform specific so fall outside the scope of this article. Project Options As well as being able to set paths for all applications in the Library section of the options dialog, you need to set your project's options against a particular platform (and configuration). As expected, the Project Options dialog has been enhanced to display a drop down of available paths for the project's targeted framework (VCL in this case). This means the drop down will contain two entries right? Wrong! It will contain six (default settings)! This is because it contains an entry for each build configuration and each platform as well as a default entry for each build configuration. Confused? Well here is a picture...

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

65

Creating 64-bit applications with Delphi XE2 (continuation 2)

Figure 5
This means that when you are modifying project-specific options, make sure you are modifying the options for precisely the platform and build configuration you desire (and not inadvertently changing options for a configuration you are not using). Output Paths When creating new projects with Delphi XE2 you will notice that the Output Directory and Unit Output Directory include the default output folder of .\$(Platform)\$(Config) ensuring that each targeted platform's binary and .dcu files are written to a unique location. Working with the Form Designer in a 64-bit application For XE2 there is no such thing as 64-bit design time packages. The IDE is still a 32-bit application. This means that no 64-bit packages are loaded in the IDE. There are a couple of scenarios where the effect of this may cause some frustration. 1. 2. Works fine at design time While debugging your 64-bit targeted application you are seeing errors that don't occur at design time. No 32-bit component available Registering Components for 32-bit and 64bit usage To target the 64-bit platform, components must be made available to the 64-bit platform. This can be done in two ways and just to be sure, I've used both ways for my components. 1. The runtime package (that the design time package requires) has the 64-bit Windows platform specified. 2. You can use the new Component Platforms Attribute. An example of using the second of these two options:
type [ComponentPlatformsAttribute(pidWin32 or pidWin64] TMyComponent = class(TComponent) end;

For those wondering about FireMonkey and OS X, another possible value is pidOSX32 Debugging Unless you are the world's greatest developer, you will need a debugger and thankfully XE2 ships with a 64-bit debugger. Remember how I mentioned the IDE is still a 32-bit application and it can't load 64-bit packages. Well it can't host a 64-bit debugger either! How do I debug my 64-bit application? Use a remote debugger of course. Since the 64-bit debugger is a remote debugger, some project linking options need to be set. To verify these settings are correct for your project you should check the Project Options dialog and select the Linking node. Make sure the correct Target at the top of the dialog is selected and make sure the following options are enabled: 1. Debug Information - checked 2. Include remote debug symbols - checked

If you want your component to be installable, you must have a 32-bit version to install. This means that it is impossible to have a 64-bit only component to use at design time. This doesn't mean you can't "fake" a 32-bit implementation, having basically a 32bit skeleton to represent the 64-bit component at design time. As long as the published properties of the 32-bit skeleton are consistent with its 64-bit counterpart, using a design-time-only 32-bit version of the component is plausible. Components All VCL components shipped with the IDE are available in 32bit and 64-bit versions.

Figure 6
66
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating 64-bit applications with Delphi XE2 (continuation 3)


For new projects created in XE2 these options should be set correctly. If you're using a project from a previous Delphi version, you will have to ensure that the above settings are correct. Can I debug my application if I am using a 32-bit operating sytem? Sure you can. What? Well you do need to be able to access a 64-bit machine for actually running the Platform Assistant server and application on. Platform Assistant is the name of the remote debugger. Adding a Remote Profile to a target platform To be able to debug remotely a remote profile must be assigned to the target platform. Within a project a remote profile can be assigned to any target platform listed under the Target Platforms node in the project manager. To create a new Remote Profile for the 64-bit Figure 7 Windows platform, right click on the 64-bit Windows This will display a dialog of previously entered remote profiles. Initially entry in the project manager and select the Assign this list will be empty so select the Add button to create a new remote Remote Profile command. profile.

Figure 8
The first page of the wizard requires you to name the remote profile. The platform should already be selected. Once the name has been entered, select Next.

Figure 9
The second page requires the IP Address of the remote machine as well as a port number and password for connection. The port number specified (64211) is the default port number used by the Platform Assistant server. If you change this value in the profile, make sure the PAServer.config file has been updated on the remote machine. When PAServer is launched on the remote machine, it requests a password - this is the password you should enter in the password field of the profile. You can test the connection now to verify it all works, provided you have already installed Platform Assistant on the remote machine (we'll step through that process shortly).

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

67

Creating 64-bit applications with Delphi XE2 (continuation 4)


The final wizard page is for C++ applications, so just leave it as-is and select the Finish button. Once you have added the profile, it will appear in the list of available remote profiles. Select the newly added profile and click OK. This will then update the project manager to display the remote profile name next to the target platform it is relevant to.

Figure 10 Figure 11
Modifying Remote Profiles It is possible to modify remote profiles in the Tools | Options dialog. Select the Remote Profiles node.

Figure 12
68
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Creating 64-bit applications with Delphi XE2 (continuation 5)


Platform Assistant The Platform Assistant has a separate installer available in the <InstallFolder>\PAServer folder. Copy the setup_paserver.exe file (~42MB) to your Windows 64-bit system and install it. NOTE: The setup_paserver.zip file is the OS X version of Platform Assistant - it won't work on Windows! It is a simple installation to complete, just remember where you installed it because you will need to start it! Running Platform Assistant To start Platform Assistant, locate the PAServer.exe file in the install folder and run it. The Platform Assistant is a command line application and will prompt for a password. This should be the same password that you entered when setting up your remote profile. The port used should also be changed if you are using a different port from the one originally selected. Change the default port by modifying the PAServer.config file. Unable to connect to Remote Host? The first time you try to debug remotely on a 64-bit machine you may receive a connection error, even if a test connection succeeds. This is probably due to your firewall preventing the debugger from starting. If so, you should allow access for the dbkw64_16_0.exe application in your firewall software. Code changes In many circumstances you will have to make modifications to your existing code in order for your applications to run successfully as 64-bit. Some of these changes will be discovered by the compiler when you add the new 64-bit platform target to your application and compile it. Others will be discovered only while running the application, so it is important to have a good test plan that covers all areas of your application. Adding a new platform target should mean your application gets a thorough test under the new application. New compiler defines Two new compiler defines have been introduced to help with 64-bit development. CPUX64 - Use this to differentiate 32-bit and 64-bit assembler. WIN64 - Use this to differentiate between specific 32-bit and 64-bit API versions. Type changes (and non-changes) Integer - remains 32-bit (4 bytes). NativeInt - use this for shared code when a 32-bit integer is required on 32-bit platforms and a 64-bit integer is required on 64-bit platforms. Extended has lost precision in 64-bit. It is now the same size as the Double type. If you currently use Extended you should thoroughly inspect areas of usage. Retrieving Extended values saved in a file will also need careful consideration. Extended has changed precision because the 64-bit compiler generates SSE and not FPU instructions. Extended80 is a new 10-byte type to cover the fact that Extended in 64-bit has been reduced in precision. The Extended80 type should be used when a 10-byte type is required under 64-bit. This new type will not be as fast as the Extended type available in 32-bit Windows. Pointers in 64-bit Windows are 8 bytes. In 32-bit Windows they are 4 bytes. This is why it is important to use NativeInt when typecasting pointers in shared platform code. Typecasting Pointers Where in the past you got away with typecasting pointers as Integer or LongInt, now you should be using NativeInt, IntPtr or LParam. They all mean the same thing. Loop Variables If you need your loop to handle 64-bit values, use NativeInt for your loop counter. Handle References In the past it was common to use Longint or Cardinal for references to handles. This is something that needs to be changed for 64-bit. So you now should use either the THandle or HWND type. Windows Messages When working with messages, typecast using WParam, LParam and LResult.
Before: After: Msg.LParam := LongInt(Self); Msg.LParam := LPARAM(Self);

Storing object references in the Tag property Typecast the address of the object with IntPtr or NativeInt (they are the same).
LControl.Tag := IntPtr(@LOriginal);

Replace Integer typecasts with IntPtr (or NativeInt). Before:


LMyInteger = Integer(ListOfIntegers[0]); After: LMyInteger = IntPtr(ListOfIntegers[0]);

VCL changes A large number of records and methods in the VCL have been updated now to use the correct type. Some methods have even been deprecated due to these changes, so make sure you inspect your application's Hints and Warnings. There could be useful information in there. Conclusion If you want your code to run on both 32-bit and 64-bit versions of Windows, identifying the above code changes will help to make the transition to 64-bit 'first class citizen' status as smooth as possible. Hopefully this article gives you a good guide to beginning your successful journey into 64-bit development with Delphi XE2.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

69

Now available for Lazarus

COMPONENTS
DEVELOPERS
The Delphi Starter Edition now also has the ability to do multi tier development for no cost at all. kbmMW CodeGear Edition v. 3.52.00 contains full support for Delphi Starter Edition, and provides access to SQLite in addition to the standard Delphi Starter Edition database connectivity frameworks, BDE and IBExpress.

also to be found as part of kbmMW CodeGear Edition in the form of TkbmMWDOMXML and the high performance TkbmMemTable.

In addition its possible to upgrade to kbmMemTable Standard Edition for only US$30. The upgrade includes full source for kbmMemTable, and includes kbmSQL which provides a SQL frontend to Delphi Starter Edition also lacks proper XML kbmMemTable. support and the local TClientDataset, which is
ESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI / C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON

kbmSQL Structured Query Language for your memory table


starter expert
DELPHI 7 and above (Win32) LAZARUS

The following samples are based on kbmSQL v. 1.01.00

You may already have used kbmMemTable in your applications. If not, then its certainly about time to try it out. You can download kbmMemTable CodeGear Edition for free from www.myc4d.com after registering, but please notice that only latest version of Delphi is supported with kbmMemTable CodeGear Edition. If you would like to use kbmMemTable for other versions of Delphi, C++Builder or FPC, you need to get kbmMemTable Standard Edition from www.components4developers.com. Its only US $30 and includes source and kbmSQL, which this article is about. kbmMemTable is known for being a very feature rich and fast memory table. Whats a memory table? It's a memory storage of row/field oriented data. kbmMemTable contains lots of features for searching, filtering, sorting and grouping the data contained in it, just like a database like Oracle, MSSQL etc. However apart from the fact that a kbmMemTable holds one single table, a big difference between Oracle/MSSQL etc and kbmMemTable is that Oracle/MSSQL supports (actually primarily uses) the SQL language for searching and manipulating data in the tables. For years, kbmMemTable was not able to understand proper SQL syntax, but we introduced a free, bundled add-on for kbmMemTable Standard Edition and kbmMemTable Professional Edition that we named kbmSQL. kbmSQL enables support for a large subset of the SQL language for any number of kbmMemTables you have. Over time kbmSQL is expected to evolve to support an even larger subset of the current SQL standard. So how to use it then? After having downloaded and installed kbmMemTable and kbmSQL, you will find several components in the kbmMemTable section.

Figure 2.
I have added a kbmMemTable (for the raw data), a kbmMemSQL component for doing our SQL, and a couple of grids to show the raw and result data, and finally a button that we will click to execute the SQL. Ill make it simple and just put the code we need into the Execute SQL button.
uses // Make sure to include this unit to have support for kbmMemTable registered in kbmSQL. kbmSQLMemTableAPI; procedure TForm1.Button1Click(Sender: TObject); begin // Prepare some raw data to work on. kbmMemTable1.Reset; kbmMemTable1.FieldDefs.Add ('StringField1',ftString,50); kbmMemTable1.FieldDefs.Add ('StringField2',ftString,50); kbmMemTable1.FieldDefs.Add ('NumberField1',ftFloat); kbmMemTable1.FieldDefs.Add ('NumberField2',ftFloat); kbmMemTable1.CreateTable; kbmMemTable1.Open; kbmMemTable1.AppendRecord (['String 1','AnotherString 1',120,10.10]); kbmMemTable1.AppendRecord (['String 2','AnotherString 1',15,20.10]); kbmMemTable1.AppendRecord (['String 3','AnotherString 1',10,10.10]); kbmMemTable1.AppendRecord (['String 4','AnotherString 2',33,10.10]); kbmMemTable1.AppendRecord (['String 5','AnotherString 2',12,10.10]); kbmMemTable1.AppendRecord (['String 6','AnotherString 2',99,20.10]); kbmMemTable1.AppendRecord (['String 7','AnotherString 3',19,10.10]); kbmMemTable1.AppendRecord (['String 8','AnotherString 3',143,20.10]); kbmMemTable1.AppendRecord (['String 9','AnotherString 3',199,10.10]); // Register kbmMemTable1 as source for kbmMemSQL1. // Notice that we have given kbmMemTable1 a name that identifies it in SQL (table1). kbmMemSQL1.Tables.Clear; kbmMemSQL1.Tables.Add('table1',kbmMemTable1); // Prepare some simple SQL. kbmMemSQL1.ExecSQL('select * from table1'); end;

Figure 1.
As usual you will find TkbmMemTable, TkbmBinaryStreamFormat, TkbmCSVStreamFormatter and TkbmThreadDataset (which is available for backwards compatibility and rarely used) components. And then as something new - TkbmMemSQL. TkbmMemSQL is in it self a memory table, that is based on TkbmMemTable, and will be the container for any results coming from a SQL statement. Those results can thus be further manipulated or presented in the same way as a regular TkbmMemTable instance by using it as a source of data for some other procedure or connect it to data aware controls. To make TkbmMemSQL tick, we need to give it two things to work with data and a SQL statement. The raw data is provided for it via other datasets. Currently kbmMemTable is supported as a data provider, but kbmSQL has been designed with extesibility in mind, since other data providers may be added later on. So let's start with a sample application. 72
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Structured Query Language for your memory table (Continuation 1)


By running this simple application and clicking the button, we quickly have our first kbmSQL result returned namely the same records that were contained in the kbmMemTable data source. Result:

Calculations and string concatenation:


select StringField2+ Some text as MyField, NumberField2 / 2 as Numbers from table1

Result:

Figure 3.
Notice that kbmSQL operates with case insensitive field/table names, and thus the field names are uppercase by default. Lets play a little with the different syntaxes kbmSQL supports. Change the SQL code in the ExecSQL call to try them out, one by one. Remember to escape (duplicate) single quotes (') when used in strings in Delphi. You can also use double quotes () which do not need to be escaped (duplicated) in Delphi strings. I've already shown the SQL syntax for how to get data from all fields and all records. See above. Specific fields: select StringField1,NumberField2 from table1 Result:

Figure 6. Sorting ascending:


select * from table1 order by NumberField1

Result:

Figure 7. Sorting descending:


select * from table1 order by NumberField1 desc

Result:

Figure 4.
Field alias with descriptive text: select Figure 8. StringField2 as MyField (My string field), NumberField2 as Numbers from table1 Filtering: select * Result:

from table1 where NumberField1<30 and NumberField2>15

Result:

Figure 5. Figure 9.
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

73

Structured Query Language for your memory table (Continuation 2)


Statistical functions: select count(*), min(NumberField1), max(NumberField1),avg(NumberField1), sum(NumberField1) from table1 Result: Figure 14. In addition kbmSQL supports filtering using between and in keywords. Eg.
select NumberField2 from table1 where NumberField1 between 10 and 20

Figure 10. Grouping:


select NumberField2, count(NumberField2), sum(NumberField1) from table1 group by NumberField2

Result:

Result:

Figure 15. Figure 11 Inserting new data using SQL:


insert into table1 (StringField1,StringField2, NumberField1,NumberField2) values (New String1,New string2,88.2,383)

And
select StringField1 from table1 where NumberField1 in (15,99,33)

Result:

Result (in raw data source):

Figure 16. And the SQL LIKE operator is also supported. LIKE matches string masks using * and ? as wildcards. * matches any number of arbitrary characters and ? matches exactly one arbitrary character. Eg.
select * from table1 where StringField1 like *6*

Figure 12. Changing data using SQL:


update table1 set NumberField1= NumberField1 + 100 where NumberField2<20

Result:

Result (in raw data source):

Figure 17. also contains a large predefined set of functions that can be used in expressions. To use them, you need to add kbmSQLStdFunc to the uses clause of the application. The functions include:
kbmSQL COS(n), TAN(n), LOG(n), LOG2(n), TRUNC(n), MOD(n1,n2), DIV(n1,n2), SQR(n), UPPER(s), LOWER(s), TRIM(s), NOW, DATE(n), TIME(n), YEAR(n), MONTH(n), DAY(n), HOURS(n), MINUTES(n) and SECONDS(n).

Figure 13. Deleting data using SQL:


delete from table1 where NumberField2<20

Result (in raw data source):


74
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Structured Query Language for your memory table (Continuation 3)


Eg.
select COS(NumberField1) from table1

Result:

Figure 18.

Basically what we do in the code, is to define our function, check that the correct arguments are available, produce the results that are to be used as arguments (d1 and d2) to our calculation and calculate a result. In addition the function contains functionality to tell kbmSQL about the resulting field width size, when kbmSQL needs to get that information. In the initialization section, we register the new function, and all that's needed for that function to be available for your SQL, is to include the unit in your main applications uses clause. Eg.
select COS(NumberField1), Pythagoras(10,20)

Notice that the default field name is now generated as F1. You from table1 can of course redefine the name using the AS syntax as shown Result: earlier on. If you would like to use an expression function that is currently not available in kbmSQL its very easy to extend kbmSQL with new functionality. Lets say we want to define a function that calculates Pythagoras' hypotenuse value:
PYTHAGORAS(x,y) = sqr(x*x+y*y)

Then we create a new unit and add it to the project. Lets call it myfunctions.pas. Put the following into it:
interface uses DB, kbmSQLElements, kbmSQLFuncAPI, Math, Classes, SysUtils; implementation procedure CheckArgs(const AArgs:TkbmSQLNodes; const ACnt:integer); begin if AArgs.Count<>ACnt then raise Exception.Create('Invalid number of arguments. Expected '+inttostr(ACnt)); end; function SQLPythagoras(ASender:TObject; AOperation:TkbmSQLFunctionOperation; AArgs:TkbmSQLNodes; var AResult:variant):boolean; var d1,d2:double; begin CheckArgs(AArgs,2); if AOperation=foExecute then begin d1:=AArgs.Node[0].Execute; d2:=AArgs.Node[1].Execute; AResult:=sqrt(d1*d1+d2*d2); end else AResult:=AArgs.Node[0].Width; // Let it take width after first argument for function. Result:=true; end; initialization kbmSQLFunctionRegistrations.RegisterFunction ('PYTHAGORAS',@SQLPythagoras,ftFloat); end.

Figure 19. Obviously we could have used other field values or expressions instead of the constant values 10 and 20, used as arguments for Pythagoras. All the above examples can be mixed and matched as required to perform complex searches and calculations on a single table. Currently kbmSQL does not support joins or HAVING syntax, but that's expected to be provided later on.

To receive a copy of the printed magazine you need to order a subscription from our website: www.blaisepascal.eu
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

75

High-Level Multithreading
starter expert

By Primoz Gabrijelcic

DELPHI 7 and above (Win32) LAZARUS

Working with threads at a low level is fine for those who enjoy it, but most programmers don't want to do that just as they don't want to code in assembler. They prefer high-level languages, the VCL, and packaged solutions. For multithreading, the OmniThreadLibrary is such a high-level tool. Although the initial motivation for its design was to create an easy-to-use TThread wrapper, this low-level TThread replacement has turned out to be an excellent tool for writing high-level constructs that allow any user to use parallel processing to advantage, without having to put too much thought into the low-level plumbing details. This article focuses on writing multithreaded code using the OmniThreadLibrary. As this library is constantly being developed, the article focuses on the last stable release, version 2.1. In its 2.1 release the OmniThreadLibrary supports six high-level parallelization constructs: Async (simple background execution) Join (execution of multiple background tasks) Future (execution of background tasks that return results) ForEach (a parallelized for statement) Pipeline (a parallelized staged pipeline) ForkJoin (implementing a parallel divide and conquer strategy). The implementation of these software tools uses anonymous methods extensively, which is why they are supported only in Delphi 2009 and subsequent Delphi versions. To start using the OmniThreadLibrary (called OTL from here on), download it from
http://code.google.com/p/omnithreadlibrary/ downloads/list Unpack it to a new folder (such as c:\omnithreadlibrary). Add this folder and its \src subfolder (c:\omnithreadlibrary\src in this example) to Delphi's

Yes, that's it. Parallel.Async will create a background thread (or reuse a previously created thread that is now waiting idly for some work) and run your code in it. If you don't believe me, put a breakpoint on the MessageBeep call, run the program and check the Threads window (View, Debug windows, Threads). Running unattended background tasks is fine, but sometimes you need additional information. For example you may want to be informed when the task has been completed. For such situations, Async accepts a second parameter, Parallel.TaskConfig, which you can configure in various ways. If you just want to know when the task has completed, you have to write an OnTerminated handler as in the example below:
procedure TfrmAsync.btnOnTerminatedClick (Sender: TObject); begin btnOnTerminated.Enabled := false; Parallel.Async( procedure begin // executed in background thread Sleep(500); MessageBeep($FFFFFFFF); end, Parallel.TaskConfig.OnTerminated( procedure (const task: IOmniTaskControl) begin // executed in main thread btnOnTerminated.Enabled := true; end ) ); end;

library path or to your project's search path. Add OtlParallel to your program's uses lists. And that's all folks! If you have any questions after reading this article, visit
http://otl.17slon.com/

where you'll find pointers to articles about the OTL and a web forum where you can raise your questions. Async

Clicking the btnOnTerminated button disables it and executes the background task. Method btnOnTerminatedClick then exits immediately and your app can proceed, executing other code. After half a second sleep, MessageBeep is executed in a background thread. The background task then terminates. As it finishes its termination code re-enables the previously clicked button in the main thread. (Again, you can put a breakpoint on the btnOnTerminated.Enabled := true to verify my claims). If you're wondering what the const task parameter is doing there it represents the underlying task control interface, something that you would use when working with the OmniThreadLibrary at a low level, and you can safely ignore it here. You just have to remember to add this parameter to the OnTerminated handler and to add the OtlTaskControl unit to the uses statement. A second example shows how you can get a section of code to execute in the context of the main thread. In other words, once you are running the background task, you can schedule some code to run back in the main thread. This is very important if you want to interact with the VCL because you must never try to do that from a background task! The VCL is not thread-safe. It is written on the assumption that it will always run from the main thread, and very bad things can happen if you try to update a form or execute other VCL tasks from a background thread! The code below uses task.Invoke to execute a code fragment in the main thread (again, this fragment could be an anonymous method, a normal method, or a classless procedure). It is similar in its operation to the Queue method (not to Synchronize) since it doesn't force the code to complete.

The Async method allows you to create simple, one-shot background tasks that don't require interaction with the main thread. To create a background task (that is, a piece of code that will execute in a background thread), call Parallel.Aync and pass it a block of code. This can be a parameterless method, procedure or anonymous method. For short examples, such as those in this article, I like to stick with anonymous methods as they need less typing. Let's write a simple background task that just beeps and nothing more.
Parallel.Async( procedure begin MessageBeep($FFFFFFF); end );

76

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

High-Level Multithreading (continuation 1)


Code is scheduled to be executed in the main thread at the first opportunity. Meanwhile, the background task continues with its own agenda. To use Invoke, you have to pass the IOmniTask parameter to the Async task and add the OtlTask unit to your uses list.
procedure TfrmAsync.btnInvokeClick(Sender: TObject); formThreadID: DWORD; var begin formThreadID := GetCurrentThreadID; Parallel.Async( procedure (const task: IOmniTask) var taskThreadID: DWORD; begin procedure TfrmJoin.btnAnonymousClick(Sender: TObject); begin btnJoinAnon.Enabled := false; Update; Parallel.Join( procedure begin Sleep(2000); end, procedure begin Sleep(3000); end ); btnJoinAnon.Enabled := true; end;

// this will execute in the context of the worker thread


taskThreadID := GetCurrentThreadID; task.Invoke( procedure begin

// this will execute in the context of the main thread


frmAsync.lbLog.Items.Add(Format ('Current thread ID: %d, task thread ID: %d, ' + ' form thread ID: %d', [GetCurrentThreadID, taskThreadID, formThreadID])); end

Similarly to Async, Join accepts Parallel.TaskConfig as a second parameter. It also supports the IOmniTask parameter which you can use to communicate with the main thread. Although the Join in release 2.1 is very simple, it was greatly improved after that release. New features are described in an article at
http://www.thedelphigeek.com/2011/07/lifeafter-21-paralleljoins-new-clothes.html.

Future Future is a tool that enables you to start a background ); calculation and then forget about it until you need the result of end; the calculation. To start a background calculation, you simply Further information about TaskConfig can be found on my create an IOmniFuture instance of a specific type (indicating the type returned from the calculation). blog: http://www.thedelphigeek.com/2011/04/ Future := Parallel.Future<type>(calculation); configuring-background-otlparallel.html Calculation will start in the background, and the main thread Join will continue with its work. When the calculation result is Join is another very simple tool which allows you to start needed, you simply query Future.Value. If the calculation has multiple background tasks and wait until they have all already completed its work, Value will be returned immediately. completed. No result is returned at least directly, since you can If not, the main thread will block until the background always store the result in a shared variable. If your code returns calculation is done. a result you are better off using Future or ForkJoin. The example below starts a background calculation that returns The simple demonstration of Join (shown below) starts two tasks the number of prime numbers in the range 1..1,000,000. While one sleeps for two and the other sleeps for three seconds. When the calculation is running, it uses the main thread for creative you run this code, Parallel.Join will create two background work outputting numbers into a listbox and sleeping. At the threads and run RunTask1 in the first thread and run RunTask2 end, the calculation result is returned by querying future.Value. in the second thread. It then waits for both threads to finish, and only then will the main thread execution continue. procedure
); end TfrmOTLDemoFuture.btnCalcFutureClick(Sender: procedure TfrmJoin.btnParallelClick(Sender: TObject); TObject); const begin CMaxPrimeBound = 1000000; btnJoinMethods.Enabled := false; Update; var Parallel.Join([RunTask1, RunTask2]); future : IOmniFuture<integer>; btnJoinMethods.Enabled := true; i integer; : end; numPrimes: integer; begin procedure TfrmJoin.RunTask1; // create the background calculation begin future := Parallel.Future<integer>( Sleep(2000); function: integer end; begin Result := CountPrimesTo(CMaxPrimeBound); procedure TfrmJoin.RunTask2; end begin ); Sleep(3000); // simulate another task end; for i := 1 to 10 do begin lbLog.Items.Add(IntToStr(i)); Join ensures compatibility with single-core computers. If you Sleep(20); run the above code on a single-core machine (or if you limit the lbLog.Update; process to one core of a multicore machine) it will simply execute the end; tasks sequentially, without creating a thread. // get the result Log(Format('Num primes up to %d: %d', Join accepts anonymous methods. The above demo could also CMaxPrimeBound, future.Value])); be coded as a single method executing two anonymous methods. [ end;

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

77

High-Level Multithreading (continuation 2)


If you have data in a container that supports enumeration (with As with Join, there are two Future<T> overloads, one one limitation the enumerator must be implemented as a class, not as exposing the internal task parameter and another not. TaskConfig can be provided as an optional second parameter. an interface or a record) then you can enumerate over it in parallel.
class function Future<T>(action: TOmniFutureDelegate<T>; taskConfig: IOmniTaskConfig = nil): IOmniFuture<T>; overload; class function Future<T>(action: TOmniFutureDelegateEx<T>; taskConfig: IOmniTaskConfig = nil): IOmniFuture<T>; overload; nodeList := TList.Create; // Parallel.ForEach<integer>(nodeList).Execute( procedure (const elem: integer) begin if IsPrime(elem) then outQueue.Add(elem); end);

IOmniFuture<T> has some other useful features. You can cancel the calculation (Cancel) and check if calculation has been cancelled (IsCancelled). You can also check if the calculation has already completed (IsDone and TryValue).
IOmniFuture<T> = interface procedure Cancel; function IsCancelled: boolean; function IsDone: boolean; function TryValue(timeout_ms: cardinal; var value: T): boolean; function Value: T; end;

ForEach is extremely powerful and allows you to iterate over various containers, to aggregate results, to run without blocking the main thread and more. For a longer introduction, see my blog post :
www.thedelphigeek.com/2010/06/omnithreadlibrar y-20-sneak-preview-1.html

and the implementation trilogy articles:


www.thedelphigeek.com/2011/01/ parallel-for-implementation-1-overview.html www.thedelphigeek.com/2011/01/parallel-forimplementation-2-input.html www.thedelphigeek.com/2011/02/parallel-forimplementation-3-output.html

Further information about Future can be found at:


www.thedelphigeek.com/2010/06/omnithreadlibrar y-20-sneak-preview-2.html

Also, there were some changes after the 2.1 release, mostly relating to exception handling:
http://www.thedelphigeek.com/2011/07/ life-after-21-exceptions-in.html.

ForEach Parallel For (actually called ForEach because For would clash with the reserved keyword for) is a construct that enumerates in a parallel fashion over different containers. The most typical usage is enumerating over a range of integers (just as with the classical for), but it can also be used similarly to the for..in construct for enumerating over Delphi (or Windows) enumerators. The following very simple example loops over an integer range and increments a global counter for each number that is prime. This is another way to count the number of primes in the range 1..CHighPrimeBound.
procedure TfrmForEach.btnForEachIntClick (Sender: TObject); var numPrimes: TGp4AlignedInt; begin numPrimes.Value := 0; Parallel .ForEach(2, CHighPrimeBound) .Execute( procedure (const value: integer) begin if IsPrime(value) then numPrimes.Increment; end ); lbLog.ItemIndex := lbLog.Items.Add(Format('%d primes', [numPrimes.Value])); end;

Pipeline The Pipeline construct implements high-level support for multistage processes. The assumption is that the process can be split into stages (or sub-processes) connected by data queues. Data flows from the (optional) input queue into the first stage, where it is partially processed and then emitted into an intermediate queue. The first stage then continues execution, processing more input data and outputting more output data. This continues until the entire input has been processed. The intermediate queue leads into the next stage which does the processing in a similar manner and so on and so on. At the end, the data is output into a queue which can then be read and processed by the program that created this multistage process. Overall a multistage process functions as a pipeline data goes in, and eventually data comes out.

What is important here is that no stage shares its state with any other stage. The only interaction between stages is via the data passed through the intermediate queues. The quantity of data, however, doesn't have to be constant. It is entirely possible for a stage to generate more data (or less data) than it received at its input. In a classical single-threaded program the execution plan for a multistage process is very simple.

As the code accesses a shared variable from multiple threads, it must ensure that the threads don't mutually interfere. That's why the shared variable (numPrimes) is not a simple integer, but a special thread-safe object, provided by the GpStuff unit, which is included in the standard OmniThreadLibrary distribution. 78
COMPONENTS
DEVELOPERS

In a multithreaded environment, however, we can do better than that. Because the stages are largely independent, they can be executed in parallel.

To receive a printed copy of the magazine you need to go to our website to place your order

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

High-Level Multithreading (continuation 3)

TIME

STAGE 1 STAGE 2 STAGE 3 --STAGE N

Stages are implemented as anonymous procedures, procedures or methods taking two queue parameters one for input and one for output. Except in the first stage where the input queue may not be defined, both queues are automatically created by the Pipeline implementation and passed to the stage delegate. To use the pipeline, you will have to add OtlCollections to your uses list as it is the home of the IOmniBlockingCollection.
TPipelineStageDelegate = reference to procedure (const input, output: IOmniBlockingCollection);

The various pipeline stages are shown below. The first stage ignores the input (which is not provided) and generates elements internally. Each element is written to the output queue.
procedure StageGenerate(const input, output: IOmniBlockingCollection); var i: integer; begin for i := 1 to CNumTestElements do if not output.TryAdd(i) then Exit; end;

A pipeline is created by calling the Parallel.Pipeline function which returns an IOmniPipeline interface. There are two overloaded versions one for general pipeline building and another for simple pipelines that don't require any special configuration.
class function Pipeline: IOmniPipeline; overload; class function Pipeline( const stages: array of TPipelineStageDelegate; const input: IOmniBlockingCollection = nil): IOmniPipeline; overload;

The following three stages read data from their input (by using a for..in loop), and output modified data into their output queue. For..in will automatically terminate when a previous stage terminates and its input queue runs out of data (that's a feature of the IOmniBlockingCollection enumerator). The latter version takes two parameters an array of processing As you can see from the code, values in input/output stages and an optional input queue. An input queue can be queues are not integers, but TOmniValue s (declared in the used to provide initial data to the first stage. It is also completely OtlCommon unit), which is an OTL version of Delphi's valid to pass 'nil' as the input queue parameter and to run the Variant. first stage without any input.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

79

High-Level Multithreading (continuation 4)


procedure StageMult2(const input, output: IOmniBlockingCollection); var value: TOmniValue; begin for value in input do if not output.TryAdd(2 * value.AsInteger) then Exit; end; procedure StageMinus3(const input, output: IOmniBlockingCollection); var value: TOmniValue; begin for value in input do if not output.TryAdd(value.AsInteger - 3) then Exit; end; procedure StageMod5(const input, output: IOmniBlockingCollection); var value: TOmniValue; begin for value in input do if not output.TryAdd(value.AsInteger mod 5) then Exit; end; forkJoin := Parallel.ForkJoin<integer>;

and then create computations owned by this instance


max1 := forkJoin.Compute( function: integer begin Result := end); max2 := forkJoin.Compute( function: integer begin Result := end);

To access the computation result, simply call the computation object's Value function.
Result := Max(max1.Value, max2.Value);

The code below shows how Fork/Join can be used to find the maximum element in an array. At each computation level, ParallelMaxRange receives a slice of original array. If it is small enough, a sequential function is called to determine the maximum element in the slice. Otherwise, two sub-computations are created, each working on one half of the original slice. Results from both subcomputations are aggregated by calling the Max function, and the result is returned to the upper level.
function TfrmForkJoin.ParallelMax(const forkJoin: IOmniForkJoin<integer>; left, right: integer): integer; var computeLeft : IOmniCompute<integer>; computeRight: IOmniCompute<integer>; mid : integer; function Compute(left, right: integer): IOmniCompute<integer>; begin Result := forkJoin.Compute( function: integer begin Result := ParallelMax(forkJoin, left, right); end ); end; begin if (right - left) < CSeqThreshold then Result := SequentialMax(left, right) else begin mid := (left + right) div 2; computeLeft := Compute(left, mid); computeRight := Compute(mid + 1, right); Result := Max(computeLeft.Value, computeRight.Value); end; end;

The last stage also reads data from its input but outputs only one number a sum of all input values.
procedure StageSum(const input, output: IOmniBlockingCollection); var sum : integer; value: TOmniValue; begin sum := 0; for value in input do Inc(sum, value); output.TryAdd(sum); end;

Read more about pipelines in the OmniThreadLibrary at


www.thedelphigeek.com/2010/11/ multistage-processes-with.html

Fork/Join ForkJoin is the most complicated high-level parallel construct in the OTL. It is an implementation of the divide-and-conquer technique. In short, ForkJoin allows you to execute multiple tasks, wait for them to terminate and collect their results. The trick here is that subtasks may spawn new subtasks and so on ad infinitum (probably a little less than infinite, or you'll run out of stack ). For optimum execution, ForkJoin must therefore guarantee that the code is never executing too many background threads (the optimal value is usually equal to the number of cores in the system), and that those threads don't run out of work. ForkJoin subtasks are in many way similar to Futures. They offer slightly less functionality (there is no cancellation support) but they are enhanced in another way when a ForkJoin subtask runs out of work, it will start executing some other task's workload, keeping the system busy. A typical way to use Fork/Join is to create an
IOmniForkJoin<T> instance

My blog post at:


http://www.thedelphigeek.com/2011/05/divideand-conquer-in-parallel.html contains more information about ForkJoin, and shows how to implement a parallel QuickSort with the help of this OTL construct.

To receive a printed copy of the magazine you need to go to our website to place your order

80

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

productivity software building blocks

TMS Scripter Studio Pro


By Wagner Landgraf & Bruno Fierens
starter expert
Delphi 5 and above

Flexibility and the possibility of customization is important in every application. Your application needs to be flexible enough to fit the requirements of all your customers, even if you have a large, heterogeneous customer base. You can make your application customizable by allowing features to be adaptable depending on the software configuration. You also need to provide dialogs so users can configure the available options. All this takes development time and, worse still, requires that you define in advance which features will be customizable and which won't be. If a customer unexpectedly asks you if he can change some font colour, you can point him to your application's font options dialog unless you never imagined any customer would ask for that and you just don't have a font options dialog. That's one of the purposes of a scripting system: to allow for customization. By adding scripting to your application, you raise your application's flexibility to a new level, by letting users (or your support team) customize the application using scripts, without having to worry about pre-preparing the application for a particular customization. In the example above, you could have just prepared your application to run a script before the form opens, and then the user could customize the form the way he wants including setting the colour with a simple Font.Color := clBlue;. The purpose of this article is to show how scripting increases your application's flexibility. Not only that our intention is to show how quickly you can do this using TMS Scripter Studio Pro and all its built-in features that are almost plug-and-play. Subject: The ActionBars demo To illustrate scripting usage we will take an existing application and customize it, since this is what happens in real life. You don't often build an application designing it to be fully customizable. Rather, you have already built your working application and subsequently wish to make it customizable. So we will use the ActionBars demo that comes with Delphi's sample applications. This demo is intended to show how to use action bars, but in fact it's just a small RTF editor. What we will do is build a macro system so users can create new macros and run them, yielding similar functionality to Microsoft Word's macro feature.

Figure 1
Preparing the GUI Before going into scripting itself, let's just quickly modify the application's graphical user interface so that users can create and run macros. Basically we will just add a new menu called Macros with two options: Edit Macros and Execute Macro. The first option allows users to create and edit macro content, and the second option allows users to choose a macro to be executed.

Figure 2: Here is a screenshot of our modified ActionBars demo.


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
81

TMS Scripter Studio Pro (Continuation 1)


Here is what those two menu options do: Edit Macros: Opens a scripting IDE in which users can create new scripting projects and save them into files. Execute Macro: Opens a file dialog for the user to choose a script project and then execute it. Note that the GUI is very simple and it's not the purpose of this article to elaborate it to provide a more complex one. We could obviously offer more options such as a list of recent used macros, macro shortcuts, the ability to save macros in a database, and so on. Initial scripting setup To start building very simple macros, we will just use TMS Scripter Studio's basic setup. First we need to drop three components on the form: TIDEEngine, TIDEScripter and TIDEDialog.

Figure 3
TIDEEngine is the core of scripting projects. It handles opening, saving and running projects among other things. It has a property named Scripter, which we must point to the TIDEScripter component. This is the scripting engine, which effectively runs the scripts and holds information about supported types. Finally, TIDEDialog is a high-level GUI dialog which opens a ready-to-use IDE for us to edit and debug our scripts. It must be linked to the TIDEEngine component through its Engine property. In addition to these components, we should also add some basic types to our scripting system. For example, we want our users to be able to call the IntToStr procedure, to be able to use edits and combos in the script form, so we will add some imported libraries to our application. Those are libraries already deployed with TMS Scripter Studio, and they include almost all the VCL imports. We will add basic ones like Forms, and SysUtils. To do that, you just need to add some units to the uses clause of the ActionBars project:
implementation uses ap_SysUtils, ap_Windows, ap_Classes, ap_Forms, ap_Dialogs, ap_StdCtrls, ap_Controls, ap_Graphics, TypInfo;

Once we do that, we are able to run scripts. Now we just need to integrate the scripting system with our new GUI. We will add code to our two newly created actions, acEditMacros and acExecuteMacro. Here is how (see listing ):
procedure TForm1.acEditMacrosExecute(Sender: TObject); begin IDEEngine1.NewProject; IDEDialog1.Execute; end; procedure TForm1.acExecuteMacroExecute(Sender: TObject); begin if OpenDialog1.Execute then begin IDEEngine1.OpenProject(OpenDialog1.FileName); IDEEngine1.RunProject; end; end;

It's as simple as that. In acEditMacrosExecute, we just call the TIDEEngine.NewProject method in order to clear any macro project that is in memory, then call TIDEDialog.Execute to show the IDE to edit the macros. In the acExecuteMacroExecute method, we let the user choose a script file, then load the project using the TIDEEngine.OpenProject method and later run it using TIDEEngine.RunProject.

82

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

TMS Scripter Studio Pro (Continuation 2)


The Hello World Macro So here's what happens when end-user clicks Edit Macro: the IDE is displayed with a new blank project.

Figure 4
This blank project just creates and displays an empty form. Let's just remove Unit2 from the project (this holds the empty form), by selecting Unit2, and then choosing menu option File | Remove from project. Then we change the source code of Unit1 to just show a Hello world message:

Figure 5
We can just use File | Save All menu option to save all the files. We can rename Unit1 as HelloWorldUnit.psc and Project1 as HelloWorld.ssproj, and we're done, our new macro is created. Executing the Hello World Macro Now in our ActionBars main form we can choose the Macros | Execute Macro menu option. It will display a file dialog from which we can choose our HelloWorld.ssproj project file, and then execute it:

Figure 6
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
83

TMS Scripter Studio Pro (Continuation 3)

Figure 7

Figure 8
Full integration with the application This is already a start, but we can't do many interesting things with a stand-alone scripting system if we don't integrate it into our application. This is a key part of a good flexible system. In summary, what we will do is make parts of our application available from scripting. Usually and ideally you would have a collection of business objects and make them available so end-users can manipulate those objects from scripting. In the present case, our main purpose is to get the user to interact with the text editor itself. So we will make the TRichEdit available to the script. We will create an InitScripter method and we will call it from the FormCreate event:
procedure TForm1.FormCreate(Sender: TObject); <snip> begin InitScripter; // New line added to FormCreate <snip> procedure TForm1.InitScripter; begin IDEScripter1.DefineClassByRTTI(TRichEdit, '', [mvPublic, mvPublished], true, roOverwrite); IDEScripter1.AddComponent(RichEdit1); end;

That's all we need! In InitScripter, we make the scripter aware of the TRichEdit class, most of its methods and properties, and also subclasses, like TTextAttributes. The second line makes the script aware of a TRichEdit instance we have in our application, RichEdit1,so it can be used from the macros. Note that this code uses a new feature that is only supported in Delphi 2010 and up, as a result of the new RTTI system. TMS Scripter Studio Pro is very powerful and flexible. You can add any class, method or property in your application manually to the scripting system. You could add each method of TRichEdit individually, and you could add other instances (for example we could have added the toolbar so users could manipulate the toolbar). But this is the most interesting one, because it's very straightforward at only two lines of code! This allows us to build much more interesting macros, with full code completion and parameter hints! 84

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

TMS Scripter Studio Pro (Continuation 4)


The CommentSelection macro This macro illustrates how adding the RichEdit1 instance to the scripting system can make the system much more flexible. Let's build a simple macro that comments selected text:

Figure 9: Now our users can just select a piece of text in the editor:

Figure 10: Execute and select the CommentSelection macro:

Figure 11: And our text becomes commented:


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

85

TMS Scripter Studio Pro (Continuation 5)


The GotoLine macro

As a final example, we will build a GotoLine macro to illustrate how to use the Form Designer to improve flexibility even more. We will build a macro that firstly displays a form (so the user can input the line number which he wants the cursor to be positioned at), and then proceeds to change the cursor position to the given line number. In this macro we will use two scripts, one for the form, and the other to display the form and execute the operation. The following screenshots show all the files:

Figure 12

Figure 13

Figure 14
86

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

TMS Scripter Studio Pro (Continuation 6)

Figure 15: So when a user asks to execute the GotoLine macro, this is what happens:

Figure 16: On pressing [Ok], the cursor will be positioned at the line six.

Figure 17
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
87

TMS Scripter Studio Pro (Continuation 7)


Debugging It's important to note that when editing the macros, you can easily debug them to test your code, using watches to see what's going on. Here is a screenshot of a debug session looking at the CommentSelection macro:

Figure 18: Debugging in Scripter Studio Conclusion In this article we wanted to show you how quickly an application could be customized using TMS Scripter Studio Pro, by using the ready-to-use IDE and the new RTTI which allows you to have scripting integrated into your application with just a few lines of code. For more detailed information, advanced topics, and testing, you can download a trial and try it out for yourself. For more information and a free trial download of TMS Scripter Studio Pro, visit:
http://www.tmssoftware.com/site/ scriptstudiopro.asp

About the Author Wagner R. Landgraf Electronics Engineer, M.Sc. The Product Manager at TMS Software since 1998, has been working with Delphi since its version 1. Besides developing components and frameworks for Delphi and .NET in the past years, has also worked with ERP software and also Digital Signal Processing tools.

88

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

FPVectorial - A vector graphics library


starter expert
LAZARUS

Felipe Monteiro de Cavalho

Creating vectorial images FPVectorialial's simplest use is in creating vector images. To do this simply create a new instance of the TvVectorDocument class. The document's size can be defined in millimetres in the TvVectorDocument.Width and TvVectorDocument.Height properties. This information is helpful for viewer applications, but not a requirement. You can then use any of the various Besides the raster image there is another kind, the vector image, TvVectorDocument methods to add paths, geometrical which is less well known, but widely used in fields such as elements, text and other objects. To create a new path you must commercial drawing and design, marketing, cartography, geology always begin with the StartPath() method, which must include and all types of engineering. The basic concept behind vector information about the path's start position. Thereafter you can (or vectorial) images is completely different from raster images. use any of the path segment addition methods to add path Vector images are described in terms of elements (for example segments to this intermediate path as it is being constructed. text, lines, polygons, etc.) rather than pixels (dots), and this Listing 1. Creating two vectorial documents makes it possible to modify each element of the image program fpvwritetest; separately. So you can change the elements' Z-order and carry {$mode objfpc}{$H+} out other operations which are impossible with raster images. There are hundreds of vector image formats, but a few of them uses fpvectorial, svgvectorialwriter, fpvutils; have evolved to become the most widely used. For example const cFormat = vfSVG; cExtension = '.svg'; SVG is the vector image format recommended by the World Vec: TvVectorialDocument; var Wide Web Consortium (W3C) for web pages. Other formats supported by FPVectorial are: DXF (the AutoCAD format), {$R *.res} Encapsulated PostScript, PDF and G-code for CNC (Computer begin Numerical Control) machines. Vec := TvVectorialDocument.Create; An advantage of vector images over raster images is the try // 10cm x 10cm potential of resizing vector images without resolution loss or Vec.Width := 100; Vec.Height := 100; pixellation. Since there is no unique mapping of the image to screen pixels, the actual resized onscreen appearance will depend // single_line_1.svg on how the image is rendered, and it is possible during this Vec.StartPath(0, 20); rendering to scale it without getting a low-resolution result. Most Vec.AddLineToPath(30, 30); Vec.EndPath(); vector image formats allow real-world units to be specified in Vec.WriteToFile('single_line_1' the drawing of the image. (The most notable exception is the + cExtension, cFormat); SVG format, which does not allow real-world unit specification). // polyline_1.svg The FPVectorial library was created with these unique characteristics of vector images in mind, and currently it supports documents which can contain paths, drawing elements, text and raster images. Paths are the most important part of vector images and they represent a set of connected points. The segments between points may be straight lines or Bzier curves, and the internal area of the path may or may not be filled. Elements are usually geometrical elements, such as polygons, ellipses, and so on, but there might also be a non-geometrical element such as a size measurement drawing. Text is encoded as UTF8, as in the Lazarus LCL. 90
COMPONENTS
DEVELOPERS

Pascal developers are most familiar with a kind of image known as a raster image. In both Delphi and Lazarus TBitmap is the principal class that implements support for raster images. This class represents an image stored in memory and alongside this class are many other related classes, such as TCanvas, TPen, TBrush and TFont for drawing geometrical figures and text. Lazarus additionally provides the TLazIntfImage class, which offers fast pixel access to a bitmap. As well as the standard raster image support provided by Delphi's VCL and Lazarus' LCL there are many other raster image libraries, for example Graphics32, which works both in Delphi and Lazarus. In a raster image, the picture is composed from a matrix of pixels, each pixel having an associated colour and representing the smallest dot possible within the image. Computer monitors are specifically designed for displaying pixel matrices, which is why raster images are the most frequently encountered type of computer image.

The special place which paths occupy in FPVectorial derives from the original need for this library - to be used in software controlling a CNC machine. In computer-controlled milling machines it is desirable that there are no complex drawings at all, so all elements must be converted to paths which the machine can execute by physically moving its drill or laser focal point. You can use software such as CorelDraw to convert vector image elements into paths, and then use FPVectorial to open a CorelDraw-generated PDF, and convert it to G-Code to control the CNC milling machine. The many vector image formats in use each support different features, which can lead to data loss when loading and saving vector image files. For example AutoCAD supports dimension elements, but most other formats (which are not geared towards engineering) do not support dimension elements. Most formats support only one page in each file, but PDF supports multiple pages. This article shows you how to use FPVectorial to create new vector drawings, how to load them from a disk file, and how to convert data between the different formats.

Vec.Clear; Vec.StartPath(0, 0); Vec.AddLineToPath(10, 10); Vec.AddLineToPath(20, 30); Vec.AddLineToPath(30, 20); Vec.EndPath(); Vec.WriteToFile('polyline_1' + cExtension, cFormat); finally Vec.Free; end; end.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

FPVectorial - A vectorial graphics library (continuation 1)


Each segment starts where the previous one ends, so they are always connected (though it is possible to have invisible segments which give the illusion of disconnection). Each segment is a line or a curve connecting its starting and finishing points. After adding all segments you must call the EndPath() method which will record the path in the document. The basic routines to add path segments are: AddLineToPath() to add line segments AddBezierToPath() which adds Bzier cubic curves AddMoveToPath() which adds an empty segment (which can also be interpreted as an invisible segment) One way to verify if the generated drawing is correct is to save it to a file using the TvVectorDocument. SaveToFile method, and use a vector image viewer to inspect it. The following example creates two documents, one with a straight line and another with a line composed from various segments, and saves them to SVG files. You can use a browser such as Opera or Firefox to open SVG files, or you can use a vector image editor, such as Inkscape, to visualize the generated SVG files. To be able to create two documents using a single TvVectorDocument instance we use the Clear() method. This removes all the document's current contents, effectively giving you a new document from the existing instance.
Listing 2. Creating a Bzier curve

// bezier_1.svg
Vec.Clear; Vec.StartPath(0, 0); Vec.AddLineToPath(10, 10); Vec.AddBezierToPath(10, 20, 20, 20, 20, 10); Vec.AddLineToPath(30, 0); Vec.EndPath(); Vec.WriteToFile('bezier_1' + cExtension, cFormat);

Figure 4: The bezier_1.svg file rendered by Inkscape Creating text Along with lines and curves it is also possible to add text to the vector drawing. The text is defined as a Pascal string containing UTF8 characters (just as the Lazarus IDE and LCL use), and by the position where it is placed. Text will be drawn starting upwards and to the right of the stated position, which means in the positive directions of the X and Y coordinates. It is important to note that these coordinates are understood in exactly the same way as the LCL defines them. However, in FPVectorial the axes' origin is the bottom-left corner, while in the LCL it is the top-left corner. This means that the X axis is identical in FPVectorial and in the LCL, but the Y axis starts at a different place from the LCL's Y axis, and also points in the opposite direction. In FPVectorial the Y axis grows upwards (whereas the LCL's Y axis grows downloads). This difference is introduced because FPVectorial uses the coordinate system preferred for vector documents. Besides defining a text and a position, you can also added a colour, a text size and the name of the preferred font to render it. The way in which the font name is handled will depend on the program being used to render the drawing. The following example shows how to add multiple elements to the same drawing. First lines and curves are added as explained above, and then text is added with the AddText() method. The text itself is encoded using UTF8, and its correct rendering will depend on the fonts available on the computer and on the program being used to render it.
Listing 3. Creating a vectorial document with paths and text

Figure 1: The single_line_1.svg file rendered by Inkscape (note that Inkscape adds a document border).

Figure 2: The polyline_1.svg file rendered by Inkscape..


Creating curves Now you know how to create straight lines, the next step is to create curves, and for that you need to understand how Bzier curves work. FPVectorialial uses a cubic Bzier curve, and it builds the curve starting at a point P0 and ending at the final point P3. The path to be followed is determined by two control points, P1 and P2. Any kind of curve can be created by combining various Bzier segments together. The equation of a cubic Bzier curve is shown below. B(t) = (1 t)3P0 + 3(1 t)2tP1 + 3(1 t)t2P2 + t3P3 , t [0,1]

// multi_test_1.svg
Vec.Clear; Vec.StartPath(0, 20); Vec.AddLineToPath(30, 30); Vec.EndPath(); Vec.StartPath(0, 0); Vec.AddLineToPath(100, 0); Vec.AddLineToPath(100, 100); Vec.AddLineToPath(0, 100); Vec.AddLineToPath(0, 0); Vec.EndPath(); Vec.StartPath(0, 0); Vec.AddLineToPath(10, 10); Vec.AddBezierToPath(10, 20, 20, 20, 20, 10); Vec.AddLineToPath(30, 0); Vec.EndPath(); Vec.AddText (10, 10, 0, '10,10 Some text in english.'); Vec.AddText(20, 20, 0, '20, 20 Mwic, czesc, Wlosku,Parabns.'); Vec.AddText(30, 30, 0, '30, 30 '); Vec.WriteToFile('multi_test_1' + cExtension, cFormat);

Figure 3: The Bzier cubic curve equation


The following example shows how to create a path composed of two line segments and a Bzier curve. The previously presented TvVectorDocument.AddLineToPath() method adds the line segments, and the AddBezierToPath() method is used to draw the Bzier curve. The six parameters for this method follow this sequence: The X and then the Y coordinate of control point 1, the X and then the Y coordinate of control point 2, and the X and then the Y coordinates of the curve's final point. Note that the listing presented here shows only how to use an already created instance of the TvVectorDocument class. (The code needed to create the instance was given above and is not repeated in Listing 2).

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

91

FPVectorial - A vectorial graphics library (continuation 2)


Modifying documents The methods shown above are adequate for adding new elements to a document, but not for modifying existing elements already in a document. For that you have to use other methods. Each instance of the TvVectorDocument class contains a list of elements already in the drawing. Everything is considered an element: paths, texts and other entities. To modify an element you have to obtain it with the GetEntity() routine which takes the index of the element in this list as a parameter. Note that the index might change if elements are removed from the list. The index goes from zero to GetEntityCount() 1, and with this information it is possible to iterate through all elements in the document. There are also two very similar methods which list only the path elements called GetPath() and GetPathCount(). They work very similarly and can be used to obtain just the path elements of the document. They are provided mostly for backwards compatibility.
Listing 5. The methods to obtain elements of a document function GetPath(ANum: Cardinal): TPath; function GetPathCount: Integer; function GetEntity(ANum: Cardinal): TvEntity; function GetEntityCount: Integer;

Figure 5: The multi_test_1.svg file as rendered by the Opera navigator. Setting an element's colour and width Besides creating paths and text, it is also possible to define an element's colour, width and style. The width of a path to be constructed can be defined with the TvVectorDocument.SetPenWidth() method. This method can only be used for newly constructed paths, so it can only be executed between a call to StartPath() and EndPath(). A single colour, style and width can be specified for the whole path, or you can add segments which each specify their own style data which overrides the main path style. You use SetPenColor() to define a path's colour, and similarly you use SetPenWidth() to define the width of the path. FPVectorial utilizes the main FCL colour type, TFPColor, which provides 2 bytes for each of the four main colour channels: red, green, blue and alpha. There is a convenience routine in the fpvutils unit called RGBToFPColor(). This allows for the easy creation of a 100% opaque colour, and its parameters are respectively the red, green and blue channels in 1-byte values (which are used more commonly than the 2-byte values in TFPColor).

Listing 4. Creating a document with colors // pen_test_2.svg Vec.Clear; Vec.StartPath(0, 20); Vec.AddLineToPath(30, 30); Vec.SetPenWidth(10); Vec.SetPenColor(RGBToFPColor(255, 0, 0)); Vec.EndPath(); Vec.StartPath(0, 0); Vec.AddLineToPath(100, 0); Vec.AddLineToPath(100, 100); Vec.AddLineToPath(0, 100); Vec.AddLineToPath(0, 0); Vec.SetPenWidth(10); The latest version of FPVectorial includes a Vec.SetPenColor(RGBToFPColor(0, 255, 0)); TvEntity.Translate method which allows you to move Vec.EndPath(); Vec.StartPath(0, 0); entities very easily, without the need to know precisely Vec.AddLineToPath(10, 10); how the entity was formed. Knowing how to iterate Vec.AddBezierToPath(10, 20, 20, 20, 20, 10); through segments of a path and how to access an entity's Vec.AddLineToPath(30, 0); type can be very useful. You can see the FPVectorial class Vec.SetPenWidth(10); Vec.SetPenColor(RGBToFPColor(0, 0, 255)); chart below, which shows more information about how Vec.EndPath(); the different types of entities are related. Vec.WriteToFile('pen_test_2' + cExtension, cFormat);

The example below shows how you can modify a vector document. It reads a file called bezier_1.svg (created in the previous example), and it will move the contents of the drawing 10mm upwards, which means that it will increase the Y coordinate of every element by 10mm. The modified file is then saved as bezier_1_mod.svg. The first step to accomplish this is to use ReadFromFile() to read the file, and then use a loop to iterate through all the file's paths and segments. The loop goes from zero to GetPathCount() 1, and it uses GetPath() to obtain the individual paths. The general position of a path does not mean anything, because its elements are what really contain the position information, so we use the TPath.PrepareForSequencialReading method together with TPath.Next to iterate though all a path's segments. PrepareForSequencialReading should be called once, and then you can call Next() until Next() returns nil, which means that there are no more segments in the file. The segments may be of various types, so we really have to watch out for the particularities of each kind of segment. The commonest ones are T2DSegment which is a line segment, and T2DBezierSegment which is a cubic Bzier segment.

Image 4. The file pen_test_2.svg rendered by Inkscape.


92
COMPONENTS
DEVELOPERS

Figure 5: The bezier_1_mod.svg file rendered by Inkscape


SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

FPVectorial - A vectorial graphics library (continuation 3)


Listing 6. Modifying a vectorial document program fpvmodifytest; {$mode objfpc}{$H+} uses fpvectorial, svgvectorialwriter, svgvectorialreader, fpvutils; const cFormat = vfSVG; cExtension = '.svg'; var Vec: TvVectorialDocument; Path: TPath; i: Integer; Segment: TPathSegment; _2DSegment: T2DSegment; BezSegment: T2DBezierSegment; begin Vec := TvVectorialDocument.Create; try // Read the file Vec.ReadFromFile('bezier_1.svg'); // Now add 10 to the Y coordinate of all elements for i := 0 to Vec.GetPathCount() - 1 do begin Path := Vec.GetPath(i); Path.PrepareForSequentialReading(); Path.Next(); while Path.CurPoint <> nil do begin Segment := Path.CurPoint; if Segment is T2DBezierSegment then begin BezSegment := Segment as T2DBezierSegment; BezSegment.Y := BezSegment.Y + 10; BezSegment.Y2 := BezSegment.Y2 + 10; BezSegment.Y3 := BezSegment.Y3 + 10; end else if Segment is T2DSegment then begin _2DSegment := Segment as T2DSegment; _2DSegment.Y := _2DSegment.Y + 10; end; Path.Next(); end; end; // Write the changed file to disk Vec.WriteToFile('bezier_1_mod' + cExtension, cFormat); finally Vec.Free; end; end.

Figure 6: The FPVectorial class hierarchy

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

93

FPVectorial - A vectorial graphics library (continuation 4)


Converting between vectorial formats Another use of this library is for conversion of files from one vector format to another, and it is very easy to do this kind of conversion with FPVectorial. You just call the TvVectorDocument.ReadFromFile()method to read a file and then use WriteToFile() to save it in the new format. These two routines have very similar parameters and both have an overloaded version which allows you to specify the format, and another one which omits this parameter. This means that FPVectorial tries to auto-detect the format based on the file extension. In the table below you can see the formats and file extensions supported by FPVectorial.
Constant Extension Descriptionv fPDF .pdf Listing 7. Converting between vectorial formats procedure TformVectorialConverter.buttonConvertClick (Sender: TObject); var Vec: TvVectorialDocument; begin Vec := TvVectorialDocument.Create; try Vec.ReadFromFile(editInput.FileName); Vec.WriteToFile(editOutPut.FileName, vfPDF); finally Vec.Free; end; end;

of

vfSVG

.svg

vfEncapsulated PostScript .eps vfDXF .dxf

vfCorelDRAW .cdr

vfGCodeAvisoCNC PrototipoV5 .g

PDF is not a pure vectorial image format, it is actually a document format more similar to MS Word documents which can hold a plethora different information. PDFs can hold rich text, vectorial images, raster images and even miscellaneous objects such as user interface controls and even Videos in the latest versions. FPVectorial will use only the vectorial image data when reading a PDF currently, so it should only be used with PDFs generated from vectorial image editors like CorelDraw or Inkscape, and not with just any random PDF file. The vectorial image data in PDFs is stored as PostScript commands. PDFs are a simple textbased format, but the text data may be compressed. This is the vectorial image format recommended by W3C for usage in the internet. It is an open format and with a simple and well documented specification. The main shortcomming of SVG is that it doesn't allow the use of real world units, it simply supports only measures in raw numbers, without any units and therefore the size must be decided by the rasterizer Listing 8. The function rawFPVectorialToCanvas software. Some software just hardcodes to 90 dpi for SVG, for procedure DrawFPVectorialToCanvas(ASource: example Inkscape, Opera Browser and TvVectorialDocument; fpvectorial. But some other software {$ifdef USE_LCL_CANVAS} uses other values. ADest: TCanvas;{$else}ADest: TFPCustomCanvas;{$endif} These files contain PostScript code ADestX: Integer = 0; ADestY: Integer = 0; inside them and can represent very AMulX:Double = 1.0; AMulY: Double = 1.0); complex vectorial images Below you can see how an example application, fpvviewer, uses One of the formats from AutoCAD. FPVectorial to visualize vector images. This application was The DXF is designed to be used by AutoCAD when there is need for the written in Lazarus and runs equally well in Windows, Linux and use of the file by third-parties, Mac OS X. It can be downloaded from the lazarus-ccr (Lazarus because the main file format from Code and Components Repository) by anonymous svn from the AutoCAD, DWG is not documented and directory lazarus-ccr/applications/fpvviewer. Figure 7 shows is a commercial secret of this software how this example application uses the manufacturer. This format has no open specification, DrawFPVectorialToCanvas procedure to draw a vector image to so any implementation of read and the screen when the user clicks on the [Visualize] button. The write support for it relies only on area on the screen where the image is drawn is a custom control guessing and reverse engineering. An implemented as a descendant of TCustomControl. It has an offattempt was made to add support for screen TBitmap used as a buffer for drawing the full image. The this format to fpvectorial, but without display only shows the necessary section of the image each time, much success. which makes for fast panning across the full image. The user can A G-Code format for a particular move around the image by dragging it or by using the keyboard milling machine.

Drawing a vector image on the screen As demonstrated earlier, it is possible to work with vector files, save them to a disk file, and then use an external application to open and visualize them. But FPVectorial also allows vector images to be drawn directly in a Delphi or Lazarus form. The unit fpvtocanvas comes with a routine for drawing a vector image on a TCanvas. It is very flexible and allows a document to be drawn with a configurable zoom factor. At this point it is important to consider which drawing strategy to use. You can either draw the entire document to a buffer and then show a part of it each time, or you can draw only the visible part of the document. In the first case it will take some time to zoom the document, because a new drawing will need to be generated at each zoom. In the second case it will take longer to move around the document, and this can be seen in many programs which visualize vector documents (Inkscape, for example). The currently provided routine in FPVectorial follows the strategy of drawing everything, which can be time-consuming if the document has hundreds of thousands of elements. In the future we may add a routine to draw only part of a document. You can see this routine's declaration below, and the parameters AMulX and AMulY indicate the zoom level. ADestX and ADestY indicate where the image should start to be drawn in the canvas. When you use this routine it is important to remember that TCanvas uses different units than FPVectorial does, so the basic values to give a normal view (100% zoom) are 1.0 for AMulX and -1.0 for AMulY. Similarly ADestY should receive the size of the canvas less the desired position.

arrow keys. Figure 7 demonstrates this application running under Mac OS X.


COMPONENTS
DEVELOPERS

94

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

FPVectorial - A vectorial graphics library (continuation 5)


Listing 9. Drawing a vectorial document to the screen procedure TfrmFPVViewer.btnVisualizeClick (Sender: TObject); const FPVVIEWER_MAX_IMAGE_SIZE = 1000; FPVVIEWER_MIN_IMAGE_SIZE = 100; FPVVIEWER_SPACE_FOR_NEGATIVE_COORDS = 100; var Vec: TvVectorialDocument; CanvasSize: TPoint; begin Vec := TvVectorialDocument.Create; try Vec.ReadFromFile(editFileName.FileName); ... Drawer.Drawing.Width := CanvasSize.X; Drawer.Drawing.Height := CanvasSize.Y; Drawer.Drawing.Canvas.Brush.Color := clWhite; Drawer.Drawing.Canvas.Brush.Style := bsSolid; Drawer.Drawing.Canvas.FillRect(0, 0, Drawer.Drawing.Width, Drawer.Drawing.Height); DrawFPVectorialToCanvas(Vec, Drawer. Drawing. Canvas,FPVVIEWER_SPACE_FOR_NEGATIVE_COORDS, Drawer.Drawing.Height FPVVIEWER_SPACE_FOR_NEGATIVE_COORDS, spinScale.Value, -1 * spinScale.Value); Drawer.Invalidate; finally Vec.Free; end; end;

Figure 8: Turbo Circuits and Vectors in action


Conclusion This article has shown you how to manipulate complex vector images using FPVectorial. To accomplish this without FPVectorial would be an immense amount of work, because each supported format (pdf, eps, dxf, svg, gcode, etc.) differs greatly from all the other formats, and some formats have specifications which are very hard to understand or are trade secrets. This library was written entirely in Object Pascal so Lazarus projects can use it without needing any other external dependency. For example it is possible to add an AutoCAD or SVG visualizer to an application without the user having to install a huge and very expensive application like AutoCAD or CorelDraw. The advantage on Linux is even greater, because most CAD software won't run on non-Windows or non-Mac systems. In addition, the library allows you to convert between vector graphic formats, generate new vector images, modify existing ones, and render vector images into bitmaps. Of course there is still a lot of room for improvement in FPVectorial, and this library is growing all the time as people contribute code and money to this project. Links FPVectorial internet page
http://wiki.lazarus.freepascal.org/fpvectorial

Figure 7: The sample viewer program


We can also implement a simple viewer with FPVectorial, and the image below shows Turbo Circuits and Vectors, which is a vector image editor written in Lazarus using FPVectorial. This application has many tools for drawing electric circuit symbols and designs, as well as tools for drawing of any vector image. Its source code can be downloaded from the SourceForge page:
http://sourceforge.net/projects/turbocircuit/

Wikipedia article about Bzier curves


http://en.wikipedia.org/wiki/Bzier_curve
About the Author Monteiro de Cavalho Felipe is an electrical engineer who graduated at the University of So Paulo. He is one of the developers of the Lazarus and Free Pascal projects, currently focusing on the development of software for smartphone platforms and the development of various libraries such as for handling vectorial images, spreadsheets, etc. His current employment is with Opera Software in Wrocaw - Poland as a quality assurance engineer.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

95

A tale of the nightingale


a story for starters
by Howard Page -Clark- telling the tale Detlef Overbeek - creating the project and illustrations Jean Pierre Hoefnagels - advices on coding starter expert
DELPHI 7 and above (Win32) LAZARUS

This project is designed as an example of how you can program very simple image animation while simultaneously playing accompanying sound. The technique employed is quite straightforward so you can easily adapt it for your own purposes using different images in a similar setup. All you need is a bit of inspiration or access to the goddess of creation: Thetis. To make this project suitable for two different IDEs (Integrated Development Environments) Lazarus and Delphi - I chose to create two differently coded versions, both interchangeable. One version uses a common-sense algorithm, the other uses a compressed sort of algorithm. (Put simply, an algorithm is a step-by-step procedure for carrying out a sequence of calculations or instructions).

You can pass null for this parameter to stop the sound playing. We want to use the asynchronous flag here (snd_async) because then the call will exit immediately, allowing the animation to proceed. You might think that a synchronous call would mean the animation and sound were synchronised. But here it does not mean that! Theoretically you could achieve the same effect in another way: cut up the program time into tiny slices and quickly alternate a bit of sound and a bit of animation. The computer chip works in this sliced fashion. But that is very complicated and I do not believe it would be an appropriate method for such a simple application as this. Another solution yielding simultaneous sound and animation would be multithreading. But again it is more complicated than we need here. Windows offers an easy alternative with its asynchronous PlaySound call. So why not go for this easier alternative?

The simple algorithm we started with is largely selfdocumenting, particularly since I added suitable comments where I felt an explanation was needed. Now the fun is that the compiler is not interested in the beauty of the code, nor in its length. It will process whatever you have written, whether in short or long Pascal statements. I would advise you to try both the simpler (more extended) We will start of course with the simplest presentation of the version as well the shorter compressed version. You will learn algorithm, written as a sequence of instructions so as to be from looking at both. By the way, for lovers of multithreading: readily understandable. For the alternative compressed Primoz Gabrijelicic has written a further article about High algorithm we will organise the instructions differently and make Level Multithreading in this issue (page74). them more compact, saving the code in a separate unit. This can lead to a noticeable reduction in the number of lines of code. Although better in some ways it is not so readily understood by You can download both of these nightingale projects: one unit is for Delphi the other for Lazarus. But they are beginners (and we were all beginners once). interchangeable without a problem. The image data files are My personal opinion is that abbreviated code, while often very included with the projects, so you can see exactly how the beautiful, can be hard to understand later on, especially if you animation works. have not documented it well at the time of writing. The algorithm in this project uses an array, a Windows API call The sound and action have to be triggered by an event, so we (sndPlaySound) and a timer object. Nothing too difficult. have made that event the starting point. To animate an image requires drawing it in a sequence of subtly different positions. Drawing this sequence of images in quick succession gives the illusion of movement. So for an animated bird I found a photograph of a real nightingale, from which I created a series of sketches incorporating subtle variations. To make it easy to do the sketches I kept the bird's body in exactly the same position and just let the bird's head and beak change position (like some politicians). (Did you know that all birds evolved from dinosaurs? To be precise from Velociraptors. So eat more Kentucky Fried Chicken, they're all Dinos!) Apart from looking beautiful this nightingale also sings splendidly. I would like to be woken each morning with its song. (Maybe we could ask Philips to add the sound, or add it ourselves via an iPhone, as featured in the Philips Wake Light which comes with a slot for your iPhone)

Figure 1: The Nightingale in a natural pose


The aim of the project What we want to produce is a program that displays a moving, singing bird where the animation and sound happen together (i. e. simultaneously, not separately). By the way, the nightingale's sound is taken from an original live nightingale recording. As an extra you can listen to the story of the Chinese Nightingale (a Chineseinspired Hans Andersen tale) read by Howard Page-Clark. As soon as you click on the bird you should hear the story. (Maybe you could make it respond to gestures). It is not as easy as it looks to use the Windows sound API because the sndPlaySound() call in the MMSystem unit has two parameters which must be understood for its correct use. The first is a string identifying the waveform audio (.wav) file containing the sound to play, and the second parameter is a combination of flags that determine the sound play options. 96
COMPONENTS
DEVELOPERS

Figure 2: The Philips Wake-up light

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

A tale of the nightingale (continuation 1)


Const FNames:array[0..49] of string = ( '..\jpg\Nightingale1.jpg', '..\jpg\Nightingale2.jpg', '..\jpg\Nightingale2.jpg', '..\jpg\Nightingale2.jpg', '..\jpg\Nightingale3.jpg', '..\jpg\Nightingale3.jpg', '..\jpg\Nightingale3.jpg', '..\jpg\Nightingale4.jpg', '..\jpg\Nightingale4.jpg', '..\jpg\Nightingale4.jpg', '..\jpg\Nightingale5.jpg', '..\jpg\Nightingale5.jpg', '..\jpg\Nightingale5.jpg', '..\jpg\Nightingale5.jpg', '..\jpg\Nightingale6.jpg', '..\jpg\Nightingale6.jpg', '..\jpg\Nightingale6.jpg', '..\jpg\Nightingale6.jpg', '..\jpg\Nightingale6.jpg', '..\jpg\Nightingale7.jpg', '..\jpg\Nightingale7.jpg', '..\jpg\Nightingale7.jpg', '..\jpg\Nightingale8.jpg', '..\jpg\Nightingale8.jpg', '..\jpg\Nightingale8.jpg', '..\jpg\Nightingale9.jpg', procedure TfrmNightingale.Timer1Timer(Sender: TObject); '..\jpg\Nightingale9.jpg', '..\jpg\Nightingale9.jpg', procedure DoPic(const fPic: TFilename; aTag: integer); '..\jpg\Nightingale8.jpg', begin '..\jpg\Nightingale8.jpg', imgNightingale.Picture.LoadFromFile(fPic); '..\jpg\Nightingale8.jpg', imgNightingale.Tag:= aTag; '..\jpg\Nightingale7.jpg', end; '..\jpg\Nightingale7.jpg', '..\jpg\Nightingale7.jpg', begin '..\jpg\Nightingale6.jpg', case imgNightingale.Tag of '..\jpg\Nightingale6.jpg', 0: DoPic('..\jpg\Nightingale1.jpg', 1 ); '..\jpg\Nightingale6.jpg', 1: DoPic('..\jpg\Nightingale2.jpg', 2 ); '..\jpg\Nightingale5.jpg', 2: DoPic('..\jpg\Nightingale2.jpg', 3 ); '..\jpg\Nightingale5.jpg', 3: DoPic('..\jpg\Nightingale2.jpg', 4 ); '..\jpg\Nightingale5.jpg', 4: DoPic('..\jpg\Nightingale3.jpg', 5 ); '..\jpg\Nightingale4.jpg', 5: DoPic('..\jpg\Nightingale3.jpg', 6 ); '..\jpg\Nightingale4.jpg', 6: DoPic('..\jpg\Nightingale3.jpg', 7 ); '..\jpg\Nightingale4.jpg', 7: DoPic('..\jpg\Nightingale4.jpg', 8 ); '..\jpg\Nightingale3.jpg', 8: DoPic('..\jpg\Nightingale4.jpg', 9 ); '..\jpg\Nightingale3.jpg', 9: DoPic('..\jpg\Nightingale4.jpg', 10 ); '..\jpg\Nightingale3.jpg', 10: DoPic('..\jpg\Nightingale5.jpg', 11 ); '..\jpg\Nightingale2.jpg', ... '..\jpg\Nightingale2.jpg', ... '..\jpg\Nightingale2.jpg', 45: DoPic('..\jpg\Nightingale3.jpg', 46 ); '..\jpg\Nightingale2.jpg'); 46: DoPic('..\jpg\Nightingale2.jpg', 47 ); 47: DoPic('..\jpg\Nightingale2.jpg', 48 ); procedure TfrmNightingale.Timer1Timer 48: DoPic('..\jpg\Nightingale2.jpg', 49 ); (Sender: TObject); begin 49: begin if State >= length(FNames)-1 DoPic('..\jpg\Nightingale2.jpg', 0); then State:= 0 // To go the starting position else inc(State); end; imgNightingale.Picture.LoadFromFile (FNames[State]); end; end; end;

Once you have an image of the nightingale (BMP or JPG or PNG) you need to separate the head from the body. (I wish it were always that easy). Drawing suitable small variations to the head and beak will make for smooth animated movement. You will have to do quite a number of sketches the beak slightly open and increasingly opened further; the head a bit oblique and with some emerging blush to show the effort the bird is making It eventually took me a whole day's work before I was satisfied with all the sketches needed. I created loads of action while sitting still on my chair...

Compare code above with the following compressed version shown here: You need to create an array of constants which enables you to repeat the images continuously. You repeat the same code section with each iteration, so you don't have to write out the whole sequence repeatedly. The procedure calls in the two versions are exactly the same.

procedure TfrmNightingale.btnStartClick(Sender: TObject); begin sndPlaySound('..\wav\Nightingale_song.wav', SND_FILENAME or SND_ASYNC); Timer1.Enabled := True; end; end.

Thats what it is all about...

So this is not very difficult. You might decide to create such a tale for your own child to enjoy. Perhaps change the Nightingale into a Warrior; or create a drawing application with different sounds associated with its buttons; or create a talking interface or ... whatever. Have fun.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

97

barns s en
DEVELOPMENT & DATABASE TOOLS

Barnsten B.V. - Embarcadero Technology Centre Postbus 5010 2000 GA Haarlem Nederland Tel.: +31 23 542 22 27 / Fax.: +31 84 755 52 60 Web: www.barnsten.com Info: info@barnsten.com

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

99

FireMonkey (continuation 1)

FireMonkey System Requirements FireMonkey development requires Delphi XE2, C++Builder XE2, or RAD Studio XE2 running on Windows iOS development requires Delphi XE2 or RAD Studio XE2 FireMonkey applications run on multiple platforms with these requirements: WindowsOS XiOSSupported virtualization products for running in a Virtual Machine (host Intel x86-compatible, Pentium 4 or later Basic GPU Any vendor DirectX 9.0 class or better (Pixel Shader Level 2) 32-bit or 64-bit Windows Microsoft Windows 7 Microsoft Windows Vista SP2 Microsoft Windows XP Home or Professional, SP2 or SP3 Microsoft Windows Server 2003 SP1, 2008, or 2008 R2 Mac OS X 10.6 Snow Leopard or OS X 10.7 Lion 2 GB RAM or more All Intel Macs have a qualified GPU iOS 4.2 or higher All iOS devices have a qualified GPU VMware Fusion 3 VMware Workstation 7 100

requires GPU)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Using the Advantage Database Client Engine Michal Van Canneyt


starter expert
DELPHI 7 and above (Win32) LAZARUS

Advantage Database server has an embedded client engine (the Advantage Client Engine) which can be used royalty-free for desktop applications. This article shows how you can quickly build an application that needs an SQL database using the Advantage Client Engine. Introduction The Advantage Database Server is a mature, full-blown SQL database which runs on Intel Windows 32/64 bit and Linux 32/64 bit. It supports stored procedures using SQL programming, external functions in native code, and it supports checking referential integrity and enforcing other constraints. Last but not least, it offers Full Text Search (FTS) on the database as a native mechanism. You may not know that an embedded version of this engine is also available, which allows you to create SQL-enabled applications that are easy to deploy. You simply need to copy a few DLLs to your application's installation directory, and everything is ready to go. This embedded engine can be used royalty-free, provided it is used in what are essentially single-user applications. As soon as you build multi-tier or webserver applications on top of the engine, you need to obtain a licence. Licensing details can be found on the website indicated below. For multi-tier or multi-user applications it may be altogether preferable to use the full Advantage Database Server solution. An application that starts out as single-user may evolve to become a complete client/server application. If so, upgrading is as easy as distributing another connection library and setting a different connection string in the application. Advantage Database is a solution that scales easily. The client engine (whether embedded or not) can be used from several languages apart from Object Pascal. Other supported languages include C++, .NET, Python, PHP and Java. Object Pascal support for the Advantage Client Engine is very complete. It comes with its own TDataset descendants, ready for use in Delphi or Lazarus. Object Pascal can also be used to write stored procedures or triggers for an Advantage Database. The installer is available at http://www.advantagedatabase.com/ (look for the Delphi TDataset component). The installer will automatically install the components in the installed Delphi IDE. For Lazarus users, a Lazarus package (adsl) is available which must be compiled and installed in the IDE. (Instructions can be found in the installed Help file). Following a successful installation, a new Advantage tab appears on the component palette. It contains the following components, which will seem quite familiar to the experienced database developer: TAdsConnection This component represents a connection to the database. It is equivalent to the TADOConnection or TSQLConnection components in Delphi or Lazarus. TAdsTable This TDataset descendant represents a table in the ADS database. It is the equivalent of TADOTable or TIBTable in Delphi. In Lazarus, it is roughly equivalent to the TDbf component.

TAdsQuery This TDataset descendant can be used to execute SQL statements. It is equivalent to the TADOQuery, TIBQuery or TSQLQuery components in Delphi and Lazarus. TAdsStored This component can be used to execute or retrieve data from a stored procedure. Again, it is the equivalent of existing stored procedure components in Delphi, tuned for use with the Advantage Database server. TAdsSettings This component can be used to manipulate the local database engine settings. This is something which can also be done in custom code, but the component makes the process easier. TAdsDictionary This component encapsulates an Advantage Data Dictionary. More is written about data dictionaries in what follows. There are several other Advantage components as well, but those listed above are the most important. Creating a database Advantage Databases have no single database file as in Firebird or MS-SQL Server. Basically, an Advantage database is a directory with a collection of table files. Therefore, it is not really necessary to create a database, other than creating a directory to contain the table files. This kind of database is termed 'Free Tables', and is in fact very similar to a Paradox database: namely a set of flat-file tables that are connected only through the logic of program that uses them. For simple systems, this may well be enough. However, a data dictionary can be associated with a database. The data dictionary contains metadata about the tables in the database: it describes constraints on tables, relations between tables, stored procedures, user access rights and many more such attributes all the kinds of features one looks for in an RDBMS system. A data dictionary can be created in one of 3 ways: 1. Through a low-level API call of the Advantage Client Engine library (AdsDDCreate). 2. Through a method of the TAdsDictionary class. The class is marked as deprecated, but it is in fact the easiest way to create a data dictionary in code. 3. Using the Advantage Data Architect. This is a program which has to be downloaded and installed separately from the ADS TDataset support. It comes with full source, which is an invaluable source of information on using the ADS Delphi components. Using the Advantage Data Architect is in fact the fastest way to create an Advantage Database. Under the 'File' or 'Connection' menu items you will find a 'New connection Wizard'. The wizard starts by asking whether a connection should be made to an existing database, or whether a new database is to be created.

To receive a printed copy of the magazine you need to go to our website to place your order
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

101

Using the Advantage Database Client Engine (continuation 1)


For most purposes, the data dictionary is the better option. The wizard will then prompt you for the necessary parameters such as the name of the dictionary, and the location of the database. The dictionary file will then be saved using the name of the dictionary and the extension .add. If you choose the 'Free Tables' option, instead of a dictionary file being created, a new alias is introduced which refers to the database directory.

Creating tables
Once the data dictionary has been created you can add tables to it. There several ways to do this: 1. Using the Data Architect to structure the table. 2. Using the Data Architect to import existing data into a new table. 3. Use Delphi code based on the TAdsTable component which contains a table-creation method. 4. Using a DDL SQL statement in the Data Architect or from a Delphi program. The first option is certainly the easiest. A simple set of tables to contain Blaise Pascal Magazine's issues and their contents can be modelled in less than 5 minutes, and is shown in Figure 2. A particularly nice thing about the Data Architect is that it can at will - write code (in several programming languages) to recreate the modelled table. It can do the same with SQL, writing SQL statements which will recreate the whole data dictionary or a simple table. This is a useful feature also found in the Lazarus Database Desktop, for instance.

Figure 1: Creating a new data dictionary


After choosing 'Connection to a new database', the wizard will ask whether a database consisting of 'Free Tables' is to be created, or whether an actual data dictionary is to be created.

Figure 2: Creating a new table


102
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Using the Advantage Database Client Engine (continuation 2)


Either method can be used to insert code in an application to create an empty Advantage Database when the application is first installed and started on the end-user's system, so there is no need to distribute the data dictionary or table files. The following code, for instance, will create the 'contents' table. It uses a TAdsTable component, called TContents:
Procedure TBlaiseModule.CreateContentsTable; Function AddDef(AName: String; AType: TFieldType): TFieldDef; begin Result:=TContents.FieldDefs.AddFieldDef; Result.Name:=AName; Result.DataType:=AType; end; begin With TContents do begin TableName := 'contents'; TableType := ttAdsADT; AdsTableOptions.AdsCollation := 'ansi'; end; TContents.FieldDefs.Clear; AddDef('ISSUE',ftInteger); AddDef('STARTPAGE',ftSmallInt); AddDef('ENDPAGE',ftSmallInt); AddDef('AUTHOR',ftWideString).Size:=50; AddDef('TITLE',ftWideString).Size:=100; AddDef('ABSTRACT',ftWideMemo).Size:=1; AddDef('Keywords',ftWideString).Size:=200; AddDef('PageCount',ftSmallInt); TContents.CreateTable; end;

The QCreate component needs to be connected to a TADSConnection instance. The SQL property (of type TStrings) can be filled with an SQL statement that the Advantage Server understands, and ExecSQL will execute the query. The supported SQL syntax is documented in the Advantage help file. The Data Architect tool creates more than one SQL statement per table since the table constraints (required fields, indexes and so on) are not included in the CREATE TABLE statement, but are created using stored procedures. For instance, the following procedures will create an index on the 'AUTHOR' field in the contents table and will set the Required flag to True:
EXECUTE PROCEDURE sp_CreateIndex90( 'contents','contents.adi','AUTHOR', 'AUTHOR', '', 2, 512, ':en_US' ); EXECUTE PROCEDURE sp_ModifyFieldProperty ( 'contents', 'AUTHOR', 'Field_Can_Be_Null', 'False', 'APPEND_FAIL', 'contentsfail' );

The code above will seem very familiar to programmers who have used the TTable or TDbf components to create Paradox or DBF tables. First you add every field to the TAdsTable component's collection of FieldDefs. Once you have specified all the needed field definitions and properties, the CreateTable call creates both the table in the database and CREATE PROCEDURE KEYWORDARTICLES( akeyword CHAR (50), the data dictionary (the TAdsTable component must be title CHAR (100) OUTPUT, connected to a TAdsConnection instance). author CHAR (50) OUTPUT, The Advantage Data Architect can also write a set of SQL issue Integer OUTPUT) statements to recreate the tables. They can be executed using the BEGIN DECLARE cursor1 CURSOR AS TAdsQuery component like this:
procedure TBlaiseModule.CreateContentsSQL; Const SCreateSQL = 'CREATE TABLE contents ('+ ' ISSUE Integer,'+ ' STARTPAGE Short,'+ ' ENDPAGE Short,'+ ' AUTHOR NVarChar(50),'+ ' TITLE NVarChar(100),'+ ' ABSTRACT NMemo,'+ ' Keywords NVarChar(200),'+ ' PageCount Short) IN DATABASE'; begin With QCreate do begin SQL.Text:=SCreateSQL; ExecSQL; end; end;

The complete list of stored procedures that can be used to modify the data dictionary is included in the ADS help file. Under normal circumstances, it is not necessary to know this list as the Data Architect can be used to create the necessary statements to recreate a table. In addition to tables, it is also possible to create stored procedures. Stored procedures are useful for reducing lengthy operations' execution time by letting the server execute them. The Advantage Data Architect lets you define stored procedures, and also allows you to debug them, which is a very handy feature. The following is an example of a stored procedure that returns all records from the contents table that have a particular word in the keywords or title field:

SELECT TITLE,KeyWords,Author,Issue FROM CONTENTS; DECLARE thelike varchar(55); thelike=(select rtrim(ltrim(akeyword)) from __input); thelike='%'+thelike+'%'; OPEN cursor1; While FETCH Cursor1 do if (Cursor1.TITLE like thelike) or (Cursor1.Keywords like thelike) then Insert into __Output values(Cursor1.title,cursor1.Author,Cursor1.is sue); end if; end while; Close Cursor1; END;

To receive a printed copy of the magazine you need to go to our website to place your order

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

103

Using the Advantage Database Client Engine (continuation 3)


The syntax of this stored procedure is pretty self-explanatory. The use of __input and __output to access the input and output parameters is something you have to get used to, but other than that, the syntax is straightforward and easy to learn. You can then call this stored procedure like this:
EXECUTE PROCEDURE KEYWORDARTICLES('Editorial');

It returns a list of all articles containing the word 'Editorial'. Note that three parameters for this procedure are of type 'CHAR', which necessitates some trimming prior to using the parameter in the condition. The need for this is explained in the 'Getting data using stored procedures' section below.

IndexFieldNames This must be set to Issues. The index is needed to filter the contents table on the Issue field. MasterSource This property must be set to DSissues which tells the TContents component that it should filter the records shown depending on what is in the current record in the TIssues dataset. MasterFields This must also be set to Issues. This field will be used to filter the records using the value of the matching field in the TIssues dataset. As the user scrolls through the TIssues dataset, the TContents dataset will automatically refresh itself. Once this is done, an MDI Child form can be created. The unit in which the datamodule is defined (dmBlaise) must be added to the uses clause of the form. All that remains to be done is to drop a couple of navigators and grid components on the form, connect them to the datasources on the data module and the form is ready to go. We also need to ensure that the datasets are opened when the form is first shown:
procedure TTablesForm.FormShow(Sender: TObject); begin BlaiseModule.TIssues.Open; BlaiseModule.TContents.Open; end;

Accessing Data
Now that the database has been created, accessing the data can be done using one of three possible components: TAdsTable This TDataset descendant can be used to view all table data. It supports filtering and searching using the standard TDataset properties and methods (Filter and Locate). Two tables can easily be joined together in a master-detail relationship using the MasterSource and MasterFields properties, a well known mechanism from the Delphi BDE and TDBF components. TAdsQuery This component can be used to run a standard SQL SELECT statement if data from several tables needs to be shown in a single dataset, or it can be used to display a selection of records from a single table.

The MDI child form is shown using a menu item in the application's main form, and should look like Figure 3. Figure 3: Browsing the contents of Blaise using TAdsTable components

TAdsStoredProc This can be used to read data returned The application is in fact ready to be used, and it's possible to by a stored procedure (or to execute one if it does not add or edit records in both grids. To make sure that the masterreturn data). detail relationship is respected when a new record is inserted in the TContents dataset, a value is inserted in the Issues field: To demonstrate this we will create a small application. It will connect to the database that was created in the Data Architect. The connection component (of type TAdsConnection and named CBlaise) and the table components are all placed on a data module (named BlaiseModule). This module will contain the methods to create the tables indicated earlier: Issues is a table that contains information about the various issues of Blaise: the magazine number, the date of publication, the issue's theme, and the number of pages. Contents is a table that holds information about the articles in a single Blaise issue: title, author, keywords etc. It is linked to the 'Issues' table using a foreign key (an 'RI Object' in the Data Architect) through the field 'Issue'. For each of these tables, a TAdsTable component is dropped on the module (the components are named TIssues and TContents), and are linked to a set of TDatasource instances (DSIssues and DSContents). Both are also connected to the
TAdsComponent.

The application is a MDI application, and one of the MDI forms (TTablesForm) shows how to browse the issues using a master-detail relationship between the two TAdsTable components. In order to establish a master-detail relationship, you have to set three properties of the TContents table: 104
COMPONENTS
DEVELOPERS

To receive a copy of the printed magazine you need to order a subscription from our website: www.blaisepascal.eu

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Using the Advantage Database Client Engine (continuation 4)


procedure TBlaiseModule.TContentsAfterInsert(DataSet: TDataSet); begin If TIssues.Active then TContentsISSUE.AsInteger:= TIssuesIssue.AsInteger; end;

Figure 4: Using a parameterised TAdsQuery


The 'Issue' column in the grid is hidden, so the user cannot override the inserted value.

Parameterised queries
The same setup for browsing data can be coded using TAdsQuery components. In fact, they are even better suited for such situations: the TAdsQuery component supports parameterised queries, and this mechanism can be used to create master-detail relationships as well. A parameterised query is one which contains a named parameter. Here is an example:
SELECT Issues.Date, Issues.Issue, Issues.Theme, Contents.Title,Contents.Author,Contents. Abstract,Contents.StartPage FROM Contents Left Join Issues on (Contents.Issue=issues.Issue) WHERE Keywords like :Keyword

The ':Keyword' indicates a parameter. Its value is so far unknown, but the engine can already prepare the query, parse it, check for syntax errors, allocate resources and calculate the query plan. This only needs to be done once. The query might be executed many times (each time with a different value for the keyword parameter). Since the engine has already done the preparatory work, it will therefore return a result faster. In TAdsQuery, when one or more parameters are detected in the The ':Keyword' indicates a parameter. Its value is so far unknown, but the engine can already prepare the query, parse it, check for syntax errors, allocate resources and calculate the query plan. This only needs to be done once. The query might be executed many times (each time with a different value for the keyword parameter). Since the engine has already done the preparatory work, it will therefore return a result faster. In TAdsQuery, when one or more parameters are detected in the SQL property, the Params collection is filled with an item for each named parameter. The Params collection is used to hold values for the parameters. When the query is activated, the value of the parameter is fetched from the item in the Params collection and supplied to the database engine. The above query can be used to implement a window to search for articles in the contents table, based on the keyword. Implementing this window is very simple: we just need an edit control (EKeyword), a button (BSearch) and a grid (GSearch). The grid is hooked up to a TADsQuery instance (named QSearch) which is located on the project's datamodule. The button's OnClick event handler contains the following code:
procedure TSearchQueryForm.BSearchClick(Sender: TObject); Var S : String; begin S:='%'+EKeyword.Text+'%'; With BlaiseModule.QSearch do begin if not Prepared then Prepare; Close; Params.ParamByName('KeyWord').AsString:=S; Open; end; end;

We surround the value of the search term entered by the user with wildcards. Then the query is prepared, closed (if it was still open from a previous search run), supplied with the parameter and then opened again. The result of a user entering a search term is shown in Figure 4.
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

105

Using the Advantage Database Client Engine (continuation 5)


Now, how can this be used to establish master-detail relationships using queries? By connecting the query to another dataset using the DataSource property, the TAdsQuery will, when opened, fetch the parameter values (for parameters where no value was given explicitly) from the connected dataset. At the same time, when a user scrolls through the connected dataset, the query component will react to this and re-open itself with new values for all parameters. This can be demonstrated using two query components. The first (QIssues) has an SQL statement like this:
SELECT * FROM Issues

Getting data using stored procedures


We created a stored procedure earlier in this article which essentially performs the same operation as the search query presented above. Using the stored procedure is even more efficient than the query, since it is already analyzed and prepared from the moment the database is created. (There is an even more efficient way than using a stored procedure, which is to use Full Text Search). It is possible to use a parameterised TAdsQuery component to execute the stored procedure and show its results. The following SQL statement would do it:
EXECUTE PROCEDURE KEYWORDARTICLES(:KeyWord)

and the SQL property of the second query (QContents) contains a parameter:
SELECT * FROM Contents where (Issue=:Issue)

The DataSource property of the QContents query is set to a datasource connected to QIssues We can now program a form in exactly the same way as was done with the two TAdsTable components, and the result will look exactly the same. Except this mechanism is vastly more powerful than the mechanism with tables, since the use of parameters allows much more freedom (and normally is also more speed efficient).

procedure TSearchStoredProcForm.BSearchClick(Sender: TObject); begin With BlaiseModule.SPSearch do begin Close; Params.ParamByName ('aKeyWord').AsString:=EKeyword.Text; Open; end; end;

The demonstration application shows this. However, there is a descendant, called TAdsStoredProc, which was created specially to deal with stored procedures, whether they return a result or not. This descendant just needs the name of the stored procedure it should execute or get data from. After that, it behaves like a regular TDataset. The sample application also demonstrates this component. A TADSStoredProc component is dropped on the data module and named SPSearch after which it is hooked up to the connection component. Finally, the name of the stored procedure is set (KEYWORDARTICLES). At this point, the Params property of the component will automatically be filled with the names of the parameters. Note that at the time of writing, TAdsStoredProc lacks support for Unicode string parameters, which is why the stored procedure was declared using CHAR typed parameters instead of NVARCHAR. The form which demonstrates this is identical to the form which searches using a regular query, but is of course hooked to the TAdsStoredProc instance, which means there is a slight difference when passing the parameter prior to opening the dataset: As can be seen, there is no need to enclose it in wildcards. The form will now look and behave exactly like the first search form.
TDataset

Conclusion
Advantage Database Server is a prime candidate for an embedded SQL database solution because it is so easy to deploy (two DLLs suffice for most installations). Added to this, its excellent support for stored procedures and embedded functions makes it very suitable for deployment with Object Pascal applications. The free licensing scheme, easy upgrade path to a complete client/server solution with remote database, and last but not least its strict adherence to SQL typing make it a preferred option compared to alternatives such as sqlite.

106

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Introduction to Databases Part 5: Configuring OLE DB Providers and ODBC Drivers By Cary Jensen
starter expert
DELPHI 5 and above (Win32)

This is the fifth article in a series designed to introduce you to Delphi database development. In part 4 of this series I considered the various database access mechanisms that Delphi supports out of the box. Beginning with this installment, I will focus on some of the individual data access mechanisms in greater depth. This article takes a look at the configuration of drivers for the most versatile data access mechanism supported directly by Delphi, dbGo. I begin with a quick overview, following by a discussion of OLE DB Providers and their configuration. I will then consider the Microsoft OLE DB Provider for ODBC Drivers that is included with every installation of Windows, which will give me the opportunity to show you how you can use ODBC drivers in your Delphi applications. Overview of dbGo As you learned in the preceding article in this series, dbGo is Delphi's trademark for its data access mechanism that employs OLE DB Providers. OLE DB Providers are part of Microsoft's Data Access Components (MDAC), a collection of COM-based technologies available in some form in every version of Windows since Windows 95. Since MDAC is natively available in the operating system, and most database vendors provide an OLE DB Provider for their database, dbGo offers Delphi developers with a convenient mechanism for accessing most databases. Those database vendors who do not supply an OLE DB Provider almost always provide an ODBC driver. Fortunately, Microsoft provides an OLE DB Provider that interfaces with ODBC drivers, making MDAC the most comprehensive data access mechanism available. From the Delphi perspective, dbGo conforms to Delphi's TDataSet interface. This makes it convenient for you to access your data without having to learn an additional interface, specifically ActiveX Data Objects, or ADO. Making the Connection You define how your dbGo components connect to your data using a connection string. All four of the dbGo TDataSet components (ADODataSet, ADOQuery, ADOTable, and ADOStoredProc), as well as the ADOCommand component, have a ConnectionString property. If you assign a properly configured connection string to that property of any of these components they can be activated and used to execute queries and/or stored procedures on your database.

There is another benefit of using an ADOConnection. It permits you to control whether or not the user will be prompted for a username and password before they can successfully connect to the database. When the ADOConnection.LoginPrompt property is set to True (its default), a dialog box is displayed to the user to collect a username and password before a connection is attempted. If the user fails to provide a valid username and password, the connection will fail. On the other hand, you can set LoginPrompt to false if you provide another mechanism for supplying a username and password. For example, you may display a custom dialog box for retrieving the user's name and password. Alternatively, you could hard code the username and password within your application. Obviously, this alternative poses a significant security risk, in that ts permits anybody who has access to the application to access data. In addition, a hard coded password can be hacked, unless you take specific measures to encrypt it within your source code. I'm not going to spend any more time considering the issues associated with passwords and security, other than to acknowledge that it's an important issue that you must take into consideration. Instead, I am moving on to the issue of defining a connection string Using Connection Strings If you are well versed in the configuration of ADO for your particular OLE DB Provider, you could simply write your connection string using any editor (such as Notepad) and assign that text to the ConnectionString property of your ADOConnection. What makes this task especially challenging is that each OLE DB Provider has its own unique set of properties. As a result, a connection string that you use to attach to Microsoft's SQL Server is different from the one you use to connect to IBM's DB2. Fortunately, it is almost never necessary for you to become a connection string expert since Windows provides you with the Data Link Properties dialog box which simplifies the process of creating connection strings. Furthermore, Delphi exposes this dialog box as both a component editor for the ADOConnection component, as well as a property editor for the ADOConnection.ConnectionString property.

To display this dialog box, either right-click an ADOConnection and select Edit Connection or click the ellipsis button that appears when you select the ConnectionString property in the In most cases, though, you will not define a connection string to Object Inspector. Delphi responds by displaying the ConnectionString dialog box, shown in Figure 1. individual dbGo TDataSets. You are more likely to use an ADOConnection component, and then associate your dbGo TDataSets (or ADOCommands) with that one connection through their Connection properties, which are of type TADOConnection. This allows you to configure a single component (the ADOConnection) and share its connection with those other components.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

107

Configuring OLE DB Providers and ODBC Drivers (continuation 1)


I am going to continue this demonstration by selecting the Microsoft Jet 4.0 OLE DB Provider, which you can use to connect to Microsoft Access database (*.mdb) files. This will be convenient, since Delphi installs a Microsoft Access database as part of its installation, and we will use this connection string to connect to it. With the Microsoft Jet 4.0 OLE DB Provider selected, either click the Next button or select the Connection tab. Windows responds by displaying the Connection page of the Data Link Properties dialog box, shown in Figure 3.

Figure 1
You use this dialog box to either enter a connection string (or build it), or to select a data link file. Data link files are discussed later in this article, so I will focus here on building the connection string. To build the connection string, click the button labeled Build. Windows responds by displaying the Provider page of the Data Link Properties dialog box, shown in Figure 2. The Data Link Properties dialog box has four pages. You use the Provider page of this dialog box to select which installed OLE DB Provider you want to use to connect to data. Windows ships with a rather generous collection of OLE DB Providers, including providers for SQL Server, Oracle, and MS Access. These can be seen in Figure 2. If you have installed additional OLE DB Providers, those will appear on this page as well As you can see from Figure 2, I have two custom OLE DB Providers: One for the Advantage Database Server and another for IBM's AS400.

Figure 3
Here you select the database to which you want to connect. If you are using an OLE DB Provider that connects to a remote database, you might have additional options, such as selecting the server on which your database is installed. In this case, you can use the ellipsis button to select the Microsoft Access .mdb file and optionally provide a username and password (assuming that the database is encrypted). To select the MS Access database installed by Delphi, click the ellipsis button () and navigate to C:\Program Files (x86)\Common Files\CodeGear Shared\Data (this directory might be different if you are using Delphi XE2 or later or Delphi 2005 or earlier). Select the dbdemos.mdb file located in that directory and select Open. You may have to supply additional information, but not in this case, since the dbdemos.mdb file is not encrypted. You are now ready to test your connection. Click the Test Connection button. If everything is ok, you will see a dialog box similar to the following.

Figure 2
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18
COMPONENTS
DEVELOPERS

109

Configuring OLE DB Providers and ODBC Drivers (continuation 1)

Figure 4
Select OK to close this dialog box, then select Next or click the Advanced tab to continue to the Advanced page of the Data Link Properties dialog box, shown in Figure 5.

Figure 6
Here is where you can see that OLE DB Provider properties, and the corresponding connection string values, are specifically associated with the OLE DB Provider you selected. If you have selected any other OLE DB Provider from the Provider page of the Data Link Properties dialog box the properties displayed on the All page would be significantly different. Examples of properties that you might encounter here include whether or not to employ a connection pool, the connection pool size, connection timeout, and transaction isolation level, among others. Again, we don't need to make any additional changes in this case, so click OK to accept the connection string you have built and return to the ConnectionString dialog box. The newly constructed connection string will now appear in the Connection String field, as shown here.

Figure 5
You use this page of the Data Link Properties dialog box to define additional connection information, including what locking mechanism you want to use to connect to the database. In this case we don't need to make any changes. Click the All tab to display the All page of the Data Link Properties dialog box, shown in Figure 6.

Figure 7

110

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Configuring OLE DB Providers and ODBC Drivers (continuation 1)


Finally, select OK to close the ConnectionString dialog box. The connection string that you have defined will now appear in the ConnectionString property of the ADOConnection component. If you now connect an ADOTable component to the ADOConnection, and select the ADOTable.TableName property in the Object Inspector, you will see the names of the tables in the dbdemos.mdb file to which the connection string points, as shown in Figure 8. The purpose of a data link file is to store connection string information outside of the application. When setting up the connection string for an ADOConnection component, you can select the data link file by selecting the Use Data Link File radio button on the ConnectionString dialog box (shown in Figure 1). You can then select from one of the data link files that reside in the data link file directory from the provided combo box. Once you have selected a data link file, Delphi assigns this fully-qualified filename to the ConnectionString property using the following form:
FILE NAME=path\filename.udl

When you attempt to connect an ADOConnection component to its database when a data link file is employed, Windows expands the contents of the data link file to a fully formed connection string. Creating Data Link Files Creating a data link file is actually very simple, and is it demonstrated in the following steps. These steps assume that you are running a 32-bit version of Windows, which will surface the 32-bit Data Link Properties dialog box (the one displayed by Delphi XE and earlier). If you are running 64-bit Windows, this technique will display the 64-bit Data Link Properties dialog box, which will only include the 64-bit OLE DB Providers. Begin by opening Notepad as an Administrator. Next, select Save. Set the Save as type dropdown to Any file (*.*) and then proceed to save a file named MyData.udl in the directory returned by the DataLinkDir function. (On my system, I actually had to create this directory.) Once you have saved the file, close Notepad and use the Windows Explorer to navigate to the directory where you saved the file. From there, right-click the MyData.udl file and select Open with | OLE DB Core Services (on older Windows systems it was sufficient to simply select Open). Windows responds by displaying the Data Link Properties dialog box shown earlier in this article. Configure the connection string as you would do normally. When you finally close the Data Link Properties dialog box, the non-default property data is written to the data link file in plain text. For example, if you configured the data link file to use the Microsoft Jet 4.0 OLE DB Provider (as described earlier), the contents of the data link file will look like that shown in the following listing:
[oledb] ; Everything after this line is an OLE DB initstring Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C:\Program Files (x86)\Common Files\CodeGear Shared\Data\dbdemos.mdb

Figure 8
It is worth noting that all of an OLE DB Provider's properties have default values, and the values that appear in the connection string are only for those properties where you did not select the default property value. Using Data Link Files A data link file is a file with a .udl extension which contains the information necessary to connect to a data store using an OLE DB Provider. In most cases, this file is stored in a directory defined in the Windows registry. Fortunately, the ADODB unit in Delphi also surfaces a function, DataLinkDir, which returns the fully qualified path to this directory. In a typical Windows 7 installation, this directory is c:\Program Files (x86)\Common Files\System\OLE DB\Data Links for 32-bit applications, and c:\Program Files\Common
Files\System\OLE DB\Data Links for 64-bit applications.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

Pag 111

Configuring OLE DB Providers and ODBC Drivers (continuation 1)


If you are running Windows 7 64-bit, you can use the following command from the command prompt (run as Administrator) to launch the 32-bit version of the Data Link Properties dialog box. Due to the length of the command, it appears here on more than one line, but it must be entered into the command prompt as a single statement.
C:\Windows\syswow64\rundll32.exe "C:\Program Files (x86)\Common Files\System\OLE DB\oledb32.dll", OpenDSLFile mydata.udl

To do this, navigate to the C:\Windows\SysWOW64 directory. There you will find a program named odbcad32.exe. Run this program to display the ODBC Data Source Administrator for 32-bit ODBC drivers, shown in Figure 10.

Once the data link file has been created, you can use the ConnectionString dialog box to select it, as shown here.

Figure 9
Using ODBC Drivers ODBC is a data access mechanism based on the openSQL call level interface (CLI). It provides a generic mechanism for executing SQL (structured query language) statements against a database. In order to access a particular database using ODBC, an appropriate ODBC driver must already be installed.

Figure 10

One of the more important decisions you will make will be to choose between using a user, system, or file DSN. A user DSN can be only used by the user who is currently logged onto the machine. A system DSN, by comparison, can be used by any user (and this is very important if you are creating a DSN that will be accessed from a non-user account, such as those accessed from a Windows service or an ISAPI Web server extension). Like system DSNs, file While ODBC is a rather old technology (it is just slightly newer DSNs can be used by any user, but the connection information than ODAPI, the Open Database API, now referred to as the is stored in a physical file. Borland Database Engine for Windows), it is nonetheless a There is more to configuring an ODBC DSN, but those topics universal data access mechanism available in Windows. For those are beyond the scope of this article. databases that do not provide an OLE DB Provider, they are almost guaranteed to provide a suitable ODBC driver. Summary While you can still use installed and configured ODBC drivers from the Borland Database Engine, the preferred way to use ODBC drivers is to use them through Microsoft's OLE DB Provider for ODBC Drivers. Once you have chosen the Microsoft OLE DB Provider for ODBC Drivers on the Provider page of the Data Link Properties dialog box (see Figure 2), you can advance to the Connection page to select the Data Source Name (DSN) associated with your installed and configured ODBC Driver. Installation of an ODBC driver is performed by the software provided by the ODBC Driver publisher. Configuration of the DSN is something that you must do (or can be done by your installation program, if it is ODBC aware). If you are running 32-bit Windows, you select the Data Source (ODBC) applet from the Administrative Tools applet on the control panel. If you are running 64-bit Windows, and want to use a 32-bit ODBC driver from a 32-bit Delphi executable, things are a little more complex. Instead of using the Data Source (ODBC) applet from Administrative Tools (which only works with 64-bit ODBC drivers), you must run the 32-bit version of the ODBC Data Source Administrator. 112
COMPONENTS
DEVELOPERS

Delphi's dbGo components permit you to use Microsoft's Data Access Components, including ADO (ActiveX Data Objects), a COM-based mechanism built into the Windows operating system for access your data. While most database vendors provide custom OLE DB Providers for accessing their data, those who do not will likely provide an ODBC driver. Using the Microsoft OLE DB Provider for ODBC drivers, you can also access these drivers using dbGo. In the next installment in this series, I will take a more detailed look at accessing data using ClientDataSets.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 LiveBinding


starter expert
DELPHI XE 2

By Bob Swart The Links group consists of a TBindLink (to link datasets to lists for example), TBindListLink (to link a component to a list), TBindGridLink (to link a component to a grid) and TBindPosition (to keep track of the position). Finally, the Lists group consists of a TBindList (binding components to lists) and TBindGridLists (binding components to grids). For the first example, we can use a simple bind expression between the TEdit and TLabel. Since I want to keep the TLabel synchronised with the TEdit, the obvious choice here is the TBindExprItems class.

Since its very first version Delphi has offered so-called data-aware VCL controls to provide data access. For the FireMonkey Application Platform, Embarcadero decided to implement a different way of data binding, not just for a particular set of data-aware components, but for all visible (and even invisible) components. This technique is called LiveBinding, and you should realize that XE2 carries the first implementation of this, and there will be enhancements in future versions of Delphi. The good news is that LiveBinding is not limited to FMX, but is also available for VCL controls and applications. In fact, it will probably be easiest to start with a VCL example to demonstrate the capabilities of LiveBinding. As an example, create a Delphi XE VCL Forms Application, and place a TEdit and a TLabel component on the form. We can now define a LiveBinding between the TEdit and the TLabel, updating the contents of the TLabel when the contents of the TEdit changes. Since the TLabel is the component that needs to be controlled, we should start there. Select the TLabel component and look for the LiveBinding property in the Object Inspector. If you open the drop-down combobox for the value of the LiveBinding property, you can select the New LiveBinding option. Selecting that will result in a New LiveBinding dialog where we can specify the type of LiveBinding that we want to add to the TLabel control:

Figure 2
This choice will create a BindExprItemsLabel11 instance for the LiveBindings property of the Label1 control. We need to set a number of properties: the SourceComponent should point to Edit1, and the Category should be set to Binding Expressions already. Then, we should define the ClearExpressions (to define what the Label will show initially) and the FormatExpressions (to define how the Label should mimic the Edit value). However, before we define these two Expressions, lets first take a look at the form, and notice that we suddenly have an extra non-visual component: the TBindingsLists. If you double-click on the TBindingsList component, you'll get a list of all LiveBindings, and can select them individually to edit the properties (including the FormatExpressions and ClearExpressions.

Figure 1
There are eight different LiveBinding types available here, divided into three logical groups. The Binding Expressions group consists of a simple TBindExpression (for a binding expression) and a TBindExprItems (for keeping controls synchronized).
To receive a printed copy of the magazine you need to go to our website to place your order

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

113

Delphi XE2 LiveBinding (continuation 1)

Figure 3
Let's define the FormatExpressions now. Click on the ellipsis for the FormatExpressions property in the Object Inspector, which will show another window where we can specify the Control Expression (from the TLabel) as well as the Source Expression (from the TEdit).
procedure TForm5.Edit1Change(Sender: TObject); const {$J+} Busy: Boolean = False; begin if not Busy then begin Busy := True; try BindingsList1.Notify(Sender, '') finally Busy := False end end end;

Figure 4
Here, we can specify property names, like the Caption of the TLabel and the Text property of the TEdit. But we can also stipulate that the TLabel should show the UpperString version of the Text. Right-click in the FormatExpressions list and Add a new item. Set the SourceExpression to Text (from the Edit1) and the ControlExpression to Caption (from the TLabel)

The effect of this additional implementation can be seen immediately. Any change to the TEdit's Text property is immediately reflected in the TLabel's Caption. While this took a very impressive number of steps, we could just have implemented the OnChange event handler of the TEdit as follows:
Label1.Caption := Edit1.Text;

for the same effect. I totally agree, but I just wanted to start with a simple example, before showing some LiveBinding examples that may not be very trivial to implement otherwise. But before we move to other examples, let's try to extend this example a little bit. Instead of simply using the Text as SourceExpression, we can also add expressions here, like
"UpperCase:" + UpperCase(Text)

Note that any literal text should be placed in double-quotes, and we can call a limited number of functions in the Expression, like UpperCase and LowerCase. An almost useful example of LiveBinding can be given for a TMemo that starts with its height equal to a single line, but which grows as soon as the number of lines increases. For this, Figure 5 we have to set both the SourceComponent and the This will be enough to set the Caption of the TLabel to the ControlComponent to the same TMemo, and the Text property of the TEdit. However, if we make changes to SourceExpression to something like Lines.Count * 22, to result in a height of 22 pixels for each line in the Lines the TEdit's Text, we should notify the LiveBinding that property. Of course, we also need to implement the OnChange updates may be required. For this, we can implement the event handler to ensure that the TMemo will actually grow while OnChange event of the TEdit as follows: we're typing. Note that a modification caused by a LiveBinding can cause another change (I'll give an example shortly), which can lead to an endless recursive loop, so it's safest to use a semaphore (called Busy) to ensure that we're not notifying the LiveBindings To receive a printed copy of the magazine if we're already being notified of changes. you need to go to our website to place your order 114
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 LiveBinding (continuation 2)


LiveBinding to a StringGrid A more complex example of LiveBinding involves a StringGrid (not a TDBGrid, but a regular TStringGrid, which is normally hard to bind to a dataset). We should also have a TDataSet filled with data, such as a TClientDataSet connected to the biolife example data. We also need a TBindScopeDB component, connected to the TDataSource which in turn is connected to the TClientDataSet (or any other dataset you want to bind to the StringGrid). We can optionally also enter a FormatColumnExpression, to control how the column should be formatted, for example the width of the column or the title on top of the column. For the column position in the StringGrid, we have to specify Cells with two arguments: the first one for the column, and the second one for the row (this should always be 0 if we want to assign to the header of the StringGrid of course). We can use the Object Inspector to enter two FormatColumnExpressions for the aforementioned examples, as follows:

Figure 8

Figure 6
We should create a new LiveBinding for the StringGrid, and this time select the TBindGridLink type. The SourceComponent of the new LiveBinding should point to the TBindScopeDB component (connected to the dataset). Then, we can define the ColumnExpressions using the ellipsis in the Object Inspector to show the ColumnExpressions dialog. Individual TColumnExpression items have a ColumnName, ColumnIndex (starting at 0), SourceMemberName (the fieldname in the dataset), and a FormatColumnExpressions item. The latter consists of a SourceExpression (from the DataSet) and a ControlExpression (from the StringGrid). To cut a long story short, let's configure the first ColumnExpression as follows: ColumnName = Species No, SourceMemberName = Species No (during the typing of this string, the IDE will try to auto-fill it as Species Name, which comes before Species No alphabetically of course). We also need a FormatCellExpression to specify that the contents of the Species No field will end up in some cell of the StringGrid. So, add a FormatCellExpression with Control Expression = Cells[0] (the left-most cell of the line in the StringGrid) and Source Expression = AsString. This will ensure that for each record in the dataset, the AsString value of the Species No field is shown in Cells[0] of the StringGrid (where each record will get its own row in the StringGrid).

Become BLAISE PASCAL MAGAZINE subscriber ...


A special offer for all Delphi users: Anyone who buys a new DELPHI product and takes out a new subscription for BLAISE PASCAL MAGAZINE (whether for a year or more) will receive the Blaise Pascal Library on a 4GB USB stick for free, including future free updates to include all issues to the end of 2011. The last update will be available in January 2012 through our web service. The free USB stick has approximately 2.8 GB unused capacity. Existing magazine subscribers renewing their subscriptions have two options: - You can buy the Blaise Pascal Library on a 4GB USB stick for the discounted price of 15 (the cost to nonsubscribers is 50) - You can download the Blaise Pascal Library for free as a 1.2GB ISO file for burning to a DVD of your own. This also includes free access to future magazine issues updated to the end of 2011. The last update will be available in January 2012 through our web service. The Blaise Pascal Library is available to non-subscribers on a USB stick, priced at 50 The Blaise Pascal Library contains all issues both articles and code, including program examples, totalling 17 English issues (Issue 1 up to Issue 17), with all the code ever published in Blaise Pascal Magazine: 17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article

Figure 7
SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

115

Delphi XE2 LiveBinding (continuation 3)


We can repeat this for the fields Category, Common_Name, Species_Name, Length_In, etc.. However, rather than using the Object Inspector to start the item's editors each time, we can use a more direct approach now. If we double-click on the TBindingsList component on the form, we get the BindingsList Editor, which looks like the following illustration:

Figure 9
In order to work on the BindGridLinkStringGrid11, we can double-click on that line in the above dialog. This will produce a dialog where we can directly edit the expressions for the Columns of the StringGrid. Right now, only one column exists, for the Species No:

Figure 10
Right-click on the Columns node to add new columns, and then use the Object Inspector to set the ColumnName and SourceMemberName (for example both to Category), and then use the LiveBindings Expressions dialog to select the ColFormat, CellFormat and optionally CellParse nodes, right-click on any of them to add a new item, and edit them using this Binding Expression Editor. For example, right-click on the ColFormat node for the new Category column, and add a new ColFormat expression, this will produce the following display in the Binding Expression Editor:

116

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 LiveBinding (continuation 4)

Figure 11
Right-click on the Columns node to add new columns, and then use the Object Inspector to set the ColumnName and SourceMemberName (for example both to Category), and then use the LiveBindings Expressions dialog to select the ColFormat, CellFormat and optionally CellParse nodes, right-click on any of them to add a new item, and edit them using this Binding Expression Editor. For example, right-click on the ColFormat node for the new Category column, and add a new ColFormat expression, this will produce the following display in the Binding Expression Editor:

Figure 12

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

117

Delphi XE2 LiveBinding (continuation 5)

Figure 13
In a similar way we can add a new Expression item for the CellFormat, set the Control Expression to Cells[1] and the Source Expression to AsString. Leading to the following total of five expressions for the two columns in the StringGrid:

Figure 15

Figure 14
When running this VCL Forms application with an active TClientDataSet, the result is very similar to a TDBGrid, only this time we're actually seeing the data inside a regular TStringGrid. I leave it as exercise for the reader to add the other fields and columns, which shouldn't be hard now. This will take a bit more effort to produce using regular Delphi code (without using data-aware controls), and the usefulness of LiveBindings becomes even more apparent if you realize that FireMonkey, the cross-platform GUI framework, does not have a concept of data-aware controls, but only relies on LiveBindings to put any data in components, including datasets in edits or grids. In fact, FireMonkey makes it even easier, as we'll see when we create a Mac OS X demo project

118

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 LiveBinding (continuation 6)


FireMonkey LiveBindings In order to create a FireMonkey application for the Mac, we should first create a FireMonkey HD Application for Delphi (which initially will be for Win32), and then add a new Target Platform for OSX32 (see my earlier article about the steps to add a FireMonkey OS X project and how to run and deploy it). For the LiveBindings demo, and especially for the one that shows the biolife data in a TStringGrid like the last VCL LiveBindings example, we need to start with a TClientDataSet and a TDataSource, exactly like we did for the VCL version. The biolife example data can be found in the C:\Users\Public\Documents\RAD Studio\9.0\ Samples\Data directory as biolife.xml. However, after assigning the FileName property of the TClientDataSet, we should open the TClientDataSet (by setting the Active property to True), and then clear the FileName property again. Obviously, the Windows fully qualified path will be invalid when run on the Mac. However, with the TClientDataSet open, the data will be stored in the form file (which is .fmx for FireMonkey by the way, where VCL uses .dfm). Make sure to connect the TDataSource to the TClientDataSet, in the usual way, and then place a TStringGrid on the FireMonkey form. The display will be a bit different than usual, with the black border, and the TStringGrid itself will also look a bit different at design-time.

Figure 17
If you click on OK now, then two new components will be created and placed on the FMX form: a TBindScopeDB and a TBindingList (that we saw before in the VCL example). Also, the TStringGrid is filled with the live data from the TClientDataSet:

Figure 16
The TStringGrid has an Align property that works in FireMonkey as well. We get a bit more choices however: alBottom, alCenter, alClient, alContents, alFit, alFitLeft, alFitRight, alHorizontal, alHorzCenter, alLeft, alMostBottom, alMostLeft, alMostRight, alMostTop, alNone (the default), alRight, alScale, alTop, alVertCenter,and alVertical.

Figure 18
For this demo, I've selected alFitLeft. Next, we can open up the LiveBindings property of the TStringGrid, and notice that apart from a choice New LiveBinding (that we saw in the VCL world), there is now also a choice Link to DB DataSource. If we pick the latter, we get a dialog where we can select the datasource (DataSource1). We can even open up the DataSource, to see which fields are exposed and made available by selecting that particular DataSource. Where did the magic come from? If you double-click on the BindingList, you'll see a DB links category (one that isn't available to select in the VCL world), and the DBLinkStringGrid11 that links the grid control StringGrid1 to the source BindScopeDB1 (which was also autocreated):
COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

119

Delphi XE2 LiveBinding (continuation 7)

Figure 19
If you double-click on the DBLinkStringGrid11, you can see all the individual Columns and Expressions that were generated automatically (and were generated by hand in the VCL example).

Figure 20
Finally, to illustrate the fact that LiveBindings are not only more important, but also better implemented for FireMonkey compared to the VCL, if we add a new LiveBinding in a FireMonkey application, the list of available choices is far more extensive, and includes a new category DB Links with TBindDBEditLink, TBindDBTextLink, TBindDBListLink, TBindDBImageLink, TBindDBMemoLink, TBindDBCheckLink, and TBindDBGridLink (the latter was used in our recent example).

120

COMPONENTS
DEVELOPERS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 LiveBinding (continuation 8)


Also, to show the image, I've added a TImageControl, with Align set to alClient, and used the LiveBinding property to create a New DB Link to the Graphic field. BTW, running the application on the Mac OS X would result in an error regarding a missing libmidas.dylib image,

Figure 21

Figure 22
which means we have to add the libmidas.dylib from the C:\Program Files\Embarcadero\RAD Studio \9.0\binosx32 directory to the list of Deployment files: BTW, running the application on the Mac OS X would result in an error regarding a missing libmidas.dylib image, which means we have to add the libmidas.dylib from the C:\Program Files\Embarcadero\RAD Studio\9.0\binosx32 directory to the list of Deployment files:

Obviously, the DB Links are less important for VCL applications since we already have data-aware controls in the VCL. In closing, the last screenshot shows the Biolife table in a FireMonkey form running on Mac OS X. Note that I've added a few more controls. A TBindNavigator with the BindScope set to BindScopeDB1 and the Align property set to alTop. The Align setting will overlap with the TStringGrid with the Align set to alFitLeft. We have to change the Align of the TStringGrid to alLeft to correct that.

Figure 23
Summary In this article, I've shown what LiveBinding is, how it works (using a number of simple to more complex demos) for both VCL and FireMonkey applications. Since the latter does not include data-aware controls, the importance of LiveBindings for FireMonkey is enormous. There may be enhancements and changes to the implementation of LiveBindings (and some extra documentation is also welcome), but for now it certainly is a good start. Bob Swart (Bob@eBob42.com) Bob Swart Training & Consultancy www.bobswart.com

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

COMPONENTS
DEVELOPERS

121

SUMMER OFFER: the complete guide


Blaise Magazine is making a summer deal available to all our subscribers. If you purchase the newly published book (Lazarus the Complete Guide) we will include with it the following bonus items at no extra charge: - a Windows installer CD for Lazarus version 0.9.30 - a preinstalled copy Lazarus on a free 4GB USB stick. - 50 additional sample projects -

LAZARUS

Blaise Pascal Magazine Library

17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article - extra: additional preinstalled component suites

PAPER BACK 50,00 + postage HARDCOVER 60,00 + postage

COMPONENTS
DEVELOPERS

Pag 87

For Android, iOS, Linux, Mac, Win CE, Windows XP / 7

Become

BLAISE PASCAL MAGAZINE


subscriber ...
A special offer for all Delphi users: Anyone who buys a new DELPHI product and takes out a new subscription for BLAISE PASCAL MAGAZINE (whether for a year or more) will receive the Blaise Pascal Library on a 4GB USB stick for free, including future free updates to include all issues to the end of 2011. The last update will be available in January 2012 through our web service. The free USB stick has approximately 2.8 GB unused capacity. Existing magazine subscribers renewing their subscriptions have two options: - You can buy the Blaise Pascal Library on a 4GB USB stick for the discounted price of 15 (the cost to non-subscribers is 50) - You can download the Blaise Pascal Library for free as a 1.2GB ISO file for burning to a DVD of your own. This also includes free access to future magazine issues updated to the end of 2011. The last update will be available in January 2012 through our web service. The Blaise Pascal Library is available to non-subscribers on a USB stick, priced at 50 The Blaise Pascal Library contains all issues both articles and code, including program examples, totalling 17 English issues (Issue 1 up to Issue 17), with all the code ever published in Blaise Pascal Magazine: 17 complete issues on a 4GB USB stick 850 pages of articles very fast search facility comprehensive index covering all issues locate the article of interest with one click separate code index separate index by author overall index for complex searches covering all articles all code completely accessible, linked to the relevant article Here are the new company subscription prices: Blaise Pasacal Magazine is now published 6 times a year (bimonthly). The magazine has a minimum of 60 full colour pages. Company subscription rates: single person annual subscription by download 35 5-person annual subscription by download 150 10 -person annual subscription by download 300 50 -person annual subscription by download 1125 100 - person annual subscription by download 2250 single person annual subscription for printed copies 50 (excluding postage) 5-person annual subscription for printed copies 225 (excluding postage) 10-person annual subscription for printed copies 425 (excluding postage) 50-person annual subscription for printed copies 2250 (excluding postage) 100-person annual subscription for printed copies 4250 (excluding postage)

David I

Marco Cant

Delphi-Tage 2011 in Cologne (Kln) The meanwhile seventh Delphi - Days in Cologne- Gremany took place during the weekend of 8 - 10 September 2011 in the so called MediaPark in Cologne. A large part of the visitors arrived at the Community-night (Friday) had an incredible enjoyable dinner in the Restaurant "Frh am Dom". Impressive were the steps you needed to go up and down in the verry old catacombs, the beer was hard needed - it was incredible warm that day. I met a lot of the very international crowd over there. It was a very good organized and meaningfull meeting: 280 vsistors only here!