Pascal Newsletter Issue 53
Pascal Newsletter #53 - 23-JANUARY-2005
Contents
1. A Few Words From the Editors
2. Code Profiling with Non-Breaking Breakpoints
3. Introducing YAPI
4. How to Set a Component's Default Event Handler
5. Forums / Mailing Lists
6. Delphi on the Net
- Components, Libraries and Utilities
- Freeware
- Borland Product Updates
- Articles, Tips and Tricks
- Tutorials and Training
- News
- Other / Misc Sites
________________________________________________________________________
1. A Few Words From the Editors
Happy New Year! And, welcome to the first issue of the Pascal Newsletter
for 2005.
We'd like to thank the authors who contributed articles: Jim McKeeth,
Owen Mooney and Peter Johnson. We're pleased to award them the prizes
for this issue:
* Peter Johnson - 'How to Set a Component's Default Event Handler'
InstallAWARE 3.0 Enterprise Edition - by MimarSinan Int. ($559.95)
InstallAWARE 3.0 for Windows Installer - by MimarSinan International
Develop setups for Windows Installer without any knowledge of MSI!
InstallAWARE automatically converts a conditionally flowing script
into a logo certifiable, ICE-compliant MSI database at build time.
The IDE features a visual UI which generates your setup script for
you automatically and you can fully customize the script behaviour.
Special limited offer: 30% off all editions, Enterprise only $559.95!
http://www.installaware.com/landingea.html
* Owen Mooney - 'Introducing YAPI'
KnowedgeBASE Vortex 2.9 - by Delphinium Software ($49.35)
KnowledgeBASE Vortex features a highly searchable and expandable
information tree viewed in outline or audited within a built-in
wordprocessor. Includes a database for stored references.
http://www.download.com/KnowledgeBase-Vortex/3000-2064-10342084.html
* Jim McKeeth - 'Code Profiling with Non-Breaking Breakpoints'
YAPI Professional - by Owen Mooney ($95)
Offers the easiest and yet powerful printing from Delphi. It provides:
WYSIWYG setup, print preview, Non database, Database, text, Grids,
tabs, bitmaps, TCanvas operation, precise positioning or free flow
positioning - all using simple "writeln" statements.
http://free.hostdepartment.com/o/owenmooney/
Special thanks to Delphinium Software for donating KnowledgeBASE Vortex
and Owen Mooney for donating YAPI Professional as ongoing prizes for
future issues. In the next issue we have three prizes up for grabs:
InstallAWARE 3.0 Professional Edition, KnowedgeBASE Vortex and YAPI
Professional. Issue #54 will be published in March so if you've got an
idea for an article, get writing!
We're also proud to continue to offer readers a special discount on
InstallAWARE 3 for Windows Installer, thanks to the nice people at
MimarSinan International. For a limited time, readers can get 30% off
by following this link: http://www.installaware.com/landingea.html
Now, on with the code...
Regards,
Dave Murray and Ernesto De Spirito
pascal-newsletter-owner@yahoogroups.com
________________________________________________________________________
Help & Manual 3.50 by EC Software - Shareware ($ 299) - Help & Manual is
a WYSIWYG help authoring tool that will aid you in creating standard
WinHelp files (.HLP), Adobe PDF files, HTML pages and the new HTML HELP
(.CHM) files introduced in Windows 98, as well as other file formats and
printed documentation, everything from a single source. This is a must-
have for any software developer. http://www.helpandmanual.com/hmpage.htm
________________________________________________________________________
2. Code Profiling with Non-Breaking Breakpoints
By Jim McKeeth <jim at mckeeth dot org>
Abstract: Sometimes it is nice to know how long a specific piece of code
takes to execute. The Borland IDE actually provides a means to
accomplish this using non-breaking breakpoints and the event log.
This article originally appeared on the Borland Developer Network (see
References) and included several images. These images can be found in
the zip archive accompanying this issue.
Either there is a point in your program when it is running much slower
then you expected, or you are debating which way would be faster to do
something. Either way, you need to profile the performance of some code.
Back in the old days we would add code to our programs to output the
start and stop time to a log for specific processes, then we would
remove it or comment it out until we needed it again. Nowadays we use
code profilers that time the execution of every line of our code and
provide us with detailed reports and graphs. We could spend weeks
examining all the data available.
Code profilers are nice, but sometimes we don't have one convenient or
we really don't want to mess with one to test a couple lines of code.
Maybe inserting all the additional debug information for a profiler to
work changes the behaviour of our program too much. Instead of going
back to adding all that code to log start and stop times we can instead
use non-breaking break points.
The major advantage of this technique over a traditional code profiler
is when you have a fairly complex program running and you want to
profile the code now without restarting it and launching the profiler.
You can target this profiling so specifically that it doesn't have the
impact on the overall behaviour of your program that other profilers do.
In instances were you have a few select routines you want to profile you
will find this technique much quicker and easier to deploy.
This article is written for Delphi 7, but the techniques should work
with little or no changes in other versions of Delphi as well as C++
Builder and Kylix. The attached code is specific for Windows, but I am
sure a resourceful Kylix programmer would know which Linux API's to use
instead.
Before I show you how to use non-breaking breakpoints to profile your
code I am going to cover some basics about non-breaking breakpoints. If
you are already comfortable with the advanced features of breakpoints
then you can skip to the section on Profiling.
What are Non-Breaking Breakpoints?
----------------------------------
I know you are probably thinking that a non-breaking break point sounds
like an oxymoron. As we will see it really is not. Cary Jensen offers a
great article titled 'Using Non-Breaking Breakpoints in Delphi'
available on the Borland Developer Network (see References). Not to
worry if you haven't read that one. I'll cover everything you need to
know here.
The Free Online Dictionary of Computing defines a breakpoint as follows:
Breakpoint
A point in a program that, when reached, triggers some special behaviour
useful to the process of debugging; generally, breakpoints are used to
either pause program execution, and/or dump the values of some or all of
the program variables. Breakpoints may be part of the program itself; or
they may be set by the programmer as part of an interactive session with
a debugging tool for scrutinizing the program's execution.
So breakpoints only generally pause program execution. Maybe a better
name would be non-pausing breakpoints. Regardless of what you call them,
we are using breakpoints that still trigger special behaviour, but don't
actually pause execution. So how do you obtain one of these funny
breakpoints? It really is very simple.
1. Create a breakpoint however you usually do (I usually use F5)
2. Bring up the Source Breakpoint Properties dialog (I usually right
click the breakpoint and choose Breakpoint Properties...)
3. Click the Advanced >> button.
4. Uncheck the Break check box.
5. Click the OK button.
Fig 1. Basic Breakpoint Properties
The Source Breakpoint Properties dialog before the Advanced button is
clicked shows the default configuration for a breakpoint. Notice it
specifies the filename and the line number that the breakpoint is on.
These are greyed out from this view because we are editing an existing
breakpoint's properties. If we were creating or editing the breakpoint
from the Breakpoint List ([CTRL]+[ALT]+[B] for the list, [CTRL]+[A] and
[CTRL]+[E] to create a breakpoint) then these fields would not be greyed
out and we could specify them.
Fig 2. Advanced Breakpoint Properties
The Breakpoint Properties dialog after the Advanced button is clicked
includes a lot of very powerful features. For now we are just interested
in the Break checkbox. Uncheck it and click OK.
Your breakpoint still looks the same. It is not disabled (as indicated
by a green line and a grey stop symbol), but if you run your program now
it won't pause on that line. If, when you run your program the line
changes colour to a dark green, then your breakpoint is on a
non-executing line. Choose one of the lines with a blue dot next to it
and repeat the process. It's ok, I'll wait.
If you did it right then your program will run without pausing on that
line. Right now this isn't very useful, but at least you know how to
make a non-breaking breakpoint.
Logging with a Breakpoint
-------------------------
Now that we have our non-breaking breakpoint we want it to write to a
log to know when the line of code it marks is executed. Go back to the
Breakpoint Properties dialog. Notice it goes straight to the Advanced
view since you have a change in the Actions section. We are going to
make another change. We are going to add text to the Log message:
combobox. When profiling my code I usually have a logging breakpoint at
the start and end of my block of code to be profiled. I usually have
those breakpoint log "Begin" and "End", respectively.
Now if you run your program after making this change you will not notice
any obvious difference. The message was logged to the Event Log, and the
Event Log can be accessed from View -> Debug Windows -> Event Log, or by
pressing [Ctrl]+[Alt]+[V]. I had three breakpoints set in my program,
with the first and last ones logging a message. This is what my Event
Log looks like:
Fig 3. Event Log
The lines in red are the ones resulting from a break point. You will
notice three "Source Breakpoint" lines and two "Breakpoint Message"
lines. Now while all the other lines are interesting they are a little
distracting so lets hide those for now. Simply [Right-Click] in the log
window and choose Properties. That will display the Debugger Event Log
Properties.
Fig 4. Debugger Event Log Properties
In the Messages groupbox you can uncheck any checkbox for a message you
do not want to see. Simply uncheck all of them including the Breakpoint
messages checkbox. The breakpoint messages checkbox is a little
deceiving. It actually removes the "Source Breakpoint" lines, leaving
the Breakpoint Message lines. You can also change the colours and other
information if you like. I find the log is easier to read for profiling
if you clear the Display process info with event checkbox as well. If
you use your Event Log for other purposes as well you will want to
remember to change these settings back when you are done.
Now when I run my program my Event Log looks like this. Hopefully yours
is similar.
Fig 5. Concise Event Log
I imagine you will agree that this is much more concise.
TIP: Instead of using Begin and End you may find other messages more
useful. Some suggestions: name of a function being called, line number,
object being accessed, name of query being run, loop definition, etc.
Profiling
---------
Now that we know how to enter logs into our event log we can use this
feature to profile our code performance. While showing that a block of
code is entered and exited is useful, it is not exactly profiling. What
we need is a measurement of time to show how long it takes to execute
some code. This can be accomplished using the Eval Expression feature of
the breakpoint. Go back to the Source Breakpoint Properties dialog. This
time put GetTickCount in the Eval Expression combobox.
GetTickCount is a Win32 API call that returns a cardinal containing the
number of milliseconds since the computer was last booted. For Delphi
programming this function is found in the Windows unit, so make sure it
is in your uses clause. While the number returned is in milliseconds,
the resolution isn't actually in milliseconds. If you have two calls
that are just a couple milliseconds apart then they may still return the
same number. This resolution can be found by calling the
GetSystemTimeAdjustment API. Also, since a cardinal (or DWORD) is used
the, the value will loop around to 0 every 49.7 days. Not that you will
probably need to worry about running on a MS Windows system that has
been up that long, but it is something to keep in mind.
In my program I added GetTickCount to all three breakpoints, leaving the
Begin and End on the first two. Running my program again generates a
much more useful Event Log.
Fig 6. Event Log With GetTickCount
The Salmon or Pink coloured lines show the results of the expression
evaluation. Notice there are two Breakpoint Message lines, and three
Breakpoint Expression lines. If we put a message on the second
breakpoint, such as "middle" then we would have an even number of each.
Otherwise we have no way to associate the GetTickCount result with a
specific place in the code. If we re-enable the Breakpoint messages to
be displayed in the Debugger Event Log Properties dialog then we would
have the line number as well. I personally prefer using meaningful
messages (maybe including the line number), but you can use whatever
best suits your needs.
As we can see for this data it took 1.5 seconds to get from the first
break point to the second, and about 1 second to get to the third. Looks
like I have some optimizing to do. If you are curious where I arrived at
those values it was simply a matter of subtracting each number from the
one after it and dividing by 1000, rounding to the nearest half second.
When using the Eval Expression feature of the breakpoints the expression
it evaluates must be referenced globally and can only be a function that
is included in your compiled code. Since Delphi has an optimizing linker
it removed functions that have no chance of being executed by the
program. GetTickCount is one of those functions that always has a chance
of getting called and is included if we call it specifically or not, so
it is always in there. If we use another function, one we write, or
another library or API call, we need to make sure it is linked in. Also,
when a method call into an object that change a field value those
changes may not actually carry over after the evaluation completes.
Advanced Profiling
------------------
What if you don't want to do subtraction in your head? Or maybe you
really need 1 millisecond resolution in your profiling. This involves
adding some code to your program, but there is a trick we can use to
ensure that the code is actually not compiled in when your program is
built without debug information.
Instead of using GetTickCount we can use the high-performance timer
routines also provided in the Windows API. These routines are not as
straightforward to use, so instead of putting in complex Eval
Expressions I suggest building some functions to make things easier.
These functions can also subtract the values between calls.
The QueryPerformanceCounter and QueryPerformanceFrequency API calls
access the high-performance timer available on most systems. I am not
aware of a situation when these would not be available. For more
information on these API calls consult the MSDN or the Windows SDK
document included with Delphi. For now just be aware that is uses
Int64's instead of Cardinals, and is accurate beyond 1 millisecond.
In the zip archive accompanying this issue you will find my library to
use these API calls. It is very simple to use; there are three
functions: ElapsedMS, ResetElapsed and UpTimeMS. Add the unit
JimProfiling.pas to your uses clause and you can use these functions in
your Eval Expressions.
ElapsedMS
Displays how many milliseconds have elapsed since the last call to
ElapsedMS.
ResetElapsed
Restart the elapsed counter.
UpTimeMS
Displays how many milliseconds have elapsed since the program started.
After running your program you have really useful, and highly accurate,
output in your Event Log.
Fig 7. Event Log Using Profiling Unit
Notice that the second call to ElapsedMS has a lower value than the
first. This is because it is the time since the first call. The UpTimeMS
call is actually greater then the sum of the two calls to ElapsedMS
because it includes the time before the call to ResetElapsed as well as
the small amount of time after the second ElapsedMS call.
TIP: If you need a line to put a breakpoint on for a call to
ResetElapsed or some other function then just add a line of Sleep(0);
While this does have a slight impact on your program it should be very
minor. The exception is if you are dealing with multiple threads since
it will give another thread a time slice when you call sleep.
All of the code in this unit in enclosed inside {$IFOPT D+} directives
so that when you compile your application without debug information it
will be left out, you don't need to remember to remove it from your uses
clause. Since the scope of the Eval Expression is global you can
actually just put the unit in any one uses clause in your application
and still be able to use it in all other units throughout your
application.
Final Tips
----------
If you want to keep your profiling setup between programming sessions be
sure to use the Autosave Project Desktop option. This will save the
location of all your windows as well as any Breakpoints you may have.
You can enable it from the Tool -> Environment Options screen, on the
Preferences tab under the Autosave Options header.
If you don't want your desktop changes to always be saved you can turn
this option on, save the project once, and then turn it off. That will
make a snapshot of things that will not change when future changes are
made. These settings are stored in the [project].dsk file, which is
plain text (INI format). You can edit it directly if you like.
Remember if you edit this file, and you have the IDE open and the auto
save enabled, then it may overwrite your changes. Also, they are tracked
by line number, so if you insert or delete a line above a line with a
Breakpoint then the it may be moved onto the wrong line.
TIP: If you specify a condition then the logging will only be carried
out when that condition is true.
The Breakpoint List screen is another valuable resource. I mentioned it
earlier as another way to create Breakpoints. You can get to the screen
using the [CTRL]+[ALT]+[B] keyboard shortcut, or from the View -> Debug
Windows -> Breakpoints menu item.
Fig 8. Breakpoint List
Notice the Action column shows the messages and expressions we are
logging. You could have various groups of profiling breakpoints, each in
a different group. Then from the context menu you can enable or disable
an entire group of breakpoints. You can also use the Enable / Disable
Group feature of another breakpoint to turn these groups on or off as
needed.
TIP: Editing (Right click or use [CTRL]+[E]) breakpoints from this
screen allows to you move them to other lines or files. This is
especially useful if you have complex actions setup and don't want to
recreate them. You can also set multiple breakpoints on a single line
(use the Keep Existing Breakpoint option).
Conclusion
----------
This technique for profiling your code using only the standard Delphi
IDE and non-breaking break points provides you with highly targeted, 1
ms accurate execution profiling times. While this doesn't completely
replace the need for a code profiling tool in your programmers toolbox,
it does provide you with another powerful alternative to create faster
programs in less time using the powerful IDE you already own.
References
----------
'Using Non-Breaking Breakpoints in Delphi' by Cary Jensen
http://bdn.borland.com/article/0,1410,31263,00.html
'Code Profiling with Non-Breaking Breakpoints' by Jim McKeeth
http://bdn.borland.com/article/0,1410,31905,00.html
'Non-Breaking Breakpoint Profiling Library 1.0' by Jim McKeeth
http://cc.borland.com/Item.aspx?id=21280
________________________________________________________________________
Vote for the Pascal Newsletter in The Borland Top 100!
http://top100borland.com/in.php?who=20
________________________________________________________________________
3. Introducing YAPI
By Owen Mooney <owenm at scottech dot net>
http://free.hostdepartment.com/o/owenmooney/
Yapi - now at version 2.
I'm 'senior' programmer now.
I cut my teeth on Fortran, did my Thesis in Algol-60 (anyone remember
that? - before Pascal) and, in the early 90's, learned to hate the
Windows API using Borland C++ and OWL ('Orrible Windows Library). Then I
discovered Delphi and that's what I've used ever since.
The one thing Delphi has always lacked is a decent printing tool.
QReports were useless for general printing. We purchased a third party
product by Nevrona. With trembling and enthusiastic fingers I opened the
manual and started to read. Oh dear! The tool never got used. It sat on
the shelf for about 4 years till I binned it last year. In the end, all
of my printing got done with the TPrinter canvas. I've heard that many
others Delphi programmers have done the same. I haven't used the new
tools with D2005 yet, so I can't comment on them. I probably won't try
them because I don't need to.
After a few years of frustration I built my own printing tools and - if
you will excuse a moment of self-congratulation - they are the best!
Design - The Programming
------------------------
I wanted the new tools to be easy to use, to follow the most important
Delphi principle - simplicity. After some years of struggling with C++
for Windows I came across Delphi and said "Ah - Yes! That's how it
should be." I wanted my printing tools to evoke the same reaction. They
shouldn't be a just a little simpler than other printing tools but a LOT
simpler - they should be stunning!
I started from first principles.
In the old days we programmed reports something like:
writeln(printer,' My Big Report'); // Centred
writeln(printer); // Empty line
writeln(printer,'The first line');
for i := 0 to count - 1 do begin
writeln(printer,'The data for line ',i,' ',Data[i]);
end;
With each writeln the dot matrix printer would burst into life and zip
across the page, adding to the noise pollution in the office.
While the technology may seem very crude by today's standards,
programming this sort of thing was easy. That's because of a very
important reason: A very complex report could contain hundreds of writes
and writelns, but the general flow of the code followed the general flow
of the report. This correspondence made programming easier to both write
and follow. You could look at a report and imagine how it would be
coded, and you could look at the code and imagine the report.
This methodology falls to pieces with modern printers and the
requirement to provide a print preview. Tools like QReports and others,
tend to have a structure that either executes SQL statements, or calls
user written event code. The flow of execution is controlled by THEIR
code, not yours. All you get is some events. If you have a complex
report you get a 'complexity blow-out' as your code tries to remember
what happens when. I don't know about you lot, but I gave up.
Yapi provides the same logic flow as the old writeln. In Yapi, the
equivalent code to the older code above is:
Paper.clear;
Header.writeln('My Big Report'); // Centered, but no counting spaces
Header.writeln;
Body.writeln('The first line');
for i:=0 to count-1 do begin
Body.write('The data for line ');
Body.writeattab(inttostr(i),1);
Body.writeattab(inttostr(Data[i]),2);
end;
Paper.preview;
This is all the code required to print the equivalent to the old dot
matrix printout, but of course with margins, nice fonts, headers and
footers, automatic pagination and a very cool print preview screen.
Design - The Formatting
-----------------------
Well of course people expect WYSIWYG control over page layout, fonts,
tabs, and other formatting. I'm not so daft as to provide any thing
else.
Yapi provides several components for this as follows:
The Paper Component:
This provides WYSIWYG set-up of all the report structure. Margins and
tabs are set-up as well as paper orientation. Typically the dimensions
are in mm but inches are also provided.
The paper component shows all of these details at design time, but is
invisible in the running program.
The Text Component:
The text components are used to set up the fonts (including colour) for
reports. These are used for the bulk of the printing.
The Tab Component:
These setup tabs. They can be dragged into position, or have their
position set (in mm or inches). Dynamic tabulation in code is also
provided.
These three components are the basic set.
Trying It Out
-------------
Lets get to the nitty gritty. How do you get something printed, and how
easy is to get started? Well the absolute simplest steps are as follows:
1. Drag a Yapi Paper component on to your form
2. Drag a Yapi Text component on to the paper.
3. Add a button to the form. In its onclick event add 3 lines of code:
YapiPaper1.clear;
YapiText1.writeln('Hello World');
YapiPaper1.preview;
That's it. Run the program, press the button and you have a print
preview screen, and another button in the preview screen gives you your
printout. The documents have a 5-minute tutorial, which does more or
less this. If you can't do that in 5 minutes you may have a promising
career as a basket weaver.
More Components
---------------
The three components above (available as a free set) allow almost any
text based printout generation. Of course people want more. Other
components are:
* A header and footer component (one component does both)
* A grid component for doing reports like a stringgrid or a dbgrid
* An image component for doing bit maps (the pictures can be determined
at design time or run time)
* A paintbox component
* A db report component
The header and footer component does (of course) page numbers.
Producing a grid was a really interesting design problem. How does one
program a grid without contradicting the rather nice design philosophy I
already had? The answer came after some beers (actually that is a
fanciful insert to relieve the boredom of an otherwise dry and technical
dissertation). The grid-text component is just like an ordinary text
component, but it draws a box around itself. Put two side by side, and
their adjoining edges merge to make one line. Put some underneath
others, and the tops and bottom merge to a single grid line. Put rows
and columns together and 'hey presto' - a grid! All of this makes grids
just as easy as ordinary tabbed text.
The image component was a reasonably obvious inclusion and works well,
but there remained the one last printing challenge - how would Yapi deal
with arbitrary graphics and text? The answer came as the yapi-paintbox.
This operates with ALL of the existing TCanvas methods.
At this point some gnome like programmer will comment: 'Hey Noddy! If
you program yapiPaintbox as a Canvas Object, why not just use the
TPrinter canvas?' The answer is simple: 'Because, Big Ears! the
yapipaintbox also provides for a print preview.'
A db report was included with V2. This is really just for those
programmers who believe you should be able to drag and drop a whole
program and complain bitterly at each and every line of code they have
to write. This component will produce a report from a TQuery or a TTable
with just one statement: yapidbreport.open
I have provided db report as a separate object file. This prevents
Delphi's database code being linked into non-database programs.
Implementation
--------------
Well - it wasn't easy!
With each write or writeln, Yapi creates a token to represent the
printed item. It stores and then uses these tokens to render the
relevant page on the print preview, or onto the TPrinter canvas. Sounds
easy doesn't it?
Each token is an object. Inheritance was used to generalise the code as
much as possible.
The object hierarchy for the relevant tokens is:
TyapiObject - Abstract class handles much of the processing
TyapiTextObject - can be either a text or a grid text
TyapiGraphicObject - can be either an image or a paintbox
Each use of write or writeln generates a new token to represent the new
item in the report.
The initial coding had these all stored in an array in the paper
component, but I had a complexity blowout. Time to rip up the code and
start again. I added a new class for each line and the complexity came
under control.
In this second version each write or writeln adds a new token in the
current line object. In addition, each writeln completes the current
line object and starts a new one. The lines are stored as a dynamic
array within the paper object.
The components themselves have been created in fairly standard manner.
The paper component inherits from TCustomPanel and operates as a line
container as well as a tool to configure the report. The tab, paintbox
and image inherit from the TGraphicControl component. Both the text and
grid-text inherit from a common abstract base class which in turn
inherits from TCustomLabel.
I was very grateful for Borland's provision of the VCL source. I crawled
through quite a lot of this to see how things were done.
The Print Preview
-----------------
The Print Preview is a standard Delphi form. It renders the printout
onto its own TPaintbox.
The print preview does what you would expect. Selecting page number,
zooming, printer selection, page range selection - all are provided.
In addition it has a couple of cute tricks. The first is the ability to
control widows and orphans. A unique feature of the Yapi preview is the
ability to change tabs and margins at run time, in the preview screen.
That pesky line at the bottom can be eliminated just by pulling the
bottom page margin up. The line disappears and automatically appears on
the next page.
Another trick of the preview screen is saving to a file. There are two
options: If you save to a file with a .csv extension, that's what you
get. The report tabs are used to separate the csv file columns. If you
save to a .bmp file you get the current page as a bitmap. I chose bmp
output format as this is supported by the basic VCL. This means you
won't have linking problems.
If you want a pdf file then I suggest you get a pdf printer driver.
Conclusion
----------
Just because Yapi is very easy to use don't mistake it for a crude tool.
There are lots of subtle tricks going on the background to make your
life easier (e.g. word wrapping, wrapping in a grid box, handling of
memo.text to mention a few).
Have a look at some of the sample reports (e.g. FishFacts.pdf) on the web
page. All samples are PDFs using a PDF printer driver.
The basic Yapi is available free. It has the basics but is still a very
useful tool. See:
http://free.hostdepartment.com/o/owenmooney/
B.T.W. Yapi stands for 'Yet Another Printer Interface.'
________________________________________________________________________
InstallAWARE 3.0 for Windows Installer by MimarSinan International
Special time limited offer: 30% off Enterprise Edition, only $559.95!
InstallAWARE is a next generation setup authoring tool with unique
features such as partial web deployment, Flash and HTML progress
billboards, ten striking setup themes, fully customizable installation
dialogs, and a setup script that automatically gets converted to a
Windows Installer file. >> http://www.installaware.com/landingea.html <<
________________________________________________________________________
4. How to Set a Component's Default Event Handler
By Peter Johnson, Copyright (c) 2004
<delphidabbler at tiscali dot co dot uk>
http://www.delphidabbler.com/
Why This Article?
-----------------
When you double click most Delphi components at design time the IDE
automatically creates an empty event handler for the default event.
Sometimes you need a different event to be used as the default. The
purpose of this article is to explain how to do this. (There is some
information on this topic in the Delphi 7 help file, but the example
provided there is incorrect).
Let's make this a little more concrete by considering these three
components:
type
TCompA = class(TComponent)
private
fOnFoo: TNotifyEvent;
fOnBar: TNotifyEvent;
fOnClick: TNotifyEvent;
fOnChange: TNotifyEvent;
fOnChanged: TNotifyEvent;
fOnCreate: TNotifyEvent;
published
property OnFoo: TNotifyEvent read fOnFoo write fOnFoo;
property OnBar: TNotifyEvent read fOnBar write fOnBar;
property OnChange: TNotifyEvent read fOnChange write fOnChange;
property OnChanged: TNotifyEvent read fOnChanged write fOnChanged;
property OnClick: TNotifyEvent read fOnClick write fOnClick;
property OnCreate: TNotifyEvent read fOnCreate write fOnCreate;
end;
TCompB = class(TComponent)
private
fOnFoo: TNotifyEvent;
fOnBar: TNotifyEvent;
fOnChange: TNotifyEvent;
published
property OnFoo: TNotifyEvent read fOnFoo write fOnFoo;
property OnBar: TNotifyEvent read fOnBar write fOnBar;
property OnChange: TNotifyEvent read fOnChange write fOnChange;
end;
TCompC = class(TComponent)
private
fOnFoo: TNotifyEvent;
fOnBar: TNotifyEvent;
published
property OnFoo: TNotifyEvent read fOnFoo write fOnFoo;
property OnBar: TNotifyEvent read fOnBar write fOnBar;
end;
Double clicking the components in the Delphi IDE creates the following
event handlers:
* TCompA: OnCreate
* TCompB: OnChange
* TCompC: OnBar
Suppose we want the OnFoo event to be created in all cases? We'll find
out how to do this in the course of this article.
Some Background
---------------
Before we can solve our problem we need to examine how Delphi 7 decides
which event to use as the default. (Things are a subtly different in
Delphi 4 - as we'll see later).
When a component is double clicked, Delphi calls the Edit method of the
registered component editor. The default component editor is
TDefaultEditor and it is this editor that is responsible for creating
the event handler. Let's examine how TDefaultEditor.Edit decides on the
the default event.
Through a round about process, the Edit method ultimately calls the
TDefaultEditor.EditProperty method once for each of the component's
properties. Rather than pass a reference to the property itself to
EditProperty, it is a reference to the associated property editor that
is actually passed. EditProperty checks the name of each event property
and records the property editor of the preferred event. These events,
in order of preference, are:
* OnCreate
* OnChange
* OnChanged
* OnClick
* the first event alphabetically
This means that if OnCreate is present it is used as the default. If it
is not present then OnChange is used, and so on. If none of the listed
events is present then the event that is first alphabetically is used.
Overriding the Default Behaviour
--------------------------------
From the discussion above it is clear that if we are to specify the
default event ourselves we need to change the way that
TDefaultEditor.EditProperty works. Luckily for us this method is
virtual, so we can subclass TDefaultEditor and override EditProperty.
The new class can be declared very simply as follows:
type
TMyCompEditor = class(TDefaultEditor)
protected
procedure EditProperty(const PropertyEditor: IProperty;
var Continue: Boolean); override;
end;
The implementation is equally straightforward:
procedure TMyCompEditor.EditProperty(const PropertyEditor: IProperty;
var Continue: Boolean);
begin
// only call inherited method if required event name
if CompareText(PropertyEditor.GetName, 'OnFoo') = 0 then
inherited;
end;
Let's look at how this works. Recall that, in the base class,
TDefaultEditor.EditProperty was called once for each property in the
component. The method chose the default event from all those it was
passed. Now, if TDefaultEditor.EditProperty was only called once then
the default event must be the one whose property editor was passed in
that single call.
Our overridden method works by ensuring that the inherited method is
only called once - for the event we want to be the default. In our
descendant class, it is TMyCompEditor.EditProperty that is called for
each property in the component. The method simply checks for a property
editor whose property has the required name ('OnFoo') and passes that
property editor - and only that property editor - to the inherited
method. And violá - OnFoo is the default event!
A Reusable Solution
-------------------
In the code we developed in the previous section we hard-wired the name
of the default event.
Our next job is to generalize the solution to make the code easier to
re-use. Since the classes are distinguished only by the name of the
default event they select, we will develop a base class that has an
abstract method that descendants override to return the name of the
required event.
Here is the declaration of the base class:
type
TCompEditorBase = class(TDefaultEditor)
protected
function DefaultEventName: string; virtual; abstract;
{ override to return name of default event }
procedure EditProperty(const PropertyEditor: IProperty;
var Continue: Boolean); override;
{ records property editor of default event }
end;
The implementation should come as no surprise - we simply replace the
hard-wired name in the earlier class with a call to the abstract
method:
procedure TCompEditorBase.EditProperty(
const PropertyEditor: IProperty;
var Continue: Boolean);
begin
if CompareText(PropertyEditor.GetName, DefaultEventName) = 0 then
inherited;
end;
We can now implement a component editor for a specific default event -
our old friend OnFoo once more - simply by overriding the abstract
DefaultEventName method as follows:
type
TCompEditor = class(TCompEditorBase)
protected
function DefaultEventName: string; override;
end;
...
function TCompEditor.DefaultEventName: string;
begin
Result := 'OnFoo';
end;
Tidying Up
----------
To get these examples to compile we need to use the Classes, SysUtils,
DesignIntf and DesignEditors units. Note that the last two units can
only be used when integrating into the IDE - they can't be used in
stand-alone applications.
We also need to register the component editor in our unit's Register
procedure as follows:
procedure Register;
begin
RegisterComponentEditor(TCompA, TCompEditor);
// etc ...
end;
Delphi 4 Differences
--------------------
In Delphi 4 TDefaultEditor.EditProperty has a different signature. It
is defined as:
procedure EditProperty(PropertyEditor: TPropertyEditor;
var Continue, FreeEditor: Boolean);
We can deal with this using conditional compilation. Assuming the
DELPHI6ANDUP symbol is defined when we are using Delphi 6 and above,
we can implement TCompEditorBase.EditProperty as follows:
procedure TCompEditorBase.EditProperty(
{$IFDEF DELPHI6ANDUP}
const PropertyEditor: IProperty; var Continue: Boolean
{$ELSE}
PropertyEditor: TPropertyEditor; var Continue, FreeEditor: Boolean
{$ENDIF}
);
begin
if CompareText(PropertyEditor.GetName, DefaultEventName) = 0 then
inherited;
end;
Additionally, we need to replace the DesignIntf and DesignEditors units
with DsgnIntf:
uses
Classes, SysUtils,
{$IFDEF DELPHI6ANDUP}
DesignIntf, DesignEditors;
{$ELSE}
DsgnIntf;
{$ENDIF}
Summary
-------
In this article we have reviewed how to change the event handler that
is opened in the IDE when a component is double clicked.
We first examined how Delphi decides which events to use for this
purpose and then developed a sub class of TDefaultEditor that can
explicitly specify the default event. We then went on to generalize the
solution by developing an abstract base class for component editors
that change the default event.
The main discussion closed with a review of the units required to
compile the new classes and looked at how to register the component
editor.
Finally we discussed the changes that need to be made to compile the
code under Delphi 4.
Demo code
---------
If you would like to experiment with this code, the zip archive
accompanying this issue contains demo code that implements the example
components and component editors discussed in this article.
__________________
Peter Johnson is a hobbyist programmer living in West Wales (UK) who
maintains the DelphiDabbler website (http://www.delphidabbler.com/)
where his articles and freeware Delphi applications & components are
published. The code for this article is also available at:
http://www.delphidabbler.com/download.php?file=article-17-demo.zip
________________________________________________________________________
Vote for the Pascal Newsletter in The Delphi Top 200!
http://top200.jazarsoft.com/delphi/rank.php3?id=latium
________________________________________________________________________
5. Forums / Mailing Lists
To join any of our forums, the best way is to subscribe from the web,
since then you'll be able to access the website features (message
archive, files section, etc). A Yahoo! ID is required for that, and you
can get yours free by registering as a Yahoo! user, but you can also
subscribe by email (you'll only have email access).
* Delphi-En: A unique forum for intermediate-level Delphi programmers.
A large group with a helpful community of members. If you are a beginner
please stay as a listener and learn from others' questions and answers.
Home Page: http://groups.yahoo.com/group/delphi-en/
Subscription: http://groups.yahoo.com/group/delphi-en/join
delphi-en-subscribe@yahoogroups.com
* Kylix: Kylix programming.
Home Page: http://groups.yahoo.com/group/KylixGroup/
Subscription: http://groups.yahoo.com/group/KylixGroup/join
KylixGroup-subscribe@yahoogroups.com
* Components: This is a forum for searching / recommending software
components (VCL and CLX components, ActiveX objects, DLL libraries,
.Net, etc.), as well as utilities, tutorials, information, etc.
Home Page: http://groups.yahoo.com/group/components/
Subscription: http://groups.yahoo.com/group/components/join
components-subscribe@yahoogroups.com
* Software Developers: A place for subjects related to software
development and the industry. For developers to share their experiences
as an academic, professional or hobbyist. It is not a programming forum,
messages here are supposed to be more general or language independent.
Home Page: http://groups.yahoo.com/group/software-developers/
Subscription: http://groups.yahoo.com/group/software-developers/join
software-developers-subscribe@yahoogroups.com
________________________________________________________________________
KnowedgeBASE Vortex 2.9 by Delphinium Software ($49.35 US)
KnowledgeBASE Vortex features a highly searchable and expandable
information tree viewed in outline or audited within a built-in
wordprocessor. Includes a database for stored references.
http://www.download.com/KnowledgeBase-Vortex/3000-2064-10342084.html
________________________________________________________________________
6. Delphi on the Net
By Dave Murray <irongut at vodafone dot net>
Components, Libraries and Utilities
===================================
Freeware
--------
* Clearer v1.7 - by Mauro Venturini (with source)
History is a great new feature of Delphi 2005. There is only a little
drawback: after project completion getting rid of all the history
files is a bit annoying. Clear makes this more easy. It also takes
care of .NET local storage accumulation due to version changes.
http://www.torry.net/tools/developers/other/Clearer1.6Clearer.zip
* Delphi Scintilla Interface Components v0.16 - Jan Pettersen (w source)
Use the Scintilla Project Syntax Highlighting edit control with Delphi
Define your own languages based on the lexers in the SciLexer.dll
(Scintilla Project), with the styles and keywords needed directly from
the Delphi designer. Tested with Delphi 7.
http://delphisci.sourceforge.net/
* Graphics32 v1.7 - by Graphics32 Team (open source, MPL)
Graphics32 is a library designed for fast 32-bit graphics handling on
Delphi and Kylix. Optimized for 32-bit pixel formats, it provides fast
operations with pixels and graphic primitives, and in most cases
Graphics32 outperforms the standard TCanvas classes. It is almost a
hundred times faster in per-pixel access and about 2-5 times faster in
drawing lines.
http://graphics32.org/
* PUses v2.2 - by Mauro Venturini (with source)
PUses implements orderly reformatting of uses clauses, including:
rewriting the unit/namespace names one per line and sorting them;
optionally adding the prefix unit (generated using PrefiIt!) names.
Supports Delphi 2005 only.
http://www.torry.net/tools/other/ide/PUses2.0PUses.zip
* TTrimmingLabel v1.0 - by Mauro Venturini (with source)
TTrimmingLabel is a Windows Forms label that can trim the Text string
to adapt it to the available Width. The trimming is controlled by the
Trimming property (of type System.Windows.Forms.StringTrimming).
Supports Delphi 2005 only.
http://www.torry.net/vcl/labels/enhancedlabels/TrimmingLabel.zip
* Undo/Redo for TeeChart Pro VCL v1.02 - by Ivo Ungermann (with source)
Component for limited or unlimited undo / redo function with TChart
(part of TeeChart Pro VCL package from Steema Software). Automatically
traced TChart events: Zoom, Unzoom, Scroll. Other chart changes traced
with a bit of code. Tested with TeeChart Pro 7 VCL, Delphi 5 - 7.
http://www.torry.net/vcl/charts/charts/TChartUndoRedo.zip
Borland Product Updates
-----------------------
* Public Beta: Delphi 8 Update 3
This patch is designed to correct issues with .NET versioning for
Delphi 8. This is a dcu breaking change. Version calculation for
dcuils and dcpils has been updated to depend on symbol names rather
than values of metadata tokens imported from .NET assemblies. This
fixes errors that users have been experiencing with updating their
service packs for NET 1.1 and will prevent any further issues.
http://community.borland.com/article/0,1410,32873,00.html
* Delphi 2005 Update 1 Now Available
This update makes Delphi 2005 rock solid!
http://community.borland.com/article/0,1410,32875,00.html
Articles, Tips and Tricks
=========================
* BDNradio: Database Features of Delphi 2005 with Ramesh Theivendran and
Joerg Weingarten
Read the chat log and listen to the replay of the live interview with
Ramesh and Joerg on Delphi 2005 database features.
http://community.borland.com/article/0,1410,32925,00.html
* BDNradio: VCL and VCL for .NET in Delphi 2005 with Seppy Bloom and
Danny Thorpe
Listen to the replay and read the chat room log of the live interview
with Danny and Seppy.
http://community.borland.com/article/0,1410,32924,00.html
* BDNradio: Unit Testing in Delphi 2005 with Mark Edington
Listen to the replay and read the chat room log of Mark Edington's
live chat on unit testing support in Delphi 2005.
http://community.borland.com/article/0,1410,32920,00.html
* BDNradio: .NET data remoting in Delphi 2005 with Ramesh Theivendran
Read the chat log and listen to the live chat with Ramesh on the easy,
flexible, and powerful way to develop .NET data remoting applications
in Delphi 2005.
http://community.borland.com/article/0,1410,32917,00.html
* BDNradio: Debugging in Delphi 2005 with Chris Hesik
Listen to the replay and read the chat room log of Chris Hesik's
interview on debugging in Delphi 2005.
http://community.borland.com/article/0,1410,32923,00.html
* BDNradio: Web Development in Delphi 2005 with Jim Tierney and
Steve Trefethen
Read the chat log and listen to the replay of this live chat with Jim
and Steve on Delphi 2005's web development support.
http://community.borland.com/article/0,1410,32881,00.html
* Recursions in Delphi - by Zarko Gajic
Recursion is a very simple, yet useful and powerful programmer's tool.
Subroutines can, and frequently do, call other subroutines, a
subroutine that activates/calls itself is called recursive. Recursion
is a general method of solving problems by reducing them to simpler
problems of a similar type. Many programmers often avoid this type of
subroutine because it can be confusing and complicated. This article
is going to make recursion in Object Pascal simple ... I hope.
http://delphi.about.com/od/objectpascalide/l/aa120799a.htm
* Graphical Combos - by Zarko Gajic
Creating owner drawn Combo Boxes in Delphi. See how to code a
graphical drop-down list; examples include a combo box of colours and
a true-type font picker.
http://delphi.about.com/od/vclusing/l/aa101700a.htm
Tutorials and Training
======================
* An Introduction to COM Programming with Delphi (6/6) - by Curtis Socha
Type Library: pros and cons, 5 steps to a Type Library, a look into
the TLB Abyss.
http://delphi.about.com/library/weekly/aa122804a.htm
* Resource Files Made Easy - by Zarko Gajic
Part 1 of a series of articles, explains how Delphi uses standard
Windows-format resource files: icons, bitmaps and cursors.
http://delphi.about.com/od/objectpascalide/l/aa113099a.htm
* Inside the EXE - by Zarko Gajic
Part 2 of a series of articles, this article will show you how to
store (and use) sound files, video clips, animations and any kind of
binary files in a Delphi executable.
http://delphi.about.com/od/objectpascalide/l/aa021301a.htm
* Web Site inside a Delphi EXE - by Zarko Gajic
Part 3 of a series of articles, shows how HTML and associated files
(pictures) can easily be included within a Delphi application.
http://delphi.about.com/od/objectpascalide/l/aa113099a.htm
* Creating and Using a Resource Only DLL with Delphi - by Zarko Gajic
Fourth article in the series about storing more than just executable
code inside a Delphi application. This part shows how to create a
dynamic link library containing only resources.
http://delphi.about.com/od/objectpascalide/l/aa113099a.htm
* Localizing Delphi Applications using StringTable Resources - Z. Gajic
Part 5 in a series of articles, while resource files enable storing
more than just program code in an EXE file, by including stringtable
resources to an application a Delphi developer can easily build
multilanguage applications. Learn how.
http://delphi.about.com/library/weekly/aa011805a.htm
News
====
* Borland Going After Services Revenue
Services are destined to play a greater part in Borland's future as
the company assists customers around Application Lifecycle Management.
http://www.cbronline.com/article_news.asp?guid=B1DA308B-4561-4A9B-83EE-F4D4EFAA07E8
* Borland Purchases Brains to Drive SDO
Borland has made its first corporate purchase in two years, acquiring
software process consulting specialist TeraQuest Metrics Inc. Borland
plans to integrate TeraQuest's expertise, encapsulated in templates
for activities such as change management, project planning and
requirements gathering, into its ALM tools, processes and services.
http://www.cbronline.com/article_news.asp?guid=C5F6EE17-B508-432B-987B-F274EC223A20
* Borland Schedules Q4 and Year End 2004 Conference Call and Webcast
Borland today announced that its fourth quarter and year end 2004
teleconference and simultaneous Webcast is scheduled to begin at
2:30 p.m. Pacific Time, on Tuesday 1st February 2005.
http://www.tmcnet.com/usubmit/2005/Jan/1106470.htm
* Firebird Database Readies SMP Release
Firebird developers are due to do an alpha release of version 2.0 of
the open source database later this month and have completed work on
SMP support, which is due to be released in late spring.
http://news.zdnet.co.uk/software/developer/0,39020387,39183292,00.htm
Other / Misc Sites
==================
* New BDN Feature: Community Calendar
At the BorCon 2004 closing session, one of the new BDN applications
launched was a community calendar, code named EventCentral. Any BDN
member can use EventCentral to post events of interest to Borland
customers anywhere around the world. Events must be approved by a
sysop to be publicly visible.
http://community.borland.com/article/0,1410,32936,00.html
* CodeFez Blogs
Blogs by Charlie Calvert, Julian Bucknall, Lino Tadros, Nick Hodges
and Steve Teixeira. Topics include software development, Delphi and
other Borland tools.
http://www.codefez.com/Default.aspx?tabid=79&newsType=NewsListing
* PGD DogFight Game Programming Competition
DelphiGamer.com recently relauched and will soon be known as "Pascal
Game Development" or PGD for short. To coincide with the relaunch, a
themed game programming competition has been announced.
http://www.pascalgamedevelopment.com/viewtopic.php?t=1747
________________________________________________________________________
Irongut's Delphi Pages
Dedicated to programming with Borland Delphi and Kylix. We have articles
on programming, Borland and Delphi news, source code and components to
use in your applications and more. >> http://www.paranoia.clara.net/ <<
________________________________________________________________________
YOU CAN HELP US
We need your help to keep this newsletter going and growing. You can
help by referring the newsletter to your colleagues:
http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php
Or you can help by voting for us in some or all of these rankings to
give more visibility to our web site and thus increase the number of
subscriptions to this newsletter:
http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E
http://www.programmingpages.com/?r=latiumsoftwarecomenpascal
http://top100borland.com/in.php?who=20
http://top200.jazarsoft.com/delphi/rank.php3?id=latium
It's just a few seconds for you that REALLY mean a lot to us.
Don't forget we also need articles and there are prizes available for
contributions. All articles will be considered but we are particularly
interested in articles about Kylix because there is so little available
online to help Kylix developers. Send articles to:
<pascal-newsletter-owner@yahoogroups.com>
We are also looking for shareware authors who would like to offer their
components or applications as prizes for articles in the newsletter. In
return you will be promoted in the newsletter and on the Latium Software
and Irongut's Delphi Pages websites. For more info contact:
Dave Murray <irongut at vodafone dot net>
________________________________________________________________________
If you haven't received the full source code examples for this issue,
you can get them from http://www.paranoia.clara.net/downloads/p0053.zip
________________________________________________________________________
This newsletter is provided "AS IS" without warranty of any kind. Its
use implies the acceptance of our licensing terms and disclaimer of
warranty you can read at http://www.latiumsoftware.com/en/legal.php
where you will also find a note about legal trademarks. Articles are
copyright of their respective authors and they are reproduced here with
their permission. You can redistribute this newsletter as long as you do
it in full (including copyright notices), without changes, and gratis.
________________________________________________________________________
Main page: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php
Group Home: http://groups.yahoo.com/group/pascal-newsletter/
Subscribe: pascal-newsletter-subscribe@yahoogroups.com
Unsubscribe: pascal-newsletter-unsubscribe@yahoogroups.com
Problems with your subscription? pascal-newsletter-owner@yahoogroups.com
________________________________________________________________________
Latium Software: http://www.latiumsoftware.com/en/index.php
Irongut's Delphi Pages: http://www.paranoia.clara.net/
Copyright 2005 by Ernesto De Spirito + Dave Murray. All rights reserved.
________________________________________________________________________