;;; xtwm.el --- Frame configuration assuming an external tiling window manager.
;;
;; Filename: xtwm.el
;; Description: Frame configuration that uses one frame per window.
;; Author: Drew Adams
;; Maintainer: Drew Adams
;; Copyright (C) 1999-2009, Drew Adams, all rights reserved.
;; Created: Fri Apr  2 12:34:20 1999
;; Version: 21.1
;; Last-Updated: Wed Feb 11 16:18:28 2009 (-0800)
;;           By: dradams
;;     Update #: 2384
;; URL: http://www.emacswiki.org/cgi-bin/wiki/xtwm.el
;; Keywords: local, frames
;; Compatibility: GNU Emacs 20.x, GNU Emacs 21.x, GNU Emacs 22.x
;;
;; Custom notes:
;; - minibuffer-auto-raise t
;; - pop-up-frame-function (make-frame default-frame-alist))
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:


(require 'hexrgb) ;; hexrgb-color-values-to-hex, hexrgb-hsv-to-rgb, hexrgb-rgb-to-hsv


(defgroup External-Tiling-Window-Manager nil
  "Options to define initial frame configuration."
  :prefix "xtwm-" :group 'frames
  :link `(url-link :tag "Send Bug Report"
          ,(concat "mailto:" "john" "@" "yates-sheets" ".org?subject=\
xtwm.el bug: \
&body=Describe bug here, starting with `emacs -q'.  \
Don't forget to mention your Emacs and library versions."))
  )

(defcustom xtwm-minibuffer-frame-active-background "#000040"
  "XTWM minibuffer's base color when actually active.  Modified
  on the fly to reflect minibuffer recursion depth."
  :type 'color
  :group 'External-Tiling-Window-Manager)

(defcustom xtwm-minibuffer-frame-isearch-background "400000"
  "XTWM minibuffer's color when displaying `isearch' status."
  :type 'color :group 'External-Tiling-Window-Manager)



;;; Minibuffer frame: ********************************
;;;
(defvar xtwm-minibuffer-frame nil
  "External-Tiling-Window-Manager Emacs's sole minibuffer frame.")

(defvar xtwm-minibuffer-frame-original-background nil
  "XTWM minibuffer's original / inactive background color.")

(defvar xtwm-minibuffer-frame-original-height nil
  "XTWM minibuffer's original / inactive background height.")



;;; Main command ***************************************
;;;
;;;###autoload
(defun xtwm-emacs ()
  "External-Tiling-Window-Manager Emacs setup."
  (interactive)
  ;; *Completions* frame
  ;; If `xtwm-minibuffer-frame-flag' is non-nil, then *Completions* frame must be treated
  ;; specially, so that it gets focus from the minibuffer frame.  This is so, even if
  ;; `xtwm-*Completions*-frame-flag' is nil.
  ;; (add-to-list 'special-display-buffer-names
  ;;              `("*Completions*" xtwm-display-*Completions*-frame))

  ;; `display-buffer' (& `*-other-window' fns) will use separate frames.
;  (setq pop-up-frames  t)

  ;; Minibuffer frame
  (setq xtwm-minibuffer-frame default-minibuffer-frame)
  ;; remember initial properties
  (setq xtwm-minibuffer-frame-original-background
        (frame-parameter xtwm-minibuffer-frame 'background-color))
  (setq xtwm-minibuffer-frame-original-height
        (frame-parameter xtwm-minibuffer-frame 'height))

  ;;(setq minibuffer-auto-raise t)

  ;; (xtwm-reset-minibuffer-frame)

  ;; Background colors of minibuffer frame: 3 states
  ;; (add-hook 'isearch-mode-hook 'xtwm-color-minibuffer-frame-isearch)
  ;; (add-hook 'isearch-mode-end-hook 'xtwm-color-minibuffer-frame-on-exit)
  (add-hook 'minibuffer-setup-hook 'xtwm-minibuffer-setup)
  (add-hook 'minibuffer-exit-hook 'xtwm-minibuffer-exit)
;;  (select-window (minibuffer-selected-window))
  (select-frame-set-input-focus (last-nonminibuffer-frame))
)


;; This is essentially a version of `doremi-increment-color-component' for value only.
(defun xtwm-adjust-for-depth (color depth)
  ""
  (unless (string-match "#" color)      ; Convert color name to #hhh...
    (setq color (hexrgb-color-values-to-hex (x-color-values color))))
  ;; Convert RGB to HSV
  (let* ((rgb (x-color-values color))
         (red   (/ (float (nth 0 rgb)) 65535.0)) ; Convert from 0-65535 to 0.0-1.0
         (green (/ (float (nth 1 rgb)) 65535.0))
         (blue  (/ (float (nth 2 rgb)) 65535.0))
         (hsv (hexrgb-rgb-to-hsv red green blue))
         (hue        (nth 0 hsv))
         (saturation (nth 1 hsv))
         (value      (nth 2 hsv)))
    (setq value (* value (/ (float depth) 2.0)))
    (hexrgb-color-values-to-hex (mapcar (lambda (x) (floor (* x 65535.0)))
                                        (hexrgb-hsv-to-rgb hue saturation value)))))

(defun xtwm-minibuffer-depth-based-background (depth-delta)
  "Change background of minibuffer frame to reflect the minibuffer depth."
  (save-window-excursion
    (select-frame xtwm-minibuffer-frame)
    (let ((depth (+ (minibuffer-depth) depth-delta)))
      (set-background-color
       (if (= depth 0)
           xtwm-minibuffer-frame-original-background
         (xtwm-adjust-for-depth xtwm-minibuffer-frame-active-background depth))))))

(defun xtwm-minibuffer-setup ()
  ""
  (xtwm-minibuffer-depth-based-background 0))

(defun xtwm-minibuffer-exit ()
  ""
  (xtwm-minibuffer-depth-based-background -1))


(defun xtwm-display-*Completions*-frame (buf &optional args)
  "Display *Completions* buffer in its own frame.
`special-display-function' is used to do the actual displaying.
Completion input events are redirected to `xtwm-minibuffer-frame'.
BUF and ARGS are the arguments to `special-display-function'."
  (let ((old-ptr-shape (and (boundp 'x-pointer-shape) x-pointer-shape))
        return-window)
    (setq return-window (select-window (funcall special-display-function buf args)))

    ;; We reposition frame this way, instead of binding `special-display-frame-alist'
    ;; with this value, because `after-make-frame-functions' might resize frame.
    ;; (when xtwm-*Completions*-frame-at-right-flag
    ;;   (modify-frame-parameters
    ;;    (selected-frame)                 ; Hard-code 7 here - what does it depend on?
    ;;    `((left . ,(- (x-display-pixel-width) (+ (frame-pixel-width) 7))))))

    (raise-frame)
;    (redirect-frame-focus (selected-frame) xtwm-minibuffer-frame)
    return-window))


(defun xtwm-color-minibuffer-frame-isearch ()
  "Use `xtwm-minibuffer-frame-isearch-background' for minibuffer."
  (and (boundp 'xtwm-minibuffer-frame)
       (save-window-excursion
         (select-frame xtwm-minibuffer-frame)
         (set-background-color
          ;; Can also try `x-defined-colors', defined in `x-win.el'.
          ;; It contains all colors currently supported by X windows.
          (if (x-color-defined-p xtwm-minibuffer-frame-isearch-background)
              xtwm-minibuffer-frame-isearch-background
            "white")))))

(defun xtwm-flash-ding-minibuffer-frame (&optional do-not-terminate)
  "Ring bell (`ding'), after flashing minibuffer frame, if relevant.
Terminates any keyboard macro executing, unless arg DO-NOT-TERMINATE non-nil."
  (flash-ding do-not-terminate (and (boundp 'xtwm-minibuffer-frame)
                                    xtwm-minibuffer-frame)))


;; Bind this to some key in the minibuffer completion maps.  I use `C-o'.
;;;###autoload
(defun xtwm-fit-minibuffer-frame ()
  "Fit the standalone minibuffer frame height to its contents.
Repeat to increase the height by 1.
Bind this in minibuffer keymaps to a key such as `C-o' that you can
use during minibuffer input."
  (interactive)
  (unless (require 'fit-frame nil t)
    (error "You need to load library `fit-frame.el' to use this command"))
  ;; We could assume the minibuffer frame is `xtwm-minibuffer-frame', but we don't.
  (when (and (active-minibuffer-window)
             (save-selected-window
               (select-window (minibuffer-window))
               ;; We should be able to use just (one-window-p),
               ;; but an Emacs bug means we need this:
               (one-window-p nil 'selected-frame)))
    (let* ((frame (save-selected-window
                    (select-window (minibuffer-window)) (selected-frame)))
           (frame-height (frame-height frame)))
      (cond ((eq last-command this-command)
             (set-frame-height frame (1+ (frame-height frame)))
;             (xtwm-set-minibuffer-frame-top/bottom)
             (condition-case nil
                 (scroll-down (frame-height frame))
               (error nil)))
            (t
             (let* ((beg (xtwm-minibuffer-prompt-end))
                    (frame-width (frame-width frame))
                    (fit-frame-min-width frame-width)
                    (window-min-width frame-width)
                    (fit-frame-max-width frame-width)
                    (fit-frame-empty-width frame-width)
                    (fit-frame-empty-special-display-width frame-width)
                    (fit-frame-min-height frame-height)
                    (window-min-height frame-height)
                    (fit-frame-empty-height frame-height)
                    (fit-frame-empty-special-display-height frame-height))
               (fit-frame frame)
               (when (>= emacs-major-version 21)
                 (set-frame-height frame (1+ (frame-height frame)))) ; A little extra.
;               (xtwm-set-minibuffer-frame-top/bottom)
               (condition-case nil (scroll-down (frame-height frame)) (error nil))))))))

(defun xtwm-minibuffer-prompt-end ()
  "Version of `minibuffer-prompt-end' that works for Emacs 20 and later."
  (if (fboundp 'minibuffer-prompt-end) (minibuffer-prompt-end) (point-min)))

(provide 'xtwm)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; xtwm.el ends here
