;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: GARNET-GADGETS; Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;         The Garnet User Interface Development Environment.      ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code was written as part of the Garnet project at          ;;;
;;; Carnegie Mellon University, and has been placed in the public   ;;;
;;; domain.  If you are using this code or any part of Garnet,      ;;;
;;; please contact garnet@cs.cmu.edu to be put on the mailing list. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;;  Pull Down Menu Function
;;;
;;;
;;;  This file contains the functions that are applicable to BOTH the
;;;  motif-menubar and the GARNET menubar.  It is loaded in the loader
;;;  files for both the menubars.  What used to be functions for the
;;;  menubars are now METHODS.  The macros in this file just call the
;;;  appropriate method.
;;;
;;;  CHANGE LOG:
;;;
;;;  06/12/93 Andrew Mickish - Added Check-Bar-Item
;;;  06/10/93 Andrew Mickish - opal::safe-functionp ---> gu:safe-functionp
;;;  03/17/93 Brad Myers - add special-popdown functions for gilt, etc.
;;;  03/12/93 Andrew Mickish - Now Check-Menubar-Items allows empty lists of
;;;             submenu items
;;;  03/08/93 Andrew Mickish - Added error-checking: Check-Menubar-Items
;;;  03/03/93 Andrew Mickish - Added Menubar-Change-Item
;;;  01/07/93 Rajan Parthasarathy - Created
;;;

(in-package "GARNET-GADGETS" :use '("LISP" "KR"))

(export '(Add-Submenu-Item Remove-Submenu-Item
	  Menubar-Components Submenu-Components Get-Submenu-Component
	  Find-Submenu-Component Get-Bar-Component Set-Menubar Set-Submenu
	  Menubar-Enable-Component Menubar-Disable-Component
	  Menubar-Enabled-P Menubar-Installed-P
	  Menubar-Get-Title Menubar-Set-Title))
	  
;;; This is the DESTROYER (*gasp*) of the menubars

(defun MENUBAR-DESTROY (a-menubar &optional erase)
  ; first, make sure no submenus are popped up
  (kr-send a-menubar :special-popdown-func a-menubar)
  ; Destroy each window that is associated with this menubar
  (dolist (win (g-value a-menubar :submenu-window-list))
    (opal:destroy win))
  ; Destroy the menubar itself
  (call-prototype-method a-menubar erase))

;;; This is the macro that calls add-submenu-item

(defmacro Add-Submenu-Item (a-menubar b-item s-item &rest args)
  `(let ((my-menubar ,a-menubar))
    (kr-send my-menubar :add-submenu-item my-menubar ,b-item ,s-item ,@args)))

;;; And this, of course, is the macro that calls remove-submenu-item

(defmacro Remove-Submenu-Item (a-menubar b-item s-item)
  `(let ((my-menubar ,a-menubar))  
    (kr-send my-menubar :remove-submenu-item my-menubar ,b-item ,s-item)))

;;; This one.. for menubar-components.  The next few are self-explanatory.

(defmacro Menubar-Components (a-menubar)
  `(let ((my-menubar ,a-menubar))  
    (kr-send my-menubar :menubar-components my-menubar)))

(defmacro Submenu-Components (a-bar-item)
  `(let ((my-bar-item ,a-bar-item))  
    (kr-send my-bar-item :submenu-components my-bar-item)))

(defmacro Get-Submenu-Component (a-bar-item item)
  `(let ((my-bar-item ,a-bar-item))    
    (kr-send my-bar-item :get-submenu-component my-bar-item ,item)))

(defmacro Find-Submenu-Component (a-menubar submenu-title submenu-item)
  `(let ((my-menubar ,a-menubar))  
    (kr-send my-menubar :find-submenu-component my-menubar ,submenu-title ,submenu-item)))

(defmacro Set-Menubar (a-menubar new-menus)
  `(let ((my-menubar ,a-menubar))  
    (kr-send my-menubar :set-menubar my-menubar ,new-menus)))

;;; This function is IDENTICAL for both the motif and regular menubars.
;;; So we just define it here and s-value it into the :set-submenu-fn
;;; slot of both menubars.  The macro will then call the correct function.

(defun Set-Submenu-Fn (a-bar-item new-desc)
  (let ((components (copy-list (Submenu-Components a-bar-item))))
    (dolist (old-comp components)
      (opal:remove-item a-bar-item old-comp)))
  (dolist (comp new-desc)
    (opal:add-item a-bar-item comp)))

(defmacro Set-Submenu (a-bar-item new-desc)
  `(let ((my-bar-item ,a-bar-item))
    (kr-send my-bar-item :set-submenu-fn my-bar-item ,new-desc)))

;;; Same as above for Get-Bar-Component

(defun Get-Bar-Component-Fn (a-menubar item)
  (find-if #'(lambda (a-bar-item)
	       (if (listp item)
		   (equal item (g-value a-bar-item :desc))
		   (equal item (g-value a-bar-item :menu-obj))))
	   (Menubar-Components a-menubar)))

