Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : BOXLIB.ZIP
Filename : CPI.DOC

 
Output of file : CPI.DOC contained in archive : BOXLIB.ZIP
Clipper Programming Insights
by Thomas Leylan


Look toward the past...

If I had to describe the secret to good Clipper programming in a single
phrase I would have to say "look toward the past with an eye toward the
future."

Why would I say that, because Clipper programming is not significantly
different from C programming and BASIC programming and Pascal programming
or I would imagine Ada, COBOL, Forth, Fortran and SmallTalk programming.

Oh sure the syntax is different... the structure of the program is
different, the memory management is different and just about 1000
other things are different... but other than that they're identical.

And to me anyway, that's great. Why, because it means that the
resource pool of information is far larger than we are ordinarily
led to believe.


Information is all around us...

To tell you the truth, it is rare that I pick up a "Clipper" book
for information on programming in Clipper and when I do it is usually
Rick Spence's book, "Clipper Programming Guide". I'm not suggesting
that you should overlook the obvious sources of information but rather
that you should not overlook the less than obvious.

Personally I find books on the C language extremely helpful as well
as non-language specific books on the fundamentals of applications
design. Lately I've been reading books on OOPs, Actor and writing a
compiler, as well as Microsoft's Quick C and Borland's Turbo Assembler
manuals.

Object-oriented Software Construction by Bertrand Meyer
1988 Prentice-Hall International (ISBN 0-13-629049-3)

Object-Oriented Programming Featuring Actor by Marty Franz
1990 Scott, Foresman and Company (ISBN 0-673-38641-4)

Compiler Design in C by Allen I. Holub
1990 Prentice-Hall, Inc. (ISBN 0-13-155045-4)

Two standard references, never far from my desk, are The C Programming
Language also known as "the white book", because of it's white cover
and The Elements of Programming Style which also has a white cover
(merely coincidence or a message from far beyond the stars ?)

The C Programming Language by Brian W. Kernighan and Dennis Ritchie
1978, 1988 Prentice-Hall, Inc. (ISBN 0-13-1103262-8)

The Elements of Programming Style by Kernighan and Plauger
1974 McGraw-Hill Book Company (ISBN 07-034199-0)

But to come to get point... I'm simply saying that a string centering
function, that leap year calculation routine, our 24 hour to 12 hour
time converter as well as a good many of the other things that we
labor over have been discussed and/or written before.


Not as plain as the nose on our faces but pretty plain...

Granted examples predating the harnessing of electricity may not be
presented in Clipper code though a good number of the more recent ones
will be in C or BASIC or Pascal and armed with that source code one can
usually convert to Clipper fairly easily. Let me mention also that you
probably don't need to know C or BASIC or Pascal to figure out what's
going on in a short routine though I have to admit that it would help.

But good information is good information regardless of the language
used. Read for instance just a few one-liners from The Elements of
Programming Style.

Write clearly - don't sacrifice clarity for "efficiency"

Replace repetitive expressions by calls to a common function

Parenthesize to avoid ambiguity

Don't compare floating point numbers solely for equality

That book was first published in 1974 and contains quotes from material
written in 1965. Why shouldn't we learn from other people's efforts ?

The four samples cited have direct applicability to our Clipper work
and the admonition against comparing floating point values is quite
interesting as this "problem" is "discovered" and posted in a message
on NANFORUM (Nantucket's forum on Compuserve) once every other month.


A simple example using C...

In The C Programming Language a simple example of C code is used to
illustrate C syntax and program structure. The code, reprinted here
outputs a simple table of Fahrenheit temperatures and their centigrade
or Celsius equivalents.


/* fahr.c */

#include

/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */

main()
{
int fahr, celsius;
int lower, upper, step;

lower = 0; /* lower limit of temperature table */
upper = 300; /* upper limit */
step = 20; /* step size */

fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr - 32) / 9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
}


I don't know what the above listed code looks like from the "never
having seen any C code perspective" but I hope it looks familiar enough
so that you recognize the following Clipper translation.


