; group windows into dvrooms
;
;;File: dvrooms.gwm
;;Author: duanev@mcc.com (Duane Voth)
;;Revision: 1.5 -- Nov 18 1990
;
; History:  1.0 -- Oct 18 1989	original
;	    1.1 -- Oct 26 1989	windows are placed back on the screen (in
;				iconic form) when a dvroom manager dies
;	    1.2 -- Nov 21 1989	use gwm-quiet
;	    1.3 -- Nov 22 1989  create rmgrs from wool (colas)
;	    1.4 -- May 11 1990  use GWM_ROOM property to remeber room windows
;	    1.5 -- Nov 18 1990  Philippe Kaplan (phk)
;				act as icon boxes:
;				  do (setq dvroom.icon-box t) before load
;				exit bug fixed around line 190
;
; dvrooms.gwm must be loaded in the .profile.gwm before any set-window,
; set-icon-window, set-placement, or set-icon-placement calls.  it should
; also follow loading of icon-groups.gwm if used.  (this probably isn't
; necessary but it seems to be programatically correct)
;
; to use rooms, the following can be added to your .profile.gwm:
;
; (defun screen-startup ()
;    (setq count-of-windows-on-screen (length (list-of-windows)))
;    (new-dvroom-manager "home")
;    (new-dvroom-manager "lisp")
;    (new-dvroom-manager "wysiwyg")
;    (dvroom-reattach)
;    (if (= 0 count-of-windows-on-screen)
;          (! "/bin/sh" "-c" "$HOME/.xrc")))
; 
; (setq to-be-done-after-setup (+ to-be-done-after-setup '((screen-startup))))
;
; be sure also to attach add-to-dvroom and remove-from-dvroom to some unused
; mouse button / keyboard modifier combinations.  Example:
;
; (: standard-behavior
;   (state-make
;         ....
;       (on (buttonpress 1 with-control) (add-to-room))
;       (on (buttonpress 3 with-control) (remove-from-room))
;   ))
; 
;;=============================================================================
;;                    1.5
;;=============================================================================
; New version of "dvrooms.gwm". Small changes produce big results:
; 
;  - You may open any number of rooms, instead of only one.
;  - you can attach a window to several rooms.
; 
; Be carefull:
; 
;  - The notion of current-dvroom becomes "the last room openned" (add-to-room
;  and remove-from-room use current-dvroom).
; 
; Since it changes the concept of rooms, we'd better speak about "icon boxes".
; Note that you can again remove and add any window to/from any room, so
; this is clever than icon-group.gwm.
; 
;      To enable this feature, put:
; (setq dvroom.icon-box t)
;      before
; (load "dvrooms")
; 
; Put in your .profile.gwm some lines like:
; 
; (defun epoch-decos ()
;   '(if (member "Minibuffer" window-name)
;        (no-frame)
;        (progn
;          (if (and (boundp 'dvroom-managers) (find-dvroom-by-name "epoch"))
;              (add-to-dvroom-group (find-dvroom-by-name "epoch") (wob))
;              (set-x-property "GWM_ROOM" "epoch"))
;          (simple-win))))
; 
; (set-window Emacs epoch-decos)
; 
; (set-icon-placement Gwm     rows.top-left.placement) ; set rooms placements
; (set-placement      Gwm     rows.top-left.placement)
; 
; So every new epoch screen belongs to "epoch"'s room.

;;=============================================================================
;;                    code
;;=============================================================================

; global dvroom variables
(declare-screen-dependent
  dvroom.font
  dvroom.background
  dvroom.foreground
)

(setq dvroom-managers ())		; list of windows for dvroom managers
(setq current-dvroom ())		; index into dvroom-managers of the current dvroom

; user-settable resources
(for screen (list-of-screens)
  (defaults-to
    dvroom.font (font-make "8x13")
    dvroom.background white
    dvroom.foreground black
  ))

(defaults-to
  dvroom.borderwidth borderwidth
  dvroom.auto-add ()			; new windows added to current room?
  dvroom.icon-box ()			; act as icon boxes
  dvroom.x 0
  dvroom.y 0
  dvroom.name "Room #"
  dvroom.rootmenupos 5			; where to place root menu items
  dvroom.menupos 2			; where to place menu menu items
  edit-keys.return "Return"
  edit-keys.backspace "BackSpace"
  edit-keys.delete "Delete"
)

(defaults-to dvroom.name.number 0)

; save current iconify-window function
(if (not (boundp 'pre-dvrooms-iconify-window))
    (setq pre-dvrooms-iconify-window iconify-window))

; add w to the list of windows managed by a dvroom-manager
(defun add-to-dvroom-group (dvroom-manager w)
 (if (not (member w (nth 'rgroup dvroom-manager)))
   (progn
     (with (wob w) (setq window-wm-state-icon
			 (with (wob dvroom-manager) window-icon)))
     (replace-nth 'rgroup dvroom-manager (+ (nth 'rgroup dvroom-manager) (list w))))))

; remove w from the list of windows managed by a dvroom-manager
;; be careful, the window might not exist anymore, if we get called on closing
;; of an application!

(defun remove-from-dvroom-group (dvroom-manager w)
  (if (window-is-valid w)
    (with (wob w) (setq window-wm-state-icon 0))
  )
  (with (slot (member w (nth 'rgroup dvroom-manager)))
    (if slot (delete-nth slot (nth 'rgroup dvroom-manager)))))

; a version of print that honors gwm-quiet
(defun qprint args
  (if (= gwm-quiet 0) (eval (+ '(print) args))))


; open all windows in a dvroom
; assumes "window" is the dvroom manager being opened
(defun open-dvroom ()
    (for window (nth 'rgroup window-window)
	; map the window if it was mapped, else map the icon
	(if (nth 'rgroup-state window)
	    (map-window window)
	    (map-window window-icon))))


; close all windows in a dvroom
; assumes "window" is the dvroom manager being closed
(defun close-dvroom ()
    (for window (nth 'rgroup window)
	; save window state - is it a window or an icon
	(replace-nth 'rgroup-state window window-is-mapped)
	; remove both windows and icons from the screen
	(if window-is-mapped (unmap-window window))
	(if (window-icon?)
	    (with (window window-icon)
	      (if window-is-mapped (unmap-window window))))))


; redefine iconify-window so we can do dvroom specific stuff
(defun iconify-window ()
    (if (= window-name "rmgr")
	; (de)iconifing a dvroom manager
	(if (= window-status 'window)

	    ; iconifing (closing) a dvroom manager
	    (progn
		(close-dvroom)
		(if (not dvroom.icon-box)
		    (setq current-dvroom ()))
		(pre-dvrooms-iconify-window)
	    )
	    
	    ; deiconifing (opening) a dvroom manager
	    (with (rmgr-index (member window-window dvroom-managers))

		; close previous dvroom manager
		(if (and current-dvroom (not dvroom.icon-box))
		    (with (window (nth current-dvroom dvroom-managers))
			(if (= window-status 'window)
			    (iconify-window)	; recurse to close dvroom
			)
		    )
		)

		(if rmgr-index
		    ; existing dvroom manager
		    (open-dvroom)
		    ; register a new dvroom manager
		    (progn
			; save window-window as the manager may be iconic and
			; we need a consistent value in the dvroom-mgr list
			(setq dvroom-managers
				(+ dvroom-managers (list window-window)))
			(setq rmgr-index (member window-window dvroom-managers))
		    )
		)

		(setq current-dvroom rmgr-index)
		(pre-dvrooms-iconify-window)
	    )
	)

	; (de)iconifing other windows
	(progn
	    (pre-dvrooms-iconify-window)
	)
    )
)


(defun dvroom-icon-name (dvroom)
  (with (window dvroom) window-icon-name))

(defun find-dvroom-by-name (name)
  (tag room-found
    (for dvroom dvroom-managers
      (if (= name (dvroom-icon-name dvroom))
	(exit room-found dvroom)))))

;; register a new dvroom manager -or- readd a window to a room
;; (should be called when new windows become known to gwm (ie. via
;; gwm global opening))
;; assumes "window" is the new dvroom manager or window

(defun add-dvroom-manager ()
  (if (= window-name "rmgr")
    (if (not (member window-window dvroom-managers))
      (progn (qprint "new dvroom manager " window-icon-name "\n")
	    ;; save window-window as the manager may be iconic and
	    ;; we need a consistent value in the dvroom-mgr list
	(setq dvroom-managers (+ dvroom-managers (list window-window)))))
	;; else it's not a manager.  see if this window previously belonged to
	;; a room and add a new manager if the named manager does not exist

    (with (room-name (get-x-property "GWM_ROOM"))
      (if (< 0 (length room-name))
	(if (not (find-dvroom-by-name room-name))
	  (new-dvroom-manager room-name))))
    () 

    ;; if dvroom.auto-add is true, then add to current dvroom if one exists
    (if (and dvroom.auto-add (= (type current-dvroom) 'number))
      (add-to-dvroom))
  )
)

; add add-dvroom-manager to progn of funcs to eval when opening a new window
; assumes dvroom.gwm is before the set-* calls in .profile.gwm
(setq opening (+ opening '((add-dvroom-manager))))


; reattach windows that have a GWM_ROOM property to the room managers

(defun dvroom-reattach window-list
  (for window (if window-list window-list (list-of-windows))
    (with (room-name (get-x-property "GWM_ROOM"))
      (if (<  0 (length room-name))
	(for dvroom dvroom-managers
	  (if (= room-name (dvroom-icon-name dvroom))
	    (progn
	      (add-to-dvroom-group dvroom window)
	      (if (not (= dvroom current-dvroom))
		(progn
		  ; remove both window and icon from the screen
		  (if window-is-mapped (unmap-window window))
		  (if (window-icon?)
		      (with (window window-icon)
		        (if window-is-mapped (unmap-window window))))))
	      (qprint "adding   <" window-name ">   to dvroom "
				(dvroom-icon-name dvroom) "\n"))))))))


; add a window to a dvroom
; assumes "window" is the application window to add
(defun add-to-dvroom ()
    (with (window window-window)
	(if (and (= (type current-dvroom) 'number)
		 (not (= window-name "rmgr")))
	    (with (dvroom-manager (nth current-dvroom dvroom-managers))
		(if (not (member window (nth 'rgroup dvroom-manager)))
		    (progn (add-to-dvroom-group dvroom-manager window)
			   (set-x-property "GWM_ROOM"
				(with (wob dvroom-manager) window-icon-name))
			   ; give the user *some* kind of feed back
			   (qprint "adding   <" window-name ">   to dvroom "
				(dvroom-icon-name dvroom-manager) "\n")))))))


; remove a window from a dvroom
; assumes "window" is the application window to remove
(defun remove-from-dvroom ()
    (with (window window-window)
	(if (= (type current-dvroom) 'number)
	    (with (dvroom-manager (nth current-dvroom dvroom-managers))
		(if (member window (nth 'rgroup dvroom-manager))
		  (progn
		    (set-x-property "GWM_ROOM" "")
		    ; give the user *some* kind of feed back
		    (qprint "removing <" window-name "> from dvroom "
			(dvroom-icon-name dvroom-manager) "\n"))
		  (qprint "not a dvroom member\n"))
		(remove-from-dvroom-group dvroom-manager window)))))


; if a normal window, remove it from any dvroom manager, but if a dvroom
; manager and not current, make all of the dvrooms windows visible.
; (this function needs to be called when an application exits (ie. via gwm
; global closing) so that gwm won't try to operate on non-existant windows)
; assumes "window" is the application window that is exiting
(defun flush-dvroom-lists ()
    (for dvroom-manager dvroom-managers
	(remove-from-dvroom-group dvroom-manager window-window))
    (with (index (member window-window dvroom-managers))
	; if a dvroom manager
	(if index 
	    (progn
		(if (= current-dvroom index)
		    (setq current-dvroom ())
		    (progn
			(open-dvroom)
			(if (> current-dvroom index)
			    (setq current-dvroom (- current-dvroom 1)))))
		(delete-nth index dvroom-managers)))))

; add flush-dvroom-lists to progn of funcs to eval when closing an old window
; assumes dvroom.gwm is loaded before the set-* calls in .profile.gwm
(setq closing (+ closing '((flush-dvroom-lists))))


; colas: create dvroom managers as placed menus
(defun new-dvroom-manager args
  (if (not (find-dvroom-by-name (# 0 args)))
    (with (fsm window-fsm
	       background dvroom.background foreground dvroom.foreground
	       borderwidth dvroom.borderwidth
	       direction vertical
	       label-horizontal-margin 4 label-vertical-margin 2
	       menu-min-width 30 menu-max-width 1000
	       name (if args (# 0 args) (new-dvroom-manager-name))
	       )
      (setq wob 
	    (with (icon-name name starts-iconic t)
		  (place-menu
		   'rmgr
		   (menu-make
		    (bar-make
		     (with (fsm dvroom.fsm
			        property (+ (list
			'title name
			'background dvroom.background
			'foreground dvroom.foreground
			'borderwidth dvroom.borderwidth
			'font dvroom.font
		      ) property))
			   (plug-make
			    (label-make name dvroom.font)))))
		   dvroom.x dvroom.y)))
      (## 'title wob name)
      (add-dvroom-manager))))


; generates a new dvroom name
(defun new-dvroom-manager-name ()
  (setq dvroom.name.number (+ 1 dvroom.name.number))
  (+ dvroom.name (itoa dvroom.name.number)))

; editable plug fsm

(setq dvroom.fsm
  (fsm-make 
    (: dvroom.edit-fsm.normal 
      (state-make
	(on (double-button any any) 
	  (progn
	    (set-focus wob)
	    (wob-background (# 'foreground wob))
	    (with (
		foreground (# 'background wob)
		background (# 'foreground wob)
	      )
	      (wob-tile 
		(active-label-make
		  (# 'title wob) (# 'font wob)))))
	  dvroom.edit-fsm.editable)
	(on (button any (together with-alt with-control))
	  (progn
	    (set-focus wob)
	    (wob-background (# 'foreground wob))
	    (with (
		foreground (# 'background wob)
		background (# 'foreground wob)
	      )
	      (wob-tile 
		(active-label-make
		  (# 'title wob) (# 'font wob)))))
	  dvroom.edit-fsm.editable)
	icon-behavior
	standard-behavior
    ))
    (: dvroom.edit-fsm.editable
      (state-make
	(on (keypress (key-make edit-keys.return) any)
	  (dvroom.edit-fsm.de-edit)
	  dvroom.edit-fsm.normal)
	(on (double-button any any)
	  (dvroom.edit-fsm.de-edit)
	  dvroom.edit-fsm.normal)
	(on (keypress edit-keys.backspace any)
	  (progn
	    (## 'title wob 
	      (if (: s (match "\\(.*\\)."
		    (# 'title wob) 1))
		s
		(setq s "")))
	    (with (
		foreground (# 'background wob)
		background (# 'foreground wob)
	      )
	      (wob-tile (active-label-make s (# 'font wob))))
	))
	(on (keypress edit-keys.delete any)
	  (progn
	    (## 'title wob (: s ""))
	    (with (
		foreground (# 'background wob)
		background (# 'foreground wob)
	      )
	      (wob-tile (active-label-make s (# 'font wob))))
	))
	(on (keypress any any)
	  (progn
	    (## 'title wob 
	      (: s (+ (# 'title wob) (last-key))))
	    (with (
		foreground (# 'background wob)
		background (# 'foreground wob)
	      )
	      (wob-tile (active-label-make s (# 'font wob))))
	))
	(on focus-out
	  (dvroom.edit-fsm.de-edit)
	  dvroom.edit-fsm.normal)
	icon-behavior
	standard-behavior
    ))
))
))

(if (not (boundp 'update-icon))
    (defun update-icon (update-icon.title)
      (if (window-icon?)
	  (send-user-event 'get-title (window-icon)))))

(de dvroom.edit-fsm.de-edit ()
  (wob-background (# 'background wob))
  (with (background (# 'background wob)
      foreground (# 'foreground wob)
    )
    (wob-tile (label-make (# 'title wob) (# 'font wob)))
    (update-icon (# 'title wob)))
)

;
; find dvroom window belongs to
;

(defun find-window-in-any-dvroom (win)
  (tag found-in-manager
       (for dvr dvroom-managers
	    (with (slot (member win (nth 'rgroup dvr)))
		  (if slot (exit found-in-manager dvr))
		  )
	    )
       )
  )

;
; detach window from all room managers
;

(defun remove-window-from-all-dvroom (win)
  (with (dvr (find-window-in-any-dvroom win))
	(if dvr (remove-from-dvroom-group dvr win))
	)
  )

;
; automatic room attachment based upon name
;

(defun auto-window-attach (w)
  (with (w-name (with (window w) (window-name)))
	(with (d-name (match "\\(.*\\)::" w-name 1))
	      (if (< 0 (length d-name))
		  (with (dvr (find-dvroom-by-name d-name))
			(if dvr
	;;
	;; by now, we've found a dvroom with the desired name
	;; just in case, try to detach this window from it's dvroom
	;; and attach it to the target dvroom
	;;
			    (progn
			      (qprint "Auto-Add " w-name " to " d-name "\n" )
			      (remove-window-from-all-dvroom w)
			      (add-to-dvroom-group dvr w)
			      (set-x-property "GWM_ROOM" d-name)
			      )
			  )
			)
		)
	      )
	)
  )

;
; automatic room attachment for all windows
;

(defun magic-dvroom-attach ()
  (for win (list-of-windows 'window)
       (auto-window-attach win)
       )
  )

;
; unmap all windows/icon that belong to a room
;

(defun dvroom-remapping ()
  (for win (list-of-windows 'window)
       (with (dvr (find-window-in-any-dvroom win))
	     (if dvr
		 (with (window win)
		       (if window-is-mapped (unmap-window win))
		       (if (window-icon?)
			   (with (window window-icon)
				 (if window-is-mapped (unmap-window window-icon))
				 )
			 )
		       )
	       )
	     )
       )
  )

;
; Next dvroom number
;

(defun increment-dvroom ()
  (with (room-leng (length dvroom-managers))
	(if (> room-leng 0)
	    (if current-dvroom
		(with (room (+ current-dvroom 1))
		      (if (= room (length dvroom-managers))
			  0
			room
			)
		      )
	      0
	      )
	  ()
	  )
	)
  )

;
; Previous dvroom number
;

(defun decrement-dvroom ()
  (with (room-leng (length dvroom-managers))
	(if (> room-leng 0)
	    (if current-dvroom
		(with (room (- current-dvroom 1))
		      (if (< room 0)
			  (- room-leng 1)
			room
			)
		      )
	      (- room-leng 1)
	      )
	  ()
	  )
	)
  )

;
; Close current Room
;

(defun close-current-dvroom ()
  (if current-dvroom
      (with (window (# current-dvroom dvroom-managers))
	    (progn
	      (close-dvroom)
	      (setq current-dvroom ())
	      (pre-dvrooms-iconify-window)
	      )
	    )
    )
  )

;
; Open dvroom by number
;

(defun open-room-number (room)
  (if room
      (progn
	(close-current-dvroom)
	(with (window (# room dvroom-managers))
	      (progn
		(open-dvroom)
		(setq current-dvroom room)
		(with (window window-icon)
		      (pre-dvrooms-iconify-window)
		      )
		)
	      )
	)
    )
  )

;
; Open next room
;

(defun roll-rooms-up ()
  (open-room-number (increment-dvroom))
  )

;
; Open previous room
;

(defun roll-rooms-down ()
  (open-room-number (decrement-dvroom))
  )

;; adds the "add to room" and "remove from room" menu items in the
;; window menu, "New dvroom" in the root menu

(if (not (boundp 'dvroom.menu-added)) (progn
    (setq dvroom.menu-added t)
    
    (if (eq window-pop-items icon-pop-items)
      (setq window-pop-equals-icon-pop t)
      (setq window-pop-equals-icon-pop ()))
    
    (insert-at '(item-make "New dvroom" (new-dvroom-manager))
      root-pop-items
      dvroom.rootmenupos)
    (insert-at '(multi-item-make 
	"Room: "
	()
	("Add" (add-to-dvroom))
	("Remove" (remove-from-dvroom)))
      window-pop-items
      dvroom.menupos)
    
    (if window-pop-equals-icon-pop
      (setq icon-pop-items window-pop-items))
))
