home *** CD-ROM | disk | FTP | other *** search
- /*
- Program: SETFILT.PRG
- System: GRUMPFISH LIBRARY
- Author: Greg Lief
- Copyright (c) 1988-90, Greg Lief
- Clipper 5.01 Version
- Compile instructions: clipper setfilt /n/w/a
-
- The monstrous interactive query builder
- */
-
- //───── begin preprocessor directives
-
- #include "grump.ch"
- #include "inkey.ch"
- #include "dbstruct.ch"
-
- //───── end preprocessor directives
-
- //───── begin global declarations
-
- /* the following three items are declared here because they must
- be visible within several functions */
- static mfilter // the filter condition being built
- static mefilter // english translation of same
- static fieldinfo // array of info for the current field
-
- /* manifest constants to make things more readable */
- #define FieldName fieldinfo[DBS_NAME]
- #define FieldType fieldinfo[DBS_TYPE]
- #define FieldLen fieldinfo[DBS_LEN]
-
- /* manifest constants for return values from AddFilt() to NewFilt() */
- #define DISCARDED 1
- #define DONE 8
- #translate ShowFilt() => memoedit(mefilter, 04, 01, 17, 37, .f., .f.)
-
- //───── end global declarations
- external soundex
-
- function setfilt
- local malias, run_it_now, oldscore := set(_SET_SCOREBOARD, .f.), ;
- maincolor := ColorSet(C_APICK_BOXOUTLINE, .T.), browse, column, ;
- mcount, buffer, oldcolor, key, oldfkeys_
-
- if empty( malias := upper(alias()) )
- err_msg("No database open")
- else
- //───── must disable F7, F8, F9, F10 because they are used in my main
- //───── keypress loop below
- oldfkeys_ := { setkey(K_F7, NIL), setkey(K_F8, NIL), ;
- setkey(K_F9, NIL), setkey(K_F10, NIL) }
- GFSaveEnv(.t., 0) // shut off cursor
- cls
- if ! file('queries.dbf') .or. ! file('queries.dbt')
- waiton('initializing query file... please wait')
- dbcreate('queries.dbf', { { "DESCRIP", "C", 50, 0 } , ;
- { "MFILTER", "M", 10, 0 } , ;
- { "MEFILTER", "M", 10, 0 } , ;
- { "QUERY_FILE","C", 8, 0 } } )
- waitoff()
- endif
- use queries new
-
- //───── give user choice of existing queries first
- //───── create new browse object
- browse := TBrowseDB( 5, 15, 19, 64)
- browse:headSep := "═"
- browse:colorSpec := maincolor + ',' + if(iscolor(), '+W/N', 'I') + ;
- ',W/B, N/W'
- column := TBColumnNew( "Query Description", { | | queries->descrip } )
- browse:addColumn(column)
-
- @ 24,03 ssay 'move'
- @ 24,14 ssay 'count'
- @ 24,25 ssay 'delete'
- @ 24,37 ssay 'view'
- @ 24,48 ssay 'new query'
- @ 24,63 ssay 'select'
- @ 24,76 ssay 'exit'
- setcolor('I')
- @ 24,00 ssay chr(24)+chr(25)
- @ 24,11 ssay 'F7'
- @ 24,22 ssay 'F8'
- @ 24,34 ssay 'F9'
- @ 24,44 ssay 'F10'
- @ 24,60 ssay chr(17) + "┘"
- @ 24,72 ssay 'Esc'
- ColorSet(C_APICK_BOXOUTLINE)
- set filter to trim(queries->query_file) == malias
- go top
- shadowbox(04, 14, 20, 65, 3)
- 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
- do while ! browse:stabilize() .and. (key := inkey()) = 0
- enddo
-
- if browse:stable
- key := ginkey(0, "KEY")
- endif
-
- //───── deal with the keypress
- do case
-
- case key == K_UP
- browse:up()
-
- case key == K_LEFT
- browse:left()
-
- case key == K_RIGHT
- browse:right()
-
- case key == K_DOWN
- browse:down()
-
- case key == K_CTRL_PGUP
- browse:goTop()
-
- case key == K_CTRL_PGDN
- browse:goBottom()
-
- case key == K_PGUP .or. key == K_HOME
- browse:pageUp()
-
- case key == K_PGDN .or. key == K_END
- browse:pageDown()
-
- case key == K_ESC
- exit
-
- case key == K_ENTER
- if eof()
- err_msg("No query to select!")
- else
- if ! empty(queries->mfilter)
- select(select(malias))
- waiton('Searching records.. please wait')
- dbsetfilter(&("{ || " + queries->mfilter + "}"), ;
- queries->mfilter )
- go top
- if eof()
- err_msg('No records meet those criteria')
- set filter to
- select queries
- go top
- waitoff()
- else
- exit
- endif
- else
- select(select(malias))
- go top
- exit
- endif
- endif
-
- case key == K_F7 // count records for this query
- if eof()
- err_msg('No query to count')
- else
- select(select(malias))
- waiton('Now counting records.. please wait')
- go top
- //───── if the filter condition is empty, then the "filter" is
- //───── a moot point -- show 'em total # of records
- if empty(queries->mfilter)
- mcount := lastrec()
- else
- mfilter := &("{ | | " + queries->mfilter + "}" )
- count for eval(mfilter) to mcount
- endif
- oldcolor := ColorSet(C_WAITMESSAGE)
- @ 12, 23 ssay padr( str(mcount, 8) + ' record' + ;
- if(mcount > 1, 's', '') + ;
- ' for this criteria', 34)
- ginkey(0)
- waitoff()
- setcolor(oldcolor)
- select queries
- endif
-
- case key == K_F8 // delete query
- if eof()
- err_msg('No query to delete')
- else
- if yes_no('Are you sure you want to delete this query')
- if rlock()
- delete
- use queries exclusive
- if ! neterr()
- waiton('deleting query.... please wait')
- pack
- copy structure to tempqry
- use tempqry exclusive
- append from queries
- ferase('queries.dbf')
- ferase('queries.dbt')
- use
- frename('tempqry.dbf', 'queries.dbf')
- frename('tempqry.dbt', 'queries.dbt')
- use queries
- set filter to trim(queries->query_file) == malias
- go top
- waitoff()
- browse:refreshAll()
- else
- err_msg(NETERR_MSG)
- endif
- else
- err_msg(NETERR_MSG)
- endif
- unlock
- endif
- endif
-
- case key == K_F9 // view criteria
- if eof()
- err_msg('No query to view')
- else
- oldcolor := ColorSet(C_ERRORMESSAGE)
- setcolor('w/r')
- buffer := shadowbox(07, 25, 17, 54, 4, 'Query Criteria')
- @ 17,30 ssay '┤ press Esc to exit ├'
- memoedit(queries->mefilter, 8, 26, 16, 53, .f.)
- setcolor(oldcolor)
- byebyebox(buffer)
- endif
-
- case key == K_F10 // create new query
- buffer := savescreen(0, 0, 24, 79)
- select(select(malias))
- run_it_now := NewFilt(malias)
- restscreen(0, 0, 24, 79, buffer)
- go top
- if run_it_now
- select(select(malias))
- waiton('Searching records.. please wait')
- if ! empty(mfilter)
- mfilter := &("{ | | " + mfilter + "}" )
- set filter to eval(mfilter)
- endif
- go top
- if eof()
- err_msg('No records meet those criteria')
- set filter to
- select queries
- go top
- else
- exit
- endif
- endif
- browse:refreshAll()
-
- endcase
- enddo
-
- GFRestEnv()
- select queries
- use
- if len(malias) > 0
- select(select(malias))
- endif
-
- //───── reset F7, F8, F9, F10 keys to their previous values
- for key := 1 to 4
- setkey(-(key + 5), oldfkeys_[key])
- next
- endif
- return NIL
-
- * end function SetFilt()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: NewFilt()
- */
- static function NewFilt(malias)
- local botrow, returncode, run_it_now := .f., oldcolor := setcolor(), ;
- firstloop := .t., browse, column, key, mdescrip := space(50), ;
- marker := recno(), fields_ := dbstruct(), ele := 1, getlist := {}
- //───── initialize filter condition and english translation
- mfilter := []
- mefilter := 'ALL RECORDS'
-
- //───── use phantom record to get initial value for this field
- go bott
- skip
-
- setcolor('w/n')
- cls
- setcolor('w/b')
- @0,33 ssay " query builder "
- exbox(3, 0, 18, 38, 2, 0, '', .f., 'selection criteria')
- @ 04,01 ssay mefilter
- botrow := min(fcount() + 2, 23)
- SINGLEBOX(20, 00, 24, 25)
- @ 21,05 ssay 'Move Highlight Bar'
- @ 22,06 ssay 'To Select An Item'
- @ 23,06 ssay 'To End Selection'
- ShadowBox(1, 54, botrow, 67, 4, 'Fields')
- @ 21,02 ssay chr(24)+chr(25) color 'I'
- @ 22,02 ssay chr(17)+"─┘" color 'I'
- @ 23,02 ssay 'Esc' color 'I'
- firstloop := .T.
- //───── create new browse object
- browse := TBrowseNew( 2, 55, botrow - 1, 66)
- browse:colorSpec := '+W/B,' + if(iscolor(), "+W/N", "I") + ',W/B, W/B'
- browse:skipBlock := { |SkipCnt| AwSkipIt(@ele, SkipCnt, len(fields_)) }
- column := TBColumnNew( "", { | | fields_[ele][DBS_NAME] } )
- column:width = 10
- column:colorBlock := {|| if(fields_[ele][DBS_TYPE] != "M", {1, 2}, {3, 4} ) }
- browse:addColumn(column)
- 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
- do while ! browse:stabilize() .and. (key := inkey()) = 0
- enddo
-
- if browse:stable
- ShowFilt()
- key := ginkey(0, "KEY")
- endif
-
- //───── deal with the keypress
- do case
-
- case key == K_UP
- browse:up()
-
- case key == K_DOWN
- browse:down()
-
- case key == K_ENTER .and. fields_[ele][DBS_TYPE] != "M"
- //───── dump this particular array element to a mini-array!
- //───── note that fieldinfo is declared as an external static
- //───── at the top of this program because we need to have it
- //───── visible in the hot-key function VIEW_VALS
- fieldinfo := fields_[ele]
- if (returncode := AddFilt(fieldinfo)) = DONE
- exit
- endif
- if firstloop .and. returncode > DISCARDED
- firstloop := .f.
- endif
-
- case key == K_ESC
- exit
-
- endcase
- enddo
- go marker
- setcolor('w/b')
- ShowFilt()
- select queries
- ColorSet(C_MESSAGE)
- shadowbox(09, 07, 15, 72, 2)
- @ 10,09 ssay 'You may now enter a description of up to 50 characters for'
- @ 11,09 ssay 'this query. If you wish to run this query immediately without'
- @ 12,09 ssay 'saving it, leave the description empty and press Enter. If '
- @ 13,09 ssay 'you want to exit without saving this query, press Esc.'
- @ 14,15 get mdescrip
- setcursor(1)
- read
- setcursor(0)
- if lastkey() != K_ESC // if user did not press esc
- if len(trim(mdescrip)) == 0 // left it blank -- do not save it
- run_it_now := .t. // to run query immediately - see querybrow()
- else
- //───── force this record to be added with the following 'endless loop'
- do while .t.
- append blank
- if ! neterr()
- replace queries->descrip with mdescrip, ;
- queries->query_file with malias, ;
- queries->mfilter with mfilter, ;
- queries->mefilter with mefilter
- exit
- endif
- enddo
- endif
- endif
- setcolor(oldcolor)
- return run_it_now
-
- * end static function NewFilt()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: AwSkipIt()
- Purpose: Custom Skip UDF for TBROWSE() above
- Author: Greg Lief
- */
- static function AwSkipIt(ele, skip_cnt, maxval)
- local movement // this will be returned to TBROWSE
- //───── increment the current element pointer by the appropriate amount
- if skip_cnt >= 0 .and. ele + skip_cnt > maxval
- movement := maxval - ele
- ele := maxval
- elseif skip_cnt < 0 .and. ele + skip_cnt < 1
- movement := 1 - skip_cnt
- ele := 1
- else
- movement := skip_cnt
- ele += skip_cnt
- endif
- return movement
-
- * end static function AwSkipIt()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: ADDFILT()
- */
- static function AddFilt(fieldinfo)
-
- /* arrays to be used when selecting fields and operators */
- static op_string := { '=', '<', '>', '<=', '>=', '<>' }
- static operators := { 'Equal to', 'Less than', 'Greater than', ;
- 'Less than or equal to', 'Greater than or equal to', 'Not equal to', ;
- 'Contains', 'Does not contain', 'Sounds like' }
- static op_choices := { .t., .t., .t., .t., .t., .t., .t., .t., .t. }
- static booleans := { 'Discard', ' .AND. ', ' .OR. ', ' .AND. (', ' .OR. (', ;
- ') .AND. ', ') .OR. ', '<<done>>' }
- static bl_choices := { .t., .t., .t., .t., .t., .t., .t., .t. }
- static openparen := 0 // number of open parentheses in filter criteria
- local buffer := savescreen(2, 1, 20, 74), buffer1, mvalue, mfield, ;
- mefield, wid, boxbott, op, mop, meop, mboolean, pic_len, mpic, ;
- mplain := ColorSet(C_APICK_BOXOUTLINE, .T.) + ',I', oldaltv
- local menhanced := '+' + mplain, getlist := {}
- mvalue := fieldget(fieldpos(FieldName))
- do case
- case FieldType == 'C'
- if len(mvalue) > 35
- mpic := "@S35"
- pic_len := 35
- else
- mpic := replicate("X", (pic_len := len(mvalue)) )
- endif
-
- case FieldType == 'D'
- pic_len := 8
- mpic := "@D"
-
- case FieldType == 'N'
- op := str(mvalue)
- if "." $ op
- mpic := replicate('9', at(".", op) - 1) + "."
- mpic += replicate('9', len(op) - len(mpic))
- else
- mpic := replicate('9', len(op))
- endif
- pic_len := len(mpic)
-
- case FieldType == 'L'
- pic_len := 1
- mpic := "Y"
- endcase
- mop := ' = '
- meop := ' equal to '
- if FieldType != 'L' // get operator for non-logical fields
- setcolor(if(iscolor(), '+w/bg', 'w/n'))
- boxbott := if(FieldType == 'C', 13, 10)
- buffer1 := shadowbox(2, 47, boxbott - 1, 72, 3, fieldinfo[DBS_NAME])
- op := 0
- //───── only permit access to soundex() and substr() choices if this
- //───── is a character variable -- logical enough, eh?
- op_choices[7] := op_choices[8] := op_choices[9] := (FieldType = 'C')
- do while op == 0
- op := achoice(3, 48, boxbott - 2, 71, operators, op_choices)
- enddo
- ByeByeBox(buffer1)
- meop := ' ' + trim(operators[op]) + ' '
- if op < 7
- mop := ' ' + op_string[op]
- endif
- endif
- wid := max(len(FieldName) + len(meop) + pic_len, 30)
- ColorSet(C_MESSAGE)
- ShadowBox(05, 68 - wid, 08, 72, 4)
- /*
- establish ALT_V as hot-key for viewing values in database. Please
- note the passing of MVALUE by reference in the code block. This
- is a very sneaky way of passing a LOCAL variable to another function!
- */
- oldaltv := setkey( K_ALT_V, {| p, l, v | View_Vals(p, l, @mvalue)} )
- @ 07, 42 ssay '(Alt-V for available values)'
- @ 06, 70-wid ssay FieldName + meop
- @ 06, col() get mvalue picture mpic
- setcursor(1)
- read
- setcursor(0)
- setkey( K_ALT_V, oldaltv) // restore ALT_V to its previous state
- do case
- case FieldType == 'N'
- mfield := FieldName + mop + ltrim(str(mvalue))
- mefield := FieldName + meop + ltrim(str(mvalue))
- case FieldType == 'L'
- mfield := if(mvalue, FieldName, '! ' + FieldName)
- mefield := if(mvalue, FieldName, 'not ' + FieldName)
- case FieldType == 'D'
- mfield := FieldName + mop + "ctod('" + dtoc(mvalue) + "')"
- mefield := FieldName + meop + dtoc(mvalue)
- otherwise
- //───── criteria 7 and 8 ('contains' and 'sounds like') are special cases
- //───── and must be processed a bit differently than the first six
- do case
- case op == 7 // 'contains'
- mfield := '[' + trim(mvalue) + '] $ ' + FieldName
- case op == 8 // 'does not contain'
- mfield := '! [' + trim(mvalue) + '] $ ' + FieldName
- case op == 9 // 'sounds like'
- mfield := 'soundex(' + FieldName + ') = soundex([' + ;
- trim(mvalue) + '])'
- otherwise
- mfield := FieldName + mop + "[" + trim(mvalue) + "]"
- endcase
- mefield := FieldName + meop + "'" + trim(mvalue) + "'"
- endcase
- ColorSet(C_ERRORMESSAGE)
- shadowbox(10, 56, 19, 65, 3)
- mboolean := 0
- bl_choices[6] := bl_choices[7] := openparen > 0
- //───── force them to make a selection
- do while mboolean == 0
- mboolean := achoice(11, 57, 18, 64, booleans, bl_choices)
- enddo
- if mboolean == 8
- //───── add the required number of closed parenthesis to balance it out
- mfilter += mfield + replicate(')', openparen)
- mefilter := strtran(mefilter,'ALL RECORDS', '') + mefield + ;
- replicate(')', openparen)
- elseif mboolean > 1 // selection 1 means they want to discard this condition
- openparen += if(mboolean = 4 .or. mboolean = 5, 1, ;
- if(mboolean > 5, -1, 0))
- mfilter += mfield + booleans[mboolean]
- mefilter := strtran(mefilter, 'ALL RECORDS', '') + mefield + ;
- booleans[mboolean]
- setcolor(mplain)
- @ 23,02 ssay space(23)
- endif
- restscreen(02, 01, 20, 74, buffer)
- return mboolean
-
- * end static function AddFilt()
- *--------------------------------------------------------------------*
-
-
- /*
- Function: VIEW_VALS
- Hot key (Alt-V) to pop up field values for quick reference
- */
- static function view_vals(p, l, v)
- local browse, column, key, buffer, marker := recno(), ;
- oldblock := setkey( K_ALT_V, NIL )
- gfsaveenv(, 0, '+W/B') // shut off cursor and change color
- buffer := shadowbox(10, 70 - min(FieldLen, 70), 20, 71, 2)
- browse := TBrowseDB(11, 71 - min(FieldLen, 70), 19, 70)
- browse:headSep := "═"
- browse:colorSpec := '+W/B,' + if(iscolor(), '+W/N', 'I') + ',W/B, N/W'
- column := TBColumnNew( "Value", FieldBlock(FieldName) )
- browse:addColumn(column)
- go top
- 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
- do while ! browse:stabilize() .and. (key := inkey()) = 0
- enddo
-
- if browse:stable
- key := ginkey(0, "KEY")
- endif
-
- //───── deal with the keypress
- do case
-
- case key == K_UP
- browse:up()
-
- case key == K_DOWN
- browse:down()
-
- case key == K_CTRL_PGUP
- browse:goTop()
-
- case key == K_CTRL_PGDN
- browse:goBottom()
-
- case key == K_PGUP .or. key == K_HOME
- browse:pageUp()
-
- case key == K_PGDN .or. key == K_END
- browse:pageDown()
-
- case key == K_ESC
- exit
-
- case key == K_ENTER
- exit
-
- endcase
- enddo
- if lastkey() != K_ESC
- v := fieldget(fieldpos(FieldName))
- endif
- go marker
- byebyebox(buffer)
- gfrestenv()
- setkey(K_ALT_V, oldblock) // reset Alt-V for next time
- return NIL
-
- * end static function View_Vals()
- *--------------------------------------------------------------------*
-
- * eof setfilt.prg
-