home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Zodiac Super OZ
/
MEDIADEPOT.ISO
/
FILES
/
13
/
N_B_V203.ZIP
/
TINPUT.TXT
< prev
next >
Wrap
Text File
|
1996-07-04
|
26KB
|
507 lines
$if 0
┌──────────────────────────╖ PowerBASIC v3.20
┌──┤ DASoft ╟──────────────────────┬──────────────────╖
│ ├──────────────────────────╢ Copyright 1995 │ DATE: 1995-10-01 ╟─╖
│ │ FILE NAME TINPUT .TXT ║ by ╘════════════════─ ║ ║
│ │ ║ Don Schullian, Jr. ║ ║
│ ╘══════════════════════════╝ ║ ║
│ A license is hereby granted to the holder to use this source code in ║ ║
│ any program, commercial or otherwise, without receiving the express ║ ║
│ permission of the copyright holder and without paying any royalties, ║ ║
│ as long as this code is not distributed in any compilable format. ║ ║
│ IE: source code files, PowerBASIC Unit files, and printed listings ║ ║
╘═╤═════════════════════════════════════════════════════════════════════╝ ║
│ .................................... ║
╘═══════════════════════════════════════════════════════════════════════╝
$endif
┌──────────────────────────────────────────────────────────────────┐
│ fTinput$ and fTinputM$ both use the same TINPUT_?.INC files. │
└──────────────────────────────────────────────────────────────────┘
Before you get into all of this: The info in this file is vital but,
despite the length of the file it is NOT difficult to understand if
you don't try to make it into something more than it is. The concept
was taught to me by my guru in about 30 seconds on the margin of the
daily news paper! So, if I can grasp it that fast, nuff said! d;D
A program is not worth much if it cannot input data and instructions
from the user; how this information and data is input is one of the
most important features of any program. It should be as easy and as
fool-proof as possible. Your job, as programmer, is to make all this
happen.
As there are several different types of data one all-purpose routine
just doesn't make the cut! On the other hand, to attempt to have an
individual routine for each style is just too much to handle. I have
come up with a happy medium: a suite of routines that allows you to
call a single function from within your program. It calls a specific
handler for the data style required. Sorta' like one stop shopping!
There are 9 field styles. Eight of them provide specific support and
the ninth is a general, all-purpose field that catches everything
else. The fields are controlled by a string of data that you send.
The string is loaded into a TYPE by the function before it is passed
to the specific handler.
As you can see the first 8 bytes of the control string fit into the
TYPE but from position nine and on you can also send more data for
some of the styles that require it or can use it. This area of the
string is known an the MASK. Also, not all the members of the TYPE
are used by all the styles and .Misc is a kind of catch-all member.
Despite their comings and goings, the members hold their purpose in
life throughout. Below is a generalized description for each member
then a style by style discussion.
┌──────────────────────────────────────────────────────────────────┐
│ One important thing to remember is that fTinput assumes that the │
│ incoming data is IN THE CORRECT FORMAT for the designated Style. │
└──────────────────────────────────────────────────────────────────┘
TYPE TinputTYPE '┌───────────────────────────────────
Style AS STRING * 1 '│ field type ie: A,B,D,H,M,N,P,Q,T
Row AS BYTE '│ screen row
Col AS BYTE '│ left most screen column
Cols AS BYTE '│ N° of visible columns
MustBe AS BYTE '│ > 0 if field is mandatory
Cased AS BYTE '│ 0 = none 1 = UCASEed 2 = LCASEed
Misc AS BYTE '│ max length of string => Cols
Just AS BYTE '│ N° of routine to call fJUSTIFY$ with
END TYPE '└────────────────────────┤ 9 bytes ├───
.Style always indicates which field driver to use
.Row always the starting screen row for the field
.Col always the left-most column(s) for the field
.Cols always the maximum visible columns of the field
in most cases this is also the maximum length of the field
.MustBe if > 0 then the field is mandatory
this field is used differently by each style
.Cased if 0 then no casing is done to the input
if 1 then all input is UCASEd using UCASEstr
if 2 then all input is LCASEd using LCASEstr
.Misc used differently by each style
.Just an extra byte for your use as it is NOT used by fTinput$
──────────────────────────────────────────────────────────────────────────
ANYTHING GOES
──────────────────────────────────────────────────────────────────────────
Style = "A" Anything goes
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of visible columns
MustBe = 0 or x > 0 if field is mandatory
Cased = 0, 1 or 2 0 = NOCASING 1 = UCASEed 2 = LCASEed
Misc = x maximum field length if => Cols
Just = x NOT USED
Mask$ = if NULL any characters accepted
ELSE only chars in Mask$ eg: " 0123456789-"
DATA IN : any string value from NULL to .Misc in length
DATA OUT: a string of up to .Col or .Misc length
As you can see this field can and will scroll through a long string of
data. By using Mask$ you can restrict the keyboard input to only a few
keys. ie: "0123456789-() " works well for phone numbers
Another property of Alpha is that it will automatically issue a CHR$(13)
if .Cols = 1 and an acceptable key is input. This allows you to use ALPHA
for yes/no answers and the like, but QUERY does a better job of it.
──────────────────────────────────────────────────────────────────────────
BLOCK O' TEXT
──────────────────────────────────────────────────────────────────────────
Style = "B" Block of text
Row = 1 -> x top most screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of columns
MustBe = NOT USED 0 is assumed
Cased = NOT USED 0 is assumed
Misc = x maximum number of rows of .Cols chars
Just = NOT USED
Mask$ = NOT USED
DATA IN : any string of text
DATA OUT: a string of text
A simple word-wrap is employed SEE: fWrapPosF%
<UP> and <DOWN> keys are used and <ENTER> acts as <DOWN>
fTinput$ can return <UP> and <DOWN> as defaults from this function
──────────────────────────────────────────────────────────────────────────
DATES
──────────────────────────────────────────────────────────────────────────
Style = "D" Dates
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 8 or 10 8 is default ( + LEN(day$) )
MustBe = 0 or x > 0 if field is mandatory
Cased = NOT USED
Misc = NOT USED
Just = NOT USED
Mask$ = "Sun |Mon |Tue |Wed |Thr |Fri |Sat "
NOTE: if Mask$ is used
* all day names need be the same length
* .Cols must include this length
* D$ is _NOT_ effected
The data field looks like "Mon 01-01-1995"
and the user only inputs the date not the
day.
DATA IN : an 8 or 10 character date string in SetDateFormat style
DATA OUT: same as data in
If .MustBe > 0 and incoming D$ is NULL then the system's date is used
ELSE
Before the field exits the date entered must be a correct and legal date
or a NULL field will exit with a value of SPACE$(.Cols) and display.
The grey "*" resets the date to the current date
"-" - 1 day (yesterday) ' these keys come in handy, along
<CTRL>"-" - 28 days ( 4 weeks) ' with the day of the week for
<ALT> "-" -364 days (52 weeks) ' businesses that need to set-up
"+" + 1 day (yesterday) ' appointments, meetings, etc.
<CTRL>"+" + 28 days ( 4 weeks) '
<ALT> "+" +364 days (52 weeks) '
───────────────────────────────────────────────────────────────────────────
HEX & DECIMAL ASCII VALUES
───────────────────────────────────────────────────────────────────────────
Style = "H" Hexadecimal & decimal
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of visible columns
MustBe = 0 or x > 0 if field is mandatory
Cased = NOT USED
Misc = x max number of chars to be edited
Just = NOT USED
Mask$ = NOT USED
DATA IN : any string eg: "HELLO" user sees: "48h,45h,4Ch,4Ch,4Fh"
DATA OUT: ditto or: "072,069,076,076,079"
DATA IN : is a simple string "ABC" but is displayed as "41h,42h,43h"
during editing. The user also has the use of the <SPACE> key to
switch between HEX and DECimal display where "ABC" would become
"065,066,067". During editing both HEX and DECimal are accepted
and converted afterwards. Before exiting the whole mess is
cleaned up and values > 255 are rejected.
DATA OUT: "ABC" format
The total length of the field is ( ( .Misc * 4 ) - 1 ) so .Misc
is only the individual character count and not the total space
required to show each character in HEX and/or DECimal format
──────────────────────────────────────────────────────────────────────────
MASKED
──────────────────────────────────────────────────────────────────────────
Style = "M" Masked input
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of visible columns (Not the mask length)
MustBe = 0 or x > 0 if field is mandatory
Cased = 1 1 = UCASEed input only
Misc = NOT USED
Just = NOT USED
Mask$ = see below eg: "SS_A_N: NNN-NN-NNNN"
DATA IN: a correct data string in the prescribed format or NULL
DATA OUT: a correct data string or NULL if .MustBe = 0
Input is strictly controlled by use of an encoded Mask$.
There are only six character types:
N = mandatory (0123456789)
n = optional (0123456789) and <SPACE>
A = mandatory (A -> Z)
a = optional (A -> Z) and <SPACE>
E = mandatory > <SPACE>
e = optional anything goes!
_ preceding N,n,A,a,E, or e prints the letter
__ prints a single underline character
Mask$, as shown above, would present "SSAN: - - " to the user. As
the numbers are filled in the cursor would automatically skip the "-"'s
allowing for rapid, trouble free formatted input. The cursor can be moved
around the field but will only land on character positions requiring
input. Individual character deletion can also be done but, once again,
only on characters in the "active" positions.
Mask$ "N-NN-NNNNNN-E" would work will for ISBNs where the last position
"E" could be either a number or the letter "X".
Mask$ "Nn-Nn-NNnn" could be used for dates but a poor second to the
individual date style
──────────────────────────────────────────────────────────────────────────
NUMERIC
──────────────────────────────────────────────────────────────────────────
Style = "N" Numbers (as values not strings)
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x length of Mask$
MustBe = 0 or x > 0 if field is mandatory
Cased = NOT USED
Misc = NOT USED
Just = NOT USED
Mask$ = "+#,###.#" DO NOT USE "+####,.#" format!
DATA IN: incoming data is passed through VAL() to determine it's
numeric value (if any)
DATA OUT: a string suitable for conversion with VAL()
Once again what is input is governed totally by Mask$. As shown above the
value input can be from -9,999.99 to +9,999.99. As you can see Mask$ must
be a valid format string suitable for use with USING$. If there is a
leading "+" then both positive and negative numbers will be accepted else
only positive numbers can be accepted. There seems to be a slight bug in
v3.1 that incorrectly converts "####,.##" style masks when there are two
or more commas involved in the output so use "#,###.##" just to be safe.
Input for this field emulates that of a calculator; where the number
"grows" from right to left and only the right most character can be
erased using <DEL> or <BKSPC>. <CTRL><DEL> ZERO's the field's value.
user presses "1" sees: 1
user presses "2" sees: 12
user presses "3" sees: 123
user presses "4" sees: 1234
───────────────────────────────────────────────────────────────────────────
PATH
───────────────────────────────────────────────────────────────────────────
Style = "P" DOS Path's, file names, etc.
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of visible columns
MustBe = 0 or x > 0 if field is mandatory
Cased = 2 2 = LCASEed (UCASE is assumed)
Misc = x maximum field length => Cols
Just = NOT USED
Mask$ = ":\.?*" any or all of these chars but NO OTHERS please
any character can be made available however
DATA IN : any legal DOS string value from NULL to .Misc in length
This field is, in fact, only a sub-set of the Alpha field. It adds to
the Mask$ you send all the legal DOS characters except the 5 listed
above. As you can see these 5 control the field's ability to input
drive and/or path information and/or wild cards. It also defaults to
upper case conversion unless you send an explicit value of 2 for .Cased
Some testing is done on the input string in an attempt to forestall user
mistakes but it's not perfect as no one can tell just what a user will do
when given a chance ;)
──────────────────────────────────────────────────────────────────────────
QUERY
──────────────────────────────────────────────────────────────────────────
Style = "Q" Query or Questions
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 1 -> x N° of visible columns
MustBe = 0 or x > 0 if field is mandatory
Cased = NOT USED UCASE only
Misc = x maximum field length => Cols
Just = NOT USED
Mask$ = see below "MFN?|Male|Female|Not Disclosed|(Male,Female,None)"
DATA IN : a single character
if NULL then the left most character in Mask$ is assumed
DATA OUT: one of the characters in the 1st field of Mask$ (except "?")
This field acts as a mini-menu. Mask$ is cut into several pieces by the
pipe "|" character. The first (left most) field are the legal selections
with their long versions following in order. As the long versions are what
the user sees .Cols must always represent the longest of the set.
If the question mark "?" is used it must be the last selection and will
cause the final field to be displayed in the event the user presses an
illegal key. It can not be returned as an answer.
Using Mask$ as we have it above:
The user may press "M", "F", or "N" to get an acceptable answer and
immediate exit from the field. Assuming the "M" is pressed the string
"Male" will be printed on the screen. If, however, the "?" were pressed
then "(Male, Female or None)" would be displayed for 2 seconds and would
either exit upon a valid key or revert back to the original value if the
time elapsed first.
NOTE: an approximation of this process can be duplicated by using the
Alpha field with .Cols = 1
.Cased = 1
Mask$ = "MFN"
HINT: if you want your answers printed with color commands (like the
first letter blue and the rest grey) use a color attribute value
of ZERO for the normal attribute then embed the color command
in the answer pieces
───────────────────────────────────────────────────────────────────────────
TIMES
───────────────────────────────────────────────────────────────────────────
Style = "T" Times
Row = 1 -> x screen row
Col = 1 -> x left most screen column
Cols = 6 or 9 6 is default
MustBe = 0 or x > 0 if field is mandatory
Cased = NOT USED
Misc = NOT USED
Just = NOT USED
Mask$ = NOT USED
DATA IN: a 6 or 9 char time string eg: "HH:MM " or "HH:MM:SSx"
DATA OUT: same format as data in SEE: SetTimeFormat
if .MustBe > 0 and incoming D$ is NULL then the system's time will
be automatically loaded
input style is dependent upon the values in SetTimeFormat
NOTE: times are ALWAYS edited as 24 hour clocks avoiding the "a" and "p"
───────────────────────────────────────────────────────────────────────────
──── ────
───────────────────────────────────────────────────────────────────────────
fTinput$ ( {D$}, Control$, ExitKeys$, Nattr?, Hattr? )
fTinputM$ ( {D$}, Control$, ExitKeys$, Nattr?, Hattr?, HotSpot% )
D$ is both the incoming and exiting data
if NULL then incoming data has failed some internal test
or has a value of ZERO for "N"umbers
Control$ is a construct of ( TinputTYPE + Mask$ )
just a quick word here just in case it isn't quite clear what
we are looking for:
TinputTYPE is used to convert the first 8 characters of
Control$ into the separate members while Mask$ is actually
cut out of the middle. LSET tINP = LEFT$( Control$, 8 )
Mask$ = MID$( Control$, 9 )
The reason this is done is to simplify storage of control
strings in data files. You can, of course, use the TYPE in
your code too.
ExitKeys$ has 2 default keys loaded by the function:
CHR$(13) <ENTER> = acceptance of the data
CHR$(27) <ESC> = disregard changes/restore original value
You may add any other key(s) you wish.
F-1 and F-10 would be ExitKeys$ = CHR$(0,59,0,68)
Nattr? is the color attribute to use upon exiting the field
Hattr? is the color attribute to use while the field is being edited
HotSpot% the item number for the field being edited
fTinput$ RETURNS CHR$(?) exiting key-press
fTinputM$ RETURNS CHR$(?) exiting key-press
NULL mouse clicked in another item of current event
CHR$(255) mouse clicked in another event
───────────────────────────────────────────────────────────────────────────
The following keys are default unless also found in ExitKeys$
<BkSpc> delete character to the left of the cursor
<HOME> cursor to #1 character position
<LEFT> move cursor 1 position left
<RIGHT> move cursor 1 position right
<END> cursor to end of data or last character position
<INS> toggle insert ON/OFF SEE: fINSERTmsg%
<DEL> delete character under the cursor
<CTRL><LEFT> next word left
<CTRL><RIGHT> next word right
<CTRL><DEL> erase all data in the field
<ENTER> exit field, accept new data
<ESC> exit field, keep original data
NOTE: some of these keys have no effect on some of the field styles.
( Logic dictates here. ie: CTRL/LEFT and Numerics isn't a match )
If a key cannot be used by any field style it is simply ignored.
Even though each field can handle incoming NULL values for the data I've
found it best to supply whatever defaults your program may require. Even
on the odd chance that the style's idea of a default value is the same as
the program's there is no guarantee that the user will pass through that
particular field. This would leave your data "out of shape" and may cause
problems in other portions of the program. It is much safer to <KNOW> the
data is in the proper format throughout the program than to be constantly
checking it. ( Saves on the Raid bill, too... :)
════════════════════════════════════════════════════════════════════════════
═════ putting it all together ═════
════════════════════════════════════════════════════════════════════════════
So far I've only been talking about single fields and that, no matter how
good it is, is only the tip of the iceberg. Greater things are afoot here
and can now be addressed.
More times than not there is more that one field of data that needs to
be input. One medical program I built years ago had over 2,500 fields for
each record spread over 40 different screens, and without a consolidated
screen input routine I would still be writing code for them all. Enter
the arrays! Now no matter how many fields your screens may have, you can
simply pack the data and control strings for each field into arrays and
put the whole process into a single loop. By loading ExitKeys$ with <UP>,
<DOWN>, <F-10>, etc. you can easily control movement within the array.
Then (and you thought I forgot) by using the .Just member of the TYPE
your loop can be made to perform field specific checking, rejection and
message warnings, or any one of a dozen other tasks quite simply!
SEE: fJustify$
As it is a very real possibility that not all programs will require all
the field styles, each style is split into a separate .INC file.
TINPUT.UNT has 9 integer constants at the beginning of the file that will
either include or exclude each of the TINPUT_?.INC files. This allows you
to keep your programs down to their proper size without a batch of useless
code laying around.
The following files are required:
TINPUT .TYP ' the type declaration
TINPUT .UNT ' fTinput$ and all the supporting routines
TINPUTM .UNT ' fTinputM$ and it's supporting routines (Mouse Aware)
TINPUT_A.INC ' Alpha input
TINPUT_B.INC ' Blocks
TINPUT_D.INC ' Dates ( requires TINPUT_M.INC )
TINPUT_H.INC ' Hex & Decimal ( requires TINPUT_A.INC )
TINPUT_M.INC ' Masked
TINPUT_N.INC ' Numbers
TINPUT_P.INC ' Path ( requires TINPUT_A.INC )
TINPUT_Q.INC ' Query
TINPUT_T.INC ' Times ( requires TINPUT_M.INC )
fTinput2$ ( SEG D$(), SEG F$(), SEG T?(), SEG H$(), SEG Fld% )
D$() all the incoming/outgoing data for the fields
F$() all the control strings
T?() if UBOUND(T?(1)) > 0 then the field numbers to be tabbed to
the field numbers must be in ascending order
H$() if UBOUND(H$(1)) > 0 then the help strings are printed by
calling fHelpLine$
Fld% the field to start processing at
This routine is ready to be input directly into your programs, but you
will probably want to modify it to fit your exact needs. The "tab fields"
is a handy gizmo if your program uses some monster input screens
but really does very little on the small stuff and would just be a
nuisance. Rip it out! Ditto with the help line. I like it (obviously)
and use it constantly but.....
There is no reason that you couldn't compile it and stuff it in the
.PBL but I don't think I've every written 2 programs that were enough
alike to do that and that is why it's an .INC file.
The last little part is the use of fJustify$. The individual field
routines are pretty smart to start with and you may find that fJustify$
just gets in the way. No problemo... <CTRL>Y is at your beck n' call!
In TINPUT.DMO I've hung the whole thing together so you could see it in
action, and play with it.
fJustify$ is also, finally, put to some practical use in this file. It
really doesn't have a lot of use in the demo but I'm sure you will get
the general idea. It is not inconceivable that you could construct yet
another function ( fPreJust$ ) to load default values! Two old saws come
to mind:
"Never write the same code twice."
"There are exceptions to every rule."
Last minute entry:
fQueryPrint$ ( Row?, Col?, Cols?, Mask$, Answer$ )
This one was added as a good afterthought to all of this. If you
remember the Query field you know that the incoming/outgoing data is
a single letter that is found in the first piece of Mask$ and that the
first answer is default if Answer$ is NULL.
Mask$ = "YNM|Yes|No|Maybe"
fQueryPrint$ will return the correct piece of Mask$ and if Row? > ZERO
will use TprintCLEAR with an attribute of ZERO to print the string.
While writing a small, small program for a local business I just got
tired of doing the 2 or 3 lines of code necessary to make all this
happen, so.......
See you in TINPUT.DMO!