Dec 112017
 
Aston Tate dBase IV Tech Notes for March 91. Useful information.
File TN9103.ZIP from The Programmer’s Corner in
Category Dbase Source Code
Aston Tate dBase IV Tech Notes for March 91. Useful information.
File Name File Size Zip Size Zip Type
TN0391.TXT 65006 22974 deflated

Download File TN9103.ZIP Here

Contents of the TN0391.TXT file


1 Read Me First

These articles are reprinted from the March '91 edition of TechNotes/dBASE
IV. Due to the limitations of this media, certain graphic elements such as
screen shots, illustrations and some tables have been omitted. Where
possible, reference to such items has been deleted. As a result,
continuity may be compromised.

TechNotes is a monthly publication from the Ashton-Tate Software Support
Center. For subscription information, call 800-545-9364.



2 Q&A

Command Editing On the Fly

Behind every dot prompt expression there's an editing window.

There's No Place Like Ctrl-Home

My dot prompt expressions are starting to be quite lengthy. As the amount
of parenthetical insets increases, I find it more and more difficult to
scroll back and forth trying to determine where possible errors exist.
What can I do to make debugging dot prompt expressions less cumbersome?

By pressing Ctrl-Home, you enter the text editor. Here, you can use the
Enter key to break complex expressions, thus becoming easier to view (it's
all on the screen, either auto-wrapped or explicitly broken by lines) and
debug. To return to the dot prompt, simply press Ctrl-End and your command
is processed. To cancel, press Escape.

You most likely have never gotten to the point where your expressions
exceed 254 characters. In that case, you would receive the warning that
your command was too long and how to work around the limitation.
Memo Exporting in WordPerfect

Although I've been able to export databases without memos into WordPerfect,
I'm curious as to which command to use to also export memo information into
that environment. Is there a way to do this?

It is easy enough to export a file without memos into a more universal text
format such as:

COPY TO DELIMITED

and then translate the file into a WordPerfect format using the CONVERT
utility that WordPerfect provides. However, when translating a file and
its memo information, a small program is required, as shown below.

SET TALK OFF
USE
SET ALTERNATE TO Txtout
SET ALTERNATE ON
SCAN
? '"'
?? TRIM()
?? '",'
?? LTRIM(STR(, 10, 2))
?? ',"'
?? DTOS()
?? ',"'
?? IIF(, ".T.", ".F.")
?? ',"'
lcnt = 1
DO WHILE lcnt < MEMLINE() + 1
?? MLINE(note, lcnt)
lcnt = lcnt + 1
ENDDO
?? '"'
ENDSCAN
CLOSE ALTERNATE
RETURN


The ?? lines where TRIM(), DTOS() and IIF() are used on various field types
illustrate how to do such fields. Additionally, the ?? statements which
contain only commas, double and single quotes are important to delimit the
different fields correctly. The italic markers shown in angle brackets
should be replaced with the actual names of the fields of your database.
They can appear in whatever order you prefer.

In the WP directory run CONVERT. When prompted, enter the input filename
(with .TXT extension) and the output filename. Select option 9 from the
next menu. Enter a comma as a field delimiter, and as record delimiter,
type {13}{11}. The character to strip out would be a double quote. After
entering these parameters, process the conversion.
The Letter of the Law (of Indexing)

I am attempting to index on a field that contains part numbers. The part
numbers sometimes begin with letters, sometimes with numbers. What I want
is an index that shows the part numbers beginning with letters first
(ascending alphabetically). Afterwards, the part numbers starting with
numbers would be listed sequentially. None of the ASCII or dictionary
sorts can accomplish this. Is there a way?

Your request is possible by way of a complex index expression. Suppose
your part number field is 10 characters in width and is called PART. Your
index expression would appear as follows:
INDEX ON STR(VAL(Part), 10) + SUBSTR(Part, 1, 10) TAG

One exception to this index expression would prevent part numbers with
leading zeros from being considered as a numeric (the VAL() function would
remove the leading zeros in its string to numeric conversion). So a part
number such as 00AJ98207 would be evaluated starting from its third
position, or the value "A" since the leading zeros would be stripped by the
STR() and VAL() conversion. If a part number started with a letter, it
would be interpreted as a zero after being filtered by the VAL() function.

You might try displaying the above index expression from the dot prompt to
see the effect of the expression on your field.

? STR(VAL(Part), 10) + SUBSTR(Part, 1, 10)

This index expression would not be most ideal for a SEEK operation and is
more suited toward a logical sort for reporting purposes. However, seeing
how the index expression is laid out will give you a good idea as to how
you might SEEK with such an index.
Save the Last Edit For Me

I have developed a format screen in which the last field must have an
editing condition satisfied. The validation works fine except for those
times where I realize there is data in above fields that requires further
editing. In that case, I am stuck in the last field requiring the valid
entry and cannot get back up to other fields. Once I enter an invalid
entry, I am forced to enter something valid but then the edit screen flips
to the next record to be entered. I tried using SET CONFIRM ON to no
avail. Is there any way around this, short of rearranging the fields?

I was just about to say you could rearrange the fields. But, seriously, if
that isn't a viable option and your format screen is aesthetically perfect
in its present form, you might want to modify your valid clause in your
screen design. Under Edit options: Accept value when, add the following to
your present condition:

.OR. LASTKEY() = 5

What this does is to allow the pressing of the Up-arrow key as part of the
validity check. That way, regardless of whether you've entered valid data
in the last field, you can always go to previous fields.

There is one drawback to this method. Once you've backed up to previous
fields, leaving an invalid entry in the last field, you could easily skip
to other records with the PgUp or PgDn key. These, of course, can be
restricted from use by the use of ON KEY LABEL statements earlier in your
program.

If the extra work required for this key trapping seems too much, you might
want to take my first suggestion, rearrange the fields.



3 Converting Text Files to Postscript

Converting Text Files to PostScript

Martin Leon

Filtering output files through this utility allows them to print on a
PostScript printer.

Although the dBASE IV version 1.1 implementation of PostScript printer
support is adequate for most needs, there is one situation where you will
not be able to get your output to print on a PostScript printer. Many
programmers often choose to create printed output by using SET DEVICE TO
PRINT and @..SAY commands to produce the desired output. Although there is
nothing wrong with this technique, it would take a lot of extra coding to
get the same program to be able to print to a PostScript printer. The
problem is twofold. First, when you use the @..SAY command to output to a
printer, the dBASE IV print engine is bypassed and has no influence over
the resulting output. Secondly, the PostScript printer is not expecting to
receive lines of text to be printed, but rather is expecting a series of
commands.

With most printers, it doesn't matter if you bypass the dBASE IV print
engine because as long as they receive a stream of ASCII characters (as
with the @..SAY technique), they print the characters on the page as they
were sent. However, if you do this with a PostScript printer, the printer
tries to interpret what you're sending to it as instructional data rather
than literal output. The end result is that the printer light blinks,
indicating that the printer is processing your print job, but nothing is
ever printed, leaving you tearing at your hair, banging on the top of the
printer, rebooting your computer and a number of other human conditional
responses to an uncooperative computer operation.

The C program PostOut.EXE presented here provides a means for you to get
around this obstacle. In order to understand how the program works we must
first discuss how PostScript works and how it is that dBASE IV is able to
print to a PostScript printer.

What Is PostScript?

