Blaise 18 Uk to Taal Free

126
18 18 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 ALL ABOUT DELPHI AND DELPHI PRISM(.Net) , LAZARUS & PASCAL AND RELATED LANGUAGES Pascal SPECIAL ISSUE 126 PAGES 's the code I need to call? t a h w , l l a w e h t n o r o r r i m , r o r r i M Cylindrical anamorphosis / Chinese Nightingale James Duff Howard Page-Clark Fikret Hasovic David Dirkse Detlef Overbeek Daniele Teti Alexander Alexeev Bob Swart ergey Lyubeznyy Henk Schreij Jeremy North Kim Madsen PrimozGabrijelcic Bruno Fierens Felipe Monteiro de Cavalho Detlef Overbeek Michaël Van Canneyt Cary Jensen Bob Swart Book review Writing New Components in Lazarus, Part 2 Cylindrical anamorphosis Web Service Toolkit as a rich type framework for Plug-in development Polygon colouring and area calculation - part 1 An Android client for a DataSnap Server – Part 2 Anti-freeze for the VCL Learning to use FastReport 4 for VCL - Part 3 Question and Answers kbmSQL – Structured Query Language for your memory table High Level Multithreading TMS Scripter Studio Pro & FreePascal vectorial The tale of the Nigthtingale Using the Advantage Database Client Engine Configuring OLE DB Providers and ODBC Drivers Delphi XE2 is the future Delphi XE2 New Features Creating 64-bit applications with Delphi XE2 Delphi XE2 LiveBinding - theme XE2 - theme XE2 - theme XE2 - theme XE2 Peter Bijlsma Inoussa Ouedraogo S Wagner R. Landgraf Creating a Database program from scratch

description

delphi programming magazine

Transcript of Blaise 18 Uk to Taal Free

Page 1: Blaise 18 Uk to Taal Free

1818

September 2011Publisher: Foundation for Supporting the Pascal Programming Language

in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep)© Stichting Ondersteuning Programmeertaal Pascal

BLAISE PASCAL MAGAZINEALL ABOUT DELPHI AND DELPHI PRISM(.Net) , LAZARUS & PASCAL

AND RELATED LANGUAGES

Pascal

SPECIAL ISSUE 126 PAGES

's the code I need to call?ta

hw ,lla

w eht no rorrim ,rorri

M

Cylindrical anamorphosis / Chinese Nightingale

James Duff Howard Page-Clark

Fikret Hasovic

David Dirkse Detlef Overbeek

Daniele TetiAlexander Alexeev

Bob Swartergey Lyubeznyy

Henk Schreij Jeremy North

Kim Madsen PrimozGabrijelcic

Bruno FierensFelipe Monteiro de Cavalho

Detlef OverbeekMichaël Van Canneyt

Cary JensenBob Swart

Book review Writing New Components in Lazarus, Part 2

Cylindrical anamorphosis Web Service Toolkit as a rich type framework for Plug-in development

Polygon colouring and area calculation- part 1

An Android client for a DataSnap Server – Part 2 Anti-freeze for the VCL

Learning to use FastReport 4 for VCL - Part 3 Question and Answers

kbmSQL – Structured Query Language for your memory table High Level Multithreading

TMS Scripter Studio Pro & FreePascal vectorial

The tale of the Nigthtingale Using the Advantage Database Client Engine

Configuring OLE DB Providers and ODBC Drivers

Delphi XE2 is the future

Delphi XE2 New Features

Creating 64-bit applications with Delphi XE2

Delphi XE2 LiveBinding

- theme XE2

- theme XE2

- theme XE2

- theme XE2

Peter Bijlsma

Inoussa Ouedraogo

S

Wagner R. Landgraf

Creating a Database program from scratch

Page 2: Blaise 18 Uk to Taal Free

2 COMPONENTSDEVELOPERS 4

Advertisers

Subscription info

Advantage Database Server

Cary Jensen’s newest book Components for Developers page 71Database Workbench Pro- Upscene Productions page 45Embarcadero FastCube FastReport LAZARUS Book page 60TMS Software

page 3AnyDac page 70Barnsten page 98Be-Delphi page 124BitTime page 36

page 106

page 99, 100page 63

page 62

page 89

page 61, 115

Editor - in - chief

News and Press Releases

Authors

Editors

Copyright Trademarks

Caveat

Subscriptions

ABN AMRO Bank

Subscription department

Detlef D. Overbeek, NetherlandsTel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68

email only to

N Jeremy North,O Tim Opsteeg,

Howard Page-Clark, R Michael Rozlog,

Peter Bijlsma,

Howard Page-Clark, James D. Duff

( 2011 prices )

(including code, programs and download magazine)

or by written order, or by sending an email to

Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.

[email protected]

www.blaisepascal.eu

[email protected]

[email protected]

A Alexander

Account no. 44 19 60 863 or by credit card: Paypal or TakeTwoName: Pro Pascal Foundation-

NL82 ABNA 0441960863ABNANL2A VAT no.: 81 42 54 147

(Stichting Programmeertaal Pascal)

Alexeev

L Wagner R. Landgraf, Sergey Lyubeznyy

Inoussa Ouedraogo

BC Marco Cantù, D David Dirkse,

G Primož Gabrijelčič,H

P Herman Peeren,

S Henk Schreij, Rik Smit, Bob Swart,

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

Page 118

All trademarks used are acknowledged as the property of their respective owners.

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.

1: Printed version: subscription € 60.-- (including code, programs and printed magazine, 4 issues per year including postage).

2: Non printed subscription € 35.--

Subscriptions can be taken out online at

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:

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

Peter Bijlsma, Michaël Van Canneyt,

Daniele TetiF Bruno Fierens

Fikret HasovicJ Cary Jensen

M KIm Madsen, Felipe Monteiro de Cavalho

Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)

Correctors

Subscriptions run per calendar year.

IBAN:BIC

Volume 18, ISSN 1876-0589

BLAISE PASCAL MAGAZINE 18

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

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Articles

James Duff page 4

Howard Page-Clark page 6

Fikret Hasovic page 13

page 17

page 21

David Dirkse page 24

Detlef Overbeek page 29

Daniele Teti page 34

Alexander Alexeev page 39

Bob Swart page 46

ergey Lyubeznyy page 53

Henk Schreij page 16

Jeremy North page 64

Kim Madsen page 72

PrimozGabrijelcic page 76

Bruno Fierens page 81

Felipe Monteiro de Cavalho page 90

Detlef Overbeek page 96

Michaël Van Canneyt page 101

Cary Jensen page 107

Bob Swart page 113

Book review

Writing New Components in Lazarus, Part 2

Cylindrical anamorphosis

Web Service Toolkit as a rich type framework for Plug-in development

Polygon colouring and area calculation

- part 1

An Android client for a DataSnap Server – Part 2

Anti-freeze for the VCL

Learning to use FastReport 4 for VCL - Part 3

Question and Answers

kbmSQL – Structured Query Language for your memory table

High Level Multithreading

TMS Scripter Studio Pro &

FreePascal vectorial

The tale of the Nigthtingale

Using the Advantage Database Client Engine

Configuring OLE DB Providers and ODBC Drivers

Delphi XE2 is the future

Delphi XE2 New Features

Creating 64-bit applications with Delphi XE2

Delphi XE2 LiveBinding

- theme XE2

- theme XE2

- theme XE2

- theme XE2

Peter Bijlsma

Inoussa Ouedraogo

S

Wagner R. Landgraf

Creating a Database program from scratch

Page 4: Blaise 18 Uk to Taal Free

4 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Book Review

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:

"Lazarus is an integrated development environment (IDE) for the Object Pascal Language. The Free Pascal Compiler provides the backend for Lazarus. This combination of backend engine and integrated development environment is comparable to Visual Studio for C++ or Borland Delphi. It yields efficient, highly optimised native source code. Whereas Java and C# development environments produce bytecode that must be interpreted in a runtime environment or converted into poorly optimised code by a JIT compiler."

Go Pascal!

ReviewThis is a large book that contains 735 pages comprising 12 chapters. A CD containing the source code is also provided with the book, as well as a free USB stick that contains an installed version of Lazarus - just plug it in and go.

The book's chapters are listed here together with a small summary of each to give an idea of the book's coverage.

1: The Architecture of LazarusThe 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 LazarusInstalling Lazarus for Windows, Linux, FreeBSD and MacOS X, plus installation of version management systems: TortoiseSVN and Subversion

3: The IDEUsing the Lazarus IDE (Integrated Development Environment) for rapid application development, explained in detail

4: ProjectsDeveloping different types of projects - GUI (Graphical User Interface), console and unit test applications, and using packages to develop new components

5: Target PlatformsUnderstanding the differences among the platforms Lazarus supports - Windows 32 and 64, Windows CE, Unix Systems (FreeBSD) and MacOS X

6: The Class LibrariesThe class libraries available: RTL, FCL and LCL, and a description of the functionality supplied with Lazarus in its two hundred components.

7: Porting Delphi ComponentsHow to port existing Delphi components to Lazarus (where possible)

8: Files and DevicesWorking with files, directories, serial and parallel ports, and printers.

9: Graphics ProgrammingLazarus graphics programming - working with images and icons, the necessary basics for making attractive interfaces in this day and age

10: Processes and ThreadsOperating system processes, and programming with multiple threads - functions that highlight the ability to make the compiled software operate efficiently

11: Network ProgrammingWeb services, client/server and TCP/IP network programming

12: Database AccessWorking with flat-file and client/server databases (via a unified database interface to DBF files, Firebird, Interbase, MySQL, Oracle, PostGreSQL, SQLite and ODBC), SQL and reporting (based on FastReports)

Background

LAZARUS – The Complete Guide

James D (Jim) Duff

Page 5: Blaise 18 Uk to Taal Free

5SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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.

The chapters are presented in a logical courseware type layout and sequence for beginners to the Pascal language, one of the key target audiences of Lazarus. It also provides sufficient details for experienced Pascal/Delphi programmers wishing to utilise the benefits of Lazarus.

One has a feeling of the good old days when manuals provided the reader with a logical and educational theme for learning about particular software products, such as Turbo Pascal or Delphi.

This book has been generated by a group of highly skilled contributors and, together with the other positive benefits mentioned above, is highly recommended for use by both beginners and experienced Pascal developers.

Am I going beyond the subject of reviewing the book 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 another positive element. Hmmm, I think this book has done its job on me.

Summary

The BookLAZARUS – The Complete GuideIncluding CD

Authors:M. van Canneyt, M. Gärtner, S. Heinig, F. Monteiro de Cavalho, I. OuedraogoDevelopment Editor, Production Editor and Cover Designer: Detlef OverbeekCopyright © 2010 by C&L Computer und LiteraturverlagCopyright © 2011 All rights for the English version reserved by Blaise Pascal Magazine

ISBN: 978-94-90968-02-1 for the paperbackISBN: 978-94-90968-03-8 for the hardcover

USB STICK

Update Service

There is also an additional Lazarus USB STICK. It contains a preinstalled copy of Lazarus on a free 4GB USB stick. 50 additional sample projectsThe book includes the folowing example programs:

B: borderstyle\ borderwidth \ bouncerD: dbftrack\ dbfviewer\ dialogdemo\ docking

dragdrop\ dragdropobject\ dynamicformsE: editordemo\ eventsdemoF: filedragdrop\ formeventsG: gentrackdata\ griddemoI: ibtracker\ initialwindowposition\ inputqueryL: lazreport\ listviewdemoM: menudemo\ messagdlg\ morecontrolsP: pacman \ paramsdemo \ pinguinQ: questiondlgS: showmessage\ sizing-samples\ sizingwindows

splitterdemo\ splitterpairdemo\ statusbardemoT: toolbardemo\ traffic light \ treeviewdemoW: windowmenu\ windowstate

Additional preinstalled component suites on the USB Stick:Additional /Chart / Common / Controls / DataAccess / DataControls / Dialogs / Indy Misc - Core / Intercepts- Core /IO Handlers / Servers Core / Clients core / Misc Protocols Decoder / Misc Protocols Encoder / Misc Protocols / IO Handlers Protocols / IO SASL Protocols / IO Intercept Protocols / IO Servers Mapped Port / IO Servers Protocols (nz) / IO Servers Protocols (am) / Clients Protocols (am) / Clients Protocols (nz) Inet / Ipro / LazControls/ Lazreport / Misc / RTTI / SQLDB / Standard / SynEdit System / VE

there is an update service available that ensures you have the latest version. It cost only € 25, for a maximum of 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.

Book ReviewLAZARUS – The Complete Guide (continuation)

COMPONENTSDEVELOPERS 4

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

Page 6: Blaise 18 Uk to Taal Free

6 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

By Howard Page-ClarkWriting New Components in Lazarus, Part 2

The first article in this series looked at the Lazarus component hierarchy, and some properties of TControl and TComponent that are important for a background understanding of component development. This subsequent article focuses on the stepwise development of a specific simple component.

You write a new component to fill a gap in the functionality of the components available to you. You need some GUI (or other) functionality that you can't find on the Lazarus Component Palette, so you wonder about creating a new component that does meet your need. The default Component Palette found on a newly installed latest-release Lazarus (0.9.30) has 13 pages containing 186 components in total. But that is not enough! Perhaps you find a component that almost – but not quite – does what you want.Say you are working primarily on Windows and want to use buttons that can be shown in subtly different colours. Although a TButton's Color property can apparently be 'changed' in the Object Inspector to something other than clDefault, you discover that it always gets reset to clDefault! Checking the Object Inspector's Restricted page confirms that a Windows widgetset restriction applies: the native Windows button-widget's colour cannot be changed. You search beyond the Standard page. But neither TBitBtn, TSpeedButton (Additional page) nor TColorButton (Misc page), nor TTIButton or TTIColorButton (TTI page) suit your purpose, in spite of their hopeful button-containing names.In fact in this case there is a custom drawn component that fits the bill exactly. Felipe Monteiro de Carvalho and JiXian Yang have written several custom drawn Lazarus components. These include a TCDButton, which provides button colour-changing functionality on all Lazarus-supported platforms inclu-ding Windows. For more information on these components see

In a standard Lazarus Windows installation you'll find the package from which you can install these components atC:\lazarus\components\customdrawn\customdrawn.lpkIn this particular case you can take advantage of the work of other developers which they have made freely available. But supposing we can't find the component functionality we need? Then we have to roll our own.

http://wiki.lazarus.freepascal.org/Lazarus_Custom_Drawn_Controls

Setting properties to make a 'new' componentThe simplest way to customise an existing component to better suit your purpose is to pre-set the default properties of that component, and save this altered component as a 'new' component on the Lazarus Component Palette (with a slightly altered name, of course, since component names must be unique). If you find yourself time and again dropping a TLabel on a 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 to appear as a new item in the IDE's Component Palette.

A little bit of work up-front in creating and saving the package, setting the component's properties and recompiling the IDE will save you a lot of repetitive work in the future (until you change company, that is; although the component will continue to benefit other employees).If you've never created a new component in Lazarus before, this is a good way to familiarise yourself with the process, 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 a series. There's plenty of scope for component complexity ahead – but let's begin simply. If you don't want this component installed long-term, it is easy enough to remove it from the IDE later. You can walk through the construction stages simply as a learning process. Subsequently you can remove the component by recompiling Lazarus without the newly added package (and delete the CompanyLabel package directory altogether if desired). Note that in Lazarus (unlike Delphi) new components can be introduced into a Component Palette page only by recompiling the Lazarus IDE, since the new component is linked statically with the rest of the IDE code. There is no provision (currently) for a dynamic package system of component dlls such as Delphi uses (these are .dpl and .bpl files together with .dcp files, which in versions from Delphi 2 up to Delphi XE have all been Win32-specific, and depend on compiler 'magic' to work dynamically).