/* fahr.prg */
/* compile : clipper fahr /n /w */

/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */

FUNCTION main()

local fahr, celsius
local lower, upper, step

lower = 0 /* lower limit of temperature table */
upper = 300 /* upper limit */
step = 20 /* step size */

fahr = lower
while (fahr <= upper)
celsius = 5 * (fahr - 32) / 9
QOut(fahr, celsius)
fahr = fahr + step
end

return NIL


I purposely kept it as similar to C as possible and with Clipper 5.0
this is much easier than it used to be. The point however is not to
illustrate how close Clipper is to C, that isn't something that the
Clipper development group is interested in promoting, their stated
goal being an object-oriented implementation of the language.

The point is rather, and I think adequately illustrated, that with
very few adjustments for syntax the programming resources of the
last 30 years is available for our use.


A simple example using Pascal...

Lest you think that I'm suggesting you spend the next few months only
reading books on C, I submit the following example from Mastering Turbo
Pascal 4.0, Second Edition.

Mastering Turbo Pascal 4.0, Second Edition by Tom Swan
1988 Hayden Books (ISBN 0-672-48421-8)

The following code to perform a binary sort is given as an example of
how to write in Turbo Pascal.


{ sortdemo.pas }

PROGRAM BinarySort;
CONST
MaxElements = 100; { Maximum array size }
TYPE
Element = Integer;
ElementArray = ARRAY[ 1 .. MaxElements ] of Element;
VAR
a : ElementArray;
i, n : Integer;

PROCEDURE Sort( VAR a : ElementArray; n : Integer );
{ n = actual number elements in array a }
{ Algorithm = Binary Insertion }

VAR
i, j, Bottom, Top, Middle : Integer;
Temp : Element;

BEGIN
FOR i := 2 to n DO
BEGIN
Temp := a[ i ]; Bottom := 1; Top := i - 1;

WHILE Bottom <= Top DO
BEGIN
Middle := ( Bottom + Top ) DIV 2;
IF Temp < a[ Middle ]
THEN Top := Middle - 1
ELSE Bottom := Middle + 1
END; { while }

FOR j := i - 1 DOWNTO Bottom DO
a[ j + 1 ] := a[ j ]

a[ Bottom ] := Temp

END { for }
END; { Sort }

BEGIN

Writeln( 'Binary Insertion Sort' );
Writeln;

REPEAT
Write( 'How many ? (2 to ', MaxElements, ') ? ' );
Readln( n )
UNTIL n <= MaxElements;

FOR i := 1 to n DO
BEGIN
a[ i ] := Random( Maxint );
Write( a[ i ]:8 )
END; { for }

Writeln; Writeln;

Sort( a, n);

FOR i := 1 to n DO
Write( a[ i ]:8 );

Writeln;
END.


Again, I'm hoping that the Pascal code looks "english" enough that
a translation wouldn't require reading more than a few pages out of
the Pascal manual to figure out what's going on.


&& sortdemo.prg
&& compile : clipper sortdemo /w

&& PROGRAM BinarySort
&& CONST
# define MaxElements 100 && Maximum array size

MEMVAR getlist

Demo()

FUNCTION Sort( a, n );
&& n = actual number elements in array a
&& Algorithm = Binary Insertion

&& VAR
local i, j, Bottom, Top, Middle
local Temp

&& BEGIN
FOR i := 2 to n

Temp := a[ i ]; Bottom := 1; Top := i - 1

WHILE Bottom <= Top
Middle := int(( Bottom + Top ) / 2)
IF Temp < a[ Middle ]
Top := Middle - 1
ELSE
Bottom := Middle + 1
ENDIF Temp
END && while

FOR j := i - 1 TO Bottom STEP -1
a[ j + 1 ] := a[ j ]
NEXT j

a[ Bottom ] := Temp

NEXT i
&& END { Sort }
return NIL


FUNCTION Demo
LOCAL a := array(MaxElements), n := 0, i

&& BEGIN

QOut( 'Binary Insertion Sort' )
QOut()