(defmacro Get-Bar-Component (a-menubar item)
  `(let ((my-menubar ,a-menubar))  
    (kr-send my-menubar :get-bar-component-fn my-menubar ,item)))

;;; The following four functions are identical to both the menubars.
;;; Not only that, they are extremely primitive.  And since they
;;; work for BOTH menubars, we don't need to have a macro or a
;;; define method.  We can just use them as they are!

(defun Menubar-Disable-Component (menubar-component)
  (s-value menubar-component :enabled NIL))

(defun Menubar-Enable-Component (menubar-component)
  (s-value menubar-component :enabled T))
  
(defun Menubar-Enabled-P (menubar-component)
  (g-value menubar-component :enabled))
  
(defun Menubar-Installed-P (menubar-component)
  (g-value menubar-component :parent))

;;; This macro is to call the get-title function.  Now, menubar-component
;;; can be a BAR-ITEM, SUBMENU-ITEM, MOTIF-BAR-ITEM, or MOTIF-MENU-ITEM.
;;; BAR-ITEM and SUBMENU-ITEM have the same function in their :get-title-fn,
;;; as do MOTIF-BAR-ITEM and MOTIF-MENU-ITEM.  

(defmacro Menubar-Get-Title (menubar-component)
  `(let ((menubar-comp ,menubar-component))  
    (kr-send menubar-comp :get-title-fn menubar-comp)))

;;; This macro is for the set-title.  Works on the same principles as the previous
;;; macro.

(defmacro Menubar-Set-Title (menubar-component string)
  `(let ((menubar-comp ,menubar-component))    
    (kr-send menubar-comp :set-title-fn menubar-comp ,string)))


;; The :change-item method for both menubars
;;
(defun Menubar-Change-Item (a-bar-item new-item n)
  ;; Never use the aggrelist trick of just setting the :items list, even
  ;; though the two items are of the same type.  We have to do elaborate
  ;; bookkeeping in the higher-level menubar :items list with the
  ;; add/remove-item methods.
  (let ((items (g-value a-bar-item :items)))
    (if (or (>= n (length items))
	    (< n 0))
	(warn "Bad index in change-item: ~A" n)
	(progn
	  (opal:remove-item a-bar-item (nth n items))
	  (opal:add-item a-bar-item new-item :at n)))))


(defun Check-Bar-Item (top-level-item)
  (let ((bar-item-title (first top-level-item))
	(bar-item-action (second top-level-item))
	(submenu-items (third top-level-item)))
    (unless (or (stringp bar-item-title) (keywordp bar-item-title)
		(schema-p bar-item-title))
      (error "Got ~S for the title of a bar-item, but it should be a string, keyword, or schema." bar-item-title))
    (unless (or (null bar-item-action)
		(and (symbolp bar-item-action)
		     (not (keywordp bar-item-action)))
		(gu:safe-functionp bar-item-action))
      (error "Got ~S for the action of a bar-item, but it should be a function, symbol, or NIL." bar-item-action))
    
    (unless (listp submenu-items)
      (error "Got ~S for a list of submenu items." submenu-items))
    (dolist (submenu-item submenu-items)
      (unless (consp submenu-item)
	(error "Got ~S for a submenu-item description, but it should be a list." submenu-item))
      (let ((submenu-item-title (first submenu-item))
	    (submenu-item-action (second submenu-item)))
	(unless (or (stringp submenu-item-title)
		    (keywordp submenu-item-title)
		    (schema-p submenu-item-title))
	  (error "Got ~S for a submenu-item, but it should be a string, keyword, or schema." submenu-item-title))
	(unless (or (null submenu-item-action)
		    (and (symbolp submenu-item-action)
			 (not (keywordp submenu-item-action)))
		    (gu:safe-functionp submenu-item-action))
	  (error "Got ~S for the action of a submenu-item, but it should be a function, symbol, or NIL." submenu-item-action)))))
  )
  
(defun Check-Menubar-Items (items)
  (or (null items)
      (dolist (top-level-item items)
	(Check-Bar-Item top-level-item)))
  T) ;; Would crash inside Check-Bar-Item if there was a problem.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; Put down the display of the submenu, if popped up from gilt, etc.
;;; Works for both motif and regular menubars
      
(defun Put-Down-MenuBar-Popups (gadget)
  (let (obj)
    (when (schema-p gadget)
    (s-value gadget :*bar-item-popped-up NIL)
    (when (and (setq obj (g-value gadget :menubar-select))
	       (schema-p obj))
      (s-value obj :prev-baritem NIL))
      (dolist (bar-item (Menubar-Components gadget))
	(when (and (schema-p bar-item)
		   (setq obj (g-value bar-item :submenu-window))
		   (schema-p obj))
	  (s-value obj :visible NIL))))))

(defun DoSpecialPopUpMenubar (gadget bar-item)
  (unless (eq bar-item (g-value gadget :*bar-item-popped-up))
    (Put-Down-MenuBar-Popups gadget)	; put any other sub-menus down
    (s-value gadget :*bar-item-popped-up bar-item)
    (let ((win (g-value bar-item :submenu-window)))
      (if win
	  (let* ((inter (g-value gadget :menubar-select))
		 (prev (g-value inter :prev-baritem)))
	    (when prev
	      (s-value prev :interim-selected NIL))
	    (s-value inter :prev-baritem bar-item)
	    (s-value (g-value bar-item :submenu) :value-obj NIL)
	    (s-value win :visible T)
	    (opal:raise-window win)
	    (opal:update-all)
	    win)			; return window if successful
	  ;; else error
	  (progn
	    (format T "**Menubar ~s item ~s window=NIL; not initialized?~%"
		    gadget bar-item)
	    NIL)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