The basic steps of new component creation/installationWe will illustrate the basic steps involved in installing a new Lazarus component from scratch by creating the simple TCompanyLabel component suggested above, and then consider how these basic stages need to be amplified when writing a more sophisticated, more widely useful component. If you don't want to have your component installed in the IDE (employing its functionality merely by adding its unit to your application's uses clause and instantiating it through code) that is certainly possible. We will look at that scenario in due course. However, the full process of installing a visual component into the IDE that can be manipulated by both the Object Inspector and the Form Designer requires registration of the component with the IDE. To accomplish that, you need to follow these steps:• Decide on your new component's ancestry and name

(Optionally) design an icon to identify your component on the PaletteCreate a new folder for the new component packageCreate a new component packageWrite (or adapt) the component unit as appropriate to provide new functionalityTest the new component's functionality before installation, debugging as requiredCheck the new package's integrity by doing a test compilationInstall the package in the Lazarus IDE by recompiling the IDETest the component's IDE functionality after installation, debugging if needed

•••

••

expertstarter

Figure 1: Two indistinguishable icon-less components added to the Common Controls page

Lazarus 9.30

COMPONENTSDEVELOPERS 4

Page 7: Blaise 18 Uk to Taal Free

7COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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

TWinControlTCustomControlTCustomxxxx

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 mouse-clicks 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 IdentityTo 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

Tools→Options… menu).

The requirement for a unique name needs careful consideration. A long-standing convention names new components with a two- or three-letter prefix to identify the component's author or owning company, such as the TkbmSQL component featured in this issue from Components4Developers ( . 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:

see page 60)

..\ \ \ \ \ \ \ \ \ \ \

ComponentPackageDirectory sourceimageslanguagestestsdocs

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-a-single-folder approach is a recipe for later name conflicts!

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

Creating a new component packageLazarus' 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.

Page 8: Blaise 18 Uk to Taal Free

8 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 2: The New… dialog accessed from the File→New… menu

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).

Figure 4: The newly created (empty) companylabel.lpk package open in the Package Editor

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 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):

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 2), or you can access it from the Package→New package menu (Figure 3).

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

Page 9: Blaise 18 Uk to Taal Free

9COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

If you're familiar with Delphi's New Component wizard, you'll appreciate that analagous pages from that wizard are compressed here (sensibly) into a single page of required information. The Lazarus version of its Delphi equivalent lacks the colourful designs that have been lovingly lavished on the Delphi wizard, 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 mandatory information that the component writer has to supply, and optionally you can specify a sixth, an icon. When you drop down the Ancestor Type combobox's list, you'll find that it lacks any TCustomXXX entries apart from TCustom Control. So so if you wanted to use say, TCustomLabel, you'd have to type it in manually (you're not restricted to classes only found in the drop-down list), and if it exists Lazarus will find it without any problems (unless you spelled it incorrectly). We want to use the standard TLabel, so can just select it in the drop-down list. Lazarus then fills in the other fields with some defaults, which you will probably want to change. If you type the name of a non-existent Palette Page: entry, Lazarus will create that page for you, and place your new component on the newly added page. To add an icon you click on the ellipsis[…]button labelled Icon (maximum 24x24) which lets you locate a .png icon file. Note that this must be named with the new component's class name (so tcompanylabel.png in this case). The completed New Component page looked as follows, before clicking the [OK] button:

Figure 7: The information needed to complete the New Component page

When specifying the Unit File Name: information there is an intruigingly labelled [ <> ]button beside the ellipsis [ … ]button (that brings up a Save As dialog). The [<>] button is a Shorten or expand button which usefully shows either the short filename (source\companylbl.pp here) or the expanded filename with the full path. This affects the name display only. Lazarus saves all files with fully qualified paths, however much of that path is actually displayed in the dialog. Clicking the [OK] button creates a new companylbl.pp in the location you have specified. If the package has not registered any of the paths you've just entered an intermediate dialog will pop up asking if you want the package to add the paths (to which you should agree).

Figure 8: A confirmation dialog for changing the unitpath

Lazarus then opens the newly created companylbl.pp file in the Source Editor. You'll find that it contains the following skeleton code written for you by Lazarus:unit

interface

uses

typeclass

private

public

published

end

procedure Register

implementation

procedure Registerbegin

end

end

;

, , , , , , , ;

= ( ) ;

;

;

( ,[ ]);

;

.

CompanyLbl

Classes SysUtils LResources Forms ControlsGraphics Dialogs StdCtrls

TCompanyLabel TLabel

protected

RegisterComponents TCompanyLabel

{$mode objfpc}{$H+}

{ Private declarations }

{ Protected declarations }

{ Public declarations }

{ Published declarations }

{$I Companylbl_icon.lrs}'Additional'

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

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 …} directive which has inserted a Lazarus-generated resource file (based on the icon you specified) named Companylbl_icon.lrs, and places the registered component on the Palette page given earlier via the RegisterComponents()procedure.Of course you could write all this code yourself, but it is easier and less error-prone to use the New Component page of the Package Editor to do it for you. If we look at the Package Editor

now (via the Window → Package CompanyLabel* menu) we see that Lazarus has now added the LCL in the Required Packages treeview node (TLabel obviously needs the LCL) and the component file \source\companylbl.pp as a package file, as we would expec (see Figure 9). If we highlight the companylbl.pp file you'll see in the File Properties section near the bottom of the Package Editor 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.

Page 10: Blaise 18 Uk to Taal Free

10 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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.

Customising the component codeFor 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.

TCompanyLabel TLabel

SetDefaults

Create TheOwner TComponent

TCompanyLabel Create TheOwnerTComponent

Create TheOwnerSetDefaults

TCompanyLabel SetDefaults

CaptionAutosize TrueAlignment taCenterColor clMoneyGreenParentColor FalseFont Color clMaroonParentFont FalseFont NameFont Style fsBold fsItalicFont HeightBorderSpacing InnerBorderBorderSpacing AroundConstraints MinHeightConstraints MinWidthHintShowHint TrueTransparent False

= ( ) ; ( : );

; ;

. ( : );

( ); ;

;

. ;

:= ; := ; := ; := ; := ; . := ; := ; . := ; . := [ , ]; . := - ; . := ; . := ; . := ; . := ; := ; := ; := ;

;

classprivate

procedurepublic

constructoroverride

end

Constructor

BeginInherited

End

ProcedureBegin

192123140

End

The new constructor and SetDefaults procedure are as follows:

'Timeless Limited'

'Calibri'

'Timeless can meet all your software needs'

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.

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).

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

Figure 10: Selecting the Package Editor's Install menu option

Page 11: Blaise 18 Uk to Taal Free

11COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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.

Well, almost all the properties you want. Disappointingly, not every property we set via SetDefaults in the component's constructor has come through. The Caption, which we set in code to be 'Timeless Limited' has been overridden by Lazarus to become 'CompanyLabel1' (see Figure 13).

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:

ProcedureBegin

End

. ;

:= - [ ]; := ;

;

TCompanyLabel SetDefaults

ControlStyle ControlStyle csSetCaptionCaption 'Timeless Limited'// … etc.

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

Figure 14: TCompanyLabel atdesign time and runtime

Finishing touches and removalWe 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 crashes.If we open the component package now in the Package Editor and highlight the companylbl.pp source file, we see that the previously empty Registered plugins region is now updated to reflect the registration of this new component with the IDE. The name of the registered plugin is shown (TCompanyLabel)with its icon, as well as the Component Palette page where it is located (the Additional page).

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

Figure 13: The newly dropped component with the 'wrong' caption

Figure 12: The Additional page of the Component Palette sporting a new component icon

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.

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 16: Getting rid of an unwanted component plugin

Page 12: Blaise 18 Uk to Taal Free

12 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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 EditorThe 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.

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.

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

Figure 17: Confirmation is required before component removal and IDE recompilation

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

Figure 19: The Package Options's IDE Integration page

The authorHoward 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.

Page 13: Blaise 18 Uk to Taal Free

13COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Delphi XE2 is the future

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.

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 cross-platform 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.

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.

- The FireMonkey Application Platform for cross-platform applications that run on both 32-bit and 64-bit Windows, Mac OS X, and iOS. The VCL now supports 64-bit and 32-bit Windows, and the Delphi RTL now supports Mac OS X and both 32-bit and 64-bit Windows.

Key features of the XE2 release:

To create Cross-Platform applications you follow these steps…Compiling and building, but not running, cross-platform applications is similar to the same operations for building native Windows applications, with a few additions. Running and debugging a true cross-platform application requires that the development system be connected to the target platform, where the Platform Assistant (the remote application server) is running in listening mode.

- 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).

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

by Fikret Hasovic

Figure 1 Th splash screen of Delphi XE2

expertstarter DELPHI XE2

2

Page 14: Blaise 18 Uk to Taal Free

14 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

The name of the active platform is boldfaced in the Project Manager. The native Win32 platform is the default active platform.

Add a target platform (right-click the Target Platforms node, and click Add Platform):

To make the current project a cross-platform project

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

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.

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:

essages System SysUtilsSystem Variants System Classes Vcl GraphicsVcl Controls Vcl Forms Vcl Dialogs

An example FireMonkey uses clause:

System SysUtils System Types System UITypesSystem Classes System Variants FMX TypesFMX 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):

A comparison of the VCL and FireMonkey…

uses

uses

, . , . , . , . ,

. , . , . ;

. , . , . , . , . , . ,

. , . , . ;

Winapi.Windows, Winapi.M

Figure 6 The FireMonkey designer (cross-platform):

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:

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:

Figure 3

Figure 4

Figure 5

Delphi XE2 is the future (continuation 1)

Page 15: Blaise 18 Uk to Taal Free

15COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

DataSnap Mobile Connectors in RAD Studio XE2A 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.

Figure 7

Delphi XE2 is the future (continuation 2)

Page 16: Blaise 18 Uk to Taal Free

16 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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-one-to-one with the Delphi version, but similar.

LiveBindings is a data-binding feature supported by both the VCL and FireMonkey in RAD Studio. LiveBindings is expression-based, meaning that it uses expressions to bind objects to each other, by means of their properties.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 LiveBindings propagate, you can even alter properties of objects that are connected to other objects that are bound to a control object. This type of functionality is no longer restricted just to data-aware components.

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

Deployment can have two different meanings in RAD Studio:

· Deployment is a required step that the IDE automatically performs when you run your cross-platform application in the debugger.

· Deployment is the final step that you perform in successfully delivering any completed project.

LiveBindings in RAD Studio XE2

Delphi XE2 Compiler improvements

Lastly, how do we deploy cross-platform applications?

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 executable, but a set of object files and shared libraries (dylibs).I am impressed with how stable and good this XE2 release feels... Thumbs up! We have a very exciting time ahead of us!

Delphi XE2 is the future (continuation 3)

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 can’t get focus. If you need the button to get focus, there is a further solution. Take a Panel component, sized to match theBitButton (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.

Q&AQUESTIONS AND ANSWERS

& USEFUL CODE SNIPPETS

#4 How to #show a hinton an disabled button

Author: Henk Schreij

For the code of the complete project see downloads:www.blaisepascal.eu

Page 17: Blaise 18 Uk to Taal Free

17COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Cylindrical Anamorphosis by Peter Bijlsma

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.

Constructing anamorphic picturesCylindrical 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 1: An anamorphic image

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 3: Calculation of horizontal component Ihor

expertstarter DELPHI 3 and above (Win32)

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)

Page 18: Blaise 18 Uk to Taal Free

18 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

procedurevarbegin

if 0 then 360if thenif then

if 0 then begin22

end else begin

endif 0 then

if 0 thenif 0 thenif 0 then

end

. ; , , , , , , , , : ;

< := + ; < := ; > := ; := ( ); := * ( ); := * ( ); := + ; := - ; := * / ; := ( / ); = := / ; := / ; := ( ( / )); := ( ( / )); ; := - ( + ); < := - ; := - ; < := + ; := * ( ); < := - ; := * ( ); < := - ; := + ; := + ;

;

TForm1 ConvCylToImgHdis Vdis A1 A3 A4 A5 A6 Ia Ib double

Pa Pa PaPa PaMin Pa PaMinPa PaMax Pa PaMax

Par DegToRad PaCx R sin ParCy R cos ParHdis Ed CyVdis Eh PvIhor Ed Pv VdisA1 ArcTan Cx Hdis

CxA3 PiA4 Pi

A3 Abs ArcTan Cy CxA4 Abs ArcTan Hdis Cx

A5 Pi A4 A3 A1 A5 A5A6 A5 A3 A1 A6 A3 A5Ihx Ihor Cos A6 A1 Ihx IhxIhy Ihor Sin A6 A1 Ihy IhyIx Ihx CxIy Ihy Cy

{convert cylinder coordinates to image coordinates on paper}

Figure 5: The user interface after Hello has been clicked

Figure 4: Calculation of Image points Ix and Iy

The ProgramThe 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:

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:

Cylindrical Anamorphosis (continuation 1)

Page 19: Blaise 18 Uk to Taal Free

19COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

I added a [Demo] button to demonstrate the reflection 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 most interesting part of the program is its capability of automatically converting an existing picture (e. g. a photograph) into its anamorphic image. To do this you load a picture (jpg or bmp) by clicking the [Load Pict] button, and then convert it by clicking [Make Image]. You can alter several conversion parameters in the provided EditBoxes: the Cylinder radius (R), the Cylinder height (PvMax) and the Eye distance (Ed and Eh, equal values). You save the converted image by clicking the [Save Image] button. To print the image you'll have to make use of a separate program.

Figure 6: Fitting the picture in the available area

= : ; : ; : ; ;

TCylPntAngle doubleHeight doubleNew Boolean

record

end

//angular position Pa//vertical position Pv//beginning of a new line

Each coordinate pair Pa/Pv is converted to a plotting point (Ix/Iy) by calling the procedure ConvCylToImg. The drawing is then made by connecting the points on the image using LineTo commands. When the Boolean MirrPnts.New has been set to True for any given point, a MoveTo command is executed to start a new line. Magazine subscribers will find the details in the source code listing, which is available on the website.

www.blaisepascal.eu

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 morphing process, a calculation is made to fit the picture efficiently on the available cylinder space. Using the tangents from the eye to the cylinder (Ihor then forms a straight line from the viewing direction in Figure 4),a maximum and minimum angle for Pa can be calculated and therefore also the maximum width (Wmax) of the picture wrapped around the cylinder. These values are displayed on the interface. The picture to be converted is centred on the available area in such a way that for “portrait” type pictures the height is decisive and for “landscape” type pictures the width is decisive (see Figure 6 down left).

The procedure CheckFormat calculates the high and low values for the angular position Pa (Pahi and Palo), and for the vertical position Pv (Pvhi and Pvlo):

procedurevarbegin

if then begin

22

end else begin

0180

2

endend

. ; , , , : ;

:= / ; := . / . ; < := ; := ; := / ; := * /( * ); := + ; := - ; := ; := ; := ; := ( - )* /( * ); := + ; := - ; ;

;

TForm1 CheckFormatTemp Mid HVRatio BMRatio double

HVRatio Wmax PvMaxBMRatio BitmapOrg Width BitmapOrg Height

HVRatio BMRatioPahi PaMaxPalo PaMinMid PvMaxTemp PvMax HVRatio BMRatioPvhi Mid TempPvlo Mid Temp

Pvhi PvMaxPvloMidTemp Pamax PaMin BMRatio HVRatioPahi Mid TempPalo Mid Temp

Cylindrical Anamorphosis (continuation 2)

Page 20: Blaise 18 Uk to Taal Free

20 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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 ScanlinesThe 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

record

endarray 0..6000 of

vararray of

TPixColB byteG byteR byte

TPixArray TPixCol

pPixArray ^TPixArray

ScanlCyl pPixArray

//sequence is BGR in scanlines

//a length of 6000 bits is sufficient//scanlinepointers

Now the following code snippet :

:= . ; ( , ); := - [ ] := . [ ];

NumbScl BitmapOrg HeightSetLength ScanlCyl NumbScl

I NumbSclScanlCyl I BitmapOrg ScanLine I

for 0 to 1 do

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.

procedurevar

array ofbegin

if then begin

end else begin

