home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-28 | 70.7 KB | 1,972 lines |
- /*
- Program: GRUMPBROW()
- System: GRUMPFISH LIBRARY
- Author: Greg Lief
- Copyright (c) 1988-90, Greg Lief
- Clipper 5.01 Version
- Compile instructions: clipper brow /n/w/a
-
- Awesome generic database browser
-
- Syntax: GRUMPBROW([<security>, <top>, <left>, <bottom>, <right>, ;
- <box>, <fields>, <heads>, <pictures>, <alternates>, ;
- <low value>, <high value>])
- */
-
- //───── begin preprocessor directives
-
- #include "grump.ch"
- #include "inkey.ch"
- #include "dbstruct.ch"
- #include "error.ch"
-
- //───── remove the next statement if you do not need the screen painter
- #define REMBRANDT
-
- //───── remove the next statement if you do not need the code generator
- #define CODEGEN
-
- //───── shorthand
- #translate TextAt(<r>, <c>) => substr(savescreen(<r>, <c>, <r>, <c>), 1, 1)
- #translate ColorAt(<r>, <c>) => substr(savescreen(<r>, <c>, <r>, <c>), 2, 1)
-
- /*
- GETs will be shown on screen in inverse, so all we need to do is save
- a chunk of the screen and look at the color attribute if the color
- attribute is 112 (inverse), then we must be in a GET
- */
- #translate IsItAGet(<r>, <c>) => ;
- substr(savescreen(<r>, <c>, <r>, <c>), 2, 1) == chr(112)
-
- //───── manifest constants for main browse window coordinates
- #define TopRow boxcoords[1]
- #define LeftColumn boxcoords[2]
- #define BottomRow boxcoords[3]
- #define RightColumn boxcoords[4]
-
- //───── manifest constants for get "objects" (no, this isn't OOPS)
- #translate GetRow(<xx>) => gets_\[<xx>, 1]
- #translate GetCol(<xx>) => gets_\[<xx>, 2]
- #translate GetLength(<xx>) => gets_\[<xx>, 3]
- #translate GetName(<xx>) => gets_\[<xx>, 4]
- #translate GetPicture(<xx>) => gets_\[<xx>, 5]
-
- //───── manifest constants for drawn box "objects" (again, this isn't OOPS)
- #translate BoxTop(<xx>) => boxes_\[<xx>, 1]
- #translate BoxLeft(<xx>) => boxes_\[<xx>, 2]
- #translate BoxBottom(<xx>) => boxes_\[<xx>, 3]
- #translate BoxRight(<xx>) => boxes_\[<xx>, 4]
- #translate BoxString(<xx>) => boxes_\[<xx>, 5]
- #translate BoxColor(<xx>) => boxes_\[<xx>, 6]
- #translate BoxFill(<xx>) => boxes_\[<xx>, 7]
-
- //───── indentation of generated source code (my preference is three)
- #define INDENT(x) space(x * 3)
-
- //───── end preprocessor directives
-
- //───── begin global declarations
-
- static qrystring, ; // used for query-by-example
- num_flds, ;
- num_boxes, ;
- boxcoords, ; // array holding main box coordinates
- gets_ := {}, ; // array to hold info for GET fields
- boxes_ := {}, ; // array to hold info for boxes
- maincolor, ; // primary color throughout
- piclens_ := {} // array holding lengths of PICTURE clauses
-
- static hival, lowval // for viewing data subsets
- static oldskip, oldgotop, oldgobott // original movement blocks
-
- /*
- the following three items are used in the add/edit/view (GFBRECVIEW),
- but must be declared as external static because they must be visible in
- two hot-key routines (GFBSKIPAGET and GFBBACKAGET)
- */
- static goingdown, firstfield, curr_get
-
- //───── end global declarations
-
- function GrumpBrow(sec_level, ntop, nleft, nbottom, nright, mbox, ;
- tfields_, heads_, pics_, alternate_, hi, lo)
- local security[7], mid, xx, options, mfield, buffer, lreadexit, ;
- mtype, paintok, key, browse, mget, column, marker, ;
- oldscore := set(_SET_SCOREBOARD, .F.), keepgoing, mrow, mcol, ;
- searchstr, fields_, stru_, scrnbuff
-
- GFSaveEnv(.t., 0) // shut off cursor
-
- //───── reset high and low values for data subsets
- hival := hi
- lowval := lo
-
- default sec_level to ''
- default ntop to 0
- default nleft to 0
- default nbottom to maxrow() - 1
- default nright to maxcol()
- default mbox to .t.
- default alternate_ to ARRAY(7)
- nbottom := min(nbottom, maxrow() - 1)
-
- //───── initialize external STATICs
- boxcoords := if(mbox, { ntop, nleft, nbottom, nright }, ;
- { 0, 0, maxrow(), maxcol() } )
- maincolor := ColorSet(C_GRUMPBROW_SAY, .T.) + ',' + ;
- ColorSet(C_GRUMPBROW_GET, .T.) + ',,,'+ ;
- ColorSet(C_GRUMPBROW_SAY, .T.)
-
- options := []
- afill(security, .f.)
- if "A" $ upper(sec_level)
- security[1] := .t.
- options += "[A]dd "
- endif
- if "D" $ upper(sec_level)
- security[2] := .t.
- options += "[D]elete "
- endif
- if "E" $ upper(sec_level)
- security[3] := .t.
- options += "[E]dit "
- /*
- Determine if user will be able to edit the cell by pressing Enter.
- By default, if you pass "E" so that they can edit, they will be able
- to edit an individual cell. However, if you don't want them to be able
- to do this, pass an "N" as part of the security string (thanks: Imad)
- */
- security[7] := ! ("N" $ upper(sec_level))
- endif
- if "Q" $ upper(sec_level)
- security[4] := .t.
- options += "[Q]uery "
- endif
- if "S" $ upper(sec_level) .and. type(indexkey(0)) == "C"
- security[5] := .t.
- options += "[S]earch "
- endif
- if "V" $ upper(sec_level)
- security[6] := .t.
- options += "[V]iew "
- endif
- options += "[Esc]=quit"
-
- //───── must pass a "P" in the security parameter to allow screen painting
- paintok := ("P" $ upper(sec_level))
-
- //───── create multi-dimensional FIELDS array based on the fields
- //───── array you passed as a parameter
- if tfields_ != NIL
- fields_ := aclone(tfields_)
- //───── create array TFIELDS_ containing all fields in this database
- stru_ := dbstruct()
- num_flds := len(fields_)
- for xx := 1 to num_flds
- //───── find this field in the database
- if (mfield := ascan(stru_, { | a | ;
- upper(a[DBS_NAME]) == upper(fields_[xx]) } ) ) > 0
- fields_[xx] := stru_[mfield]
- //───── uh oh, you called for a field that's not in the .dbf - boom!
- else
- return .f.
- endif
- next
- else
- fields_ := dbstruct()
- num_flds := len(fields_)
- endif
-
- //───── create array heads_ (column headings) if necessary
- if heads_ == NIL
- //───── dump all field names from array FIELDS_ into HEADS_
- heads_ := {}
- aeval(fields_, { | a | aadd(heads_, a[1]) } )
- endif
-
- //───── create array pics_ (picture clauses) if necessary
- if pics_ == NIL
- /*
- okay - create the array and fill it with garbage hi bit ASCII
- why? because we must go through the following FOR..NEXT to
- establish the array holding PICTURE lengths, regardless of
- whether or not they passed the PICS_ array. I didn't want to
- duplicate the code because that is totally wasteful, so I resorted
- to CHR(254) as an indicator that we had to create the array here.
- */
- pics_ := ARRAY(num_flds)
- afill(pics_, chr(254))
- endif
-
- /*
- create PICLENS_ array to hold the length of each GET.
- NOTE: because PICLENS_ is an external STATIC, it might still
- have something in it from the last time we visited GrumpBrow().
- Therefore, we will clean it out before proceeding.
- */
- asize(piclens_, 0)
- for xx = 1 to num_flds
- do case
-
- case fields_[xx][DBS_TYPE] == 'D'
- if pics_[xx] == chr(254)
- pics_[xx] := "@D"
- endif
- aadd(piclens_, 8)
-
- case fields_[xx][DBS_TYPE] == 'M'
- pics_[xx] := "<memo>"
- aadd(piclens_, 6)
-
- case fields_[xx][DBS_TYPE] == 'C'
- if pics_[xx] == chr(254)
- //───── limit maximum width to 35 characters
- if fields_[xx][DBS_LEN] > 35
- pics_[xx] := "@S35"
- aadd(piclens_, 35)
- else
- pics_[xx] := replicate("X", aadd(piclens_, fields_[xx][DBS_LEN]))
- endif
- else
- aadd(piclens_, fields_[xx][DBS_LEN])
- endif
-
- case fields_[xx][DBS_TYPE] == 'L'
- if pics_[xx] == chr(254)
- pics_[xx] := "Y"
- aadd(piclens_, 1)
- else
- mid := pics_[xx]
- aadd(piclens_, len(mid))
- endif
-
- otherwise
- if pics_[xx] == chr(254)
- mid := str(fieldget(fieldpos(fields_[xx][DBS_NAME])))
- if "." $ mid
- pics_[xx] := replicate('9', at(".", mid) - 1) + "."
- pics_[xx] += replicate('9', len(mid) - len(pics_[xx]))
- else
- pics_[xx] := replicate('9', len(mid))
- endif
- endif
- aadd(piclens_, len(pics_[xx]))
- endcase
- next
-
- ColorSet(C_GRUMPBROW_BOX)
-
- //───── create a browse object
- browse := TBrowseDB(ntop + 1, nleft + 1, nbottom - 1, nright - 1)
-
- //───── save original movement blocks in case they need to be reset later
- oldgotop := browse:goTopBlock
- oldgobott := browse:goBottomBlock
- oldskip := browse:skipBlock
-
- //───── set up movement blocks if hi/low values were passed as parameters
- if (hival != NIL .or. lowval != NIL) .and. ! empty(indexkey(0))
- pseudofilt(browse, .f.)
- endif
-
- browse:colorSpec := setcolor()
- browse:headSep := "═╤═"
- browse:colSep := " │ "
- for xx = 1 to num_flds
- /* memos must be treated differently */
- if fields_[xx][DBS_TYPE] == "M"
- column := TBColumnNew(heads_[xx], { | | "<memo>" } )
- else
- column := TBColumnNew(heads_[xx])
- //───── handle picture clause for this column (if there is one)
- if pics_[xx] == chr(254) // no picture
- column:block := fieldblock(fields_[xx][DBS_NAME])
- else
- //───── use TRANSFORM() to simulate PICTURE.
- column:block := &("{ | | transform(" + fields_[xx][DBS_NAME] + ;
- ", '" + pics_[xx] + "') }")
- /*
- Now load cargo with a two element array. Element 1
- contains the "un-transformed" retrieval code block
- for this field, and element 2 contains the desired
- PICTURE clause. These are used by GETNEW() when
- editing a cell directly (see below).
- */
- column:cargo := { &("{ | x | if( pcount() == 0, " + ;
- alias() + "->" + fields_[xx][DBS_NAME] + ", " + ;
- alias() + "->" + fields_[xx][DBS_NAME] + " := x) }"), pics_[xx] }
- endif
- endif
- * column:width := piclens_[xx]
- browse:AddColumn( column )
- next
- if mbox
- ShadowBox(ntop, nleft, nbottom, nright, 1)
- mid := nleft + (int(nright - nleft) / 2) // calculate midpoint
- @ nbottom, mid - INT(LEN(options) / 2) ssay options
- else
- @ maxrow(), int((maxcol() - LEN(options))/2) ssay options
- endif
- setcolor(maincolor)
- do while .t.
- //───── do not allow cursor to move into frozen columns
- if browse:colPos <= browse:freeze
- browse:colPos := browse:freeze + 1
- endif
- dispbegin()
- do while ! browse:stabilize() .and. (key := inkey()) == 0
- enddo
- dispend()
- if browse:stable
- //───── draw arrows if data off to left or right
- //───── must take frozen columns into account
- mrow := row()
- mcol := col()
- if browse:leftvisible > browse:freeze + 1
- @ nbottom, nleft say chr(17) + chr(196) ;
- color colorset(C_GRUMPBROW_GET, .t.)
- else
- @ nbottom, nleft say if(mbox, chr(200)+chr(205), space(2)) ;
- color colorset(C_GRUMPBROW_BOX, .t.)
- endif
- if browse:rightvisible < browse:colCount
- @ nbottom, nright - 1 say chr(196) + chr(16) ;
- color colorset(C_GRUMPBROW_GET, .t.)
- else
- @ nbottom, nright - 1 say if(mbox, chr(205)+chr(188), space(2)) ;
- color colorset(C_GRUMPBROW_BOX, .t.)
- endif
- setpos(mrow, mcol)
- key := ginkey(0)
- endif
- do case
-
- case key == K_LEFT
- browse:left()
-
- case key == K_RIGHT
- browse:right()
-
- case key == K_UP
- browse:up()
-
- case key == K_DOWN
- browse:down()
-
- case key == K_PGUP .or. key == K_HOME
- browse:pageUp()
-
- case key == K_PGDN .or. key == K_END
- browse:pageDown()
-
- case key == K_CTRL_PGUP
- browse:goTop()
-
- case key == K_CTRL_PGDN
- browse:goBottom()
-
- case key == K_CTRL_LEFT
- browse:panLeft()
-
- case key == K_CTRL_RIGHT
- browse:panRight()
-
- case key == K_ESC
- exit
-
- case key == K_ENTER .and. security[7] .and. browse:stable // direct edit
- //───── if they are on a memo, display it for viewing only
- if fields_[browse:colPos][DBS_TYPE] == "M"
- buffer := ShadowBox(8, 20, 16, 60, 2, ;
- fields_[browse:colPos][DBS_NAME])
- memoedit(fieldget(fieldpos(fields_[browse:colPos][DBS_NAME])), ;
- 9, 21, 15, 59, .F.)
- ByeByeBox(buffer)
- elseif security[3] // must have Edit access to do this
- if Rec_Lock()
- //───── yank the current column object out of the browse object
- column := browse:getColumn(browse:colPos)
- //───── create a corresponding GET
- setcursor(1)
- //───── enable up/down arrow keys to exit the read
- lreadexit := readexit(.t.)
- /*
- Create corresponding GET object with GETNEW() and
- read it now. Note the use of the TBcolumn cargo instance
- variable. Cargo is a two-element array. The first
- element contains the retrieval code block for this
- data item. The second contains the PICTURE clause.
- This was initialized above.
- */
- readmodal( { getnew(Row(), Col(), column:cargo[1], ;
- column:heading, column:cargo[2], browse:colorSpec) } )
- setcursor(0)
- readexit(lreadexit)
- //───── check if this field is part of an active filter
- if upper(column:heading) $ upper(dbfilter())
- //───── if so, see if filter condition is still fulfilled
- //───── if not, bounce record pointer so edited record
- //───── will no longer be displayed
- if ! eval( &("{ || " + dbfilter() + "}"))
- skip
- browse:refreshAll()
- else
- browse:refreshCurrent()
- endif
- endif
- //───── if this field is part of the active index, repaint
- if upper(column:heading) $ upper(indexkey(0))
- browse:refreshAll()
- else
- browse:refreshCurrent()
- endif
- //───── if we exited with an arrow key, pass it through
- //───── and start editing same field in the next record
- xx := lastkey()
- if xx == K_UP .or. xx == K_DOWN
- keyboard chr(xx) + chr(K_ENTER)
- endif
- unlock
- endif
- endif
-
- case (key == 76 .or. key == 108) // lock columns
- xx := browse:freeze
- boxget xx prompt "Lock how many columns" picture '##'
- if lastkey() != K_ESC
- browse:freeze := xx
- browse:invalidate()
- endif
-
- case (key == 115 .or. key == 83) .and. security[5] .and. ;
- type(indexkey(0)) == "C" // search
- if ! override(alternate_[5], browse)
- //───── save affected portion of top row of box
- scrnbuff := savescreen(ntop, (maxcol() + 1)/2 - 11, ;
- ntop, (maxcol() + 1)/2 + 10)
- @ ntop, (maxcol() + 1) / 2 - 11 ssay "[" + space(20) + "]"
- key := ginkey(0, "KEY")
- searchstr := ''
- do while ( (key > 31 .and. key < 255) .or. key == K_BS )
- marker := recno()
- if key == K_BS
- searchstr := substr(searchstr, 1, len(searchstr) - 1)
- if len(searchstr) > 0
- seek searchstr
- else
- searchstr := NIL
- exit
- endif
- else
- seek searchstr + chr(key)
- endif
- if eof()
- go marker
- else
- if key != K_BS
- searchstr += chr(key)
- endif
- @ ntop, (maxcol() + 1) / 2 - 10 ssay padc(searchstr, 20)
- //───── only refresh screen if record pointer moved
- if recno() != marker
- //───── save current record number... see below
- marker := recno()
- //───── force a redisplay
- dispbegin()
- browse:refreshAll()
- do while ! browse:stabilize()
- enddo
- /*
- Sometimes the refreshAll() method will not leave
- us on the correct record. If this was the case,
- we'll move up until we get to the right record.
- */
- do while recno() != marker
- browse:up()
- skip -1
- enddo
- dispend()
- endif
- endif
- key := ginkey(0, "KEY")
- enddo
- restscreen(ntop, (maxcol() + 1)/2 - 11, ntop, ;
- (maxcol() + 1)/2 + 10, scrnbuff)
- searchstr := NIL
- else
- browse:refreshAll()
- endif
-
- case (key == 97 .or. key == 65) .and. security[1] // Add record
- if ! override(alternate_[1], browse)
- gfbrecview('A', fields_, heads_, pics_)
- endif
- browse:refreshAll()
-
- case (key == 100 .or. key == 68) .and. security[2] // delete record
- if ! override(alternate_[2], browse)
- if yes_no('This record will be deleted from the file',;
- 'Do you want to do this')
- if rec_lock()
- delete
- skip -1
- browse:refreshall()
- unlock
- else
- err_msg(NETERR_MSG)
- endif
- endif
- else
- browse:refreshall()
- endif
-
- case (key == 101 .or. key == 69) .and. security[3] // edit record
- if ! override(alternate_[3], browse)
- gfbrecview('E', fields_, heads_, pics_)
- browse:refreshCurrent()
- else
- browse:refreshAll()
- endif
-
- #ifdef REMBRANDT
-
- case (key == 112 .or. key == 80) .and. paintok // screen painter
- gfbrecview('S', fields_, heads_, pics_)
-
- #endif
-
- case (key == 113 .or. key == 81) .and. security[4] // query
- if ! override(alternate_[4], browse)
- keepgoing := .t.
- if ! empty(qrystring)
- if yes_no("Use most recent query search criteria")
- keepgoing := .f.
- marker := recno()
- continue
- if eof()
- err_msg("No more matches")
- go marker
- else
- browse:refreshAll()
- endif
- endif
- endif
- if keepgoing
- gfbrecview('Q', fields_, heads_, pics_)
- browse:refreshAll()
- endif
- else
- browse:refreshAll()
- endif
-
- case (key == 118 .or. key == 86) .and. security[6] // view
- if ! override(alternate_[6], browse)
- gfbrecview('V', fields_, heads_, pics_)
- else
- browse:refreshAll()
- endif
-
- case key == K_ALT_I // switch index order
- //───── first make sure that there is a second index!
- if ! empty(indexkey(2))
- set order to if(indexord() == 1, 2, 1)
- endif
- browse:refreshAll()
-
- case key == K_ALT_S // view subset
- //───── first make sure there is an open index!
- if ! empty(indexkey(0))
- pseudofilt(browse, .t.)
- endif
-
- endcase
- enddo
- GFRestEnv()
- set(_SET_SCOREBOARD, oldscore)
- return NIL
-
- * end function GrumpBrow()
- *--------------------------------------------------------------------*
-
-
- /*
- Override(): See if a procedure/function name was passed to override
- the generic add/edit/delete/search routines -- if so,
- and if it exists in the application, run that instead
- of the generic routine
- */
- static function Override(melement, browse)
- local mproc, ret_val := .f., xx
- if ! empty(melement)
- //───── first check if they specified a code block, and if so, evaluate
- //───── the block passing the browse object as a parameter
- if valtype(melement) == "B"
- ret_val := .t.
- eval(melement, browse)
- else
- if ! type(melement) $ 'UE'
- ret_val := .t.
- eval( &("{ | | " + melement + "}") )
- endif
- endif
- endif
- return ret_val
-
- * end static function Override()
- *--------------------------------------------------------------------*
-
- /*
- GfbRecView(): The heart of the order, so to speak. This is
- where all SAYs and GETs are SAID and GOTTEN
- */
- static function GfbRecView(mode, fields_, heads_, pics_)
- local scatter[len(fields_)], picstring, mstring, mfield, mrow, handle, ;
- pic_len, xx, yy, marker, buffer, oldcolor, is_memo, mtype, key, ;
- oldscrn := savescreen(0, 0, maxrow(), maxcol()), picclause, ;
- getlist := {}, mscrnfile := alias() + '.gfs', mainloop := .t., ;
- curr_fld, bytes := (maxrow() + 1) * (maxcol() + 1) * 2
- num_boxes := 0
- firstfield := 1
- setcolor(maincolor)
- cls
- if mode != "S"
- @ 0, 2 ssay '[ ' + if(mode == 'Q', "Query database", ;
- if(mode == 'A', "Add" , if(mode == "E", "Edit", "View")) + ;
- "ing record") + ' ]'
- endif
- if mode $ "AEQ"
- @ maxrow(), 25 ssay 'save edits'
- @ maxrow(), 43 ssay 'exit without saving'
- @ maxrow(), 18 ssay 'Ctrl-W' color 'I'
- @ maxrow(), 39 ssay 'Esc' color 'I'
- endif
-
- asize(gets_, 0)
-
- //───── if we have a pre-saved screen file, read it in now
- if file(mscrnfile)
- if ( handle := fopen(mscrnfile) ) == -1
- err_msg("could not open " + mscrnfile)
- else
- buffer := space(bytes)
- //───── first paint the static text by restoring the saved screen
- fread(handle, @buffer, bytes)
- restscreen(0, 0, maxrow(), maxcol(), buffer)
- buffer := [ ]
- //───── then read next byte, which will tell us how many fields
- //───── are stored in this file
- fread(handle, @buffer, 1)
- num_flds := bin2i(buffer)
- //───── now read in that number of fields - dynamically growing GETS_ array
- for yy = 1 to num_flds
- gfreadline(@buffer, handle)
- aadd(gets_, { bin2i(substr(buffer, 1, 1)), ; // row position
- bin2i(substr(buffer, 2, 1)), ; // column position
- bin2i(substr(buffer, 3, 1)), ; // row position
- substr(buffer, 4, 10), ; // field name
- substr(buffer, 14) } ) // PICTURE clause
- next
- num_flds := len(gets_)
- //───── okay, now read next byte, which will tell us how many boxes
- //───── are stored in this file -- note that we are limited to 20
- buffer := [ ]
- fread(handle, @buffer, 1)
- num_boxes := min(bin2i(buffer), 20)
- buffer := space(14)
- //───── now read in that number of boxes - dynamically growing BOXES_ array
- asize(boxes_, 0)
- for xx = 1 to num_boxes
- fread(handle, @buffer, 14)
- aadd(boxes_, { bin2i(substr(buffer, 1, 1)), ; // top row
- bin2i(substr(buffer, 2, 1)), ; // left column
- bin2i(substr(buffer, 3, 1)), ; // bottom row
- bin2i(substr(buffer, 4, 1)), ; // right column
- substr(buffer, 5, 8) , ; // box string
- bin2i(substr(buffer, 13, 1)), ; // box color
- substr(buffer, 14, 1) } ) // fill box? Y/N
- next
- fclose(handle)
- endif
- endif
-
- //───── determine topmost row at which to paint SAYs and GETs
- mrow := max(12 - int(num_flds / 2), 1)
- curr_fld := 1
- do while curr_fld <= num_flds .and. mainloop
- if file(mscrnfile)
- mfield := GetName(curr_fld)
- pic_len := GetLength(curr_fld)
- picclause := GetPicture(curr_fld)
- else
- mfield := fields_[curr_fld][DBS_NAME]
- pic_len := piclens_[curr_fld]
- picclause := pics_[curr_fld]
- endif
- mtype := type(mfield)
-
- //───── determine initial value for each get field
- do case
- //───── if you went nuts and changed the structure on me, there might
- //───── be a field that is undefined. Fortunately for you, Mr. Grump
- //───── is here to protect you from the deadly DOS drop (but cut out
- //───── your monkey business, because next time I might not be around)
- case mtype == 'U'
- err_msg(trim(mfield) + " is undefined - please delete the " + ;
- mscrnfile + " file")
- restscreen(0, 0, maxrow(), maxcol(), oldscrn)
- return .f.
-
- case mtype == 'D'
- scatter[curr_fld] := if(mode == "S", padr(mfield, 8), ;
- if(mode $ "AQ", ctod(''), fieldget(fieldpos(mfield))))
- case mtype == 'M'
- //───── embed an ascii 255 at the front of this -- we need this when
- //───── in the get loop down below so that we know that this is a memo!
- scatter[curr_fld] := if(mode == "S", padr(mfield, 6), chr(255) + ;
- if(mode $ "AMQ", [], fieldget(fieldpos(mfield))))
- case mtype == 'C'
- scatter[curr_fld] := if(mode == "S", padr(mfield, pic_len), ;
- if(mode $ "AQ", space(pic_len), ;
- fieldget(fieldpos(mfield))))
- case mtype == 'L'
- scatter[curr_fld] := if(mode == "S", substr(mfield, 1, 1), ;
- if(mode $ "AQ", .f., fieldget(fieldpos(mfield))))
- otherwise
- scatter[curr_fld] := if(mode == "S", padr(mfield, pic_len), ;
- if(mode $ "AQ", 0, fieldget(fieldpos(mfield))))
- endcase
-
- //───── do generic says and gets only if we didn't use a pre-saved screen file
- if ! file(mscrnfile)
- @ curr_fld + mrow, 37 - len(heads_[curr_fld]) say heads_[curr_fld]
- if mode == "S"
- aadd(gets_, { curr_fld + mrow, 39, pic_len, mfield, picclause } )
- setcolor("i")
- endif
- //───── if this is a memo field, we want to simply display the
- //───── word "<memo>" (defined above as picstring) rather than
- //───── showing the memo. the memo will be displayed when they
- //───── cursor to it.
- if type(mfield) == "M"
- @ curr_fld + mrow, 39 say "<memo>"
- else
- //───── truncate character fields that would spill off the right side
- if type(mfield) == "C" .and. len(scatter[curr_fld]) > 40
- @ curr_fld + mrow, 39 say substr(scatter[curr_fld], 1, 40) ;
- picture picclause
- else
- @ curr_fld + mrow, 39 say scatter[curr_fld] picture picclause
- endif
- endif
- if mode == "S"
- setcolor(maincolor)
- endif
- else
- //───── gotta display the fields in inverse for maintenance mode
- if mode == "S"
- setcolor("i")
- endif
- //───── pull get coordinates and info out of the GETS_ array
- //───── note: if this is a memo field, we want to simply display the
- //───── word "<memo>" rather than the memo
- if type(mfield) == "M"
- @ GetRow(curr_fld), GetCol(curr_fld) say "<memo>"
- else
- @ GetRow(curr_fld), GetCol(curr_fld) say scatter[curr_fld] ;
- picture picclause
- endif
- if mode == "S"
- setcolor(maincolor)
- endif
- endif
-
- //───── we have either reached the last field or the bottom of the screen
- //───── whichever came first -- time to stop for the pause that refreshes
- if row() == maxrow() - 1 .or. curr_fld == num_flds
- /*
- GOINGDOWN is a flag that indicates in which direction we are
- moving through the gets. if it is true, then we are indeed
- moving downward. if false, we are moving upward. it is
- important only because when we escape from editing a memo,
- we will not know where to go unless we have such a flag.
- trust me -- i went through all kinds of gyrations on this one.
- */
- goingdown := .t.
- do case
-
- #ifdef REMBRANDT
- case mode == "S"
- gfbmaint()
- //───── we must force an exit from the main loop or else
- //───── the sucker will bomb horribly
- mainloop := .f.
- #endif
-
- case mode != "V"
- setcursor(1)
- setkey(K_UP, {|p, l, v| gfbackaget(p, l, v) } )
- setkey(K_DOWN, {|p, l, v| gfskipaget(p, l, v) } )
- for curr_get = firstfield to curr_fld
- /*
- now for the tricky part - figuring out if this is a memo.
- memos will look like character strings, but since we were
- thoughtful enough to embed the chr(255), that will assist
- us in making the determination now
- */
- is_memo := if(valtype(scatter[curr_get]) != "C", .f., ;
- substr(scatter[curr_get], 1, 1) == chr(255))
- if ! is_memo
- if ! file(mscrnfile)
- //───── truncate characters that would spill off right side
- if valtype(scatter[curr_get]) == "C" .and. ;
- len(pics_[curr_get]) > 40
- @ curr_get + mrow, 39 get scatter[curr_get] ;
- picture "@S40 " + pics_[curr_get]
- else
- @ curr_get + mrow, 39 get scatter[curr_get] ;
- picture pics_[curr_get]
- endif
- else
- @ GetRow(curr_get), GetCol(curr_get) get ;
- scatter[curr_get] picture GetPicture(curr_get)
- endif
- read
- //───── check last keypress
- key := lastkey()
- do case
-
- /* proceed downward */
- case key == K_ENTER .and. goingdown
- goingdown := .t.
-
- /* jump to end of this screen */
- case key == K_PGDN
- curr_get := curr_fld
-
- /* back to first get */
- case key == K_PGUP
- curr_get := firstfield - 1
-
- /* (save and then) fall out */
- case key == K_CTRL_W .or. key == K_ESC
- mainloop := .f. // force exit from main loop
- curr_get := curr_fld // force exit from this loop
- endcase
- else
- buffer := shadowbox(mrow + curr_get, 20, ;
- mrow + curr_get + 4, 60, 2)
- @ mrow+curr_get+4, 25 ssay "Ctrl-W to save, or Esc to exit"
- //───── undefine left and right arrows for the memoedit
- setkey(K_UP, NIL)
- setkey(K_DOWN, NIL)
- scatter[curr_get] := chr(255) + ;
- memoedit(substr(scatter[curr_get], 2), mrow + ;
- curr_get + 1, 21, mrow + curr_get + 3, 59, .t.)
- //───── okay, now define them again
- setkey(K_UP, {|p, l, v| gfbackaget(p, l, v) } )
- setkey(K_DOWN, {|p, l, v| gfskipaget(p, l, v) } )
- if lastkey() == K_ESC .and. ! goingdown
- gfbackaget()
- endif
- byebyebox(buffer)
- endif
- next
- setcursor(0)
- //───── undefine left and right arrows for the memoedit
- setkey(K_UP, NIL)
- setkey(K_DOWN, NIL)
- otherwise
- ginkey(0)
- endcase
- mrow -= (BottomRow - TopRow) - 1
- //───── if we still have another pageful of gets to get,
- //───── then by all means clear out the previous bunch
- if mainloop .and. curr_fld != num_flds
- firstfield := curr_fld + 1
- scroll(TopRow + 1, LeftColumn + 1, BottomRow - 1, RightColumn - 1, 0)
- endif
- endif
- curr_fld++
- enddo
- clear gets
- if lastkey() != K_ESC .and. mode $ "AEQ"
- if mode == 'Q' // query-by-example
- //───── loop through all fields and build the query string
- qrystring := []
- for xx = 1 to curr_fld - 1
- mfield := if(file(mscrnfile), GetName(xx), fields_[xx][DBS_NAME])
- mtype := type(mfield)
- //───── first get rid of the dratted chr(255) littering the front of a memo
- if ( mtype := type(mfield) ) == "M"
- scatter[xx] := substr(scatter[xx], 2)
- endif
- if ! empty(scatter[xx])
- do case
- case mtype == "N"
- picstring := mfield + " == " + ltrim(str(scatter[xx]))
- case mtype == "L"
- picstring := if(! scatter[xx], "! ", "") + mfield
- case mtype == "D"
- picstring := mfield + " == ctod('" + dtoc(scatter[xx]) + "')"
- otherwise
- /*
- if user specified "..whatever.." in character field,
- then they want to find the first occurrence containing
- wwhatever; consequently, we must first check for this
- situation by looking for two periods
- */
- if substr(scatter[xx], 1, 2) == '..'
- picstring := 'upper([' + ;
- trim(strtran(scatter[xx], '..', '')) + ;
- ']) $ upper(' + mfield + ')'
- else
- //───── note the case-insensitive search - feel free to
- //───── change this if you need to by stripping out the
- //───── references to "UPPER("
- picstring := "upper(" + mfield + ") = [" + ;
- upper(trim(scatter[xx])) + "]"
- endif
- endcase
- qrystring += if(! empty(qrystring), " .and. ", "") + picstring
- endif
- next
- marker := recno()
- if ! empty(qrystring)
- //───── compile this string to a code block, which will speed up the
- //───── process a tad over the traditional macro expansion rigmarole
- qrystring := MakeBlock(qrystring)
- locate for eval(qrystring) while inkey() == 0
- if eof()
- err_msg("No match found")
- go marker
- endif
- endif
- else && add/edit mode: gather memory variables back into fields
- if yes_no(if(mode == "A", "Add this new record", "Save your edits"))
- if if(mode == "A", Add_Rec(), Rec_Lock())
- for xx = 1 to curr_fld - 1
- mfield := if(file(mscrnfile), GetName(xx), fields_[xx][DBS_NAME])
- //───── note: memos will still have a chr(255) stuck on the front
- //───── and we do not want to save that, so let's act accordingly
- if type(mfield) == "M"
- fieldput(fieldpos(mfield), substr(scatter[xx], 2))
- else
- fieldput(fieldpos(mfield), scatter[xx])
- endif
- next
- endif
- endif
- endif
- endif
- unlock
- restscreen(0, 0, maxrow(), maxcol(), oldscrn)
- return NIL
-
- * end static function GFBRecView()
- *--------------------------------------------------------------------*
-
-
- /*
- GfBackAGet(): trap up arrow in the READ to
- move back up by one GET
- */
- static function gfbackaget(p, l, v)
- curr_get := max(curr_get - 2, firstfield - 1)
- /*
- we might have gotten here in one of two ways:
- (a) up arrow, in which case there would have
- been three parameters passed
- (b) direct call when escaping from a memofield
- and moving upwards through the get list
- we only need to stuff the keyboard in the first situation
- */
- if pcount() == 3
- keyboard chr(K_ENTER)
- endif
- goingdown := .f.
- return NIL
-
- * end static function GFBackAGet()
- *--------------------------------------------------------------------*
-
-
- /*
- GfSkipAGet(): trap down arrow in the READ to
- move down one GET
- */
- static function gfskipaget(p, l, v)
- keyboard chr(K_ENTER)
- goingdown := .t.
- return NIL
-
- * end static function GFSkipAGet()
- *--------------------------------------------------------------------*
-
-
- #ifdef REMBRANDT
-
- /*
- GfbMaint(): The Long-Awaited Screen Painter
- */
- static function gfbmaint
- local key := 0, mrow := 0, mcol := 0, mfile, buffer, xx, yy, zz, mstart, ;
- nleft, nbottom, nright, oldf1, handle, mfield, mstring, mtype, ngets, ;
- oldinsert := readinsert(.f.), mcolor, oldprintf, oldconsole, ;
- firstfield, bytes := (maxrow() + 1) * (maxcol() + 1) * 2, getlist := {}
-
- //───── disable f1 key so that they will only get my help screen!
- oldf1 := setkey(K_F1, nil)
- setcursor(1)
- do while key != K_F10
- //───── show current screen position at top right corner
- @ 0, maxcol() - 6 ssay padr(ltrim(str(mrow, 2)) + ', ' + ;
- ltrim(str(mcol, 3)), 7)
- setpos(mrow, mcol)
- key := ginkey(0)
- do case
-
- //───── help!
- case key == K_ALT_H .or. key == K_F1
- gfbhelpme()
-
- //───── alphanumeric or something printable
- case key > 31 .and. key < 255
- if isitaget(mrow, mcol)
- err_msg("You cannot type over a GET")
- else
- //───── if insert mode is on, push everything else on this row over
- if readinsert()
- /*
- we don't want to move any gets, but how to do this?
- simple... we jump merrily through every character on
- this row and check for its color. if its color is
- inverse (112), then we know it must be a get and we
- skip it.
- */
- mstart := 0
- for xx = 0 to maxcol() - mcol
- if isitaget(mrow, mcol + xx)
- restscreen(mrow, mcol + mstart + 1, mrow, mcol + xx - 1, ;
- savescreen(mrow, mcol + mstart, mrow, mcol + xx - 2))
- //───── now loop through until we find the next non-get
- do while xx++ < maxcol() + 1 - mcol .and. ;
- isitaget(mrow, mcol+xx)
- enddo
- mstart := xx
- endif
- next
-
- //───── if there were no gets, mstart will still be zero.
- if mstart == 0
- restscreen(mrow, mcol + 1, mrow, maxcol(), ;
- savescreen(mrow, mcol, mrow, maxcol() - 1))
- endif
- endif
- @ mrow, mcol ssay chr(key)
- if mcol == maxcol()
- /*
- Because Clipper 5 doesn't assume a fixed screen of
- 25 rows x 80 columns, it allows you to keep typing
- off the right side of the screen. Therefore, we
- must restrict this sort of nonsense ourselves by
- forcing a wrap from the bottom row to the top.
- */
- if mrow == maxrow()
- mrow := 0
- else
- mrow++
- endif
- mcol := 0
- else
- mcol++
- endif
- endif
-
- //───── move a field or resize a box
- case key == K_ENTER
- if isitaget(mrow, mcol)
- if gfbmovefld()
- mrow := row()
- mcol := col()
- //───── let's re-sort the gets_ array so that they will fall in the
- //───── order that they appear on the screen (rather than how they
- //───── appear in the .dbf)
- asort(gets_,,, { | x, y | if(x[1] == y[1], ;
- x[2] < y[2], x[1] < y[1] ) } )
- endif
- else
- //───── are we on a box outline?? only the shadow knows
- if num_boxes > 0
- if ( xx := isitabox() ) > 0
- boxes_[xx] := gfbsizebox(xx)
- endif
- endif
- endif
-
- case key == K_BS .and. mcol > 0
- mcol--
- //───── wipe out this space if we are not on a get
- if ! isitaget(mrow, mcol)
- @ mrow, mcol ssay [ ]
- endif
- setpos(mrow, mcol)
-
- case key == K_DEL
- //───── first check if we are on a box outline
- if ( xx := isitabox() ) > 0
- if yes_no("Are you sure you want to delete this box")
- //───── wipe it off the screen
- @ BoxTop(xx), BoxLeft(xx), BoxBottom(xx), BoxRight(xx) ;
- box space(8)
- //───── then blast it from the array
- adel(boxes_, xx)
- //───── finally, resize box array and decrement box counter
- asize(boxes_, --num_boxes)
- endif
- //───── next check if we are on a get field
- elseif isitaget(mrow, mcol)
- if yes_no("Are you sure you want to delete this field")
- //───── first find this element
- xx := gfbwhichget(mrow, mcol)
- if xx > 0
- //───── wipe it off the screen
- scroll(GetRow(xx), GetCol(xx), GetRow(xx), GetCol(xx) + ;
- GetLength(xx) - 1, 0)
- //───── blast it out of the array
- adel(gets_, xx)
- //───── finally, resize box array and decrement box counter
- asize(gets_, --num_flds)
- endif
- endif
- else
- /*
- note that we don't want to move any gets, so we must check
- each character on this row to see if it is part of a get.
- however, so that we do not have to go through this loop
- on lines without gets, we do a quick scan of the array
- to see if there is a get on this row
- */
- xx := ascan(gets_, { | a | a[1] == mrow} )
- if xx > 0 // yes, there is at least one get on this row
- mstart := 0
- for xx = 0 to maxcol() - mcol
- if isitaget(mrow, mcol + xx)
- restscreen(mrow, mcol + mstart, mrow, mcol + xx - 2, ;
- savescreen(mrow, mcol + mstart + 1, mrow, mcol + xx - 1))
- @ mrow, mcol + xx - 1 ssay [ ]
- //───── now loop through until we find the next non-get
- do while xx++ < maxcol() + 1 - mcol .and. ;
- isitaget(mrow, mcol+xx)
- enddo
- mstart := xx
- endif
- next
- else // no gets on this row
- restscreen(mrow, mcol, mrow, maxcol() - 1, ;
- savescreen(mrow, mcol + 1, mrow, maxcol()))
- @ mrow, maxcol() ssay [ ]
- endif
- endif
-
- //───── toggle insert mode
- case key == K_INS
- setcursor( if( readinsert(! readinsert()), 1, 3) )
-
- //───── color palette to change active color
- case key == K_ALT_P
- setcolor(colorpal())
-
- //───── draw box
- case key == K_ALT_B
- gfbdrawbox(mrow, mcol)
-
-
- #ifdef CODEGEN
-
- //───── generate code
- case key == K_SH_F10
- mfile := upper(alias()) + ".prg"
- if if(! file(mfile), .t., yes_no(mfile + " already exists", "Overwrite it"))
- buffer := savescreen(0, 0, maxrow(), maxcol())
- dispbegin()
- gfbcleanup()
- oldprintf := set(_SET_PRINTFILE, mfile)
- set print on
-
- //───── first, get rid of position indicator so it doesn't get
- //───── stuck in the generated code (gasp!)
- scroll(0, maxcol() - 6, 0, maxcol(), 0)
-
- //───── next, let's get rid of the boxes so that they don't get
- //───── displayed as part of the text
- for xx = 1 to num_boxes
- @ BoxTop(xx), BoxLeft(xx), BoxBottom(xx), BoxRight(xx) box space(8)
- next
- oldconsole := set(_SET_CONSOLE, .F.)
- //───── next, get rid of
- //───── standard header -- change it as you see fit
- QOut("/*")
- QOut(INDENT(1) + "Program: " + mfile)
- QOut(INDENT(1) + "Date: " + dtoc(date()))
- QOut(INDENT(1) + "Time: " + time())
- QOut(INDENT(1) + "Dialect: Clipper 5.01")
- QOut(INDENT(1) + "Generated by GrumpBrow()")
- QOut(INDENT(1) + "Copyright (c) 1990 Greg Lief")
- QOut(INDENT(1) + "Notes: data entry screen for " + alias() + ".dbf")
- QOut(INDENT(1) + "expects to be passed a parameter (mode),")
- QOut(INDENT(1) + "which could be either [A]dd, [E]dit, [V]iew")
- QOut("*/")
- QOut()
- QOut('#include "inkey.ch"')
- //───── must maintain a counter separate from XX, because
- //───── if we skip any memos the counter value will be off
- //───── in the generated code, and chaos will ensue...
- yy := 0
- ngets := len(gets_)
- for xx = 1 to ngets
- if type(GetName(xx)) != "M"
- QOut("#define m" + padr(GetName(xx), 12) + " scatter_[" + ;
- ltrim(str(++yy)) + "]")
- endif
- next
- QOut()
- QOut("function addedit(mode)")
- QOut("memvar getlist")
- QOut("local scatter_ := {}, oldcurs, marker")
- QOut("local fieldnames_ := { ")
- firstfield := .t.
- for xx = 1 to ngets
- if type(GetName(xx)) != "M"
- if firstfield
- firstfield := .f.
- else
- QQOut(", ;")
- QOut(space(23))
- endif
- QQOut("'" + trim(GetName(xx)) + "'")
- endif
- next
- QQOut("}")
- QOut("use " + alias())
- QOut()
- QOut("//───── static text")
- //───── draw the boxes, dude (in Saudi Arabia, "dude" means "worm",
- //───── so that was not necessarily a term of endearment)
- for xx = 1 to num_boxes
- QOut("@ " + ltrim(str(BoxTop(xx))) + ", " + ;
- ltrim(str(BoxLeft(xx))) + ", " + ;
- ltrim(str(BoxBottom(xx))) + ", " + ;
- ltrim(str(BoxRight(xx))) + " box '" + BoxString(xx) + ;
- if(BoxFill(xx) == "Y", " ", "") + ;
- "' color '" + color_n2s(BoxColor(xx)) + "'")
- next
- //───── now draw all other text
- QOut("setcolor('" + maincolor + "')")
- for xx = 0 to maxrow()
- mstring := [ ]
- yy := 0
- do while yy <= maxcol() .and. asc(TextAt(xx, yy)) < 33
- yy++
- enddo
- if yy <= maxcol()
- do while yy <= maxcol()
- mstring := TextAt(xx, yy)
- mcolor := ColorAt(xx, yy)
- zz := yy + 1
- do while ColorAt(xx, zz) == mcolor .and. zz <= maxcol()
- mstring += TextAt(xx, zz)
- zz++
- enddo
- if ! empty(mstring)
- if color_s2n(maincolor) != bin2i(mcolor)
- maincolor := color_n2s(mcolor)
- QOut("setcolor('" + maincolor + "')")
- endif
- QOut("@ " + str(xx, 2) + ", " + str(yy, 2) + ;
- " say [" + trim(mstring) + "]")
- endif
- yy := zz
- enddo
- endif
- next
-
- QOut()
- QOut("//───── use phantom record to grab initial values if adding")
- QOut("if mode == 'A'")
- QOut(INDENT(1) + "marker := recno()")
- QOut(INDENT(1) + "go 0")
- QOut("endif")
- QOut()
- QOut("//───── initialize memory variables")
- QOut("aeval(fieldnames_, { | a | aadd(scatter_, " + ;
- "fieldget(fieldpos(a))) } )")
-
- /* do the gets */
- QOut()
- QOut("//───── go GET 'em")
- for xx = 1 to ngets
- if type(GetName(xx)) != "M"
- QOut("@ " + str(GetRow(xx), 2) + ", " + ;
- str(GetCol(xx), 2) + " get m" + GetName(xx) + ;
- " picture '" + GetPicture(xx) + "'")
- endif
- next
-
- /* basic stuff */
- QOut("oldcurs := setcursor(if(mode == 'V', 0, 1))")
- QOut("if mode != 'V'")
- QOut(INDENT(1) + "read")
- /* do the replaces */
- QOut(INDENT(1) + "//───── do the replaces if they didn't escape out")
- QOut(INDENT(1) + "if lastkey() != K_ESC")
- QOut(INDENT(2) + "if mode == 'A'")
- QOut(INDENT(3) + "append blank")
- QOut(INDENT(2) + "endif")
- QOut(INDENT(2) + "aeval(fieldnames_, { | a, x | " + ;
- "fieldput(fieldpos(a), scatter_[x]) } )")
- QOut(INDENT(1) + "//───── if in add mode, reset record pointer")
- QOut(INDENT(1) + "elseif mode == 'A'")
- QOut(INDENT(2) + "go marker")
- QOut(INDENT(1) + "endif")
- QOut("else")
- QOut(INDENT(1) + "getlist := {}")
- QOut(INDENT(1) + "inkey(0)")
- QOut("endif")
- QOut("setcursor(oldcurs)")
- QOut("return NIL")
- QOut()
- QOut("* end of file " + mfile)
-
- set print off
- set(_SET_PRINTFILE, oldprintf)
- set(_SET_CONSOLE, oldconsole)
- restscreen(0, 0, maxrow(), maxcol(), buffer)
- dispend()
- waiton("Code successfully generated to " + mfile)
- inkey(1)
- waitoff()
- endif
-
- #endif // CODEGEN
-
- case key == K_ESC
- if yes_no('Your changes will be lost','Are you sure you want to exit')
- exit
- endif
-
- otherwise
- gfarrowkey(key, maxcol(), @mrow, @mcol)
-
- endcase
- enddo
- setkey(K_F1, oldf1) // re-enable f1 key to whatever it was before this
- readinsert(oldinsert)
-
- if key != K_ESC
- gfbcleanup()
- mfile := alias() + ".gfs"
- if ( handle := fcreate(mfile) ) == -1
- err_msg("Could not create " + mfile)
- else
- if fwrite(handle, ;
- savescreen(0, 0, maxrow(), maxcol()), bytes) != bytes
- err_msg("Error writing to " + mfile)
- endif
- //───── save number of fields
- fwrite(handle, chr(num_flds))
- ngets := len(gets_)
- for xx = 1 to ngets
- fwrite(handle, chr(GetRow(xx)) + chr(GetCol(xx)) + ;
- chr(GetLength(xx)) + padr(GetName(xx), 10) + ;
- GetPicture(xx) + CRLF)
- next
- //───── save number of boxes
- fwrite(handle, chr(num_boxes))
- if num_boxes > 0
- for xx = 1 to num_boxes
- fwrite(handle, chr(BoxTop(xx)) + chr(BoxLeft(xx)) + ;
- chr(BoxBottom(xx)) + chr(BoxRight(xx)) + ;
- BoxString(xx) + chr(BoxColor(xx)) + BoxFill(xx) )
- next
- endif
- fclose(handle)
- endif
- endif
- setcursor(0)
- return NIL
-
- * end static function GFBMaint()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbCleanup(): clear GET fields in preparation for
- saving .GFS file or generating code
- */
- static function gfbcleanup
- local xx, ngets := len(gets_)
- //───── we must go through the gets_ array and wipe all inverse
- //───── gets off the screen -- if we leave them here, they will be
- //───── restored when adding/editing only to be wiped out again,
- //───── which creates an annoying little visual blip.
- setcolor(maincolor)
- for xx = 1 to ngets
- //───── if we have more than one screen of gets, we must add error-
- //───── trapping here so that the thing don't crash!!
- if valtype(gets_[xx]) == "U"
- exit
- else
- scroll(GetRow(xx), GetCol(xx), GetRow(xx), GetCol(xx) + ;
- GetLength(xx) - 1, 0)
- endif
- next
- //───── get rid of the coordinates at the top right corner
- scroll(0, maxcol() - 6, 0, maxcol(), 0)
- return nil
-
- * end static function GFBCleanUp()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbHelpMe(): help screen for screen painting module
- */
- static function gfbhelpme
- gfsaveenv(.t., 0, '+w/b') // shut off cursor & change color
- @ 0, 0, maxrow(), maxcol() box BOXFRAMES[5] color "+gr/b"
- @ 1, 19 ssay "GRUMPBROWSE() SCREEN PAINTER - ACTIVE KEYS" color "+gr/b"
- @ 2, 16 ssay "draw box"
- @ 3, 16 ssay "change color"
- @ 4, 16 ssay "toggle insert mode on/off"
- @ 5, 16 ssay "destructive backspace"
- @ 6, 16 ssay "delete from cursor position"
- @ 7, 16 ssay "exit without saving changes"
- @ 8, 16 ssay "save screen file (.gfs) and exit"
- @ 9, 16 ssay "generate code for this screen and exit"
- @11, 16 ssay "move left one column"
- @11, 54 ssay "move right one column"
- @12, 16 ssay "move to top left"
- @12, 54 ssay "move to bottom right"
- @13, 16 ssay "move right five columns"
- @13, 54 ssay "move left five columns"
- @14, 16 ssay "move to top row"
- @14, 54 ssay "move to bottom row"
- @15, 16 ssay "move to left column"
- @15, 54 ssay "move to right column"
- @17, 4 ssay "Boxes may be filled or unfilled. If you create a filled box, any text"
- @18, 4 ssay "underneath it will be pulled in, and will be displayed in the color of"
- @19, 4 ssay "the box. To delete a box, place the cursor on it outline and press"
- @20, 4 ssay "delete. to resize a box, place the cursor on it and press Enter. To"
- @21, 4 ssay "delete a GET, place the cursor on it and press Delete. To move a get,"
- @22, 4 ssay "place the cursor on it and press Enter."
- @23, 20 ssay "press any key to return to screen painter"
- setcolor("i")
- @ 2, 4 ssay "Alt-B"
- @ 3, 4 ssay "Alt-P"
- @ 4, 4 ssay "Insert"
- @ 5, 4 ssay "Backspace"
- @ 6, 4 ssay "Delete"
- @ 7, 4 ssay "Esc"
- @ 8, 4 ssay "F10"
- @ 9, 4 ssay "Shift-F10"
- @11, 4 ssay "LtArrow"
- @11, 42 ssay "RtArrow"
- @12, 4 ssay "Home"
- @12, 42 ssay "End"
- @13, 4 ssay "Tab"
- @13, 42 ssay "Shift-Tab"
- @14, 4 ssay "PgUp"
- @14, 42 ssay "PgDn"
- @15, 4 ssay "Ctrl-Left"
- @15, 42 ssay "Ctrl-Right"
- ginkey(0)
- gfrestenv()
- return nil
-
- * end static function GFBHelpMe()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbMoveFld(): logic to drag a field around on the screen
- */
- static function gfbmovefld
- local oldcolor, xx, mrow, mcol, mlength, key, yy, buffer, ;
- buffer2, ret_val := .f.
- oldcolor := setcolor()
- xx := gfbwhichget(row(), col())
- if xx > 0
- mrow := GetRow(xx)
- mcol := GetCol(xx)
- mlength := GetLength(xx)
- key := 0
- //───── initialize a scrap buffer which will hold the underlying
- //───── screen area as we drag the field around. if we did not
- //───── do this, then the field would act as a giant erase,
- //───── annihilating static text (and other fields) in its wake!
- buffer := []
- for yy = 2 to mcol + mlength
- buffer += chr(32) + chr(23)
- next
- buffer2 := savescreen(mrow, mcol, mrow, mcol + mlength - 1)
- do while key != K_ENTER .and. key != K_ESC
- @ 0, maxcol() - 6 ssay padr(ltrim(str(mrow, 2)) + ', ' + ;
- ltrim(str(mcol, 3)), 7)
- setpos(mrow, mcol)
- key := ginkey(0)
- if key != K_ENTER .and. key != K_ESC
- setcolor(oldcolor)
- dispbegin()
- restscreen(mrow, mcol, mrow, mcol + mlength - 1, buffer)
- gfarrowkey(key, maxcol() + 1 - mlength, @mrow, @mcol)
- buffer := savescreen(mrow, mcol, mrow, mcol + mlength - 1)
- restscreen(mrow, mcol, mrow, mcol + mlength - 1, buffer2)
- dispend()
- buffer2 := savescreen(mrow, mcol, mrow, mcol + mlength - 1)
- endif
- enddo
- dispbegin()
- restscreen(mrow, mcol, mrow, mcol + mlength - 1, buffer)
- if key == K_ENTER
- ret_val := .t.
- //───── make sure that we are not plopping this get down over
- //───── another one!
- for yy = mcol to mcol + mlength - 1
- if isitaget(mrow, yy)
- dispend()
- Err_Msg("You cannot place a get on top of a get")
- ret_val := .f.
- exit
- endif
- next
- endif
- if ret_val
- //───── change this array element to reflect the new position
- GetRow(xx) := mrow
- GetCol(xx) := mcol
- restscreen(mrow, mcol, mrow, mcol + mlength - 1, buffer2)
- dispend()
- else
- //───── either they escaped, or they tried to place this get
- //───── on top of another one -- in either case we must now
- //───── redraw the original get
- @ GetRow(xx), GetCol(xx) ssay padr(GetName(xx), GetLength(xx)) color 'I'
- endif
- endif
- setcolor(oldcolor)
- return ret_val
-
- * end static function GFBMoveFld()
- *--------------------------------------------------------------------*
-
-
-
- /*
- IsItABox(): determine whether or not we are on a box outline
- useful for resizing and deleting boxes
- */
- static function isitabox
- local ret_val := 0, xx, temprow := row(), tempcol := col()
- for xx = 1 to num_boxes
- if ((BoxTop(xx) == temprow .or. BoxBottom(xx) == temprow) .and. ;
- BoxLeft(xx) <= tempcol .and. BoxRight(xx) >= tempcol) .or. ;
- ((BoxLeft(xx) == tempcol .or. BoxRight(xx) == tempcol) .and. ;
- BoxTop(xx) <= temprow .and. BoxBottom(xx) >= temprow)
- ret_val := xx
- exit
- endif
- next
- return ret_val
-
- * end static function IsItABox()
- *--------------------------------------------------------------------*
-
-
- /*
- GFBWhichGet(): locate current get in get array
- */
- static function gfbwhichget(mrow, mcol)
- local mget := 0, xx
- //───── figure out which field it is by scanning the array
- //───── for the value of the row we are currently on
- xx := ascan(gets_, { | a | a[1] == mrow} )
- do while xx > 0 .and. mget == 0
- //───── now check the length of this field (character 3 in
- //───── the array element) to see if we are actually on it
- if xx > 0
- if mcol < GetCol(xx) + GetLength(xx)
- mget := xx
- else
- xx := ascan(gets_, { | a | a[1] == mrow }, xx + 1)
- endif
- endif
- enddo
- return mget
-
- * end static function GFBWhichGet()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbDrawBox(): logic to drag a field around on the screen
- */
- static function gfbdrawbox(mrow, mcol)
- local buffer, mbox, key, ntop, nleft, oldscrn, mfill, mwidth, fillbuff, xx
- local scrnbuff := savescreen(0, maxcol() - 16, 0, maxcol())
- static boxtypes := {}
- //───── only load boxtypes array if it is empty (i.e., first time through)
- if empty(boxtypes)
- for xx = 1 to 5
- aadd(boxtypes, substr(BOXFRAMES[xx], 1, 8))
- next
- endif
-
- /*
- even though it seems wasteful, we must make a safety copy of
- the entire screen because you might decide not to keep the
- box, right? remember the old maxim: users change their mind
- as often as Cher changes costumes
- */
- oldscrn := savescreen(0, 0, maxrow(), maxcol())
- setpos(mrow, mcol)
- buffer := shadowbox(9, 35, 15, 44, 2, "Boxes")
- mbox := achoice(10, 36, 14, 43, boxtypes)
- byebyebox(buffer)
- if mbox > 0
- mfill := yes_no("Would you like this box to be filled")
- key := 0
- ntop := mrow
- nleft := mcol
- buffer := savescreen(ntop, nleft, mrow, mcol)
- do while key != K_ENTER .and. key != K_ESC
- //───── display current box coordinates at top right corner
- //───── but only if the box is not in the top right corner!!
- if ntop > 0 .or. mcol < maxcol() - 16
- @ 0, maxcol() - 16 ssay padr(ltrim(str(ntop, 2)) + ', ' + ;
- ltrim(str(nleft, 3)) + ', ' + ;
- ltrim(str(mrow, 2)) + ', ' + ;
- ltrim(str(mcol, 3)), 16)
- endif
- setpos(mrow, mcol)
- key := ginkey(0)
- if ! ((mrow == maxrow() .and. key == K_DOWN) .or. ;
- (mrow == ntop .and. key == K_UP) .or. ;
- (mcol == nleft .and. key == K_LEFT) .or. ;
- (mcol > 78 .and. key == K_RIGHT))
- dispbegin()
- restscreen(ntop, nleft, mrow, mcol, buffer)
- gfarrowkey(key, maxcol(), @mrow, @mcol)
- buffer := savescreen(ntop, nleft, mrow, mcol)
- @ ntop, nleft, mrow, mcol box boxtypes[mbox] + if(mfill, " ", "")
- dispend()
- endif
- enddo
- if key == K_ESC
- restscreen(0, 0, maxrow(), maxcol(), oldscrn)
- //───── restore original cursor position too
- setpos(ntop, nleft)
- else
- //───── restore screen contents under the coordinates area (top right)
- restscreen(0, maxcol() - 16, 0, maxcol(), scrnbuff)
- //───── only fill the box if there is at least one row inside it!
- if mfill .and. mrow > ntop + 1
- gfbfillbox(ntop, nleft, mrow, mcol, buffer)
- endif
- num_boxes++
- aadd(boxes_, { ntop, nleft, mrow, mcol, boxtypes[mbox], ;
- color_s2n(), if(mfill, "Y", "N") } )
- endif
- endif
- return NIL
-
- * end static function GFBDrawBox()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbSizeBox(): logic to resize a box
- */
- static function gfbsizebox(xx)
- local oldscrn, key := 0, buffer, oldcolor, ntop, nleft, nbottom, nright, ret_val
- ntop := BoxTop(xx)
- nleft := BoxLeft(xx)
- nbottom := BoxBottom(xx)
- nright := BoxRight(xx)
- //───── once again, we must make a safety copy of the entire screen
- //───── because you might decide to leave the box size as is. picky users!
- oldscrn := savescreen(0, 0, maxrow(), maxcol())
- //───── get rid of the box for a split second and save the underlying screen
- //───── otherwise, we are going to have a real mess on our hands!
- @ ntop, nleft, nbottom, nright box space(8)
- buffer := savescreen(ntop, nleft, nbottom, nright)
- oldcolor := setcolor(color_n2s(BoxColor(xx)))
- @ ntop, nleft, nbottom, nright box BoxString(xx) + ;
- if(BoxFill(xx) == "Y", " ", "")
- do while key != K_ENTER .and. key != K_ESC
- setpos(nbottom, nright)
- key := ginkey(0)
- if ! ((nbottom == maxrow() .and. key == 24) .or. ;
- (nbottom == ntop .and. key == 5) .or. ;
- (nright == nleft .and. key == 19) .or. ;
- (nright > 78 .and. key == 4))
- dispbegin()
- restscreen(ntop, nleft, nbottom, nright, buffer)
- gfarrowkey(key, maxcol(), @nbottom, @nright)
- buffer := savescreen(ntop, nleft, nbottom, nright)
- @ ntop, nleft, nbottom, nright box BoxString(xx) + ;
- if(BoxFill(xx) == "Y", " ", "")
- dispend()
- endif
- enddo
- if key == K_ESC
- restscreen(0, 0, maxrow(), maxcol(), oldscrn)
- ret_val := boxes_[xx]
- else
- if BoxFill(xx) == "Y"
- gfbfillbox(ntop, nleft, nbottom, nright, buffer)
- endif
- ret_val := { ntop, nleft, nbottom, nright, BoxString(xx), BoxColor(xx), ;
- BoxFill(xx) }
- endif
- setcolor(oldcolor)
- return ret_val
-
- * end static function GFBSizeBox()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbFillBox(): fill box with underlying screen
- */
- static function gfbfillbox(ntop, nleft, nbottom, nright, buffer)
- /*
- we have just drawn a filled box. there is every likelihood
- that we have some relevant information underneath this box.
- therefore, we must pull the information from the underlying
- screen into the box. but not so fast! we must make this
- information conform to the color specification of the box.
- one more thing, leave the gets in inverse. got all that? good.
- */
- local mwidth := (nright - nleft + 1) * 2, fillbuff := [], xx, yy, ;
- mcolor := chr(color_s2n())
- buffer := substr(buffer, mwidth + 1, len(buffer) - mwidth * 2)
- for xx = 0 to (nbottom - ntop - 2)
- for yy = 2 to nright - nleft
- fillbuff += substr(buffer, xx * mwidth + yy * 2 - 1, 1)
- if substr(buffer, xx * mwidth + yy * 2, 1) != chr(112)
- fillbuff += mcolor
- else
- fillbuff += chr(112)
- endif
- next
- next
- restscreen(ntop + 1, nleft + 1, nbottom - 1, nright - 1, fillbuff)
- return NIL
-
- * end static function GFBFillBox()
- *--------------------------------------------------------------------*
-
-
- /*
- GfbArrowKey(): process arrow keypresses etcetera
- */
- static function gfarrowkey(key, maxlength, mrow, mcol)
- do case
- case key == K_UP
- if mrow == 0
- mrow := maxrow()
- else
- mrow--
- endif
-
- case key == K_DOWN
- if mrow == maxrow()
- mrow := 0
- else
- mrow++
- endif
-
- case key == K_LEFT .and. mcol > 0
- mcol--
-
- case key == K_RIGHT .and. mcol < maxlength
- mcol++
-
- case key == K_HOME
- mrow := mcol := 0
-
- case key == K_END
- mrow := maxrow()
- mcol := maxlength
-
- case key == K_PGUP
- mrow := 0
-
- case key == K_PGDN
- mrow := maxrow()
-
- case key == K_CTRL_LEFT
- mcol := 0
-
- case key == K_CTRL_RIGHT
- mcol := maxlength
-
- case key == K_TAB
- mcol := min(mcol + 5, maxlength)
-
- case key == K_SH_TAB
- mcol := max(mcol - 5, 0)
-
- endcase
- return NIL
-
- * end static function GFBArrowKey()
- *--------------------------------------------------------------------*
-
- #endif // REMBRANDT
-
-
- //───── the next five functions are for viewing subsets with the
- //───── three TBrowse navigation blocks... you
-
- static function pseudofilt(b, needinput)
- local oldhandler, newhandler, newblock, getlist := {}
- local oldscrn, initval := { space(40), 0.00, ctod('') } ;
- [at(valtype(eval( &("{||" + indexkey(0) + "}"))),"CND")]
- if needinput
- oldscrn := shadowbox(7, 9, 10, 70, 1, "View Subset")
- hival := if(empty(hival), initval, ;
- if(valtype(hival) != "C", hival, padr(hival, 40)))
- lowval := if(empty(lowval), initval, ;
- if(valtype(lowval) != "C", lowval, padr(lowval, 40)))
- @ 8, 11 say "Enter low value: " get hival
- @ 9, 11 say "Enter high value: " get lowval
- setcursor(1)
- read
- setcursor(0)
- byebyebox(oldscrn)
- endif
-
- //───── trim if character type
- if valtype(initval) == "C"
- hival := trim(hival)
- lowval := trim(lowval)
- endif
-
- if lastkey() != K_ESC .or. ! needinput
- if ! empty(hival) .and. ! empty(lowval)
- newhandler = { | e | blockhead(e, oldhandler) }
- oldhandler = errorblock(newhandler)
- begin sequence
- b:goTopBlock := &(" { | | dbseek(" + convertit(hival) + ", .t.) }")
- b:goBottomBlock := &(" { | | dbseek(" + ;
- convertit(lowval, .t.) + ", .t.) , dbskip(-1) }")
- b:skipBlock := { | SkipCnt | Gilligan(SkipCnt, ;
- &("{ || " + indexkey(0) + "}")) }
- //───── force "filter" to be set by "going top"
- b:goTop()
- end
- errorblock(oldhandler) /* reset previous error handler */
- else
- //───── reset original movement blocks
- b:goTopBlock := oldgotop
- b:goBottomBlock := oldgobott
- b:skipBlock := oldskip
- b:goTop()
- endif
- endif
- return nil
-
- * end static function PseudoFilt()
- *--------------------------------------------------------------------*
-
-
- static function gilligan(skipcnt, val)
- local movement := 0
- do case
- /* no movement */
- case skipcnt == 0
- skip 0
- /* moving forward */
- case skipcnt > 0
- do while movement < skipcnt .and. eval(val) <= lowval .and. ! eof()
- skip 1
- movement++
- enddo
- // make sure that we are within range - if not, move backward
- do while (eval(val) > lowval .or. eof()) .and. ! bof()
- skip -1
- movement--
- enddo
- if bof() // no data in range... fall out
- keyboard chr(K_ESC)
- endif
- /* moving backward */
- case skipcnt < 0
- do while movement > skipcnt .and. eval(val) >= hival
- skip -1
- if bof()
- exit
- endif
- movement--
- enddo
- // make sure that we are within range -- if not, move forward
- do while eval(val) < hival .and. ! eof()
- skip
- movement++
- enddo
- if eof() // no data within range... fall out
- keyboard chr(K_ESC)
- endif
- endcase
- return movement
-
- * end static function Gilligan()
- *--------------------------------------------------------------------*
-
-
- /*
- do type conversion for character/numeric/date index keys
- in preparation for compiling goTopBlock and goBottomBlock
- 2nd parameter passed to increment low value for SOFTSEEK
- */
- static function convertit(val, lo)
- local ret_val := valtype(val)
- do case
- case ret_val == "D"
- ret_val := "CTOD('" + dtoc(val + if(lo != NIL, 1, 0)) + "')"
- case ret_val == "N"
- ret_val := ltrim(str(val + if(lo != NIL, 1, 0)))
- case ret_val == "C"
- //───── if 2nd parameter was passed, then use CHR(255) instead
- //───── of last character in the string. This is required for
- //───── proper placement of the record pointer.
- if lo != NIL
- ret_val := "'" + left(val, len(val) - 1) + "' + chr(255)"
- else
- ret_val := "'" + val + "'"
- endif
- endcase
- return ret_val
-
- * end static function ConvertIt()
- *--------------------------------------------------------------------*
-
-
- /*
- BlockHead(): custom error handler for code block syntax bombs
- */
- static function blockhead(e, oldhandler)
- if e:gencode() == EG_SYNTAX .or. e:gencode() == EG_ARG
- dispend()
- err_msg("Error in code block syntax")
- break
- return .t.
- endif
- return eval(oldhandler, e)
-
- * end static function BlockHead()
- *--------------------------------------------------------------------*
-
- * eof brow.prg
-