WHILE (n > MaxElements) .or. (n == 0)
@ row(), col() say 'How many ? (2 to ' + str(MaxElements, 3, 0) + ') ? '
@ row(), col() get n
read
QOut()
END

FOR i := 1 to n
a[ i ] := Random( seconds() + i )
Qout( a[ i ] )
NEXT i

QOut(); QOut()

Sort( a, n)

FOR i := 1 to n
Qout( a[ i ] )
NEXT i

QOut()
&& END.

return NIL


FUNCTION Random( iSeed )
return INT((((( iSeed * 31415821) + 1) % 1000000) / 1000000) * 32767)


This time I tried to keep it as close to Pascal as possible and believe
me if I was really determined to make it look alike it would have been
quite easy by using the Clipper 5.0 preprocessor and the judicious
application of the #translate and #command directives.

Again however I'm not trying to turn you into a Pascal programmer but
rather pointing out that binary sorting algorithms can honestly be
termed "ancient" and that one cannot "invent" a binary sort but can
only "resurrect" it.


A terrific example using Z-80 Assembler...

I wanted to really reach into the past for an example and I hope that
Zilog appreciates my mention of their "classic" CPU. Proving that one
just doesn't know where the next algorithm will turn up.

This one is from a book called Z80 Assembly Language Programming.

Z80 Assembly Language Programming by Lance A. Leventhal
1979 Osborne/McGraw-Hill (ISBN 0-931988-21-7)


; led.asm

LD A,00001111B ;MAKE PORT B OUTPUT
OUT (PIOCRB),A
LD B,BLANK ;GET BLANK CODE
LD A,(40h) ;GET DATA
CP 10 ;IS DATA A DECIMAL DIGIT?
JR NC,DSPLY ;NO, DISPLAY BLANKS
LD DE,SSEG ;GET BASE ADDRESS OF 7-SEGMENT TABLE
LD H,0 ;MAKE DATA INTO A 16-BIT INDEX
LD L,A
ADD HL,DE ;ACCESS ELEMENT IN TABLE
LD B,(HL) ;GET 7-SEGMENT CODE
DSPLAY: LD A,B
OUT (PIODRB),A
HALT
ORG 20H
SSEG: DEFB 3FH
DEFB 06H
DEFB 5BH
DEFB 4FH
DEFB 66H
DEFB 6DH
DEFB 7DH
DEFB 07H
DEFB 7FH
DEFB 6FH
BLANK: DEFB 00H


OK, I'm willing to give in here and admit that you would have had to
have read something in assembler before or be very perceptive or be
very persevering to just sit down and translate this into Clipper.

But I have... I'm reasonably... and I did...


/* led.prg */
/* compile : clipper led /n /w */

#include "setcurs.ch"
#define BLANK 11

FUNCTION led
local iRow := 10, iCol := 10 //POSITION LED DISPLAY

local aSegCode := { 63, 6, 91, 79, 102, 109, 125, 7, 127, 103, 0 }
local aAltCode := { 63, 6, 91, 79, 102, 109, 124, 7, 127, 111, 0 }

local iCode := aSegCode[BLANK] //ASSIGN BLANK VALUE
local iKey

local xCursor := set( _SET_CURSOR, SC_NONE)
cls

@ 1, 0 say "Press the numeric keys 0 - 9 or ESC key to exit"

PIODRB( iRow, iCol, iCode ) //DISPLAY BLANK

do while ((iKey := inkey(0)) != 27) //GET DATA

if Isdigit( chr(iKey) ) //IS DATA A DECIMAL DIGIT?
iCode := aSegCode[ val(chr(iKey)) + 1 ] //ASSIGN 7-SEGMENT VALUE
endif

PIODRB( iRow, iCol, iCode ) //DISPLAY

enddo
set( _SET_CURSOR, xCursor)
return NIL