for 0 to 1do

for 0 to 1 do

tryfor 1 downto 0 do begin

for 0 to 1 do begin

end

endexcept

on do begin

endend

end

with do begin

10 202

end

2.54

end

. ( : ); , , , : ;

, : ; : ; : ;

. ( ); ; ;

; := . ; . := ; . := . ; . := . ; ( , . ); := . -

[ ] := . [ ];

:= . ; := . ; ( , ); := - [ ] := . [ ];

:= ( - )/ ; := ( - )/ ; := ; := - := ; := - ; [ ( )][ ( )] :=

[ ][ ]; := + ; ; := + ; ; . := ; ; ; ;

. . := ;

. . := ; ( (- ), ( ), ( ), (- )); ( , . - ,

+ ( * )); ;

. ; . :=

+ ( ( * . /( - ))) + ;

;

TForm1 BtMakeImgClick Sender TObjectI J NumbPix NumbScl integerDegStep VertStep doubleBitmapTmp TBitmapScanlTmp pPIxArray

BitmapOrg EmptyShowMessageexit

ClearScreen

CheckformatBitmapTmp TBitmap CreateBitmapTmp PixelFormat pf24bitBitmapTmp Height BitmapImg HeightBitmapTmp Width BitmapImg WidthSetLength ScanlTmp BitmapTmp Height

I BitmapTmp HeightScanlTmp I BitmapTmp ScanLine I

NumbPix BitmapOrg WidthNumbScl BitmapOrg HeightSetLength ScanlCyl NumbScl

I NumbSclScanlCyl I BitmapOrg ScanLine I

DegStep Pahi Palo NumbPixVertStep Pvhi Pvlo NumbSclPv Pvlo

J NumbSclPa Palo

I NumbPixConvCylToImgScanlTmp Yim Iy Xim Ix

ScanlCyl J IPa Pa DegStep

Pv Pv VertStep

ExceptionLabel6 Caption

Image1 Picture Bitmap BitmapTmp

Image1 CanvasPen Color clBlack

Ellipse Xim R Yim R Xim R Yim RTextOut Image1 Height

FloatToStr R

BitmapTmp FreeLabel6 Caption

IntToStr round BitmapOrg HeightPvhi Pvlo

'First load a picture'

//makes Image1 white

//copy scanline pointers

//copy scanline pointers

//retrieve Ix, Iy

//further ignored

{draw the Cylinder base}

'Cyl. diameter: '

'resol. '

'dpi'

'error'

Cylindrical Anamorphosis (continuation 3)

Page 21: Blaise 18 Uk to Taal Free

21 COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

ConclusionI'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.

Figure 7: The image of Figure 1 revealed

So now you can surprise your guests by showing your home-made 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

Cylindrical Anamorphosis (continuation 4)

Web Service Toolkit - a framework offering rich potential for Plug-in development

By Inoussa Ouedraogo

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.

Figure 1: Client and server modules in a client process

A Library-based Application Server

expertstarter Lazarus

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.

Page 22: Blaise 18 Uk to Taal Free

22 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

( : ;

: ; : ): ;

<Code unit=library_server_intf.pas>

</Code>

function

var

wstHandleRequest ARequestBuffer IwstStreamAErrorBuffer Pointer

AErrorBufferLen LongInt LongInt

Figure 2: Plug-in Main Program

Plug-in exampleWST 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).

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.

Figure 3: Architecture

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.

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

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.

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.

Web Service Toolkit (continuation 1)

Page 23: Blaise 18 Uk to Taal Free

COMPONENTSDEVELOPERS 4 23SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

<Code unit=pluginservice.pas>

</Code>

= ( ) : ; : ; : ; : ; ;

= ( ) : ; : ; ;

= ( )

(): ; ; (): ; ; ( : ) : ; [ : ] : ; ; ;

= ( ) [ ] (): ; ( : ; : ): ; ;

TPluginDesc TBaseComplexRemotable

Identifier FIdentifier FIdentifierDescription FDescription FDescriptionCaption FCaption FCaptionFunctions TFunctionDescList FFunctions FFunctions

TFunctionDesc TBaseComplexRemotable

FName FNameCaption FCaption FCaption

TFunctionDescList TObjectCollectionRemotable

GetItemClass TBaseRemotableClassAdd TFunctionDescAddAt APosition Integer TFunctionDescItem AIndex Integer TFunctionDesc Read GetItem

IPluginService IInvokable

GetDescription TPluginDescExecuteFunction AOperation AValue Double Double

classpublishedproperty stringproperty stringproperty stringproperty

end

classpublishedproperty Name stringproperty string

end

classpublic class function overridefunction inlinefunction constproperty Default

end

interface

functionfunction const string const

end

read write read write

read write read write

read write read write

{$IFDEF USE_INLINE} {$ENDIF}

'{26FEB177-55C3-4D59-A5C6-3CF7AD651E2A}'

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]

WST can be checked out at

[2] “Lazarus The Complete Guide”, ISBN 978-94-90968-02-1, Blaise Pascal Magazine,

http://wiki.lazarus.freepascal.org/Web_Service_Toolkit,

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

http://www.blaisepascal.eu/index.php?actie=./subscribers/lazarusbookinfoEnglish

Web Service Toolkit (continuation 2)

Pag 87COMPONENTSDEVELOPERS 4

€ 60,00 + postageHARDCOVER PAPER BACK € 50,00 + postage

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

Page 24: Blaise 18 Uk to Taal Free

24 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

By David DirksePolygon colouring and area calculation

IntroductionThis 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:

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' anglesA 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 > 1To 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.)

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.

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.

However … look at the following 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.

expertstarter DELPHI 3 and above (Win32)

Figure 2

Figure 3

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.

Figure 5

Page 25: Blaise 18 Uk to Taal Free

COMPONENTSDEVELOPERS 4 25SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

The complete algorithmBelow is a more complex polygon for which the algorithm we have worked out so far fails to distinguish 'outside' angles correctly.

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:

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 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:

'outside'

Vector start and end points may, or may not, lie on the extended vector AB. In each case we compute a score, the crosscount (see the figure left bottom (8)) which indicates whether just one end, or whether both ends, of the crossing vector span the line, taking direction into account as indicated, to give the sign of the crosscount.Now the simple rule for an 'inside' angle is: ‘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.

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).

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 6

Figure 9

Figure 10

Figure 7

Figure 8

Polygon colouring and area calculation(continuation 1)

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 11

Of course this must be repeated for all points (D) other than A, B and C. For simple polygons like bars and arrows, this algorithm suffices.

Page 26: Blaise 18 Uk to Taal Free

26 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

// the maximum number of points allowed

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

// the coordinates of starting point// the horizontal, vertical lengths

// the direction in radians

// the number of vectors

// (x,y) of A,B,C

//the number of entries in trianglelist

const 100

var array 1.. of

typerecord

end

var array 1.. of

type record

end

var array 1.. of

= ;

: [ ] ; : ;

.

=

, : ;

, : ;

: ; ;

: [ ] ;

: ;

=

, , , , , : ;;

: [ ] ;

: ;

maxpolypoint

points maxpolypoint Tpointpcount byte

Tvector

x y longInt

dx dy longInt

dir double

vectorlist maxpolypoint TVector

vcount byte

Ttriangle

ax ay bx by cx cy longInt

trianglelist maxpolypointTtriangle

tcount byte

Points are defined in a points array:

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

From the vectorlist a triangle-list is generated.

Coding a vector: (screen coordinates)

Figure 12: The vectorlist becomes successively shorter(by removing vectors) as the trianglelist is built.

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.

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:

Figure 13

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)). where a, b, c are the lengths of the sides, and s =

a,b and c are simply calculated using Pythagoras' theorem.For a proof of Heron's theorem please look here:

0 5. *( + + )a b c // half the triangle perimeter.

http://home.hccnet.nl/david.dirkse/math/geocalc/geocalc.html#area

procedure

procedure

procedure

procedure

( : );

( : );

( : );

( : );

setCanvas cvs Tcanvas

setpencolor pc longInt

setBGcolor bgc longInt

setBG BGmode boolean

//select canvas//pen color//background color

//BG true/false

//triangles on/off

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

geoClear; initializes all arrays.

setShowTriangles(trMode : boolean);

If trMode = true then draw the triangles, which is used only for test purposes.

polyResultMessage

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:

polyresultcode byte

longInt

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.

procedure

procedure

function string;

//debug purposes

//message of code

// polyResultcode = 0 if processing is error-free

// polyTime is the processing time in microseconds

:

: ;

: ;

var

polyTime

The structure of the polygon projectAll polygon type definitions, variables, functions and procedures are declared in the poly_unit unit.

The routines provided include:

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

This function returns the area and draws the polygon on the previously selected canvas.

function ( : ; : ): ;polyArea pts pointer n byte double //area of polygon

Page 27: Blaise 18 Uk to Taal Free

27COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

The exerciser

The poly_unit

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.

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:

Figure 14: The exerciser program

function

varbegin

0

if 0 then

if 0 then

if 0 then

end

( : ; : ) : ;

, : ;

( ); := ;

:= ( ); := ;

; <> ;

;

; <> ;

; <> ;

;

:= ; ( ); := ( - );

; ;

polyArea pts pointer n byte double

t1 t2 Int64

getCPUticks t1polyresultcode

pp Ppoints ptspcount n

checkpointspolyresultcode exit

buildvectorlist

checkvectorspolyresultcode exit

buildtrianglelistpolyresultcode exit

trianglesums

result areagetCPUticks t2polyTime ProcTime t2 t1

drawpoly

//calculate area, draw polygon

//clock cycles since power on

//pointer to points[1]

//check closed, sufficient points

//crossing edges, duplicate points

//summarize area of triangles//later version: result = area / 400

//draw polygon on selected canvas

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 const

varbegin

if 0then

begin

end

if then 0if then 0if 1 then 1if 1 then 1

end

( , : );

, , : ;

:= . * . - . * . ; =

:= ; ;

; := ; := . - . ; := . - . ; := ( * . - * . )/ ; := ( * . - * . )/ ;

( ) < := ; ( ) < := ; ( - ) < := ; ( - ) < := ;

;

vrsect v1 v2 TVector

d vx vy double

d v1 dx v2 dy v1 dy v2 dxd

fvalid falseexit

fvalid truevx v2 x v1 xvy v2 y v1 yf1 vx v2 dy vy v2 dx df2 vx v1 dy vy v1 dx d

abs f1 frnd f1abs f2 frnd f2abs f1 frnd f1abs f2 frnd f2

//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

//discriminant

//round to 1e-6

Other “low-level” procedures are:outward vnr word boolean

VDir deltaX deltaY double double

Empty3 i1 i2 i3 word boolean

i1,i2,i3 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:

function

function

function

( : ) : ;

( , : ) : ;

( , , : ) : ;

//return true if vectorlist[vnr] points outward

//return direction of vector in radians

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

procedure

procedure

procedure

procedure

procedure

;

;

;

;

;

checkpoints

buildVectorlist

checkvectors

buildTriangleList

TriangleSums

// tests for sufficient points and a closed polygon

// builds a vectorlist from array points[ ]

// tests for intersecting edges and duplicate points

// builds trianglelist from vectorlist

// 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:

geoTriColor tr Ttrianglebrushcol LongInt

This draws the background of a triangle, using horizontal lines.Angles are first sorted by ascending y values.

drawpoint p Tpoint

procedure const

procedure

( : ;: );

( : );

procedure drawline(p1,p2 : Tpoint);// draws a line from point p1 to p2

// draws point p

Page 28: Blaise 18 Uk to Taal Free

28 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

To know the intersection point following set of equations must be solved:x1 + f1*dx1 = x2 + f2*dx2y1 + 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.dy2dx2(y1 + f1*dy1) = y2.dx2 + f2*dx2.dy2

Now subtract row 2. from row 1. 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..andvx = x2 - x1 ...andvy = y2 - y1 ...then

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

Note : for negative f the vector changes direction.

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.

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

dx1 The notation of vector AB=(dy1) dx2 BC=(dy2)

dx1+dx2 (dy1+dy2)

x y()

xd dy( )

x y() x1 = y1( ) dx1+ f1 1(dy )

dx1 f.dx1=f ( )( )f. dy1dy1

(see figure below)

The sum of two vectors AB+BC =

the vector is multiplied by f.

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)

The intersection of two lines:

F or d = 0 the vectors coincide or are parallel.In this case flag fvalid = false indicating there is no intersection point.

The direction of a vector:Appendix 2

Two problems arise:1. if dx = 0 then the direction is 0.5pi for dy > 0 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.

f

dx1

d y 1

f.dx1

f . d y 1

A point on the line has

x x1 dx1 = + f1y y1()( ) (dy1)x x2 dx2 = + f2y y2 ( )()( ) dy2

line 1

line 2

vx.dy2-vy.dx2 f1=

d

vx.dy1-vy.dx1 f2=

dand similar

of vector the arctangent function is used to get the direction.

direction = arctan in radians.yd ( )dx

David DirkseBorn 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

Polygon colouring and area calculation(continuation 3)

Figure 17

COMPONENTSDEVELOPERS 4

Page 29: Blaise 18 Uk to Taal Free

29SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

by Detlef. D. OverbeekCreating a Database program from scratch

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 [email protected]) with any helpful feedback. I estimate this will take at least a year of preparing, designing and coding.

The first hurdle: the DatabaseWe already have an application in rudimentary form which works after a fashion. However, it is not adequate for our current and future needs. I think it's best to start from scratch, using only some ideas and details from our current application. So we have reusable parts and ideas, but we have not yet settled on a database. Since we are a user group, we need to use open source where possible to keep the price down, which limits our choices. A further issue is choice of the operating system on which the database server will run. Will it be Linux, Windows, Mac or Android? How can all our users have easy access? Linux is cheap (almost free), but then you 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 keep as close as possible to Pascal. So the next question is whether we use Delphi or Lazarus libraries? It doesn't really affect the choice of database, especially if we opt for the Interbase equivalent : Firebird. But...If we use some of the best component suites for database connection (from Components4Developers) as well as other attractive offerings from TMS Software we should opt for Delphi. (TMS surprises us time and again. At the recent Delphi Conference in Cologne (Köln), Germany they presented an astonishing new product: Desktop - Touch –Table. I was excited - like a little boy who had 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 Firebird will be our database.

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.

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:

Which program will we use for creating and

handling the database?

1. What database?The database handling tool must be capable of creating the Firebird database.

2. Auto creation and visual designIt should be able to automatically create tables and allowyou to design them visually.

3. ReengineeringWe need reengineering since we already have many existing tables.

4. The ability to create and then visually alter the designWe 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

6. Support printable overviewsSupport printable overviews, at least A3 or bigger, in colour.

7. Create images in a variety of popular formatsBe able to create images with several different sorts of extensions.

8. Have export and import capabilities to other databases

9. Have a data pump10. Documentation

Last but not least: have very good quality documentation.

And now the million dollar question:Can we as a poor user group get that free? Who might help us?We are of course poor, but not short of fantasy. Why not ask the manufacturers? And within no time we came up with the solution. There is a Dutch company that offers such a product, and as chauvinistic as I am - I love that.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:

Upscene). They have created a program called Database Workbench which meets all of our needs. So let's start testing it…

expertstarter DELPHI XE and above (Win32)

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

Page 30: Blaise 18 Uk to Taal Free

30 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Testing Database WorkbenchWe 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 1: The Splash screen...Figure 2 : The IDE opening screen

Figure 3: Firebird's local SQL dialect

Creating a Database program from scratch (Continuation 1)

Page 31: Blaise 18 Uk to Taal Free

COMPONENTSDEVELOPERS 4 31SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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

Creating a Database program from scratch (Continuation 2)

Page 32: Blaise 18 Uk to Taal Free

32 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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!

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...

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 5: The IDE and the menu where you can process reverse engineering

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.

Creating a Database program from scratch (Continuation 3)

Page 33: Blaise 18 Uk to Taal Free

33COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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... [email protected]

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.

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 11: Import capabilities

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

Figure 12: Export capabilities

Figure 10: The Draw menu

Creating a Database program from scratch (Continuation 1)

Page 34: Blaise 18 Uk to Taal Free

34 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

By Daniele TetiAn Android client for a DataSnap Server – Part 2

Introducing Android developmentAs Google says… “Android is a software stack for mobile devices that includes an operating system, middleware and key applications. The Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.” (

)

A lot has been written on the web about the pros and cons of Android, so we will not repeat that here. Android is a great operating system and development platform. To get an introduction to Android and the motivations that led the way for Google to undertake the development of Android, you can consult Wikipedia at the following URL

Before starting to develop for Android, you must set up the development environment. Although it is not mandatory to use a specific development environment, Google has written a very useful plug-in for Eclipse called ADT (Android Development Tools), and later in this article we will use Eclipse with ADT. Google provides an excellent guide on how to configure everything you need to develop on Android. In Throughout this article I'll assume that your development environment is configured as recommended in this guide ( l) and that you have successfully tried the HelloWorld tutorial (

). 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/basics/what-is-android.html

http://en.wikipedia.org/wiki/Android_%28operating_system%29.

http://developer.android.com/sdk/installing.htm

http://developer.android.com/resources/tutorials/he

llo-world.html

http://developer.android.com/guide/topics/fundamentals.html#appcomp

Layout

View and Event Handling

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.

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.

Specifically, the graphical controls are called widgets and are defined in the package android.widget. Some of these widgets (buttons, edits and so on) will be used on the app that we'll create.One difference for a Delphi developer is the event handling mechanism is different from that to which a Delphi developer is used to, but it is very similar to that of the graphics libraries available on Java SE, so a Java developer should not have problems in understanding the mechanism.The application we are going to build will allow us to understand how the listener mechanism works, and how to parse a string in JSON format.I have not structured this article as a step-by-step tutorial. Rather I will focus on the specific issues involved while interfacing Android with DataSnap. If you need a step-by-step tutorial on Android development, you can browse the amazing Google documentation, or a white paper that I wrote for Embarcadero some months ago. You can download the whitepaper for free at

The white paper talks about a PHP REST service and Android. However the Android part is almost the same. This is the code for the main activity.

http://www.embarcadero.com/rad-in-

action/php-android.

In this second article we'll create an Android client application that talks to the previously built DataSnap server. Basic knowledge of Java syntax is assumed in what follows.

public class MainActivity extends Activity implements OnItemClickListener {privateprivate

public voidsuper

this

protected voidsuper

private voidnew this

newthis

public voidint long

new this class

public voidnew this class

ListView ; List<ToDo> ;

private final int = 1001;private final int = 1002;private final int = 1003;

@Override onCreate(Bundle ) {.onCreate(savedInstanceState);

setContentView(R.layout. );setTitle(R.string. );

= (ListView) findViewById(R.id. );.setOnItemClickListener( );

}

@Override onResume() {

.onResume();refresh_data();

} refresh_data() {

= RESTProxy( ).getToDos();.setAdapter( ToDoListAdapter

( , ));}@Override

onItemClick(AdapterView<?> , View , , ) {

Intent = Intent( , EditToDoActivity. );

ToDo = .get(position);

i.putExtra( , todo.getId());i.putExtra( , todo.getCompleted());i.putExtra( , todo.getDescription());// start the other activitystartActivity(i);

} newTodoClick(View ) {

Intent = Intent( , EditToDoActivity. );// start the other activitystartActivity(i);

}

}

lvtodo_list

MENU_REFRESHMENU_CONFIGMENU_ABOUT

mainapp_title

lv lv_todolv

todo_listlv

todo_list

todo_list

/** Called when the activity is first created. */

// the selected item

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

//Menu management code. Remove for brevity

savedInstanceState

parentview position id

i

todo

"id""completed""description"

btni

expertstarter DELPHI 2007 and above

Page 35: Blaise 18 Uk to Taal Free

35COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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 onResume() method is called just before the activity runs, or returns to running. So, in that method I've put the actual data loading from the server. This application is a demo. In a real world application you would put all the remote invocation, or more generally, all the time-consuming operations in a secondary thread. The Android SDK provides strong multithreading support. If you need further information about threads in Android, you should read these articles:

(http://developer.android.com/reference/androi

d/app/Activity.html#ActivityLifecycle)

http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html, http://developer.android.com/reference/java/lang/Thread.html, http://developer.android.com/reference/android/os/AsyncTask.html).

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 execHttpRequest method:Permission denied (maybe missing INTERNET permission)This is because, in Android, every application that uses a network connection, must inform the operating system.To inform Android that our application needs access to the internet, open the file AndroidManifest.xml and add just before the closing tag </manifest>, the following line<uses-permission android:name="android.permission.INTERNET"></uses-permission>Even in this case you can use the visual editor if you prefer. Our todo application requires this permission.

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 server the service that we built in the previous article. The interface is minimal but effective. The main activity just shows a list of all the 'todo' items, and whether they have been completed or not yet completed. Clicking on a TODO item, using an Intent, will open a new activity that will then allow us to edit it. In addition, you can also insert a new 'todo' items directly from the main activity using the "New ToDo" button.

The To Do List Proxy

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:

The DataSnap proxyThe most interesting part of the application is the proxy. In this application the DataSnap proxy is written from scratch (although in Delphi XE2 there is very cool automatic proxy generation for Android and other mobile platforms). However, if you have Delphi XE and 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 object method that uses a connection to a webserver.So the URL

is mapped to the a proxy instance method getTodos() and so on.

…/datasnap/rest/TSrvMethodsTODO/Todos

Accessing the web serverIt is simple to send a request to a web server from an Android application. For these this purposes, Android provides the well-known 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:

As an example, here's the code to send an http request for a URL and gets its text representation in a String.

http://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html

private String execHttpRequest(String )

new

new

catch

url

httpclient

httpget

response

entity

e

{

{

} {

}}

try

HttpClient = DefaultHttpClient();

HttpGet = HttpGet(url);

HttpResponse =httpclient.execute(httpget);

HttpEntity = response.getEntity();

EntityUtils.toString(entity); (Exception )

e.printStackTrace(); e.getMessage();

//Create default http client

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

//Execute GET request over the httpclient

//Retrieve the response body or "entity"

//Return the entity as String

//If something goes wrong, print the stack trace //and return the Exception message

return

return

Figure 1 : The main activity

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

Page 37: Blaise 18 Uk to Taal Free

37COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Daniele Teti

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.

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

Figure 2: The code to get the ToDo list and to convert the JSON array ToDo items into an ArrayList

Figure 3: The create and update methods

Page 38: Blaise 18 Uk to Taal Free

38 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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:

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

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:

SummaryIn 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-platform-number> of the SDK .

Figure 5.

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

Page 39: Blaise 18 Uk to Taal Free

39COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

By Alexander Alexeev

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:

Anti-freeze for VCL applicationsexpertstarter DELPHI 3 and above (Win32)

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. 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 to Windows' requests”, but not both simultaneously.

The wrong solutionSo 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.

Threads?The thing is that processes (applications) don't run any code at all. They are just containers for threads.

Page 40: Blaise 18 Uk to Taal Free

40 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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?

The multi-threaded solutionWell, 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:

). To use the TasksEx unit you just need to follow some simple rules. The unit offers you only two principal procedures:

http://andy.jgknet.de/blog/?page_id=100

procedureprocedure

try

finally

end

; ;

;

;;

EnterWorkerThreadLeaveWorkerThread

EnterWorkerThread

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)

// 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.

// This code runs in main thread

You can use any global or local(!) variables in code between EnterWorkerThread 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 plug-ins 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.

Page 41: Blaise 18 Uk to Taal Free

41COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

procedureprocedure const String

var Stringbegin

if 0thentry

repeatif Name and Name thenbegin

Nameifthen else

end0

finally end

end

begin

try

finally end

end

. ( : ); ( : ;

: ); : ; : ; ( + , , ) =

( . <> ) ( . <> ) := + . ; ( )

( + , ) . ( ); ; ( ) <> ; ( ); ; ;

. . ; ( ( . ),

. ); . . ; ;

;

TForm1 Button1Click Sender TObjectEnumFiles AFolder

AFiles TStringsSR TSearchRec S

FindFirst AFolder faAnyFile SR

SR SR

S AFolder SRDirectoryExists S

EnumFiles S AFilesAFiles Add S

until FindNext SRFindClose SR

Memo1 Lines BeginUpdate

EnumFiles IncludeTrailingPathDelimiter Edit1 TextMemo1 Lines

Memo1 Lines EndUpdate

'

'.' '..'

'\'

*.*'

Anti-freeze for VCL applications (continuation 2)

You just need to separate your code in two areas which don't overlap:a) Time-consuming codeb) 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 examplesHere 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:

Incidentally, notice how we're avoiding the “magic button” anti-pattern 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.

uses

procedure

procedure const String

begin

end

var String

begin

try

try

try

finally

end

try

finally

end

finally

end

finally

endend

;

. ( : );

( : ; : );

;

: ; : ;

. := ;

:= . ;

:=

( . ); ;

; ( , );

; ;

; . . ;

. . ( );

. . ;

;

( );

;

. := ;

;;

TasksEx

TForm1 Button1Click Sender TObject

EnumFiles AFolderAFiles TStrings

Str TStringList Folder

Button1 Enabled False

Str TStringList Create

FolderIncludeTrailingPathDelimiter Edit1 Text

EnterWorkerThread

EnumFiles Folder Str

LeaveWorkerThread

Memo1 Lines BeginUpdate

Memo1 Lines Assign Str

Memo1 Lines EndUpdate

FreeAndNil Str

Button1 Enabled True

// ... no changes ...

//1

//2

//3

//3

//3

//4

//4

//4//2

//2//1

//1

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.

RulesEven though this was a very simple example, it illustrates several important rules:1. Calling EnterWorkerThread/LeaveWorkerThread always to be like this:

must

EnterWorkerThread

LeaveWorkerThread

;

;;

try finally

end

// do work

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.

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:

Page 42: Blaise 18 Uk to Taal Free

42 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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:

The same holds true for similar mechanisms.4. This rule is similar to previous one: not all external

functions allow simultaneous calls from several threads. Some external functions may not allow calls from a non-primary thread at all. The VCL is the best known example, but we're talking about all sorts of external functions here. For example, GDI objects typically have no thread-affinity, but you must guarantee single thread access at one time. Windows objects have thread-affinity: only the thread which created a window owns it. A further example would be calls you make to loadLibrary and FreeLibrary. Even though they don't formally have thread-limitations, the DLL they load and free may have implicit expectations about the loading thread being the application's primary thread. In such cases you must protect calls to these functions with critical sections (though you can ignore this if you're sure that you won't create more than one worker thread). For functions which don't like calls from other threads at all, you will need to serialize calls to the main thread. You can either make these calls before (or after) spawning the secondary thread, or you can use the technique described in the next item (Rule 5). A function's description does not always mention the requirements for multi-threaded calls, which makes life difficult for inexperienced programmers. So sometimes you will need to do some study and research before writing code. Of course, you can risk trial-and-error programming, but I strongly recommend against doing this.

EnterWorkerThread

CoInitialize

CoUninitialize

LeaveWorkerThread

;

;

; ;

;;

try

try finally

endfinally

end

// your-code

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 perform VCL work (instead of the secondary thread itself performing the VCL work). You can send messages both sync and async (of which async has the least performance penalty). This is a very good and flexible way to work. Unfortunately, there are too many potential issues to fully describe all its aspects in this article. If you have worked with window messages before you shouldn't have any problems with this approach. Since all messaging work is done inside a single process, no inter-process communication (IPC) is necessary. You can see an example of this approach in the code examples accompanying this article (available at the Blaise magazine website).Temporarily switch to the main thread. (For the benefit of readers who are familiar with Synchronize and TThread, this is analogous to calling Synchronize in TThread). You can do this as follows:

procedureprocedure const String

begin ifthen elsebegin

try finally end

end

endbegin

try try

finallyend

finally

endend

. ( : ); ( : ;

: );

( )

( + , ) ; . ( ); ; ; ;

;

. . ; . := ; ; (

( . ), . ); ; ; . := ; . . ; ;

;

TForm1 Button1Click Sender TObjectEnumFiles AFolder

AFiles TStrings

DirectoryExists SEnumFiles S AFiles

EnterMainThreadAFiles Add S

LeaveMainThread

Memo1 Lines BeginUpdateButton1 Enabled False

EnterWorkerThreadEnumFiles IncludeTrailingPathDelimiter

Edit1 Text Memo1 LinesLeaveWorkerThread

Button1 Enabled TrueMemo1 Lines EndUpdate

// ... no changes ...// ... no changes ...

// ... no changes ...

'\'

Page 43: Blaise 18 Uk to Taal Free

43COMPONENTSDEVELOPERS 4SEPTEMBER 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 buttonWhen 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

procedure const String const

beginif 0then

tryrepeat

0finally

endend

begin

try

finally

endend

procedurebegin

end

. ( : );

( : ; : );

( + , , ) =

; ( ) <> ; ( ); ; ;

. := ; . := ; . := ; . := ; ;

;

. ( : );

;;

TForm1 Button1Click Sender TObject

EnumFiles AFolderAFiles TStrings

FindFirst AFolder faAnyFile SR

CheckAbort

until FindNext SR

FindClose SR

Button1 Enabled FalseButton2 Enabled True

Button1 Enabled TrueButton2 Enabled False

TForm1 Button2Click Sender TObject

AbortAllWorkerThreads

// ... no changes ...

'*.*'

..

// <- added// ... no changes ...

// start button// stop button

// ... no changes .

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 detailsA 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:

Page 44: Blaise 18 Uk to Taal Free

44 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Anti-freeze for VCL applications (continuation 5) ...

;

... ; ...

;;

... ;

;

...

;

;;

EnterWorkerThread

P

LeaveWorkerThread

P

EnterWorkerThread

LeaveWorkerThread

try

finally

end

procedurebegin

try

finally

endend

// does nothing since we're already in worker thread

// does nothing, since nearest EnterWorkerThread

// above didn't do anything too

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:

// This code runs in main thread

{ This code runs in worker thread }

// This code runs in main thread

{ This code runs in worker thread which could be the very same worker thread, but also can be a completely different thread. }

EnterMainThread/LeaveMainThread

// This code runs in main thread

{ This code runs in worker thread }

// This code runs in main thread

{ This code runs in worker thread }{ (guaranteed to be the same worker thread) }

// This code runs in main thread

EnterWorkerThread

LeaveWorkerThread

EnterWorkerThread

LeaveWorkerThread

EnterWorkerThread

EnterMainThread

LeaveMainThread

LeaveWorkerThread

;

;;

;

;;

;

;

; ;

;;

try

finally

end

try

finally

end

try

try finally

end

finally

end

#1

#2,

#1

#1

To temporarily switch to the main thread – use the functions. For

example:

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.

When NOT to use this solutionIt'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.

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

Page 45: Blaise 18 Uk to Taal Free

COMPONENTSDEVELOPERS 4 Pag 45SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 46: Blaise 18 Uk to Taal Free

Delphi XE2 New Features

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 EditionsSpeaking of Editions, there are now no less than five (5!) different Delphi XE2 editions available to buy. These are listed below with pricing details both for first-time buyers and for upgrading. All prices are shown without VAT, and these editions are available from for EU customers, together with a corresponding 12 month subscription price.

http://www.bobswart.com

