home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 5 / 05.iso / a / a067 / 1.img / GRUMP501.EXE / SETFILT.PRG < prev    next >
Encoding:
Text File  |  1991-08-24  |  19.9 KB  |  626 lines

  1. /*
  2.    Program: SETFILT.PRG
  3.    System: GRUMPFISH LIBRARY
  4.    Author: Greg Lief
  5.    Copyright (c) 1988-90, Greg Lief
  6.    Clipper 5.01 Version
  7.    Compile instructions: clipper setfilt /n/w/a
  8.  
  9.    The monstrous interactive query builder
  10. */
  11.  
  12. //───── begin preprocessor directives
  13.  
  14. #include "grump.ch"
  15. #include "inkey.ch"
  16. #include "dbstruct.ch"
  17.  
  18. //───── end preprocessor directives
  19.  
  20. //───── begin global declarations
  21.  
  22. /* the following three items are declared here because they must
  23.    be visible within several functions                           */
  24. static mfilter            // the filter condition being built
  25. static mefilter           // english translation of same
  26. static fieldinfo          // array of info for the current field
  27.  
  28. /* manifest constants to make things more readable */
  29. #define FieldName  fieldinfo[DBS_NAME]
  30. #define FieldType  fieldinfo[DBS_TYPE]
  31. #define FieldLen   fieldinfo[DBS_LEN]
  32.  
  33. /* manifest constants for return values from AddFilt() to NewFilt() */
  34. #define DISCARDED 1
  35. #define DONE      8
  36. #translate ShowFilt() => memoedit(mefilter, 04, 01, 17, 37, .f., .f.)
  37.  
  38. //───── end global declarations
  39. external soundex
  40.  
  41. function setfilt
  42. local malias, run_it_now, oldscore := set(_SET_SCOREBOARD, .f.), ;
  43.       maincolor := ColorSet(C_APICK_BOXOUTLINE, .T.), browse, column, ;
  44.       mcount, buffer, oldcolor, key, oldfkeys_
  45.  
  46. if empty( malias := upper(alias()) )
  47.    err_msg("No database open")
  48. else
  49.    //───── must disable F7, F8, F9, F10 because they are used in my main
  50.    //───── keypress loop below
  51.    oldfkeys_ := { setkey(K_F7, NIL), setkey(K_F8, NIL), ;
  52.                   setkey(K_F9, NIL), setkey(K_F10, NIL) }
  53.    GFSaveEnv(.t., 0)       // shut off cursor
  54.    cls
  55.    if ! file('queries.dbf') .or. ! file('queries.dbt')
  56.       waiton('initializing query file... please wait')
  57.       dbcreate('queries.dbf', { { "DESCRIP",   "C", 50, 0 } , ;
  58.                               { "MFILTER",   "M", 10, 0 } , ;
  59.                               { "MEFILTER",  "M", 10, 0 } , ;
  60.                               { "QUERY_FILE","C",  8, 0 } } )
  61.       waitoff()
  62.    endif
  63.    use queries new
  64.  
  65.    //───── give user choice of existing queries first
  66.    //───── create new browse object
  67.    browse := TBrowseDB( 5, 15, 19, 64)
  68.    browse:headSep := "═"
  69.    browse:colorSpec := maincolor + ',' + if(iscolor(), '+W/N', 'I') + ;
  70.                        ',W/B, N/W'
  71.    column := TBColumnNew( "Query Description", { | | queries->descrip } )
  72.    browse:addColumn(column)
  73.  
  74.    @ 24,03 ssay 'move'
  75.    @ 24,14 ssay 'count'
  76.    @ 24,25 ssay 'delete'
  77.    @ 24,37 ssay 'view'
  78.    @ 24,48 ssay 'new query'
  79.    @ 24,63 ssay 'select'
  80.    @ 24,76 ssay 'exit'
  81.    setcolor('I')
  82.    @ 24,00 ssay chr(24)+chr(25)
  83.    @ 24,11 ssay 'F7'
  84.    @ 24,22 ssay 'F8'
  85.    @ 24,34 ssay 'F9'
  86.    @ 24,44 ssay 'F10'
  87.    @ 24,60 ssay chr(17) + "┘"
  88.    @ 24,72 ssay 'Esc'
  89.    ColorSet(C_APICK_BOXOUTLINE)
  90.    set filter to trim(queries->query_file) == malias
  91.    go top
  92.    shadowbox(04, 14, 20, 65, 3)
  93.    do while .t.
  94.  
  95.       //───── wait for the display to stabilize, which will
  96.       //───── loop once for each row in the browse window.
  97.       //───── allow a keypress to bust out of this loop
  98.       do while ! browse:stabilize() .and. (key := inkey()) = 0
  99.       enddo
  100.  
  101.       if browse:stable
  102.          key := ginkey(0, "KEY")
  103.       endif
  104.  
  105.       //───── deal with the keypress
  106.       do case
  107.  
  108.          case key == K_UP
  109.             browse:up()
  110.  
  111.          case key == K_LEFT
  112.             browse:left()
  113.  
  114.          case key == K_RIGHT
  115.             browse:right()
  116.  
  117.          case key == K_DOWN
  118.             browse:down()
  119.  
  120.          case key == K_CTRL_PGUP
  121.             browse:goTop()
  122.  
  123.          case key == K_CTRL_PGDN
  124.             browse:goBottom()
  125.  
  126.          case key == K_PGUP .or. key == K_HOME
  127.             browse:pageUp()
  128.  
  129.          case key == K_PGDN .or. key == K_END
  130.             browse:pageDown()
  131.  
  132.          case key == K_ESC
  133.             exit
  134.  
  135.          case key == K_ENTER
  136.             if eof()
  137.                err_msg("No query to select!")
  138.             else
  139.                if ! empty(queries->mfilter)
  140.                   select(select(malias))
  141.                   waiton('Searching records.. please wait')
  142.                   dbsetfilter(&("{ || " + queries->mfilter + "}"), ;
  143.                               queries->mfilter )
  144.                   go top
  145.                   if eof()
  146.                      err_msg('No records meet those criteria')
  147.                      set filter to
  148.                      select queries
  149.                      go top
  150.                      waitoff()
  151.                   else
  152.                      exit
  153.                   endif
  154.                else
  155.                   select(select(malias))
  156.                   go top
  157.                   exit
  158.                endif
  159.             endif
  160.  
  161.          case key == K_F7     // count records for this query
  162.             if eof()
  163.                err_msg('No query to count')
  164.             else
  165.                select(select(malias))
  166.                waiton('Now counting records.. please wait')
  167.                go top
  168.                //───── if the filter condition is empty, then the "filter" is
  169.                //───── a moot point -- show 'em total # of records
  170.                if empty(queries->mfilter)
  171.                   mcount := lastrec()
  172.                else
  173.                   mfilter := &("{ | | " + queries->mfilter + "}" )
  174.                   count for eval(mfilter) to mcount
  175.                endif
  176.                oldcolor := ColorSet(C_WAITMESSAGE)
  177.                @ 12, 23 ssay padr( str(mcount, 8) + ' record' + ;
  178.                                   if(mcount > 1, 's', '') + ;
  179.                                   ' for this criteria', 34)
  180.                ginkey(0)
  181.                waitoff()
  182.                setcolor(oldcolor)
  183.                select queries
  184.             endif
  185.  
  186.          case key == K_F8                     // delete query
  187.             if eof()
  188.                err_msg('No query to delete')
  189.             else
  190.                if yes_no('Are you sure you want to delete this query')
  191.                   if rlock()
  192.                      delete
  193.                      use queries exclusive
  194.                      if ! neterr()
  195.                         waiton('deleting query.... please wait')
  196.                         pack
  197.                         copy structure to tempqry
  198.                         use tempqry exclusive
  199.                         append from queries
  200.                         ferase('queries.dbf')
  201.                         ferase('queries.dbt')
  202.                         use
  203.                         frename('tempqry.dbf', 'queries.dbf')
  204.                         frename('tempqry.dbt', 'queries.dbt')
  205.                         use queries
  206.                         set filter to trim(queries->query_file) == malias
  207.                         go top
  208.                         waitoff()
  209.                         browse:refreshAll()
  210.                      else
  211.                         err_msg(NETERR_MSG)
  212.                      endif
  213.                   else
  214.                      err_msg(NETERR_MSG)
  215.                   endif
  216.                   unlock
  217.                endif
  218.             endif
  219.  
  220.          case key == K_F9                     // view criteria
  221.             if eof()
  222.                err_msg('No query to view')
  223.             else
  224.                oldcolor := ColorSet(C_ERRORMESSAGE)
  225.                setcolor('w/r')
  226.                buffer := shadowbox(07, 25, 17, 54, 4, 'Query Criteria')
  227.                @ 17,30 ssay '┤ press Esc to exit ├'
  228.                memoedit(queries->mefilter, 8, 26, 16, 53, .f.)
  229.                setcolor(oldcolor)
  230.                byebyebox(buffer)
  231.             endif
  232.  
  233.          case key == K_F10                    // create new query
  234.             buffer := savescreen(0, 0, 24, 79)
  235.             select(select(malias))
  236.             run_it_now := NewFilt(malias)
  237.             restscreen(0, 0, 24, 79, buffer)
  238.             go top
  239.             if run_it_now
  240.                select(select(malias))
  241.                waiton('Searching records.. please wait')
  242.                if ! empty(mfilter)
  243.                   mfilter := &("{ | | " + mfilter + "}" )
  244.                   set filter to eval(mfilter)
  245.                endif
  246.                go top
  247.                if eof()
  248.                   err_msg('No records meet those criteria')
  249.                   set filter to
  250.                   select queries
  251.                   go top
  252.                else
  253.                   exit
  254.                endif
  255.             endif
  256.             browse:refreshAll()
  257.  
  258.       endcase
  259.    enddo
  260.  
  261.    GFRestEnv()
  262.    select queries
  263.    use
  264.    if len(malias) > 0
  265.       select(select(malias))
  266.    endif
  267.  
  268.    //───── reset F7, F8, F9, F10 keys to their previous values
  269.    for key := 1 to 4
  270.       setkey(-(key + 5), oldfkeys_[key])
  271.    next
  272. endif
  273. return NIL
  274.  
  275. * end function SetFilt()
  276. *--------------------------------------------------------------------*
  277.  
  278.  
  279. /*
  280.    Function: NewFilt()
  281. */
  282. static function NewFilt(malias)
  283. local botrow, returncode, run_it_now := .f., oldcolor := setcolor(), ;
  284.       firstloop := .t., browse, column, key, mdescrip := space(50), ;
  285.       marker := recno(), fields_ := dbstruct(), ele := 1, getlist := {}
  286. //───── initialize filter condition and english translation
  287. mfilter := []
  288. mefilter := 'ALL RECORDS'
  289.  
  290. //───── use phantom record to get initial value for this field
  291. go bott
  292. skip
  293.  
  294. setcolor('w/n')
  295. cls
  296. setcolor('w/b')
  297. @0,33 ssay " query builder "
  298. exbox(3, 0, 18, 38, 2, 0, '', .f., 'selection criteria')
  299. @ 04,01 ssay mefilter
  300. botrow := min(fcount() + 2, 23)
  301. SINGLEBOX(20, 00, 24, 25)
  302. @ 21,05 ssay 'Move Highlight Bar'
  303. @ 22,06 ssay 'To Select An Item'
  304. @ 23,06 ssay 'To End Selection'
  305. ShadowBox(1, 54, botrow, 67, 4, 'Fields')
  306. @ 21,02 ssay chr(24)+chr(25) color 'I'
  307. @ 22,02 ssay chr(17)+"─┘" color 'I'
  308. @ 23,02 ssay 'Esc' color 'I'
  309. firstloop := .T.
  310. //───── create new browse object
  311. browse := TBrowseNew( 2, 55, botrow - 1, 66)
  312. browse:colorSpec := '+W/B,' + if(iscolor(), "+W/N", "I") + ',W/B, W/B'
  313. browse:skipBlock := { |SkipCnt| AwSkipIt(@ele, SkipCnt, len(fields_)) }
  314. column := TBColumnNew( "", { | | fields_[ele][DBS_NAME] } )
  315. column:width = 10
  316. column:colorBlock := {|| if(fields_[ele][DBS_TYPE] != "M", {1, 2}, {3, 4} ) }
  317. browse:addColumn(column)
  318. do while .t.
  319.  
  320.    //───── wait for the display to stabilize, which will
  321.    //───── loop once for each row in the browse window.
  322.    //───── allow a keypress to bust out of this loop
  323.    do while ! browse:stabilize() .and. (key := inkey()) = 0
  324.    enddo
  325.  
  326.    if browse:stable
  327.       ShowFilt()
  328.       key := ginkey(0, "KEY")
  329.    endif
  330.  
  331.    //───── deal with the keypress
  332.    do case
  333.  
  334.       case key == K_UP
  335.          browse:up()
  336.  
  337.       case key == K_DOWN
  338.          browse:down()
  339.  
  340.       case key == K_ENTER .and. fields_[ele][DBS_TYPE] != "M"
  341.          //───── dump this particular array element to a mini-array!
  342.          //───── note that fieldinfo is declared as an external static
  343.          //───── at the top of this program because we need to have it
  344.          //───── visible in the hot-key function VIEW_VALS
  345.          fieldinfo := fields_[ele]
  346.          if (returncode := AddFilt(fieldinfo)) = DONE
  347.             exit
  348.          endif
  349.          if firstloop .and. returncode > DISCARDED
  350.             firstloop := .f.
  351.          endif
  352.  
  353.       case key == K_ESC
  354.          exit
  355.  
  356.    endcase
  357. enddo
  358. go marker
  359. setcolor('w/b')
  360. ShowFilt()
  361. select queries
  362. ColorSet(C_MESSAGE)
  363. shadowbox(09, 07, 15, 72, 2)
  364. @ 10,09 ssay 'You may now enter a description of up to 50 characters for'
  365. @ 11,09 ssay 'this query.  If you wish to run this query immediately without'
  366. @ 12,09 ssay 'saving it, leave the description empty and press Enter.  If '
  367. @ 13,09 ssay 'you want to exit without saving this query, press Esc.'
  368. @ 14,15 get mdescrip
  369. setcursor(1)
  370. read
  371. setcursor(0)
  372. if lastkey() != K_ESC            // if user did not press esc
  373.    if len(trim(mdescrip)) == 0   // left it blank -- do not save it
  374.       run_it_now := .t.          // to run query immediately - see querybrow()
  375.    else
  376.       //───── force this record to be added with the following 'endless loop'
  377.       do while .t.
  378.          append blank
  379.          if ! neterr()
  380.             replace queries->descrip with mdescrip, ;
  381.                     queries->query_file with malias, ;
  382.                     queries->mfilter with mfilter, ;
  383.                     queries->mefilter with mefilter
  384.             exit
  385.          endif
  386.       enddo
  387.    endif
  388. endif
  389. setcolor(oldcolor)
  390. return run_it_now
  391.  
  392. * end static function NewFilt()
  393. *--------------------------------------------------------------------*
  394.  
  395.  
  396. /*
  397.    Function: AwSkipIt()
  398.    Purpose:  Custom Skip UDF for TBROWSE() above
  399.    Author:   Greg Lief
  400. */
  401. static function AwSkipIt(ele, skip_cnt, maxval)
  402. local movement // this will be returned to TBROWSE
  403. //───── increment the current element pointer by the appropriate amount
  404. if skip_cnt >= 0 .and. ele + skip_cnt > maxval
  405.    movement := maxval - ele
  406.    ele := maxval
  407. elseif skip_cnt < 0 .and. ele + skip_cnt < 1
  408.    movement := 1 - skip_cnt
  409.    ele := 1
  410. else
  411.    movement := skip_cnt
  412.    ele += skip_cnt
  413. endif
  414. return movement
  415.  
  416. * end static function AwSkipIt()
  417. *--------------------------------------------------------------------*
  418.  
  419.  
  420. /*
  421.    Function: ADDFILT()
  422. */
  423. static function AddFilt(fieldinfo)
  424.  
  425. /* arrays to be used when selecting fields and operators */
  426. static op_string := { '=', '<', '>', '<=', '>=', '<>' }
  427. static operators := { 'Equal to', 'Less than', 'Greater than', ;
  428.        'Less than or equal to', 'Greater than or equal to', 'Not equal to', ;
  429.        'Contains', 'Does not contain', 'Sounds like' }
  430. static op_choices := { .t., .t., .t., .t., .t., .t., .t., .t., .t. }
  431. static booleans := { 'Discard', ' .AND. ', ' .OR. ', ' .AND. (', ' .OR. (', ;
  432.        ') .AND. ', ') .OR. ', '<<done>>' }
  433. static bl_choices := { .t., .t., .t., .t., .t., .t., .t., .t. }
  434. static openparen := 0       // number of open parentheses in filter criteria
  435. local buffer := savescreen(2, 1, 20, 74), buffer1, mvalue, mfield, ;
  436.       mefield, wid, boxbott, op, mop, meop, mboolean, pic_len, mpic, ;
  437.       mplain := ColorSet(C_APICK_BOXOUTLINE, .T.)  + ',I', oldaltv
  438. local menhanced := '+' + mplain, getlist := {}
  439. mvalue := fieldget(fieldpos(FieldName))
  440. do case
  441.    case FieldType == 'C'
  442.       if len(mvalue) > 35
  443.          mpic := "@S35"
  444.          pic_len := 35
  445.       else
  446.          mpic := replicate("X", (pic_len := len(mvalue)) )
  447.       endif
  448.  
  449.    case FieldType == 'D'
  450.       pic_len := 8
  451.       mpic := "@D"
  452.  
  453.    case FieldType == 'N'
  454.       op := str(mvalue)
  455.       if "." $ op
  456.          mpic := replicate('9', at(".", op) - 1) + "."
  457.          mpic += replicate('9', len(op) - len(mpic))
  458.       else
  459.          mpic := replicate('9', len(op))
  460.       endif
  461.       pic_len := len(mpic)
  462.  
  463.    case FieldType == 'L'
  464.       pic_len := 1
  465.       mpic := "Y"
  466. endcase
  467. mop := ' = '
  468. meop := ' equal to '
  469. if FieldType != 'L'       // get operator for non-logical fields
  470.    setcolor(if(iscolor(), '+w/bg', 'w/n'))
  471.    boxbott := if(FieldType == 'C', 13, 10)
  472.    buffer1 := shadowbox(2, 47, boxbott - 1, 72, 3, fieldinfo[DBS_NAME])
  473.    op := 0
  474.    //───── only permit access to soundex() and substr() choices if this
  475.    //───── is a character variable -- logical enough, eh?
  476.    op_choices[7] := op_choices[8] := op_choices[9] := (FieldType = 'C')
  477.    do while op == 0
  478.       op := achoice(3, 48, boxbott - 2, 71, operators, op_choices)
  479.    enddo
  480.    ByeByeBox(buffer1)
  481.    meop := ' ' + trim(operators[op]) + ' '
  482.    if op < 7
  483.       mop := ' ' + op_string[op]
  484.    endif
  485. endif
  486. wid := max(len(FieldName) + len(meop) + pic_len, 30)
  487. ColorSet(C_MESSAGE)
  488. ShadowBox(05, 68 - wid, 08, 72, 4)
  489. /*
  490.    establish ALT_V as hot-key for viewing values in database.  Please
  491.    note the passing of MVALUE by reference in the code block.  This
  492.    is a very sneaky way of passing a LOCAL variable to another function!
  493. */
  494. oldaltv := setkey( K_ALT_V, {| p, l, v | View_Vals(p, l, @mvalue)} )
  495. @ 07, 42 ssay '(Alt-V for available values)'
  496. @ 06, 70-wid ssay FieldName + meop
  497. @ 06, col() get mvalue picture mpic
  498. setcursor(1)
  499. read
  500. setcursor(0)
  501. setkey( K_ALT_V, oldaltv)     // restore ALT_V to its previous state
  502. do case
  503.    case FieldType == 'N'
  504.       mfield := FieldName + mop + ltrim(str(mvalue))
  505.       mefield := FieldName + meop + ltrim(str(mvalue))
  506.    case FieldType == 'L'
  507.       mfield := if(mvalue, FieldName, '! ' + FieldName)
  508.       mefield := if(mvalue, FieldName, 'not ' + FieldName)
  509.    case FieldType == 'D'
  510.       mfield := FieldName + mop + "ctod('" + dtoc(mvalue) + "')"
  511.       mefield := FieldName + meop + dtoc(mvalue)
  512.    otherwise
  513.       //───── criteria 7 and 8 ('contains' and 'sounds like') are special cases
  514.       //───── and must be processed a bit differently than the first six
  515.       do case
  516.          case op == 7        // 'contains'
  517.             mfield := '[' + trim(mvalue) + '] $ ' + FieldName
  518.          case op == 8        // 'does not contain'
  519.             mfield := '! [' + trim(mvalue) + '] $ ' + FieldName
  520.          case op == 9        // 'sounds like'
  521.             mfield := 'soundex(' + FieldName + ') = soundex([' + ;
  522.                      trim(mvalue) + '])'
  523.          otherwise
  524.             mfield := FieldName + mop + "[" + trim(mvalue) + "]"
  525.       endcase
  526.       mefield := FieldName + meop + "'" + trim(mvalue) + "'"
  527. endcase
  528. ColorSet(C_ERRORMESSAGE)
  529. shadowbox(10, 56, 19, 65, 3)
  530. mboolean := 0
  531. bl_choices[6] := bl_choices[7] := openparen > 0
  532. //───── force them to make a selection
  533. do while mboolean == 0
  534.    mboolean := achoice(11, 57, 18, 64, booleans, bl_choices)
  535. enddo
  536. if mboolean == 8
  537.    //───── add the required number of closed parenthesis to balance it out
  538.    mfilter += mfield + replicate(')', openparen)
  539.    mefilter := strtran(mefilter,'ALL RECORDS', '') + mefield + ;
  540.                replicate(')', openparen)
  541. elseif mboolean > 1    // selection 1 means they want to discard this condition
  542.    openparen += if(mboolean = 4 .or. mboolean = 5, 1, ;
  543.                 if(mboolean > 5, -1, 0))
  544.    mfilter += mfield + booleans[mboolean]
  545.    mefilter := strtran(mefilter, 'ALL RECORDS', '') + mefield + ;
  546.                booleans[mboolean]
  547.    setcolor(mplain)
  548.    @ 23,02 ssay space(23)
  549. endif
  550. restscreen(02, 01, 20, 74, buffer)
  551. return mboolean
  552.  
  553. * end static function AddFilt()
  554. *--------------------------------------------------------------------*
  555.  
  556.  
  557. /*
  558.     Function: VIEW_VALS
  559.     Hot key (Alt-V) to pop up field values for quick reference
  560. */
  561. static function view_vals(p, l, v)
  562. local browse, column, key, buffer, marker := recno(), ;
  563.       oldblock := setkey( K_ALT_V, NIL )
  564. gfsaveenv(, 0, '+W/B')    // shut off cursor and change color
  565. buffer := shadowbox(10, 70 - min(FieldLen, 70), 20, 71, 2)
  566. browse := TBrowseDB(11, 71 - min(FieldLen, 70), 19, 70)
  567. browse:headSep := "═"
  568. browse:colorSpec := '+W/B,' + if(iscolor(), '+W/N', 'I') + ',W/B, N/W'
  569. column := TBColumnNew( "Value", FieldBlock(FieldName) )
  570. browse:addColumn(column)
  571. go top
  572. do while .t.
  573.  
  574.    //───── wait for the display to stabilize, which will
  575.    //───── loop once for each row in the browse window.
  576.    //───── allow a keypress to bust out of this loop
  577.    do while ! browse:stabilize() .and. (key := inkey()) = 0
  578.    enddo
  579.  
  580.    if browse:stable
  581.       key := ginkey(0, "KEY")
  582.    endif
  583.  
  584.    //───── deal with the keypress
  585.    do case
  586.  
  587.       case key == K_UP
  588.          browse:up()
  589.  
  590.       case key == K_DOWN
  591.          browse:down()
  592.  
  593.       case key == K_CTRL_PGUP
  594.          browse:goTop()
  595.  
  596.       case key == K_CTRL_PGDN
  597.          browse:goBottom()
  598.  
  599.       case key == K_PGUP .or. key == K_HOME
  600.          browse:pageUp()
  601.  
  602.       case key == K_PGDN .or. key == K_END
  603.          browse:pageDown()
  604.  
  605.       case key == K_ESC
  606.          exit
  607.  
  608.       case key == K_ENTER
  609.          exit
  610.  
  611.    endcase
  612. enddo
  613. if lastkey() != K_ESC
  614.    v := fieldget(fieldpos(FieldName))
  615. endif
  616. go marker
  617. byebyebox(buffer)
  618. gfrestenv()
  619. setkey(K_ALT_V, oldblock)   // reset Alt-V for next time
  620. return NIL
  621.  
  622. * end static function View_Vals()
  623. *--------------------------------------------------------------------*
  624.  
  625. * eof setfilt.prg
  626.