Update of /project/mcclim/cvsroot/mcclim
In directory clnet:/tmp/cvs-serv21686
Modified Files:
presentations.lisp menu-choose.lisp input-editing.lisp
builtin-commands.lisp
Log Message:
Improved the implementation of `menu-choose' - now supports almost all
features demanded by the spec (though some in a nonoptimal
way). Changed a few calls to `menu-choose' in McCLIM to utilize
labels.
--- /project/mcclim/cvsroot/mcclim/presentations.lisp 2006/03/20 08:15:26 1.76
+++ /project/mcclim/cvsroot/mcclim/presentations.lisp 2006/08/05 19:54:31 1.77
@@ -1880,6 +1880,7 @@
(setq items (nreverse items))
(multiple-value-bind (item object event)
(menu-choose items
+ :label label
:associated-window window
:printer #'(lambda (item stream)
(document-presentation-translator
--- /project/mcclim/cvsroot/mcclim/menu-choose.lisp 2006/03/29 10:43:37 1.18
+++ /project/mcclim/cvsroot/mcclim/menu-choose.lisp 2006/08/05 19:54:31 1.19
@@ -29,22 +29,15 @@
;;; Mid time TODO:
;;;
-;;; - Menu item options: :active.
-;;;
;;; - Documentation.
;;;
-;;; - Menu position.
-;;;
;;; - Empty menu.
-
-;;; TODO:
;;;
-;;; + returned values
-;;; + menu frame size
-;;; + layout
+;;; - :DIVIDER type menu items.
(in-package :clim-internals)
+;; Spec function.
(defgeneric menu-choose
(items
&key associated-window printer presentation-type default-item
@@ -52,6 +45,7 @@
max-width max-height n-rows n-columns x-spacing y-spacing row-wise
cell-align-x cell-align-y scroll-bars pointer-documentation))
+;; Spec function.
(defgeneric frame-manager-menu-choose
(frame-manager items
&key associated-window printer presentation-type default-item
@@ -59,12 +53,18 @@
max-width max-height n-rows n-columns x-spacing y-spacing row-wise
cell-align-x cell-align-y scroll-bars pointer-documentation))
+;; Spec function.
(defgeneric menu-choose-from-drawer
(menu presentation-type drawer
&key x-position y-position cache unique-id id-test cache-value cache-test
default-presentation pointer-documentation))
-;;;
+(defgeneric adjust-menu-size-and-position (menu &key x-position y-position)
+ (:documentation "Adjust the size of the menu so it fits
+ properly on the screen with regards to the menu entries. `menu'
+ should be the menu pane. This is an internal,
+ non-specification-defined function."))
+
(defun menu-item-value (menu-item)
(cond ((atom menu-item)
menu-item)
@@ -84,7 +84,9 @@
nil))
(defun menu-item-option (menu-item option &optional default)
- (getf (menu-item-options menu-item) option default))
+ (if (listp menu-item)
+ (getf (menu-item-options menu-item) option default)
+ default))
(defun print-menu-item (menu-item &optional (stream *standard-output*))
(let ((style (getf (menu-item-options menu-item) :style '(nil nil nil))))
@@ -101,6 +103,7 @@
(medium-background stream)))
(princ (menu-item-display menu-item) stream))))))
+;; Spec function.
(defun draw-standard-menu
(stream presentation-type items default-item
&key item-printer
@@ -110,20 +113,39 @@
(orf item-printer #'print-menu-item)
(format-items items
:stream stream
- :printer (lambda (item stream)
- (let ((activep (menu-item-option item :active t)))
- (with-presentation-type-decoded (name params options)
- presentation-type
- (let ((*allow-sensitive-inferiors* activep))
- (with-text-style (stream (or (getf (menu-item-options item) :style)
- '(:sans-serif nil nil)))
- (with-output-as-presentation
- (stream
- item
- `((,name ,@params)
- :description ,(getf (menu-item-options item) :documentation)
- ,@options))
- (funcall item-printer item stream)))))))
+ :printer
+ (lambda (item stream)
+ (ecase (menu-item-option item :type :item)
+ (:item
+ ;; This is a normal item, just output.
+ (let ((activep (menu-item-option item :active t)))
+ (with-presentation-type-decoded (name params options)
+ presentation-type
+ (let ((*allow-sensitive-inferiors* activep))
+ (with-text-style
+ (stream (menu-item-option
+ item :style
+ '(:sans-serif nil nil)))
+ (with-output-as-presentation
+ (stream
+ item
+ `((,name ,@params)
+ :description ,(getf (menu-item-options item) :documentation)
+ ,@options))
+ (funcall item-printer item stream)))))))
+ (:label
+ ;; This is a static label, it should not be
+ ;; mouse-sensitive, but not grayed out either.
+ (with-text-style (stream (menu-item-option
+ item :style
+ '(:sans-serif nil nil)))
+ (funcall item-printer item stream)))
+ (:divider
+ ;; FIXME: Should draw a line instead.
+ (with-text-style (stream (menu-item-option
+ item :style
+ '(:sans-serif :italic nil)))
+ (funcall item-printer item stream)))))
:presentation-type nil
:x-spacing x-spacing
:y-spacing y-spacing
@@ -135,7 +157,7 @@
:cell-align-y (or cell-align-y :top)
:row-wise row-wise))
-
+;; Spec macro.
(defmacro with-menu ((menu &optional associated-window
&key (deexpose t) label scroll-bars)
&body body)
@@ -148,37 +170,38 @@
,associated-window ; XXX
',deexpose ; XXX!!!
,label
- ,scroll-bars))))
+ ,scroll-bars))))
(defun invoke-with-menu (continuation associated-window deexpose
label scroll-bars)
- (declare (ignore deexpose label scroll-bars)) ; FIXME!!!
(let* ((associated-frame (if associated-window
(pane-frame associated-window)
*application-frame*))
(fm (frame-manager associated-frame)))
(with-look-and-feel-realization (fm associated-frame) ; hmm... checkme
- (let* ((stream (make-pane-1 fm associated-frame 'command-menu-pane
- :background +gray80+))
- (raised (make-pane-1 fm associated-frame 'raised-pane
- :border-width 2 :background +gray80+
- :contents (list stream)))
- (frame (make-menu-frame raised
- :left nil
- :top nil)))
- (adopt-frame fm frame)
- (change-space-requirements stream :width 1 :height 1) ;What is that supposed to do? --GB 2003-03-16
- ; Shadow bug somewhere else?
- (unwind-protect
- (progn
- (setf (stream-end-of-line-action stream) :allow
- (stream-end-of-page-action stream) :allow)
- (funcall continuation stream))
- (disown-frame fm frame))))))
+ (let* ((menu-stream (make-pane-1 fm associated-frame 'clim-stream-pane
+ :background +gray80+))
+ (container (scrolling (:scroll-bar scroll-bars)
+ menu-stream))
+ (frame (make-menu-frame (if label
+ (labelling (:label label
+ :label-alignment :top
+ :background +gray80+)
+ container)
+ container)
+ :left nil
+ :top nil)))
+ (adopt-frame fm frame)
+ (unwind-protect
+ (progn
+ (setf (stream-end-of-line-action menu-stream) :allow
+ (stream-end-of-page-action menu-stream) :allow)
+ (funcall continuation menu-stream))
+ (when deexpose ; Checkme as well.
+ (disown-frame fm frame)))))))
(define-presentation-type menu-item ())
-;;;
(defmethod menu-choose
(items &rest args &key associated-window &allow-other-keys)
(let* ((associated-frame (if associated-window
@@ -193,8 +216,10 @@
&key associated-window printer presentation-type
(default-item nil default-item-p)
text-style label cache unique-id id-test cache-value cache-test
- max-width max-height n-rows n-columns x-spacing y-spacing row-wise
- cell-align-x cell-align-y scroll-bars pointer-documentation)
+ max-width max-height n-rows (n-columns 1) x-spacing y-spacing row-wise
+ cell-align-x cell-align-y (scroll-bars :vertical)
+ ;; We provide pointer documentation by default.
+ (pointer-documentation *pointer-documentation-output*))
(flet ((drawer (stream type)
(draw-standard-menu stream type items
(if default-item-p
@@ -214,7 +239,9 @@
:cell-align-x cell-align-x
:cell-align-y cell-align-y)))
(multiple-value-bind (object event)
- (with-menu (menu associated-window)
+ (with-menu (menu associated-window
+ :label label
+ :scroll-bars scroll-bars)
(when text-style
(setf (medium-text-style menu) text-style))
(letf (((stream-default-view menu) +textual-menu-view+))
@@ -226,59 +253,127 @@
:cache-value cache-value
:cache-test cache-test
:pointer-documentation pointer-documentation)))
- (let ((subitems (menu-item-option object :items 'menu-item-no-items)))
- (if (eq subitems 'menu-item-no-items)
- (values (menu-item-value object) object event)
- (apply #'frame-manager-menu-choose
- frame-manager subitems
- options))))))
-
-#+NIL
-(defmethod menu-choose-from-drawer
- (menu presentation-type drawer
- &key x-position y-position cache unique-id id-test cache-value cache-test
- default-presentation pointer-documentation)
- (funcall drawer menu presentation-type)
- (when (typep menu 'command-menu-pane)
- (with-bounding-rectangle* (x1 y1 x2 y2)
- (stream-output-history menu)
- (declare (ignorable x1 y1 x2 y2))
- (change-space-requirements menu
- :width x2
- :height y2
- :resize-frame t)))
- (let ((*pointer-documentation-output* pointer-documentation))
- (handler-case
- (with-input-context (presentation-type :override t)
- (object type event)
- (loop (read-gesture :stream menu))
- (t (values object event)))
- (abort-gesture () (values nil)))))
+ (unless (null event) ; Event is NIL if user aborted.
+ (let ((subitems (menu-item-option object :items 'menu-item-no-items)))
+ (if (eq subitems 'menu-item-no-items)
+ (values (menu-item-value object) object event)
+ (apply #'frame-manager-menu-choose
+ frame-manager subitems
+ options)))))))
+
+(defun max-x-y (frame)
+ "Return the maximum X and Y coordinate values for a menu for
+`frame' (essentially, the screen resolution with a slight
+padding.)"
+ ;; FIXME? There may be a better way.
+ (let* ((port (frame-manager-port (frame-manager frame)))
+ (graft (find-graft :port port)))
+ (values (- (graft-width graft) 50)
+ (- (graft-height graft) 50))))
+
+(defun menu-size (menu frame)
+ "Return two values, the height and width of MENU (adjusted for
+maximum size according to `frame')."
+ (multiple-value-bind (max-width max-height)
+ (max-x-y frame)
+ (with-bounding-rectangle* (x1 y1 x2 y2) menu
+ (declare (ignore x1 y1))
+ (values (min x2 max-width)
+ (min y2 max-height)))))
+
+(defmethod adjust-menu-size-and-position ((menu clim-stream-pane)
+ &key x-position y-position)
+ ;; Make sure the menu isn't higher or wider than the screen.
+ (multiple-value-bind (menu-width menu-height)
+ (menu-size (stream-output-history menu) *application-frame*)
+ (change-space-requirements menu
+ :width menu-width
+ :height menu-height
+ :resize-frame t)
+
+ ;; If we have scroll-bars, we need to do some calibration of the
+ ;; size of the viewport.
+ (when (pane-viewport menu)
+ (multiple-value-bind (viewport-width viewport-height)
+ (menu-size (pane-viewport menu) *application-frame*)
+ (change-space-requirements (pane-scroller menu)
+ ;; HACK: How are you supposed to
+ ;; change the size of the viewport?
+ ;; I could only find this way, where
+ ;; I calculate the size difference
+ ;; between the viewport and the
+ ;; scroller pane, and set the
+ ;; scroller pane to the desired size
+ ;; of the viewport, plus the
+ ;; difference (to make room for
+ ;; scroll bars).
+ :width (+ menu-width
+ (- (pane-current-width (pane-scroller menu))
+ viewport-width))
+ :height (+ menu-height
+ (- (pane-current-height (pane-scroller menu))
+ viewport-height))
+ :resize-frame t)))
+
+ ;; Modify the size and location of the frame as well.
+ (let* ((label-pane (sheet-parent (pane-scroller menu)))
+ (top-level-pane (sheet-parent label-pane)))
+ (when (not (typep label-pane 'label-pane))
+ ;; Oops, we have no label. Rebind...
+ (setf top-level-pane label-pane)
+ (setf label-pane nil))
+ (multiple-value-bind (frame-width frame-height)
+ (menu-size top-level-pane *application-frame*)
+ (multiple-value-bind (res-max-x res-max-y) (max-x-y *application-frame*)
+ ;; Move the menu frame so that no entries are outside the visible
+ ;; part of the screen.
+ (let ((max-left (- res-max-x frame-width))
+ (max-top (- res-max-y frame-height)))
+ ;; XXX: This is an ugly way to find the screen position of
+ ;; the menu frame, possibly even undefined.
+ (multiple-value-bind (left top)
+ (with-slots (dx dy) (sheet-transformation top-level-pane)
+ (values dx dy))
+ (when x-position
+ (setf left x-position))
+ (when y-position
+ (setf top y-position))
+ ;; Adjust for maximum position if the programmer has not
+ ;; explicitly provided coordinates.
+ (if (null x-position)
+ (when (> left max-left)
+ (setf left max-left)))
+ (if (null y-position)
+ (when (> top max-top)
+ (setf top max-top)))
+ (move-sheet top-level-pane
+ (max left 0) (max top 0)))))))))
+
+(defmethod adjust-menu-size-and-position (menu &key &allow-other-keys)
+ ;; Nothing.
+ nil)
+;; Spec function.
(defmethod menu-choose-from-drawer
(menu presentation-type drawer
&key x-position y-position cache unique-id id-test cache-value cache-test
default-presentation pointer-documentation)
+ (declare (ignore cache unique-id
+ id-test cache-value cache-test default-presentation))
(with-room-for-graphics (menu :first-quadrant nil)
(funcall drawer menu presentation-type))
- (when (typep menu 'command-menu-pane)
- (with-bounding-rectangle* (x1 y1 x2 y2)
- (stream-output-history menu)
- (declare (ignorable x1 y1 x2 y2))
- (change-space-requirements menu
- :width x2
- :height y2
- :resize-frame t)))
- (let ((*pointer-documentation-output* pointer-documentation))
- (tracking-pointer (menu :context-type presentation-type
- :multiple-window t :highlight t)
- (:pointer-button-press (&key event x y) ; Close if pointer clicked outside menu.
- (unless (and (sheet-ancestor-p (event-sheet event) menu)
- (region-contains-position-p (sheet-region menu) x y))
- (return-from menu-choose-from-drawer (values nil))))
- (:presentation-button-release (&key event presentation x y)
- (if (and (sheet-ancestor-p (event-sheet event) menu)
- (region-contains-position-p (sheet-region menu) x y))
- (return-from menu-choose-from-drawer
- (values (presentation-object presentation) event))
- (return-from menu-choose-from-drawer (values nil)))))))
+
+ (adjust-menu-size-and-position
+ menu
+ :x-position x-position
+ :y-position y-position)
+
+ (let ((*pointer-documentation-output* pointer-documentation))
+ (let ((*pointer-documentation-output* pointer-documentation))
+ (handler-case
+ (with-input-context (`(or ,presentation-type blank-area) :override t)
+ (object type event)
+ (prog1 nil (read-gesture :stream menu))
+ (blank-area nil)
+ (t (values object event)))
+ (abort-gesture () nil)))))
--- /project/mcclim/cvsroot/mcclim/input-editing.lisp 2006/05/05 10:24:02 1.51
+++ /project/mcclim/cvsroot/mcclim/input-editing.lisp 2006/08/05 19:54:31 1.52
@@ -620,7 +620,9 @@
nmatches mode))
(when (and (> nmatches 0) (eq mode :possibilities))
(multiple-value-bind (menu-object item event)
- (menu-choose (possibilities-for-menu possibilities))
+ (menu-choose (possibilities-for-menu possibilities)
+ :label "Possibilities"
+ :n-columns 1)
(declare (ignore event))
(if item
(progn
--- /project/mcclim/cvsroot/mcclim/builtin-commands.lisp 2006/03/20 08:15:26 1.22
+++ /project/mcclim/cvsroot/mcclim/builtin-commands.lisp 2006/08/05 19:54:31 1.23
@@ -133,7 +133,9 @@
(presentation frame window x y)
(call-presentation-menu presentation *input-context*
frame window x y
- :for-menu t))
+ :for-menu t
+ :label (format nil "Operation on ~A"
+ (presentation-type presentation))))
;;; Action for possibilities menu of complete-input
;;;