home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
qbnewsl
/
qbnws302
/
qbnws302.nws
Wrap
Text File
|
1992-06-29
|
44KB
|
1,160 lines
Volume 3, Number 2 June 29, 1992
**************************************************
* *
* QBNews *
* *
* International QuickBASIC Electronic *
* Newsleter *
* *
* Dedicated to promoting QuickBASIC around *
* the world *
* *
**************************************************
The QBNews is an electronic newsletter published by Clearware
Computing. It can be freely distributed providing NO CHARGE is charged
for distribution. The QBNews is copyrighted in full by Clearware
Computing. The authors hold the copyright to their individual
articles. All program code appearing in QBNews is released into the
public domain. You may do what you wish with the code except
copyright it. QBNews must be distributed whole and unmodified.
You can write The QBNews at:
The QBNews
P.O. Box 507
Sandy Hook, CT 06482
Copyright (c) 1992 by Clearware Computing.
The QBNews Page i
Volume 3, Number 2 June 29, 1992
----------------------------------------------------------------------
T A B L E O F C O N T E N T S
1. From the Editor's Desk
Is this Goodbye? ............................................. 1
2. DataBASICs and File I/O
File Types 100 by Richard Vannoy ............................. 2
Indexing 101 by Richard Vannoy ............................... 5
Hashing-It isn't all Corned Beef by Dave Cleary and Mike Avery 10
Create dBASE III Data Files from QuickBASIC by Dennis Gellert 11
Adding LANtastic Support to your Programs by Chip Morrow ..... 13
A powerful line editor for QB by Larry Stone ................. 15
The QBNews Page ii
Volume 3, Number 2 June 29, 1992
----------------------------------------------------------------------
F r o m t h e E d i t o r ' s D e s k
----------------------------------------------------------------------
Is this Goodbye?
As you may have noticed, this edition of The QBNews is not up to the
usual high standards set by previous issues. The problem is that I am
running out of people who I can hit up for free articles. Most of the
articles that have appeared in The QBNews were because of direct
solicitations from me. This issue relied on on contributions sent in
to me without my asking for them.
For some reason, at least from the mail I have (or have not is more
like it) received, interest seems to be wanning in The QBNews. Because
of this, I have no choice but to suspend offering disk subscriptions
and as of right now, Volume 3 will be the end of The QBNews. The
months that passed since the 301 issue have really been disappointing
as far as response goes. I don't expect this issue to generate much
either because of how poor it is. However, I am committed to
continuing on until 304, and alot can happen in those six months. If
possible, I would like to continue producing The QBNews. However, that
means the term "user supported" will have to take on much more meaning
in the coming months
Dave Cleary - Publisher of The QBNews
The QBNews Page 1
Volume 3, Number 2 June 29, 1992
----------------------------------------------------------------------
D a t a B A S I C s a n d F i l e I / O
----------------------------------------------------------------------
File Types 100 by Richard Vannoy
This article will familiarize beginners to computer programming and
the QuickBASIC programming language with the basics of file creation
and a brief on the types of files generally used in computer programs.
First, we need a few definitions that describe how data is stored.
Definitions:
Field: A particular type of information in a file.
Common field names would be phone number, name,
address, or date.
Record: The sum of the fields for one person, place or
thing.
Field 1: Name: Richard <----- Together, these three
Field 2: Phone: 777-1212 <----- fields make one
Field 3: Birthday: 04\26\60 <----- record.
There are generally three types of files most commonly used today.
They are sequential, random access and binary.
Sequential, as the name implies, means that data is written to the
file in sequence, or one after the other, so if I write the name and
phone numbers of a few friends in a sequential file, it might look
like the line below. (The commas are called delimiters. They are put
in by you or the program to separate each piece of data)
Sam,777-5155,George,123-4567,Bill,323-1212
Notice that all of the information (fields and records) are slammed
together and that there is no way to tell where the name Bill starts
without reading the file from the beginning. To retrieve, or read the
items, we read them from the beginning, sequentially, until we get to
the information desired. If Richard is the 100th name in the list, we
have to READ the first 99 names/phone numbers to get to it.
In a random access file, the fields are defined to a specific length
and any spaces not used are filled with blanks. This allows each
record to be the exact same size. Like..
Name: 10 bytes |Richard |
Phone: 8 bytes |777-1212|
Now we have a record length of 18 bytes, no matter how long the data
is, so lets write the same info as above to a file..
Sam 777-5155George 123-4567Bill 323-1212
| | |
Note how a new record starts every 18 bytes, and that "unused" bytes
in the records are filled with spaces, so we can predict where every
The QBNews Page 2
Volume 3, Number 2 June 29, 1992
record is going to start. And we don't need separaters since we know
exactly where each record and each field starts. Not only that, if we
know that Richard's info is in the 100th record, we can go directly to
it since the record length is constant. Because of this
predictability, which transforms to SPEED when it is time to find
information, random access records are well suited to storing and
retrieving large quantities of data.
These are the two most common storage techniques, but there are many
more! One, called Indexed Sequential Access Method (ISAM) is stored
somewhat like a sequential file, but is accessed through an indexing
system, which gives it one of the main advantage of sequential files
(packing info tightly) and also one of the main advantage of random
access files (FAST recovery).
Binary files... Well, ALL files are binary files to the extent that
any DOS file can be opened in the binary mode. By binary, we
generally mean we want the ability to see, get or write to any byte in
the file. In the examples above, if we wanted to know the three digit
prefix of Bill's phone number, with both sequential and random access,
we would have to read in the whole number, and pull out the first
three digits, but with binary, we could go right to the applicable
bytes and grab just the 323 prefix.
Another common use of binary files is when we want to a machine
language (EXE, COM) file and perhaps correct or change just a few
bytes.
Also, if you have no idea what is in a file, opening it in binary lets
you look around easier and snoop the file to determine the contents of
field/record layout. Opening any of these types of files is handled
with the OPEN command. Check your QuickBASIC reference which will
expand on the use and syntax of the following examples.
OPEN "DATA.FIL" FOR INPUT AS #1
This opens a file for INPUT only. You can't write to or change the
file contents. You can then read the information one variable at a
time or one line at a time.
OPEN "DATA.FIL" FOR OUTPUT AS #1
This opens a file for output. It creates a new file and allows you to
write information into it.
OPEN "DATA.FIL" FOR APPEND AS #1
The APPEND mode does not create a new file. It allows you to add
information on to the end of an existing file.
OPEN "DATA.DBF" FOR RANDOM AS #3
This allows to to define (with the TYPE statement) the type and length
of fields you want in your data file, such as:
TYPE Information
firstName AS STRING * 15
lastName AS STRING * 20
The QBNews Page 3
Volume 3, Number 2 June 29, 1992
age AS INTEGER
salary AS SINGLE
END TYPE
The TYPE defines the fields and sets the proper size so you don't have
to keep track of the byte counts.
OPEN "FIND.EXE" FOR BINARY AS #4
Now you are telling the system to open the file and allow you to
retrieve, see or write any information from one to many bytes.
Each file has its preferred uses. Data bases, where there are many
entries such as customers, employees, or items, typically use RANDOM
files. Applications where you just need to store and retrieve a small
number of items generally use SEQUENTIAL files in the INPUT or OUTPUT
mode. BINARY files have many special uses such as overlays, graphic
image files and other specialized applications where binary (as
opposed to text or ASCII) is stored.
I hope the basic introduction will give you some insight into the
available file types and their uses. Have fun programming!
The QBNews Page 4
Volume 3, Number 2 June 29, 1992
Indexing 101 by Richard Vannoy
So here's the basics of file indexing. I'll use the file extensions
.DBF to indicate a normal RANDOM data file and .NDX to show an index
file. We'll start with a data file:
TYPE EmployeeData
lastname AS STRING * 15
firstname AS STRING * 10
employeeNumber AS INTEGER
SSN AS STRING * 11
salary AS SINGLE
hireDate AS STRING * 8
'Many more fields could (and usually do) go here for a
'typical company data base. Imafine that there are
'perhaps 60 to 80 fields using maybe 400 to 600 bytes
'each. It's important to remember, as we go along, that
'ALL of this data for all the employees is much too
'massive to fit in the memory of most machines.
END TYPE
DIM EMPL AS EmployeeData
Now let's suppose the personnel department wants us to design the data
base so they can quickly retrieve an employee's information, and they
want to be able to enter EITHER the first and last name OR the
employee number. As you could guess, if you were sitting at a
terminal all day, you would quickly find out that if you can enter
just the employee number instead of the full name, that data entry
will be much faster and more efficient. So if we are dealing with
documents that have the employee number (as most work type documents
do), then that would be quicker. But we also have to allow for the
case where the employee number is not known. So, we must also allow
the employee's name to be entered also.
So we decide to index the data base in two ways; first by employee
number:
We need a small data structure to contain just two things: the
employee number (what we're indexing on), and the record number where
that employee's information can be found. So..
TYPE EmpNumberIndex
EmpNumber AS SINGLE
RecordNumber AS SINGLE
END TYPE
DIM NUMB AS EmpNumberIndex
Then we need a small data structure for the first name/Last name
information:
TYPE EmpNameIndex
BothNames AS STRING * 26
RecordNumber AS SINGLE
END TYPE
The QBNews Page 5
Volume 3, Number 2 June 29, 1992
DIM ENAM AS EmpNameIndex
Note that the BothNames field is big enough to hold the first name, a
space, and then the last name. Also note that both EmpNumberIndex and
EmpNameIndex have the RecordNumber for where that employee is stored,
since we don't need ALL the employee info, just a pointer to where we
can find it.
We're going to open three files...
OPEN "EMPLOYEE.DBF" FOR RANDOM AS #1 LEN = LEN(EMPL)
OPEN "EMPNUMBR.NDX" FOR RANDOM AS #2 LEN = LEN(NUMB)
OPEN "EMPLNAME.NDX" FOR RANDOM AS #3 LEN = LEN(ENAM)
Let's start with three employees.
EMPLOYEE.DBF will have:
RECLast Name First Name Employee Numb. AND all the------>
-- --------- ---------- -------------- rest of the------>
1 Vannoy Richard 46 fields for------->
2 Que Suzie 32 for several------>
3 McGee Bobbie 23 hundred bytes---->
EMPNUMBR.NDX will have:
Employee Numb. REC
-------------- ---
46 1
32 2
23 3
EMPLNAME.NDX will have:
BothNames REC
--------- ---
Vannoy Richard 1
Que Suzie 2
McGee Bobbie 3
The last thing we have to do before using this data base is to sort
each .NDX file so we can use a binary search to quickly find what we
want.
EMPLNUMBR.NDX will now look like:
Employee Numb. REC
-------------- ---
23 3 (Sorted by Employee number)
32 2
46 1
EMPLNAME.NDX will now look like:
BothNames REC
--------- ---
McGee Bobbie 3 (Sorted by name)
Que Suzie 2
Vannoy Richard 1
The QBNews Page 6
Volume 3, Number 2 June 29, 1992
So far it seems like a lot of work to do all this for just three
records, but remember that we probably have hundreds, maybe even
thousands of records, so be aware that the problem of NOT having to
search thousands of records for a name OR an employee number (and BOTH
can never be in the same order in the original .DBF file) is what got
us here in the first place.
In order to understand the speed of an indexing system, it is
necessary to know how a binary search works, so we'll digress a moment
to cover it. The first requirement of a binary search is that the
records we are searching MUST BE IN ORDER, either numerically or
alphabetically, for the search to work. That's why we sorted the .NDX
files above.
Let's say we are searching the EMPLNAME.NDX file for me (Vannoy
Richard), all the records are in alphabetical order by name, there are
1000 records and my name happens to be at record 687.
First set LOW to 1 and HIGH to the number of records (1000). the
formula for our first LOOK is going to be (HIGH + LOW)\2 or 500. LOOK
at record #500. Is the name there (alphabetically) greater than or
less than MY name? Well, since I'm at 687, the name at 500 has to be
LESS. We now readjust and recompute LOOK as folows:
IF NameAt LOOK < MyName THEN
LOW = 500
ELSE
HIGH = 500
END IF
NewLOOK = (HIGH + LOW)/2
Notice, we have cut the search area IN HALF (thus the name BINARY) by
looking at the file JUST ONCE. And by refining our LOOK record to the
middle of the new search area which has just been halved, it won't
take long to find the right record. The code looks like:
found = 0
low = 1
high = numberOfRecords
DO
look = (high + low) \ 2 'Integer division
GET #3, look, ENAM
IF ENAM.BothNames < SearchingForName$ THEN
low = look
ELSEIF ENAM.BothNames > SearchingForName$ THEN
high = look
ELSE
found = 1
EXIT DO
END IF
LOOP UNTIL low = high
IF found THEN
'Process the record
ELSE
The QBNews Page 7
Volume 3, Number 2 June 29, 1992
PRINT "Record not found."
END IF
So eventually, one of two things has to happen, either the record is
found (ENAM.BothNames = SearchName$) or, if the record is NOT in the
data base, the HIGH and LOW numbers will run together (Thus the LOOP
UNTIL low = high).
Again, this may SEEM like a lot of trouble, because, say with 100
records, the best case is that the record you want is at record #50 (1
look!) but the WORST case is that it will take 7 looks, for an average
of about four looks at 100 records to find someone. (Remember that in
a sequential file, the worst case could be 100 looks for an average of
about 50 looks!)
And the beauty of binary search is that YOU CAN DOUBLE THE NUMBER OF
RECORDS AND ONLY INCREASE THE AVERAGE NUMBER OF LOOKS BY ONE!
So, to sum up the binary search routine, lets look for an employee by
number and name.
IF input was a number THEN
Binary search the EMPLNUMB.NDX for the number.
If found, go to the indicated record in EMPLOYEE.DBF and
display or process the data.
If not found, print an error message.
IF input was a string
Binary search the EMPLNAME.NDX for the name.
If found, got to the indicated record in EMPLOYEE.DBF and
display or process the data.
If not found, print an error message.
Note that the logic for a number or a string is identical, so you
write the code for a number looked up in EMPLNUMB.NDX, then make a
copy of the same code and make the minor modifications of looking for
a string in EMPLNAME.NDX.
That's how it works! Indexing may seem bulky or unnecessary at first,
but I can never emphasize enough the increase in speed you get. You
will just have to try it or see it for yourself.
The part of an indexing system that takes the most caution and care is
the maintenance of the .NDX files. Remember that whenever you add a
record, delete a record, or change the data (field) that is in an
index file, then THAT index file must be IMMEDIATELY updated with the
correct information for the system to work. It can be easy if you
take it a step at a time.
In our example, let's say you add an employee. The new employee's
data goes in the next available record of EMPLOYEE.DBF, say number
1234. His name is Jones Casey, and his employee number is 2345. Now
create a new record for EMPNUMBR.NDX
EmpNumb. Record Number
The QBNews Page 8
Volume 3, Number 2 June 29, 1992
2345 1234
Put this info at the end of the EMPNUMBR.NDX file and reverse bubble
sort it up to where it belongs. Do the same with
EmpName RecordNumber
Jones Casey 1234
in the EMPLNAME.NDX file.
All maintenance must be done on the fly (right NOW!), because the very
next operation may be to find the employee you just added, or
otherwise process the record that was just manipulated, so do it now.
That should acquaint you with the basic theory of index file creating
and use. The best way to fully understand the concepts is to set up
an indexed file and try things out. I suggest you set up a data base
with no index, then add one index, and later one other to get you
started.
**********************************************************************
Richard Vannoy started programming as a hobby back in 1975. In 1980,
he began his career as a computer programmer and teacher upon his
retirement as a naval submarine officer. His projects have included
. Writing a BASIC navigation program to assist in locating objects
of value lost on the bottom of the ocean.
. Maintaining an extensive technical manual data base on an Imsai
8080 in North Star BASIC.
. Maintaining a large government contractor accounting program on
a PDP-11 with Fortran.
. Various contracting/consulting jobs programming with dBASE III+.
Richard presently resides in Portland, Oregon where he writes software
for local businesses in QuickBASIC.
**********************************************************************
The QBNews Page 9
Volume 3, Number 2 June 29, 1992
Hashing - It isn't all Corned Beef by Dave Cleary and Mike Avery
A few months ago, Mike Avery posted some code on the QUIK_BAS echo
concerning Hashing. I thought this was pretty interesting, so I'm
including his code in The QBNews and decided to write a little
introductory to it.
Hashing is an algorithm where you take a record key, and compute a
file address or record number from it. This approach alows you to keep
file accesses down to a minimum since there is no index file to worry
about. You take your key, perform some mathamatical functions on it,
and bingo, you have your record number of your data.
The first step in hashing is to compute a hash function. An ideal hash
function would compute a different number for an infinite amount of
different keys. However, if you were to come up with a perfect hash
function, you would probably need to use a supercomputer to get decent
performance. So after coming up with a hash function, you need to come
up with some sort of collision resolution for the times when two
different keys hash out to the same record number.
The collision resolution Mike chose to implement was adding a power of
two to the result of the hash function. Another method would be to use
linked lists, or store multiple records per computed hash number. An
example of having multiple records would be this. Say you have 128
byte records. A disk access of 4096 bytes is an efficient number to
use. Therefore, you could have up to 32 (4096 \ 128) records per hash
number.
The code Mike presents is well commented and works. I encourage the
reader to take some time and play with it. For more information on
hashing, I suggest you look at some books of basic algorithms. They
all should contain a section on hashing.
Code for this article appears in HASH.ZIP
The QBNews Page 10
Volume 3, Number 2 June 29, 1992
Create dBASE III Data Files from QuickBASIC by Dennis Gellert
With the need for a QuickBASIC routine which was able to create a
dBASE III data file, and encouraged by David Perrys article in
Volume 1, no 5, I set to work.
This routine is quite sufficient if there is a need to export data
from your program in dBASE III format. However, a full database
application would almost certainly require, in addition,
Index file management routines.
Armed with the information from the above article, the dBASE III
manual, and PC Tools, I set to work. The dBASE manual actually
specifies the header and data structure fairly well, so you're not
left in the dark when working with the dBASE III file structure.
dBASE refuses to open a data file if it detects there is anything
amiss with the file structure, so you have to be careful to get
everything right. I used two checks along the development path:
firstly, that dBASE would actually open the file, and secondly,
to compare contents of this file, byte-by-byte, with the contents
of an identically structured file created with dBASE III itself.
As it stands, the program is an example which creates a dBASE data
file, and writes one record. As the file structure is embedded in
the program, it will be necessary to modify the source code to suit
your needs. The dBASE file structure specification is contained in
data statements, so this is quite easy to change. The structure of
the data written to the file will also need to be looked at.
(Commented in the source code).
Notes on Specifying a dBASE III Structure
-----------------------------------------
When creating a new dBASE data file, you must specify its data
structure. This consists of supplying the Name, Data type,
Total length (including decimal point, if used), and Decimal
places for each field.
Field Names:
- Field names must be in upper-case, or dBASE will not be able to
access this field. This program uses UCASE$ to ensure this.
- Field names must not be repeated. This program does not check
this. If there is a repeated field name, dBASE will not be able
to access the repeated field.
- Field names begin with a Letter, and may contain letters, digits,
or underscores.
- Field name length, by convention, may be from 1 to 10 characters.
(but 11 bytes are reserved for this ?).
Field Types and lengths:
The field type is specified by an upper-case character. The
maximum lengths for each of the types is shown:
C - Character 254
The QBNews Page 11
Volume 3, Number 2 June 29, 1992
N - Numeric 19
L - Logical 1
D - Date 8 (set length)
M - Memo 10 (set length of pointer)
Code for this article appears in QB-DB3.ZIP
The QBNews Page 12
Volume 3, Number 2 June 29, 1992
Adding LANtastic Support to your Programs by Chip Morrow
Ever thought it would be nice to throw some rudimentary LANtastic function
calls into your QB programs? This set of 8 routines makes it possible to:
- Determine if LANtastic netbios is present
- Cancel redirection of a device
- Determine LANtastic machine name currently in use
- Get a list of redirected device entries
- Get a list of servers that you're not currently logged into
- Get a list of servers that you ARE currently logged into
- Determine LANtastic version currently in use
- Redirect a device
The DECLARE block used in the sample program LANSTAT:
DECLARE FUNCTION NetBios% () 'Is netbios present?
DECLARE FUNCTION NetCancel% (DevName$) 'Cancel redirection
DECLARE FUNCTION NetName% (Machine$) 'Get machine name
DECLARE SUB GetDevice (DeviceNum%, DevName$, NetPath$) 'Get device entry(s)
DECLARE SUB Inactive (EntryNum%, Returned$) 'List available servers
DECLARE SUB LoggedIn (EntryNum%, LogName$) 'List active servers
DECLARE SUB NetVersion (Major%, Minor%) 'Get LANtastic version
DECLARE SUB Redirect (DevType%, DevName$, NetPath$) 'Redirect a device
DECLARE SUB Strip (A$, B$) 'Used by these routines
NetBios - Returns 0 if netbios not installed, or -1 if installed.
NetCancel - Cancel redirection of an already-active device. Pass the
device name to this routine in the form "c:", "d:", "prn",
"lpt1:", etc. NetCancel% will return zero if successful,
or an error code otherwise.
NetName - Determine machine name currently in use.
GetDevice - Get a redirected device entry. Pass a value (EntryNum%) as the
index number to check, beginning at zero. DevName$ and NetPath$
are returned with this entry's information. If nothing found
for EntryNum%, DevName$ returns "".
Inactive - Get the name of a server that you're not currently logged into.
Pass EntryNum% as the index number to check, beginning at zero.
Returned$ will be the name of the inactive server, or "" if
nothing found.
LoggedIn - The reverse of Inactive. Pass EntryNum% in the same manner,
and the returned string will be the active server name, or
"" if nothing found.
NetVersion - Determine LANTastic version currently in use. Major% and
Minor% will be returned values. Version 2.57 of the NOS
would be Major = 2, Minor = 57.
Redirect - Redirect a device to an active server's path. Works similar
The QBNews Page 13
Volume 3, Number 2 June 29, 1992
to LANtastic's NET USE command. Passed values (from the
DECLARE statement above:
DevType% = 3 if a printer device, 4 for disk. No other
values accepted.
DevName$ = Your local device name. "C:", "D:", "LPT1:", etc.
NetPath$ = Server's network path to use, in the format
\\server_name\pathname
DevType% returns zero if successful, or an error code otherwise.
Strip - Rudimentary method of converting fixed-length strings to
variable length (there's probably a better way). Used by
most of the above routines in order to pass QB-standard
variable-length strings back to you.
Last thing to note here is that I've used these routines only with
version 2.57 of the NOS. I don't see why they wouldn't work with
other versions of LANTastic, but wanted to make you aware.
Happy INTERRUPT'ing!
Code for this article appears in LATASTIC.ZIP.
The QBNews Page 14
Volume 3, Number 2 June 29, 1992
A powerful line editor for QB by Larry Stone
I use QB's INPUT statement to quickly test code. However, I find that
its limitation of 255 characters, the fact that it will automatically
wrap at the eightieth column, its inability to pre-size a window or
work within a virtual window, it's complete lack of character masking,
and the fact that the floating point library is loaded with it, when-
ever it is used, whether needed or not, leaves the QB INPUT statement
well short of a desirable routine to use with a "finished" program.
Although I have, and use, an effective substitute routine written in
ASM, the idea of a pure QB routine remained a fancy for quite a while.
The opportunity to do a pure QB routine came as I was trying to come
up with a project for the class I taught at the local community col-
lege. I teach (part-time) "Introduction to Modular Programming" and
the compiler we use to teach modular programming is, you guessed it,
QB. The project began in the Fall term - a simple line editor to use
in lieu of QB's INPUT statement. My Winter term students were required
to expand its capabilities to include virtual windowing and automatic
exiting when the end of the input line is reached (an optional feature
used with field entries such as, zip codes and telephone numbers).
The result of our effort (well - mostly my effort) is LINEEDIT.BAS, an
extremely effective line editor ready to include with your code. With
LINEEDIT.BAS are two other highly useful modules, KEYBOARD.BAS and
VIDTOOLS.BAS (These two routines have been abbreviated for brevity).
The KEYBOARD module must be loaded with LINEEDIT. The VIDTOOLS module
is not necessary but is included because the test program, EDITDEMO,
makes calls to two of its routines. The main module, EDITDEMO.BAS is
a test vehicle for the editor and illustrates how to setup parameters
needed to call the editor.
LineEdit has twelve parameters in its parameter list. Row%, Col%, A$,
and DisplayLen% are required. You must tell LineEdit what row to work
on and what column to display the string from. You must also supply a
string to edit (A$), and define the length of the display (better than
QB's automatic, 255 character length that is guaranteed to wreck havoc
on your nicely designed screen form).
Let's review some of LineEdit's other parameters - the ones that turn
a simple line editor into a power-packed routine.
VwindowSize%, if defined as less than the string's DisplayLen, has no
effect on LineEdit. However, if it is a larger number than DisplayLen
then it defines a virtual window. Let's say that DisplayLen is defined
for 60 characters. If VwindowSize% is smaller then the maximum string
size allowed is 60 characters. But, if we define VwindowSize% as 300
characters then we can now edit a string up to 300 characters within a
60 character window. The displayed string will simply slide left or
right 10 characters at a time, when we reach the end of the display.
Separaters$, when non-null, tells LineEdit what characters are word
separaters for jumping the cursor between words (use Ctrl plus arrow).
The QBNews Page 15
Volume 3, Number 2 June 29, 1992
An effective Separaters$ string might be, " .:-()\/".
Terminators%() MUST BE DIMmed by your controlling program. It is an
array defining what key presses to exit the editor. Escape, up & down
arrows, PgUp and PgDn, are examples of terminator keys. The zeroeth
element of the array informs LineEdit what the last terminator is.This
allows extreme flexibility. Let's say you have defined 7 terminators,
Escape, Up, Down, PgUp, PgDn, Ctrl + PgUp, and Ctrl + PgDn. Now let's
say that your first screen contains a single edit item - all you need
is the escape key. You LET Terminators%(0) = 1. Your first terminator
is escape and that is now the only terminator that LineEdit will use.
Your next screen has five edit items. You now need to use up, down,
PgUp and, PgDn so that your user can navigate between the strings. You
LET Terminators%(0) = 5. If you have multiple screens of edit strings
you would LET Terminators%(0) = 7 so that Ctrl + PgUp sends the cursor
to the first string of the first screen and Ctrl + PgDn goes to the
end string on the last screen.
EditMask$ when null has no effect. However when filled with character
A's, tells LineEdit to force every key stroke to upper case. If the
EditMask$ looked like, "Aaaaa" then LineEdit would force the 1st char
to upper case and the next four characters to lower case.
CurPos% and CurOffset% are input/output parameters. They can be used
to send the cursor to the same position it was in or, set them to zero
for fresh starts at the beginning of the edit string.
Kee% is a value that defines what keystroke terminated LineEdit. You
would treat Kee = 13 (enter key) differently than Kee = 27 (escape).
While we are on the subject of Kee%, LineEdit uses a routine from KEY-
BOARD.BAS to handle all keyboard activity. The routine, KeyPressed%,
returns extended keystrokes as negative numbers. In other words, down
is returned as the value -80 (not CHR$(0) + CHR$(80) as QB does). This
means that you must define them this way for you Terminators%() array.
Below is a listing of LineEdit's built-in capabilities:
Backspace Deletes character to left of cursor
Delete Deletes character under cursor
Ctrl + Home Deletes from cursor to beginning of line
Ctrl + End Deletes from cursor to end of line
Ctrl + Right Move to word on right (skips separaters)
Ctrl + Left Move to word on left (skips separaters)
Home Move to beginning of string
End Move to space after last char of string
Right Move cursor one character to right
The LineEdit code is self-explanatory. However, one section of code
deals with word seperaters and is constructed a little different.
StepValue is set to -1 when the user presses Ctrl + Left and StepValue
is set to 1 when the user presses Ctrl + Right. The code reads:
The QBNews Page 16
Volume 3, Number 2 June 29, 1992
IF StepValue < False THEN X = 1 ELSE X = LEN(A$)
FOR N = StrPos TO X STEP StepValue
'---- Look into A$, one character at a time - is it a seperater?
J = INSTR(Separaters$, MID$(A$, N, 1))
IF J THEN 'Found a separater character
FoundSeparater = J 'Save J's value
'---- Move our cursor to this separater position
FOR i = StrPos TO N STEP StepValue
IF StepValue < False THEN GOSUB CursorLeft ELSE _
GOSUB CursorRight
NEXT
EXIT FOR 'Cursor is on a separater so exit the loop
END IF
NEXT
This loop simply steps through the string, one char at a time, then
looks into Separaters$ to see if this one character has been defined
as a word separater. If it is a separater, its location in the sepa-
rater string is saved and the cursor is moved to that character in the
string being edited.
'---- If a separater was found, skip any repeating sequences of it.
DO WHILE J 'Loop while Separater has been found
N = N + StepValue 'Increment or Decrement N
IF N = False THEN EXIT DO 'Prevent error with MID$() function
'---- Only looking for repeating sequences of FoundSeparater
J = INSTR(MID$(Separaters$, FoundSeparater, 1), MID$(A$, N, 1))
IF J THEN 'If we found another seperater
IF StepValue < False THEN GOSUB CursorLeft ELSE _
GOSUB CursorRight
END IF
IF N >= LEN(A$) THEN EXIT DO
LOOP
This next loop simply looks to see if the found separater character is
repeated. While it is repeated, the cursor is moved left or right.
Once past the found separater, the loop is exited. In other words, if
you have defined the space character (ASCII 32) as a separater, and
the string has a word followed by ten spaces followed by another word,
LineEdit will jump across all ten spaces with a Ctrl + left or right
arrow keys (a slick little trick).
To see how to use LineEdit, load and run its test program, EDITDEMO.
Final note. LineEdit has no code for the Tab key. I leave that to you
to encode into the routine.
The QBNews Page 17
Volume 3, Number 2 June 29, 1992
Code for this article appears in LINEDIT.ZIP.
The QBNews Page 18