FUNCTION PIODRB( iRow, iCol, iVal, sOn, sOff )
local iSeg, xsColor
local aSeg := {{ 0, 1, "ÄÄÄÄÄ" },;
{ 1, 6, "³" },;
{ 3, 6, "³" },;
{ 4, 1, "ÄÄÄÄÄ" },;
{ 3, 0, "³" },;
{ 1, 0, "³" },;
{ 2, 1, "ÄÄÄÄÄ" }}

xsColor := setcolor()
for iSeg := 1 to 7
setcolor( if(IsBitOn( chr(iVal), iSeg - 1), "+W", "+N" ))
@ iRow + aSeg[ iSeg, 1], iCol + aSeg[ iSeg, 2] say aSeg[ iSeg, 3]
next iSeg
setcolor(xsColor)
return NIL


FUNCTION IsBitOn(sByte, iBit)
local iByte
iByte := asc(sByte)
iByte := int(iByte * (2 ^ (7 - iBit)))
iByte := int(iByte % 256)
iByte := int(iByte / 128)
return (iByte == 1)


Perhaps an explanation is in order. The original Z80 code is addressing
a parallel input/output circuit (PIO) on the computer to which would be
connected a seven-segment LED. The coding scheme used to represent the
segments is fixed by the hardware and requires a call to the control
register PIOCR and the data register PIODR (there are two of them named
A and B that's how we got PIOCRA and PIODRB).

The segments look like this. ùÄÄaÄÄù
f b
ùÄÄgÄÄù
e c
ùÄÄdÄÄù

The characters look like this. ùÄÄÄÄÄù ù ùÄÄÄÄÄù ùÄÄÄÄÄù ù ù
³ ³ ³ ³ ³ ³ ³
ù ù ù ùÄÄÄÄÄù ùÄÄÄÄÄù ùÄÄÄÄÄù
³ ³ ³ ³ ³ ³
ùÄÄÄÄÄù ù ùÄÄÄÄÄù ùÄÄÄÄÄù ù
ùÄÄÄÄÄù ùÄÄÄÄÄù ùÄÄÄÄÄù ùÄÄÄÄÄù ùÄÄÄÄÄù
³ ³ ³ ³ ³ ³ ³
ùÄÄÄÄÄù ùÄÄÄÄÄù ù ùÄÄÄÄÄù ùÄÄÄÄÄù
³ ³ ³ ³ ³ ³ ³
ùÄÄÄÄÄù ùÄÄÄÄÄù ù ùÄÄÄÄÄù ù


There are two ways to represent the number 6 and the number 9 but
the alternate 6 for instance is used to represent the lowercase b
and to illustrate I have included the alternate representation in
the array aAltCode but have not implemented it's display.

The IsBitOn() function was written and uploaded to NANFORUM by
Ted Means, the author of (among other things) DBFtrieve a Btrieve
library for Clipper S'87 and 5.0, in response to another member's
request for a way to test individual bits. Boy was I lucky and
Ted's timing is excellent.


Don't judge a book by it's subject matter...

Z80 Assembly Language Programming is one example of a book that is
considerably more than what the title suggests. It is packed full
of stuff. Chapter 1, "Introduction to Assembly Language Programming"
and the chapter on "Assemblers" along with a reference guide to the
Z80 instruction set with graphical representations of what happens
internally would be expected in such a book. The sample programs
are a bonus and the final few chapters are a well thought out treatise
on, "The Tasks of Software Development".

Mr. Leventhal, discusses the stages of software development

o Problem definition
o Program design
o Coding
o Debugging
o Testing
o Documentation
o Maintenance and redesign

Do these things sound familiar? It almost seems that designing
applications in Z80 assembler in 1979 is similar to designing
applications in Clipper in 1990.

Among the many things I learned was how a seven-segment LED operates
and programmed the routine accordingly. Without being judgemental, I
consider it unlikely that a "dBase only" programmer would write an LED
simulator this way. I'll guess that we would see a whole lot of case
statements and possibly an array containing all the symbol variations
which besides taking longer and using more RAM doesn't come close to
operating like a hardware LED.


If it's insights you want...

Program in "computerese" not in Clipper. Learn to control a computer
by "using" the language and avoid just "learning the language". It
may only be a subtle difference but try looking at this way:

1) I want to be able to clear the screen, position the cursor,
prompt for input and output some text

