]> code.delx.au - gnu-emacs-elpa/blob - packages/svg-clock/svg-clock.el
Get "make -k" to go through
[gnu-emacs-elpa] / packages / svg-clock / svg-clock.el
1 ;;; svg-clock.el --- Analog clock using Scalable Vector Graphics
2
3 ;; Copyright (C) 2011 Free Software Foundation, Inc.
4
5 ;; Author: Ulf Jasper <ulf.jasper@web.de>
6 ;; Created: 22. Sep. 2011
7 ;; Keywords: demo, svg, clock
8 ;; Version: 0.5
9
10 ;; This file is part of GNU Emacs.
11
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.
16
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.
21
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/>.
24
25 ;;; Commentary:
26
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.
32
33 ;; Installation
34 ;; ------------
35
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)
39
40 ;; ======================================================================
41
42 ;;; Code:
43 (defconst svg-clock-version "0.5" "Version number of `svg-clock'.")
44
45 (require 'image-mode)
46
47 (defgroup svg-clock nil
48 "svg-clock"
49 :group 'applications)
50
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))
57 :group 'svg-clock)
58
59 (defvar svg-clock-timer nil)
60
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%\" >
67 <defs>
68 <symbol id=\"tick\">
69 <line x1=\"50\" y1=\"2\" x2=\"50\" y2=\"4\"
70 style=\"stroke:%FG%;stroke-width:1\"/>
71 </symbol>
72 <symbol id=\"ticklong\">
73 <line x1=\"50\" y1=\"2\" x2=\"50\" y2=\"9\"
74 style=\"stroke:%FG%;stroke-width:2\"/>
75 </symbol>
76 <symbol id=\"hand-hour\">
77 <line x1=\"50\" y1=\"22\" x2=\"50\" y2=\"54\"
78 style=\"stroke:%FG%;stroke-width:3\"/>
79 </symbol>
80 <symbol id=\"hand-minute\">
81 <line x1=\"50\" y1=\"12\" x2=\"50\" y2=\"55\"
82 style=\"stroke:%FG%;stroke-width:3\"/>
83 </symbol>
84 <symbol id=\"hand-second\">
85 <line x1=\"50\" y1=\"10\" x2=\"50\" y2=\"59\"
86 style=\"stroke:%FG%;stroke-width:0.5\"/>
87 </symbol>
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)\" />
97 </g>
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)\" />
111 </g>
112
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)\" />
117 </g>
118
119 <g id=\"clock\">
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)\" />
128
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)\" />
133
134 <use xlink:href=\"#hand-second\"
135 transform=\"rotate(%SECOND%, 50, 50)\">
136 </use>
137 <use xlink:href=\"#hand-minute\"
138 transform=\"rotate(%MINUTE%, 50, 50)\">
139 </use>
140 <use xlink:href=\"#hand-hour\"
141 transform=\"rotate(%HOUR%, 50, 50)\">
142 </use>
143
144 <circle cx=\"50\" cy=\"50\" r=\"3\" fill=\"%FG%\"/>
145 </g>
146 </defs>
147 <use xlink:href=\"#clock\"
148 transform=\"scale(%SCALE%, %SCALE%)\"/>
149 </svg>"
150 "The template for drawing the `svg-clock'.")
151
152 (defvar svg-clock--actual-size 100
153 "Actual size of the svg clock.")
154
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))))
159
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)
164 (replace-match to)))
165
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))
173 (hours (nth 2 time))
174 (bg-colour (svg-clock-color-to-hex (face-background 'default)))
175 (fg-colour (svg-clock-color-to-hex (face-foreground 'default))))
176 (erase-buffer)
177 (insert svg-clock-template)
178
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)))
189 (image-mode)
190 (image-toggle-display-image))))
191
192 (defun svg-clock-update ()
193 "Update the clock."
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))))
198
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)
202 (if perform-update
203 (svg-clock-update)))
204
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."
208 (interactive)
209 (svg-clock-set-size (+ 10 svg-clock--actual-size) t))
210
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."
214 (interactive)
215 (svg-clock-set-size (max 10 (- svg-clock--actual-size 10)) t))
216
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."
220 (interactive)
221 (let ((clock-win (get-buffer-window "*clock*")))
222 (if clock-win
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)))))
227
228 (defun svg-clock-stop ()
229 "Stop the svg clock and hide it."
230 (interactive)
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")))
237
238 (defun svg-clock-start ()
239 "Start the svg clock."
240 (if svg-clock-timer
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))
247 (svg-clock-mode)
248 (message "Clock started")))
249
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)
256 map))
257
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))
262
263 ;;;###autoload
264 (defun svg-clock ()
265 "Start/stop the svg clock."
266 (interactive)
267 (if svg-clock-timer
268 (svg-clock-stop)
269 (svg-clock-start)))
270
271 (provide 'svg-clock)
272
273 ;;; svg-clock.el ends here