1 ;;; svg-clock.el --- Analog clock using Scalable Vector Graphics
3 ;; Copyright (C) 2011 Free Software Foundation, Inc.
5 ;; Author: Ulf Jasper <ulf.jasper@web.de>
6 ;; Created: 22. Sep. 2011
7 ;; Keywords: demo, svg, clock
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software: you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation, either version 3 of the License, or
15 ;; (at your option) any later version.
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 ;; svg-clock provides a scalable analog clock. Rendering is done by
28 ;; means of svg (Scalable Vector Graphics). Works only with Emacsen
29 ;; which were built with svg support -- (image-type-available-p 'svg)
30 ;; must return t. Call `svg-clock' to start/stop the clock.
31 ;; Set `svg-clock-size' to change its size.
36 ;; Add the following lines to your Emacs startup file (`~/.emacs').
37 ;; (add-to-list 'load-path "/path/to/svg-clock.el")
38 ;; (autoload 'svg-clock "svg-clock" "Start/stop svg-clock" t)
40 ;; ======================================================================
43 (defconst svg-clock-version "0.4" "Version number of `svg-clock'.")
47 (defgroup svg-clock nil
51 (defcustom svg-clock-size t
52 "Size (width and height) of the clock.
53 Either an integer which gives the clock size in pixels, or t
54 which makes the clock fit to its window automatically."
55 :type '(choice (integer :tag "Fixed Size" :value 250)
56 (const :tag "Fit to window" t))
59 (defvar svg-clock-timer nil)
61 (defconst svg-clock-template
62 "<?xml version=\"1.0\"?>
63 <!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"
64 \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">
65 <svg xmlns=\"http://www.w3.org/2000/svg\"
66 width=\"%SIZE%\" height=\"%SIZE%\" >
69 <line x1=\"50\" y1=\"2\" x2=\"50\" y2=\"4\"
70 style=\"stroke:%FG%;stroke-width:1\"/>
72 <symbol id=\"ticklong\">
73 <line x1=\"50\" y1=\"2\" x2=\"50\" y2=\"9\"
74 style=\"stroke:%FG%;stroke-width:2\"/>
76 <symbol id=\"hand-hour\">
77 <line x1=\"50\" y1=\"22\" x2=\"50\" y2=\"54\"
78 style=\"stroke:%FG%;stroke-width:3\"/>
80 <symbol id=\"hand-minute\">
81 <line x1=\"50\" y1=\"12\" x2=\"50\" y2=\"55\"
82 style=\"stroke:%FG%;stroke-width:3\"/>
84 <symbol id=\"hand-second\">
85 <line x1=\"50\" y1=\"10\" x2=\"50\" y2=\"59\"
86 style=\"stroke:%FG%;stroke-width:0.5\"/>
88 <g id=\"minute-ticks-a\">
89 <use xlink:href=\"#tick\"
90 transform=\"rotate(6, 50, 50)\" />
91 <use xlink:href=\"#tick\"
92 transform=\"rotate(12, 50, 50)\" />
93 <use xlink:href=\"#tick\"
94 transform=\"rotate(18, 50, 50)\" />
95 <use xlink:href=\"#tick\"
96 transform=\"rotate(24, 50, 50)\" />
98 <g id=\"minute-ticks-b\">
99 <use xlink:href=\"#minute-ticks-a\"
100 transform=\"rotate(0, 50, 50)\" />
101 <use xlink:href=\"#minute-ticks-a\"
102 transform=\"rotate(30, 50, 50)\" />
103 <use xlink:href=\"#minute-ticks-a\"
104 transform=\"rotate(60, 50, 50)\" />
105 <use xlink:href=\"#minute-ticks-a\"
106 transform=\"rotate(90, 50, 50)\" />
107 <use xlink:href=\"#minute-ticks-a\"
108 transform=\"rotate(120, 50, 50)\" />
109 <use xlink:href=\"#minute-ticks-a\"
110 transform=\"rotate(150, 50, 50)\" />
113 <g id=\"5-minute-ticks\">
114 <use xlink:href=\"#ticklong\" />
115 <use xlink:href=\"#ticklong\" transform=\"rotate(30, 50, 50)\" />
116 <use xlink:href=\"#ticklong\" transform=\"rotate(60, 50, 50)\" />
120 <use xlink:href=\"#5-minute-ticks\"
121 transform=\"rotate(0, 50, 50)\" />
122 <use xlink:href=\"#5-minute-ticks\"
123 transform=\"rotate(90, 50, 50)\" />
124 <use xlink:href=\"#5-minute-ticks\"
125 transform=\"rotate(180, 50, 50)\" />
126 <use xlink:href=\"#5-minute-ticks\"
127 transform=\"rotate(270, 50, 50)\" />
129 <use xlink:href=\"#minute-ticks-b\"
130 transform=\"rotate(0, 50, 50)\" />
131 <use xlink:href=\"#minute-ticks-b\"
132 transform=\"rotate(180, 50, 50)\" />
134 <use xlink:href=\"#hand-second\"
135 transform=\"rotate(%SECOND%, 50, 50)\">
137 <use xlink:href=\"#hand-minute\"
138 transform=\"rotate(%MINUTE%, 50, 50)\">
140 <use xlink:href=\"#hand-hour\"
141 transform=\"rotate(%HOUR%, 50, 50)\">
144 <circle cx=\"50\" cy=\"50\" r=\"3\" fill=\"%FG%\"/>
147 <use xlink:href=\"#clock\"
148 transform=\"scale(%SCALE%, %SCALE%)\"/>
150 "The template for drawing the `svg-clock'.")
152 (defvar svg-clock--actual-size 100
153 "Actual size of the svg clock.")
155 (defun svg-clock-color-to-hex (colour)
156 "Return hex representation of COLOUR."
157 (let ((values (color-values colour)))
158 (format "#%02x%02x%02x" (nth 0 values) (nth 1 values) (nth 2 values))))
160 (defun svg-clock-replace (from to)
161 "Replace all occurrences of FROM with TO."
162 (goto-char (point-min))
163 (while (re-search-forward from nil t)
166 (defun svg-clock-do-update (time)
167 "Make the clock display TIME.
168 TIME must have the form (SECOND MINUTE HOUR ...), as returned by `decode-time'."
169 (with-current-buffer (get-buffer-create "*clock*")
170 (let* ((inhibit-read-only t)
171 (seconds (nth 0 time))
172 (minutes (nth 1 time))
174 (bg-colour (svg-clock-color-to-hex (face-background 'default)))
175 (fg-colour (svg-clock-color-to-hex (face-foreground 'default))))
177 (insert svg-clock-template)
179 (svg-clock-replace "%BG%" bg-colour)
180 (svg-clock-replace "%FG%" fg-colour)
181 (svg-clock-replace "%HOUR%"
182 (format "%f" (+ (* hours 30) (/ minutes 2.0))))
183 (svg-clock-replace "%MINUTE%"
184 (format "%f" (+ (* minutes 6) (/ seconds 10.0))))
185 (svg-clock-replace "%SECOND%" (format "%f" (* seconds 6)))
186 (svg-clock-replace "%SIZE%" (format "%d" svg-clock--actual-size))
187 (svg-clock-replace "%SCALE%"
188 (format "%f" (/ svg-clock--actual-size 100.0)))
190 (image-toggle-display-image))))
192 (defun svg-clock-update ()
194 (if (integerp svg-clock-size)
195 (setq svg-clock--actual-size svg-clock-size)
196 (svg-clock-fit-window))
197 (svg-clock-do-update (decode-time (current-time))))
199 (defun svg-clock-set-size (size &optional perform-update)
200 "Set the SIZE of the clock and optionally PERFORM-UPDATE."
201 (setq svg-clock--actual-size size)
205 (defun svg-clock-grow ()
206 "Enlarge the size of the svg clock by 10 pixesl.
207 If `svg-clock-size' is t this command has no effect."
209 (svg-clock-set-size (+ 10 svg-clock--actual-size) t))
211 (defun svg-clock-shrink ()
212 "Reduce the size of the svg clock by 10 pixesl.
213 If `svg-clock-size' is t this command has no effect."
215 (svg-clock-set-size (max 10 (- svg-clock--actual-size 10)) t))
217 (defun svg-clock-fit-window (&optional perform-update)
218 "Make the svg clock fill the whole window it is displayed in.
219 Optionally PERFORM-UPDATE immediately."
221 (let ((clock-win (get-buffer-window "*clock*")))
223 (let* ((coords (window-inside-pixel-edges clock-win))
224 (width (- (nth 2 coords) (nth 0 coords)))
225 (height (- (nth 3 coords) (nth 1 coords))))
226 (svg-clock-set-size (min width height) perform-update)))))
228 (defun svg-clock-stop ()
229 "Stop the svg clock and hide it."
231 (if (not svg-clock-timer)
232 (message "svg-clock is not running.")
233 (cancel-timer svg-clock-timer)
234 (setq svg-clock-timer nil)
235 (replace-buffer-in-windows "*clock*")
236 (message "Clock stopped")))
238 (defun svg-clock-start ()
239 "Start the svg clock."
241 (message "svg-clock is running already")
242 (switch-to-buffer (get-buffer-create "*clock*"))
243 (unless (integerp svg-clock-size)
244 (svg-clock-fit-window))
245 (setq svg-clock-timer
246 (run-with-timer 0 1 'svg-clock-update))
248 (message "Clock started")))
250 (defvar svg-clock-mode-map
251 (let ((map (make-sparse-keymap)))
252 (define-key map [?+] 'svg-clock-grow)
253 (define-key map [?-] 'svg-clock-shrink)
254 (define-key map [?q] 'svg-clock-stop)
255 (define-key map [?f] 'svg-clock-fit-window)
258 (define-derived-mode svg-clock-mode fundamental-mode "svg clock"
259 "Major mode for the svg-clock buffer.
260 \\{svg-clock-mode-map}"
261 (buffer-disable-undo))
265 "Start/stop the svg clock."
273 ;;; svg-clock.el ends here