home *** CD-ROM | disk | FTP | other *** search
- /*
- Listing 14.7 Displaying Pull-Down Menu with PULLDOWN()
- Author: Greg Lief
- Excerpted from "Clipper 5: A Developer's Guide"
- Copyright (c) 1991 M&T Books
- 501 Galveston Drive
- Redwood City, CA 94063-4728
- (415) 366-3600
- */
-
- //───── NOTE: must be compiled with /N compiler option
-
- #include "error.ch"
-
- static coords := {} // must be file-wide for PULLDOWN() to work properly
-
- #define TEST // remove this if you don't need the test code
-
- //───── begin stub test program
-
- #ifdef TEST
-
- function main
- local newhandler, oldhandler
- /*
- establish custom error handler to handle calls to functions
- that do not exist
- */
- newhandler := { | e | bogusfunc(e, oldhandler) }
- oldhandler := errorblock(newhandler)
- pulldown({ { "Data Entry", "Enter data, naturally", ;
- { { "Customers", , { || cust() } }, ;
- { "Invoices", , { || inv() } }, ;
- { "Vendors", , { || vendors() } }, ;
- { "Exit", "Return to Main Menu" } } }, ;
- { "Reports", "Share your information" , ;
- { { "Customer List", , { || custrep() } }, ;
- { "Past Due", , { || pastdue() } }, ;
- { "Weekly Sales", , { || weeksales() } }, ;
- { "Monthly P&L", , { || monthpl() } }, ;
- { "Vendor List", , { || vendorrep() } }, ;
- { "Exit", "Return to Main Menu" } } }, ;
- { "Maintenance", "Reindex files etc" , ;
- { { "Reindex", "Rebuild index files", { || re_ntx() } }, ;
- { "Backup", "Backup data files" , { || backup() } }, ;
- { "Compress", "Compress data files", { || compress() } }, ;
- { "Exit", "Return to Main Menu" } } }, ;
- { "Quit", "Take a siesta" } } )
- return nil
-
-
- /*
- BogusFunc(): custom error handler for bogus function calls
- */
- static function bogusfunc(e, oldhandler)
- if e:gencode() == EG_NOFUNC
- return .t.
- endif
- return eval(oldhandler, e)
-
-
- #endif
-
- //───── end stub test program
-
- /*
- PULLDOWN() - display pull-down menu
- */
- #include "box.ch"
- #include "inkey.ch"
- #define TOPROW 0
- #define LEFTCOL(x) coords[x, 1]
- #define BOTROW(x) coords[x, 2]
- #define RIGHTCOL(x) coords[x, 3]
-
- #define OPTION(x) prompts[x, 1]
- #define MESSAGE(x) prompts[x, 2]
- #define ACTION(x) prompts[x, 3]
-
- function pulldown(prompts)
- local mainsel := 1, x, y, oldwrap := set(_SET_WRAP, .T.), oldmessrow, maxwidth
- local nprompts := len(prompts), oldleft, oldright, oldcursor := setcursor(0)
- local ncol, subsel, buffer, tempsel
-
- //───── if no message row has been established, use bottom screen row
- if (oldmessrow := set(_SET_MESSAGE)) == 0
- set(_SET_MESSAGE, maxrow())
- endif
- //───── loop through menu prompts array / determine coordinates for each submenu
- ncol := 0 // placeholder for left column
- for x = 1 to nprompts
-
- //───── Is there a submenu for this menu option?
- //───── If so, it would contain four elements
- if len(prompts[x]) > 2 .and. valtype( ACTION(x) ) == "A"
-
- //───── determine length of widest submenu option
- maxwidth := 0
- aeval(ACTION(x), { | a | maxwidth := max(maxwidth, len(a[1])) } )
-
- //───── add left and right columns and bottom row for the submenu box
- //───── to COORDS array and increment left column accordingly
- aadd(coords, { ncol, len(ACTION(x)) + 2, ncol + 1 + maxwidth } )
-
- //───── pad all messages in submenu so that they will occupy the
- //───── entire bottom row... otherwise, messages from submenus
- //───── might not erase messages from the main menu
- for y = 1 to len(ACTION(x))
- if ACTION(x)[y, 2] != NIL
- ACTION(x)[y, 2] := padr(ACTION(x)[y, 2], maxcol())
- endif
- next
- else
-
- //───── no submenu -- add current left column to COORDS array
- //───── and increment left column by length of this menu option
- aadd(coords, { ncol } )
- endif
- ncol += len( OPTION(x) ) + 2
-
- //───── pad all messages so that they will occupy the entire bottom row
- if MESSAGE(x) != NIL
- MESSAGE(x) := padr(MESSAGE(x), maxcol())
- endif
- next
-
- //───── begin loop for top-level menu
- do while mainsel != 0
-
- //───── loop through prompts array and display each top-level prompt
- for x = 1 to nprompts
-
- //───── display message only if one exists
- if len(prompts[x]) > 1
- @ TOPROW, LEFTCOL(x) prompt OPTION(x) message MESSAGE(x)
- else
- @ TOPROW, LEFTCOL(x) prompt OPTION(x)
- endif
- next
- menu to mainsel
- if mainsel > 0
- do case
-
- //───── no submenu or action tied to this element -- force exit
- case len(prompts[mainsel]) < 3
- mainsel := 0
-
- //───── submenu
- case valtype( ACTION(mainsel) ) == "A"
- /*
- we must store the main selection to a temporary variable
- because if we escape the submenu with a left or right
- arrow, we need to know which coordinates were used to
- draw the box around the submenu so that we can properly
- restore the screen
- */
- tempsel := mainsel
- subsel := 1
- //───── redefine left & right arrows to exit submenu
- oldleft := setkey(K_LEFT, { | | jumpleft(@mainsel, nprompts) } )
- oldright := setkey(K_RIGHT, { | | jumpright(@mainsel, nprompts) } )
- buffer := savescreen(TOPROW + 1, LEFTCOL(tempsel), ;
- BOTROW(tempsel), RIGHTCOL(tempsel))
- @ TOPROW+1, LEFTCOL(tempsel), BOTROW(tempsel), RIGHTCOL(tempsel);
- box B_SINGLE + chr(32)
- do while subsel != 0
- setpos(TOPROW + 1, 0)
- for x = 1 to len(ACTION(tempsel))
- //───── display message only if one exists
- if len(ACTION(tempsel)[x]) > 1
- @ row()+1, LEFTCOL(tempsel)+1 prompt ACTION(tempsel)[x, 1];
- message ACTION(tempsel)[x, 2]
- else
- @ TOPROW, LEFTCOL(tempsel)+1 prompt ACTION(tempsel)[x, 1]
- endif
- next
- menu to subsel
- if subsel > 0
- if len(ACTION(tempsel)[subsel]) > 2 .and. ;
- valtype(ACTION(tempsel)[subsel, 3]) == "B"
- eval( ACTION(tempsel)[subsel, 3])
- else
- subsel := 0
- endif
- endif
- enddo
- restscreen(TOPROW + 1, LEFTCOL(tempsel), BOTROW(tempsel), ;
- RIGHTCOL(tempsel), buffer)
- setkey(K_LEFT, oldleft)
- setkey(K_RIGHT, oldright)
-
- //───── action block
- case valtype( ACTION(mainsel) ) == "B"
- eval( ACTION(mainsel) )
-
-
- //───── your guess is as good as mine
- otherwise
- // ????
-
- endcase
- endif
- enddo
-
- //───── restore previous message and wrap settings
- set(_SET_MESSAGE, oldmessrow)
- set(_SET_WRAP, oldwrap)
- setcursor(oldcursor)
- return nil
-
-
- /*
- JUMPLEFT() - make left arrow exit sub-menu
- */
- static function jumpleft(main, maxprompts)
- if main == 1
- main := maxprompts
- else
- main--
- endif
- /*
- peek in COORDS array to see if there is a sub-menu attached to the
- new top-level menu option -- if so, stuff an Enter to jump right
- into the sub-menu -- if not, leave it alone
- */
- keyboard chr(K_ESC) + if(len(coords[main]) > 1, chr(K_ENTER), '')
- return nil
-
-
- /*
- JUMPRIGHT() - make left arrow exit sub-menu
- */
- static function jumpright(main, maxprompts)
- if main == maxprompts
- main := 1
- else
- main++
- endif
- /*
- peek in COORDS array to see if there is a sub-menu attached to the
- new top-level menu option -- if so, stuff an Enter to jump right
- into the sub-menu -- if not, leave it alone
- */
- keyboard chr(K_ESC) + if(len(coords[main]) > 1, chr(K_ENTER), '')
- return nil
-
- //───── end of file CHP1407.PRG
-