Pascal Newsletter #25 - 18-AUG-2001
INDEX
1. A FEW WORDS FROM THE EDITOR
2. IBADMIN 3.2 - COMPLETE INTERBASE SQL TOOL
- What is IBAdmin?
- Editions
- Features
- Prices and ordering
- Downloads
- More information
3. DIVING INTO DELPHI (I)
- A necessary theoretical introduction
- A little bit of history
- Paradigms and abstraction
- Some final words
- Bibliographical references
- Glossary
4. DOWNLOADS FROM BORLAND'S WEB SITE
5. EMBEDDING FILES AS RESOURCES IN A DELPHI EXECUTABLE
6. TRICKS
- Moving rows and columns of a StringGrid by code
- Preventing the user from selecting text in a Memo control
- Deactivating the context menu
- Sorting a TListView by the column clicked by the user
- Updating a table with data from another table using Local SQL
- Looking for text in any part of a field
- Painting Rows in a Delphi 4 DBGrid
7. DELPHI ON THE NET
________________________________________________________________________
1. A FEW WORDS FROM THE EDITOR
In this issue I'm glad to introduce IBAdmin, one of the best Interbase
administation tools available, as well as the first part of the article
"Inside Delphi" by MSc. Víctor Lorenzo Prado.
Regards,
Ernesto De Spirito
eds2008 @ latiumsoftware.com
__________________
English-language editor: Matthew J. Brock <mjb@delphi-programmer.com>
________________________________________________________________________
JfControls Library. Multi-language. Multi-appearance. Skins. Privileges.
More than 40 integrated and customizable components. Impressive GUI.
Centralized resources administration. Multiple programming problems
solved. For Delphi 3-2006 & C++ Builder 3-6. http://www.jfactivesoft.com
________________________________________________________________________
2. IBADMIN 3.2 - COMPLETE INTERBASE SQL TOOL
What is IBAdmin?
================
IBAdmin is a DBA/Development tool for managing Interbase servers and
databases. IBAdmin provides many capabilities to help with your DB
design and management. You can use the "Database Designer" to visually
design database structure, the "Grant Manager" to manage users, or the
"SQL Debugger" which can be used to debug stored procedures and
triggers. Comfortable code editing is possible with Code-Insight and
Code Completion. Other features include "Database Comparer", where you
can easily upgrade deployed databases and "Dependencies Explorer" which
will show you all dependencies between database objects.
Editions
========
IBAdmin 3 is available in three different versions:
* IBAdmin Professional - full-featured version.
* IBAdmin Standard - this is the same as Professional without the
Database Designer (useful when you don't need the built-in CASE tool).
* IBAdmin Lite - includes only basic DB management ability, Database
Comparer, Grant Manager and Interbase 5.x to 6.0 migration wizard
(useful for client deployment and small amount of Database coding).
Features
========
* Easy and comfortable metadata editors.
* Data View with data sorting and export capability.
* Description for all database objects (except generators)
* Any object can be duplicated.
* Using the SQL Editor you can:
- run any SQL or DDL statement
- get full execution statistics
- run parametrized queries
- use Visual Query Builder to visually build queries
- run query in the background
- save your queries in the "Favorites" list
- save and load session query history
- export query results to the Excel files
* Extract database metadata:
- extract only selected objects
- extract table data as INSERT (...) INTO XXX VALUES(...)
- print metadata
* Search metadata [S]
* Stored procedure debugger with breakpoints [S]
* Database Designer [P] - CASE tool for Interbase databases
* Object Grouping [S]
* Dependencies Explorer [S]
* SQL Scripts execution
* Database Comparer
* Grant Manager
* Test Data Generator [S]
* Interbase 5.x to 6.0 migration wizard
* SQL Code templates
* Keywords list setup
* Interbase 6.0 support includes:
- Backup/restore
- User Manager
- Database Shutdown and Restart
- Server Log
- Extended database properties (dialect, sweep interval, read-only
flag, forced writes)
Legend:
without sign - included in all versions.
[S] - included in Standard and Professional versions.
[P] - included in Professional version only.
Prices and ordering
===================
Up to three licenses, the costs per license are shown below:
- IBAdmin 3.2 Professional............$ 229 (USD)
- IBAdmin 3.2 Standard................$ 159 (USD)
- IBAdmin 3.2 Lite....................$ 79 (USD)
If you buy four or more licenses, the costs per license are:
- IBAdmin 3.2 Professional............$ 199 (USD)
- IBAdmin 3.2 Standard................$ 129 (USD)
- IBAdmin 3.2 Lite....................$ 69 (USD)
In the case of IBAdmin 3.2 Lite, for eight or more licenses, the cost
per license is $ 59 (USD).
The are many choices of payment thru ShareIt! For more information about
prices, discounts, and ordering, visit:
http://www.sqlly.com/order.html
Downloads
=========
There's a fully-functional shareware version you can download to try the
software for 30 days:
http://www.sqlly.com/files/ibadmin3.zip
An additional tool for IBAdmin 3 is available. IBAdmin Model Viewer can
be used for viewing and printing the database models created with
Database Designer. IBAdmin Model Viewer can be downloaded from:
http://www.sqlly.com/files/dbm_viewer.zip
More information
================
You can see some screenshots of IBAdmin at:
http://www.sqlly.com/ibadmin_screenshots.htm
For more information, you can visit the web site of SQLLY Development,
the creators of IBAdmin:
http://www.sqlly.com/
---------------------
Portions of this article were taken from SQLLY Development's web site
and are Copyright (c) 1999-2001 SQLLY Develpoment Inc. - All Rights
Reserved. They have been reproduced and modified here with the author's
permission.
________________________________________________________________________
3. DIVING INTO DELPHI (I)
By Víctor Lorenzo Prado <vlorz@yahoo.es>
A necessary theoretical introduction
====================================
In the theory of programming we usually find our teachers putting a
great deal of effort in making sure we follow the set of rules and
paradigms that, like a magic wand, will turn us into "Real Programmers".
But, "unfortunately", sometimes we are forced to stray a little bit from
their teachings in order to get the best out of our compiler and the
code it generates.
This first part will introduce some basic concepts on the necessity of
making abstractions in order to obtain quality software. The subsequent
parts will show us how the opposite, not abstracting completely from the
internals of the code implementation, could be very helpful for a better
understanding of the reasons and purposes of things, and the strengths
and weaknesses of the compiler we use.
A little bit of history
=======================
Some years ago, computer programming was considered an Art. I remember
books entitled "The Art of Computer Programming", "Programming as an
Art" and others; and somehow it was. Think a little of the scene during
those times: the most common computers were the PC-XT and compatibles, a
"poor", "tiny" and "defenseless" computer if we think in the limited
possibilities of its Intel 8088 central processor (16 bits internal and
8 bits external, 4.77 MHz CLK), with RAM memory of up to 640 Kbytes
(although it could be expanded to over 1 MByte using memory expansion
cards), together with its "primitive" operating system MS-DOS. With
this type of computer each byte of data, each byte of code counts, and
therefore the programmer cannot abstract from the kind of computer that
he uses when he generates his program code.
At present times, the scenario is very different, so much so that the
potential of today's personal computers are some years ahead (depending
on technology and according to the criteria of specialists) from the
average potential required by the software that is being produced
nowadays. If years ago the art of computer programming lay in obtaining
programs as small and fast as possible in order to fully exploit a
computer with such limited potential, today the art of computer
programming lies in how to attain an abstraction level that allows the
creation of reliable and robust models for the behaviour of objects and
entities from the real world. The availability of RAM memory or hard
disk space is no longer a major problem.
Now, let's see some concepts that will be useful to us as programmers.
Paradigms and abstraction
=========================
First, let's take a look at some of the most important paradigms in
programming theory.
Surely we will agree that every software developer always wishes to
produce quality code that is:
- PORTABLE: Who would be interested in buying a software that cannot be
used in his PC?
- EXTENSIBLE and MANTAINABLE: Who would be interested in buying
obsolete software which is not being constantly upgraded and
incorporating the latest technologies?
- RE-USABLE: Would a programmer be interested in buying or producing a
component library that is not useful for anything?
- RELIABLE: Does anybody like to be deceived or see a whole day of
work destroyed in a matter of seconds?
If we analyze what is discussed [1] about object oriented design and
[2] about the design of software for data acquisition systems (and in
related papers by other authors), we can see the extraordinary
importance that ABSTRACTION has on the software quality - quality that
is measured in part by the criteria listed above.
Let's see the following example:
Example 1. Let us suppose we have a program that is an instructive game
where images and sounds are generated. Here is a small/big problem:
accessing the video hardware (video controller card) and the generation
of sounds (sound card). Let's focus on the access to the sound card and
see the various levels of abstraction:
1. Not to consider any abstraction at all and perform direct access to
the hardware using "simple" read/write operations to the card ports.
But... Do all sound cards have the same INTERFACE? The answer is NO
and this generates a tremendous headache for us.
Not making abstractions regarding the physical implementation of the
hardware forces software to "know" all the possible hardware
configurations it will have to work with, and therefore, all the
data formats the hardware will accept or return.
Can you name software packages that are in this situation? Sure you
can, there are some that have been very popular for both
entertainment and professional applications.
*** BY DEVELOPING SOFTWARE WITHOUT CONSIDERING ABSTRACTIONS, WE
ARE GREATLY LIMITING ITS ABILITY TO ADAPT TO NEW OR CHANGING WORK
CONDITIONS ***
2. To include a set of APIs as part of the operating system (extensions)
or the software that will allow developers to obtain the necessary
functionality of the hardware with relative independence over its
implementation.
Ah..., this sounds better. Now it would be enough with, "solely",
making a set of calls to some API functions using well documented
data formats. How could the software be updated? By updating the
APIs. How could it be made possible for the APIs to be selected at
run time? Using APIs in the form of DLLs with explicit binding.
But... Again a "but"? Yes. When using the functions from an API, the
programmer is totally RESPONSIBLE for the INTEGRITY OF THE DATA. If
a function in charge of making some initialization work gives as a
result a block of data that will be used by another function (and
that, in this case, could precisely be the one that does what needs
to be done), it is only the programmer and nobody else who is
responsible for maintaining the data safety. And there are many
cases where those blocks of data are no more than intermediate data
without another utility.
An additional problem is added to this: an API, after all, is simply
a set of functions grouped in a DLL, and like all functions, they
need their parameters given using a specific syntax. If abstractions
without respect for the data are made then using syntax with strong
type specifications for the input parameters limits the development
process of an API. If it is required that a newer version maintains
compatibility with previous ones, the syntax for passing parameters
must remain the same. An error in the initial design of the API
interface, for the sake of compatibility, could be dragged on for
life.
Can you name examples? Of course you can, do you remember those
Windows API functions that end with Ex()? Surely for all of them
you'll find previous versions without the Ex() termination.
*** DEVELOPING SOFTWARE WITHOUT CONSIDERING ABSTRACTIONS OF THE
DATA TYPES AND THE INTERFACES OF THE FUNCTIONS THAT WILL PROCESS THEM
WILL ENORMOUSLY LIMIT ITS DEVELOPMENT PROCESS ***
3. To make a double level of abstraction, of data and hardware, using
programming tools that allow us to encapsulate, in a same entity, the
data and the methods of processing. Nothing more similar to Object
Oriented Programming (OOP), right?
Ah... The dream..., the great paradigm of programming - not to have
to know in detail how things are done at a low level, but solely who
must make them and, of course, how to tell him to.
Has anyone tried to develop a Delphi application without using the
VCL? How much does his productivity diminish?
*** BY CREATING OF A SET OF CLASSES THAT ENCAPSULATE ALL THE
NECESSARY FUNCTIONALITY WE WILL BE ABLE TO CREATE ROBUST AND
RELIABLE SOFTWARE, EASILY EXTENSIBLE AND --MAINLY-- MAINTAINABLE ***
Some final words
================
Theory is extremely useful when we want to do things in a professional
and elegant way, but by itself it isn't everything. There are cases
where "to abstract from abstractions" can be very useful in obtaining
the maximum performance from the compiler.
Some of these cases will be treated in next sections; some of them will
be very useful for a better understanding of how Delphi works while some
others are just simple tricks to obtain better performance from the
generated code.
Bibliographical references
==========================
[1] - Grady Booch; Object Oriented Design; 1991, pp. 39-41, 49-53.
[2] - Lorenzo Prado, Victor P.; "La Arquitectura del Software:
Importancia en el Diseño de los Sistemas de Adquisición de Datos";
Revista Ingeniería Electrónica, Automática y Computación.
ISPJAE(CUBA), Volumen XXII, No. 2, 2001, pp. 54-59.
Glossary
========
API - Applications Programmer Interface. Set of function libraries (in
DLL form) that give support to application development without
having to know exactly the technical specifications of the
hardware or the inner workings of the operating system.
CLK - Clock signal (timing) of the processor. It determines the
frequency at which instructions are executed. The higher this
frequency, the greater the microprocessor's execution speed will
be. When it is said "this processor works at THIS MANY MHz", it
doesn't necessarily mean that the entire computer will be using
that clock frequency. Generally, only the central processing unit
core works at high frequencies (mainly because of physical
limitations that cause signal delays and radiation in the form of
electromagnetic waves) while most of the computer works at lower
frequencies already standardized.
DLL - Dynamic Link Library. Functions library that is linked (explicitly
or implicitly) with the application at runtime.
KByte - Kilo Byte. 1 KByte = 1024 Bytes.
MByte - Mega Byte. 1 MByte = 1024 KBytes = 1048576 Bytes.
RAM - Random-Access Memory. Memory to which the processor can have
access for reading and writing. It is where the data and programs
are stored when they are executed.
------------------
Copyright (c) 2001 Víctor Lorenzo Prado
________________________________________________________________________
4. DOWNLOADS FROM BORLAND'S WEB SITE
Kylix Open Edition is ready for free download from Borland's site:
* Kylix Open Edition
http://www.borland.com/kylix/openedition/
As I mentioned before, the license of this edition only allows the
creation of applications distributable exclusively under the terms of
the GNU GPL.
The one that in some way would be the equivalent of Kylix Open Edition
in the Windows environment is Delphi 6 Personal. Its license precludes
its commercial use (neither direct nor indirect), and it is also
available for free download:
* Delphi 6 Personal
http://www.borland.com/delphi/personal/index.html
Also available for download are 30-day evaluation versions of Delphi
6 Enterprise and Kylix Server Developer Edition:
* Delphi 6 Enterprise - Trial Edition
http://www.borland.com/delphi/trial6/index.html
* Kylix Server Developer Edition - Trial Edition
http://www.borland.com/kylix/trial/
In the third step of the download process don't forget to click the
link "Send me a serial number and activation key via email" to receive
by email the serial number and activation key that is required to be
able to install the product.
________________________________________________________________________
5. EMBEDDING FILES AS RESOURCES IN A DELPHI EXECUTABLE
It is possible to embed any kind of file in an executable using resource
files (*.RES). Certain kinds of resources are recognized by the API and
can be used directly. Others are simply taken as binary data and its up
to you to use them. In this article we will see examples of both kinds.
To create the resource file we start with the source file (*.RC), for
example named RESOURCES.RC, which is a simple text file that contains
the resource entries (name, class and file):
sample_bmp BITMAP sample.bmp
sample_ico ICON sample.ico
sample_cur CURSOR sample.cur
sample_ani ANICURSOR sample.ani
sample_jpg JPEG sample.jpg
sample_wav WAVE sample.wav
sample_txt TEXT sample.txt
The names of the resources (sample_bmp, sample_ico, etc.) are arbitrary.
The kind of resource may be one recognized by the APIs (BITMAP, ICON,
CURSOR) or arbitrary (JPEG, WAVE, TEXT). The file names specify the
files that will be included in the .RES file (and later in the .EXE).
Now we have to compile the .RC file to produce the .RES file.
For that we can use the Borland Resource Compiler (brcc32.exe) that you
can probably find in Delphi's BIN folder. It's a simple command-line
utility that expects the name of the source file as parameter:
C:\DELPHI\P0025>brcc32 resources
Borland Resource Compiler Version 5.40
Copyright (c) 1990, 1999 Inprise Corporation. All rights reserved.
C:\DELPHI\P0025>_
To instruct the linker to embed the resource file in the executable,
we use the resource file directive ($R or $RESOURCE) in our Pascal
source code:
{$R resources.res}
Loading the resources in your application is easy for the "recongnized"
resources like BITMAP, ICON and CURSOR since the Windows API provides
functions (LoadBitmap, LoadIcon and LoadCursor respectively) to get
handles for these elements, that for example we can assign to the Handle
property of the corresponding object:
Image1.Picture.Bitmap.Handle :=
LoadBitmap(hInstance, 'sample_bmp');
Icon.Handle := LoadIcon(hInstance, 'sample_ico');
Screen.Cursors[1] := LoadCursor(hInstance, 'sample_cur');
For more alternatives when loading image resources, see the API
LoadImage.
Other resources are little bit more difficult to manage. Let's start
with JPEG images. The following function uses TResourceStream to load
the resource as a stream that will be loaded into a TJPEGImage object:
function GetResourceAsJpeg(const resname: string): TJPEGImage;
var
Stream: TResourceStream;
begin
Stream := TResourceStream.Create(hInstance, ResName, 'JPEG');
try
Result := TJPEGImage.Create;
Result.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
Example:
var
Jpg: TJPEGImage;
begin
// ...
Jpg := GetResourceAsJpeg('sample_jpg');
Image2.Picture.Bitmap.Assign(Jpg);
Jpg.Free;
// ...
end;
For WAV files we need a pointer to the resource loaded in memory, and
for a text file we need to load a resource in a string. We can do it
using TResourceStream, but let's see an example using the API:
function GetResourceAsPointer(ResName: pchar; ResType: pchar;
out Size: longword): pointer;
var
InfoBlock: HRSRC;
GlobalMemoryBlock: HGLOBAL;
begin
InfoBlock := FindResource(hInstance, resname, restype);
if InfoBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
size := SizeofResource(hInstance, InfoBlock);
if size = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
GlobalMemoryBlock := LoadResource(hInstance, InfoBlock);
if GlobalMemoryBlock = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
Result := LockResource(GlobalMemoryBlock);
if Result = nil then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
function GetResourceAsString(ResName: pchar; ResType: pchar): string;
var
ResData: PChar;
ResSize: Longword;
begin
ResData := GetResourceAsPointer(resname, restype, ResSize);
SetString(Result, ResData, ResSize);
end;
Sample calls:
var
sample_wav: pointer;
procedure TForm1.FormCreate(Sender: TObject);
var
size: longword;
begin
...
sample_wav := GetResourceAsPointer('sample_wav', 'wave', size);
Memo1.Lines.Text := GetResourceAsString('sample_txt', 'text');
end;
Once we have the wave resource loaded into memory we can play it as
many times as we want by using the API sndPlaySound declared in the
MMSystem unit:
procedure TForm1.Button1Click(Sender: TObject);
begin
sndPlaySound(sample_wav, SND_MEMORY or SND_NODEFAULT or SND_ASYNC);
end;
There are some resources (like fonts and animated cursors) that can't
be used from memory. We necessarily have to save these resources to a
temporary disk file and load them from there. The following function
saves a resource to a file:
procedure SaveResourceAsFile(const ResName: string; ResType: pchar;
const FileName: string);
begin
with TResourceStream.Create(hInstance, ResName, ResType) do
try
SaveToFile(FileName);
finally
Free;
end;
end;
The following function makes use of the previous one to save a
resource in a temporary file:
function SaveResourceAsTempFile(const ResName: string;
ResType: pchar): string;
begin
Result := CreateTempFile;
SaveResourceAsFile(ResName, ResType, Result);
end;
The discussion of the function CreateTempFile falls beyond the scope
of this article and its implementation can be seen in the example
attached to the newsletter.
The following function makes use of SaveResourceAsTempFile to save an
animated-cursor resource to a temporary file, then it loads the cursor
from the file with LoadImage and finally deletes the temporary file.
The function returns the handle returned by LoadImage:
function GetResourceAsAniCursor(const ResName: string): HCursor;
var
CursorFile: string;
begin
CursorFile := SaveResourceAsTempFile(ResName, 'ANICURSOR');
Result := LoadImage(0, PChar(CursorFile), IMAGE_CURSOR, 0,
0, LR_DEFAULTSIZE or LR_LOADFROMFILE);
DeleteFile(CursorFile);
if Result = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
end;
Sample call:
Screen.Cursors[1] := GetResourceAsAniCursor('sample_ani');
Form1.Cursor := 1;
Well, that's it. I hope you find it useful. You can find more
information about resource files in the MSDN Library:
http://msdn.microsoft.com/library/en-us/winui/hh/winui/rc_6cs3.asp
________________________________________________________________________
6. TRICKS
Moving rows and columns of a StringGrid by code
===============================================
The user can move rows and columns of a StringGrid with the mouse. Can
it also be done by code? In the help for TCustomGrid you can see the
methods MoveColumn and MoveRow, but they are hidden in TStringGrid. We
can make them accessible again by subclassing TStringGrid and declaring
these methods as public:
type
TStringGridX = class(TStringGrid)
public
procedure MoveColumn(FromIndex, ToIndex: Longint);
procedure MoveRow(FromIndex, ToIndex: Longint);
end;
The implementation of these methods simply consists of invoking the
corresponding method of the ancestor:
procedure TStringGridX.MoveColumn(FromIndex, ToIndex: Integer);
begin
inherited;
end;
procedure TStringGridX.MoveRow(FromIndex, ToIndex: Integer);
begin
inherited;
end;
You don't have to register this component in the Components Palette.
Use a TStringGrid or any TCustomGrid descendant, and when you need to
call these methods simply cast the object to the new class. For
example:
procedure TForm1.Button1Click(Sender: TObject);
begin
TStringGridX(StringGrid1).MoveColumn(1, 3);
end;
Preventing the user from selecting text in a Memo control
=========================================================
The easiest way would be to set the Enabled property of the Memo
(or Edit) control to False so that the control cannot receive events.
This drawback to this method is the user won't be able to scroll the
text and the disabled text looks bad.
In order to prevent the user from writing in the memo, we set its
ReadOnly property to True.
To prevent the user from selecting text with the mouse, we generate the
handler of the MouseMove event of the control and write the following
code:
procedure TForm1.Memo1MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
begin
if ssLeft in Shift then
Memo1.SelLength := 0;
end;
In order to prevent the user from performing a selection using the
keyboard, we generate the handlers of the KeyDown and KeyUp events,
assigning the OnKeyDown and OnKeyUp properties to the same procedure:
procedure TForm1.Memo1KeyDownUp(Sender: TObject;
var Key: Word; Shift: TShiftState);
begin
if (ssShift in Shift) and (Key in [VK_LEFT, VK_RIGHT, VK_UP,
VK_DOWN, VK_PRIOR, VK_NEXT, VK_HOME, VK_END]) then
Key := 0;
end;
Deactivating the context menu
=============================
When we right-click on an Edit component (or other components that
allow editing such as MaskEdit, Memo, DbEdit, etc.), by default
the system's context menu pops up with options to undo, copy, paste,
etc.
If for any reason we don't want this menu to be displayed, one way is
to simply put a TPopupMenu component on the form and assign it to
the PopupMenu property of the components whose context menu we want to
disable.
Sorting a TListView by the column clicked by the user
=====================================================
We want the following behaviour for a ListView:
* When the user clicks on a column header, the ListView should be sorted
by that column
* The initial sort order should be ascending. If the user clicks on the
same column again, the sort order should be toggled. If the user
clicks on another column, the sort order of the new column should be
the same as the last sorted column.
For the implementation we need two variables to hold the last column
clicked by the user and the current sort order:
var
LastSortedColumn: integer;
Ascending: boolean;
We can initialize them when the form is created:
procedure TForm1.FormCreate(Sender: TObject);
begin
LastSortedColumn := -1;
Ascending := True;
end;
In the ColumnClick event of the ListView we determine the sort order and
perform the sort:
procedure TForm1.ListView1ColumnClick(Sender: TObject;
Column: TListColumn);
begin
if Column.Index = LastSortedColumn then
Ascending := not Ascending
else
LastSortedColumn := Column.Index;
TListView(Sender).CustomSort(@SortByColumn, Column.Index);
end;
SortByColumn is a function that should be previously declared and is the
function used by CustomSort to compare two items. The value passed to
the Data parameter of CustomSort will be passed as the Data parameter to
SortByColumn and we use it for the sort column:
function SortByColumn(Item1, Item2: TListItem; Data: integer):
integer; stdcall;
begin
if Data = 0 then
Result := AnsiCompareText(Item1.Caption, Item2.Caption)
else
Result := AnsiCompareText(Item1.SubItems[Data-1],
Item2.SubItems[Data-1]);
if not Ascending then Result := -Result;
end;
Making a form "always visible"
==============================
To make a form always visible above other forms either belonging to the
same application as well as other applications, we can change the
FormStyle property to fsStayOnTop. Later we can switch back to
normal by setting FormStyle to fsNormal. However, changing the FormStyle
property at runtime isn't recommended. Instead, we should use the
Windows API function SetWindowPos, passing it HWND_TOPMOST as the second
parameter to activate the effect:
procedure TForm1.Button1Click(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE + SWP_NOSIZE);
end;
To deactivate this effect, we call SetWindowPos again, this time passing
it WND_NOTOPMOST as the second parameter:
procedure TForm1.Button2Click(Sender: TObject);
begin
SetWindowPos(Handle, WND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE + SWP_NOSIZE);
end;
Updating a table with data from another table with Local SQL
============================================================
+-------------+ +-------------+
| Orders.db | | Customer.db |
+-------------+ +-------------+
| CustNo | <<---------> | CustNo |
| ShipToAddr1 | | Addr1 |
| ShipToAddr2 | | Addr2 |
+-------------+ +-------------+
Assuming that we wanted to update the fields ShipToAddr1 and ShipToAddr2
of the Orders.db table with the values of the fields Addr1 and Addr2
respectively from the table Customer.db, for those records of Orders
that have both fields blank, and joining the tables by the field CustNo
present in both tables, perhaps we would be temped to write:
UPDATE Orders INNER JOIN Customer
ON Customer.CustNo = Orders.CustNo
SET ShipToAddr1 = Addr1, ShipToAddr2 = Addr2
WHERE ShipToAddr1 = "" AND ShipToAddr2 = ""
However, in Local SQL (the one used by the BDE), joins are not supported
in the UPDATE statement, and we have to use subqueries to achieve the
expected result:
UPDATE Orders
SET ShipToAddr1 = (SELECT Addr1 FROM Customer WHERE
Customer.CustNo = Orders.CustNo),
ShipToAddr2 = (SELECT Addr2 FROM customer WHERE
Customer.CustNo = Orders.CustNo)
WHERE ShipToAddr1 = "" AND ShipToAddr2 = ""
In the "UPDATE statement" topic of the Local SQL Guide you can find an
example of a 1-to-many relationship that uses grouping the subqueries.
Looking for text in any part of a field
=======================================
The following function searches for text in any part of a field of any
dataset (it can be for example a TTable, TQuery, TADOTable, TADOQuery,
TIBTable, TIBQuery, etc.)
type
TLocateStrOption = (loCaseSensitive, loContinue);
TLocateStrOptions = set of TLocateStrOption;
function LocateStr(Dataset: TDataset; Field: TField; Str: String;
LocateOptions: TLocateStrOptions): boolean;
// Searches text in any part of a dataset field. The search can be
// case sensitive (option loCaseSensitive) and can start from the
// beginning or from the current record (option loContinue).
//
// Returns True if the string was found (the dataset is positioned
// in that record) and False otherwise (the dataset is left in EOF)
var
ControlsDisabled: boolean;
begin
ControlsDisabled := Dataset.ControlsDisabled;
if not ControlsDisabled then Dataset.DisableControls;
try
if loContinue in LocateOptions then begin
if not Dataset.Eof then Dataset.Next;
end else
Dataset.First; // Start from the beginning
if not (loCaseSensitive in LocateOptions) then
Str := UpperCase(Str);
while not Dataset.Eof do begin
if loCaseSensitive in LocateOptions then begin
if Pos(Str, Field.AsString) <> 0 then break;
end else begin
if Pos(Str, UpperCase(Field.AsString)) <> 0 then break;
end;
Dataset.Next;
end;
Result := Dataset.Eof;
finally
if not ControlsDisabled then Dataset.EnableControls;
end;
end;
Painting rows in a Delphi 4 DBGrid
==================================
In a past issue we presented an example that painted the rows of a
DBGrid with different colors, but that code was meant for Delphi 5. The
following code uses the OnDrawDataCell event and the DefaultDrawDataCell
method, and will hopefully work in Delphi 4:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject;
const Rect: TRect; Field: TField; State: TGridDrawState);
begin
with TDBGrid(Sender) do begin
if SelectedRows.IndexOf(DataSource.Dataset.Bookmark) >= 0 then
Canvas.Brush.Color := clPurple
else if gdSelected in State then
Canvas.Brush.Color := clHighlight
else if (DataSource.Dataset.RecNo and 1) <> 0 then
Canvas.Brush.Color := $00DDEEFF
else
Canvas.Brush.Color := $00DDFFFF;
DefaultDrawDataCell(Rect, Field, State);
end;
end;
________________________________________________________________________
7. DELPHI ON THE NET
Articles
========
* Delphi Database Programming Course - By Zarko Gajic
Free online database programming course for beginner Delphi developers
focused on ADO techniques. Two new chapters have been added in the
last month (Chapter 12 "Master detail relationships" and Chapter 13
"New...Access Database from Delphi").
http://delphi.about.com/compute/delphi/library/weekly/aa010101a.htm
* Building a stand-alone Web service with Indy - By Dave Nottage
This article shows how to build a Web service using Indy and Delphi 6.
http://community.borland.com/article/0,1410,27513,00.html
* An introduction to programming strategy games - By Armando de la Torre
This article describes the creation of a game known as Pente or Go, a
simple strategy game which has similar rules to tic-tac-toe.
http://community.borland.com/article/0,1410,27512,00.html
* WebSnap is now in session - By Nick Hodges
This article discusses how to maintain session information in a
WebSnap application.
http://community.borland.com/article/0,1410,27521,00.html
* Loggin' in ain't hard to do - By Nick Hodges
This article illustrates how easy it is to add log-in capability to
your WebSnap applications
http://community.borland.com/article/0,1410,27485,00.html
* Managing sessions with Delphi 6 Web services (updated) - By Daniel
Polistchuck
Building an e-business application? Then you'll need techniques like
these to manage session states and authentication.
http://community.borland.com/article/0,1410,27575,00.html
* Groping in the dark - By John Flaxman
A first look into the Open Tools API to implement an extension to the
Alt-[ key mapping
http://community.borland.com/article/0,1410,26389,00.html
* Simulating the Windows API's FindWindow function with Kylix - By
Matthias Thoma
This article descripes a very handy FindWindow function.
http://community.borland.com/article/0,1410,27395,00.html
* Maximizing Performance of Delphi/C++Builder/InterBase Applictions - By
Robert Schieck
This paper will present some tips to help you with getting better
performance from your Delphi/C++Builder/InterBase system.
http://community.borland.com/article/0,1410,27534,00.html
* Migrating Your Delphi 5 Projects to Kylix - By Bob Swart
This paper provides an overview and detailed description of migrating
applications from Borland Delphi 5 for Windows to Kylix for Linux
http://community.borland.com/article/0,1410,27534,00.html
* Community TV: Delphi 6 WebSnap with Jim Tierney
John K interviews Jim Tierney, architect of WebSnap, for the details
on this new Delphi 6 feature. Now with text transcript and MP3 audio.
http://community.borland.com/article/0,1410,27303,00.html
* Sip From The Firehose: July 9, 2001 - Everybody's Talking About Web
Services... - by David Intersimone
In this installment of my column, we'll take a look at the hot topic
of Web Services. Publishing your web application interfaces over the
Internet is the next great evolution in the developent of distributed
objects and applications.
http://community.borland.com/article/0,1410,27501,00.html
* Aspect ratio lab report - By Earl F. Glynn
This lab report demonstrates how to display a rectangular TBitmap in a
rectangular TImage of any size while preserving the original aspect
ratio.
http://homepages.borland.com/efg2lab/ImageProcessing/AspectRatio.htm
* Cursor overlay lab report - By Earl F. Glynn
This lab report demonstrates how to convert a cursor to a bitmap and
then overlay the cursor's bitmap on another bitmap.
http://homepages.borland.com/efg2lab/Graphics/CursorOverlay.htm
* Windows Shell Extensions – Info Tip - By Larry J. Rutledge
Creating the InfoTip Shell Extension, which lets us, control the
information that appears in Explorer when the mouse hovers over a
file. Creating a Delphi Infotip that will display the FileName, the
project type (Program or Library), the Project Name (from the source
file), and the size of the file in bytes.
http://delphi.about.com/library/bluc/text/uc071701a.htm
* A more powerful Delphi Form - By Zarko Gajic
Messing with the creation process of a form object, or how to change
the default style of a window when it gets created to suit your
particular needs. Transparent forms, no caption forms, realy StayOnTop
forms, ...
http://delphi.about.com/library/weekly/aa073101a.htm
* Component writing, part 1 - By Peter Morris
This first part demonstrates some of the best approaches to building
components, and at the same time provides tips on deciding on the best
base class to inherit from, using virtual declarations, the process of
overriding, and so on.
http://www.howtodothings.com/showarticle.asp?article=310
* Real-time 2D particle systems (with gravitation!) - By Curtis W. Socha
This tutorial is going to discuss a library of Delphi routines that
will help you create your own particle systems. The term system is
defined to mean "A group of interacting, interrelated, or
interdependent elements forming a complex whole". You can use the
particle to represent a cannonball being shot out of a cannon in a
trajectory game.
http://delphi.about.com/library/bluc/text/uc071501a.htm
Other links
===========
* Freeware ADO dataset components
http://www.deer-soft.com/
http://www.alohaoi.com/Software/Products/aoado/default.htm
http://www.agric.za/freeway/ADOds.htm
* The Delphi Inspiration
Freeware Delphi Components: rjExContainers Library (Vector, List, Tree
and Hash Containers), rjHtmlParser (Parser for Html files), rjMime
(Encode and Decode), rjPCRE (Perl Regular Expressions for Delphi),
rjPasDoc (Generate Html Help from Pascal Sources).
http://www.zeitungsjunge.de/delphi/
________________________________________________________________________
YOU CAN HELP US
We need your help to keep this newsletter going and growing.
Contribute!
===========
If you have developed an interesting routine, found a solution to a
problem, etc., please write us so that we can evaluate the inclusion of
your material in the newsletter.
PLEASE don't send us compiled items, and PLEASE don't send us material
copyrighted by others. If you found something interesting in the
Internet, please limit to sending us the link.
Divulge!
========
The easiest way you can help us is by voting for us in any or all of
these rankings to help give more visibility to our web site and thus
increase the number of subscriptions to this newsletter:
http://www.programmingpages.com/?r=latiumsoftwarecomenpascal
http://top100borland.com/in.php?who=20
It's just a few seconds for you that REALLY mean a lot to us.
________________________________________________________________________
If you haven't received the full source code examples for this issue,
you can get them from http://www.latiumsoftware.com/en/file.php?id=p25
________________________________________________________________________
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 page: http://groups.yahoo.com/group/pascal-newsletter/
Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com
Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com
Problems with your subscription? eds2008 @ latiumsoftware.com
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/en/index.php
Copyright (c) 2001 by Ernesto De Spirito. All rights reserved.
________________________________________________________________________
|