vs.


2) I want to be able to use the CLEAR SCREEN command and the
@ SAY / GET and READ commands

What you want to do is choice # 1, choice # 2 is just one of the ways
of accomplishing it.


As you familiarize yourself with Clipper 5.0 you will find that there
are significant enhancements to the language. There are two ways to
look at these changes:

1) I have more ways to do what I want

vs.

2) heck there are more things to remember now

Keep the goal in mind, think more about what you want to do and a little
less about how you're going to do it and these new features will appear
to be what they were intended to be... useful.


Writing software shouldn't be approached through a "pick a command,
find a use for it" method.

A technique I use in my Introduction to Programming with Clipper course
comprises a series of very simple tasks typically starting with the
Clipper equivalent of the ever popular C "Hello World" program.

@ 1, 0 say "Hello World"

by the way... is not it

? "Hello World"

is.

@ SAY is fairly sophisticated since it involves positioning and while
we take positioning for granted it means knowing for one thing that
absolutely positioning is possible and for another it involves some
understanding of the system of screen coordinates. The origin 0,0 as
the top left of the screen is not intuitive in fact in some cases the
origin isn't in the top corner but rather centered on the screen, as
when working with graphics.

Only after putting text anywhere on the screen successfully do we
worry about placing it somewhere on purpose. Then we print it on the
printer, then we prompt for data and print it all around the screen
and so on continually deciding what we want to do and then looking
for the method to do it.


Some of you may be asking "Does he think we don't know where position
0, 0 is on the screen" ?

To be honest I am certain that you do know how to clear the screen
AND how to position the cursor. How's that for confidence ?

I also figure that you can assign variables and open files and replace
fields and construct FOR... NEXT loops and given enough time I believe
that every one of you could write a complete accounting system entirely
in Clipper... the questions are how long is "enough time" and might you
find yourself "fixing" it longer than it took to write?


Keep an eye toward to future...

Which brings us to the object-oriented programming (OOP) paradigm.

Nantucket development has expressed their committment to developing
future products based upon the principals of an object-orientated
language. Continuing my "nothing new under the sun" line of thought
it turns out that OOPs isn't "new" either.

Simula, developed in Norway in the 1960s is considered to be the first
object-oriented programming language. Smalltalk, another OOP language
developed at Xerox's Palo Alto Research Center (PARC) followed. Through
the years interest grew as newer versions were developed but until only
recently micro computers lacked the needed power to support Smalltalk
implementations. Machines built around Intel's 80386 and 80486 chips
and Motorola's 68040 CPUs, coupled with vast amounts of main memory and
large, fast disk drives have opened up micro-computing OOPs style.


The way that I see it...

A synopsis of the OOP paradigm and my views on it's importance were
published in the Feb '90 issue of Reference(Clipper). I think the
concept is important enough (and I was reasonably happy with my
explanation of it) that I'll undertake to repeat it here for the
benefit of those who didn't receive that issue.

The original code samples provided were all written in Summer '87 and
used macros extensively for referencing "instance variables" of the
box "class" I outlined. The need for using macros for this has been
eliminated in Clipper 5.0, due to the new array handling features and
as such I have updated the source code to better explain the concept.




An Object-Oriented Approach to the Clipper Language
by Thomas Leylan

The disclaimer...

What I'm going to show you is not OOPs. I do like to receive mail
but I don't want to receive too many letters from Smalltalk, Actor,
and Eiffel programmers reminding me that Clipper is not an OOPs
language. I realize that.


What it is...

What I WILL demonstrate is an "object-oriented" thought process.

Object-oriented programming is much more than simply the buzzword
for the 90's... ("database servers" IS the buzzword by the way).


OOPs, some background...

Software development styles aren't static, they grow and change and
improve. I don't know if there was a time when "spaghetti code" was
all the rage but it was practiced often enough to be considered a
"standard". Mostly it consisted of strictly linear code with jumps
to spots as arbitrary as a line number.

