
;;;; stop-raise.lj -- raises a window if it is focused and the cursor
;;;; has remained stationary over it for a period of time.

;;;; Modified by Paul Evans, 2004 <leonerd@leonerd.org.uk>, based on
;;;; the following:

;;;; stop-focus.jl -- focuses a window when the cursor enters the
;;;; window and remains stationary for a period of time.

;; Copyright (C) 2000 John Kodis <kodis@jagunet.com>.  The sawfish
;; stop-focus.jl module is based on hover-focus.jl by Nils Barth,
;; which was influenced by scwm's hover-focus.scm by John Kodis and
;; Greg Badros, which was in turn based on scwm's auto-raise.scm by
;; Maciej Stachowiak and Greg Badros.  That's one hell of a pedigree
;; for a hundred lines of code.

;; This file is free software distributed under the terms of the GNU
;; General Public License; either version 2, or (at your option) any
;; later version.  You should have received a copy of the GNU General
;; Public License along with sawfish; see the file COPYING.  If not,
;; write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA
;; 02139, USA.

(require 'timers)
(provide 'stop-raise)

;; Variables

(defcustom stop-raise-delay 250
  "Delay in milliseconds until a window raises."
  :group (focus)
  :type number
  :range (0 . 5000))

(defcustom stop-raise-slop 10
  "Distance the cursor can move when stopped."
  :group (focus)
  :type number
  :range (0 . 5000))

(defvar stop-raise-timer nil)
(defvar stop-raise-positions nil)
(defvar stop-raise-count nil)
(defvar stop-raise-interval nil)

;; Functions

;; motion -- approximates the distance from the eldest saved pointer
;; position to the current pointer location.
(defun motion ()
  (let* ((point (query-pointer))
	 (dx (abs (- (caar stop-raise-positions) (car point))))
	 (dy (abs (- (cdar stop-raise-positions) (cdr point)))))
    (+ (max dx dy) (/ (min dx dy) 2))))

;; stopped-p -- determines if the pointer has stopped, based on motion
;; and allowable position slop.
(defun stopped-p ()
  (and (>= (length stop-raise-positions) stop-raise-count)
       (< (motion) stop-raise-slop)))

;; stop-raise-timer-handler -- focuses a window if stopped, otherwise
;; re-arms timer and pushes a new position onto the position list.
(defun stop-raise-timer-handler (w)
;  (format standard-error "timer: %s, %d, %d\n"
;	  w (length stop-raise-positions) (truncate (motion)))
  (if (stopped-p)
    (progn 
;      (format standard-error "timed raise: %s\n" w) 
      ; Only raise it if we still have the focus
      (if (eq w (input-focus)) (raise-window w))
      )
    (set-timer stop-raise-timer 0 stop-raise-interval)
    (when (>= (length stop-raise-positions) stop-raise-count)
      (setq stop-raise-positions (cdr stop-raise-positions)))
    (setq stop-raise-positions
	  (append stop-raise-positions (list (query-pointer)))
	  )
    )
  )

;; stop-raise-set-timer-values -- tries to split stop-raise-delay into
;; max-count intervals (arbitrarily chosen as 5), unless this would
;; reduce the interval below min-interval (20 ms, intended to reflect what
;; can be scheduled reliably), in which case fewer intervals are used.
(defun stop-raise-set-timer-values ()
  (let* ((max-count 5)
	 (min-interval 20)
	 (stop-time stop-raise-delay)
	 (count (max 1 (min max-count (quotient stop-time min-interval))))
	 (interval (max min-interval (quotient stop-time count))))
    (setq stop-raise-count count)
    (setq stop-raise-interval interval)))

;; stop-raise-enter -- triggered on entering any window 
;; Pushes the current pointer on the positions list, computes
;; timer parameters, and starts the raise timer running.
(defun stop-raise-enter (w mode)
;  (format standard-error "enter: %s\n" w)
  (if (zerop stop-raise-delay) 
    (progn 
      (raise-window w) 
;      (format standard-error "immediate raise: %s\n" w)
      )
    (progn
      (setq stop-raise-positions (list (query-pointer)))
      (stop-raise-set-timer-values)
      (setq stop-raise-timer
	    (make-timer (lambda () (stop-raise-timer-handler w))
		0 stop-raise-interval)
	    )
      )
    )
  )

;; stop-raise-leave -- cleans up on exiting a window by deleting the
;; focus timer.
(defun stop-raise-leave (w mode)
;  (format standard-error "leave: %s\n" w)
  (when stop-raise-timer
    (delete-timer stop-raise-timer)
    (setq stop-raise-timer nil)
   )
  )

(add-hook 'enter-notify-hook stop-raise-enter)
; Need also to catch focus-in events, in case of click-to-focus and the mouse
;  has already loitered here a while
(add-hook 'focus-in-hook stop-raise-enter)
(add-hook 'leave-notify-hook stop-raise-leave)
