Use a nicer, cleaner syntax calling #'make-glyph
[emchat] / emchat-xwem.el
1 ;; emchat-xwem.el --- Activity indicator for XWEM tray
2
3 ;; Copyright (C) 2005 - 2011 Steve Youngs
4
5 ;; Author:        Steve Youngs <steve@emchat.org>
6 ;; Maintainer:    Steve Youngs <steve@emchat.org>
7 ;; Created:       <2005-10-15>
8 ;; Homepage:      http://www.emchat.org/
9 ;; Keywords:      emchat, icq, xwem, dock, tray
10
11 ;; This file is part of EMchat.
12
13 ;; Redistribution and use in source and binary forms, with or without
14 ;; modification, are permitted provided that the following conditions
15 ;; are met:
16 ;;
17 ;; 1. Redistributions of source code must retain the above copyright
18 ;;    notice, this list of conditions and the following disclaimer.
19 ;;
20 ;; 2. Redistributions in binary form must reproduce the above copyright
21 ;;    notice, this list of conditions and the following disclaimer in the
22 ;;    documentation and/or other materials provided with the distribution.
23 ;;
24 ;; 3. Neither the name of the author nor the names of any contributors
25 ;;    may be used to endorse or promote products derived from this
26 ;;    software without specific prior written permission.
27 ;;
28 ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
29 ;; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30 ;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31 ;; DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 ;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33 ;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34 ;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35 ;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
36 ;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
37 ;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
38 ;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
40 ;;; Commentary:
41 ;;
42 ;;    Puts a spiffy activity indicator into the XWEM systray.
43 ;;    To use this, add...
44 ;;
45 ;;          (require 'emchat-xwem)
46 ;;          (add-hook 'xwem-after-init-hook 'emchat-xwem-init)
47 ;;
48 ;;    ...to your ~/.xwem/xwemrc.el
49 ;;
50
51 ;;; Todo:
52 ;;
53 ;;     o Add more XWEM things... can't think of any right now.
54 ;;
55
56 ;;; Code:
57 (require 'font) ;; for font-x-registry-and-encoding-regexp
58
59 (eval-when-compile
60   (autoload 'xwem-dpy "xwem-struct" nil nil 'macro)
61   (autoload 'xwem-message "xwem-misc")
62   (autoload 'xwem-misc-find-cl-by-emacs-frame "xwem-misc")
63   (autoload 'xwem-osd-clear "xwem-osd")
64   (autoload 'xwem-osd-create "xwem-osd")
65   (autoload 'xwem-osd-create-dock "xwem-osd")
66   (autoload 'xwem-osd-destroy "xwem-osd")
67   (autoload 'xwem-osd-destroy-instances "xwem-osd")
68   (autoload 'xwem-osd-get-prop "xwem-osd")
69   (autoload 'xwem-osd-hide "xwem-osd")
70   (autoload 'xwem-osd-icon-data-add "xwem-osd")
71   (autoload 'xwem-osd-p "xwem-osd")
72   (autoload 'xwem-osd-put-prop "xwem-osd")
73   (autoload 'xwem-osd-rem-prop "xwem-osd")
74   (autoload 'xwem-osd-set-color "xwem-osd")
75   (autoload 'xwem-osd-set-font "xwem-osd")
76   (autoload 'xwem-osd-show "xwem-osd")
77   (autoload 'xwem-osd-text "xwem-osd")
78   (autoload 'xwem-osd-text-add "xwem-osd")
79   (autoload 'xwem-popup-menu "xwem-mouse")
80   (autoload 'xwem-select-client "xwem-clients")
81   (defvar xwem-current-cl)
82   )
83
84 (defgroup emchat-xwem nil
85   "Group to customize emchat's xwem capabilities."
86   :group 'emchat
87   :prefix "emchat-xwem-")
88
89 (defcustom emchat-xwem-osd-enable t
90   "*Non-nil to show incoming messages in OSD."
91   :group 'emchat-xwem
92   :type 'boolean)
93
94 (defcustom emchat-xwem-dock-enable t
95   "*Non-nil to start emchat dock in system tray."
96   :group 'emchat-xwem
97   :type 'boolean)
98
99 (defcustom emchat-xwem-osd-show-icon t
100   "*Non-nil to show ICQ icon in OSD as well as message."
101   :group 'emchat-xwem
102   :type 'boolean)
103
104 (defcustom emchat-xwem-osd-font
105   (font-create-name (make-font :weight "bold" :size 32))
106   "Font to be used to display OSD message."
107   :group 'emchat-xwem
108   :type '(restricted-sexp :match-alternatives (nil try-font-name)))
109
110 (defcustom emchat-xwem-osd-color "magenta3"
111   "*Default color for OSD messages."
112   :group 'emchat-xwem
113   :type 'color)
114
115 (defcustom emchat-xwem-osd-group-colors
116   '((:girls . "deeppink")
117     (:vip . "gold")
118     (:work . "red3")
119     (:friends . "green3"))
120   "*Alist of colors to be used in OSD for emchat groups.
121 Each element is cons in form (GROUP . COLOR)."
122   :group 'emchat-xwem
123   :type '(repeat (cons (keyword :tag "group") color)))
124
125 (defcustom emchat-xwem-osd-coordinates '(100 . 700)
126   "*Place on screen for OSD messages."
127   :group 'emchat-xwem
128   :type '(cons (number :tag "X") (number :tag "Y")))
129
130 (defcustom emchat-xwem-osd-display-time 3
131   "*Time the OSD to be shown."
132   :group 'emchat-xwem
133   :type 'number)
134
135 ;;; Internal variables
136
137 (defvar emchat-xwem-osd-icon
138   (concat "/* XPM */\n"
139 "static char *icq[] = {\n"
140 "/* columns rows colors chars-per-pixel */\n"
141 "\"50 50 8 1\",\n"
142 "\"  c #09090C\",\n"
143 "\". c #385930\",\n"
144 "\"X c #43FD32\",\n"
145 "\"o c #33C326\",\n"
146 "\"O c #ECE345\",\n"
147 "\"+ c #C5C6C7\",\n"
148 "\"@ c None\",\n"
149 "\"# c #DC3129\",\n"
150 "/* pixels */\n"
151 "\"@@@@@@@@@@@@@@@@@@@@@@@@@@@@++.+.++@@@@@@@@@@@@@@@\",\n"
152 "\"@@@@@@@@@@@@@@++++++@@@@@@++.      .+@@@@@@@@@@@@@\",\n"
153 "\"@@@@@@@@@@@@@+     .+@@@@+.          +@@@@@@@@@@@@\",\n"
154 "\"@@@@@@@@@@@@+       .+@@++    ....   .+@@@@@@@@@@@\",\n"
155 "\"@@@@@@@@@@@+    ..    +@.   oXXXXXo.   +@@@@@@@@@@\",\n"
156 "\"@@@@@@@@@@@+   oXXo   .+   oXXXXXXXo   +@@@@@@@@@@\",\n"
157 "\"@@@@@@@@@@@+  .XXXXo       XXXXXXXXXo  .+@@@@@@@@@\",\n"
158 "\"@@@@@@@@@@@.  oXXXXX.     oXXXXXXXXXo  .+@@@@@@@@@\",\n"
159 "\"@@@@@@@@@@@.  oXXXXXX     ooXXXXXXXXX   +@@@@@@@@@\",\n"
160 "\"@@@@@@@@@@@+  oXXXXXX.    oXXXXXXXXXo  .+@@@@@@@@@\",\n"
161 "\"@@@@@@@@@@@+   XXXXXXo    .XXXXXXXXX.  .@@@@@@@@@@\",\n"
162 "\"@@@@@@@@@@@+   .XXXXoo    oXXXXXXXXX   ++++++@@@@@\",\n"
163 "\"@@@@@@++...++   oXXXXo    .XXXXXXXX    ..  ..++@@@\",\n"
164 "\"@@@+++   .       oXXXo    .XXXXXXX.     .     .+@@\",\n"
165 "\"@@+.              oXXX     XXXXXX.             .+@\",\n"
166 "\"@++    .....       oXX.    oXXXXo      .ooXo.   +@\",\n"
167 "\"@+    XXXXXXo      .XXo    oXXXo      XXXXXXXo  .+\",\n"
168 "\"+.  .XXXXXXXXo      .oo    oXXX     .XXXXXXXXo  .+\",\n"
169 "\"+   XXXXXXXXXXX.      .    .XX     oXXXXXXXXXo  .+\",\n"
170 "\"+  .XXXXXXXXXXXXo           ..    oXXXXXXXXXo   +@\",\n"
171 "\".  .XXXXXXXXXXXXXX.             .oXXXXXXXXXo   .+@\",\n"
172 "\".  .XXXXXXXXXXXXXX    .OOO.    .oXXXXXoooo     +@@\",\n"
173 "\"+   oXXXXXXXXXXXoo   OOOOOOO   .oo..         .+@@@\",\n"
174 "\"+.   ooXooooo...    .OOOOOOO.               .+@@@@\",\n"
175 "\"@+.                 OOOOOOOOO                .+@@@\",\n"
176 "\"@@+.                OOOOOOOOO                  +@@\",\n"
177 "\"@@@++.              OOOOOOOO.       ..ooooo.    +@\",\n"
178 "\"@@@@@+       # ###  .OOOOOOO.  .oXXXXXXXXXXXo.  .+\",\n"
179 "\"@@@@+.    ########   .OOOOO.   oXXXXXXXXXXXXXo   +\",\n"
180 "\"@@@+.   #########     .OOO.    .XXXXXXXXXXXXXX.  +\",\n"
181 "\"@@@+   #########                .oXXXoXXXXXXXXo  .\",\n"
182 "\"@@@+  #########     .       .    .oXXXXXXXXXXXo  .\",\n"
183 "\"@@@+  ########      XXo   ooXo     oXXXXXXXXXXo  .\",\n"
184 "\"@@@.   ######      oXXX   oXXX.     oXXXXXXXXX.  .\",\n"
185 "\"@@@+              oXXXX   .XXXX.     .XXXXXXXo   +\",\n"
186 "\"@@@@+.           oXXXXX.   oXXXX      .ooXXXo   ++\",\n"
187 "\"@@@@@++.   .    ooXXXXX.   oXXXXX.       ..     +@\",\n"
188 "\"@@@@@@@+++++   .XXXXXXX.   oXXXXXo            .+@@\",\n"
189 "\"@@@@@@@@@@@.   XXXXXXXXo   .XXXXXo   ...     ++@@@\",\n"
190 "\"@@@@@@@@@@+   oXXXXXXXX.   .XXXXXX.  +@++++++@@@@@\",\n"
191 "\"@@@@@@@@@@+  .XXXXXXXXXo    XXXXXX.  .@@@@@@@@@@@@\",\n"
192 "\"@@@@@@@@@@.  .XXXXXXXXXo    .XXXXX   +@@@@@@@@@@@@\",\n"
193 "\"@@@@@@@@@@.  .XXXXXXXXXo     .XXXo   +@@@@@@@@@@@@\",\n"
194 "\"@@@@@@@@@@+  .XXXXXXXXX.      .oo.  .+@@@@@@@@@@@@\",\n"
195 "\"@@@@@@@@@@+   XXXXXXXXX   +.        +@@@@@@@@@@@@@\",\n"
196 "\"@@@@@@@@@@+.  .oXXXXXo.  .@+..    ..@@@@@@@@@@@@@@\",\n"
197 "\"@@@@@@@@@@@+    ooXo.    +@@@++..++@@@@@@@@@@@@@@@\",\n"
198 "\"@@@@@@@@@@@++           +@@@@@@@@@@@@@@@@@@@@@@@@@\",\n"
199 "\"@@@@@@@@@@@@@+.       .+@@@@@@@@@@@@@@@@@@@@@@@@@@\",\n"
200 "\"@@@@@@@@@@@@@@++.....++@@@@@@@@@@@@@@@@@@@@@@@@@@@\"\n"
201 "};\n"))
202
203 (defvar emchat-xwem-active
204   (concat "/* XPM */\n"
205 "static char *noname[] = {\n"
206 "/* columns rows colors chars-per-pixel */\n"
207 "\"16 16 17 1\",\n"
208 "\"  c #000100\",\n"
209 "\". c #013200\",\n"
210 "\"X c #31000b\",\n"
211 "\"o c #2c2c00\",\n"
212 "\"O c #035500\",\n"
213 "\"+ c #037700\",\n"
214 "\"@ c #4d0011\",\n"
215 "\"# c #736f00\",\n"
216 "\"$ c #068600\",\n"
217 "\"% c #07ab00\",\n"
218 "\"& c #09cb00\",\n"
219 "\"* c #08ed00\",\n"
220 "\"= c #a00024\",\n"
221 "\"- c #d1002f\",\n"
222 "\"; c #878200\",\n"
223 "\": c #fff82a\",\n"
224 "\"> c None\",\n"
225 "/* pixels */\n"
226 "\">>>>   >    >>>>\",\n"
227 "\">>>  +   %&. >>>\",\n"
228 "\">>> +*$ $**% >>>\",\n"
229 "\">>>  *& +**$   >\",\n"
230 "\">    O* +*%  .  \",\n"
231 "\" O**O O..& .**$ \",\n"
232 "\" &***$ oo O**%  \",\n"
233 "\" O$+O #:: .    >\",\n"
234 "\">   XX;::oO+++. \",\n"
235 "\"> @--@ #o %***& \",\n"
236 "\"> ==X $.OO +**& \",\n"
237 "\">    $*OO*O O+. \",\n"
238 "\">>> O**+.*&    >\",\n"
239 "\">>> %**+ +& >>>>\",\n"
240 "\">>> .&&    >>>>>\",\n"
241 "\">>>>    >>>>>>>\>\"\n"
242 "};\n"))
243
244 (defvar emchat-xwem-inactive
245   (concat "/* XPM */\n"
246 "static char *noname[] = {\n"
247 "\"16 16 7 1\",\n"
248 "\"     c None\",\n"
249 "\".    c #000100 s foreground\",\n"
250 "\"+    c #013200\",\n"
251 "\"@    c #2C2C00\",\n"
252 "\"#    c #000000\",\n"
253 "\"$    c #31000B\",\n"
254 "\"o    c gray s background\",\n"
255 "/* pixels */\n"
256 "\"    ... ....    \",\n"
257 "\"   ..o...oo+.   \",\n"
258 "\"   .ooo.oooo.   \",\n"
259 "\"   ..oo.oooo... \",\n"
260 "\" ....oo.ooo..+..\",\n"
261 "\".oooo.o++o.oooo.\",\n"
262 "\".ooooo.@@.oooo..\",\n"
263 "\".oooo.ooo.+.... \",\n"
264 "\" ...#$ooo@oooo+.\",\n"
265 "\" .ooo#.o@.ooooo.\",\n"
266 "\" .oo$oo+oo.oooo.\",\n"
267 "\" ....ooo#oo.oo+.\",\n"
268 "\"   .oooo#oo.... \",\n"
269 "\"   .oooo.oo.    \",\n"
270 "\"   .+oo....     \",\n"
271 "\"    ....        \"\n"
272 "};\n"))
273
274 (defvar emchat-xwem-saved-client nil
275   "Saved selected xwem client.")
276
277 (defvar emchat-xwem-keymap
278   (let ((map (make-sparse-keymap)))
279     (define-key map [button1] 'emchat-xwem-select-emchat)
280     (define-key map [button3] 'emchat-xwem-restore-client)
281     map)
282   "*Keymap for emchat dock.")
283
284 (defvar emchat-xwem-osd nil)
285 (defvar emchat-xwem-dock nil)
286
287 \f
288 (defun emchat-xwem-activity-off ()
289   "Make the EMchat/XWEM activity indicator inactive."
290   (emchat-xwem-dock-change-icon emchat-xwem-inactive)
291   (emchat-xwem-osd-hide))
292
293 (defun emchat-xwem-activity-on ()
294   "Make the EMchat/XWEM activity indicator active."
295   (emchat-xwem-dock-change-icon emchat-xwem-active)
296
297   ;; NICK and MESSAGE are set by dynamic binding
298   (declare (special nick message))
299   (emchat-xwem-osd-show-message nick message))
300
301 (defun emchat-xwem-init ()
302   "*Display EMchat activity indicator."
303   (interactive)
304
305   ;; Possible create dock
306   (emchat-xwem-dock-change-icon emchat-xwem-inactive)
307
308   (add-hook 'emchat-track-activity-hook 'emchat-xwem-activity-on)
309   (add-hook 'emchat-track-clear-hook 'emchat-xwem-activity-off))
310
311 (defun emchat-xwem-fini ()
312   "Remove the EMchat activity indicator from the XWEM systray."
313   (interactive)
314
315   (emchat-xwem-dock-destroy)
316   (emchat-xwem-osd-destroy)
317
318   (remove-hook 'emchat-track-activity-hook 'emchat-xwem-activity-on)
319   (remove-hook 'emchat-track-clear-hook 'emchat-xwem-activity-off))
320
321 \f
322 ;; OSD
323 (defun emchat-xwem-osd-create ()
324   "Create OSD for emchat messages."
325   (setq emchat-xwem-osd
326         (xwem-osd-create (xwem-dpy) (car emchat-xwem-osd-coordinates)
327                              (cdr emchat-xwem-osd-coordinates)
328                              2048 1024))
329   (xwem-osd-set-font emchat-xwem-osd emchat-xwem-osd-font))
330
331 (defun emchat-xwem-osd-destroy ()
332   "Destroy emchat OSD."
333   (when emchat-xwem-osd
334     (when (itimerp (xwem-osd-get-prop emchat-xwem-osd 'timer))
335       (delete-itimer (xwem-osd-get-prop emchat-xwem-osd 'timer)))
336     (xwem-osd-destroy emchat-xwem-osd)
337     (setq emchat-xwem-osd nil)))
338
339 (defun emchat-xwem-osd-color-for-nick (nick)
340   "Return OSD color to use for NICK."
341   (let ((groups (plist-get (assoc nick emchat-world) 'group)))
342     (or (cdr (find groups emchat-xwem-osd-group-colors
343                    :key #'car :test #'(lambda (g i)
344                                         (member i g))))
345         emchat-xwem-osd-color)))
346
347 (defun emchat-xwem-osd-show-message (nick message)
348   "Display NICK and MESSAGE in emchat OSD."
349   (when emchat-xwem-osd-enable
350     (unless emchat-xwem-osd
351       (emchat-xwem-osd-create))
352
353     ;; Find and set proper color
354     (xwem-osd-set-color emchat-xwem-osd (emchat-xwem-osd-color-for-nick nick))
355
356     ;; Just make sure OSD is clear
357     (xwem-osd-clear emchat-xwem-osd)
358
359     (when emchat-xwem-osd-show-icon
360       (xwem-osd-icon-data-add emchat-xwem-osd emchat-xwem-osd-icon))
361     (xwem-osd-text-add
362      emchat-xwem-osd
363      (if emchat-xwem-osd-show-icon
364          (glyph-width (make-glyph `[xpm :data ,emchat-xwem-osd-icon]))
365        0) 0
366      (encode-coding-string
367       (format "%s: %s" nick message)
368       (and (string-match font-x-registry-and-encoding-regexp
369                          emchat-xwem-osd-font)
370            (intern (downcase (format "%s-%s"
371                                      (match-string 1 emchat-xwem-osd-font)
372                                      (match-string 2 emchat-xwem-osd-font)))))
373       ))
374
375     (xwem-osd-show emchat-xwem-osd)
376
377     (when (itimerp (xwem-osd-get-prop emchat-xwem-osd 'timer))
378       (delete-itimer (xwem-osd-get-prop emchat-xwem-osd 'timer)))
379     (xwem-osd-put-prop emchat-xwem-osd 'timer
380       (start-itimer "emchat-osd" #'emchat-xwem-osd-hide
381                     emchat-xwem-osd-display-time))))
382
383 (defun emchat-xwem-osd-hide ()
384   "Hide emchat OSD."
385   (when (xwem-osd-p emchat-xwem-osd)
386      (xwem-osd-hide emchat-xwem-osd)
387      (xwem-osd-destroy-instances emchat-xwem-osd)
388     (when (itimerp (xwem-osd-get-prop emchat-xwem-osd 'timer))
389       (delete-itimer (xwem-osd-get-prop emchat-xwem-osd 'timer)))
390     (xwem-osd-rem-prop emchat-xwem-osd 'timer)))
391
392 ;; Dock
393 (defun emchat-xwem-dock-create ()
394   "Create emchat dock."
395   (setq emchat-xwem-dock
396         (xwem-osd-create-dock (xwem-dpy) 16 16
397                               (list 'keymap emchat-xwem-keymap)))
398   (xwem-osd-show emchat-xwem-dock))
399
400 (defun emchat-xwem-dock-destroy ()
401   "Destroy emchat dock."
402   (when (xwem-osd-p emchat-xwem-dock)
403     (xwem-osd-destroy emchat-xwem-dock))
404   (setq emchat-xwem-dock nil))
405
406 (defun emchat-xwem-dock-change-icon (icon)
407   "Change emchat dock icon to ICON."
408   (when emchat-xwem-dock-enable
409     (unless emchat-xwem-dock
410       (emchat-xwem-dock-create))
411     (xwem-osd-icon-data-add emchat-xwem-dock icon)))
412
413 \f
414 ;;; Commands
415
416 ;; Note: we can use ordinary Emacs commands here, because interactive
417 ;; form is empty, for complex commands we must use
418 ;; `define-xwem-command' and `xwem-interactive'.
419
420 (defun emchat-xwem-select-emchat ()
421   "Make emchat be current client."
422   (interactive)
423   (let ((eicl (xwem-misc-find-cl-by-emacs-frame emchat-frame)))
424     (setq emchat-xwem-saved-client (xwem-cl-selected))
425     (xwem-select-client eicl)
426     (emchat-show-window)))
427
428 (defun emchat-xwem-restore-client ()
429   "Display unseen senders."
430   (interactive)
431   (if (xwem-cl-alive-p emchat-xwem-saved-client)
432       (xwem-select-client emchat-xwem-saved-client)
433     (xwem-message 'warn "EMchat: Saved client dissapeared")))
434
435 (provide 'emchat-xwem)
436 ;;; emchat-xwem.el ends here