It didn't take too long for "structured" programming to supplant
this "anything goes" programming style but it didn't just happen.
Languages were developed that facilitated the implementation of
modular practices, often called "goto-less" programming. BASIC was
out, Pascal and C were in.

Even using the traditional structured approach there were still
difficulties that, while "overcome-able", continued to plague the
applications developer. These problems were almost always related
to the programming "glue", i.e. the interaction of the structured
modules with one another. As long as everybody on the development
team knew about each modules' interdependencies everything worked as
planned. To be certain, the best programmers have always isolated
operating system, CPU and hardware specific routines but neither
Pascal or C "required" it. Object-orientation is an attempt to
correct these nagging problems.


OOPs fundamentals...

Four principals traditionally define an OOPs language.

Encapsulation

The idea that code and data are intimately wrapped together and
that access to a particular chunk of data, (an object's data)
should be restricted to that object's methods. Methods being
what you or I would call functions and procedures.

I often refer to encapsulation as "data hiding" as it directly
relates to the visibleness of an objects' data. Only direct
requests to an object will yield the data and only through
requests can modifications be made to that data.

I think it would be fair to call it "method hiding" also since
the mechanism being used to return the data is unknown and of
little interest to the applications developer. The principal of
encapsulation insures that the programmer does not "sneak around
the back" to get the data that is desired. This is particularly
important as we port our applications across hardware and software
platforms.

A fine example of non-encapsulation is the presence of a command
such as CLEAR ALL which we inherited from the dBASE language. The
CLEAR ALL command removes all variables from memory, closes all
files and clears the screen allowing any single programmer on a
project to affect data and resources that another programmers'
module might reasonably expect to be intact, PUBLIC variables
for example.


Polymorphism

According to Webster, from the Greek "polymorphos", having, assuming
or occurring in various forms, characters or styles.

Polymorphism isn't as weird as it sounds. It is the idea that you
can send the same "message" to different classes of objects and that
each will be interpreted correctly. They won't do exactly the same
thing but each object will "open", "close" and "show" in a way that
makes sense to that particular object.


Inheritance

Inheritance is a property that object-oriented languages share.
It is the ability to define new classes of objects from existing
classes. Each defined class "inherits" the methods and traits of
the parent class. These decendent classes can add new traits and
modify existing traits.

A "Square" for example, is a decendent of class "Rectangles", which
is in turn a decendent of class "Polygons" which when carried on to
extremes (and why not) could be a decendent of class "Shapes".

A "Menu" object can be a "Box" object with additional attributes
which would include a list of items that appear in the box from
which one item can be chosen.


Dynamic binding

Binding is the process of matching methods and their data. In
languages such as Clipper and C the binding usually takes place
at compile time but in Clipper we can alternately force late
binding through the use of macros. OOPs languages carry late
binding to the extreme and call it dynamic binding.

A simple illustration of the effects of time on binding can be
demonstrated in the way we write typical screen displays. It is
almost certain that most of us write to a 25 line by 80 column
screen. This assumption is "bound" into the application at compile
time. A change to the mode settings, on video cards supporting
them, could place the monitor in 43 line or 132 column mode and
the change would not be known by our application.

The binding can be delayed by placing a function near the beginning
of our application which checks the video mode and adjusts the
display of our menus and screens for that particular mode. While
this has postponed the binding, it still precludes the setting of
the video mode while "in" our application. The latest possible
binding would involve checking the display mode each time that the
screen or menu was displayed. Only at that time would the data and
the method come together to perform one action.


Objects ? ... we don't need no stinkin' objects...

A software object is much like any physical object in that it has
attributes (data). Software objects also have methods (code).
The code and data together form an object.

Objects are classified into types, the types are called "classes".

A class can be defined for characters, for strings and for numbers.
They can be more "metaphysical" like checks, receipts and account
numbers. As noted earlier they can be shapes and the example I'll
provide are box objects.

Boxes are particularly good for describing OOPs-like concepts because
they are about as concrete as things get in computer software and they
have attributes that we can easily identify.


Yeah, but what can they do...

An object's class defines what methods it has available to it. Boxes
for instance can be drawn, saved, restored, moved and sized. Most
objects can be asked for information about themselves. From a box
object we could inquire about it's position, size, color, frame and
title.


Okay, but how can I talk to a box...

The operations which affect objects are called "messages". Over the
last year I've experimented with various ways of sending messages to
objects, narrowed it down to two and for now have settled on using
uniquely named functions.

BoxNew()
BoxShow()
BoxSet()

The other routine I tried but didn't like uses a single function
and various messages consisting of character strings.

Box("new")
Box("show")
Box("set")

I like the look of it but I didn't like the construction of the box
function itself which immediately required a case statement to
distinguish between the various messages that might be sent and
worse would necessitate sending a variable number of parameters
on each call. Nothing wrong with either approach and I could change
my mind but for now I'll stick with plan "A".

Purists will point out that, with the exception of the "new" message
I shouldn't have to preface my messages with the object type. I
should, for example, be able to simply request that an object "show"
itself without having to say BoxShow. I have no dispute with the
theory and real OOPs languages would provide such an ability but in
every case someone or something must make the decision and I've
simply placed the burden on the programmer instead of creating an
"all seeing" object handler.


Great, what should I say to a box...

Before we can ask an object to perform tricks for us we must have
one available. While there exists a class of objects called boxes
we don't actually have a box at this time. Creating a living and
breathing object is called "instantiating". When a particular box
is instantiated, a "help box" for example, the attributes for that
particular box are assigned.

Boxes as we know have properties of height and width, position (as
defined by row and column coordinates on the screen), color, frames
and in my definition they have shadows. It is important to note
that my boxes are two dimensional. They have no planar coordinate,
i.e. they have no depth. If they had a sense of depth I'd probably
call them windows.


So teach me Box Talk...

Some methods are common to all object classes.

BoxNew() Instantiates an object of class "Box"
BoxKill() Destroys a previously instantiated object


Most objects permit inquiries with current values returned.

BoxTop() Returns the current top row value
BoxLeft() Returns the current left column value
BoxLength() Returns the current length value
BoxWidth() Returns the current width value
BoxColor() Returns the current color string
BoxShadow() Returns the current shadow value
BoxFrame() Returns the current frame string
BoxTitle() Returns the current title string


Some inquiries return derived values.

BoxBottom() Returns the current bottom row value
BoxRight() Returns the current right column value


Many objects permit resetting the current values.

BoxSet() Updates the specified attribute of a
specified box object with a specified value


Objects can be asked to perform tasks on themselves.

BoxShow() Displays the specified box
BoxUnshow() Un-displays the specified box
BoxDrag() Allows re-positioning of the box
BoxResize() Allows re-sizing of the box


An object can be asked to refresh itself.

BoxPaint() Updates the screen image of itself, most
often needed when an action has been taken
that could have modified a box's appearance


A box object can be asked to preserve itself.

BoxSave() Saves the position, size and a screen image


Text can be sent to a box object for display and input can be
gotten from within it.

BoxClear() Clears the interior of a box object
BoxSay() Places string text within the box
BoxaSay() Places array text within the box
BoxGet() Gets input from within the box


Specialized tasks can be programmed into an object.

BoxPick() Creates a picklist of items within a box and
permits the selection of one of those items


A Quick Once Through...

For the best effect you will want to turn off the cursor and the
scoreboard.

#include "setcurs.ch"

/* set environment */
local xCursor, xScoreboard
xCursor := set( _SET_CURSOR, SC_NONE)
xScoreboard := set( _SCOREBOARD, .F.)


Before a box can be referenced it must be instantiated with BoxNew.

A box representing the workspace should be created.

bhScrn := BoxNew( 0, 0, 24, 80, "B/B,,,,", 0, " ", "")

The drag and resize messages require a domain to operate within
and the workspace box object must be passed as a parameter.

Notice that there is no shadow on the workspace box.


One sample box scenario :

bhScrn := BoxNew( 0, 0, 24, 80, "B/B,,,,", 0, " ", "")

bhDemo := BoxNew( 0, 0, 9, 36, "+W/BG,+GR/W,,,+W/BG", 2, "ÚÄ¿³ÙÄÀ³ ", " The Title ")
BoxShow(bhDemo)
BoxSay(bhDemo, 1, 1, "Some text in the box")
inkey(0)

BoxDrag(bhDemo, bhScrn)
BoxUnshow(bhDemo)
BoxShow(bhDemo)
inkey(0)

BoxClear(bhDemo)
BoxSay(bhDemo, 1, 1, "More text in the box")
inkey(0)

BoxUnshow(bhDemo)
BoxKill(bhDemo)

/* reset environment */
set( _SET_CURSOR, xCursor)
set( _SCOREBOARD, xScoreboard)


Some things about my stuff...

I'd like to encourage you to play with the boxes as they exist
but caution you not to consider the objects presented as finished.

There are many sub-classes of "Box" that I have yet to implement
including a number of "specialized" boxes. Among these are
"MenuBox", "ListBox", "DialogBox" and probably a few others.
Each shares attributes and methods with the plain old box but
each has a well defined set of additional functionality.

Perhaps you've noticed that I made my generic box do double duty
which in the strictest OOPs terms I really shouldn't be doing.
There is the BoxPick() function available to the generic "Box"
class but the array of choices is external to the box. A better
solution would have been the invention of a "MenuBox" sub-class
which incorporated the menu array as an additional attribute.

You will definitely notice that I have not provided a BoxGet()
function. Obviously I could have plopped a GET and READ in a
function and sort of would have done it but I'm determined to
have all the "parts" of my box work cohesively and a problem
surfaces when you allow re-sizing of a box with active gets.

I really do plan to solve these problems but my life_object is
preciously short of the "time" attribute at the moment. However
as those of you who know me can attest, I firmly believe that if
one can find the time to make excuses one can find the time to
accomplish the task... so that said I'll set about to "objectify"
more of my usual tools and suggest that you consider doing the same.

For now, read through the documentation and coding samples provided
on the disk and forward any questions you might have to me in care
of Reference(Clipper).


Why it's important...

There are a number of reasons why you should consider an "object
oriented" approach to your Clipper applications now. It really
is a superior methodology, certainly superior to "just writing
some code" and better than any else I've seen in recent years.

Clipper 5.0 will incorporate a number of new features which will
facilitate an object-oriented programming style. Principal among
these are code blocks which are traditionally found only in OOPs
languages. Static and local scoping of variables, the concept of
equivalence (arrays can be more than just simply "equal") and the
built-in preprocessor are all going to combine to boost the power
per dollar equation.



Contained on the conference diskettes are both the code and some
reasonable documentation for the box class routines.


12 more things before you go...

I wouldn't want anyone to leave before I've given you something to
argue about amongst yourselves over dinner, so let me present:

Tom Leylan's One Dozen Instant Insights

On the macroscopic scale :

o Pseudocode first, Clipper code second

o Don't settle for less than perfect (or at least not much less)

o Avoid causing side-effects with your code

o Avoid using Clipper side-effects

o Create small, reuseable, self-contained functionalia


On the microscopic scale :

o Declare the scope of all variables

o Use manifest constants

o Use double equal (==) for equality tests

o Use colon equal (:=) for assignment

o Always save and restore environmental "states"

Use the SETCOLOR() function instead of the SET COLOR TO command

Use the SET() function instead of the equivalent command for
all SET

o Use the SAVESCREEN() and RESTSCREEN() functions not the SAVE
SCREEN and RESTORE SCREEN commands

o Don't use DO WHILE or FOR... NEXT for timing or delay loops


That's it, go forth and code...




  3 Responses to “Category : Dbase (Clipper, FoxBase, etc) Languages Source Code
Archive   : BOXLIB.ZIP
Filename : CPI.DOC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/