You can upgrade from Delphi 2007 or later. However, after December 31st 2011, you will no longer be able to upgrade from Delphi 2007, and the only eligible versions to upgrade from will be Delphi 2009, 2010 or XE. Delphi XE2 Ultimate is the same as Delphi XE2 Enterprise with the addition of DB PowerStudio. Delphi XE2 Architect is the same as Delphi XE2 Enterprise with the addition of ER/Studio. These two database products are both from Embarcadero (now the proud owner of Delphi). To be honest, as a reseller I mostly sell Professional and Enterprise editions of Delphi. The Starter edition is rather limited, so check out the conditions and constraints before you purchase a Starter edition (especially if you plan to make some money with it). Apart from these five commercial editions, you can also get special Academic versions of these editions, but only if you are a registered student at a school or university (obviously, these Academic editions cannot be used for any commercial development).Finally, you can download a 30-day trial edition of Delphi XE2 Architect to get a hands-on feeling of what the product is capable of.Subscriptions are offered for the Professional and higher editions, and are valid for a period of a year. You can optionally extend a subscription each year for a further year before the subscription expires. With an active subscription, developers automatically get new editions of Delphi as soon as they are released by Embarcadero. They also have the right to three so-called “incident support calls” with Embarcadero (for example for problems that even your reseller cannot solve for you). A subscription is especially beneficial if you are a high-end Delphi user, but also for Professional users if they upgrade at least once every two years.

Major New Feature: X-PlatformThe major new feature in Delphi XE2 is the capability for cross-platform development. First of all, the 32-bit Windows compiler has been extended with a 64-bit counterpart which produces 64-bit Windows applications.

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…

Where previous Delphi projects had a special node in the Project Manager for the Build Configuration, Delphi XE2 introduces the Target Platform node. This can have a value of 32-bit Windows, 64-bit Windows, and OS X (sometimes seen as “OSX32”, so most likely 32-bit). Since the Delphi XE2 IDE itself is still a 32-bit Windows application, by default a new project will be created for the 32-bit Windows target. Depending on the project type, we can add one or more Target Platforms. 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 the Windows API, and is just about impossible to migrate to another platform. If you want to create an application for Mac OS X, you have to use either a console application (which can be 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 using a special cross-platform application framework called FireMonkey. As you can read in Fikret Hasovic's article elsewhere in this issue of Blaise Pascal Magazine, it's easy to add a 64-bit VCL target to a project. Make sure you read Jeremy North's detailed coverage of creating 64-bit applications with Delphi XE2 as well. But for Mac OS X we have to use a different framework, called FireMonkey.

In order for an application to look like a Windows application on Windows, and a Mac application on Mac OS X, Embarcadero had to bring in (read: buy) a whole new set of visual components – a new GUI framework. They actually call it the FireMonkey Application Platform, and it is specifically designed for cross-platform application development. Note that the VCL is not dead or being replaced. The VCL is still the best solution for native Windows applications (32-bit and/or 64-bit). But for cross-platform applications that need to move to other platforms like Mac OS X (and in the future probably Linux), we should use the FireMonkey Application Platform.

The FireMonkey Application Platform presents cross-platform controls and elements such as forms, dialogs, buttons, menus, and so on. It supports 2D and 3D graphics and uses the GPU for the graphics, freeing the CPU itself for doing the “real work” (like calculations or database operations). The FireMonkey Designer may feel a bit awkward at first, since it's not a clone of the VCL Designer. However, remember that FireMonkey is at version 1.0, and there will be many enhancements (and fixes) in the time ahead.

Project Targets

FireMonkey

FireMonkey ExampleTo 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:

By Bob Swart

Delphi XE2 New User Upgrade Subscription

Starter 199 149 n/a

Professional 899 499 270

Enterprise 1999 1299 600

Ultimate 2999 1999 900

Architect 3499 2299 1050

expertstarter DELPHI XE2

46 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 47: Blaise 18 Uk to Taal Free

A FireMonkey 3D Application is capable of doing more graphic “stuff ” than a FireMonkey HD Application. However, as a consequence, a FireMonkey 3D Application requires more power from the GPU. The FireMonkey HD Application is the one that most closely resembles a VCL Forms Application, with a 2D Form Designer that can host FireMonkey controls that almost look and feel like good old VCL controls.As an example, let's create a FireMonkey HD Application for Delphi, which creates a new project starting with a new empty form with a black caption and border. Save the project in FireMonkeyDemo.dpr and the form in 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.

procedurebegin

end

. ( : ); . . ( . ); ;

TForm1 Button1Click Sender TObject

ListBox1 Items Add Edit1 Text

And then we can run the application, which by default will show up as a 32-bit Windows application (the default target platform of any project in Delphi XE2).

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:

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.

GlobalDisableFocusEffect true := ;

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 and paddings work in the VCL or CSS.

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.

Figure 1: A FireMonkey 3D Application

Figure 3

Figure 2

Anyway, after you've placed a TListBox, TEdit and a TButton on the TForm (at least the control names are the same, although they originate from different units), we can write the well-known event handler for the OnClick of the TButton:

Delphi XE2 New Features (continuation 1)

47COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 48: Blaise 18 Uk to Taal Free

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:

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).

Mac OS XFirst 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 64-bit 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.

Delphi XE2 New Features (continuation 2)

48 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 4

Figure 5

Figure 6

Page 49: Blaise 18 Uk to Taal Free

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.

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:

Mac OS X Development

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.

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:

Delphi XE2 New Features (continuation 3)

49COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 7

Figure 8

Figure 9

Page 50: Blaise 18 Uk to Taal Free

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 IP-address). 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.

Delphi XE2 New Features (continuation 4)

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.

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.

50 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 10

Figure 11

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

Page 51: Blaise 18 Uk to Taal Free

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

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

Delphi XE2 New Features (continuation 5)

51COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 12

Figure 13

Figure 14

Page 52: Blaise 18 Uk to Taal Free

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!

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…

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:

Deployment

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/PAServer/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.

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!

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 thecapability 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

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 Swart Training & Consultancy

Unit Scope Names

http://www.bobswart.nl/cgi-bin/UnitScope.exe?unit=XXX

[email protected]

www.bobswart.com

Delphi XE2 New Features (continuation 6)

52 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Figure 15

Figure 16

Page 53: Blaise 18 Uk to Taal Free

Learning to use FastReport 4 for VCL Part 3

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 reportTo 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.

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 TfrxReportButton1 TButtonCheckBox1 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.procedurebeginwithdo begin

ifthenelse

endend

. ( : );

; . ; ;

;

TForm1 Button1Click Sender TObject

frxReport1

PrepareReportCheckBox1 Checked

ShowPreparedReportPrint

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

expertstarter DELPHI 6 and above

Figure 2: The FastReport Data page

53COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Sergey Lyubeznyy

Page 54: Blaise 18 Uk to Taal Free

To confirm these settings, click on the button with a green check mark. The text editor window will disappear. Then set the LoginPrompt property to False to avoid displaying a Login dialog box, and set the DatabaseName property by locating the database file MYIMAGES.FDB in the file selection dialog (Note: by default this dialog only displays files with a 'gdb' extension, so to find the MYIMAGES.FDB file, you must switch the file type to 'All Files'). The SQLDialect property must be equal to 3.So, let's look at the properties of IBXQuery1. Its main property is SQL of TStrings type. The SQL query text should be entered via the Text editor (Figure 4).

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 Report→Data menu. The data source list should look like Figure 5

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).

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 cmNow 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 non-data 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 3: Values for the Params property

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

Figure 5: The data source list

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 MasterData1 band. After that, a Text object will appear on the band containing the following text:[IBXQuery1."IBXQuery1"]Instead of quoted IBXQuery1 text, we need to put the name of the field in quotes. Our text field is called TXT. Insert this name in our object's text.Similarly, when the dataset is active, you can drag a field name from data tree onto the data band to create the text object that will display this field's text. Very convenient! This is what we will do later (see the last part of this article).Let's go back to our project. We need to configure this Text object. Set the text alignment to left justify, the Arial font size to a value of 10, and turn off its bold property.

Figure 6: The Data Tree dialog

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

54 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 55: Blaise 18 Uk to Taal Free

The toolbar button's captions show as pop-up hints on mouse-over. 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 objectFirst, 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.

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).

Figure 7: An empty Picture dialog

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.

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

55COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 56: Blaise 18 Uk to Taal Free

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 (View→Options). 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.

Figure 9: The report preview window in action

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 ObjectsThis 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

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

56 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 57: Blaise 18 Uk to Taal Free

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 ChartThis 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 employeeWHERE 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.

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

57COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 58: Blaise 18 Uk to Taal Free

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).

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 11: The Designer with Expr1000 and Salary fields

Figure 12: The Chart Editor

Figure 13: The Chart Gallery (initial view)

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

58 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 59: Blaise 18 Uk to Taal Free

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.

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.

TForm1 FormShow Sender TObject

frxReport1 ShowReport true

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 with your ideas or questions about Delphi I may be able to discuss the matters you raise in a future article. See you again.

procedurebegin

end

. ( : );

. ( );;

[email protected]

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

Figure 15: Setting up the chart properties

Figure 14: Selecting Bar chart styles in the Gallery

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

59COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 60: Blaise 18 Uk to Taal Free

Pag 87COMPONENTSDEVELOPERS 4

€ 60,00 + postageHARDCOVER PAPER BACK € 50,00 + postage

SUMMER OFFER:

LAZARUS the complete guideBlaise 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

Page 61: Blaise 18 Uk to Taal Free

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 € 355-person annual subscription by download € 15010 -person annual subscription by download € 30050 -person annual subscription by download €1125100 - 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)

Become

subscriber ... BLAISE PASCAL MAGAZINE

For , Linux, Win CE, Windows XP / 7

Android, iOS Mac,

Page 64: Blaise 18 Uk to Taal Free

Creating 64-bit applications with Delphi XE2

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.

Do I actually need 64-bit?Obviously there is no single answer that applies to every developer's applications. If you are manipulating large amounts of data (such as large media files) then offering a 64-bit application will most likely provide benefits over a 32-bit application. The main reason for this is the ability to address more than 4GB 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.

Access to gigantic memory spaces is not the only reason for wanting your application to compile as 64-bit. Being recognised as a first class citizen in the operating system has many benefits. 32-bit applications are not able to store to and access some parts of the system as 64-bit applications usually would. This includes access to the registry and file system areas. 32-bit applications are also installed to their own program files folder, called "Program Files (x86)".

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.

By Jeremy North

expertstarter DELPHI XE 2 ONLY

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

Reference: http://en.wikipedia.org/wiki/64-bit

64-bit Windows Time Line

Edition Maximun Ram

Starter 8 GB

Home Basic 8 GB

Home Premium 16 GB

Professional 192 GB

Enterprise 192 GB

Ultimate 192 GB

Shell Extensions

Working with 64-bit in the IDE

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.

This article will just cover working with VCL 64-bit applications using XE2. The new FireMonkey framework also supports 32-bit 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.

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 select the Add Platform command. This displays a dialog that allows you to select an additional target platform for your application. For VCL applications, 32-bit Windows and 64-bit Windows are the only platforms available. For a FireMonkey application, OS X is also available.

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.

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 for the IDE (not framework specific). This means that each platform's paths have to be maintained separately. When switching to the 64-bit Windows platform you will notice instances of $(Platform) in the paths displayed. The IDE treats this as a variable and whenever a path contains $(Platform) the appropriate platform abbreviation is substituted.

Library Paths

Platform Name Abbreviation

32-bit Windows Win32

64-bit Windows Win64

Figure 1

Figure 2

64 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 65: Blaise 18 Uk to Taal Free

Figure 3

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 OptionsAs 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).

Figure 4

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

65COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

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...

Page 66: Blaise 18 Uk to Taal Free

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).

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.

Output Paths

Working with the Form Designer in a 64-bit applicationFor 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. Works fine at design timeWhile debugging your 64-bit targeted application you are seeing errors that don't occur at design time.

2. No 32-bit component available

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 32-bit 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.

All VCL components shipped with the IDE are available in 32-bit and 64-bit versions.

Components

Registering Components for 32-bit and 64-

bit usageTo 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:

ComponentPlatformsAttribute pidWin32 pidWin64TMyComponent TComponent

For those wondering about FireMonkey and OS X, another possible value is pidOSX32

typeor

classend

[ ( ] = ( ) ;

DebuggingUnless 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 - checked2. Include remote debug symbols - checked

Figure 5

Figure 6

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

66 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 67: Blaise 18 Uk to Taal Free

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.

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.

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 Windows platform, right click on the 64-bit Windows entry in the project manager and select the Assign Remote Profile command.

Can I debug my application if I am using a 32-bit operating sytem?

Adding a Remote Profile to a target platform

This will display a dialog of previously entered remote profiles. Initially this list will be empty so select the Add button to create a new remote 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.

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).

Figure 7

Figure 9

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

67COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 68: Blaise 18 Uk to Taal Free

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.

Modifying Remote ProfilesIt is possible to modify remote profiles in the Tools | Options dialog. Select the Remote Profiles node.

Figure 10

Figure 11

Figure 12

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

68 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 69: Blaise 18 Uk to Taal Free

Platform Assistant

Running 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!

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.

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

Loop Variables

Handle References

Windows Messages

Storing object references in the Tag

property

VCL changes

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.

If you need your loop to handle 64-bit values, use NativeInt for your loop counter.

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.

When working with messages, typecast using WParam, LParam and LResult.

Msg LParam LongInt SelfMsg LParam LPARAM Self

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

LMyInteger IntPtr ListOfIntegers

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.

Before: After:

After:

. := ( ); . := ( );

. := (@ );

= ( [ ]);

= ( [ ]);

0

0

Unable to connect to Remote Host?

Code changes

New compiler defines

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.

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.

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.

ConclusionIf 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.

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

69COMPONENTSDEVELOPERS 4SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 71: Blaise 18 Uk to Taal Free

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 kbmMemTable.

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.

Delphi Starter Edition also lacks proper XML support and the local TClientDataset, which is

COMPONENTSDEVELOPERS4

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

Now available for Lazarus

Page 72: Blaise 18 Uk to Taal Free

kbmMemTable is known for being a very feature rich and fast 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.

After having downloaded and installed kbmMemTable and kbmSQL, you will find several components in the kbmMemTable section.

What’s a memory table?

So how to use it then?

As usual you will find TkbmMemTable, TkbmBinaryStreamFormat, TkbmCSVStreamFormatter and TkbmThreadDataset (which is

) components. And then as something new - TkbmMemSQL.

available for backwards compatibility and rarely used

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.

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.I’ll make it simple and just put the code we need into the Execute SQL button.

kbmSQL – Structured Query Language for your memory table

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 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

. Its only US $30 and includes source and kbmSQL, which this article is about.

www.myc4d.com

www.components4developers.com

uses

procedurebegin

50

50

120 10.10

15 20.10

10 10.10

33 10.10

12 10.10

99 20.10

19 10.10

143 20.10

199 10.10

end

// Make sure to include this unit to have support for kbmMemTable registered in kbmSQL.

// Prepare some raw data to work on.

// Register kbmMemTable1 as source for kbmMemSQL1.// Notice that we have given kbmMemTable1 a name that identifies it in SQL (table1).

// Prepare some simple SQL.

;

. ( : );

. ;

. .( , , );

. .( , , );

. .( , );

. .( , );

. ;

. ;

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

.([ , , , ]);

. . ;

. . ( , );

. ( );;

kbmSQLMemTableAPI

TForm1 Button1Click Sender TObject

kbmMemTable1 ResetkbmMemTable1 FieldDefs Add

ftStringkbmMemTable1 FieldDefs Add

ftStringkbmMemTable1 FieldDefs Add

ftFloatkbmMemTable1 FieldDefs Add

ftFloatkbmMemTable1 CreateTablekbmMemTable1 OpenkbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemTable1 AppendRecord

kbmMemSQL1 Tables ClearkbmMemSQL1 Tables Add kbmMemTable1

kbmMemSQL1 ExecSQL

'StringField1'

'StringField2'

'NumberField1'

'NumberField2'

'String 1' 'AnotherString 1'

'String 2' 'AnotherString 1'

'String 3' 'AnotherString 1'

'String 4' 'AnotherString 2'

'String 5' 'AnotherString 2'

'String 6' 'AnotherString 2'

'String 7' 'AnotherString 3'

'String 8' 'AnotherString 3'

'String 9' 'AnotherString 3'

