home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-26 | 34.6 KB | 1,004 lines |
- /*
- Function: GrumpCalc()
- Purpose: Pop-up Spreadsheet
- Author: Greg Lief
- Copyright (c) 1991 Greg Lief
- Dialect: Clipper 5.01
- Syntax: GrumpCalc( [<cfile>] )
- Compile: clipper grumcalc /n /w
-
- Parameter: <cfile> is the name of a presaved spreadsheet file to
- load immediately. If <cfile> is not used, GrumpCalc
- will start you out with a blank spreadsheet. If you
- have already visited GrumpCalc, your previous spreadsheet
- will be restored exactly as you left it.
-
- Hot-key: You can also configure GrumpCalc() to be called by a
- hot key with the command:
- SET KEY <whatever> TO grumpcalc
- If you do this, GrumpCalc() will detect the presence
- of the three default Clipper parameters (proc, line,
- var) and will therefore neither attempt to load a
- pre-saved spreadsheet file NOR prompt you to save it.
- The reasoning here is that such use would be to
- paste the result of a calculation into a pending GET.
-
- Paste: If you have configure GrumpCalc() as a hot-key and
- pop it up while you are GETting a numeric, the contents of
- the current cell will be pasted into the GET upon exit.
- */
-
- //───── NOTE: if you don't need Grumpcalc's context-specific help screen,
- //───── remove the following line of source code and recompile this file
- #define HELPSCREEN
-
- //───── required header files
- #include "dbstruct.ch"
- #include "inkey.ch"
- #include "box.ch"
- #include "grump.ch"
- #include "setcurs.ch"
-
- //───── basic global parameters
- #define COLUMNS 26 // maximum columns in spreadsheet
- #define ROWS maxrow() - 4 // maximum rows in spreadsheet
- #define CURR_COLUMN b:colPos - 1 // current column in spreadsheet
- #define CURR_CELL nums_[ele, CURR_COLUMN] // current cell
- #define NEXT_CELL nums_[ele, b:colPos] // cell to right of current
-
- //───── structure of three-dimensional NUMS_ array
- #define CONTENTS 1
- #define FORMULA 2
- #define LINKS 3
- #define DEPENDENCIES 4
-
- //───── structure of linked cell subarrays
- #define ROW 1
- #define COLUMN 2
- #define OPERATOR 3
-
- //───── manifest constants for coordinates when highlighting cells
- #define TOP highlight_[1]
- #define LEFT highlight_[2]
- #define BOTTOM highlight_[3]
- #define RIGHT highlight_[4]
- #define HIGHLIGHTED highlight_[5]
-
- //───── shorthand for character strings that spill over into adjacent columns
- #translate SpillOver( <r>, <c> ) => valtype(nums_\[<r>, <c>, FORMULA]) == "B"
-
- static nums_ // master spreadsheet array
- static ele // pointer into the array
- static b // TBrowse object for spreadsheet
-
- function GrumpCalc(cfile, nline, cvar)
- local bspill, c, key := 0, x, y, oldscore := set(_SET_SCOREBOARD, .f.)
- local nrow, ncol, nval, tagging := .f., highlight_ := { , , , , {} }
- local links_, ptr, coop, ccell, cbuffer // for direct cell references
- local plaincolor := 'w/b', hilitecolor := 'i' // for highlighting headings
- local refreshhilite := .f.
- local oldf10 := setkey(K_F10, NIL) // turn off F10 key, save status
- local curr_get := getactive() // for pasting into... see below
-
- #ifdef HELPSCREEN
-
- local oldf1 := setkey(K_F1, { || helpme() } )
-
- #endif
-
- default cfile to ''
-
- //───── if only the filename was passed, attempt to load spreadsheet
- if cfile != NIL .and. file(cfile) .and. nline == NIL
- waiton( { 'Loading from ' + cfile + '... please wait' }, .f.)
- nums_ := gloadarray(cfile)
- waitoff()
-
- //───── load spreadsheet on first pass only
- elseif nums_ == NIL
- nums_ := array(ROWS + 1, COLUMNS)
- for x = 1 to ROWS
- for y = 1 to COLUMNS
- nums_[x, y] := { NIL, NIL, {}, {} }
- next
- next
- //───── the last row of the array will be used to save column widths
- for y = 1 to COLUMNS
- nums_[ROWS + 1, y] := 10
- next
- endif
- gfsaveenv(.t., 0 , 'w/b')
- @ 0, 0, maxrow(), maxcol() box B_DOUBLE+' '
-
- #ifdef HELPSCREEN
-
- @ maxrow(), maxcol() - 10 say "F1 = Help"
-
- #endif
-
- ele := 1
-
- //───── configure TBrowse object on first pass only
- if b == NIL
- b := TBrowseNew(2, 1, maxrow()-1, maxcol()-1)
- b:headSep := chr(205)
- b:colSep := ''
- b:colorSpec := "w/b, i, +w/rb, +w/r"
- b:skipBlock := { | SkipCnt | AwSkipIt(@ele, SkipCnt, ROWS) }
-
- //───── create initial column for letter -- non-editable
- c := TBColumnNew(, { || str(ele, 2) })
- c:width := 2
- b:AddColumn( c )
-
- for x := 1 to 26
- c := TBColumnNew(space(4) + chr(x + 64) + space(5), makecolumn(x))
- //───── grab width from last row of the main array
- c:width := nums_[ROWS + 1, x]
- b:addColumn(c)
- next
-
- //───── double-line column separator between row letter & first editable row
- b:getColumn(2):colSep := chr(186)
- b:autoLite := .f.
- b:freeze := 1
- b:colPos := 1
- else
- //───── if already configured, force a redisplay
- b:refreshAll()
- endif
-
- //───── generic code block used by cells that contain spilled-over characters
- bspill := { |r,c| substr(nums_[r, c - 1, CONTENTS], b:getColumn(c):width + 1) }
- do while key != K_ESC
- //───── if in first column, move them to second column
- if b:colPos <= b:freeze
- b:colPos := b:freeze + 1
- endif
- dispbegin()
- do while ! b:stabilize()
- enddo
- nrow := row()
- ncol := col()
-
- //───── highlight column letter at top for quick reference
- @ b:ntop, col() say b:getColumn(b:colPos):heading color hilitecolor
-
- //───── if we just edited a cell that was on the same row as a
- //───── highlighted region, we must now refresh that highlight
- //───── because otherwise the current row will be dehighlighted
- if refreshHilite
- refreshHilite := .f.
- b:colorRect( { TOP, LEFT, BOTTOM, RIGHT }, {3, 2} )
- endif
-
- //───── higlight row number on left for quick reference
- b:colorRect( { b:rowPos, 1, b:rowPos, 1 }, {2, 2} )
-
- //───── display current position at top left corner
- @ 1,1 say "(" + chr(b:colPos + 63) + ltrim(str(ele)) + ") "
-
- //───── now display column width if it has been changed
- if b:getColumn(b:colPos):width != 10
- dispout("[W" + ltrim(str(b:getColumn(b:colPos):width)) + "] ")
- endif
-
- //───── now display cell formula
- if ! empty(CURR_CELL[FORMULA])
- dispout(CURR_CELL[FORMULA])
- endif
-
- //───── now display contents
- if valtype(CURR_CELL[FORMULA]) != "B" .and. ;
- ! empty(CURR_CELL[CONTENTS]) .and. ;
- (empty(CURR_CELL[FORMULA]) .or. trim(CURR_CELL[FORMULA]) $ ['"^])
- if valtype(CURR_CELL[CONTENTS]) == "C"
- dispout(substr(CURR_CELL[CONTENTS], 1, maxcol() - col() - 1))
- else
- dispout(CURR_CELL[CONTENTS])
- endif
- endif
- scroll(1, col(), 1, maxcol() - 1, 0)
- setpos(nrow, ncol)
- b:hiLite() // highlight current cell
- dispend()
- key := ginkey(0, "KEY")
-
- //───── dehighlight column letter
- @ b:ntop, col() say b:getColumn(b:colPos):heading color plaincolor
- //───── dehighlight row number
- b:colorRect( { b:rowPos, 1, b:rowPos, 1 }, {1, 2} )
- b:deHilite() // current cell
- do case
-
- case key == K_UP .and. ele > 1
- if tagging
- dispbegin()
- //───── if rectangle has shrunk, redraw in old color and
- //───── alter bottom row
- if b:rowPos == TOP
- TOP--
- elseif b:rowPos == BOTTOM .and. BOTTOM > TOP
- b:colorRect( { TOP, LEFT, BOTTOM--, RIGHT }, {1, 2} )
- endif
- endif
- b:up()
-
- case key == K_DOWN .and. ele < ROWS
- if tagging
- dispbegin()
- if b:rowPos == BOTTOM
- BOTTOM++
- elseif b:rowPos == TOP .and. TOP < BOTTOM
- b:colorRect( { TOP++, LEFT, BOTTOM, RIGHT }, {1, 2} )
- endif
- endif
- b:down()
-
- case key == K_LEFT
- if tagging .and. CURR_COLUMN > 1
- dispbegin()
- //───── if rectangle has shrunk, redraw in old color and
- //───── alter right column
- if b:colPos == RIGHT
- if RIGHT > LEFT
- b:colorRect( { TOP, LEFT, BOTTOM, RIGHT-- }, {1, 2} )
- else
- RIGHT := b:colPos
- LEFT--
- endif
- else
- LEFT--
- endif
- endif
- b:left()
-
- case key == K_RIGHT
- if tagging
- dispbegin()
- if b:colPos == LEFT .and. LEFT < RIGHT
- b:colorRect( { TOP, LEFT++, BOTTOM, RIGHT }, {1, 2} )
- elseif b:colPos < b:colCount
- RIGHT++
- endif
- endif
- b:right()
-
- case key == K_TAB .and. ! tagging
- //───── if rightmost column is within sight, go to it now
- if b:rightvisible == b:colCount
- b:colPos := b:colCount
- else
- //───── pan from leftmost to rightmost
- x := b:leftvisible
- y := b:rightvisible
- for c = x to y
- b:panRight()
- next
- endif
-
- case key == K_SH_TAB .and. ! tagging
- //───── if leftmost editable column is within reach, go to it now
- if b:leftvisible - (b:rightvisible - b:leftvisible) < 1
- b:colPos := 2
- else
- //───── loop from right to left
- x := b:leftvisible
- y := b:rightvisible
- for c = x to y
- b:left()
- next
- endif
-
- case key == K_CTRL_LEFT .and. ! tagging
- b:panHome()
-
- case key == K_CTRL_RIGHT .and. ! tagging
- b:panEnd()
-
- case key == K_PGUP .and. ! tagging
- b:pageUp()
-
- case key == K_PGDN .and. ! tagging
- b:pageDown()
-
- case key == K_HOME .and. ( b:colPos != 2 .or. ele != 1 )
- b:colPos := 2
- b:pageUp()
-
- case key == K_END .and. ( b:colPos != COLUMNS + 1 .or. ele != ROWS)
- b:colPos := COLUMNS + 1
- b:pageDown()
-
- case key == K_F10
- do case
- //───── if an array of highlighted cells already exists, load
- //───── it as links to the currently selected cell...
- case ! empty(HIGHLIGHTED)
- //───── must verify that current cell is not part of
- //───── the highlighted cells to preclude circularity
- if ascan(HIGHLIGHTED, { | a | a[ROW] == ele .and. ;
- a[COLUMN] == b:colPos - 1 } ) > 0
- err_msg({ "This cell is part of the highlighted region", ;
- "Therefore you cannot paste the sum here" })
- else
- CellLinks(HIGHLIGHTED)
-
- //───── store this in the formula slot
- CURR_CELL[FORMULA] := "@SUM(" + chr(64 + HIGHLIGHTED[1,2]) + ;
- ltrim(str(HIGHLIGHTED[1,1])) + ".." + ;
- chr(64 + atail(HIGHLIGHTED)[2]) + ;
- ltrim(str(atail(HIGHLIGHTED)[1])) + ")"
- HIGHLIGHTED := {}
- b:refreshCurrent()
-
- //───── are there any dependencies from this cell?
- if ! empty(CURR_CELL[DEPENDENCIES])
- Recalc(ele, CURR_COLUMN)
- endif
-
- //───── now get rid of the highlight
- dispbegin()
- b:colorRect( { TOP, LEFT, BOTTOM, RIGHT }, {1, 2} )
- dispend()
- endif
-
- //───── if we were highlighting, build array of highlighted cells
- case tagging
- for x = TOP to BOTTOM
- //───── note: must subtract one from column: 1st column locked
- for y = LEFT to RIGHT
- //───── plus sign in the next statement is the operator
- aadd(HIGHLIGHTED, { x, y - 1, '+' })
- next
- next
- tagging := .f.
-
- //───── change highlight color
- dispbegin()
- b:colorRect( { TOP, LEFT, BOTTOM, RIGHT }, {3, 2} )
- dispend()
-
- //───── start tagging: establish anchors for the rectangle
- otherwise
- TOP := BOTTOM := b:rowPos
- LEFT := RIGHT := b:colPos
- tagging := .t. // set TAGGING flag on
- endcase
-
- case key == K_ALT_P // print
- //───── if there is a highlighted region, allow user to print that
- x := yes_no2("Spreadsheet Print Options", maxrow() / 2, ;
- if(! empty(HIGHLIGHTED), " Entire ", " OK "), ;
- if(! empty(HIGHLIGHTED), " Highlight ", " Cancel "))
- if lastkey() != K_ESC
- if x
- printit(1, 1, ROWS, COLUMNS)
- elseif ! x .and. ! empty(HIGHLIGHTED)
- printit(TOP, LEFT - 1, BOTTOM, RIGHT - 1)
- endif
- endif
-
- case key == K_ALT_W // change column width
- changewidth()
-
- case key == K_ENTER .or. key == K_F2 .or. ( key > 31 .and. key < 255 )
- if key > 31
- keyboard chr(key)
- endif
-
- //───── highlight current cell for clarity
- b:hiLite()
- //───── F2 indicates not to allow left/right arrow to exit the GET
- EditCell(lastkey() != K_F2)
- b:dehiLite()
-
- //──── force redisplay of current row
- b:refreshCurrent()
-
- //───── if this cell is on a row that also contains a highlighted
- //───── region, we must set a flag to redisplay the highlight after
- //───── stabilizing the TBrowse above
- if ! empty(HIGHLIGHTED)
- if ascan(HIGHLIGHTED, { | a | a[ROW] == ele } ) > 0
- refreshHilite := .t.
- endif
- endif
- if valtype(CURR_CELL[CONTENTS]) == "C" .and. ;
- (valtype(CURR_CELL[FORMULA]) != "C" .or. ;
- left(CURR_CELL[FORMULA], 1) != "@")
-
- //───── check for direct cell references, which would begin
- //───── with "+" and be followed by other characters
- if left(CURR_CELL[CONTENTS], 1) == "+" .and. ;
- len(trim(CURR_CELL[CONTENTS])) > 1
- links_ := {}
-
- //───── if there were already direct cell references in this
- //───── cell, we must clear them out before adding new ones!
- if ( y := len(CURR_CELL[LINKS])) > 0
- RemoveLink(y)
- endif
-
- //───── first convert to uppercase for comparative purposes
- CURR_CELL[CONTENTS] := upper(CURR_CELL[CONTENTS])
-
- //───── store this in the formula slot
- CURR_CELL[FORMULA] := trim(CURR_CELL[CONTENTS])
-
- do while ! empty(CURR_CELL[CONTENTS])
- ptr := 1
- ccell := []
- coop := substr(CURR_CELL[CONTENTS], ptr++, 1)
- while ! (c := substr(CURR_CELL[CONTENTS], ptr++, 1)) $ "+-/* "
- ccell += c
- enddo
-
- //───── trim contents of cell
- CURR_CELL[CONTENTS] := substr(CURR_CELL[CONTENTS], ptr - 1)
-
- //───── derive row and column of referred cell
- ncol := asc(left(ccell, 1)) - 64
- nrow := val(substr(ccell, 2))
-
- //───── basic error trapping here; in case user entered
- //───── "+A1+B1+" or any other extraneous stuff at end
- if ncol > 0 .and. nrow > 0
- aadd(links_, { nrow, ncol, coop } )
- endif
- enddo
-
- //───── if there was an entry error, the buffer may not be
- //───── empty -- test for it now
- if empty(CURR_CELL[CONTENTS])
- CellLinks(links_)
- else
-
- //───── clear out the formula, which is now a moot point
- CURR_CELL[CONTENTS] := CURR_CELL[FORMULA] := NIL
- endif
- else
- //───── see if this character string is too wide for the column
- //───── if so, and if the cell to the right is empty, then we
- //───── embed a code block as the formula in the columns
- //───── to the right and change their contents accordingly
- //───── so as to display the entire character string
- x := len(trim(CURR_CELL[CONTENTS]))
- if x > b:getColumn(b:colPos):width .and. ;
- b:colPos < COLUMNS .and. empty(NEXT_CELL[CONTENTS])
- c := b:colPos // tracks the current column
- y := 0 // accumulates total width
-
- //───── note that loop can be broken by a non-empty cell
- do while y < x .and. empty(nums_[ele, c, CONTENTS])
- nums_[ele, c, FORMULA] := bspill
- nums_[ele, c, CONTENTS] := eval(bspill, ele, c)
- y += b:getColumn(c++):width
- enddo
- endif
- endif
-
- //───── otherwise, clear out any links that may exist
- //───── necessary step because this cell may have contained
- //───── a formula which just got obliterated by a constant
- elseif ( y := len(CURR_CELL[LINKS])) > 0
- RemoveLink(y)
-
-
- endif
-
- //───── check next cell formula for a code block, which indicates
- //───── a spillover -- this may no longer be necessary if the
- //───── character string has been shortened or changed to a numeric.
- if b:colPos < COLUMNS .and. valtype(NEXT_CELL[FORMULA]) == "B" .and. ;
- (valtype(CURR_CELL[CONTENTS]) != "C" .or. ;
- (valtype(CURR_CELL[CONTENTS]) == "C" .and. ;
- len(trim(CURR_CELL[CONTENTS])) <= b:getColumn(b:colPos):width))
- c := b:colPos
- do while SpillOver(ele, c)
- nums_[ele, c, CONTENTS] := NIL
- nums_[ele, c++, FORMULA] := NIL
- enddo
- endif
-
- //───── are there any dependencies from this cell?
- if ! empty(CURR_CELL[DEPENDENCIES])
- Recalc(ele, CURR_COLUMN)
- endif
- endcase
- if tagging
- b:colorRect( { TOP, LEFT, BOTTOM, RIGHT }, {4, 3} )
- dispend()
- endif
- enddo
- //───── only prompt user to save spreadsheet if not called as hot-key
- //───── in which case variable nLine would equal NIL
- if nline == NIL .and. yes_no("Save this spreadsheet")
- cfile := padr(cfile,12)
- boxget cfile prompt "Enter filename:" picture "@!" boxcolor 'w/rb' ;
- color '+w/rb,+w/rb'
- if lastkey() != K_ESC .and. ! empty(cfile)
- cfile := trim(cfile)
- waiton( { 'Saving to ' + cfile + '... please wait' }, .f.)
- gsavearray(nums_, cfile)
- waitoff()
- endif
- endif
- gfrestenv()
- set(_SET_SCOREBOARD, oldscore)
- setkey(K_F10, oldf10)
- #ifdef HELPSCREEN
- setkey(K_F1, oldf1)
- #endif
-
- //───── if there is a numeric GET currently active, paste
- //───── contents of current cell into it
- if curr_get != NIL .and. curr_get:type == "N" .and. ;
- valtype(CURR_CELL[CONTENTS]) == "N"
- curr_get:varPut(CURR_CELL[CONTENTS])
- endif
- return nil
-
- // end function GrumpCalc()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: PrintIt()
- Purpose: Print entire spreadsheet or highlighted region
- */
- static function printit(ntop, nleft, nbottom, nright)
- local x, y, c
- GFSaveSets()
- set(_SET_PRINTFILE, "blah.txt")
- set(_SET_PRINTER, .T.)
- set(_SET_CONSOLE, .F.)
- for x = ntop to nbottom
- for y = nleft to nright
- c := b:getColumn(y + 1)
- if nums_[x, y, CONTENTS] == NIL
- qqout(space(c:width))
- else
- qqout(alignment(nums_[x, y, CONTENTS], nums_[x, y, FORMULA], c))
- endif
- next
- qout()
- next
- GFRestSets()
- return nil
-
- // end static function PrintIt()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: AwSkipIt()
- Purpose: Custom skip function for TBrowsing arrays
- */
- static function AwSkipIt(ele, skip_cnt, maxval)
- local movement := 0
- /* moving forward */
- if skip_cnt > 0
- do while ele + movement < maxval .and. movement < skip_cnt
- movement++
- enddo
- /* moving backward */
- elseif skip_cnt < 0
- do while ele + movement > 1 .and. movement > skip_cnt
- movement--
- enddo
- endif
- ele += movement
- return movement
-
- // end static function AwSkipIt()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: EditCell()
- Purpose: Edit current cell
- */
-
- #define GETWIDTH maxcol() - 7
-
- static function editcell(arrows)
- local contents := CURR_CELL[CONTENTS], key, getlist := {}
-
- //──── set insert key to toggle both insert mode & cursor
- local oldins := setkey( K_INS, {|| setcursor( ;
- if(readinsert(! readInsert()), SC_NORMAL, SC_INSERT))} )
- local lreadexit := readexit(.t.)
- local oldleft, oldright
-
- //───── set left & right arrow keys to exit READ if specified
- if arrows
- oldleft := setkey(K_LEFT, { || stuffkeys(chr(K_ENTER)+chr(K_LEFT)) })
- oldright := setkey(K_RIGHT, { || stuffkeys(chr(K_ENTER)+chr(K_RIGHT)) })
- endif
-
- //───── if current cell is blank (indicating NIL), change to character
- //───── note: if formula is a code-block, it is a character spill-over
- //───── and must be treated as if it was empty
- if contents == NIL .or. valtype(CURR_CELL[FORMULA]) == "B"
- contents := space(GETWIDTH)
- //───── check for any other formulae besides @..SUMs
- elseif ! empty(CURR_CELL[FORMULA]) .and. left(CURR_CELL[FORMULA], 1) != "@"
- //───── if there is a justification formula, append it to front of text
- if trim(CURR_CELL[FORMULA]) $ ['"^]
- contents := trim(CURR_CELL[FORMULA]) + contents
- else
- contents := padr(CURR_CELL[FORMULA], GETWIDTH)
- endif
- //───── if current cell is numeric, must convert to character
- //───── so that the user can switch types at will
- elseif valtype(contents) == "N"
- contents := padr(ltrim(str(contents)), GETWIDTH)
- endif
-
- @ 1, 6 get contents picture '@K'
- //──── initial cursor setting based on current mode
- setcursor( if(readInsert(), SC_INSERT, SC_NORMAL) )
- read
- setcursor(0)
-
- //───── if empty, blank out this cell once again
- if empty(contents)
- CURR_CELL[CONTENTS] := CURR_CELL[FORMULA] := NIL
- //───── convert to numeric if it contains a numeric value
- elseif ( val(contents) != 0 .or. contents = "0" )
- CURR_CELL[CONTENTS] := val(trim(contents))
- CURR_CELL[FORMULA] := NIL
- else
- //───── check for justification formulae
- if left(contents, 1) $ ['"^]
- CURR_CELL[FORMULA] := left(contents, 1)
- //───── must trim the character data to the width of this column
- CURR_CELL[CONTENTS] := substr(contents, 2, b:getColumn(b:colPos):width)
- else
- CURR_CELL[CONTENTS] := contents
- //───── if this was a formula and you just entered a character
- //───── string, time to trash the formula
- if ! empty(CURR_CELL[FORMULA]) .and. left(contents, 1) != "+"
- CURR_CELL[FORMULA] := NIL
- endif
- endif
- endif
-
- setkey(K_INS, oldins) // reset INS key
- //───── reset left and right arrows if we changed them
- if arrows
- setkey(K_LEFT, oldleft)
- setkey(K_RIGHT, oldright)
- endif
- readexit(lreadexit) // reset READEXIT status
-
- //───── if we exited with an arrow key, pass it through
- key := lastkey()
- if key == K_UP .or. key == K_DOWN
- keyboard chr(key)
- endif
- return NIL
-
- // end static function EditCell()
- //────────────────────────────────────────────────────────────────────//
-
- /*
- Function: StuffKeys( <cstring> )
- Purpose: Used to allow left/right arrow keys to exit the GET
- */
- static function StuffKeys(c)
- keyboard c
- return nil
-
- // end static function EditCell()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: CellLinks()
- Purpose: Establish links/dependencies based on formula in current cell
- */
- static function CellLinks(links_)
- local nrow, ncol, nval := 0, x
-
- //───── loop through links array
- for x = 1 to len(links_)
- nrow := links_[x, ROW]
- ncol := links_[x, COLUMN]
-
- //───── do not allow circular references!!
- if nrow != ele .or. ncol != CURR_COLUMN
-
- //───── verify that this cell is not already in the links array
- if ascan(CURR_CELL[LINKS], ;
- { | cell | cell[ROW] == nrow .and. cell[COLUMN] == ncol} ) == 0
-
- //───── add this cell to the links array
- aadd(CURR_CELL[LINKS], links_[x] )
-
- //───── add current cell to dependencies array for linked cell
- aadd(nums_[nrow, ncol, DEPENDENCIES], { ele, CURR_COLUMN } )
- endif
- CalcGuts(nrow, ncol, links_[x, OPERATOR], @nval)
- endif
- next
- CURR_CELL[CONTENTS] := nval
- return nil
-
- // end static function CellLinks()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: Recalc()
- Purpose: Self-explanatory
- */
- static function Recalc(nrow, ncol)
- local ncells := len(nums_[nrow, ncol, DEPENDENCIES])
- local trow, tcol, x, nsum, oldrowpos := b:rowPos
- for x = 1 to ncells
- trow := nums_[nrow, ncol, DEPENDENCIES, x, 1]
- tcol := nums_[nrow, ncol, DEPENDENCIES, x, 2]
- nsum := 0
-
- //───── perform summation on links array
- aeval(nums_[trow, tcol, LINKS], { | cell | ;
- calcguts(cell[ROW], cell[COLUMN], cell[OPERATOR], @nsum) } )
- nums_[trow, tcol, CONTENTS] := nsum
-
- //───── switch to this row to force selective refresh
- b:rowPos := trow
- b:refreshCurrent()
-
- //───── recurse to resolve any dependencies from this cell
- if ! empty(nums_[trow, tcol, DEPENDENCIES])
- Recalc(trow, tcol)
- endif
- next
-
- //───── reset row pointer in TBrowse window
- b:rowPos := oldrowpos
- return nil
-
- // end static function Recalc()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: CalcGuts()
- Purpose: The guts of the recalculation
- Accepts row, column, operator, and counter
- */
- static function CalcGuts(nrow, ncol, coperator, ncounter)
- if valtype(nums_[nrow, ncol, CONTENTS]) == "N"
- do case
- case coperator == "+"
- ncounter += nums_[nrow, ncol, CONTENTS]
- case coperator == "-"
- ncounter -= nums_[nrow, ncol, CONTENTS]
- case coperator == "/"
- ncounter /= nums_[nrow, ncol, CONTENTS]
- case coperator == "*"
- ncounter *= nums_[nrow, ncol, CONTENTS]
- endcase
- endif
- return nil
-
- // end static function CalcGuts()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: Alignment()
- Purpose: Allow left/right-justified or centered character data
- Called from within each TBColumn data retrieval block
- */
- static function alignment(data, formula, column)
- local ret_val := data, width := column:width
- if valtype(formula) == "C"
-
- //───── look for justification formulae
- do case
- case formula == ['] // right justified
- ret_val := padr(trim(data), width)
- case formula == ["] // left justified
- ret_val := padl(trim(data), width)
- case formula = [^] // centered
- ret_val := padc(trim(data), width)
- otherwise // non-justification formula (@SUM, etc)
- ret_val := padl(ltrim(str(data)), width)
- endcase
- elseif valtype(data) == "N"
- ret_val := padl(ltrim(str(data)), width)
- endif
- return ret_val
-
- // end static function Alignment()
- //────────────────────────────────────────────────────────────────────//
-
-
- /*
- Function: ChangeWidth()
- Purpose: Change a column's width
- */
- static function changewidth()
- local c := b:getColumn(b:colPos), x, y
- x := c:width
- boxget x prompt "Enter new width:" picture '###' boxcolor 'w/rb' ;
- valid x > 0 .and. x < maxcol() - 10
- if lastkey() != K_ESC
-
- //───── change width instance variable
- c:width := x
-
- //───── change placeholder in last row of array to reflect new width
- nums_[ROWS + 1, CURR_COLUMN] := x
-
- //───── change heading so as to keep the letter centered
- c:heading := padc(chr(63 + b:colPos), x)
- b:configure()
- b:invalidate()
-
- //───── loop through all columns looking for spill-overs
- //───── if any are found, re-evaluate the formula code block
- //───── for all columns to the right so that the entire
- //───── character string continues to be properly displayed
- for x = 1 to ROWS
- if SpillOver(x, ( y := b:colPos - 1) )
- do while SpillOver(x, y)
- nums_[x, y++, CONTENTS] := eval(nums_[x, y, FORMULA], x, y)
- enddo
- endif
- next
- endif
- return nil
-
- // end static function ChangeWidth()
- //────────────────────────────────────────────────────────────────────//
-
-
- #ifdef HELPSCREEN
-
- /*
- HelpMe(): GrumpCalc() help screen
- */
- static function helpme
- local oldf1 := setkey(K_F1, NIL)
- gfsaveenv(.t., 0) // shut off cursor
- colorset(C_APICK_TAGGED)
- @ 0, 0, maxrow(), maxcol() box BOXFRAMES[5]
- SCRNCENTER(2, "Spreadsheet Help Screen")
- colorset(C_APICK_BOXOUTLINE)
- @ 4, 28 ssay "Move up a row unless already at top"
- @ 5, 28 ssay "Move down a row unless already at bottom"
- @ 6, 28 ssay "Move left a column"
- @ 7, 28 ssay "Move right a column"
- @ 8, 28 ssay "Move to top row of spreadsheet"
- @ 9, 28 ssay "Move to bottom row of spreadsheet"
- @10, 28 ssay "Move to top left cell of spreadsheet"
- @11, 28 ssay "Move to bottom right cell of spreadsheet"
- @12, 28 ssay "Move to leftmost column"
- @13, 28 ssay "Move to rightmost column"
- @14, 28 ssay "Pan right one screen"
- @15, 28 ssay "Pan left one screen"
- @16, 28 ssay "Change column width"
- @17, 28 ssay "Print all or part of spreadsheet"
- @18, 28 ssay "Enable or disable highlighting"
- @19, 28 ssay "Edit current cell"
- @20, 28 ssay "Edit current cell"
- SCRNCENTER(22, "Press spacebar for next help screen")
- colorset(C_APICK_CURRENT)
- @ 4, 13 ssay "Up Arrow"
- @ 5, 13 ssay "Down Arrow"
- @ 6, 13 ssay "Left Arrow"
- @ 7, 13 ssay "Right Arrow"
- @ 8, 13 ssay "PgUp"
- @ 9, 13 ssay "PgDn"
- @10, 13 ssay "Home"
- @11, 13 ssay "End"
- @12, 13 ssay "Ctrl-Left"
- @13, 13 ssay "Ctrl-Right"
- @14, 13 ssay "Tab"
- @15, 13 ssay "Shift-Tab"
- @16, 13 ssay "Alt-W"
- @17, 13 ssay "Alt-P"
- @18, 13 ssay "F10"
- @19, 13 ssay "Enter/F2"
- @20, 13 ssay "Letter/number"
- ginkey(0)
- colorset(C_APICK_BOXOUTLINE)
- scroll(1, 1, maxrow() - 1, maxcol() - 1, 0)
- @ 1, 5 ssay "You can specially justify character data with any of the following"
- @ 2, 5 ssay [three special characters: " to left-justify, ' to right-justify, or ^]
- @ 3, 5 ssay "to center the text in that column."
- @ 5,20 ssay ": A cell can refer to two or more other cells. To do"
- @ 6, 5 ssay "so, simply enter the addresses of the cells, being certain to precede"
- @ 7, 5 ssay [the first cell address by a plus sign ("+"). For example, "+A1+A2"]
- @ 8, 5 ssay "would contain the sum of cells A1 and A2."
- @10,34 ssay ": You may highlight a group of cells by"
- @11, 5 ssay "locating the beginning of the region and pressing F10. Then use the"
- @12, 5 ssay "arrow keys to move the cursor to the end of the region and press F10"
- @13, 5 ssay "again. You may then either print that highlighted region or paste the"
- @14, 5 ssay "sum of all its cells into a different cell. To paste the sum of the"
- @15, 5 ssay "region into a different cell, move to the target cell and press F10"
- @16, 5 ssay "for a third time."
- @18,13 ssay ": You may print either the entire spreadsheet, or the"
- @19, 5 ssay "highlighted region (if there is one), by pressing Alt-P."
- @21,17 ssay ": You may change the width of the current column by"
- @22, 5 ssay "pressing Alt-W. The default column width is 10. If the current column"
- @23, 5 ssay "has a width other than 10, it will be displayed at the top left corner."
- colorset(C_APICK_CURRENT)
- @ 5, 5 ssay "Cell References"
- @10, 5 ssay "Highlighting a Group of Cells"
- @18, 5 ssay "Printing"
- @21, 5 ssay "Column Width"
- inkey(0)
- gfrestenv()
- setkey(K_F1, oldf1)
- return nil
-
- * end static function HelpMe()
- *--------------------------------------------------------------------*
-
- #endif
-
- /*
- Function: MakeColumn()
- Author: Greg Lief
- Syntax: MakeColumn( <nCol> )
- Params: <nCol> is the appropriate column number
- Returns: Codeblock to serve as TBColumn:block instance variable
- Notes: This function takes advantage of Clipper 5.01's
- "detached locals". Detached locals are local variables
- in function that have been called by higher-level
- functions. If the lower-level function initializes a code
- block that refers to those local variables, the variables
- will remain alive as long as the code block remains alive,
- EVEN IF THE FUNCTION IS NOT IN THE CALLSTACK. In this
- situation, it means that we do not have to resort to
- macro substitution nor convoluted preprocessor directives.
- */
- static function makecolumn(num)
- return { || if(nums_[ele, num, CONTENTS] == NIL, '', ;
- Alignment(nums_[ele, num, CONTENTS], ;
- nums_[ele, num, FORMULA], ;
- b:getColumn( num + 1) )) }
-
- * end static function MakeColumn()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: RemoveLink()
- Author: Greg Lief
- Syntax: RemoveLink( <loop> )
- Params: <loop> is the length of the links array to be traversed
- Returns: Nada
- */
- static function removelink(count)
- local nrow, ncol, nval, x
- CURR_CELL[FORMULA] := NIL
- for x = 1 to count
- //───── determine coordinate of this linked cell
- nrow := CURR_CELL[LINKS, x, ROW]
- ncol := CURR_CELL[LINKS, x, COLUMN]
-
- //───── scan dependencies array for that cell
- nval := ascan(nums_[nrow, ncol, DEPENDENCIES], { | cell | ;
- cell[ROW] == ele .and. cell[COLUMN] == CURR_COLUMN} )
-
- //───── trash the now-bogus reference
- adel(nums_[nrow, ncol, DEPENDENCIES], nval)
- asize(nums_[nrow, ncol, DEPENDENCIES], ;
- len(nums_[nrow, ncol, DEPENDENCIES]) - 1 )
- next
- //───── trash the links for current cell
- asize(CURR_CELL[LINKS], 0)
- return nil
-
- * end static function RemoveLink()
- *--------------------------------------------------------------------*
-
- * eof grumcalc.prg
-