PostScript is a very powerful page description language, hence its
acceptance as a standard in the Desktop Publishing realm. It's a
programming language that allows you to define in detail everything you
want printed on a page. With it you can draw lines, boxes, circles, and
other objects, fill them in with black, white or grayscale. It is also
possible to define font attributes, print text, change the orientation of
how things are printed on the page, and a host of other maneuverings.
PostScript has changed the course of modern publishing, personalizing it in
much the same way as the first PCs made home computing a possibility.

PostScript comes complete with its own commands, operators, and syntax.
Because of its extreme flexibility in laying out a page, it takes a pretty
large program to print to a PostScript printer. Compared to what is
required to print to a dot matrix it is a much more complex exchange of
data. But consider the difference in quality.

Fortunately for us, someone already put a lot of time and thought into
designing such a program, making it not only possible but fairly
transparent to the user for dBASE IV to print to PostScript printers. This
program is provided in a file called the PostScript download file
(PostScri.DLD). It defines several procedures (called macros) and
maintains several memory variables to let the printer know which font to
use, where on the page to put the next line of text, when a page is full,
how to eject a page, and so on. None of these operations are handled in
the same manner as they would be with conventional printers.

To begin with, the ASCII control characters such as the form feed command
(ASCII 12), carriage return (ASCII 13) and line feed (ASCII 10) are not
interpreted by the PostScript printer to have the same effect as they do on
other types of printers. In order to emulate the functionality of other
printers, the PostScript driver PostScri.PR3 translates these characters
into the names of macros that are defined in the download file.

A form feed command, which is what tells most printers to advance the paper
to the beginning of the next page, is translated into the name of a macro
called "FF". This macro tells the PostScript printer to print everything
that has been defined in printer memory up to this point, eject the page,
and re-initialize several memory variables to be able to start defining the
text for the next page to be printed.

The combination of carriage return and line feed is translated into a macro
called "CRLF". This macro tells the printer what is to be printed on the
current line and to reset some more memory variables so that the printer
knows where to start the next line of text.

In PostScript, the command to put a line of text on the page is "show".
The text you want printed should be surrounded by parentheses before the
command. The macros and memory variables in the download file dictates at
what coordinates to print everything. The DLD file also has macros for
turning compressed print on and turning landscape printing on.

Here's a small example of what it would take to print two lines using the
download macros:

Init <- initializes printer settings and memory
variables
Norm <- do not use any font attributes like bold
or italic
(ECHO OFF)show <- put the words ECHO OFF at current
row/column coordinates
CRLF <- change row/column coordinates to be able
to print on next line
(PROMPT $P$G)show
CRLF
FF <- print out everything that has
been defined for current page and eject it
and prepare the next page for
printing

Of course, this all depends on the download file being sent to the printer
immediately before sending these commands. Without the download macros, it
would take many, many more commands to accomplish the simplest output.

About the Utility

The C routine starting on page 10 uses this basic knowledge of PostScript
and the dBASE IV PostScript download file to translate a standard ASCII
file into a PostScript document. It contains switches to allow you to
print using compressed print and/or landscape printing. You can set tab
character spacing, page length, and choose one of three fonts. You can
direct the PostScript output to any output device (LPT1, COM1, CON, PRN,
and so on) or to another file. Now you can continue using @..SAY
conventions, but, instead, you would redirect the printer output to a
file. Once the file is created, this program will allow you to translate
the file and send it to the desired destination along with the POSTSCRI.DLD
file.

After retrieving and verifying the command line arguments, POSTOUT simply
looks for carriage return/line feed combinations, form feed characters and
tab characters. Each line of text is prefaced with a "(" and terminated
with ")show", enabling that line to be printed. Carriage return/line feeds
are replaced with the CRLF macro, form feeds are replaced with the FF
macro, and tab characters are replaced with the appropriate number of
spaces. The program counts how many lines have been put on a page and
issues a form feed macro when the specified maximum has been reached. Low
order and high order ASCII characters are replaced by an asterisk since
PostScript has a different symbol set for these characters. This means you
will not be able to use box and line drawing.

You can use PostOut for any ASCII text file. Suppose you're on a network
and you want to print your AutoExec.BAT file to a PostScript printer
assigned to LPT2 and print in compressed print Just have PostOut.EXE in
your path as well as having the PostScri.DLD file in the same directory as
POSTOUT, then issue the following command from the DOS level:

POSTOUT AUTOEXEC.BAT LPT2 /C

It's recommended you use the default font (Courier) because it is a fixed
space font, like the one you see on the screen when you are in dBASE IV.
If you use any of the other fonts, the text will not line up like it would
on the screen. The other fonts would be used for readability or preferred
appearance, but since they are proportional fonts (each letter has a
different width), it would be difficult to get a document to line up the
way you want it when using them.

The program was created using MicroSoft Quick C and is not hardware
dependent. You should be able to compile it using any C compiler and have
it run on any machine. It does use the ANSI C standard function
prototyping for the two functions that get command line arguments. If your
C compiler does not conform to the ANSI standard, you will need to change
the function headers for these two functions and change their function
declarations.

One last note: If you have trouble printing to your PostScript printer,
whether it be from dBASE IV or using POSTOUT, try re-extracting the
download (.DLD) file from the file DRIVERS.EXE. Sometimes people will use
a text editor that puts an EOF character into the DLD file and this
prevents it from working properly. To re-extract the file, go to your
dBASE IV sub-directory and type in the command:

DRIVERS -O POSTSCRI.DLD

This will cause a fresh copy of the download file to be extracted,
overwriting your existing one.

PostOut.C

/* POSTOUT Uses dBase IV .DLD file to enable you to output a standard
ASCII text file to a PostScript printer without modifying it. Prints
7 bit ASCII set. Higher ASCII characters are converted to *'s.
Tabs are converted to spaces as specified by command line parameters.

Created by Martin Leon, Ashton Tate Software Support
Version 1.0a */

#include
#include
#include
#include
#define MAXPATH 256

FILE *infile;
FILE *outfile;
FILE *dldfile;

/* ANSI C function prototypes */
int get slash arg( char switch letter, char *argv[] );
char *get eq arg( char prefix, char *argv[] );

/* Names of PostScript macros created in the DLD file */
char crlf[] = "CRLF\n";
char show[] = "show\n";
char ffeed[] = "FF \n";
char font[3][7] = {"1FONT\n","2FONT\n","3FONT\n"};

/* Other variables */
char tab[11] = " "; /* tab set to max of 10 spaces */
char homedir[MAXPATH];
char temp[MAXPATH];
char *ptr;
int linecount = 0;
int max lines = 0;
int c = '\0';
int x = 0;
int tabspace;
int fontnumber = 0;
int in parens = 0;