'table1'

'select * from table1'

The following samples are based on kbmSQL v. 1.01.00

Figure 1.

Figure 2.

expertstarter DELPHI 7 and above (Win32)LAZARUS

72 COMPONENTSDEVELOPERS 4 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 73: Blaise 18 Uk to Taal Free

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:

Notice that kbmSQL operates with case insensitive field/table names, and thus the field names are uppercase by default.Let’s 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:

Result:

select StringField1,NumberField2 from table1

Field alias with descriptive text:

Result:

select StringField2 as MyField (“My string field”), NumberField2 as Numbers from table1

Calculations and string concatenation:

Result:

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

Sorting ascending:

Result: select * from table1 order by NumberField1

Sorting descending:

Result:

select * from table1 order by NumberField1 desc

Figure 3.

Figure 4.

Figure 5.

Filtering:

Result:

select * from table1 where NumberField1<30 and NumberField2>15

Figure 6.

Figure 7.

Figure 8.

Figure 9.

Structured Query Language for your memory table (Continuation 1)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 73COMPONENTSDEVELOPERS 4

Page 74: Blaise 18 Uk to Taal Free

Statistical functions:

Result:

select count(*), min(NumberField1), max(NumberField1),avg(NumberField1), sum(NumberField1) from table1

Grouping:

Result:

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

Inserting new data using SQL:

Result (in raw data source):

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

Changing data using SQL:

Result (in raw data source):

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

Deleting data using SQL:

Result (in raw data source): delete from table1 where NumberField2<20

In addition kbmSQL supports filtering using between and in keywords. Eg.

Result:

select NumberField2 from table1 where NumberField1 between 10 and 20

And

Result:

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

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.

Result:

select * from table1 where StringField1 like “*6*”

kbmSQL 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: 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 10.

Figure 11

Figure 12.

Figure 13.

Structured Query Language for your memory table (Continuation 2)

Figure 14.

Figure 15.

Figure 16.

Figure 17.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 1874 COMPONENTSDEVELOPERS 4

Page 75: Blaise 18 Uk to Taal Free

Notice that the default field name is now generated as F1. You can of course redefine the name using the AS syntax as shown earlier on. If you would like to use an expression function that is currently not available in kbmSQL it’s very easy to extend kbmSQL with new functionality.Let’s say we want to define a function that calculates

:

Then we create a new unit and add it to the project. Let’s call it myfunctions.pas.

Put the following into it:

Pythagoras' hypotenuse value

PYTHAGORAS(x,y) = sqr(x*x+y*y)

interface

uses

implementation

procedure constconst

beginif thenraise

end

function

varvar

begin2

ifthen

begin01

endelse

0

end

initialization

end

, , , , , ;

( : ; : );

. <>

. (+ ( ));

;

( : ; : ;

: ; : ): ;

, : ;

( , ); =

:= . [ ]. ; := . [ ]. ; := ( * + * ); := . [ ]. ; := ;

;

. ( ,@ , );

.

DB kbmSQLElements kbmSQLFuncAPIMath Classes SysUtils

CheckArgs AArgs TkbmSQLNodesACnt integer

AArgs Count ACnt

Exception Createinttostr ACnt

SQLPythagoras ASender TObjectAOperation TkbmSQLFunctionOperationAArgs TkbmSQLNodes AResult variant boolean

d1 d2 double

CheckArgs AArgsAOperation foExecute

d1 AArgs Node Executed2 AArgs Node ExecuteAResult sqrt d1 d1 d2 d2

AResult AArgs Node Width

Result true

kbmSQLFunctionRegistrations RegisterFunctionSQLPythagoras ftFloat

'Invalid number of arguments. Expected '

'PYTHAGORAS'

// Let it take width after first argument for function.

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 application’s uses clause.Eg.

Result:

select COS(NumberField1), Pythagoras(10,20) from table1

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.

Eg.

Result: select COS(NumberField1) from table1

Structured Query Language for your memory table (Continuation 3)

Figure 18.

Figure 19.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 75COMPONENTSDEVELOPERS 4

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

Page 76: Blaise 18 Uk to Taal Free

By Primoz Gabrijelcic

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.

High-Level Multithreading

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

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 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

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

http://code.google.com/p/omnithreadlibrary/downloads/list

http://otl.17slon.com/

AsyncThe 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

MessageBeep

procedurebegin

$FFFFFFFend

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

begin

procedurebegin

500$FFFFFFFF

end

procedure constbegin

end

end

.( : );

. := ; . ( ( ); ( ); , . . ( ( : ) . := ; ) );

;

TfrmAsync btnOnTerminatedClickSender TObject

btnOnTerminated Enabled falseParallel Async

SleepMessageBeep

Parallel TaskConfig OnTerminatedtask IOmniTaskControl

btnOnTerminated Enabled true

// executed in background thread

// executed in main thread

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!

. :=

expertstarter DELPHI 7 and above (Win32)LAZARUS

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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 1876 COMPONENTSDEVELOPERS 4

Page 77: Blaise 18 Uk to Taal Free

procedurevar begin

procedure constvar begin

procedurebegin

end

end

end

. ( : ); : ;

:= ; . ( ( : ) : ;

:= ; . (

. . . (

( +

, [ , ,

])); ); );

;

TfrmAsync btnInvokeClick Sender TObjectformThreadID DWORD

formThreadID GetCurrentThreadIDParallel Async

task IOmniTasktaskThreadID DWORD

taskThreadID GetCurrentThreadIDtask Invoke

frmAsync lbLog Items Add Format

GetCurrentThreadID taskThreadIDformThreadID

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

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

'Current thread ID: %d, task thread ID: %d, '' form thread ID: %d'

Further information about TaskConfig can be found on my

blog: http://www.thedelphigeek.com/2011/04/

configuring-background-otlparallel.html

JoinJoin is another very simple tool which allows you to start multiple background tasks and wait until they have all completed. No result is returned – at least directly, since you can always store the result in a shared variable. If your code returns a result you are better off using Future or ForkJoin.The simple demonstration of Join (shown below) starts two tasks – one sleeps for two and the other sleeps for three seconds. When you run this code, Parallel.Join will create two background threads and run RunTask1 in the first thread and run RunTask2 in the second thread. It then waits for both threads to finish, and only then will the main thread execution continue.

procedurebegin

end

procedurebegin

2000end

procedurebegin

3000end

. ( : );

. := ; ; . ([ , ]); . := ;

;

. ;

( );;

. ;

( );;

TfrmJoin btnParallelClick Sender TObject

btnJoinMethods Enabled false UpdateParallel Join RunTask1 RunTask2btnJoinMethods Enabled true

TfrmJoin RunTask1

Sleep

TfrmJoin RunTask2

Sleep

Join ensures compatibility with single-core computers. If you run the above code on a single-core machine (or if you limit the process to one core of a multicore machine) it will simply execute the tasks sequentially, without creating a thread.Join accepts anonymous methods. The above demo could also be coded as a single method executing two anonymous methods.

procedure

begin

procedurebegin

2000end

procedure begin3000

end

end

. ( : );

. := ; ;

. (

( ); , ( ); );

. := ;;

TfrmJoin btnAnonymousClick SenderTObject

btnJoinAnon Enabled false Update

Parallel Join

Sleep

Sleep

btnJoinAnon Enabled true

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 athttp://www.thedelphigeek.com/2011/07/life-after-21-paralleljoins-new-clothes.html.

FutureFuture is a tool that enables you to start a background calculation and then forget about it until you need the result of the calculation. To start a background calculation, you simply create an IOmniFuture instance of a specific type (indicating the type returned from the calculation).Future := Parallel.Future<type>(calculation);Calculation will start in the background, and the main thread will continue with its work. When the calculation result is needed, you simply query Future.Value. If the calculation has already completed its work, Value will be returned immediately. If not, the main thread will block until the background calculation is done.The example below starts a background calculation that returns the number of prime numbers in the range 1..1,000,000. While the calculation is running, it uses the main thread for “creative” work – outputting numbers into a listbox and sleeping. At the end, the calculation result is returned by querying future.Value.

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

const1000000

var

begin

functionbegin

end

for 1 to 10 do begin

20

end

end

. ( :

);

= ;

: < >; : ; : ;

:= . < >( : := ( ); ); := . . ( ( )); ( ); . ; ; ( ( , [ , . ]));

;

TfrmOTLDemoFuture btnCalcFutureClick SenderTObject

CMaxPrimeBound

future IOmniFuture integeri integernumPrimes integer

future Parallel Future integerinteger

Result CountPrimesTo CMaxPrimeBound

ilbLog Items Add IntToStr iSleeplbLog Update

Log FormatCMaxPrimeBound future Value

// create the background calculation

// simulate another task

// get the result'Num primes up to %d: %d'

High-Level Multithreading (continuation 1)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 77COMPONENTSDEVELOPERS 4

Page 78: Blaise 18 Uk to Taal Free

As with Join, there are two Future<T> overloads, one exposing the internal task parameter and another not. TaskConfig can be provided as an optional second parameter.

class function

niloverload

class function

niloverload

< >( : < >;

: = ): < >; ;

< >( : < >;

: = ): < >; ;

Future T actionTOmniFutureDelegate T

taskConfig IOmniTaskConfigIOmniFuture T

Future T actionTOmniFutureDelegateEx T

taskConfig IOmniTaskConfigIOmniFuture T

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 TCancelIsCancelled booleanIsDone booleanTryValue timeout_ms cardinal

value T booleanValue T

interfaceprocedurefunctionfunctionfunction

varfunction

end

Further information about Future can be found at:

Also, there were some changes after the 2.1 release, mostly relating to exception handling:

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

http://www.thedelphigeek.com/2011/07/life-after-21-exceptions-in.html.

ForEachParallel 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

var

begin0

2

procedure constbegin

if then

end

end

. ( : );

: ;

. := ; . ( , ) . ( ( : ) ( ) . ; ); . := . . ( (

, [ . ]));;

TfrmForEach btnForEachIntClick SenderTObject

numPrimes TGp4AlignedInt

numPrimes ValueParallel

ForEach CHighPrimeBoundExecute

value integer

IsPrime valuenumPrimes Increment

lbLog ItemIndex lbLog Items Add FormatnumPrimes Value

'%d primes'

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.

:= . ; . < >( ). ( ( : ) ( ) . ( ); );

nodeList TList Create

Parallel ForEach integer nodeList Executeelem integer

IsPrime elemoutQueue Add elem

// …

procedure constbegin

if then

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 :

and the “implementation trilogy” articles:

www.thedelphigeek.com/2010/06/omnithreadlibrary-20-sneak-preview-1.html

www.thedelphigeek.com/2011/01/parallel-for-implementation-1-overview.html

www.thedelphigeek.com/2011/01/parallel-for-implementation-2-input.html

www.thedelphigeek.com/2011/02/parallel-for-implementation-3-output.html

PipelineThe 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.

If you have data in a container that supports enumeration (with one limitation – the enumerator must be implemented as a class, not as an interface or a record) then you can enumerate over it in parallel.

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.

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

High-Level Multithreading (continuation 2)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 78 COMPONENTSDEVELOPERS 4

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

Page 79: Blaise 18 Uk to Taal Free

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 overloadclass function

const array ofconst nil

overload

: ; ; (

: ; : = ): ; ;

Pipeline IOmniPipelinePipeline

stages TPipelineStageDelegateinput IOmniBlockingCollection

IOmniPipeline

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.

TPipelineStageDelegateinput output IOmniBlockingCollection

= ( , : );

reference to procedureconst

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 const

var

beginfor 1 to do

if not thenend

( , : );

: ;

:= . ( ) ;

;

StageGenerate input outputIOmniBlockingCollection

i integer

i CNumTestElementsoutput TryAdd i Exit

The latter version takes two parameters – an array of processing stages and an optional input queue. An input queue can be used to provide initial data to the first stage. It is also completely valid to pass 'nil' as the input queue parameter and to run the first stage without any input.

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).As you can see from the code, values in input/output queues are not integers, but TOmniValue s (declared in the OtlCommon unit), which is an OTL version of Delphi's Variant.

High-Level Multithreading (continuation 3)

STAGE 1

STAGE 2

STAGE 3

---

STAGE N

TIME

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 79COMPONENTSDEVELOPERS 4

Page 80: Blaise 18 Uk to Taal Free

procedure const

var

beginfor in do

if not 2 then

end

procedure const

var

beginfor in do

if not 3 then

end

procedure const

var

beginfor in do

if not 5then

end

of

procedure const

var

begin0

for in do

end

( , : );

: ;

. ( * . ) ;

;

( , : );

: ;

. ( . - ) ;

;

( , : );

: ;

. ( . )

;;

– .

( , : );

: ; : ;

:= ; ( , ); . ( );

;

StageMult2 input outputIOmniBlockingCollection

value TOmniValue

value inputoutput TryAdd value AsInteger

Exit

StageMinus3 input outputIOmniBlockingCollection

value TOmniValue

value inputoutput TryAdd value AsInteger

Exit

StageMod5 input outputIOmniBlockingCollection

value TOmniValue

value inputoutput TryAdd value AsInteger

Exit

The last stage also reads data from its input but outputs onlyone number a sum all input values

StageSum input outputIOmniBlockingCollection

sum integervalue TOmniValue

sumvalue input

Inc sum valueoutput TryAdd sum

mod

Read more about pipelines in the OmniThreadLibrary at www.thedelphigeek.com/2010/11/multistage-processes-with.html

Fork/JoinForkJoin 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

forkJoin Parallel ForkJoin integer

max1 forkJoin Computeinteger

Result

max2 forkJoin Computeinteger

Result

Result Max max1 Value max2 Value

:= . < >;

:= . ( : := … );

:= . ( : := … );

:= ( . , . );

and then create computations owned by this instance

To access the computation result, simply call the computation object's Value function.

function begin

end

function begin

end

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 sub-computations are aggregated by calling the Max function, and the result is returned to the upper level.

function const

var

function

begin

functionbegin

end

end

beginif then

else begin2

1

endend

. ( : < >; ,

: ): ;

: < >; : < >; : ;

( , : ): < >;

:= . ( : := ( , ,

); ); ;

( - ) < := ( , ) := ( + ) div ; := ( , ); := ( + , ); := ( . ,

. ); ;

;

TfrmForkJoin ParallelMax forkJoinIOmniForkJoin integer left

right integer integer

computeLeft IOmniCompute integercomputeRight IOmniCompute integermid integer

Compute left right integerIOmniCompute integer

Result forkJoin Computeinteger

Result ParallelMax forkJoin leftright

right left CSeqThresholdResult SequentialMax left right

mid left rightcomputeLeft Compute left midcomputeRight Compute mid rightResult Max computeLeft Value

computeRight Value

My blog post at:

contains more information about ForkJoin, and shows how to implement a parallel QuickSort with the help of this OTL construct.

http://www.thedelphigeek.com/2011/05/divide-

and-conquer-in-parallel.html

High-Level Multithreading (continuation 4)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 80 COMPONENTSDEVELOPERS 4

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

Page 81: Blaise 18 Uk to Taal Free

productivity software building blocks

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 demoTo 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.

TMS Scripter Studio ProBy Wagner Landgraf & Bruno Fierens

expertstarter Delphi 5 and above

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

Preparing the GUIBefore 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 1

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 81

Page 82: Blaise 18 Uk to Taal Free

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:

implementationuses , , , , , , , , ;ap_SysUtils ap_Windows ap_Classes ap_Forms ap_Dialogs ap_StdCtrls ap_Controlsap_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 ):

procedurebegin

end

procedurebeginif thenbegin

endend

. ( : );

. ; . ;

;

. ( : );

. . ( . ); . ; ;

;

TForm1 acEditMacrosExecute Sender TObject

IDEEngine1 NewProjectIDEDialog1 Execute

TForm1 acExecuteMacroExecute Sender TObject

OpenDialog1 Execute

IDEEngine1 OpenProject OpenDialog1 FileNameIDEEngine1 RunProject

Figure 3

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.

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.

