;;; analog-clock.el --- Analog clock for GNU/Emacs ;; Copyright (C) 2008 Yoni Rabkin ;; ;; Author: Yoni Rabkin ;; ;; 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 3 of ;; the License, 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; if not, write to the Free ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ;; MA 02111-1307, USA. ;;; Commentary: ;; ;; This package renders an analog clock inside the Emacs window. ;; ;; To use it, do M-x analog-clock. ;;; Installation: ;; ;; Place it in your load path with: ;; ;; (add-to-list 'load-path "/PATH/TO/analog-clock/") ;; ;; Then add to your .emacs: ;; ;; (require 'analog-clock) ;;; History: ;; ;; In the beginning there was TECO... ;; Bob's (chipschap) changelog for 4/4/2008: ;; ;; 1. Added sweep second hand and adjusted run interval to suit. ;; 2. Adjusted length of hands to allow for the new second hand. ;; 3. Made the clock much rounder by adjusting horizontal parameter. ;; 4. Eliminated excessive picture-mode messages by toggling artist / ;; picture mode compatibility switch. ;; 5. Fixed spelling error (wow!). ;; ;;; To-do: ;; ;; 1. Make the `defvar's proper customization variables. ;;; Thanks: ;; ;; Thanks to chipschap AKA Bob Newell. ;;; Code: (require 'artist) (defgroup analog-clock nil "Display an analog clock." :group 'games :prefix "analog-clock-") (defvar analog-clock-buffer-name "*Analog Clock*") (defvar analog-clock-pm-compat "Picture mode compatibility") (defvar analog-clock-update-timer nil "Interval timer object.") (defvar analog-clock-run-interval 1 "How often to update the clock.") (defvar analog-clock-center-x nil) (defvar analog-clock-center-y nil) (defvar analog-clock-size nil) (defvar analog-clock-hours-factor 0.35) (defvar analog-clock-minutes-factor 0.5) (defvar analog-clock-seconds-factor 0.8) (defvar analog-clock-phase (/ (* pi 3) 2)) (defvar analog-clock-horizontal-factor 1.4) (defvar analog-clock-numerals-factor 0.8) (defvar analog-clock-numerals-type 'roman) (defvar analog-clock-numerals-positions (list 0 (/ pi 6) (/ pi 3) (/ pi 2) (/ (* pi 2) 3) (/ (* pi 5) 6) pi (/ (* pi 7) 6) (/ (* pi 4) 3) (/ (* pi 3) 2) (/ (* pi 5) 3) (/ (* pi 11) 6) )) (defvar analog-clock-numerals-western (list "3" "4" "5" "6" "7" "8" "9" "10" "11" "12" "1" "2")) (defvar analog-clock-numerals-roman (list "III" "IV" "V" "VI" "VII" "VIII" "IX" "X" "XI" "XII" "I" "II")) (defvar analog-clock-display-seconds nil "Display the seconds hand when t, otherwise only minites.") (defun analog-clock-size-from-window () "Set proportions according to the current window." (setq analog-clock-center-x (floor (/ (window-width) 2)) analog-clock-center-y (floor (/ (window-height) 2)) analog-clock-size (- (floor (/ (min (window-width) (window-height)) 2)) 5))) (defun analog-clock-prepare-buffer () "Set-up the buffer." (when (get-buffer analog-clock-buffer-name) (kill-buffer analog-clock-buffer-name)) (get-buffer-create analog-clock-buffer-name) (with-current-buffer analog-clock-buffer-name (setq analog-clock-run-interval (if analog-clock-display-seconds 1 60)) (buffer-disable-undo) (setq cursor-type nil) (and analog-clock-update-timer (cancel-timer analog-clock-update-timer)) (setq analog-clock-update-timer (run-at-time t analog-clock-run-interval 'analog-clock-update-handler)))) (defun analog-clock-draw-numerals () "Draw the numerals on the clock face." (let ((r (round (* analog-clock-size analog-clock-numerals-factor))) (j analog-clock-center-x) (k analog-clock-center-y) (numerals-list (cond ((eq analog-clock-numerals-type 'western) analog-clock-numerals-western) ((eq analog-clock-numerals-type 'roman) analog-clock-numerals-roman))) (numeral-index 0)) (dolist (t0 analog-clock-numerals-positions) (let ((x (round (+ (* (* r analog-clock-horizontal-factor) (cos t0)) j))) (y (round (+ (* r (sin t0)) k)))) (artist-text-insert-overwrite x y (nth numeral-index numerals-list)) (setq numeral-index (1+ numeral-index)))))) (defun analog-clock-draw-seconds-hand () "Draw the seconds hand on the clock face." (let* ((seconds (nth 0 (decode-time))) (t0 (+ (* (* pi 2) (/ seconds 60.0)) analog-clock-phase)) (r (round (* analog-clock-size analog-clock-seconds-factor))) (j analog-clock-center-x) (k analog-clock-center-y)) (artist-draw-line analog-clock-center-x analog-clock-center-y (round (+ (* (* r analog-clock-horizontal-factor) (cos t0)) j)) (round (+ (* r (sin t0)) k))))) (defun analog-clock-draw-minutes-hand () "Draw the minutes hand on the clock face." (let* ((minutes (nth 1 (decode-time))) (t0 (+ (* (* pi 2) (/ minutes 60.0)) analog-clock-phase)) (r (round (* analog-clock-size analog-clock-minutes-factor))) (j analog-clock-center-x) (k analog-clock-center-y)) (artist-draw-line analog-clock-center-x analog-clock-center-y (round (+ (* (* r analog-clock-horizontal-factor) (cos t0)) j)) (round (+ (* r (sin t0)) k))))) (defun analog-clock-draw-hours-hand () "Draw the hours hand on the clock face." (let* ((minutes (nth 1 (decode-time))) (hours (nth 2 (decode-time))) (t0 (+ (+ (nth (mod hours 12) analog-clock-numerals-positions) (* (/ pi 6) (/ minutes 60.0))) analog-clock-phase)) (r (round (* analog-clock-size analog-clock-hours-factor))) (j analog-clock-center-x) (k analog-clock-center-y)) (artist-draw-line analog-clock-center-x analog-clock-center-y (round (+ (* (* r analog-clock-horizontal-factor) (cos t0)) j)) (round (+ (* r (sin t0)) k))))) (defun analog-clock-draw-body () "Draw the body of the clock." (with-current-buffer analog-clock-buffer-name (artist-mode) (artist-draw-ellipse-general analog-clock-center-x analog-clock-center-y (round (* analog-clock-size analog-clock-horizontal-factor)) analog-clock-size))) (defun analog-clock-kill-buffer () "Kill the analog clock window." (interactive) (and analog-clock-update-timer (cancel-timer analog-clock-update-timer)) (kill-this-buffer)) (define-derived-mode analog-clock-mode artist-mode "Analog Clock" "Major mode for \\\\[Analog Clock]. \\{analog-clock-mode-map}" :group 'analog-clock (setq buffer-read-only t) (setq truncate-lines nil)) (setq analog-clock-mode-map (let ((map (make-sparse-keymap))) (define-key map "q" 'analog-clock-kill-buffer) (define-key map "u" 'analog-clock-update-handler) map)) (defun analog-clock-draw () "Draw the clock." (analog-clock-draw-body) (analog-clock-draw-numerals) (when analog-clock-display-seconds (analog-clock-draw-seconds-hand)) (analog-clock-draw-minutes-hand) (analog-clock-draw-hours-hand)) (defun analog-clock-update-handler () "Update the display." (interactive) (with-current-buffer analog-clock-buffer-name (setq analog-clock-pm-compat artist-picture-compatibility) (setq artist-picture-compatibility nil) (let ((inhibit-read-only t)) (let ((artist-line-char-set t) (artist-fill-char-set t) (artist-line-char artist-erase-char) (artist-fill-char artist-erase-char) (x1 2) (y1 2) (x2 (- (window-width) 2)) (y2 (- (window-height) 2))) (artist-fill-rect (artist-draw-rect x1 y1 x2 y2) x1 y1 x2 y2)) (analog-clock-draw)) ;; For some reason, artist mode needs to be turned off manually ;; after use. (setq artist-picture-compatibility analog-clock-pm-compat) (artist-mode -1))) (defun analog-clock () "Display an analog clock." (interactive) (analog-clock-prepare-buffer) (switch-to-buffer analog-clock-buffer-name) (analog-clock-size-from-window) (analog-clock-draw) (analog-clock-mode)) (provide 'analog-clock) ;;; analog-clock.el ends here