1
;;; -*- Mode:Lisp; Syntax: Common-lisp; Package:XLIB; Base:10; Lowercase: Yes -*-
4
;;; TEXAS INSTRUMENTS INCORPORATED
6
;;; AUSTIN, TEXAS 78769
8
;;; Copyright (C) 1988 Texas Instruments Incorporated.
10
;;; Permission is granted to any individual or institution to use, copy, modify,
11
;;; and distribute this software, provided that this complete copyright and
12
;;; permission notice is maintained, intact, in all copies and supporting
15
;;; Texas Instruments Incorporated provides this software "as is" without
16
;;; express or implied warranty.
22
;;;----------------------------------------------------------------------------------+
24
;;; These functions demonstrate a simple menu implementation described in |
25
;;; Kimbrough, Kerry, "Windows to the Future", Lisp Pointers, Oct-Nov, 1987. |
26
;;; See functions JUST-SAY-LISP and POP-UP for demonstrations. |
28
;;;----------------------------------------------------------------------------------+
33
"A simple menu of text strings."
34
(title "choose an item:")
35
item-alist ;((item-window item-string))
42
(geometry-changed-p t)) ;nil iff unchanged since displayed
46
(defun create-menu (parent-window text-color background-color text-font)
48
;; Create menu graphics context
49
:gcontext (CREATE-GCONTEXT :drawable parent-window
50
:foreground text-color
51
:background background-color
54
:window (CREATE-WINDOW
59
:width 16 ;temporary value
60
:height 16 ;temporary value
63
:background background-color
65
:override-redirect :on ;override window mgr when positioning
66
:event-mask (MAKE-EVENT-MASK :leave-window
70
(defun menu-set-item-list (menu &rest item-strings)
71
;; Assume the new items will change the menu's width and height
72
(setf (menu-geometry-changed-p menu) t)
74
;; Destroy any existing item windows
75
(dolist (item (menu-item-alist menu))
76
(DESTROY-WINDOW (first item)))
78
;; Add (item-window item-string) elements to item-alist
79
(setf (menu-item-alist menu)
81
(dolist (item item-strings (nreverse alist))
82
(push (list (CREATE-WINDOW
83
:parent (menu-window menu)
86
:width 16 ;temporary value
87
:height 16 ;temporary value
88
:background (GCONTEXT-BACKGROUND (menu-gcontext menu))
89
:event-mask (MAKE-EVENT-MASK :enter-window
96
(defparameter *menu-item-margin* 4
97
"Minimum number of pixels surrounding menu items.")
100
(defun menu-recompute-geometry (menu)
101
(when (menu-geometry-changed-p menu)
102
(let* ((menu-font (GCONTEXT-FONT (menu-gcontext menu)))
103
(title-width (TEXT-EXTENTS menu-font (menu-title menu)))
104
(item-height (+ (FONT-ASCENT menu-font) (FONT-DESCENT menu-font)))
106
(items (menu-item-alist menu))
109
;; Find max item string width
110
(dolist (next-item items)
111
(setf item-width (max item-width
112
(TEXT-EXTENTS menu-font (second next-item)))))
114
;; Compute final menu width, taking margins into account
115
(setf menu-width (max title-width
116
(+ item-width *menu-item-margin* *menu-item-margin*)))
117
(let ((window (menu-window menu))
118
(delta-y (+ item-height *menu-item-margin*)))
120
;; Update width and height of menu window
122
(setf (DRAWABLE-WIDTH window) menu-width
123
(DRAWABLE-HEIGHT window) (+ *menu-item-margin*
124
(* (1+ (length items))
127
;; Update width, height, position of item windows
128
(let ((item-left (round (- menu-width item-width) 2))
129
(next-item-top delta-y))
130
(dolist (next-item items)
131
(let ((window (first next-item)))
133
(setf (DRAWABLE-HEIGHT window) item-height
134
(DRAWABLE-WIDTH window) item-width
135
(DRAWABLE-X window) item-left
136
(DRAWABLE-Y window) next-item-top)))
137
(incf next-item-top delta-y))))
139
;; Map all item windows
140
(MAP-SUBWINDOWS (menu-window menu))
142
;; Save item geometry
143
(setf (menu-item-width menu) item-width
144
(menu-item-height menu) item-height
145
(menu-width menu) menu-width
146
(menu-title-width menu) title-width
147
(menu-geometry-changed-p menu) nil))))
150
(defun menu-refresh (menu)
151
(let* ((gcontext (menu-gcontext menu))
152
(baseline-y (FONT-ASCENT (GCONTEXT-FONT gcontext))))
154
;; Show title centered in "reverse-video"
155
(let ((fg (GCONTEXT-BACKGROUND gcontext))
156
(bg (GCONTEXT-FOREGROUND gcontext)))
157
(WITH-GCONTEXT (gcontext :foreground fg :background bg)
161
(round (- (menu-width menu)
162
(menu-title-width menu)) 2) ;start x
166
;; Show each menu item (position is relative to item window)
167
(dolist (item (menu-item-alist menu))
169
(first item) gcontext
175
(defun menu-choose (menu x y)
176
;; Display the menu so that first item is at x,y.
177
(menu-present menu x y)
179
(let ((items (menu-item-alist menu))
180
(mw (menu-window menu))
183
;; Event processing loop
184
(do () (selected-item)
185
(EVENT-CASE ((DRAWABLE-DISPLAY mw) :force-output-p t)
188
;; Discard all but final :exposure then display the menu
189
(when (zerop count) (menu-refresh menu))
192
(:button-release (event-window)
194
(setf selected-item (second (assoc event-window items)))
197
(:enter-notify (window)
199
(let ((position (position window items :key #'first)))
201
(menu-highlight-item menu position)))
204
(:leave-notify (window kind)
206
;; Quit if pointer moved out of main menu window
207
(setf selected-item (when (eq kind :ancestor) :none))
209
;; Otherwise, unhighlight the item window left
210
(let ((position (position window items :key #'first)))
212
(menu-unhighlight-item menu position))))
216
;;Ignore and discard any other event
222
;; Return selected item string, if any
223
(unless (eq selected-item :none) selected-item)))
226
(defun menu-highlight-item (menu position)
227
(let* ((box-margin (round *menu-item-margin* 2))
228
(left (- (round (- (menu-width menu) (menu-item-width menu)) 2)
230
(top (- (* (+ *menu-item-margin* (menu-item-height menu))
233
(width (+ (menu-item-width menu) box-margin box-margin))
234
(height (+ (menu-item-height menu) box-margin box-margin)))
236
;; Draw a box in menu window around the given item.
237
(DRAW-RECTANGLE (menu-window menu)
242
(defun menu-unhighlight-item (menu position)
243
;; Draw a box in the menu background color
244
(let ((gcontext (menu-gcontext menu)))
245
(WITH-GCONTEXT (gcontext :foreground (gcontext-background gcontext))
246
(menu-highlight-item menu position))))
249
(defun menu-present (menu x y)
250
;; Make sure menu geometry is up-to-date
251
(menu-recompute-geometry menu)
253
;; Try to center first item at the given location, but
254
;; make sure menu is completely visible in its parent
255
(let ((menu-window (menu-window menu)))
256
(multiple-value-bind (tree parent) (QUERY-TREE menu-window)
257
(declare (ignore tree))
259
(let* ((parent-width (DRAWABLE-WIDTH parent))
260
(parent-height (DRAWABLE-HEIGHT parent))
261
(menu-height (+ *menu-item-margin*
262
(* (1+ (length (menu-item-alist menu)))
263
(+ (menu-item-height menu) *menu-item-margin*))))
264
(menu-x (max 0 (min (- parent-width (menu-width menu))
265
(- x (round (menu-width menu) 2)))))
266
(menu-y (max 0 (min (- parent-height menu-height)
267
(- y (round (menu-item-height menu) 2/3)
268
*menu-item-margin*)))))
269
(WITH-STATE (menu-window)
270
(setf (DRAWABLE-X menu-window) menu-x
271
(DRAWABLE-Y menu-window) menu-y)))))
274
(MAP-WINDOW menu-window)))
276
(defun just-say-lisp (&optional (font-name "fixed"))
277
(let* ((display (open-default-display))
278
(screen (first (DISPLAY-ROOTS display)))
279
(fg-color (SCREEN-BLACK-PIXEL screen))
280
(bg-color (SCREEN-WHITE-PIXEL screen))
281
(nice-font (OPEN-FONT display font-name))
282
(a-menu (create-menu (screen-root screen) ;the menu's parent
283
fg-color bg-color nice-font)))
285
(setf (menu-title a-menu) "Please pick your favorite language:")
286
(menu-set-item-list a-menu "Fortran" "APL" "Forth" "Lisp")
288
;; Bedevil the user until he picks a nice programming language
291
((and (setf choice (menu-choose a-menu 100 100))
292
(string-equal "Lisp" choice))))
294
(CLOSE-DISPLAY display))))
297
(defun pop-up (host strings &key (title "Pick one:") (font "fixed"))
298
(let* ((display (OPEN-DISPLAY host))
299
(screen (first (DISPLAY-ROOTS display)))
300
(fg-color (SCREEN-BLACK-PIXEL screen))
301
(bg-color (SCREEN-WHITE-PIXEL screen))
302
(font (OPEN-FONT display font))
305
(parent (CREATE-WINDOW :parent (SCREEN-ROOT screen)
306
:override-redirect :on
308
:width parent-width :height parent-height
310
:event-mask (MAKE-EVENT-MASK :button-press
312
(a-menu (create-menu parent fg-color bg-color font))
313
(prompt "Press a button...")
314
(prompt-gc (CREATE-GCONTEXT :drawable parent
318
(prompt-y (FONT-ASCENT font))
319
(ack-y (- parent-height (FONT-DESCENT font))))
321
(setf (menu-title a-menu) title)
322
(apply #'menu-set-item-list a-menu strings)
324
;; Present main window
327
(flet ((display-centered-text
328
(window string gcontext height width)
329
(multiple-value-bind (w a d l r fa fd) (text-extents gcontext string)
330
(declare (ignore a d l r))
331
(let ((box-height (+ fa fd)))
333
;; Clear previous text
335
:x 0 :y (- height fa)
336
:width width :height box-height)
339
(DRAW-IMAGE-GLYPHS window gcontext (round (- width w) 2) height string)))))
343
(EVENT-CASE (display :force-output-p t)
349
(display-centered-text
360
(let ((choice (menu-choose a-menu x y)))
362
(display-centered-text
364
(format nil "You have selected ~a." choice)
369
(display-centered-text
371
"No selection...try again."
378
;;Ignore and discard any other event
381
(CLOSE-DISPLAY display)))))