Initial scripting setup

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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 82

Page 83: Blaise 18 Uk to Taal Free

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.

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:

Executing the Hello World Macro

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

TMS Scripter Studio Pro (Continuation 2)

Figure 4

Figure 6

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

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 83

Page 84: Blaise 18 Uk to Taal Free

Figure 7

Figure 8

Full integration with the applicationThis 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

begin

procedurebegin

end

. ( : );< >

; < >

. ;

. ( , , [ , ], , ); . ( );

;

TForm1 FormCreate Sender TObjectsnip

InitScriptersnip

TForm1 InitScripter

IDEScripter1 DefineClassByRTTI TRichEdit mvPublic mvPublished true roOverwriteIDEScripter1 AddComponent RichEdit1

// New line added to FormCreate

''

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!

TMS Scripter Studio Pro (Continuation 3)

84 SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18

Page 85: Blaise 18 Uk to Taal Free

The CommentSelection macroThis 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:

TMS Scripter Studio Pro (Continuation 4)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 85

Page 86: Blaise 18 Uk to Taal Free

The GotoLine macroAs 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 13

Figure 14

Figure 12

TMS Scripter Studio Pro (Continuation 5)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 86

Page 87: Blaise 18 Uk to Taal Free

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

TMS Scripter Studio Pro (Continuation 6)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 87

Page 88: Blaise 18 Uk to Taal Free

DebuggingIt'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:

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:

Conclusion

http://www.tmssoftware.com/site/scriptstudiopro.asp

TMS Scripter Studio Pro (Continuation 7)

About the AuthorWagner 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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 88

Figure 18: Debugging in Scripter Studio

Page 90: Blaise 18 Uk to Taal Free

FPVectorial - A vector graphics library

Creating vectorial imagesFPVectorialial'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 TvVectorDocument methods to add paths, geometrical elements, text and other objects. To create a new path you must always begin with the StartPath() method, which must include information about the path's start position. Thereafter you can use any of the path segment addition methods to add path segments to this intermediate path as it is being constructed.

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.

Listing 1. Creating two vectorial documentsprogram

uses

const var

begin

try100100

0 2030 30

0 010 1020 3030 20

finally

endend

;

, , ;

= ; = ; : ;

:= . ; . := ; . := ;

. ( , ); . ( , ); . (); . (

+ , ); . ; . ( , ); . ( , ); . ( , ); . ( , ); . (); . (

+ , ); . ; ;

.

fpvwritetest

fpvectorial svgvectorialwriter fpvutils

cFormat vfSVG cExtensionVec TvVectorialDocument

Vec TvVectorialDocument Create

Vec WidthVec Height

Vec StartPathVec AddLineToPathVec EndPathVec WriteToFile

cExtension cFormat

Vec ClearVec StartPathVec AddLineToPathVec AddLineToPathVec AddLineToPathVec EndPathVec WriteToFile

cExtension cFormat

Vec Free

{$mode objfpc}{$H+}

// 10cm x 10cm

// single_line_1.svg

// polyline_1.svg

'.svg'

{$R *.res}

'single_line_1'

'polyline_1'

Felipe Monteiro de Cavalho

Besides the raster image there is another kind, the vector image, which is less well known, but widely used in fields such as commercial drawing and design, marketing, cartography, geology and all types of engineering. The basic concept behind vector (or vectorial) images is completely different from raster images. Vector images are described in terms of elements (for example text, lines, polygons, etc.) rather than pixels (dots), and this makes it possible to modify each element of the image separately. So you can change the elements' Z-order and carry out other operations which are impossible with raster images. There are hundreds of vector image formats, but a few of them have evolved to become the most widely used. For example SVG is the vector image format recommended by the World Wide Web Consortium (W3C) for web pages. Other formats supported by FPVectorial are: DXF (the AutoCAD format), Encapsulated PostScript, PDF and G-code for CNC (Computer Numerical Control) machines.An advantage of vector images over raster images is the potential of resizing vector images without resolution loss or pixellation. Since there is no unique mapping of the image to screen pixels, the actual resized onscreen appearance will depend on how the image is rendered, and it is possible during this rendering to scale it without getting a low-resolution result. Most vector image formats allow real-world units to be specified in the drawing of the image. (The most notable exception is the SVG format, which does not allow real-world unit specification).

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 Bézier 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.

expertstarter LAZARUS

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 90 COMPONENTSDEVELOPERS 4

Page 91: Blaise 18 Uk to Taal Free

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..

Figure 4: The bezier_1.svg file rendered by Inkscape

Creating textAlong 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.

FPVectorial - A vectorial graphics library (continuation 1)

Listing 2. Creating a Bézier curve

. ; . ( , ); . ( , ); . ( , , , , , ); . ( , ); . (); . ( + , );

// bezier_1.svgVec ClearVec StartPathVec AddLineToPathVec AddBezierToPathVec AddLineToPathVec EndPathVec WriteToFile cExtension cFormat

0 010 10

10 20 20 20 20 1030 0

'bezier_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 Bézier 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.

Creating curvesNow you know how to create straight lines, the next step is to create curves, and for that you need to understand how Bézier curves work. FPVectorialial uses a cubic Bézier 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 Bézier segments together. The equation of a cubic Bézier curve is shown below.

The following example shows how to create a path composed of two line segments and a Bézier curve. The previously presented TvVectorDocument.AddLineToPath() method adds the line segments, and the AddBezierToPath() method is used to draw the Bézier 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).

3 2 2 3B(t) = (1 – t) P + 3(1 – t) tP + 3(1 – t)t P + t P , t ϵ [0,1]0 1 2 3

Figure 3: The Bézier cubic curve equation

Listing 3. Creating a vectorial document with paths and text

. ; . ( , ); . ( , ); . (); . ( , ); . ( , ); . ( , ); . ( , ); . ( , ); . (); . ( , ); . ( , ); . ( , , , , , ); . ( , ); . (); .

( , , , ); . ( , , ,

);

. ( , , , ); . (

+ , );

// multi_test_1.svg

'10,10 Some text in english.''20, 20 Mówic, czesc,

Wlosku,Parabéns.'

'30, 30 森林,是一个高密

Vec ClearVec StartPathVec AddLineToPathVec EndPathVec StartPathVec AddLineToPathVec AddLineToPathVec AddLineToPathVec AddLineToPathVec EndPathVec StartPathVec AddLineToPathVec AddBezierToPathVec AddLineToPathVec EndPathVec AddText

Vec AddText

Vec AddTextVec WriteToFile

cExtension cFormat

0 2030 30

0 0100 0100 1000 1000 0

0 010 10

10 20 20 20 20 1030 0

10 10 020 20 0

30 30 0 ''multi_test_1'

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 91COMPONENTSDEVELOPERS 4

Page 92: Blaise 18 Uk to Taal Free

Figure 5: The multi_test_1.svg file as rendered by the Opera navigator.

Setting an element's colour and widthBesides 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.svgVec ClearVec StartPathVec AddLineToPathVec SetPenWidthVec SetPenColor RGBToFPColorVec EndPathVec StartPathVec AddLineToPathVec AddLineToPathVec AddLineToPathVec AddLineToPathVec SetPenWidthVec SetPenColor RGBToFPColorVec EndPathVec StartPathVec AddLineToPathVec AddBezierToPathVec AddLineToPathVec SetPenWidthVec SetPenColor RGBToFPColorVec EndPathVec WriteToFile

cExtension cFormat

0 2030 30

10255 0 0

0 0100 0100 1000 1000 0

100 255 0

0 010 10

10 20 20 20 20 1030 0

100 0 255

'pen_test_2'

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

Modifying documentsThe 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.

FPVectorial - A vectorial graphics library (continuation 2)

Listing 5. The methods to obtain elements of a document ( : ): ; : ; ( : ): ; : ;

functionfunctionfunctionfunction

GetPath ANum Cardinal TPathGetPathCount IntegerGetEntity ANum Cardinal TvEntityGetEntityCount Integer

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 Bézier segment.

The latest version of FPVectorial includes a TvEntity.Translate method which allows you to move entities very easily, without the need to know precisely how the entity was formed. Knowing how to iterate through segments of a path and how to access an entity's type can be very useful. You can see the FPVectorial class chart below, which shows more information about how the different types of entities are related.

Figure 5: The bezier_1_mod.svg file rendered by Inkscape

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 92 COMPONENTSDEVELOPERS 4

Page 93: Blaise 18 Uk to Taal Free

Figure 6: The FPVectorial class hierarchy

FPVectorial - A vectorial graphics library (continuation 3)

L

{$mode objfpc}{$H+}

// Read the file

// Now add 10 to the Y coordinate of all elements

// Write the changed file to disk

isting 6. Modifying a vectorial document

_2

program

uses const var

begin

try

for 0 to 1 dobegin

while nil dobegin

if is thenbegin

as101010

endelse if is thenbegin

2 as2 2 10

end

endend

finally

endend

;

, , , ; = ; = ;

: ; : ; : ; : ; : ; : ;

:= . ; . ( ); := . () - := . ( ); . (); . (); . <> := . ; := ; . := . + ; . := . + ; . := . + ; _ := ; _ . := _ . + ; ; . (); ; ; . ( + , ); . ; ;

.

fpvmodifytest

fpvectorial svgvectorialwriter svgvectorialreader fpvutilscFormat vfSVG cExtension

Vec TvVectorialDocument Path TPath i Integer Segment TPathSegmentDSegment T2DSegment BezSegment T2DBezierSegment

Vec TvVectorialDocument Create

Vec ReadFromFile

i Vec GetPathCount

Path Vec GetPath iPath PrepareForSequentialReadingPath Next

Path CurPoint

Segment Path CurPointSegment T2DBezierSegment

BezSegment Segment T2DBezierSegmentBezSegment Y BezSegment YBezSegment Y2 BezSegment Y2BezSegment Y3 BezSegment Y3

Segment T2DSegment

DSegment Segment T2DSegmentDSegment Y DSegment Y

Path Next

Vec WriteToFile cExtension cFormat

Vec Free

'.svg'

'bezier_1.svg'

'bezier_1_mod'

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 93COMPONENTSDEVELOPERS 4

Page 94: Blaise 18 Uk to Taal Free

Converting between vectorial formatsAnother 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.

Listing 7. Converting between vectorial formatsprocedure

var

begin

try

finally

.( : );

: ;

:= . ; . ( . ); . ( . , ); . ;

TformVectorialConverter buttonConvertClickSender TObject

Vec TvVectorialDocument

Vec TvVectorialDocument Create

Vec ReadFromFile editInput FileNameVec WriteToFile editOutPut FileName vfPDF

Vec Free endend

;;

Constant Extension Descriptionv

fPDF .pdf 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

of 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 text-based format, but

vfSVG .svg 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 software. Some software just hardcodes to 90 dpi for SVG, for example Inkscape, Opera Browser and fpvectorial. But some other software uses other values.

vfEncapsulatedPostScript .eps represent very

complex vectorial imagesvfDXF .dxf One of the formats from AutoCAD.

The DXF is designed to be used by AutoCAD when there is need for the use of the file by third-parties, because the main file format from AutoCAD, DWG is not documented and is a commercial secret of this software manufacturer.

vfCorelDRAW.cdr so any implementation of read and

write support for it relies only on guessing and reverse engineering. An attempt was made to add support for this format to fpvectorial, but without much success.

vfGCodeAvisoCNCPrototipoV5

.g

the text data may be compressed.

These files contain PostScript code inside them and can

This format has no open specification,

A G-Code format for a particular milling machine.

Drawing a vector image on the screenAs 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.Listing 8. The function rawFPVectorialToCanvas

procedure

0 01.0 1.0

( : ;

: ; : ;

: = ; : = ; : = ; : = );

DrawFPVectorialToCanvas ASourceTvVectorialDocument

ADest TCanvas ADest TFPCustomCanvasADestX Integer ADestY Integer

AMulX Double AMulY Double

{$ifdef USE_LCL_CANVAS}{$else} {$endif}

Below you can see how an example application, fpvviewer, uses FPVectorial to visualize vector images. This application was written in Lazarus and runs equally well in Windows, Linux and Mac OS X. It can be downloaded from the lazarus-ccr (Lazarus Code and Components Repository) by anonymous svn from the directory lazarus-ccr/applications/fpvviewer. Figure 7 shows how this example application uses the DrawFPVectorialToCanvas procedure to draw a vector image to the screen when the user clicks on the [Visualize] button. The area on the screen where the image is drawn is a custom control implemented as a descendant of TCustomControl. It has an off-screen TBitmap used as a buffer for drawing the full image. The display only shows the necessary section of the image each time, which makes for fast panning across the full image. The user can move around the image by dragging it or by using the keyboard arrow keys. Figure 7 demonstrates this application running under Mac OS X.

FPVectorial - A vectorial graphics library (continuation 4)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 94 COMPONENTSDEVELOPERS 4

Page 95: Blaise 18 Uk to Taal Free

FPVectorial - A vectorial graphics library (continuation 5)

Listing 9. Drawing a vectorial document to the screenprocedure

const1000100

100var

begin

try

0 0

1

finally

endend

.( : );

= ; = ; = ;

: ; : ;

:= . ; . ( . ); . . := . ; . . := . ; . . . . := ; . . . . := ; . . . ( , ,

. . , . . ); ( , . .

, , . . -

, . , - * . ); . ; . ; ;

;

TfrmFPVViewer btnVisualizeClickSender TObject

FPVVIEWER_MAX_IMAGE_SIZEFPVVIEWER_MIN_IMAGE_SIZEFPVVIEWER_SPACE_FOR_NEGATIVE_COORDS

Vec TvVectorialDocumentCanvasSize TPoint

Vec TvVectorialDocument Create

Vec ReadFromFile editFileName FileName

Drawer Drawing Width CanvasSize XDrawer Drawing Height CanvasSize YDrawer Drawing Canvas Brush Color clWhiteDrawer Drawing Canvas Brush Style bsSolidDrawer Drawing Canvas FillRect

Drawer Drawing Width Drawer Drawing HeightDrawFPVectorialToCanvas Vec Drawer Drawing

Canvas FPVVIEWER_SPACE_FOR_NEGATIVE_COORDSDrawer Drawing HeightFPVVIEWER_SPACE_FOR_NEGATIVE_COORDSspinScale Value spinScale Value

Drawer Invalidate

Vec Free

...

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/

ConclusionThis 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.

Figure 8: Turbo Circuits and Vectors in action

LinksFPVectorial internet page

Wikipedia article about Bézier curveshttp://wiki.lazarus.freepascal.org/fpvectorial

http://en.wikipedia.org/wiki/Bézier_curve

About the Author Felipe is an electrical engineer who graduated at the University of São 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 Wrocław - Poland as a quality assurance engineer.

Monteiro de Cavalho

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 95COMPONENTSDEVELOPERS 4

Page 96: Blaise 18 Uk to Taal Free

A tale of the nightingale a story for startersby Howard Page -Clark- telling the taleDetlef Overbeek - creating the project and illustrationsJean Pierre Hoefnagels - advices on coding

expertstarter DELPHI 7 and above (Win32)LAZARUS

Figure 1: The Nightingale in a natural pose

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).

We will start of course with the simplest presentation of the algorithm, written as a sequence of instructions so as to be readily understandable. For the alternative “compressed” algorithm we will organise the instructions differently and make 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 beginners (and we were all beginners once).

My personal opinion is that abbreviated code, while often very beautiful, can be hard to understand later on, especially if you have not documented it well at the time of writing. The algorithm in this project uses an array, a Windows API call (sndPlaySound) and a timer object. Nothing too difficult.

The aim of the projectWhat 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 Chinese-inspired 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.

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 self-documenting, 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) version as well the shorter “compressed” version. You will learn from looking at both. By the way, for lovers of multithreading: Primoz Gabrijelicic has written a further article about High Level Multithreading in this issue

You can download both of these nightingale projects: one unit is for Delphi the other for Lazarus. But they are interchangeable without a problem. The image data files are included with the projects, so you can see exactly how the animation works.

