;;; darkroom-mode.el - Distraction free editing mode

;; Author: Martin Svenson
;; Usage: M-x darkroom-mode
;; License: free for all usages/modifications/distributions/whatever.
;; Requirements: w32-fullscreen.el or something similar under *nix

(require 'cl)

(cond ((eq window-system 'w32) 
       (require 'w32-fullscreen)))
        
(require 'frame-local-vars)

;; ------ configuration -----
(defvar darkroom-mode-face-foreground "Lucida Sans Typewriter"
  "The foreground color of the default face")

(defvar darkroom-mode-face-size 160
  "Default font size" )

(defvar darkroom-mode-center-margin 100 "")

(defvar darkroom-mode-enable-multi-monitor-support nil
  "Whether to enable multi-frame (i.e multiple monitor) support. An option since this feature is experimental")

(defvar darkroom-mode-enable-longline-wrap t
  "If longlines-mode is enabled, should longlines-wrap-follows-window-size also be enabled when going into darkroom mode?")

;; -------- code start -------
(setq *darkroom-mode-memtable* (make-hash-table))

(defun* darkroom-recall (var &optional (frame (selected-frame)))
  (cdr (assoc var (gethash frame *darkroom-mode-memtable*))))

(defun* darkroom-remember (var val &optional (frame (selected-frame)))
  (let* ((varlist (gethash frame *darkroom-mode-memtable*))
	 (target (assoc var varlist)))
    (cond (target
	   (setf (cdr target) val))
	  (t
	   (puthash frame (cons (cons var val)
				varlist) *darkroom-mode-memtable*)))))

(defun darkroom-mode-set-enabled(var)
  (darkroom-remember 'darkroom-mode-enabled var))

(defun darkroom-mode-enabledp()
  (darkroom-recall 'darkroom-mode-enabled))

(defun darkroom-mode-update-window()
  (message "Updating Window")
  (set-window-margins (selected-window)
		      left-margin-width
		      right-margin-width))

(defun darkroom-mode ()
  (interactive)
  (cond ((darkroom-mode-enabledp)
	 (darkroom-mode-disable))
	(t
	 (darkroom-mode-enable))))

(defun darkroom-mode-enable()
  (interactive)

  (darkroom-remember 'default-font (face-attribute 'default :font))
  (darkroom-remember 'default-font-size (face-attribute 'default :height))
  (set-face-attribute 'default (selected-frame) :font darkroom-mode-face-foreground)
  (set-face-attribute 'default (selected-frame) :height darkroom-mode-face-size)
  
  ; ----- margins
  ; note: margins are buffer local, so if multi-monitor support is
  ;       enabled, frame-locals are used. Otherwise, it's set
  ;       globally.
  ; - remember margins (only needed if multi-monitor support is disabled)
  (unless darkroom-mode-enable-multi-monitor-support
    (darkroom-remember 'left-margin-width
		       (default-value 'left-margin-width))
    (darkroom-remember 'right-margin-width
		       (default-value 'right-margin-width)))
  
  ; ----- other settings
  ; - remember
  (darkroom-remember 'line-spacing (frame-parameter nil 'line-spacing))
  (when (and  (boundp 'longlines-mode)
	      longlines-mode
	      darkroom-mode-enable-longline-wrap)
    (darkroom-remember 'longlines-wrap-follow
	  longlines-wrap-follows-window-size))
  ; - set
  (modify-frame-parameters (selected-frame)
			   '((line-spacing . 1)))
  (when (and 
	 (boundp 'longlines-mode)
	 longlines-mode
	 darkroom-mode-enable-longline-wrap)
    (longlines-mode 0)
    (setq longlines-wrap-follows-window-size t)
    (longlines-mode 1))
  
  ; ---- frame size
  ; - remember
  (darkroom-remember 'frame-width (frame-parameter nil 'width))
  (darkroom-remember 'frame-height (frame-parameter nil 'height))
  (darkroom-remember 'frame-left (frame-parameter nil 'left))
  (darkroom-remember 'frame-top (frame-parameter nil 'top))

  ;;  - set fullscreen
  (cond ((eq window-system 'w32)
           (w32-fullscreen-on))
         ((eq window-system 'ns)
           (set-frame-parameter nil 'fullscreen 'fullboth)))

  ; - set margins
  (cond (darkroom-mode-enable-multi-monitor-support
	 (setq-frame-default 'left-margin-width (/ (- (frame-width) darkroom-mode-center-margin) 2))
	 (setq-frame-default 'right-margin-width (/ (- (frame-width) darkroom-mode-center-margin) 2))
	 (frame-local-variables-check t))
	(t
	 (setq-default left-margin-width (/ (- (frame-width) darkroom-mode-center-margin) 2))
	 (setq-default right-margin-width (/ (- (frame-width) darkroom-mode-center-margin) 2))))
  (darkroom-mode-update-window)

  
  (darkroom-mode-set-enabled t)
  (message (format "darkroom mode enabled on %s" (selected-frame))))

(defun darkroom-mode-disable()
  (interactive)

  (set-face-attribute 'default (selected-frame) :font (darkroom-recall 'default-font))
  (set-face-attribute 'default (selected-frame) :height (darkroom-recall 'default-font-size))
  
  ; - restore other settings
  (modify-frame-parameters
   (selected-frame)
   `((line-spacing . ,(darkroom-recall 'line-spacing))))
  (when (and
	 (boundp 'longlines-mode)
	 longlines-mode
	 (darkroom-recall 'longlines-wrap-follow)
	 darkroom-mode-enable-longline-wrap)
    (longlines-mode 0)
    (setq longlines-wrap-follows-window-size
	  (darkroom-recall 'longlines-wrap-follow))
    (longlines-mode 1))
  ; - restore margins
  (cond (darkroom-mode-enable-multi-monitor-support
	 (unset-frame-default 'left-margin-width)
	 (unset-frame-default 'right-margin-width)
	 (frame-local-variables-check t))
	(t
	 (setq-default left-margin-width
		       (darkroom-recall 'left-margin-width))
	 (setq-default right-margin-width
		       (darkroom-recall 'right-margin-width))))
  (darkroom-mode-update-window)
  ; - restore frame size	 
  (cond ((eq window-system 'w32) 
           (w32-fullscreen-off))
         ((eq window-system 'ns)
         (set-frame-parameter nil 'fullscreen nil)))
  (darkroom-mode-recall-frame-size)
  (darkroom-mode-set-enabled nil)
  (message (format "darkroom-mode disabled on %s" (selected-frame)))
  ; for some reason frame size needs to be recalled again ...
  (sleep-for 0.05)
  (darkroom-mode-recall-frame-size))

(defun darkroom-mode-recall-frame-size()
  (modify-frame-parameters (selected-frame)
			   `((left . ,(darkroom-recall 'frame-left))
			     (top . ,(darkroom-recall 'frame-top))
			     (width . ,(darkroom-recall 'frame-width))
			     (height . ,(darkroom-recall 'frame-height)))))

;;;;;;;;;;;; small margins darkroom ;;;
(defun darkroom-mode-small()
  (interactive)
  (let ((darkroom-mode-left-margin 1)
	(darkroom-mode-right-margin 1))
    (darkroom-mode)))

(defun darkroom-mode-ecb()
  (interactive)
  (let ((darkroom-mode-left-margin 10)
	(darkroom-mode-right-margin 1))
    (darkroom-mode)))

;;;;;;;;;;;;;;;;; end ;;;;;;;;;;;;;;;;;
(provide 'darkroom-mode)