9eaea6d29a834881c3d2e34865fda74befd5a2bf
[emchat] / emchat-buddy.el
1 ;;; emchat-buddy.el --- "Buddy" code for EMchat
2
3 ;; Copyright (C) 2007 - 2010 Steve Youngs
4
5 ;; Author:        Steve Youngs <steve@emchat.org>
6 ;; Maintainer:    Steve Youngs <steve@emchat.org>
7 ;; Created:       2002-10-01
8 ;; Homepage:      http://www.emchat.org/
9 ;; Keywords:      comm ICQ
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 (eval-and-compile
41   (require 'emchat-menu)
42   (require 'emchat-status)
43   (require 'emchat-world)
44   (require 'emchat-history))
45
46 (eval-when-compile
47   (require 'advice)
48   (require 'bbdb))
49
50 (defgroup emchat-buddy nil
51   "Contact list preferences."
52   :group 'emchat)
53
54 ;;;###autoload
55 (defcustom emchat-buddy-window-width 20
56   "*Width of window for `emchat-buddy-buffer'."
57   :group 'emchat-interface)
58
59 (defcustom emchat-buddy-view 
60   'emchat-connected-aliases
61   "*View of buddy buffer.
62 It determines what aliases to be display in buddy buffer.  For example,
63 \(emchat-connected-aliases) means display all connected aliases.
64
65 See `emchat-buddy-view-all', `emchat-buddy-view-connected', and
66 `emchat-buddy-view-active'."
67   :group 'emchat-buddy
68   :type '(choice (item emchat-all-aliases)
69                  (item emchat-connected-aliases)
70                  (item emchat-active-aliases))
71   :initialize 'custom-initialize-default)
72
73 (defcustom emchat-buddy-show-xface nil
74   "*When non-nil, display XFace images in the buddy buffer.
75
76 The images come from BBDB.  For an image to display in the buddy
77 buffer there has to be an existing BBDB entry for the contact that
78 has both a `face' field, for the image, and a `icqnick' field, to
79 match from the contact name in the buddy buffer."
80   :type 'boolean
81   :group 'emchat-buddy
82   :require 'bbdb)
83
84 (defcustom emchat-buddy-prefer-cface-to-xface (featurep 'png)
85   "*When non-nil, display colour faces instead of X-Face if available."
86   :type 'boolean
87   :group 'emchat-buddy)
88
89 (defface emchat-face-selected
90   '((((background dark))
91      (:foreground "darkblue" :background "yellow"))
92     (((background light))
93      (:foreground "darkblue" :background "yellow")))
94   "Face for OFFLINE status."
95   :group 'emchat-buddy)
96
97 ;;; Internal variables
98
99 ;;;###autoload
100 (defvar emchat-buddy-buffer nil
101   "Buffer for contact list.")
102
103 (defun emchat-buddy-mode ()
104   "Major mode for contact list in emchat.
105 Commands: \\{emchat-buddy-mode-map}
106
107 Turning on `emchat-buddy-mode' runs the hook `emchat-buddy-mode-hook'."
108   (interactive)
109   (kill-all-local-variables)
110   (use-local-map emchat-buddy-mode-map)
111   (setq mode-name "emchat-buddy")
112   (setq major-mode 'emchat-buddy-mode)
113   ;; put easy-menu-add after set mode-name
114   (easy-menu-add emchat-main-easymenu)
115   (easy-menu-add emchat-buddy-menu)
116   (easy-menu-add emchat-log-menu)
117   (set-specifier has-modeline-p nil
118                  (cons (current-buffer) nil))
119   (set-specifier horizontal-scrollbar-visible-p nil
120                  (cons (current-buffer) nil))
121   ;(setq modeline-format "%b")
122
123   (run-hooks 'emchat-buddy-mode-hook))
124
125 (defun emchat-buddy-view-set (&optional symbol value)
126   "Set `emchat-buddy-view'."
127   (set-default symbol value)
128   (emchat-buddy-show-buffer 'new 'no-select))
129
130 (defun emchat-face-to-png (face)
131   "Base64 decode a Face header into a PNG.
132 Returns a string."
133   (with-temp-buffer
134     (insert face)
135     (base64-decode-region (point-min) (point-max))
136     (buffer-string)))
137
138 (defun emchat-buddy-show-xface (alias)
139   "Display an XFace image in the buddy buffer."
140   (unless (featurep '(and xface bbdb-autoloads))
141     (error 'unimplemented "X-Face and/or BBDB"))
142   (save-excursion
143     (when (buffer-live-p emchat-buddy-buffer)
144       (set-buffer emchat-buddy-buffer)
145       (goto-char (point-min))
146       (when (search-forward-regexp (concat "^" (regexp-quote alias) "$") nil t)
147         (let ((ext (extent-at (point)))
148               (all-records (bbdb-records))
149               face cface nick record)
150           (while all-records
151             (setq record (car all-records)
152                   nick (bbdb-record-getprop record 'icqnick)
153                   face (bbdb-record-getprop record 'face)
154                   cface (bbdb-record-getprop record 'cface))
155             (when (and (equal nick alias)
156                        (or face cface))
157               ;; put some whitespace between the image and the name
158               (set-extent-begin-glyph
159                (make-extent (point-at-bol) (point-at-eol))
160                (make-glyph " "))
161               ;; Insert the X-Face
162               (when (and face
163                          (or (not emchat-buddy-prefer-cface-to-xface)
164                              (not cface)))
165                 (set-extent-begin-glyph 
166                  ext
167                  (make-glyph (list (vector 'xface
168                                            :data (concat "X-Face: " face)
169                                            :foreground "black"
170                                            :background "white")))))
171               ;; Insert the cface
172               (when (and (featurep 'png)
173                          cface
174                          emchat-buddy-prefer-cface-to-xface)
175                 (set-extent-begin-glyph
176                  ext
177                  (make-glyph (list (vector 'png
178                                            :data (emchat-face-to-png cface)))))))
179             (setq all-records (cdr all-records))))))))
180
181 ;;;###autoload
182 (defun emchat-buddy-show-buffer (&optional new no-select)
183   "Switch to `emchat-buddy-buffer'.
184 Create buffer if buffer does not exists already or
185 NEW is non-nil.
186 Don't select buddy window if NO-SELECT is non-nil.
187 See `emchat-buddy-view' and `emchat-buddy-status-color-hint-flag'."
188   (interactive)
189   (when (or (not (buffer-live-p emchat-buddy-buffer))
190             new)
191     (setq emchat-buddy-buffer (get-buffer-create "*emchat buddy*"))
192     (set-buffer emchat-buddy-buffer)
193     (erase-buffer)
194     (loop for alias in (symbol-value emchat-buddy-view)
195       as status = (emchat-world-getf alias 'status)
196       as face = (emchat-status-face status)
197       do (insert-face (concat alias "\n") face)
198       do (when emchat-buddy-show-xface (emchat-buddy-show-xface alias))
199       do (emchat-buddy-update-face alias))
200     (emchat-buddy-mode))
201   (unless no-select
202     (switch-to-buffer emchat-buddy-buffer)))
203
204 (defun emchat-buddy-view-all ()
205   "Display all aliases in `emchat-world'.
206 See `emchat-buddy-view'."
207   (interactive)
208   (emchat-buddy-view-set 'emchat-buddy-view 'emchat-all-aliases))
209
210 (defun emchat-buddy-view-connected ()
211   "Display all connected aliases.
212 See `emchat-buddy-view' and `emchat-connected-aliases'."
213   (interactive)
214   (emchat-buddy-view-set 'emchat-buddy-view 'emchat-connected-aliases))
215
216 (defun emchat-buddy-view-active ()
217   "Display all active aliases.
218 See `emchat-buddy-view' and `emchat-active-aliases'."
219   (interactive)
220   (emchat-buddy-view-set 'emchat-buddy-view 'emchat-active-aliases))
221
222 (eval-when-compile (defvar emchat-history-directory))
223
224 (defun emchat-buddy-show-xface-in-balloon (alias)
225   "Display an XFace image in the balloon-help buffer."
226   (unless (featurep '(and xface bbdb-autoloads))
227     (error 'unimplemented "X-Face and/or BBDB"))
228   (save-excursion
229     (let ((ext (or (extent-at (point))
230                    (make-extent (point-min) (point-min))))
231           (all-records (bbdb-records))
232           face cface nick record)
233       (while all-records
234         (setq record (car all-records)
235               nick (bbdb-record-getprop record 'icqnick)
236               face (bbdb-record-getprop record 'face)
237               cface (bbdb-record-getprop record 'cface))
238         (when (and (equal nick alias)
239                    (or face cface))