The sound and action have to be triggered by an event, so we 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.

(page74).

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 2: The Philips Wake-up light

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 96 COMPONENTSDEVELOPERS 4

Page 97: Blaise 18 Uk to Taal Free

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...

procedure

procedure constbegin

end

begincase of

0 11 22 33 44 55 66 77 88 99 10

10 11

45 4646 4747 4848 49

49 begin0

endend

end

. ( : );

( : ; : ); . . ( ); . := ; ;

. : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); : ( , ); ... ... : ( , ); : ( , ); : ( , ); : ( , );

: ( , );

; ;

;

TfrmNightingale Timer1Timer Sender TObject

DoPic fPic TFilename aTag integer

imgNightingale Picture LoadFromFile fPicimgNightingale Tag aTag

imgNightingale TagDoPicDoPicDoPicDoPicDoPicDoPicDoPicDoPicDoPicDoPicDoPic

DoPicDoPicDoPicDoPic

DoPic

'..\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\Nightingale3.jpg''..\jpg\Nightingale2.jpg''..\jpg\Nightingale2.jpg''..\jpg\Nightingale2.jpg'

'..\jpg\Nightingale2.jpg'

// To go the starting position

That’s what it is all about...

Const array 0..49 of string

procedure

beginif 1

then 0 else

end

procedurebegin

or

end

end

: [ ] = ( , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , );

.( : );

>= ( )- := ( );

. . ( [ ]);

;

. ( : );

( , );

. := ;;

.

FNames

TfrmNightingale Timer1Timer Sender TObject

State length FNamesStateinc State

imgNightingale Picture LoadFromFileFNames State

TfrmNightingale btnStartClick Sender TObject

sndPlaySound SND_FILENAME SND_ASYNC

Timer1 Enabled True

'..\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''..\jpg\Nightingale9.jpg''..\jpg\Nightingale9.jpg''..\jpg\Nightingale8.jpg''..\jpg\Nightingale8.jpg''..\jpg\Nightingale8.jpg''..\jpg\Nightingale7.jpg''..\jpg\Nightingale7.jpg''..\jpg\Nightingale7.jpg''..\jpg\Nightingale6.jpg''..\jpg\Nightingale6.jpg''..\jpg\Nightingale6.jpg''..\jpg\Nightingale5.jpg''..\jpg\Nightingale5.jpg''..\jpg\Nightingale5.jpg''..\jpg\Nightingale4.jpg''..\jpg\Nightingale4.jpg''..\jpg\Nightingale4.jpg''..\jpg\Nightingale3.jpg''..\jpg\Nightingale3.jpg''..\jpg\Nightingale3.jpg''..\jpg\Nightingale2.jpg''..\jpg\Nightingale2.jpg''..\jpg\Nightingale2.jpg''..\jpg\Nightingale2.jpg'

'..\wav\Nightingale_song.wav'

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.

A tale of the nightingale (continuation 1)

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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 97COMPONENTSDEVELOPERS 4

Page 98: Blaise 18 Uk to Taal Free

barns ens enDEVELOPMENT &DATABASETOOLS

Barnsten B.V. - Embarcadero Technology CentrePostbus 5010 2000 GA Haarlem NederlandTel.: +31 23 542 22 27 / Fax.: +31 84 755 52 60Web: www.barnsten.com Info: [email protected]

Page 99: Blaise 18 Uk to Taal Free

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 99COMPONENTSDEVELOPERS 4

Page 100: Blaise 18 Uk to Taal Free

• 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 requires GPU)

• 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

FireMonkey System Requirements

FireMonkey (continuation 1)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 100

Page 101: Blaise 18 Uk to Taal Free

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 101COMPONENTSDEVELOPERS 4

expertstarter DELPHI 7 and above (Win32)LAZARUS

Using the Advantage Database Client Engine Michaël Van Canneyt

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.

IntroductionThe 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:

TAdsQuery

TAdsStored

TAdsSettings

TAdsDictionary

This TDataset descendant can be used to execute SQL statements. It is equivalent to the TADOQuery, TIBQuery or TSQLQuery components in Delphi and Lazarus.

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.

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.

This component encapsulates an Advantage Data Dictionary. More is written about data dictionaries in what follows.

Creating a databaseAdvantage 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.

TAdsConnection

TAdsTable

This component represents a connection to the database. It is equivalent to the TADOConnection or TSQLConnection components in Delphi or Lazarus.

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.

There are several other Advantage components as well, butthose listed above are the most important.

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

Page 102: Blaise 18 Uk to Taal Free

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 102 COMPONENTSDEVELOPERS 4

Creating tablesOnce 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.

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.

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.

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.

Figure 2: Creating a new table

Figure 1: Creating a new data dictionary

Using the Advantage Database Client Engine (continuation 1)

Page 103: Blaise 18 Uk to Taal Free

procedure

Const

beginWith do

begin

endend

. ;

= + + + + + + + + ;

. := ; ; ;

;

TBlaiseModule CreateContentsSQL

SCreateSQL

QCreate

SQL Text SCreateSQLExecSQL

'CREATE TABLE contents ('' ISSUE Integer,'' STARTPAGE Short,'' ENDPAGE Short,'' AUTHOR NVarChar(50),'' TITLE NVarChar(100),'' ABSTRACT NMemo,'' Keywords NVarChar(200),'' PageCount Short) IN DATABASE'

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 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:

Procedure

Function String

begin

end

beginWith do

begin

end

501001

200

end

. ;

( : ; : ): ; := . . ; . := ; . := ; ;

:= ; := ; . := ; ; . . ; ( , ); ( , ); ( , ); ( , ). := ; ( , ). := ; ( , ). := ; ( , ). := ; ( , ); . ;

;

TBlaiseModule CreateContentsTable

AddDef ANameAType TFieldType TFieldDef

Result TContents FieldDefs AddFieldDefResult Name ANameResult DataType AType

TContents

TableNameTableType ttAdsADTAdsTableOptions AdsCollation

TContents FieldDefs ClearAddDef ftIntegerAddDef ftSmallIntAddDef ftSmallIntAddDef ftWideString SizeAddDef ftWideString SizeAddDef ftWideMemo SizeAddDef ftWideString SizeAddDef ftSmallIntTContents CreateTable

'contents'

'ansi'

'ISSUE''STARTPAGE''ENDPAGE''AUTHOR''TITLE''ABSTRACT''Keywords''PageCount'

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 the data dictionary (the TAdsTable component must be connected to a TAdsConnection instance).The Advantage Data Architect can also write a set of SQL statements to recreate the tables. They can be executed using the TAdsQuery component like this:

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:

CREATE KEYWORDARTICLESakeyword CHARtitle CHAR OUTPUTauthor CHAR OUTPUTissue Integer OUTPUT

DECLARE cursor1 CURSORSELECT TITLE KeyWords Author Issue FROM

CONTENTSDECLARE thelike varcharthelike select rtrim ltrim akeyword from

inputthelike thelikeOPEN cursor1

FETCH Cursor1Cursor1 TITLE like thelike

Cursor1 Keywords like thelikeInsert into Output

values Cursor1 title cursor1 Author Cursor1 issue

Close Cursor1

( ( ), ( ) , ( ) , )

, , ,

; ( );=( ( ( ))

__ );= + + ;

;

( . ) ( . ) __

( . , . , .);

; ;

;;

PROCEDURE50

10050

BEGINAS

55

While doif

or then

end ifend while

END

'%' '%'

Using the Advantage Database Client Engine (continuation 2)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 103COMPONENTSDEVELOPERS 4

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

Page 104: Blaise 18 Uk to Taal Free

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.

Accessing DataNow 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.

TAdsStoredProc This can be used to read data returned by a stored procedure (or to execute one if it does not return data).

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:

procedure

begin

end

. ( : );

. . ; . . ;

;

TTablesForm FormShow SenderTObject

BlaiseModule TIssues OpenBlaiseModule TContents Open

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

The application is in fact ready to be used, and it's possible to add or edit records in both grids. To make sure that the master-detail relationship is respected when a new record is inserted in the TContents dataset, a value is inserted in the Issues field:

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:

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 104 COMPONENTSDEVELOPERS 4

Using the Advantage Database Client Engine (continuation 3)

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

Page 105: Blaise 18 Uk to Taal Free

procedure

beginIf then

end

. ( :

);

. . :=

. ;;

TBlaiseModule TContentsAfterInsert DataSetTDataSet

TIssues ActiveTContentsISSUE AsInteger

TIssuesIssue AsInteger

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

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:

Parameterised queries

SELECT Issues.Date, Issues.Issue, Issues.Theme, Contents.Title,Contents.Author,Contents. Abstract,Contents.StartPageFROM 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

procedure

Var String

begin

With dobeginif not then

endend

. ( : );

: ;

:= + . + ; . ; ; . ( ). := ; ; ;

;

TSearchQueryForm BSearchClick SenderTObject

S

S EKeyword TextBlaiseModule QSearch

PreparedPrepare

CloseParams ParamByName AsString SOpen

'%' '%'

'KeyWord'

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.

Figure 4: Using a parameterised TAdsQuery

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 105COMPONENTSDEVELOPERS 4

Using the Advantage Database Client Engine (continuation 4)

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:

Page 106: Blaise 18 Uk to Taal Free

Now, how can this be used to establish master-detail relation-ships 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

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).

Getting data using stored proceduresWe 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)

The demonstration application shows this. However, there is a TDataset 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

procedure

beginWith do

begin

endend

. ( :

);

. ; . ( ). := . ; ; ;

;

TSearchStoredProcForm BSearchClick SenderTObject

BlaiseModule SPSearch

CloseParams ParamByName

AsString EKeyword TextOpen

'aKeyWord'

will now look and behave exactly like the first search form.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 106 COMPONENTSDEVELOPERS 4

Using the Advantage Database Client Engine (continuation 5)

ConclusionAdvantage 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.

Page 107: Blaise 18 Uk to Taal Free

Introduction to Databases Part 5: Configuring OLE DB Providers and ODBC Drivers By Cary Jensen

expertstarter 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 dbGoAs 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.

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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 107COMPONENTSDEVELOPERS 4

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 StringsIf 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 Object Inspector. Delphi responds by displaying the ConnectionString dialog box, shown in Figure 1.

Making the ConnectionYou 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.

In most cases, though, you will not define a connection string to 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.

Page 109: Blaise 18 Uk to Taal Free

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.

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.

Configuring OLE DB Providers and ODBC Drivers (continuation 1)

Figure 1

Figure 2

Figure 3

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 109COMPONENTSDEVELOPERS 4

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.

Page 110: Blaise 18 Uk to Taal Free

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.

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.

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.

Configuring OLE DB Providers and ODBC Drivers (continuation 1)

Figure 4

Figure 5

Figure 6

Figure 7

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 110 COMPONENTSDEVELOPERS 4

Page 111: Blaise 18 Uk to Taal Free

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.

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 FilesA 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.

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 FilesCreating 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:

Configuring OLE DB Providers and ODBC Drivers (continuation 1)

Figure 8

[oledb]; Everything after this line is an OLE DB initstringProvider = Microsoft.Jet.OLEDB.4.0;Data Source = C:\Program Files (x86)\Common Files\CodeGear Shared\Data\dbdemos.mdb

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 Pag 111COMPONENTSDEVELOPERS 4

Page 112: Blaise 18 Uk to Taal Free

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

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

Using ODBC DriversODBC 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.

While ODBC is a rather old technology (it is just slightly newer than ODAPI, the Open Database API, now referred to as the Borland Database Engine for Windows), it is nonetheless a universal data access mechanism available in Windows. For those databases that do not provide an OLE DB Provider, they are almost guaranteed to provide a suitable ODBC driver.

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.

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.

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 DSNs can be used by any user, but the connection information is stored in a physical file.There is more to configuring an ODBC DSN, but those topics are beyond the scope of this article.

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.

Summary

Configuring OLE DB Providers and ODBC Drivers (continuation 1)

Figure 9 Figure 10

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 112 COMPONENTSDEVELOPERS 4

Page 113: Blaise 18 Uk to Taal Free

Delphi XE2 LiveBinding

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:

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.

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.

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).

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 113COMPONENTSDEVELOPERS 4

expertstarter DELPHI XE 2

By Bob Swart

Figure 1

Figure 2

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

Page 114: Blaise 18 Uk to Taal Free

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).

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)

This will be enough to set the Caption of the TLabel to the Text property of the TEdit. However, if we make changes to the TEdit's Text, we should notify the LiveBinding that updates may be required. For this, we can implement the OnChange event of the TEdit as follows: 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 if we're already being notified of changes.

procedureconst

beginif not thenbegin

try

finally

endend

end

. ( : );

: = ;

:= ; . ( , ) :=

;

TForm5 Edit1Change Sender TObject

Busy Boolean False

Busy

Busy True

BindingsList1 Notify Sender

Busy False

{$J+}

''

Delphi XE2 LiveBinding (continuation 1)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 114 COMPONENTSDEVELOPERS 4

Figure 3

Figure 4

Figure 5

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, we have to set both the SourceComponent and the ControlComponent to the same TMemo, and the SourceExpression to something like Lines.Count * 22, to result in a height of 22 pixels for each line in the Lines property. Of course, we also need to implement the OnChange event handler to ensure that the TMemo will actually grow while we're typing.

. := . ;

" :" + ( )

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

Page 115: Blaise 18 Uk to Taal Free

LiveBinding to a StringGridA 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 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).

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:

Delphi XE2 LiveBinding (continuation 2)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 115COMPONENTSDEVELOPERS 4

Figure 6

Figure 7

Figure 8

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

Become

BLAISE PASCAL MAGAZINEsubscriber ...

Page 116: Blaise 18 Uk to Taal Free

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:

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:

Delphi XE2 LiveBinding (continuation 3)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 116 COMPONENTSDEVELOPERS 4

Figure 9

Figure 10

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:

Page 117: Blaise 18 Uk to Taal Free

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:

Delphi XE2 LiveBinding (continuation 4)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 117COMPONENTSDEVELOPERS 4

Figure 11

Figure 12

Page 118: Blaise 18 Uk to Taal Free

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:

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.

Delphi XE2 LiveBinding (continuation 5)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 118 COMPONENTSDEVELOPERS 4

Figure 13

Figure 14

Figure 15

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…

Page 119: Blaise 18 Uk to Taal Free

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).

Delphi XE2 LiveBinding (continuation 6)

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:

We can even “open up” the DataSource, to see which fields are exposed and made available by selecting that particularDataSource. 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):

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.

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 119COMPONENTSDEVELOPERS 4

Figure 16

Figure 17

Figure 18

FireMonkey LiveBindingsIn 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.

Page 120: Blaise 18 Uk to Taal Free

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).

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).

Delphi XE2 LiveBinding (continuation 7)

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 120 COMPONENTSDEVELOPERS 4

Figure 19

Figure 20

Page 121: Blaise 18 Uk to Taal Free

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.

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:

Delphi XE2 LiveBinding (continuation 8)

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:

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 Swart Training & Consultancy –

[email protected]

www.bobswart.com

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,

SEPTEMBER 2011 BLAISE PASCAL MAGAZINE 18 121COMPONENTSDEVELOPERS 4

Figure 21 Figure 22

Figure 23

Summary

Page 122: Blaise 18 Uk to Taal Free

Pag 87COMPONENTSDEVELOPERS 4

€ 60,00 + postageHARDCOVER PAPER BACK € 50,00 + postage

SUMMER OFFER:

LAZARUS the complete guideBlaise 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

Page 123: Blaise 18 Uk to Taal Free

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 € 355-person annual subscription by download € 15010 -person annual subscription by download € 30050 -person annual subscription by download €1125100 - 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)

Become

subscriber ... BLAISE PASCAL MAGAZINE

For , Linux, Win CE, Windows XP / 7

Android, iOS Mac,

Page 124: Blaise 18 Uk to Taal Free

David I Marco Cantù

Delphi-Tage 2011 in Cologne (Köln)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 "Früh 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!