home *** CD-ROM | disk | FTP | other *** search
- 5 FOCUS: BOXGET(), a Generic GET Function
- By Greg Lief
-
-
- Introduction
-
- History has proven that I respond very well to challenges. This
- function resulted from such a challenge. Matthew Amis posted a public
- message on the Aquarium BBS regarding a function that he was working
- on. His function would draw a box, display a prompt, and GET a
- variable. He was coming up against some kind of obstacle, and
- was soliciting opinions (for which the Aquarium Message Center is
- perfectly suited).
-
- Silly me... I volunteered to write it for him. Guess what, folks? It
- is a bit trickier than it sounds! I'll show you why after we cover
- the basic syntax.
-
-
- Syntax
-
- BOXGET <var> PROMPT <prompt> [ PICTURE <pict> ] [ VALID <valid> ] ;
- [ BOXCOLOR <boxcolor> ] [ COLOR <color> ] [ ROW <row> ] ;
- [ COLUMN <column> ] [NORESTORE] [RESTOREALL] [DOUBLE]
-
-
- Required Parameters
-
- <var> is the name of the variable to be gotten.
-
- <prompt> is a character expression representing the prompt to be
- displayed.
-
-
- Optional Parameters
-
- <pict> is a character expression representing the PICTURE clause to use
- for the GET variable.
-
- <valid> is the VALID clause to use for the GET variable.
-
- <boxcolor> is a character expression representing the color in which to
- draw the box. If you do not specify this, the current color will be used.
-
- <color> is a character expression representing the GET color. You must
- specify this in the format "<standard>,<enhanced>", where <enhanced>
- will be used when the GET is highlighted. If you do not specify this,
- the current standard and enhanced color settings will be used.
-
- <row> and <column> are the top row and left column at which to display
- the box. If you do not specify these parameters, BOXGET() will
- automatically center the box horizontally and/or vertically on the
- screen.
-
- <title> is a character expression. If you specify this, it will be
- centered on the top row of the box.
-
- The NORESTORE clause directs BOXGET() not to automatically restore
- the underlying screen upon exit. This can be used primarily to leave
- a series of GETs on the screen. If you specify this clause, the contents
- of the underlying screen will be added to a stack for later restoration
- with the RESTOREALL clause.
-
- The RESTOREALL clause will restore all screen fragments stored in the
- aforementioned window stack.
-
- The DOUBLE clause will cause a double-line box to be drawn. By default,
- the box is single-line.
-
-
- Return Value
-
- BOXGET() does not return a value, because it manipulates <var> directly.
-
-
- Methodology
-
- The first thing you may wonder is how a function such as BOXGET() can
- operate on a variable that is declared local in a higher-level module
- (especially when you consider that BOXGET() returns no value). That's
- a good question, and here's a great answer: It works through the magic
- of code blocks.
-
- Look carefully at the user-defined command syntax in the adjoining
- source code file. A GET object is created with the GETNEW() function.
- GETNEW() requires a retrieval code block for the GET variable in
- question. Following is a sample retrieval block for the variable
- MNAME:
-
- { | x | if(pcount() = 0, mname, mname := x }
-
- This retrieval code block accepts one optional argument. If the
- argument is passed to the code block, the value of the argument is
- assigned to the GET variable. That is precisely what happens after you
- complete a GET.
-
- The important thing to remember is that in 5.0, GET variables are
- never manipulated directly -- rather, they are only manipulated
- through their corresponding retrieval code block.
-
-
- Housekeeping
-
- BOXGET() handles all necessary cleanup, including cursor size and
- position, screen contents, color, and SCOREBOARD status (which I
- thoroughly despise). If you specify the NORESTORE clause, the screen
- contents under each box are saved to a dynamically resizable array
- with the following statement:
-
- aadd(boxstack_, { TOP, LEFT, BOTTOM, RIGHT, ;
- savescreen(TOP, LEFT, BOTTOM, RIGHT) } )
-
- This stack is then dumped when you call BOXGET with the RESTOREALL
- clause:
-
- for x = len(boxstack_) to 1 step -1
- restscreen(boxstack_[x, 1], boxstack_[x, 2], boxstack_[x, 3], ;
- boxstack_[x, 4], boxstack_[x, 5])
- next
- asize(boxstack_, 0) // clean it out
-
-
-
- VALID Clause
-
- Unlike the internal _GET_() function, GETNEW() does not allow you to
- pass a VALID clause to be attached to the GET object. This caused me a
- bit of consternation. One solution would have been to use a temporary
- variable to hold the GET object in the user-defined command, then
- assign its postBlock instance variable. This did not feel right to
- me, so I ripped out some of my hair until I found a better solution.
-
- Finally, I decided to have the preprocessor convert the VALID clause
- to a code block with the "blockify" result-marker ("<{valid}>"). This
- code block would then be passed to BOXGET(). Once inside the function,
- I could then safely assign this code block to the postBlock instance
- variable for the GET object. It works beautifully!
-
-
- Screen Position
-
- If you look closely at the call to GETNEW(), something odd will leap
- out at you. The first two parameters are the row and column at which
- the GET should appear. "But why in Heaven's name do I use MAXROW()+1
- and MAXCOL()+1?" you ask. "Those would be off the screen, right?"
-
- Quite right, but I had my reasons for doing this. In order to
- calculate the proper coordinates for the box, it is necessary to
- determine the length of the GET variable. This could be done by
- checking the length of its PICTURE clause, but suppose that no PICTURE
- clause was specified. Moreover, we cannot always trust the PICTURE
- clause, for it may contain a deceptive template (e.g., "@!", "@R").
-
- Therefore, I felt it best to verify the length of the GET buffer,
- But the GET must be activated with the setFocus() method before you
- can check the buffer, because the g:buffer instance variable only
- exists when the GET is active. The following statements activate the
- GET, grab the length of the buffer, and de-activate it.
-
- oget:setFocus()
- getlength = len(oget:buffer)
- oget:killFocus()
-
- It must be de-activated because the box has not yet been drawn.
- Remember... we need to know the length of the buffer before we can
- think about drawing the box!
-
- Now do you see why I used MAXROW()+1 and MAXCOL()+1 as the row and
- column position for the GET? Of course... because it must be
- activated "before the fact". When we activate it to test the length
- of the buffer, it will be invisible to the user!
-
- Afterwards, it is a simple matter to re-assign the g:row and g:col
- instance variables to the proper position, but that can only be done
- after we know where the box is going to appear.
-
-
- Delimiters
-
- If you use delimiters in your programs, you will be happy to know that
- BOXGET() properly obeys them. However, it required some legwork. If
- you issue a GET using the @..GET command, the DELIMITERS setting is
- automatically taken into account. But when you create a GET object
- using GETNEW() (as I do with the BOXGET() function), delimiters are
- ignored.
-
- Therefore, I had to check for whether delimiters were on or off. If
- they were on, the function had to account for two additional
- characters, so the box would be drawn accordingly. The delimiters
- themselves had to be drawn manually using this code:
-
- dispout(' ' + left(set(_SET_DELIMCHARS), 1) + ;
- space(getlength) + right(set(_SET_DELIMCHARS), 1))
-
- It seems like a lot of trouble to go through, but perfection is worth
- any price. (This is not to say that BOXGET() is perfect, because as
- we all know, software is inherently imperfect!)
-
-
- Summary
-
- Not only will you get good mileage out of this very useful function, but
- I hope that it gives you additional insight into GET objects and 5.0
- in general. This also shows you how you can use the preprocessor to
- make it easier to use functions that have many parameters. By
- creating a user-defined command, you don't have to remember the order
- of the parameters.
-
-
- About The Author
-
- Greg Lief is the author of the Grumpfish Library. He is presenting
- Clipper 5.0 training seminars throughout North America this Spring.
- Contact Grumpfish, Inc. if you are interested in attending.
-