;; fvwm-menu.gwm --- Menu constructors for the FVWM profile
;;
;; Author: Anders Holst  (aho@sans.kth.se)  
;; Copyright (C) 1999  Anders Holst
;; Last change: 2/5 1999
;;
;; This file is copyrighted under the same terms as the rest of GWM
;; (see the X Inc license for details). There is no warranty that it
;; works. 
;;
;; --------------------------------------------------------------------- 
;;
;; This is mainly just a copy of vtwm-menu.gwm, with slight adjustment
;; of the menu look. Note that the menu constructors defined at the end
;; collides with the same functions in vtwm-menu.gwm, so only one of
;; the files should be loaded.
;;
;; This file defines how the pop-up menus behave and look. It also
;; defines a set of convenient menu constructors. To customize your
;; menus, don't change this file. Instead set the variables and call
;; the constructors from fvwmrc.gwm .
;;
;; To define a menu, you can use the following constructors:
;; 
;;   (construct-menu ENTRY ...)
;;     where ENTRY can be a string denoting a menu label, a list
;;     with a string and an action denoting a menu item, or the
;;     output from the functions 'construct-machine-menu-items' or
;;     'construct-window-menu-items' below.
;;     
;;   (construct-window-menu NAME ACTION)
;;     This constructs a menu with all current windows (except GWM
;;     menu windows), which when invoked runs the ACTION on the
;;     selected window.
;;
;;   (construct-machine-menu NAME MACHINE-LIST ACTION)
;;     Constructs a menu with entries for each of the machines in 
;;     MACHINE-LIST plus the local host, which when invoked runs
;;     the ACTION with 'host' bound to the corresponding element
;;     from MACHINE-LIST and 'name' bound to the capitalized first
;;     part of the host name.
;;
;;   (construct-xrsh-menu MACHINE-LIST COMMAND)
;;     Constructs a menu with entries for each of the machines in 
;;     MACHINE-LIST plus the local host, which when invoked runs
;;     "xrsh" on the selected host and COMMAND. COMMAND can be a
;;     list of command tokens, or the command tokens can all be
;;     inserted directly at COMMAND. The first command token will
;;     be used as the name of the menu.
;;
;;   (construct-window-menu-items WINDOW-TYPE ACTION)
;;     Returns a list of menu items to be used by 'construct-menu'.
;;     There is one item for each window matching WINDOW-TYPE, which
;;     can be an atom denoting a client class, a string denoting a
;;     window name regexp, or an arbitrary wool expression.
;;
;;   (construct-machine-menu-items MACHINE-LIST ACTION)
;;     Returns a list of menu items to be used by 'construct-menu'.
;;     There is one item for each machine in MACHINE-LIST, and the
;;     ACTION will be run with 'host' and 'name' bound as for
;;     'construct-machine-menu' above.
;;
;; To pop a menu, use 'fvwm-pop-menu'.
;; 


(declare-screen-dependent
  fvwm-menu-color
  fvwm-menu-text-color
  fvwm-menu-font
  fvwm-menu-label-font
  fvwm-menu-item-height
  fvwm-menu-min-width
)

;;
;;    USER CUSTOMIZABLE VARIABLES
;;    ---------------------------  
;;    Adjust these in your own profile
;;
(for screen (list-of-screens)
     (defaults-to
       fvwm-menu-color "gray"          ; Foreground of menus  
       fvwm-menu-text-color "black"    ; Background of menus  
       fvwm-menu-font "8x13"           ; Menu item font
       fvwm-menu-label-font ()         ; Menu label font (defaults to above)
       fvwm-menu-item-height 22        ; Height of a menu item
       fvwm-menu-min-width 100         ; Minimum width of menu
       )
)

;;=============================================================