int main( int argc, char *argv[] )
{
/* Check for at least 2 command line arguments */
if( argc < 3 ) {
printf( "\nPOSTOUT.EXE v1.0a\n\n"\
"Input and output file name required. E.G.:
POSTOUT IN.TXT OUT.TXT\n"\
"Output file can be any output device or file
name.\n"\
"Optional switches (can be used in any
order/combination):\n"\
"/C for compressed print\n"\
"/L for landscape printing\n"\
"T= for tab stop
spacing \n"\
"F= for font
number\n"\
"1 is Courier, fixed spacing
\n"\
"2 is Helvetica, proportional
spacing\n"\
"3 is Times, proportional
spacing\n"\
"L= for page length
\n");
return 1;
}

/* Check for tab setting switch and adjust tab spacing if needed */
tabspace = atoi( get eq arg( 'T', argv ) );
if( tabspace < 3 || tabspace > 10 ) {
if( tabspace ) {
printf( "\nTab setting must between 3 and 10\n" );
return 1;
}
}
if( tabspace ) {
ptr = tab;
ptr += strlen( tab );
while( (int)strlen( tab ) > tabspace ) {
ptr;
*ptr = '\0';
}
}
else
strcpy( tab ,"" );

/* Check for font setting switch and set fontnumber */
fontnumber = atoi( get eq arg( 'F', argv ) );
if( fontnumber > 3 ) {
printf( "\nFont number must be between 1 and 3\n" );
return 1;
}

/* Check for page length setting and adjust if needed */
max lines = atoi( get eq arg( 'L', argv ) );
if( max lines < 45 || max lines > 81 ) {
if( !max lines )
if( get slash arg( 'L', argv ) )
max lines = 45;
else
max lines = 63;
else {
printf( "\nPage length must be between 45 and 63\n"
);
return 1;
}
}

/* Make sure input file can be opened */
if( ( infile = fopen( argv[1], "rt" ) ) == NULL ) {
printf( "\nCannot open %s for input.\n", argv[1] );
fcloseall();
return 1;
}
/* Make sure output file can be opened */
if( ( outfile = fopen( argv[2], "wt" ) ) == NULL ) {
printf( "\nCannot open %s for output.\n", argv[2] );
fcloseall();
return 1;
}
/* Determine directory POSTOUT was called from and add DLD file
name to it*/
/* This method will only work with DOS version 3.0 or higher */
strcpy( homedir, argv[0] );
ptr = homedir;
ptr += (strlen( homedir ) - 1 );
while( *ptr != '\\' )
ptr;
strcpy( ++ptr, "POSTSCRI.DLD" );

/* Make sure dld file can be openned from same dir as POSTOUT.EXE
*/
if( ( dldfile = fopen( homedir, "rt" ) ) == NULL ) {
printf( "\nCannot open %s\n"\
"DLD file must be in same directory as
POSTOUT.EXE\n", homedir );
fcloseall();
return 1;
}

/* copy dld file to output */
printf( "\nWriting Output\r" );
while( (c = fgetc( dldfile )) != EOF )
fputc( c, outfile );

/* If you are not using an HP III with a PostScript cartridge,
add comment delimiters to next line */
fputs( "/tlxoff 18 def \n", outfile );

/* Insert call in output to DLD Init macro to initialize printjob
*/
fputs( "Init\n", outfile );

/* If /L command line argument was used, set to landscape printing
*/
if( get slash arg( 'L', argv ) )
fputs( "LAND\n", outfile );

/* If /C command line argument was used, set to compressed printing
otherwise set to normal printing */
if( get slash arg( 'C', argv ) )
fputs( "Cmp+\n", outfile );
else
fputs( "Norm\n", outfile );

/* Send font selected or set to default of first font */
if( fontnumber != 0 )
fputs( font[ fontnumber - 1 ], outfile );
else
fputs( font[0], outfile );
/* Format text for PostScript and send to output */
fputc( '(', outfile );
in parens = 1;
while( ( c = fgetc( infile ) ) != EOF ) {
/* Translate form feed characters */
if( c == '\x0C' ) {
if( in parens ) {
fputc( ')', outfile );
in parens = 0;
fputs( show, outfile );
}
fputs( ffeed, outfile );
linecount = 0;
fputc( '(', outfile );
in parens = 1;
continue;
}
/* Replace carraige returns with CRLF macro in DLD */
if( c == '\n' ) {
/* If at last line on page, call FF macro in DLD */
if( ++linecount == max lines ) {
if( in parens ) {
fputc( ')', outfile );
in parens = 0;
}
fputs( show, outfile );
fputs( ffeed, outfile );
fputc( '(', outfile );
in parens = 1;
linecount = 0;
continue;
}
/* Close parens on SHOW macro in DLD and add CRLF
macro */
if( in parens ) {
fputc( ')', outfile );
in parens = 0;
}
fputs( show, outfile );
fputs( crlf, outfile );
fputc( '(', outfile );
in parens = 1;
continue;
}
/* Replace some control character and all ASCII characters
with values greater than 128 in input with an
asterisk */
if( c > '\x7F' || c < '\t' || (c < ' ' && c > '\n') ) {
if( c == '\t' )
fputs( tab, outfile );
else
fputc( '*', outfile );
continue;
}
/* Replace tab character in input with number of spaces
determined by
command line argument */
if( c == '\t' ) {
fputs( tab, outfile );
continue;
}

/* Replace backslashes and parens in input to \\ and \( and
\) */
if( c == '\\' || c == '(' || c == ')' )
fputc( '\\', outfile );

fputc( c, outfile );
}

/* End of file reached close parens for SHOW macro */
fputc( ')', outfile );
fputs( show, outfile );
/* Send FF for macro in DLD */
fputs( ffeed, outfile );
/* Send EOT character to indicate end of job */
fputc( '\x04', outfile );
/* Close files */
fcloseall();
printf( "File Written to %s\n", argv[2] );
return 0;
}


/* Scans all command line arguments for one that begins with a specific
letter and is followed by an = sign. Returns pointer to first
character
after the equal sign */

char *get eq arg( char prefix, char *argv[] ) /* ANSI C function header */
{
char *temptr = *argv;
while( *argv )
{
char *ptr = strcpy( temp, *argv );
temptr = ptr;

if( toupper( *ptr++ ) == prefix && *ptr++ == '=' )
return ptr;
argv++;
}
temptr = (char *)argv;
return temptr;
}

/* Scans all command line arguments for one that begins with a slash and is
followed by a specific letter. Returns 1 if switch is found in
arguments */

int get slash arg( char switch letter, char *argv[] ) /*ANSI C function
header*/
{
while( *argv )
{
char *ptr = strcpy( temp, *argv );

if( *ptr == '/' && toupper( *(++ptr) ) == switch letter )
return 1;
argv++;
}
return 0;
}
/* EOF: PostOut.C */






4 Popup Combinations to Go

Popup Combinations To Go

Adam Menkes

Simple to seemingly impossible popups.

Many people would like the ability to have multiple field popups and
conditional popups in their applications. There are several ways to do
this using pseudo-popups such as a BROWSE NOEDIT NOAPPEND NODELETE or using
arrays and painting the screen. This article will progress from showing
you how to do a simple popup a few different ways to doing multi-field
popups with fields from several databases and have a seemingly impossible
conditional multiple multi-field popup active. You will also learn how to
achieve an @.GET inside a POPUP (for both adding and editing), and various
other tips, tricks, and techniques that show that there is more than one
way to SCAN a CAT. Hey, how about that? I could always get a job as a
comedian if this programming thing doesn't pan out.

The basic concept of defining a popup lies in how to define the selections
within the popup. This is not as obvious as it seems. You can

DEFINE POPUP PROMPT FIELD

but not

DEFINE POPUP PROMPT FIELDS , , ..

But what if you could store Fields 1 to n to a character variable, and
define that as the BAR of the POPUP? To further explore this concept,
consider the database structures below:

Given these database structures, and assuming the PHONE1 database is
active, the following is the definition for a simple POPUP of last names :

DEFINE POPUP SimplePop FROM 1, 40 PROMPT FIELD Lname

Alternatively, the same POPUP can be defined this way :

DEFINE POPUP ScanPop FROM 1, 40
X = 1
SCAN
DEFINE BAR X of ScanPop PROMPT Lname
X = X + 1
ENDSCAN

Now, suppose you wanted a conditional POPUP that displayed a listing of
either the last name or, there was no last name, the name of the company.
All you need do is change the DEFINE BAR in the above SCAN/ENDSCAN to read:

DEFINE BAR X of ScanPop PROMPT ;
IIF(LEN(TRIM(CoName)) = 0, Lname, CoName)

For multiple field popups :

DEFINE BAR X of ScanPop PROMPT ;
TRIM(Fname) + " " + TRIM(Lname) + " " + DTOC(DateIn)

Suppose Phone1.DBF is open in work area 1 and Phone2.DBF is open in work
area 2 (SET RELATION TO Key INTO B) and you want a multi-field popup using
fields from both databases :

DEFINE BAR X of ScanPop PROMPT ;
TRIM(Fname) + " " + TRIM(Lname) + " " + B->Phone

This will only select the first match for the Phone number in area 2 for
each KEY in area 1. Later, you will see how to get them all.

Assume you wanted a POPUP with "header" and "footer" information. The
information can be as many lines and contain any text and fields you wish :

DEFINE BAR 1 of ScanPop PROMPT "Any Header Info You Wish" SKIP
DEFINE BAR 2 of ScanPop PROMPT "Line 2 Info " + AnyFields SKIP

x = 3 && New starting BAR number.
SCAN ...
DEFINE BAR X OF ...
x = x + 1
...
ENDSCAN

DEFINE BAR X of ScanPop PROMPT "Any Footer Info You Wish" SKIP
DEFINE BAR X + 1 of ScanPop PROMPT "Line 2 " + AnyFields SKIP
...

The SKIP is optional and is used in this instance so that the header and
footer information is not selectable. AnyFields can be any field or
combination of fields from one or more databases in any work area.

Another method of defining a pick list is to DEFINE a MENU, and DEFINE PADs
within that menu. Normally, a MENU is thought of as a horizontal bar
menu. Since positioning is determined by the programmer, you can DEFINE
PADs within the MENU vertically (or randomly if so desired).

Using the same principals discussed earlier with ScanPop, below is the code
for a simple picklist using a MENU instead of a POPUP. The result looks
identical to a popup but functions somewhat differently:

DEFINE MENU ScanMenu
x = "1" && Note the quotes (").
SCAN
DEFINE PAD Pad&x of ScanMenu PROMPT Lname AT VAL(x), 40
*- Note: The 40 can be any column position.
x = LTRIM(STR(VAL(x) + 1))
*- Increment a number (x) stored as a character variable.
ENDSCAN

How is this any different from defining a POPUP? Well, a menu can not be
defined beyond the screen length, so you are limited to a picklist of 25
items (43 when your display is set to EGA43 mode), or are you? As the
DEFINE PAD also optionally uses the AT parameter to specify a column
position, and the row is defined as a variable (VAL(x)), the column can
also be defined as a variable, which in effect, gives you a multi-column
picklist, limited by the width of the PADs times the number of PADs
vertically times the number of PADs horizontally and also depends upon
available memory. You could also define the PADs AT MOD(VAL(x), 24) or
MOD(VAL(x), 42) in 43 line mode (1 less than the screen length as the
message line will blank the item) so that the picklist remains in one
column.

DEFINE MENU ScanMenu
x = "1"&& Note that x is character type.
y = 1&& Added for muli-column display.
SCAN
DEFINE PAD Pad&x of ScanMenu PROMPT Lname ;
AT MOD(VAL(x), 24), y
x = LTRIM(STR(VAL(x) + 1))
*- Increment a number (x) stored as a character variable.

IF MOD(VAL(x), 24) = 0
y = y + 17
* Increment the column position by 17 (or whatever number
* of column positions to display the next portion of the
* picklist.)
ENDIF
ENDSCAN

Since row and column positions are defined as variables, you can see how
you could easily define a menu where the items were defined to be visually
more interesting, such as defining the PADs at every other row, or defining
them in a step fashion. Using the above program code, simply change:

... AT MOD(VAL(x), 24 / 2) * 2, y && Every other row.

or more generally :

... AT MOD(VAL(x), 24 / n) * n, y && Every nth row.

To step, incrementing one column position per defined bar:

... AT MOD(VAL(x), 24), y + VAL(x)
or
... AT MOD(VAL(x), 24), y + MOD(VAL(x), 24)

IF y >= variable column position
y = y + number of spaces desired
ENDIF
*- Increments by 1 column position and y spaces between records 1-25 and
records 26-50 etc.

Now, using this principal of variable row and column coordinates, you can
see how the following menus are defined. You can do "V", "X", "<", ">",
"\", "/" rather easily and, in fact, could do any pattern or randomly if so
desired.

To get one or more ">" patterns, you need to reverse the direction of the
step at the midpoint; line 12 in 25 line mode (lines 0 - 24), and line 21
in 43 line mode (0-42). PAD1 is at row 0 (the PAD number is always 1
higher than the row) and the easiest way to have symmetry about a midpoint
(line 12 in this example) is to start with the value of the midpoint (12)
and subtract (or add) the appropriate number of rows. Using the absolute
value of the difference between the current row and the midpoint will
provide this symmetry as 11 - 11 = 0, 11 - 10 = 1, and 11 - 12 = - 1
[ABS(-1) = 1]. Here is the syntax for the ">" pattern:

DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
mCol + abs(12 + (1 - (INT(VAL(PadNum) / 24)) ;
- ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ;
" (" + AC + ") " + Phone + " "

To reverse the direction:

DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
mCol + ABS(12 + (1 - (INT(VAL(PadNum) / 24)) ;
+ ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ;
" (" + AC + ") " + Phone + " "

Can you spot the difference in the code? There is only one : Line 3 has a
minus sign for the "<" pattern and a plus sign for the ">" pattern. Surely
there must be an easier and better way to determine direction without
duplicating code. The wonders of macro substitution allow not only
character substitution but commands and operators as well.

Here's a way of performing the pattern in either direction:

DEFINE PAD pad&PadNum OF MenuPad AT MOD(VAL(PadNum), 24), ;
mCol + ABS(12 + (1 - (INT(VAL(PadNum) / 24)) ;
&PlusMinus. ABS(11 - MOD(VAL(PadNum), 24)))) PROMPT ;
" (" + AC + ") " + Phone + " "
* - PlusMinus is a character variable equal to "+" or "-"

Now, the "><" pattern is easy. In the SCAN..ENDSCAN loop, add :

PlusMinus = IIF(mCol < 17, "-", "+")
* 17 is the length of the PROMPT in this example.

These are just a few examples of the limitless possibilities of defining
POPUPs and MENUs using SCAN..ENDSCAN with proper variable substitution of
the row and column positions.

Now that you have these concepts wired (hopefully without too much
short-circuiting), you can probably figure out how to have two "popups"
active, regardless of whether or not they are multi-field or
multi-relational popups. Since you know from the documentation that two
popups can not be active at the same time, but a MENU and a POPUP can, and
since either one can be defined using SCAN / ENDSCAN with any fields from
any related databases FOR any condition (or indexed conditionally), all you
need to do is activate whichever popup coincides with each PAD. Rather
than determining this programmatically, you initialize each POPUP as you
are defining your MENU PADs. Each one of these popups will be activated by
cursor movement keys (left-arrow, right-arrow, up-arrow, down-arrow, Home,
End) if ON PAD is used, and if ON SELECTION PAD is used, the Enter key.

Below is the code for having both popups (actually one menu that looks like
a popup and many popups of which only one is active at a time). As you can
see, there are two SCAN / ENDSCAN loops, one inside the other, which define
your selections.

USE Phone1 IN 1 ORDER Lname
USE Phone2 IN 2 ORDER Phone
DEFINE MENU MenuTest

SELECT 1

x= "1"
SCAN
mKey = Key && Related field in both databases.
DEFINE PAD Pad&x of MenuTest PROMPT ;
Lname + ", "+ Fname AT VAL(x), 10

SELECT 2
DEFINE POPUP Pop&x FROM VAL(X), 52
y = 0 && No records may be selected.
SCAN for Key = mKey
*- If a matching record is FOUND(), increment the BARs,
*- DEFINE them, and initialize the ON SELECTION.
y = y + 1
DEFINE BAR Y OF Pop&x PROMPT "(" + AC + ")"+Phone
ON SELECTION POPUP Pop&x DEACTIVATE MENU
ENDSCAN

IF y > 0
*- If records ARE selected -
ON PAD Pad&x OF MenuTest ACTIVATE POPUP Pop&x
*- ACTIVATE the appropriate POPUP.
ENDIF

x = LTRIM(STR(VAL(x) + 1))
*- Increment a character numeric variable ("1" not 1).
SELECT 1
ENDSCAN
ACTIVATE MENU MenuTest

Again, keep in mind that the more pads, bars and popups defined, the more
potential for "Insufficient memory" error messages.

Other things you can do with POPUPs include having memo fields in the POPUP
and data input or edited directly (or seemingly so) in the POPUP. These
capabilities are actually quite simple. In order to GET a variable inside
a POPUP, you first DEFINE the POPUP as you normally would, then use ON
SELECTION POPUP to DEACTIVATE the POPUP, immediately afterwards SHOW the
POPUP, and GET the variable at a variable row and column position. A
partial example is listed below :

SEEK mKey
* mKey should already be initialized - i.e. mKey = "000001"

DEFINE POPUP PopPh FROM 14, 4
DEFINE BAR 1 OF PopPh PROMPT SPACE(20) + ""
DEFINE BAR 2 OF PopPh PROMPT REPLICATE(CHR(205), 70) SKIP
x = 3
SCAN WHILE KEY = mKey
DEFINE BAR x OF PopPh PROMPT " (" + AC + ") " + Phone + ;
" x " + Ext + " " + PhDesc + " " + TimeSt + " " + TimEnd
x = x + 1
ENDSCAN

ON SELECTION POPUP PopPh DEACTIVATE POPUP
ACTIVATE POPUP PopPh
MROW = ROW()

IF BAR() <> 1
mEdRec = BAR() - 2
ELSE
mEdRec = 1
ENDIF

SHOW POPUP PopPh

* This is where you would insert code to determine what
* record was selected and either store blanks (zeros) or
* the contents of the selected record to the memory vars.

IF mEdRec = 1
* - Set memory variables blank
ELSE
* - Set memory variables equal to the value of respective fields
ENDIF

SEEK mKey
* Reposition the record pointer to the first matching Key.

IF FOUND()
SKIP mEdRec - 1
* Skip the appropriate number of records.
ENDIF

IF LASTKEY() <> 27 && Esc key was not pressed.
@ mRow, 6 SAY "("
@ mRow, 7 GET mAC PICTURE "999"
@ mRow, 10 SAY ")"
@ mRow, 12 GET mPhone PICTURE "999-9999"
@ mRow, 20 SAY " x "
@ mRow, 23 GET mExt PICTURE "99999"
@ mRow, 29 GET mPhDesc PICTURE "XXXXXXXXXXXXXXXXXXXXXXXXX"
@ mRow, 55 GET mTimeSt PICTURE "99:99!M"
@ mRow, 64 GET mTimEnd PICTURE "99:99!M"
READ
ENDIF


5 Etc.

Etc.
Performance Considerations

The question of dBASE IV performance has run the gamut from lost clusters
and available memory to solar flares and a shift in the space time
continuum.
But just how do you explain an operation that runs slower now than it did
in 1.0?

The Need For Speed

Some users have informed us of speed decrease during REPLACE operations
with dBASE IV version 1.1. The same operation in dBASE IV version 1.0 was
faster. This may be due to delays caused by overlay swapping.

This can be the case when REPLACEing or APPENDing to indexed databases.
True, both versions had .MDX files but indexes are not the cause of the
problem. In version 1.1, the INDEX and REPLACE operations are in different
segments of the very large DBASE.OVL file, thus causing a good deal of
swapping in and out of memory. This is what causes the slow down. Here
are some tips for speeding up performance in the new 1.1 environment:

Set up a RAM disk and SET DBTMP (a DOS environmental variable) equal to
this RAM disk directory.

Free up as much DOS conventional memory as possible, getting rid of
things such as ANSI.SYS (which most people don't need) in your CONFIG.SYS
file. Remove other non-essential drivers from your CONFIG.SYS as well.
If you're using a 4.x version of DOS, remove the DOS APPEND command from
your AUTOEXEC.BAT file.

For Novell users, get the Novell EMSNET3 and XMSNET3. These will free up
more DOS conventional memory by loading NET3 in extended or expanded
memory. The NetWire forum on CompuServe should have these utilities
available. You may also call Novell at 1-800-LANSWER.

Play "musical indexes". Create an .MDX file called Empty with a tag of
the same name. To do this, put any database into use and type in the
command:

INDEX ON " " FOR .F. TAG Empty OF Empty

Prior to doing a large REPLACE, close the .DBF and rename its production
.MDX to another name. Rename Empty.MDX to make it your temporary
production .MDX. Use the database and perform your REPLACE or APPEND
operations. Close the database, rename the two .MDX files back to their
original names and REINDEX. Here is a series of commands that outline the
operation, using a file called Trans.DBF, which is assumed to be open:

USE && closes file
RENAME Trans.MDX TO Hold.MDX
RENAME Empty.MDX TO Trans.MDX
USE Trans
REPLACE ...

If all of the REPLACEs are to be done on a single field, the process is
much simpler. A tag for a single field can be updated by the following
command:

SET ORDER TO
REPLACE NEXT 1 WITH

This last suggestion adds some code to your program, but it will work
around the slowness you're experiencing.

Banyan BIOS
Although there are a small number of problems known to exist when running
dBASE IV with Banyan Vines 3.1 and 4.0, they are attributed to the lack of
full Net BIOS support in the software.

However, it is possible to setup the Banyan network to fully support the
NetBios protocol. All known problems will disappear once the Banyan
network is setup properly for NetBios support.

There are three steps to setting up Banyan with the full NetBios support:

1. Create a "NetBios" Service on the server by running the Banyan MSERVICE
program. This program sets up NetBios emulation protocol across the LAN
and checks for unique node addresses.

2. Run the PCNETB.COM program on the workstation. This program loads the
NetBios protocol.

3. Run the SETNETB.COM program on the workstation. This program assigns a
name to the workstation.

Adding the NetBios support will up take about 37KB of memory on top of what
is already loaded. For example, on the server, run the MSERVICE program
and set the NetBios service name to something like "NETB@Exec@OURCOMP". On
the workstation, add the following to the Autoexec.BAT:

PCNETB
SETNETB /N:nodename NETB@Exec@OURCOMP

where nodename is your unique workstation name. To be able to set NETBIOS
for Vines 4.01 you need to load the TSR using the syntax below.

"SETNETB /NAME:nodename NETB@Exec@OURCOMP"

One final observation: this TSR can't be loaded high.



6 Corrigenda

Corrigenda

In our February 1991 issue, the code included in the article "Easy .BINs in
C" contained some errors related to the placement of underscores and
dashes. Please note that in any case where the global variables argc or
argv are mentioned, each should be immediately preceded by an underscore,
thus appearing as _argc or _argv. Additionally, the last code line in
Reverse!.C shows the _argc variable being followed by a minus sign. The
line should appear as

strrev(_argv[_argc--]);

The same scenario is applied in DD.C on page 18. In the last clause, the
double minus sign was again omitted on two lines. The code should actually
read

if (negative)
string[--len] = '-';
while (len)
string[--len] = ' ';


7 Desktop Calculator

Desktop Calculator
Dan Madoni

Isn't it incredible how rapidly things change? It wasn't that long ago
when the idea of personal computing was not nearly as matter-of-fact as we
regard the activity today. Seems like it was around that same time that
the throw-away calculator that you get with a subscription to Time used to
cost a pretty penny.

I am always amused to walk into the office of a power executive and find
that same throw-away calculator sitting next to a new 33MHz Super Dynamo
386 computer. What's the point? Well, maybe the calculator is used to
calculate the ultimate numbers to be entered into a database. Wouldn't it
be great to be able to pop up a "video calculator" that does all that basic
arithmetic??

The Calc() function (see listing) does just that. Suppose you're sitting
in a numeric field, and the only thing keeping you from punching in the
numbers is the fact that you don't know which numbers to punch they
haven't been calculated. You press a function key and up pops this
calculator on the screen that works the same way as the pocket calculator
on your desk (or in your pocket.) You punch in the numbers on the video
calculator via the number pad on your keyboard and watch the result appear
after each calculation on the LED-style display. Pressing Escape erases
the calculator and returns you to your prior activity, which is left
unaffected.

After copying the Calc() UDF code below into your program, include the
following command in your program:

ON KEY LABEL F5 ?? Calc()

Whenever you need to use the calculator, press F5, and it will appear in
the middle of the screen. Use the number pad for numbers. All your
operators for addition, subtraction, multiplication and division are on the
keypad of most computer keyboards as well. The asterisk is used for
multiplication rather than an X. All other operator symbols should be
familiar. You use the Enter key to get the result, taking the place of the
equal sign.

When you are in the process of doing calculations, the number you are
working with is accumulative until you press the CLEAR button, ("C" on the
keyboard.) If you make a mistake while entering a number, use the
left-arrow key to delete the right-most number. Of course, you can use
another key other than F5. The function key F5 happens to be a key that
does not disable other full-screen functions.

Function: Calc
FUNCTION Calc

* Record current environment.
B4color = SET("ATTRIBUTES")
B4curs = SET("CURSOR")
B4dec = SET("DECIMAL")

SET CURSOR OFF
SET DECIMAL TO 2
SAVE SCREEN TO B4calc
tempclr = SET("ATTRIBUTES")

* Draw the calculator.
SET COLOR TO N/W

@ 8,32 FILL TO 18,50 COLOR W/N
@ 7,31 CLEAR TO 17,49
SET COLOR TO &tempclr

* "Buttons" for calculator.
@ 10,33 SAY " 7 " COLOR W+/N
@ 10,37 SAY " 8 " COLOR W+/N
@ 10,41 SAY " 9 " COLOR W+/N
@ 10,45 SAY " + " COLOR Gr+/N

@ 12,33 SAY " 4 " COLOR W+/N
@ 12,37 SAY " 5 " COLOR W+/N
@ 12,41 SAY " 6 " COLOR W+/N
@ 12,45 SAY " - " COLOR Gr+/N

@ 14,33 SAY " 1 " COLOR W+/N
@ 14,37 SAY " 2 " COLOR W+/N
@ 14,41 SAY " 3 " COLOR W+/N
@ 14,45 SAY " X " COLOR Gr+/N

@ 16,33 SAY " 0 " COLOR W+/N
@ 16,37 SAY " " + CHR(249) + " " COLOR Gr+/N
@ 16,41 SAY " C " COLOR Gr+/N
@ 16,45 SAY " " + CHR(246) + " " COLOR Gr+/N
@ 8,33 SAY SPACE(15) COLOR W/N
SAVE SCREEN TO curcalc

DECLARE calcnums[100] && Numbers stored as they are pressed
DECLARE calcops[100] && Operators stored as they are pressed

STORE .F. TO decon,calcdone,invkey
* decon : Turn decimal point on or off
* calcdone : Tells whether or not to keyboard a value after
* calculations have been made
* invkey : Indicates Invalid Key Pressed if .T.

newnum = .T. && Indicates new number is on the display
worknum = "0" && Number to be displayed
calccnt = 0 && Number of entries made before "=" is pressed

DO WHILE .T.
* If the user has not pressed an invalid key...
IF .NOT. invkey
worknum = IIF(LEN(worknum) = 0, "0", worknum)
worknum = IIF(LEN(worknum) > 1 .AND. SUBSTR(worknum,1, 1) = "0",;
SUBSTR(worknum, 2), worknum) * Remove leading zero
worknum = IIF(LEN(worknum) > 13, SUBSTR(worknum, 1, 13), worknum)
* Truncate long number

RESTORE SCREEN FROM curcalc
@ 8,35 SAY SPACE(13 - LEN(worknum)) + worknum COLOR R+/N && Show
number
ELSE
invkey = .F.
ENDIF

waiting = INKEY(0)
* Reset if new number
IF newnum
worknum = ""
newnum = .F.
decon = .F.
ENDIF

DO CASE
CASE waiting = 27 .OR. waiting = 23 && ESCape or Ctrl-End
EXIT
CASE waiting = 19 && Left-Arrow
@ 8,34 SAY CHR(237) COLOR Gr+/N
IF LEN(worknum) <> 0
decon = IIF(SUBSTR(worknum,LEN(worknum), 1) = ".", .F., .T.)
* Turn decimal off if deleted
worknum = SUBSTR(worknum,1,LEN(worknum) - 1) && truncate number
ENDIF

* Numbers on keypad
CASE waiting = 55
@ 10,33 SAY " 7 " COLOR W+/G
worknum = worknum + "7"
CASE waiting = 56
@ 10,37 SAY " 8 " COLOR W+/G
worknum = worknum + "8"
CASE waiting = 57
@ 10,41 SAY " 9 " COLOR W+/G
worknum = worknum + "9"
CASE waiting = 52
@ 12,33 SAY " 4 " COLOR W+/G
worknum = worknum + "4"
CASE waiting = 53
@ 12,37 SAY " 5 " COLOR W+/G
worknum = worknum + "5"
CASE waiting = 54
@ 12,41 SAY " 6 " COLOR W+/G
worknum = worknum + "6"
CASE waiting = 49
@ 14,33 SAY " 1 " COLOR W+/G
worknum = worknum + "1"
CASE waiting = 50
@ 14,37 SAY " 2 " COLOR W+/G
worknum = worknum + "2"
CASE waiting = 51
@ 14,41 SAY " 3 " COLOR W+/G
worknum = worknum + "3"
CASE waiting = 48
@ 16,33 SAY " 0 " COLOR W+/G
worknum = worknum + "0"

CASE waiting = 46 .AND. .NOT. Decon && Decimal point
@ 16,37 SAY " " + CHR(249) + " " COLOR W+/G
worknum = worknum + "."
decon = .T.
CASE STR(waiting,3) $ " 43 13 61" && ENTER, "=", or "+"
@ 10,45 SAY " + " COLOR W+/G
newnum = .T.
calccnt = calccnt + 1
calcnums[calccnt] = VAL(worknum)
calcops[calccnt + 1] = "+"
CASE waiting = 45
@ 12,45 SAY " - " COLOR W+/G
newnum = .T.
calccnt = calccnt + 1
calcnums[calccnt] = VAL(worknum)
calcops[calccnt + 1] = "-"
CASE CHR(waiting) $ "XX*"
@ 14,45 SAY " X " COLOR W+/G
newnum = .T.
calccnt = calccnt + 1
calcnums[calccnt] = VAL(worknum)
calcops[calccnt + 1] = "*"
CASE waiting = 47
@ 16,45 SAY " " + CHR(246) + " " COLOR W+/G
newnum = .T.
calccnt = calccnt + 1
calcnums[calccnt] = VAL(worknum)
calcops[calccnt + 1] = "/"
CASE CHR(waiting) $ "Cc" && The CLEAR button
calccnt = 0
newnum = .T.
OTHERWISE
invkey = .T. && User pressed an invalid key
ENDCASE

IF CHR(waiting) $ "+/-*XX=" .OR. waiting = 13
allnum = calcnums[1] && allnum will be the result of all calculations
calccnt2 = 1

* The following DO WHILE loop takes all of the numbers and
* operators that have been accumulated thus far and
* calulates them in the order entered. We can't
* concantenate them into one large string to calculate because
* of the way that dBASE IV orders the precedence of operators.
* (such as 5 + 5 / 2 calculated from left to right would result
* differently than as one dBASE expression)

DO WHILE calccnt2 < calccnt
calccnt2 = calccnt2 + 1
DO CASE
CASE calcops[calccnt2] = "+"
allnum = allnum + calcnums[calccnt2]
CASE calcops[calccnt2] = "-"
allnum = allnum - calcnums[calccnt2]
CASE calcops[calccnt2] = "*"
allnum = allnum * calcnums[calccnt2]
CASE calcops[calccnt2] = "/"
allnum = allnum / calcnums[calccnt2]
ENDCASE
ENDDO

@ 8,33 SAY SPACE(15) COLOR W/N
@ 8,35 SAY STR(allnum,13,2) COLOR R+/N && Show the result

STORE .T. TO calcdone,newnum
waiting = INKEY(0)
KEYBOARD CHR(waiting) && Keyboard next number and clear for new number
ENDIF

IF .NOT. calcdone && If number is still building on display
IF .NOT. invkey
SET BELL TO 5000,1 && Good tone if key is not invalid
ELSE
SET BELL TO 20,1 && Bad tone if key is invalid
ENDIF
?? CHR(7)
ELSE
calcdone = .F.
invkey = IIF(.NOT. CHR(waiting) $ "1234567890+-Cc*/",.T., invkey)
* Determine valid key pressed
ENDIF
ENDDO

RESTORE SCREEN FROM B4calc
RELEASE SCREEN B4calc
RELEASE SCREEN curcalc
RELEASE calcnums,calcops

SET COLOR TO &B4color
SET DECIMAL TO B4dec
SET CURSOR &B4curs


RETURN .T.
* EOF: Calc.PRG



8 Betcha Can't Pick Just One

Betcha Can't Pick Just One
Lena Tjandra

Popups are a welcome addition to the functionality of the dBASE language.
By their nature, once you press Return on a highlighted selection in the
popup, it disappears and an action is taken. You cannot make more than one
selection at a time, but there is an alternate and sometimes preferable way
of utilizing a popup.

Suppose you have a list of salespersons, and you would like to generate a
year-to-date sales report on specific salespersons only. You'd ideally
like to go through a popup list, pressing Return (or some other key to mark
the records) on those you want selected for a certain operation. When you
are done, a specific exit key would clear the popup and perform your task.
How would you do this and still incorporate the use of a popup? The
program that follows shows you how.

The purpose of this program is to create a popup from which the user can
make multiple selections. The popup is created from values stored in a
database. When the user makes a selection, the popup is redefined with a
character marker that indicates a selection. At the same time, a logical
true (.T.) value is replaced into the field, "Selected" which corresponds
to the selected option in the database. If an option on the popup is
de-selected, then a logical false (.F.) value is replaced into the
"Selected" field and the popup is redefined without the character marker.

If your popup contains a lot of options to choose from, then your best bet
is to store them in a database and create the popup based on the values in
that database. There are several good reasons for doing this such as code
brevity, modularity, and clarity. For instance, instead of having to
DEFINE BARs for each option in the popup, you can define all the BARs in
just a few statements as shown on lines 19 - 22. Second, it makes the
program more modular because now you can modify the options through the
database rather than through the program, thus, giving your program more
flexibility. Third, you can create an additional field in the database to
store marked selections, thus, saving you from declaring an array of
unnecessary size. Furthermore, it makes the goal easier to process.
Suppose you stored selected values in an array. The consequence would be
having to look in each element to see which options have been selected and
create a macro expression for the report. If the values were marked in the
database by assigning .T. to a field called SELECTED, then a single command
will suffice:

REPORT FORM sales FOR selected

I would also like to point out a few tips contained in this program. You
may wonder why a SHOW POPUP on line 35 is issued before an ACTIVATE
POPUP. For those of you who have worked with popups a lot, you may notice
that the popup will flicker very quickly when control is returned to the
ACTIVATE POPUP statement or when the popup is re-activated. By issuing a
SHOW POPUP, we can alleviate the flickers.

Lines 53 - 54 may seem peculiar, but since the database has an active
index, we can only move to the record relative to the top of the database.
However, if the database were in natural order, we can reference it
directly with a GOTO statement. Another method is to do a
SEEK, but doing a SKIP is the only way that works with both an indexed or
un-indexed database.

Additionally, you may also notice that the F9 and F10 keys have been
defined as hotkeys to allow the user the choice of selecting or
de-selecting all the options on the popup at once. This brings me to one
final point. In the procedure entitled PSelect, the bar is redefined, but
it is not necessary to deactivate and reactivate the popup. Since this
procedure was called from an ON SELECTION POPUP, the ON SELECTION takes
care of reactivating the popup with the current bar definitions. On the
other hand, the procedure Sel All was called from an ON KEY, so we need to
deactivate the popup manually as on line 77, and reactivate it again in
order for the new bars to display.

1 CLEAR
2 SET STATUS OFF
3 SET TALK OFF
4
5 * Store ASCII value of {down-arrow} and {Esc} to variables
6 dnarrow = CHR(24)
7 escape = 27
8
9 * Open database
10 SELECT 1
11 USE salesrep ORDER name
12
13 * Define popup, pop1
14 DEFINE POPUP pop1 FROM 5, 15 TO 23, LEN(name) + 15
15
16 bar cnt = 1
17
18 * Define bars of popup from the "name" field in database
19 SCAN
20 DEFINE BAR bar cnt OF pop1 PROMPT TRIM(Name) + IIF(selected, CHR(17),
"")
21 bar cnt = bar cnt + 1
22 ENDSCAN
23
24 * Define popup action
25 ON SELECTION POPUP pop1 DO PSelect
26
27 * Define hotkey for selecting all options
28 ON KEY LABEL F10 DO Sel All WITH .T.
29
30 * Define hotkey for de-selecting all options
31 ON KEY LABEL F9 DO Sel All WITH .F.
32
33 * Activate popup
34 DO WHILE LASTKEY() <> escape
35 SHOW POPUP pop1
36 ACTIVATE POPUP pop1
37 ENDDO
38
39 CLEAR
40 CLEAR ALL
41 SET STATUS ON
42 SET TALK ON
43
44
45 PROCEDURE pselect
46
47 * Redefine bar based on selection/de-selection
48 DEFINE BAR BAR() OF pop1 PROMPT IIF(RIGHT(PROMPT(), 1) = CHR(17),;
49 SUBSTR(PROMPT(), 1, AT(CHR(17), PROMPT()) - 1), PROMPT() + CHR(17))
50
51 * Change the "Selected" field to .T. if option has been selected
52 * or .F. if option has been de-selected.
53 GO TOP
54 SKIP BAR() - 1
55 REPLACE selected WITH IIF(selected, .F., .T.)
56
57 * Move highlight in popup down to the next option
58 KEYBOARD dnarrow
59
60 RETURN
61
62 PROCEDURE Sel All
63
64 PARAMETER s key
65
66 * Change all values in the "Selected" field .T. or .F. in database
67 REPLACE ALL Selected WITH s key
68
69 bar cnt = 1
70
71 * Re-define bars of popup from the "Name" field in database
72 SCAN
73 DEFINE BAR bar cnt OF pop1 PROMPT TRIM(name) + IIF(selected, CHR(17),
"")
74 bar cnt = bar cnt + 1
75 ENDSCAN
76
77 DEACTIVATE POPUP
78
79 RETURN



9 Support That Never Sleeps

Support That Never Sleeps

Introducing the newest member of our software support team:
Auto-Tate for dBASE IV.

Auto-Tate is an automated audio support system, that is accessed via a
touch tone telephone, capable of handling up to 16 callers simultaneously.
It is a system designed to "remember" all of the choices each caller makes
and route them to the information that pertains specifically to them. It
will also allow callers to "save" their place so they can hang up and call
back, even days later, and pick up right where they left off. Best of all,
this great new service is toll-free!

In this "Age of Information", people have a high level of expectation when
it comes to response time, availability, and content of the information
they seek. Ashton-Tate's software support department is always looking for
new ways to meet these expectations.

Employing automated voice technology allows us to offer our customers many
new advantages, the most obvious being that it's toll free, 24 hours a day,
7 days a week. This means you can get help with problems at night and on
weekends but, perhaps, more importantly, you can spend time learning from
Auto-Tate at your convenience, which is not always during your busy work
day.

Beyond convenience, Auto-Tate provides a resource that can be modified and
updated continually, it can grow and change in a way that no manual or
written word can. Imagine going back to using a typewriter instead of the
word processor you now have; if one change is required, it means having to
re-type the entire page! Manuals have the same limitation and therefore
must be written in less specific terms in order to retain validity over a
longer period of time. With Auto-Tate we can address the areas that seem
the most confusing to our customers and be quite specific, getting right to
the point.

Our technicians continually relay what topics concern our customers the
very most. Currently, two very asked-about subjects are installation and
printing. With Auto-Tate, you can approach these topics from two angles:
by listening to tutorial information or troubleshooting a specific
problem. You can do a "walk through" for background and preparation, or
you can select an error message or symptom you have enountered, and listen
to the probable cause and solution.

For example: you are ready to install version 1.1, and you have a lot of
questions. A walk-thru will tell you what to expect at each step and what
you need to do (or not do as the case me be).

But suppose you've already installed and done everything correctly, only
the program won't start. Troubleshooting will take you through the same
steps a technician would, telling you where to look for the problem and
giving suggestions or alternatives.

The tutorials and troubleshooting are designed by technicians with years of
phone support experience; they know the questions customers really ask, and
they know how to break down the answers into logical steps. Each subject
is handled by a technician who has specialized in that particular area,
giving you the benefit of many technicians' knowledge at once.

There's a feeling that some customers get when they know the answer is
somewhere in their manuals, but they're pressed for time and just need
someone to direct them to the right spot. Both tutorial and
troubleshooting will refer you to specific pages in the documentation where
you can get more information.

How To Use It

Auto-Tate consists of two parts: a voice tree and a diagnostic. The voice
tree is something you are most likely familiar with these days if you call
any type of service organization. You select a topic from a menu and
follow directions to obtain more information. You can return to the menu
and make a different choice if you wish at any step.

The diagnostic is quite different, because it is leading you through a
progression of informative steps based on the responses you make.
Sometimes you will select from a menu of choices, other times you will
simply answer "yes" or "no". If you feel you have made an incorrect
response or need to repeat something, you press the * (asterisk) to backup
one step.

In a diagnostic session, you will often be asked to check or try something,
which may take time, so you can "save" your session by pressing "00".
Auto-Tate will assign you a session number, then you can hang up and go do
what you need to do. Call back any time and resume the session by entering
your session number when prompted.

Suppose you're having printing problems. When you attempt to print,
nothing comes out of the printer. Auto-Tate will ask if you can print from
a DOS level to determine if you're having a problem within dBASE IV
specifically or with your hardware environment. Auto-Tate will tell you
exactly what to type on your computer to test the printing capability of
your computer from a DOS level. Sometimes, you may not be in a position to
try the suggestions at the time of your call to Auto-Tate. Should that be
the case, press 00 to save your place in the session, hang up and try the
suggestions when you're in front of your computer! If the results are not
conclusive and you need more information, when you call Auto-Tate again,
you'll pick up right where you left off!

Once you're in diagnostic mode, you can save your session anytime, even if
you just need to take another call, go to a meeting, or if you're just not
in the mood anymore. Auto-Tate will be there when you come back, saving
your place so you don't have to remember where you were or listen to the
same recorded prompts repeatedly.

The following diagram illustrates the menu access for Auto-Tate. As you
can see, only 1 and 2 take you to diagnostics, the other selections behave
in regular voice tree manner, meaning you can return to the menu and choose
something else.

Choosing the Different Options

The present offerings are exclusively for dBASE IV 1.1. Choosing the first
option begins a diagnostic session for tutorial or troubleshooting dBASE IV
version 1.1.

Choosing option 2, "Resume Previous Session", is for those users who have
previously been in a diagnostic session and have saved their place they now
wish to resume. Auto-Tate prompts you to enter your session number it
assigned during your last phone call on the subject.

The third option is "Hardware Certification List". Aston-Tate has
certified various PCs, printers and Local Area Networks for use with dBASE
IV version 1.1. Here, you can find out if your equipment has been tested
and is approved for use with our software.

Option #4, "Bulletin Board System" gives phone numbers and brief
instructions for accessing and downloading files on the Ashton-Tate
Bulletin Board System and CompuServe. Public domain or shareware programs
such as PKZIP are explained since you may need one or more of these
utilities to use the files you have downloaded.

The option entitled "This week's Top Five" is intended to give a quick look
at the top five support topics of the week. Included could be
announcements, helpful hints, common problems (and their solutions);
anything Software Support thinks could be of interest to dBASE IV users and
developers. This information will change weekly, so you can check
routinely.

We always include the option to bypass the introduction and instructions,
for those repeat users who are familiar with the system but we do recommend
you listen to them the first time around. You'll want to become familiar
with how to navigate through the system.

In all categories, the information will be updated on a regular basis,
evaluating what customers are calling about and adding or updating
information. So, you have a dynamic, efficient and constantly available
new medium for obtaining support on dBASE IV. We've taken great strides to
create this system and have much in store for this service in the future.
We feel it will be a tremendous help to our customers getting up and
running with dBASE IV. Auto-Tate is going to make a great contribution to
our support team. In a sense, we've hired the ideal technician: efficient,
listens well, never gets tired or bored and remembers everything. But most
importantly to our support staff, he never hogs the pizza!




 December 11, 2017  Add comments

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)