PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/popupmenu.sc

https://bitbucket.org/bunny351/ezd
Scala | 348 lines | 304 code | 44 blank | 0 comment | 4 complexity | c9fe916ec0d49dfc945bc7b328eb3bd9 MD5 | raw file
  1. ;;; ezd - easy drawing for X11 displays.
  2. ;;;
  3. ;;; The procedures in this module implement popup menus.
  4. ;* Copyright 1990-1993 Digital Equipment Corporation
  5. ;* All Rights Reserved
  6. ;*
  7. ;* Permission to use, copy, and modify this software and its documentation is
  8. ;* hereby granted only under the following terms and conditions. Both the
  9. ;* above copyright notice and this permission notice must appear in all copies
  10. ;* of the software, derivative works or modified versions, and any portions
  11. ;* thereof, and both notices must appear in supporting documentation.
  12. ;*
  13. ;* Users of this software agree to the terms and conditions set forth herein,
  14. ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
  15. ;* right and license under any changes, enhancements or extensions made to the
  16. ;* core functions of the software, including but not limited to those affording
  17. ;* compatibility with other hardware or software environments, but excluding
  18. ;* applications which incorporate this software. Users further agree to use
  19. ;* their best efforts to return to Digital any such changes, enhancements or
  20. ;* extensions that they make and inform Digital of noteworthy uses of this
  21. ;* software. Correspondence should be provided to Digital at:
  22. ;*
  23. ;* Director of Licensing
  24. ;* Western Research Laboratory
  25. ;* Digital Equipment Corporation
  26. ;* 250 University Avenue
  27. ;* Palo Alto, California 94301
  28. ;*
  29. ;* This software may be distributed (but not offered for sale or transferred
  30. ;* for compensation) to third parties, provided such third parties agree to
  31. ;* abide by the terms and conditions of this notice.
  32. ;*
  33. ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  34. ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  35. ;* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
  36. ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  37. ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  38. ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  39. ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  40. ;* SOFTWARE.
  41. (module popupmenu)
  42. (include "struct.sch")
  43. (include "match.sch")
  44. (include "ginfo.sch")
  45. (include "commands.sch")
  46. (include "display.sch")
  47. (include "window.sch")
  48. (include "drawing.sch")
  49. ;(include "view.sch")
  50. ;(include "graphic.sch")
  51. (include "ezd.sch")
  52. (include "events.sch")
  53. (include "text.sch")
  54. (include "xternal.sch")
  55. ;;; The procedure DEFINE-POPUP defines a procedure that implements a popup
  56. ;;; menu. The user supplies a procedure name, a list of menu entries and
  57. ;;; actions and optionally a font. Each menu entry/action is a list of either
  58. ;;; a string, a procedure, or an expression that evaluates to a string and an
  59. ;;; action procedure or expression to be evaluated.
  60. ;;;
  61. ;;; The menu is attached to some object by using a WHEN command to associate
  62. ;;; the evaluation of the popup procedure with a button press. When the button
  63. ;;; is pressed, the menu will come up under the button. When the button comes
  64. ;;; up, the action associated with the menu entry is executed. Moving the
  65. ;;; mouse outside the menu without releasing the button will cause the menu to
  66. ;;; disappear.
  67. ;;;
  68. ;;; The popup menu object has the following attributes. Address them in the
  69. ;;; drawing "popup-name", object POPUP.
  70. ;;;
  71. ;;; ENTRIES
  72. ;;; REPLACE-NAME
  73. ;;; REPLACE-ACTION
  74. ;;; FOREGROUND
  75. ;;; BACKGROUND
  76. ;;; FONT
  77. ;;;
  78. ;;; DELETE-OBJECT
  79. ;;; ATTRIBUTES
  80. (define (DEFINE-POPUP popup-name name-action-list colors font)
  81. (define FOREGROUND (if colors (car colors) 'black))
  82. (define BACKGROUND (if colors (cadr colors) 'white))
  83. (define WINDOW #f)
  84. (define VISIBLE #f)
  85. (define MENU-X #f)
  86. (define MENU-Y #f)
  87. (define MENU-WIDTH 0)
  88. (define MENU-HEIGHT 0)
  89. (define MENU-LENGTH (length name-action-list))
  90. (define POPPED-WINDOW #f)
  91. (define POPPED-DRAWING #f)
  92. (define POPPED-OBJECT #f)
  93. (define POPPED-OBJECT-X #f)
  94. (define POPPED-OBJECT-Y #f)
  95. (define (ANY-BUTTON?) (or *mouse-button1* *mouse-button2* *mouse-button3*
  96. *mouse-button4* *mouse-button5*))
  97. ;;; Compute the size of a menu entry text string.
  98. (define (COMPUTE-MENU-WIDTH-AND-HEIGHT text)
  99. (let ((height-width (text->height-width (string-append " " text
  100. " ") font)))
  101. (set! menu-height
  102. (max menu-height (* 2 (car height-width))))
  103. (set! menu-width
  104. (max menu-width (cadr height-width)))))
  105. ;;; Service procedure to make the window invisible.
  106. (define (MAKE-INVISIBLE)
  107. (when visible
  108. (set! visible #f)
  109. (ezd `(delete-view ,popup-name ,popup-name))))
  110. ;;; When the window is initially poped up, the following procedure is
  111. ;;; called to verify that the mouse is still in the window.
  112. (define (DROP-WHEN-NOT-THERE)
  113. (if (and visible
  114. (or (< *mouse-x* menu-x)
  115. (> *mouse-x* (+ menu-x menu-width))
  116. (< *mouse-y* menu-y)
  117. (> *mouse-y* (* menu-length (+ menu-y menu-height)))
  118. (not (any-button?))))
  119. (make-invisible)))
  120. ;;; When the button event occurs, the following procedure is called to
  121. ;;; display the popup menu in its own window.
  122. (define (POPITUP)
  123. (set! menu-x (max 0 (- *mouse-x* (quotient menu-width 2))))
  124. (set! menu-y (max 0 (- *mouse-y* (quotient menu-height 2))))
  125. (set! popped-window *user-event-window*)
  126. (set! popped-drawing *user-event-drawing*)
  127. (set! popped-object *user-event-object*)
  128. (set! popped-object-x *user-event-x*)
  129. (set! popped-object-y *user-event-y*)
  130. (if window
  131. (xmovewindow *dpy* (window-xwindow window) menu-x menu-y)
  132. (begin (ezd `(window ,popup-name ,menu-x ,menu-y ,menu-width
  133. ,(* menu-length menu-height)))
  134. (set! window (name->window popup-name))
  135. (xchangewindowattributes *dpy* (window-xwindow window)
  136. (bit-or cwsaveunder cwoverrideredirect)
  137. (let ((x (make-xsetwindowattributes)))
  138. (xsetwindowattributes-save_under! x 1)
  139. (xsetwindowattributes-override_redirect! x 1)
  140. x))))
  141. (set! visible #t)
  142. (ezd `(overlay ,popup-name ,popup-name)))
  143. ;;; Procedures to draw a menu entry as either high or low lighted.
  144. (define (HIGHLIGHT object y text)
  145. (draw-text object y text background foreground #f))
  146. (define (LOWLIGHT object y text)
  147. (draw-text object y text foreground background #f))
  148. (define (DRAW-TEXT object y text forecolor backcolor stipple)
  149. (ezd `(set-drawing ,popup-name)
  150. `(object ,object (fill-rectangle 0 ,y ,menu-width
  151. ,menu-height ,backcolor)
  152. (text 0 ,y ,menu-width ,menu-height center center
  153. ,(menu-text text) ,forecolor
  154. ,@(if stipple (list stipple) '())
  155. ,@(if font (list font) '())))))
  156. ;;; Compute the actual text string.
  157. (define (MENU-TEXT menu-name)
  158. (cond ((string? menu-name) menu-name)
  159. ((procedure? menu-name) (menu-name))
  160. (else (eval menu-name))))
  161. ;;; The following procedure "configures" the drawing representing the
  162. ;;; popup menu. It is called
  163. (define (CONFIGURE-POPUP)
  164. (if window (ezd `(delete-window ,popup-name)))
  165. (set! window #f)
  166. (set! visible #f)
  167. (set! menu-width 0)
  168. (set! menu-height 0)
  169. (set! menu-length (length name-action-list))
  170. ;;; 1. Compute size of the menu.
  171. (for-each
  172. (lambda (name-action) (compute-menu-width-and-height
  173. (menu-text (car name-action))))
  174. name-action-list)
  175. ;;; 2. Draw the entries in the menu and arm their event handlers.
  176. (ezd '(save-drawing)
  177. `(set-drawing ,popup-name)
  178. '(object popup)
  179. `(when popup get-attributes ,get-attributes)
  180. `(when popup set-attributes ,set-attributes))
  181. (let loop ((y 0) (args name-action-list))
  182. (if (pair? args)
  183. (let* ((name-action (car args))
  184. (object (string->symbol (format "~s-~s" popup-name y)))
  185. (text (car name-action))
  186. (action (cadr name-action)))
  187. (define (ENTER-OBJ)
  188. (if (any-button?)
  189. (if action (highlight object y text))
  190. (make-invisible)))
  191. (define (EXIT-OBJ)
  192. (if action (lowlight object y text))
  193. (unless (and *mouse-window*
  194. (eq? (window-name *mouse-window*)
  195. popup-name)
  196. *mouse-object*)
  197. (make-invisible)))
  198. (define (EXECUTE-OBJ)
  199. (lowlight object y text)
  200. (make-invisible)
  201. (set! *user-event-type* 'popup)
  202. (set! *user-event-window* popped-window)
  203. (set! *user-event-drawing* popped-drawing)
  204. (set! *user-event-object* popped-object)
  205. (set! *user-event-x* popped-object-x)
  206. (set! *user-event-y* popped-object-y)
  207. (set! *user-event-misc*
  208. (list popup-name (menu-text text)))
  209. (ezd `(set-drawing *user-event-drawing*)
  210. '(draw-now))
  211. (if (procedure? action) (action) (eval action)))
  212. (draw-text object y text foreground background
  213. (if action #f 's8))
  214. (if action
  215. (ezd `(when ,object button1up ,execute-obj)
  216. `(when ,object button2up ,execute-obj)
  217. `(when ,object button3up ,execute-obj)
  218. `(when ,object button4up ,execute-obj)
  219. `(when ,object button5up ,execute-obj))
  220. (ezd `(when ,object * #f)))
  221. (ezd `(when ,object enter ,enter-obj)
  222. `(when ,object exit ,exit-obj))
  223. (loop (+ y menu-height) (cdr args)))))
  224. (ezd `(when * expose ,drop-when-not-there)
  225. '(restore-drawing)))
  226. ;;; Attributes are accessed via the following procedure.
  227. (define (GET-ATTRIBUTES)
  228. (map (lambda (a)
  229. (case a
  230. ((ENTRIES) (flatten name-action-list))
  231. ((FOREGROUND) foreground)
  232. ((BACKGROUND) background)
  233. ((FONT) font)
  234. ((ATTRIBUTES) '(entries replace-name
  235. replace-action foreground
  236. background font delete-object
  237. attributes))
  238. (else (ezd-error 'check-button
  239. "Illegal attribute: ~s" a))))
  240. *user-event-misc*))
  241. ;;; List conversion functions ((a b) (c d)) <--> (a b c d)
  242. (define (FLATTEN x)
  243. (if (pair? x) (cons* (caar x) (cadar x) (flatten (cdr x))) '()))
  244. (define (PAIR-UP x)
  245. (if (pair? x)
  246. (cons (list (car x) (cadr x)) (pair-up (cddr x)))
  247. '()))
  248. ;;; Attribute argument parsers.
  249. (define (NAME-ACTION-LIST? x)
  250. (or (null? x)
  251. (and (pair? x) (popup-entry-name? (car x))
  252. (pair? (cdr x)) (popup-entry-action? (cadr x))
  253. (name-action-list? (cddr x)))))
  254. (define (ENTRY-INDEX? x)
  255. (and (exact? x) (<= 0 x) (< x (length name-action-list))))
  256. ;;; Attributes are changed via the following procedure.
  257. (define (SET-ATTRIBUTES)
  258. (let ((delete #f))
  259. (for-each
  260. (lambda (a)
  261. (cond ((and (pair? a) (eq? (car a) 'ENTRIES)
  262. (name-action-list? (cdr a)))
  263. (set! name-action-list (pair-up (cdr a))))
  264. ((match? (REPLACE-NAME entry-index?
  265. popup-entry-name?) a)
  266. (set-car!
  267. (list-ref name-action-list (cadr a))
  268. (caddr a)))
  269. ((match? (REPLACE-ACTION entry-index?
  270. popup-entry-action?) a)
  271. (set-car!
  272. (cdr (list-ref name-action-list
  273. (cadr a)))
  274. (caddr a)))
  275. ((match? (FOREGROUND color?) a)
  276. (set! foreground (cadr a)))
  277. ((match? (BACKGROUND color?) a)
  278. (set! background (cadr a)))
  279. ((match? (FONT string?) a)
  280. (set! font (cadr a)))
  281. ((equal? '(DELETE-OBJECT) a)
  282. (set! delete #t))
  283. (else (ezd-error 'slider
  284. "Illegal attribute: ~s" a))))
  285. *user-event-misc*)
  286. (if delete
  287. (ezd `(delete-window ,popup-name)
  288. '(save-drawing)
  289. `(set-drawing ,popup-name)
  290. '(clear)
  291. '(restore-drawing))
  292. (begin (if visible (make-invisible))
  293. (configure-popup)))))
  294. ;;; Install POPITUP as the top-level value of the value of POPUP-NAME.
  295. (configure-popup)
  296. (set-top-level-value! popup-name popitup))
  297. ;;; Booleans for command and attribute parsing.
  298. (define (POPUP-ENTRY-NAME? x) (or (string? x) (procedure? x) (pair? x)))
  299. (define (POPUP-ENTRY-ACTION? x) (or (pair? x) (procedure? x) (eq? x #f)))
  300. (define-ezd-command
  301. `(define-popup ,symbol? (repeat ,popup-entry-name? ,popup-entry-action?)
  302. (optional ,color? ,color?) (optional ,string?))
  303. "(define-popup popup-name item-action-list [foreground background] [\"font\"])"
  304. define-popup)