home *** CD-ROM | disk | FTP | other *** search
- /*
- Program: APICK.PRG
- System: GRUMPFISH LIBRARY
- Author: Greg Lief
- Copyright (c) 1988-90, Greg Lief
- Clipper 5.01 Version
- Compile instructions: clipper apick /n/w/a
- */
-
-
- //───── begin preprocessor directives
-
- #include "inkey.ch"
- #include "grump.ch"
- #define ISCHAR(a) valtype(a) == "C"
-
- //───── end preprocessor directives
-
-
- //───── global declarations
-
- static searchstr // used for additive searches
-
- //───── end global declarations
-
- function apick(mtop, mleft, mbottom, mright, marray, cboxcolor, ;
- cbarcolor, cstatcolor, actual_len, ltagging, cunselected, ;
- chighlight, ele, cselected, cunavailable, ctitle, lwrap)
-
- local browse, column, key, xx, max_ele, temp, telem, oldscrn, maxwidth, ;
- choice, available := {}, drawthebar, bar_line, oldrow, midpoint
-
- GFSaveEnv(, 0) // shut off cursor
-
- /*
- establish colors for box, status bar, and indicator (see
- COLORSET.PRG for default settings). Pay attention to the second
- parameter -- this tells ColorSet() to merely return the default
- value rather than actually changing the color
- */
- default cboxcolor to ColorSet(C_APICK_BOXOUTLINE, .T.)
- default cbarcolor to ColorSet(C_APICK_STATUSBAR, .T.)
- default cstatcolor to ColorSet(C_APICK_INDICATOR, .T.)
- default cunselected to ColorSet(C_APICK_UNSELECTED, .T.)
- default chighlight to ColorSet(C_APICK_CURRENT, .T.)
- default cselected to ColorSet(C_APICK_TAGGED, .T.) + ',' + ;
- ColorSet(C_APICK_TAGGEDCURRENT, .T.)
- default cunavailable to ColorSet(C_APICK_UNAVAILABLE, .T.) + ',' + ;
- ColorSet(C_APICK_UNAVAILABLECURRENT, .T.)
- default ele to 1 // this will serve as our placeholder in the array
- default ctitle to '' // title for APICK() box
- default lwrap to .t. // enable wraparound from top to bottom of array
- default ltagging to .f. // do not allow tagging unless specified
- searchstr := [] // reset search string for additive search
-
- //───── determine # of last element by grabbing length of array,
- //───── unless of course the actual length was passed as parameter #9
- max_ele := if(valtype(actual_len) != "N", len(marray), actual_len)
-
- //───── determine maximum width for the column
- //───── use length of box title as a starting point
- maxwidth := len(ctitle) + 4
- aeval(marray, { | a | maxwidth := if(ISCHAR(a), ;
- MAX(maxwidth, len(a)), maxwidth) } )
-
- //───── if the maximum width was changed in the AEVAL() above, and
- //───── if we are allowing tagging, we must increment it by one so
- //───── that the checkmark will actually appear!
- if ltagging .and. maxwidth > len(ctitle) + 4
- maxwidth++
- endif
-
- //───── establish coordinates if not passed as parameters
- default mtop to if(max_ele > maxrow() - 2, 6, int((maxrow() - max_ele - 1) / 2))
- default mleft to int( maxcol() - maxwidth - 2) / 2
- default mbottom to if(max_ele > maxrow() - 2, maxrow() - 6, mtop + max_ele + 1)
- if mright = NIL .or. mright < mleft + maxwidth + 1
- mright = mleft + maxwidth + 1
- endif
-
- //───── if we are allowing the user to tag elements, we must add a space to
- //───── each array element to hold the ubiquitous checkmark (√)
- default ltagging to .f.
- if ltagging
- aeval(marray, { | a, b | if(ISCHAR(marray[b]), marray[b] += " ", NIL) } )
- endif
-
- //───── establish parallel array to hold logicals (selectable vs. unselectable)
- aeval(marray, { | a | aadd(available, ;
- if(ISCHAR(a), substr(a, 1, 1) != '~', .f.) ) } )
-
- //───── determine coordinates for displaying the search string
- midpoint := mleft + int((mright - mleft) / 2) - 5
-
- //───── Create tbrowse object set to the appropriate coordinates
- browse := TBrowseNew(mtop + 1, mleft + 1, mbottom - 1, mright - 1)
- browse:colorSpec := cunselected + "," + chighlight + "," + cselected + ;
- "," + cunavailable
- //───── establish navigation blocks for the tbrowse object
- browse:skipBlock := { |SkipCnt| AwSkipIt(@ele, SkipCnt, max_ele) }
- browse:goTopBlock := { || ele := 1 }
- browse:goBottomBlock := { || ele := max_ele }
-
- //───── look for horizontal lines -- any elements like so: "~─"
- for xx = 1 to max_ele
- if ISCHAR(marray[xx])
- if substr(marray[xx], 1, 1) = '~'
- marray[xx] = substr(marray[xx], 2)
- //───── see if they want to draw a horizontal line - if so,
- //───── trimmed length of this array element will now be one.
- if len(trim(marray[xx])) = 1
- marray[xx] := replicate(trim(marray[xx]), maxwidth)
- endif
- endif
- endif
- next
-
- //───── create column for the browse object
- column := TBColumnNew("", { | | marray[ele] } )
- column:width = maxwidth
-
- /*
- establish color setup - use the following pairs for these items:
- unselected/available options --> {1, 2}
- tagged/available options --> {3, 4}
- unavailable options --> {5, 6}
-
- these numbers correspond to colors as follows:
- 1) unselected 2) hilight 3) tagged (unselected)
- 4) tagged (selected) 5) unavailable (unselected) 6) unavailable (selected)
- */
- column:colorBlock := {|x| if(! available[ele], {5, 6}, ;
- if(right(x, 1) = chr(251), {3, 4}, {1, 2} ))}
-
- //───── add this column to the browse object and get ready for browsing fun
- browse:AddColumn( column )
-
- //───── draw box and status bar, using box title if it was passed
- setcolor(cboxcolor)
- oldscrn := ShadowBox(mtop, mleft, mbottom, mright, 1, ctitle)
- //───── draw status bar if not all of the array elements fit in the window
- bar_line := mtop + 2
- if (drawthebar := (max_ele > mbottom - mtop - 1))
- @ mtop + 1, mright, mbottom - 1, mright box chr(176) color cbarcolor
- endif
-
- /*
- If the initially highlighted element is NOT the first element of
- the array, we will send the appropriate number of down() method
- calls to the TBrowse object. This is so that the highlighted element
- is not shown as the top element in the window, which would present
- the misleading impression that there are no elements above it for
- selection. (Modification prompted by Dangerous Dave Harrington.)
- */
- if ele > 1
- do while ele-- > 1
- browse:down()
- enddo
- ele := 1
- endif
-
- do while .t.
-
- //───── wait for the display to stabilize, which will
- //───── loop once for each row in the browse window.
- //───── allow a keypress to bust out of this loop
- dispbegin()
- do while ! browse:stabilize() .and. (key := inkey()) = 0
- enddo
- dispend()
-
- if browse:stable .and. drawthebar
- oldrow := row()
- //───── draw arrows if there are elements beyond top or bottom of window
- //───── first the bottom
- @ mbottom, mright ssay if(max_ele - ele >= mbottom - oldrow, ;
- chr(25), chr(188)) color cboxcolor
- //───── then the top
- @ mtop,mright ssay if(oldrow - ele < mtop, chr(24), chr(187))
-
- //───── if status bar position has changed...
- if bar_line != mtop + 1 + ;
- int((ele / max_ele) * (mbottom - mtop - 2))
- //───── first, blank out previous status bar
- @ bar_line, mright ssay chr(176) color cbarcolor
- //───── then recalculate position of status bar
- bar_line := mtop + 1 + int((ele / max_ele) * ;
- (mbottom - mtop - 2))
- //───── finally, redraw it
- @ bar_line, mright ssay CHR(219) color cstatcolor
- endif
- endif
-
- if browse:stable
- key := ginkey(0, ele) // pass along array subscript
- endif
-
- //───── deal with the keypress
- do case
-
- case key == 32 .and. available[ele] .and. ltagging // tag 'em, Dan-O
- marray[ele] = left(marray[ele], ;
- len(marray[ele]) - 1) + ;
- if(right(marray[ele], 1) = chr(251), chr(32), chr(251))
- searchstr := []
- showstring(mbottom, midpoint, cboxcolor, mleft)
- //───── force redrawing this item to change its color
- browse:refreshCurrent()
-
- case key == K_F8 .and. ltagging // tag 'em all, Dan-O
- aeval(marray, { | a, b | marray[b] := if(available[b], ;
- left(marray[b], len(marray[b]) - 1) + chr(251), ;
- marray[b]) } )
- //───── force redrawing entire window to change color of all items
- browse:refreshAll()
-
- case key == K_F9 .and. ltagging // set 'em free, Dan-O
- aeval(marray, { | a, b | marray[b] := if(available[b], ;
- left(marray[b], len(marray[b]) - 1) + chr(32), ;
- marray[b]) } )
- //───── force redrawing entire window to change color of all items
- browse:refreshAll()
-
- case key == K_F10 .and. ltagging // switch all tags (Chinese Fire Drill)
- aeval(marray, { | a, b | marray[b] := if(available[b], ;
- left(marray[b], len(marray[b]) - 1) + ;
- if(right(marray[b], 1) = chr(32), chr(251), chr(32)), ;
- marray[b]) } )
- //───── force redrawing entire window to change color of all items
- browse:refreshAll()
-
- case key == K_UP // up one row
- //───── if we are already at the top element, wrap to bottom
- if ele == 1
- if lwrap
- browse:goBottom()
- endif
- else
- browse:up()
- endif
-
- case key == K_DOWN // down one row
- //───── if we are already at the bottom element, wrap to top
- if ele == max_ele
- if lwrap
- browse:goTop()
- endif
- else
- browse:down()
- endif
-
- case key == K_CTRL_PGUP // take it to the top, Jerome!
- browse:goTop()
-
- case key == K_CTRL_PGDN // goin' down.... down.... down ........
- browse:goBottom()
-
- case key == K_PGUP .or. key == K_HOME // top o' window
- browse:pageUp()
-
- case key == K_PGDN .or. key == K_END // bottom o' window
- browse:pageDown()
-
- case key == K_ESC // aloha, you quitter
- choice := 0
- exit
-
- case key > 31 .and. key < 255 // search 'em
- if (telem := Ascan2(marray, searchstr + chr(key))) > 0
- searchstr += chr(key)
- //───── if moving backwards (up) through the array,
- //───── we have to handle it manually
- if ele > telem
- for xx := 1 to ele - telem
- browse:up()
- next
- elseif ele != telem
- browse:refreshAll()
- ele := telem
- endif
- showstring(mbottom, midpoint, cboxcolor, mleft)
- endif
-
- case key == K_BS .OR. key == K_LEFT // truncate the search string
- if len(searchstr) > 0
- searchstr := substr(searchstr, 1, len(searchstr) - 1)
- if (telem := Ascan2(marray, searchstr)) > 0
- ele := telem
- browse:refreshAll()
- endif
- showstring(mbottom, midpoint, cboxcolor, mleft)
- endif
-
- case key = K_ENTER .and. available[ele] // select if available
- choice := ele
- exit
-
- endcase
- enddo
- ByeByeBox(oldscrn)
- GFRestEnv()
- return choice
-
- * end function APick()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: AwSkipIt()
- Purpose: Custom Skip UDF for TBROWSE() above
- */
- static function AwSkipIt(ele, skip_cnt, maxval)
- local movement := 0 // this will be returned to TBROWSE
- if skip_cnt > 0
- do while ele + movement < maxval .and. movement < skip_cnt
- movement++
- enddo
- elseif skip_cnt < 0
- if ele = 1 .and. skip_cnt = -1
- movement := maxval - 1
- else
- do while ele + movement > 1 .and. movement > skip_cnt
- movement--
- enddo
- endif
- endif
- ele += movement
- return movement
-
- * end static function AwSkipIt()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: ShowString()
- Purpose: Display the search string
- */
- static function showstring(row, col, ccolor, mleft)
- @ row, col ssay if(len(searchstr) == 0, ;
- if(col == mleft, chr(200), chr(205)) + ;
- replicate(chr(205), 9), ;
- "[" + pad(searchstr, 8) + "]") color ccolor
- return NIL
-
- * end static function ShowString()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: AScan2()
- Purpose: Perform case-insensitive ASCAN()
- */
- static function AScan2(array, value)
- return ascan(array, { | a | if(ISCHAR(a), upper(a) = upper(value), .F.) }, 1)
-
- * end static function AScan2()
- *--------------------------------------------------------------------*
-
- * eof apick.prg
-