(require 'fvwm-window)

(defun fvwm-menu-father ()
  (with (wob wob)
    (while (and wob-parent
                (not (member (with (wob wob-parent) wob-status) '(menu root))))
      (setq wob wob-parent))
    (if (= (with (wob wob-parent) wob-status) 'menu)
        wob-parent
      ())))

(defun fvwm-menu-father-item ()
  (with (wob wob)
    (while (and wob-parent
                (not (member (with (wob wob-parent) wob-status) '(menu root))))
      (setq wob wob-parent))
    (if (= (with (wob wob-parent) wob-status) 'menu)
        wob
      ())))

(defun fvwm-menu-root ()
  (with (wob wob mf ())
    (while (setq mf (fvwm-menu-father))
      (setq wob mf))
    wob))

(defun fvwm-pop-cascade ()
  (if (# 'menu-expr wob)
      (setq wob-menu (eval (# 'menu-expr wob))))
  (with (cascademenu (menu-wob wob-menu)
	 cascaderoot (fvwm-menu-root)
         wm wob-menu)
    (ungrab-server)
    (grab-server cascaderoot)
    (with (wob (fvwm-menu-father))
      (## 'cascademenu wob cascademenu)) 
    (## 'poped cascademenu t)
    (with (wob (fvwm-menu-father-item))
      (fvwm-pop-menu wm (+ wob-x (width wob)) wob-y))
    (ungrab-server)
    (grab-server cascaderoot)))

(defun fvwm-unpop-cascade ()
  (if (# 'cascademenu wob)
      (with (wob (# 'cascademenu wob))
        (fvwm-unpop-cascade)
	(with (wob (fvwm-menu-father-item))
          (send-user-event 'inactive wob))
	(## 'poped wob nil)
        (fvwm-unpop-menu)))
  (## 'cascademenu wob nil))

(defun fvwm-unpop-old-cascade ()
  (with (thiswob (if wob-menu (menu-wob wob-menu))
         wob (fvwm-menu-father))
    (if (not (= (# 'cascademenu wob) thiswob))
        (fvwm-unpop-cascade))))

(defun fvwm-unpop-all-cascade ()
  (with (wob (fvwm-menu-root))
    (fvwm-unpop-cascade)
    (## 'poped wob nil)
    (fvwm-unpop-menu)))

(defun fvwm-unpop-menu ()
  (unpop-menu wob))
  

(setq fvwm-menu-item-fsm
      (fsm-make
       (state-make
        (on (buttonrelease any any)
            (with (calling-wob (with (wob (fvwm-menu-root)) wob-parent)
                   action (# 'action wob-property))
              (send-user-event 'inactive wob)
              (wob wob-parent)
              (fvwm-unpop-all-cascade)
              (if (and autocolormap
                       (not (eq root-window calling-wob)))
                  (set-colormap-focus (with (wob calling-wob) window)))
              (wob calling-wob)
              (eval action)
              ))
        (on enter-window 
            (progn
              (fvwm-unpop-old-cascade)
              (send-user-event 'active wob)
              (if (if wob-menu 
                      (not (# 'poped (menu-wob wob-menu)))
                    (# 'menu-expr wob-property))
                  (fvwm-pop-cascade))
              ))
        (on leave-window 
            (if (not (or (# 'menu-expr wob-property)
                         wob-menu))
                (send-user-event 'inactive wob)))
        (on (user-event 'active)
            (wob-tile (# 'atile wob)))
        (on (user-event 'inactive)
            (wob-tile (# 'tile wob))))))

(setq fvwm-menu-item-inner-fsm
      (fsm-make
       (state-make
        (on (user-event 'active)
            (wob-tile (# 'atile wob)))
        (on (user-event 'inactive)
            (wob-tile (# 'tile wob))))))

(setq fvwm-menu-label-fsm
      (fsm-make
       (state-make
        (on enter-window 
            (progn
              (fvwm-unpop-old-cascade)
              )))))

(setq fvwm-menu-fsm
   (fsm-make 
    (state-make
     (on (buttonrelease any any) 
         (with (calling-wob (with (wob (fvwm-menu-root)) wob-parent))
            (fvwm-unpop-all-cascade)
            (if (and autocolormap
                     (not (eq root-window calling-wob)))
                (set-colormap-focus (with (wob calling-wob) window)))))
     )))

(defun fvwm-menu-topbar ()
  (with (tile ()
         background (# 0 color-scheme)
         foreground background
         bar-max-width 2
         bar-min-width bar-max-width
         pix (pixmap-make 2 2)
         foreground (# 2 color-scheme))
    (draw-line pix 1 1 1 1)
    (bar-make () (plug-make pix))))

(defun fvwm-menu-bottombar ()
  (with (tile ()
         background (# 2 color-scheme)
         foreground (# 0 color-scheme)
         bar-max-width 2
         bar-min-width bar-max-width
         pix (pixmap-make 2 2)
         foreground background)
    (draw-line pix 1 1 1 1)
    (with (background (# 2 color-scheme))
      (bar-make (plug-make pix) ()))))

(defun fvwm-menu-labeltile (size)
  (with (tile ()
         pix (with (foreground background) (pixmap-make 1 size)))
    (draw-line pix 0 (- size 3) 0 (- size 3))
    (draw-line pix 0 (- size 1) 0 (- size 1))
    pix))

(defun fvwm-menu-itemtile (size active)
  (with (fsm fvwm-menu-item-inner-fsm
         background (# 1 color-scheme)
         tile ()
         foreground background
         pix (pixmap-make 1 size))
    (if active
        (progn
          (with (foreground (# 0 color-scheme))
            (draw-line pix 0 0 0 1))
          (with (foreground (# 2 color-scheme))
            (draw-line pix 0 (- size 2) 0 (- size 1)))))
    pix))

(defun fvwm-menu-vplug (size side)
  (with (fsm fvwm-menu-item-inner-fsm
         tile ()
         foreground (# 1 color-scheme)
         pix (pixmap-make 1 size)
         foreground (# (if (= side 'left) 0 2) color-scheme)
         apix (pixmap-make 1 size))
    (if (= side 'left)
        (with (foreground (# 2 color-scheme))
          (draw-line apix 0 (- size 1) 0 (- size 1)))
      (with (foreground (# 0 color-scheme))
        (draw-line apix 0 0 0 0)))
    (with (property (list 'tile pix 'atile apix))
      (plug-make pix))))

(defun fvwm-menu-subarrow (size)
  (with (fsm fvwm-menu-item-inner-fsm
         tile ()
         foreground (# 1 color-scheme)
         size (- size 4)
         pix (pixmap-make size size)
         apix (pixmap-make size size)
         x1 (/ size 3)
         x2 (- size x1 (% size 2) -1)
         h (/ size 2))
    (with (foreground (# 0 color-scheme))
      (draw-line pix x1 x1 x1 x2)
      (draw-line pix x1 x1 x2 h)
      (draw-line apix x1 x2 x2 h))
    (with (foreground (# 2 color-scheme))
      (draw-line apix x1 x1 x1 x2)
      (draw-line apix x1 x1 x2 h)
      (draw-line pix x1 x2 x2 h))
    (with (property (list 'tile pix 'atile apix))
      (plug-make pix))))

(defun fvwm-menu-separator-make ()
  (with (fsm ()
         tile ()
         borderwidth 0
         color-scheme (make-color-scheme fvwm-menu-color fvwm-menu-text-color)
         background (# 2 color-scheme)
         foreground (# 0 color-scheme)
         bar-max-width 2
         bar-min-width bar-max-width
         left (pixmap-make 1 2)
         right (pixmap-make 1 2)
         tile (pixmap-make 1 2)
         foreground background
         )
    (draw-line tile 0 0 0 0)
    (draw-line right 0 0 0 1)
    (bar-make (plug-make left) () (plug-make right))))

(defun fvwm-menu-item-make (label action)
  (with (borderwidth 0
         color-scheme (make-color-scheme fvwm-menu-color fvwm-menu-text-color)
         font (if (= (type fvwm-menu-font) 'string)
                  (font-make fvwm-menu-font)
                fvwm-menu-font)
         background (# 1 color-scheme)
         foreground (# 3 color-scheme)
         fsm fvwm-menu-item-fsm
         menu (if (= (type action) 'menu) action)
         menu-expr (if (member (type action) '(expr subr fexpr fsubr))
                       (list action))
         bar-min-width fvwm-menu-item-height
         bar-max-width bar-min-width
         plug-separator 0
         ntile (label-make label)
         left0 (with (fsm ()
                      bar-max-width 2
                      bar-min-width bar-max-width
                      background (# 0 color-scheme))
                (bar-make ))
         right0 (with (fsm ()
                       bar-max-width 2
                       bar-min-width bar-max-width
                       background (# 2 color-scheme))
                 (bar-make ))
         left (fvwm-menu-vplug fvwm-menu-item-height 'left)
         right (fvwm-menu-vplug fvwm-menu-item-height 'right)
         atile (fvwm-menu-itemtile fvwm-menu-item-height t)
         tile (fvwm-menu-itemtile fvwm-menu-item-height ())
         arrow (if (or menu menu-expr) 
                   (fvwm-menu-subarrow fvwm-menu-item-height))
         property (+ (if (not (or menu menu-expr))
                         (list 'action action))
                     (if menu-expr
                         (list 'menu-expr menu-expr))
                     (list 'tile tile 'atile atile)))
    (bar-make
     left0
     left
     (with (borderwidth 0
            fsm ()
            property ())
       (plug-make ntile))
     ()
     arrow
     right
     right0)))
     
(defun fvwm-menu-label-make (label)
  (with (borderwidth 0
         color-scheme (make-color-scheme fvwm-menu-color fvwm-menu-text-color)
         background (# 1 color-scheme)
         foreground (# 3 color-scheme)
         bar-min-width fvwm-menu-item-height
         bar-max-width bar-min-width
         plug-separator 0
         fsm fvwm-menu-label-fsm
         left (with (fsm ()
                     bar-max-width 2
                     bar-min-width bar-max-width
                     background (# 0 color-scheme))
                (bar-make ))
         right (with (fsm ()
                      bar-max-width 2
                      bar-min-width bar-max-width
                      background (# 2 color-scheme))
                 (bar-make ))
         tile (fvwm-menu-labeltile fvwm-menu-item-height)
         menu ())
    (bar-make
     left
     ()
     (with (borderwidth 0
            fsm ()
            font (if (= (type fvwm-menu-label-font) 'string)
                     (font-make fvwm-menu-label-font)
                   fvwm-menu-label-font
                     fvwm-menu-label-font
                   (= (type fvwm-menu-font) 'string)
                     (font-make fvwm-menu-font)
                     fvwm-menu-font))
       (plug-make (label-make label)))
     ()
     right)))

(defun fvwm-menu-make args
  (if args
      (with (menu ()
             fsm fvwm-menu-fsm
             menu-min-width fvwm-menu-min-width
             borderwidth 0
             bar-separator 0
             direction vertical
             property ()
             color-scheme (make-color-scheme fvwm-menu-color
                                             fvwm-menu-text-color)
             top (fvwm-menu-topbar)
             bottom (fvwm-menu-bottombar)
             res ())
        (apply 'menu-make (+ (list top) args (list bottom))))))

(defunq fvwm-pop-menu args
  (with (pmenu (if (= (length args) 0)
                   (wob-menu)
                 (eval (# 0 args)))
         pos (if (= (length args) 3)
                 (list (eval (# 1 args)) (eval (# 2 args)))
               ())
         calling-wob wob
         wob (menu-wob pmenu)
         bw wob-borderwidth
         wdt wob-width
         hgt wob-height
         wob calling-wob
         xm (current-event-x)
         ym (current-event-y)
         xpos (if pos (# 0 pos) (- xm (/ wdt 2) bw))
         ypos (if pos (# 1 pos) (- ym (/ fvwm-menu-item-height 2) bw)))
    (if (< xpos 0)
        (setq xpos 0)
      (> (+ xpos wdt) screen-width)
      (setq xpos (- screen-width wdt)))
    (if (< ypos 0)
        (setq ypos 0)
      (> (+ ypos hgt) screen-height)
      (setq ypos (max 0 (- screen-height hgt))))
    (move-window (menu-wob pmenu) xpos ypos)
    (if (and autocolormap
             (not (eq root-window calling-wob)))
        (set-colormap-focus ()))
    (pop-menu pmenu 'here)
))
      

;;
;; Some useful routines
;;

(defun append-sublists (lists)
  (eval (+ '(+) (mapfor lst lists (list 'quote lst)))))

(defun remove-nil (lst)
  (with (ele ())
    (while (setq ele (member () lst))
      (delete-nth ele lst))
    lst))

(defun upchar (char)
  (or (# (atom char) '(a "A" b "B" c "C" d "D" e "E" f "F" g "G"
                       h "H" i "I" j "J" k "K" l "L" m "M" n "N"
                       o "O" p "P" q "Q" r "R" s "S" t "T" u "U"
                       v "V" w "W" x "X" y "Y" z "Z"))
      char))

(defun capitalize (str)
  (if (= (length str) 0)
      " "
    (with (pair (match "^\\(.\\)\\(.*\\)" str 1 2))
      (+ (upchar (# 0 pair)) (# 1 pair)))))

(defun is-menu-item (obj)
  (= (type obj) 'bar))

(defun is-sub-menu (obj)
  (and (= (type obj) 'list)
       ()))

;;
;; Remote machine command menus
;;

(defun machine-name (host)
  (capitalize (match "^\\([^.]*\\)" host 1)))

(setq xrsh-program "xrsh")

(defun xrsh-command (host command)
  (if (equal host ".")
      (with (host hostname)
        (eval (+ '(!) command)))
    (eval (+ (list '! xrsh-program host) command))))

(defun do-command (command)
  (eval (+ '(!) command)))

(defun construct-machine-menu (name machine-list expr)
  (apply 'fvwm-menu-make
         (+ (list (fvwm-menu-label-make name))
            (with (name (machine-name hostname))
              (list (fvwm-menu-item-make name 
                                         (` (with (host "." name (, name))
                                                  (, expr))))))
            (mapfor host machine-list
              (with (name (machine-name host))
                (fvwm-menu-item-make name 
                                     (` (with (host (, host) name (, name))
                                              (, expr)))))))))

(defun construct-xrsh-menu args
  (with (machine-list (# 0 args)
         command (if (= (type (# 1 args)) 'list)
                     (# 1 args)
                   (sublist 1 (length args) args)))
    (construct-machine-menu (capitalize (# 0 command))
                            machine-list
                            (` (xrsh-command host (quote (, command)))))))

(defun construct-machine-menu-items (machine-list expr)
  (mapfor host machine-list
    (with (name (machine-name host))
      (fvwm-menu-item-make name 
                           (` (with (host (, host) name (, name))
                                    (, expr)))))))

(defun construct-window-menu (name expr)
  (apply 'fvwm-menu-make
         (+ (list (fvwm-menu-label-make name))
            (remove-nil
             (mapfor wob (list-of-windows 'window) 
               (if (not (= window-client-class 'Gwm))
                   (fvwm-menu-item-make window-name
                                        (` (if (wob-is-valid (, wob))
                                               (with (wob (, wob))
                                                 (, expr)))))))))))

(defun construct-window-menu-items (wlst expr)
  (remove-nil
   (mapfor wob (list-of-windows 'window) 
     (if (matches-token wlst)
         (fvwm-menu-item-make window-name
                              (` (if (wob-is-valid (, wob))
                                     (with (wob (, wob))
                                           (, expr)))))))))

(defun construct-menu item-lists
  (apply 'fvwm-menu-make
         (apply '+
                (mapfor items item-lists
                  (if (= (type items) 'string)
                      (list (fvwm-menu-label-make items))
                    (is-menu-item items)
                      (list items)
                    (and (= (type items) 'list)
                         (is-menu-item (# 0 items)))
                      items
                    (and (= (type items) 'list)
                         (= (type (# 0 items)) 'string))
                      (if (and (= (type (# 1 items)) 'atom)
                             (boundp (# 1 items))
                             (= (type (eval (# 1 items))) 'list))
                          (list (fvwm-menu-item-make (# 0 items)
                                                     (apply construct-menu
                                                          (eval (# 1 items)))))
                        (and (= (type (# 1 items)) 'atom)
                             (boundp (# 1 items))
                             (member (type (eval (# 1 items)))
                                     '(expr subr fexpr fsubr)))
                          (list (fvwm-menu-item-make (# 0 items)
                                                     (eval (# 1 items))))
                        (and (= (type (# 1 items)) 'list)
                             (member (type (# 0 (# 1 items)))
                                     '(string list)))
                          (list (fvwm-menu-item-make (# 0 items)
                                                     (apply construct-menu
                                                            (# 1 items))))
                          (list (fvwm-menu-item-make (# 0 items)
                                                     (# 1 items))))
                      ())))))
  
