home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / MENUSPCS.ZIP / MENUS.S < prev    next >
Encoding:
Text File  |  1987-12-14  |  31.5 KB  |  833 lines

  1. ;;; ------------------------------------------------------------
  2. ;;; Menus for Scheme (tm)
  3. ;;; CLASS DEFINITIONS, METHODS, CONSTRUCTORS, AND UTILITIES
  4. ;;; $Revision:   1.4  $    $Date:   14 Dec 1987  0:18:00  $
  5. ;;; ------------------------------------------------------------
  6. ;;; Copyright (c) 1987 by Morton Goldberg.
  7. ;;;
  8. ;;; Permission is granted to make copies of this program text
  9. ;;; for personal, non-commercial use provided this notice of
  10. ;;; copyright appears in all copies and derived works.  All
  11. ;;; other rights reserved.  In particular, you may not publish
  12. ;;; or otherwise distribute any copies of this text or any
  13. ;;; derived works without permission from the copyright holder.
  14. ;;; ------------------------------------------------------------
  15. ;;; Implementation Language: Texas Instruments' PC SCHEME
  16. ;;; (Version 3.0)
  17. ;;; ------------------------------------------------------------
  18.  
  19. (writeln "Menus for Scheme (tm)")
  20. (writeln "Copyright (c) 1987 by Morton Goldberg")
  21. (let ((rev-number "$Revision:   1.4  $"))
  22.   ;; Write the revision number string without the leading and
  23.   ;; trailing dollar-signs (which are needed by the version
  24.   ;; control system).
  25.   (writeln (substring rev-number
  26.                       1
  27.                       (sub1 (string-length rev-number)))))
  28.  
  29. ;;; ------------------------------------------------------------
  30. ;;; READ A LINE OF CHARACTERS FROM A WINDOW
  31. ;;; ------------------------------------------------------------
  32.  
  33. ;;; This procedure reads characters as they are typed into a
  34. ;;; window.  It echos the characters so the user can see the
  35. ;;; type-in.  Destructive backspace is supported for minor
  36. ;;; editing.  Pressing the enter-key, ends reading and returns
  37. ;;; the characters typed as a string.  Pressing the escape-key,
  38. ;;; ends reading characters and returns an empty string.  The
  39. ;;; procedure is used by the read-response method of class popup
  40. ;;; query window.
  41. (define (readln window)
  42.   (define chr #\?)
  43.   (define ln '())
  44.   (define (read-loop window)
  45.     (set! chr (read-char window))
  46.     (case (char->integer chr)
  47.       ;; Typing a #\RETURN exits the loop and returns
  48.       ;; a list of the characters read.
  49.       (#xD ln)
  50.       ;; Typing an #\ESC exits the loop and returns an
  51.       ;; empty list.
  52.       (#x1B '())
  53.       ;; This implements a destructive backspace.
  54.       (#x8
  55.        (set! ln (cdr ln))
  56.        (let ((cursor (window-get-cursor window)))
  57.          (window-set-cursor! window
  58.                              (car cursor)
  59.                              (sub1 (cdr cursor)))
  60.          (write-char #\SPACE window)
  61.          (window-set-cursor! window
  62.                              (car cursor)
  63.                              (sub1 (cdr cursor))))
  64.        (read-loop window))
  65.       ;; Add another character to the list.
  66.       (else
  67.        (write-char chr window)
  68.        (set! ln (cons chr ln))
  69.        (read-loop window))))
  70.   (list->string (reverse (read-loop window))))
  71.  
  72. ;;; ------------------------------------------------------------
  73. ;;; CLASS KEY MONITOR
  74. ;;; ------------------------------------------------------------
  75. ;;; Class key-monitor is a component of the SCOOPS-based menu
  76. ;;; system for Scheme programs, where it is used as a mixin.
  77. ;;; However, class key-monitor may be instantiated; instances of
  78. ;;; key-monitor can be created and used without any reference to
  79. ;;; the rest of menu-system.
  80.  
  81. ;;; A key-monitor object reads characters from the current input
  82. ;;; port, usually but not necessarily the keyboard, and produces
  83. ;;; an action; that is, it executes a procedure that has been
  84. ;;; associated with the character.  The association between the
  85. ;;; character and the action is maintained in a key action
  86. ;;; table.  The class provides methods for modifying a key
  87. ;;; action table at run time.  Consequently, a program using
  88. ;;; class key-monitor may implement dynamic key-to-action
  89. ;;; mapping.
  90. (define-class key-monitor
  91.   ;; The class variables are the key actions which are used
  92.   ;; to initialize the key action tables.  Each key action
  93.   ;; is a procedure taking one argument, a character.
  94.   (classvars
  95.     ;; Just return the character.
  96.     (no-op
  97.       (lambda (ch)
  98.         ch))
  99.     ;; Get another character and use the special-key action
  100.     ;; table to handle it.
  101.     (special-key
  102.       (lambda (ignore)
  103.         (read-again-and-act)))
  104.     ;; Just return the abort symbol.
  105.     (user-abort
  106.       (lambda (ignore)
  107.         'USER-ABORT)))
  108.   ;; The instance variables are key action tables.  A key
  109.   ;; action table is a vector of 128 procedures of the form:
  110.   ;;     #(key-action-0 key-action-1 ... key-action-127)
  111.   (instvars
  112.     ;; This table holds the actions for the keys producing
  113.     ;; characters in the normal ASCII range.
  114.     (actions-for-ascii-keys
  115.       (let ((tmp (make-vector 128 no-op)))
  116.         (vector-set! tmp 0 special-key)
  117.         (vector-set! tmp #x1B user-abort)
  118.         tmp))
  119.     ;; This table holds the actions for the function keys,
  120.     ;; the cursor control keys, and the so-called word
  121.     ;; processing keys.
  122.     (actions-for-special-keys
  123.       (make-vector 128 no-op)))
  124.   (options
  125.     (gettable-variables no-op special-key user-abort)))
  126.  
  127. (compile-class key-monitor)
  128.  
  129. ;;; ------------------------------------------------------------
  130. ;;; METHODS FOR CLASS KEY MONITOR
  131.  
  132. ;;; Install a key action procedure in the designated table at
  133. ;;; the designated index.  This the shared, private key
  134. ;;; installation method.  Programs using key monitors should use
  135. ;;; one of two following methods rather than this one.
  136. (define-method (key-monitor install-key) (table indx proc)
  137.   (vector-set! table indx proc))
  138.  
  139. ;;; Install a key action procedure, proc, in the ASCII key table
  140. ;;; at the position, indx.  Use for effect; returns nothing
  141. ;;; useful.
  142. (define-method (key-monitor install-ascii-key) (indx proc)
  143.   (install-key actions-for-ascii-keys indx proc))
  144.  
  145. ;;; Install a key action procedure, proc, in the special key
  146. ;;; table at the position, indx.  Use for effect; returns
  147. ;;; nothing useful.
  148. (define-method (key-monitor install-special-key) (indx proc)
  149.   (install-key actions-for-special-keys indx proc))
  150.  
  151. ;;; Process a key if one is ready, but don't waste time waiting
  152. ;;; if one isn't ready.  Returns whatever the key action
  153. ;;; associated with the character returns if a character is
  154. ;;; avaliable; otherwise returns #f.
  155. (define-method (key-monitor look-for-key) ()
  156.   (if (char-ready?)
  157.     (read-key-and-act actions-for-ascii-keys)))
  158.  
  159. ;;; Reads a byte (character) and executes the action stored in
  160. ;;; the designated key action table at the position indexed by
  161. ;;; the byte.  Returns whatever the key action returns.
  162. (define-method (key-monitor read-key-and-act) (table)
  163.   (let((ch (read-char)))
  164.     ((vector-ref table (char->integer ch)) ch)))
  165.  
  166. ;;; Reads the second byte of a special key and executes the
  167. ;;; action in the special key table at the position indexed by
  168. ;;; the byte.  Returns whatever the special key action returns.
  169. (define-method (key-monitor read-again-and-act) ()
  170.   (read-key-and-act actions-for-special-keys))
  171.  
  172. ;;; ------------------------------------------------------------
  173. ;;; CLASS BASIC POPUP WINDOW
  174. ;;; ------------------------------------------------------------
  175. ;;; This class supplies defines the instance variables and basic
  176. ;;; methods for simple popup windows that can be used for
  177. ;;; various purposes.  The class can only be used as a mixin.
  178. ;;; 
  179. ;;; (*) indicates a user-supplied value; the values for the
  180. ;;; other instvars are computed.
  181. (define-class basic-popup-window
  182.   (instvars
  183.     w-rows  ; height of the window (*)
  184.     w-cols  ; width of the window (*)
  185.     n-attr  ; window normal attribute (*)
  186.     border? ; if #f, window won't have border (*)
  187.     w-top   ; top row of the window on the screen
  188.     w-left  ; left column of the window on the screen
  189.     window) ; the port assigned to the window
  190.   (options
  191.     (gettable-variables
  192.       w-top w-left w-rows w-cols border?)
  193.     (settable-variables
  194.       w-top w-left)
  195.     (inittable-variables
  196.       w-top w-left n-attr border?)))
  197.  
  198. (compile-class basic-popup-window)
  199.  
  200. ;;; ------------------------------------------------------------
  201. ;;; METHODS FOR CLASS BASIC POPUP WINDOW
  202.  
  203. ;;; Make the popup window appear on the screen.
  204. (define-method (basic-popup-window expose) ()
  205.   (if border?
  206.     (window-set-attribute! window
  207.                            'border-attributes
  208.                            n-attr))
  209.   (window-set-attribute! window 'text-attributes n-attr)
  210.   (window-set-position! window w-top w-left)
  211.   (window-set-size! window w-rows w-cols)
  212.   (window-popup window))
  213.  
  214. ;;; Erase the popup window from the screen.
  215. (define-method (basic-popup-window expunge) ()
  216.   (window-popup-delete window))
  217.  
  218. ;;; ------------------------------------------------------------
  219. ;;; CLASS POPUP TEXT WINDOW
  220. ;;; ------------------------------------------------------------
  221. ;;; This class is built on the basic-popup-window class.  It
  222. ;;; adds the methods for writing one or more lines of text into
  223. ;;; a popup window.  The class can be instantiated or it can be
  224. ;;; used as a mixin. 
  225. ;;; 
  226. ;;; (*) indicates a user-supplied value; the values for the
  227. ;;; other instvars are computed. 
  228. (define-class popup-text-window
  229.   (instvars
  230.     ;; The contents of the window, a vector of text strings.
  231.     ;; Each element of the vector appears as one line of
  232.     ;; text in the window.  The size of the window is
  233.     ;; adjusted to fit the text by the adjust-size method.
  234.     (text (active "" #f adjust-size)))
  235.   (mixins
  236.     basic-popup-window))
  237.  
  238. (compile-class popup-text-window)
  239.  
  240. ;;; ------------------------------------------------------------
  241. ;;; CONSTRUCTOR
  242. ;;; 
  243. ;;; The constructor returns the instance it creates.  Init-list
  244. ;;; is a list of form
  245. ;;; 
  246. ;;; (instvar-name initial-value ... )
  247. ;;; 
  248. ;;; and text is a vector of the form
  249. ;;;
  250. ;;; #(string ... ).
  251. (define (make-popup-text-window init-list text)
  252.   (let ((a-popup-text-window
  253.     (eval `(make-instance popup-text-window ,@init-list))))
  254.     (send a-popup-text-window init)
  255.     (send a-popup-text-window set-text text)
  256.     a-popup-text-window))
  257.  
  258. ;;; ------------------------------------------------------------
  259. ;;; METHODS FOR CLASS POPUP TEXT WINDOW
  260.  
  261. ;;; This method must be called before an instance of popup-
  262. ;;; text-window can be used for the first time.  This is done by
  263. ;;; the constructor make-popup-text-window.
  264. (define-method (popup-text-window init) ()
  265.   ;; Assign a window to the popup
  266.   (set! window (make-window #f border?)))
  267.  
  268. ;;; This method is called whenever the instvar text, an active
  269. ;;; value, is changed by a (send ... set-text .. ) message.
  270. (define-method (popup-text-window adjust-size) (txt)
  271.   ;; Determine the size of the window.
  272.   (set! w-rows (vector-length txt))
  273.   (set! w-cols
  274.         (apply max (map string-length (vector->list txt))))
  275.   txt)
  276.  
  277. ;;; The method used by applications to popup a popup-text-
  278. ;;; window.  The popup-text-window disappears when [ESC] is
  279. ;;; pressed.  Used for effect; returns nothing useful.
  280. (define-method (popup-text-window popup) ()
  281.   (expose)
  282.   (write-text)
  283.   (watch-for-esc)
  284.   (expunge))
  285.  
  286. ;;; Write a vector of text strings into the popup-text-window.
  287. ;;; The vector is the stored in the instvar text.
  288. (define-method (popup-text-window write-text) ()
  289.   (do ((row 0 (add1 row)))
  290.       ((>= row w-rows))
  291.     (window-set-attribute! window 'text-attributes n-attr)
  292.     (window-set-cursor! window row 0)
  293.     (display (vector-ref text row) window)))
  294.  
  295. ;;; Watch for keystrokes, ignore everything but [ESC].
  296. (define-method (popup-text-window watch-for-esc) ()
  297.   (case (char->integer (read-char))
  298.     (#x1B
  299.      '())
  300.     (#x0
  301.      (read-char)
  302.      (watch-for-esc))
  303.     (else
  304.      (watch-for-esc))))
  305.  
  306. ;;; ------------------------------------------------------------
  307. ;;; CLASS POPUP QUERY WINDOW
  308. ;;; ------------------------------------------------------------
  309. ;;; This class is built on the popup-text-window class.  It adds
  310. ;;; the methods for placing an input field within a text window
  311. ;;; and for reading data into the field.  The class can be
  312. ;;; instantiated.
  313. ;;; 
  314. ;;; (*) indicates a user-supplied value; the values for the
  315. ;;; other instvars are computed.
  316. (define-class popup-query-window
  317.   (instvars
  318.     hl-attr     ; window highlight attribute (*)
  319.     cursor-row  ; starting row of the input field (*)
  320.     cursor-col  ; starting column of the input field (*)
  321.     input-width ; width of the input field (*)
  322.     spaces      ; empty input field
  323.     response    ; the response made by the user, a string
  324.     ;; The contents of the window, a vector of text strings.
  325.     ;; Each element of the vector appears as one line of
  326.     ;; text in the window.  The spaces representing the
  327.     ;; input field are added to one of elements by the
  328.     ;; add-input-field method.
  329.     (text (active "" #f add-input-field)))
  330.   (mixins
  331.     popup-text-window)
  332.   (options
  333.     (gettable-variables
  334.       response)
  335.     (inittable-variables
  336.       hl-attr cursor-row cursor-col input-width)))
  337.  
  338. (compile-class popup-query-window)
  339.  
  340. ;;; ------------------------------------------------------------
  341. ;;; CONSTRUCTOR
  342. ;;; 
  343. ;;; The constructor returns the instance it creates.  Init-list
  344. ;;; is a list of form
  345. ;;; 
  346. ;;; (instvar-name initial-value ... )
  347. ;;; 
  348. ;;; and text is a vector of the form
  349. ;;;
  350. ;;; #(string ... ).
  351. (define (make-popup-query-window init-list text)
  352.   (let ((a-popup-query-window
  353.     (eval `(make-instance popup-query-window ,@init-list))))
  354.     (send a-popup-query-window init)
  355.     (send a-popup-query-window set-text text)
  356.     a-popup-query-window))
  357.  
  358. ;;; ------------------------------------------------------------
  359. ;;; METHODS FOR CLASS POPUP QUERY WINDOW
  360.  
  361. ;;; This method is called whenever the instvar text, an active
  362. ;;; value, is changed by a (send ... set-text .. ) message.
  363. (define-method (popup-query-window add-input-field) (txt)
  364.   (set! spaces (make-string input-width #\SPACE))
  365.   (set! cursor-col
  366.         (string-length (vector-ref txt cursor-row)))
  367.   (vector-set! txt
  368.                cursor-row
  369.                (string-append (vector-ref txt cursor-row)
  370.                               spaces))
  371.   (adjust-size txt)
  372.   txt)
  373.  
  374. ;;; The method used by applications to popup a popup-query-
  375. ;;; window.  It calls the method read-response to get the user's
  376. ;;; reponse.  When read-repsponse return, this method expuges
  377. ;;; the popup-query-window from the screen.  The users response
  378. ;;; is put in the instance variable response from which it can
  379. ;;; be retrieve by the sending the get-response message.
  380. (define-method (popup-query-window popup) ()
  381.     (set! response "")
  382.     (expose)
  383.     (write-text)
  384.     (set! response (read-response))
  385.     (expunge))
  386.  
  387. ;;; This method displays the input field using the window
  388. ;;; high-light attribute, reads in a line of characters with
  389. ;;; echo to the input field, and converts the characters into a
  390. ;;; string, which it returns.
  391. (define-method (popup-query-window read-response) ()
  392.   (window-set-attribute! window 'text-attributes hl-attr)
  393.   (window-set-cursor! window cursor-row cursor-col)
  394.   (display spaces window)
  395.   (window-set-cursor! window cursor-row cursor-col)
  396.   (readln window))
  397.  
  398. ;;; ------------------------------------------------------------
  399. ;;; CLASS MENU-ITEM
  400. ;;; ------------------------------------------------------------
  401. ;;; This class provides the instance variables, i.e., the
  402. ;;; internal representation, of a menu item.  A vector of
  403. ;;; instances of this class forms an item-table, which is the
  404. ;;; principal structure of class basic-menu (see below).
  405. ;;; 
  406. ;;; (*) indicates a user-supplied value; the values for the
  407. ;;; other instvars are computed.
  408. (define-class menu-item
  409.   (instvars
  410.     action     ; action taken when this item is selected (*)
  411.     label      ; string describing the menu item (*)
  412.     label-row  ; row position of the label in the menu
  413.     label-col) ; column position of the label in the menu
  414.   (options
  415.     gettable-variables
  416.     (settable-variables
  417.       label-row label-col)
  418.     (inittable-variables
  419.       action label)))
  420.  
  421. (compile-class menu-item)
  422.  
  423. ;;; ------------------------------------------------------------
  424. ;;; CONSTRUCTOR
  425. ;;; 
  426. ;;; The constructor returns the instance it creates.  Its
  427. ;;; argument is a list of the form
  428. ;;; 
  429. ;;; (label-string selector action)
  430. ;;; 
  431. ;;; which is the standard format of an item table entry.  Note
  432. ;;; that the second element of the argument list, the selector,
  433. ;;; is ignored by the constructor but is used by the menu object
  434. ;;; that contains the menu item constructed here.
  435. (define (make-menu-item item-inits)
  436.   (let ((labl-val (car item-inits))
  437.         (actn-val (caddr item-inits)))
  438.   (make-instance menu-item 'action actn-val
  439.                            'label labl-val)))
  440.  
  441. ;;; ------------------------------------------------------------
  442. ;;; CLASS BASIC MENU
  443. ;;; ------------------------------------------------------------
  444. ;;; This is the base class.  It supplies the methods for
  445. ;;; creating and dealing with the basic representation of a
  446. ;;; menu.  It is intended to be used as a mixin.
  447. ;;; 
  448. ;;; (*) indicates a user-supplied or defaulted value; the values
  449. ;;; for the other instvars are computed.
  450. (define-class basic-menu
  451.   (instvars
  452.     hl-attr     ; window highlight attribute (*)
  453.     item-index  ; index into item-table
  454.     items       ; total entries in item-table
  455.     item-table) ; table of instances of class menu-item
  456.   (mixins
  457.     basic-popup-window key-monitor)
  458.   (options
  459.     (settable-variables
  460.       item-index)
  461.     (inittable-variables
  462.       hl-attr)))
  463.  
  464. (compile-class basic-menu)
  465.  
  466. ;;; ------------------------------------------------------------
  467. ;;; METHODS FOR CLASS BASIC MENU
  468.  
  469. ;;; The init method for all menus.  Item-list is a list of the
  470. ;;; form
  471. ;;; 
  472. ;;; ((label selector action) ... )
  473. ;;; 
  474. ;;; where there is one entry for each menu item.  It is passed
  475. ;;; to the make-item-table method.
  476. (define-method (basic-menu init) (item-list)
  477.   ;; The arrow keys will move the bar cursor.
  478.   (install-special-key 72 item<-) ; up-arrow
  479.   (install-special-key 75 item<-) ; left-arrow
  480.   (install-special-key 77 item->) ; right-arrow
  481.   (install-special-key 80 item->) ; down-arrow
  482.   ;; The enter key will select the menu item indicated by
  483.   ;; the bar cursor.
  484.   (install-ascii-key 13 select)
  485.   ;; Assign a window to the menu.
  486.   (set! window (make-window #f border?))
  487.   ;; Create and partially initialize the item table.
  488.   (set! item-index 0)
  489.   (make-item-table item-list)
  490.   ;; The following is a method that must be supplied by any
  491.   ;; menu class built on basic-menu.  Its main function is
  492.   ;; to complete the entries in the item table.
  493.   (format-specific-init))
  494.  
  495. ;;; This method is passed an item-list as described for method
  496. ;;; menu-init.  It uses the list to create and partially
  497. ;;; initialize an item-table entry for each menu item.  At the
  498. ;;; same time, it also installs an item selection procedure for
  499. ;;; each menu item in the ASCII key-action table.
  500. (define-method (basic-menu make-item-table) (item-list)
  501.   (set! items (length item-list))
  502.   (set! item-table (make-vector items))
  503.   (do ((index 0 (add1 index)))
  504.       ((>= index items))
  505.     (let* ((item-inits (list-ref item-list index))
  506.            (slctr-val (cadr item-inits)))
  507.       ;; Install one item in the item-table.
  508.       (vector-set! item-table
  509.                    index
  510.                    (make-menu-item item-inits))
  511.       ;; Install the selection procedure(s) in the key-action
  512.       ;; table.
  513.       (for-each (lambda (slctr-char)
  514.                   (install-ascii-key (char->integer slctr-char)
  515.                                      (lambda (ignore)
  516.                                        (shift index)
  517.                                        (select 'IGNORE))))
  518.                 (if (atom? slctr-val)
  519.                   ;; This `if' expression makes the case of a
  520.                   ;; single selector character the same as the
  521.                   ;; case of multiple selector characters.
  522.                   (list slctr-val)
  523.                   slctr-val)))))
  524.  
  525. ;;; The method used by applications to invoke a menu.
  526. (define-method (basic-menu popup) ()
  527.   (expose-menu)
  528.   (keybd-watch)
  529.   (expunge))
  530.  
  531. ;;; Watch for keystrokes and act on them.
  532. (define-method (basic-menu keybd-watch) ()
  533.   (if (not (eq? 'USER-ABORT (look-for-key)))
  534.     (keybd-watch)))
  535.  
  536. ;;; Make the menu appear on the screen.
  537. (define-method (basic-menu expose-menu) ()
  538.   (expose)
  539.   (do ((index 0 (add1 index)))
  540.       ((>= index items))
  541.     (write-label index n-attr))
  542.   (write-label item-index hl-attr))
  543.  
  544. ;;; Advance item-index and the bar cursor forward by one menu
  545. ;;; item.
  546. (define-method (basic-menu item->) (ignore)
  547.     (shift (modulo (add1 item-index) items)))
  548.  
  549. ;;; Advance item-index and the bar cursor backward by one menu
  550. ;;; item.
  551. (define-method (basic-menu item<-) (ignore)
  552.     (shift (modulo (sub1 item-index) items)))
  553.  
  554. ;;; This the general item-index and bar cursor mover.
  555. (define-method (basic-menu shift) (new-index)
  556.   (write-label item-index n-attr)
  557.   (write-label new-index hl-attr)
  558.   (set! item-index new-index))
  559.  
  560. ;;; Write an item's label with the designated color attribute.
  561. ;;; The method is passed the color attribute and the index of
  562. ;;; the item.
  563. (define-method (basic-menu write-label) (index attr)
  564.   (let* ((item (vector-ref item-table index))
  565.          (label (send item get-label))
  566.          (row (send item get-label-row))
  567.          (col (send item get-label-col)))
  568.     (window-set-attribute! window 'text-attributes attr)
  569.     (window-set-cursor! window row col)
  570.     (display label window)))
  571.  
  572. ;;; For whatever menu item is at the item-index position of the
  573. ;;; item-table, extract its action (a thunk) and call it.  The
  574. ;;; init-menu method installs this method in the ASCII key-
  575. ;;; action table at the index for the enter-key, so it can
  576. ;;; provide the hit-enter component for the traditional move-
  577. ;;; the-bar-cursor-and-hit-enter way of selecting menu items.
  578. (define-method (basic-menu select) (ignore)
  579.   ((send (vector-ref item-table item-index) get-action)))
  580.  
  581. ;;; ------------------------------------------------------------
  582. ;;; CLASS HORIZONTAL MENU
  583. ;;; ------------------------------------------------------------
  584. ;;; This class can be instantiated.  It adds a method supplying
  585. ;;; the form-specific initializaton to the methods supplied by
  586. ;;; the class basic-menu.
  587. (define-class horizontal-menu
  588.   (instvars
  589.     label-spacing) ; how many space chars between labels (*)
  590.   (mixins
  591.     basic-menu)
  592.   (options
  593.     (inittable-variables
  594.       label-spacing)))
  595.  
  596. (compile-class horizontal-menu)
  597.  
  598. ;;; ------------------------------------------------------------
  599. ;;; CONSTRUCTOR
  600. ;;; 
  601. ;;; The constructor returns the instance it creates.  Its
  602. ;;; arguments are init-list and item-list.  Init-list is a list
  603. ;;; of the form
  604. ;;; 
  605. ;;; (instvar-name initial-value ... ) 
  606. ;;; 
  607. ;;; and item-list is a list of the form
  608. ;;; 
  609. ;;; ((label selector action) ... )
  610. ;;; 
  611. ;;; where there is one entry for each menu item.
  612. (define (make-horizontal-menu init-list item-list)
  613.   (let ((a-menu (eval `(make-instance horizontal-menu
  614.                                       ,@init-list))))
  615.     (send a-menu init item-list)
  616.     a-menu))
  617.  
  618. ;;; ------------------------------------------------------------
  619. ;;; METHOD FOR CLASS HORIZONTAL MENU
  620. ;;; ------------------------------------------------------------
  621.  
  622. ;;; This is the format-specific intializer required of any class
  623. ;;; built on basic-menu.  For each item entry in the item table
  624. ;;; of a menu, it completes the entry by filling-in the fields
  625. ;;; which hold computed values.
  626. (define-method (horizontal-menu format-specific-init) ()
  627.   (let* ((offset 0)
  628.          (complete-an-entry
  629.            (lambda (index)
  630.              (let* ((item (vector-ref item-table index))
  631.                     (width (string-length
  632.                              (send item get-label))))
  633.                (send item set-label-row 0)
  634.                (send item set-label-col offset)
  635.                (set! offset (+ offset
  636.                                width
  637.                                label-spacing))))))
  638.   (do ((index 0 (add1 index)))
  639.       ((>= index items))
  640.     (complete-an-entry index))
  641.   (set! w-rows 1)
  642.   (set! w-cols offset)))
  643.  
  644. ;;; ------------------------------------------------------------
  645. ;;; CLASS VERTICAL MENU
  646. ;;; ------------------------------------------------------------
  647. ;;; This class can be instantiated.  It adds a method supplying
  648. ;;; the form-specific initializaton to the methods supplied by
  649. ;;; the class basic-menu.
  650. (define-class vertical-menu
  651.   (mixins
  652.     basic-menu))
  653.  
  654. (compile-class vertical-menu)
  655.  
  656. ;;; ------------------------------------------------------------
  657. ;;; CONSTRUCTOR
  658. ;;; 
  659. ;;; The constructor returns the instance it creates.  Its
  660. ;;; arguments are init-list and item-list.  Init-list is a list
  661. ;;; of the form
  662. ;;; 
  663. ;;; (instvar-name initial-value ... )
  664. ;;; 
  665. ;;; and item-list is a list of the form
  666. ;;; 
  667. ;;; ((label selector action) ... )
  668. ;;; 
  669. ;;; where there is one entry for each menu item.
  670. (define (make-vertical-menu init-list item-list)
  671.   (let ((a-menu (eval `(make-instance vertical-menu
  672.                                       ,@init-list))))
  673.     (send a-menu init item-list)
  674.     a-menu))
  675.  
  676. ;;; ------------------------------------------------------------
  677. ;;; METHOD FOR CLASS VERTICAL MENU
  678.  
  679. ;;; This is the format-specific intializer required of any class
  680. ;;; built on basic-menu.  For each item entry in the item table
  681. ;;; of a menu, it completes the entry by filling- in the fields
  682. ;;; which hold computed values.
  683. (define-method (vertical-menu format-specific-init) ()
  684.   (let* ((offset 0)
  685.          (label-widths '())
  686.          (complete-an-entry
  687.            (lambda (index)
  688.              (let* ((item (vector-ref item-table index))
  689.                     (width (string-length
  690.                              (send item get-label))))
  691.                (send item set-label-row offset)
  692.                (send item set-label-col 0)
  693.                (set! label-widths (cons width label-widths))
  694.                (set! offset (add1 offset))))))
  695.   (do ((index 0 (add1 index)))
  696.       ((>= index items))
  697.     (complete-an-entry index))
  698.   (set! w-rows items)
  699.   (set! w-cols (apply max label-widths))))
  700.  
  701. ;;; -------------------------------------------------------------
  702. ;;; POSITIONING FUNCTIONS FOR POPUP WINDOWS
  703. ;;; -------------------------------------------------------------
  704. ;;; VERTICAL POSITIONING
  705.  
  706. ;;; Perform the specified vertical justification on the
  707. ;;; specified window.  Used for effect; returns nothing useful.
  708. (define (v-justify to-where window)
  709.   ;; Return a screen row so that if the window designated by the
  710.   ;; second argument were so placed, it would be vertically
  711.   ;; positioned on the screen as specified by the first argument,
  712.   ;; which must be one of the symbols TOP, CENTER, or BOTTOM,
  713.   ;; where TOP and BOTTOM indicate flush-to-top and flush-to-
  714.   ;; bottom, respectively.
  715.   (define (top-row to-where window)
  716.     (let ((border? (send window get-border?))
  717.           (tmp (- 24 (send window get-w-rows))))
  718.           ;; the definition of tmp assumes that the working area
  719.           ;; of the screen has 24 rows.
  720.       (case to-where
  721.         (TOP
  722.          (if border?
  723.            1
  724.            0))
  725.         (CENTER
  726.          (quotient tmp 2))
  727.         (BOTTOM
  728.           (if border?
  729.             (- tmp 1)
  730.             tmp)))))
  731.   (send window set-w-top (top-row to-where window)))
  732.  
  733. ;;; -------------------------------------------------------------
  734. ;;; HORIZONTAL POSITIONING
  735.  
  736. ;;; Perform the specified horizontal justification on the
  737. ;;; specified window.  Used for effect; returns nothing useful.
  738. (define (h-justify to-where window)
  739.   ;; Return a screen column so that if the window designated by
  740.   ;; the second argument were so placed, it would be horizontally
  741.   ;; positioned on the screen as specified by the first argument,
  742.   ;; which must be one of the symbols LEFT, CENTER, or RIGHT,
  743.   ;; where LEFT and RIGHT indicate flush-left and flush-right,
  744.   ;; respectively.
  745.   (define (left-col to-where window)
  746.     (let ((border? (send window get-border?))
  747.           (tmp (- 80 (send window get-w-cols))))
  748.           ;; the definition of tmp assumes that the working area
  749.           ;; of the screen has 80 columns.
  750.       (case to-where
  751.         (LEFT
  752.          (if border?
  753.            1
  754.            0))
  755.         (CENTER
  756.          (quotient tmp 2))
  757.         (RIGHT
  758.           (if border?
  759.             (- tmp 1)
  760.             tmp)))))
  761.   (send window set-w-left (left-col to-where window)))
  762.  
  763. ;;; ------------------------------------------------------------
  764. ;;; MAKE AN ITEM-LIST FOR A MENU FROM A LIST OF STRINGS
  765. ;;; ------------------------------------------------------------
  766.  
  767. ;;; This procedure will help the programmer build a menu from a
  768. ;;; list of strings.  The resulting menu will have the follow-
  769. ;;; ing properties: 1) each string in the list appears in the
  770. ;;; menu along with a selector character which is automatically
  771. ;;; assigned, and 2) a specifed action will be applied to any
  772. ;;; of the strings when it is selected from the menu by the
  773. ;;; user.
  774. ;;; 
  775. ;;; The first argument `strings' is a list of strings.  The
  776. ;;; second argument `action' is a procedure taking one argu-
  777. ;;; ment, a string on which it will act.
  778. ;;; 
  779. ;;; The procedure returns a list where each element is a menu
  780. ;;; item descriptor of the form
  781. ;;;
  782. ;;; ("<u-chr> <string>" #\<l-chr> action-thunk)
  783. ;;;
  784. ;;; and where <u-chr> is the uppercase form and <l-chr> is the
  785. ;;; lowercase form of a letter of the alphabet and <string> is
  786. ;;; one the strings in the `strings' list.
  787. (define (strings->items strings action)
  788.   (define nn 96) ; one less than the ASCII value of `a'
  789.   (define (make-item string)
  790.     (set! nn (1+ nn)) ; the ASCII value of <l-chr>
  791.     (list 
  792.       ;; The menu item label: "<l-chr> <string>".
  793.       (string-append
  794.         (list->string (list (integer->char nn) #\space))
  795.         string)
  796.       ;; The menu item selector character, #\<l-chr>
  797.       (integer->char nn)
  798.       ;; The menu item action thunk.
  799.       (lambda () (action string) 'USER-ABORT)))
  800.   (map make-item strings))
  801.  
  802. ;;; ------------------------------------------------------------
  803. ;;; MAKE A MENU FROM THE LIST RETURN BY A DOS-DIR CALL
  804. ;;; ------------------------------------------------------------
  805.  
  806. ;;; This procedure returns a menu built from the list returned
  807. ;;; by the call (dos-dir wild-card), providing the returned list
  808. ;;; contains between one and `max-vertical' elements.  Other-
  809. ;;; wise, a range error message is displayed in the status line
  810. ;;; and the procedure returns #f.  The menu will have the
  811. ;;; following properties: 1) it will be created using the menu
  812. ;;; init-list passed via the argument `init-list', 2) each label
  813. ;;; appearing in the menu will show one file-spec matching the
  814. ;;; `wild-card' along with an automatically assigned selector
  815. ;;; character, and 3) `action', a procedure of one argument,
  816. ;;; will be applied to the file corresponding to a menu item
  817. ;;; when the item is selected by the user.  See the procedure
  818. ;;; `strings->items', which does most of the work, for more
  819. ;;; details.
  820. (define (files->menu wild-card max-vertical action init-list)
  821.   (define range-error
  822.     "Can't make menu -- no such files or too many")
  823.   (define files (dos-dir wild-card))
  824.   (if (and files (<= (length files) max-vertical))
  825.     (make-vertical-menu init-list
  826.                         (strings->items files action))
  827.     (begin
  828.       (window-clear pcs-status-window)
  829.       (display range-error pcs-status-window)
  830.       #f)))
  831.  
  832. ;;; ------------------------------------------------------------
  833.