*** empty log message ***
[gnus] / lisp / gnus-picon.el
1 ;;; gnus-picon.el --- displaying pretty icons in Gnus
2 ;; Copyright (C) 1996 Free Software Foundation, Inc.
3
4 ;; Author: Wes Hardaker <hardaker@ece.ucdavis.edu>
5 ;; Keywords: news xpm annotation glyph faces
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;; Usage:
27 ;;     - You must have XEmacs (19.12 or above I think) to use this.
28 ;;     - Read the variable descriptions below.
29 ;;
30 ;;     - chose a setup:
31 ;;
32 ;;       1) display the icons in its own buffer:
33 ;;
34 ;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
35 ;;          (add-hook 'gnus-summary-prepare-hook 'gnus-group-display-picons t)
36 ;;          (setq gnus-picons-display-where 'picons)
37 ;;
38 ;;          Then add the picons buffer to your display configuration:
39 ;;          The picons buffer needs to be at least 48 pixels high,
40 ;;          which for me is 5 lines:
41 ;;
42 ;;          (gnus-add-configuration
43 ;;           '(article (vertical 1.0 
44 ;;                             (group 6)
45 ;;                             (picons 5)
46 ;;                             (summary .25 point)
47 ;;                             (article 1.0))))
48 ;;
49 ;;          (gnus-add-configuration
50 ;;           '(summary (vertical 1.0 (group 6)
51 ;;                      (picons 5)
52 ;;                      (summary 1.0 point))))
53 ;;
54 ;;       2) display the icons in the summary buffer
55 ;;
56 ;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
57 ;;          (add-hook 'gnus-summary-prepare-hook 'gnus-group-display-picons t)
58 ;;          (setq gnus-picons-display-where 'summary)
59 ;;
60 ;;       3) display the icons in the article buffer
61 ;;
62 ;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
63 ;;          (add-hook 'gnus-article-prepare-hook 'gnus-group-display-picons t)
64 ;;          (setq gnus-picons-display-where 'article)
65 ;;
66 ;;
67 ;; Warnings:
68 ;;     - I'm not even close to being a lisp expert.
69 ;;     - The 't' (append) flag MUST be in the add-hook line
70 ;;
71 ;; TODO:
72 ;;     - Remove the TODO section in the headers.
73 ;;
74
75 ;;; Code:
76
77 (require 'xpm)
78 (require 'annotations)
79 (eval-when-compile (require 'cl))
80
81 (defvar gnus-picons-buffer "*Icon Buffer*"
82   "Buffer name to display the icons in if gnus-picons-display-where is 'picons.")
83
84 (defvar gnus-picons-display-where 'picons
85   "Where to display the group and article icons.")
86
87 (defvar gnus-picons-database "/usr/local/faces"
88   "Defines the location of the faces database.  
89 For information on obtaining this database of pretty pictures, please
90 see http://www.cs.indiana.edu/picons/ftp/index.html" )
91
92 (defvar gnus-picons-news-directory "news"
93   "Sub-directory of the faces database containing the icons for newsgroups."
94 )
95
96 (defvar gnus-picons-user-directories '("local" "users" "usenix" "misc/MISC")
97   "List of directories to search for user faces."
98 )
99
100 (defvar gnus-picons-domain-directories '("domains")
101   "List of directories to search for domain faces.  
102 Some people may want to add \"unknown\" to this list."
103 )
104
105 (defvar gnus-group-annotations nil)
106 (defvar gnus-article-annotations nil)
107 (defvar gnus-x-face-annotations nil)
108
109 (defun gnus-picons-remove (plist)
110   (let ((listitem (car plist)))
111     (while (setq listitem (car plist))
112       (if (annotationp listitem)
113           (delete-annotation listitem))
114       (setq plist (cdr plist))))
115 )
116
117 (defun gnus-picons-remove-all ()
118   "Removes all picons from the Gnus display(s)."
119   (interactive)
120   (gnus-picons-remove gnus-article-annotations)
121   (gnus-picons-remove gnus-group-annotations)
122   (gnus-picons-remove gnus-x-face-annotations)
123   (setq gnus-article-annotations nil
124         gnus-group-annotations nil
125         gnus-x-face-annotations nil)
126   (if (bufferp gnus-picons-buffer)
127       (kill-buffer gnus-picons-buffer))
128 )
129
130 (defun gnus-get-buffer-name (variable)
131   "Returns the buffer name associated with the contents of a variable."
132   (cond ((symbolp variable)
133          (let ((newvar (cdr (assq variable gnus-window-to-buffer))))
134            (cond ((symbolp newvar)
135                   (symbol-value newvar))
136                  ((stringp newvar) newvar))))
137         ((stringp variable)
138          variable)))
139
140 (defvar gnus-picons-x-face-file-name 
141   (format "/tmp/picon-xface.%s.xbm" (user-login-name))
142   "The name of the file in which to store the converted X-face header.")
143
144 (defvar gnus-picons-convert-x-face (format "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | pbmtoxbm > %s" gnus-picons-x-face-file-name)
145   "Command to convert the x-face header into a xbm file."
146 )
147        
148 (defun gnus-picons-article-display-x-face ()
149   "Display the x-face header bitmap in the 'gnus-picons-display-where buffer."
150   ;; delete any old ones.
151   (gnus-picons-remove gnus-x-face-annotations)
152   (setq gnus-x-face-annotations nil)
153   ;; display the new one.
154   (let ((gnus-article-x-face-command 'gnus-picons-display-x-face))
155     (gnus-article-display-x-face)))
156
157 (defun gnus-picons-display-x-face (beg end)
158   "Function to display the x-face header in the picons window.
159 To use:  (setq gnus-article-x-face-command 'gnus-picons-display-x-face)"
160   (interactive)
161   ;; convert the x-face header to a .xbm file
162   (let ((process-connection-type nil)
163       (process nil))
164     (process-kill-without-query
165      (setq process (start-process
166       "gnus-x-face" nil "sh" "-c" gnus-picons-convert-x-face)))
167     (process-send-region "gnus-x-face" beg end)
168     (process-send-eof "gnus-x-face")
169   ;; wait for it.
170     (while (not (equal (process-status process) 'exit))
171       (sleep-for .1)))
172   ;; display it
173   (save-excursion
174     (set-buffer (get-buffer-create (gnus-get-buffer-name 
175                                     gnus-picons-display-where)))
176     (gnus-add-current-to-buffer-list)
177     (beginning-of-buffer)
178     (let ((iconpoint (point)))
179       (if (not (looking-at "^$"))
180         (if buffer-read-only
181             (progn 
182               (toggle-read-only)
183               (open-line 1)
184               (toggle-read-only)
185               )
186           (open-line 1)))
187       (end-of-line)
188       ;; append the annotation to gnus-article-annotations for deletion.
189       (setq gnus-x-face-annotations 
190           (append
191            (gnus-picons-try-to-find-face
192             gnus-picons-x-face-file-name iconpoint)
193            gnus-x-face-annotations)))
194     ;; delete the tmp file
195     (delete-file gnus-picons-x-face-file-name)))
196
197 (defun gnus-article-display-picons ()
198 "Display faces for an author and his/her domain in gnus-picons-display-where."
199   (interactive)
200   (if (and (featurep 'xpm) 
201            (or (not (fboundp 'device-type)) (equal (device-type) 'x)))
202       (save-excursion
203         (let* ((iconpoint (point)) (from (mail-fetch-field "from"))
204           (username 
205            (progn
206              (string-match "\\([-_a-zA-Z0-9]+\\)@" from)
207              (match-string 1 from)))
208            (hostpath
209             (concat (gnus-picons-reverse-domain-path
210                      (replace-in-string
211                       (replace-in-string from ".*@\\([_a-zA-Z0-9-.]+\\).*" 
212                                          "\\1") 
213                       "\\." "/")) "/")))
214           (switch-to-buffer (gnus-get-buffer-name gnus-picons-display-where))
215           (gnus-add-current-to-buffer-list)
216           (beginning-of-buffer)
217           (setq iconpoint (point))
218           (if (not (looking-at "^$"))
219               (if buffer-read-only
220                   (progn 
221                     (toggle-read-only)
222                     (open-line 1)
223                     (toggle-read-only)
224                     )
225                 (open-line 1)))
226
227           (end-of-line)
228           (gnus-picons-remove gnus-article-annotations)
229           (setq gnus-article-annotations 'nil)
230           (if (equal username from)
231                 (setq username (progn
232                                  (string-match "<\\([_a-zA-Z0-9-.]+\\)>" from)
233                                  (match-string 1 from))))
234           (mapcar '(lambda (pathpart) 
235                      (setq gnus-article-annotations
236                            (append
237                                    (gnus-picons-insert-face-if-exists 
238                                     (concat 
239                                      (file-name-as-directory 
240                                       gnus-picons-database) pathpart)
241                                     (concat hostpath username) 
242                                     iconpoint)
243                                     gnus-article-annotations))) 
244                   gnus-picons-user-directories)
245           (mapcar '(lambda (pathpart) 
246                      (setq gnus-article-annotations 
247                            (append
248                                    (gnus-picons-insert-face-if-exists 
249                                     (concat (file-name-as-directory 
250                                              gnus-picons-database) pathpart)
251                                     (concat hostpath "unknown") 
252                                     iconpoint)
253                                     gnus-article-annotations))) 
254                            gnus-picons-domain-directories)
255           (add-hook 'gnus-summary-exit-hook 'gnus-picons-remove-all)
256           ))))
257
258 (defun gnus-group-display-picons ()
259   "Display icons for the group in the gnus-picons-display-where buffer." 
260   (interactive)
261   (if (and (featurep 'xpm) 
262            (or (not (fboundp 'device-type)) (equal (device-type) 'x)))
263       (save-excursion
264       (let
265           ((iconpoint (point)))
266         (switch-to-buffer (gnus-get-buffer-name gnus-picons-display-where))
267         (gnus-add-current-to-buffer-list)
268         (beginning-of-buffer)
269         (cond 
270          ((listp gnus-group-annotations)
271           (mapcar 'delete-annotation gnus-group-annotations)
272           (setq gnus-group-annotations nil))
273          ((annotationp gnus-group-annotations)
274           (delete-annotation gnus-group-annotations)
275           (setq gnus-group-annotations nil))
276          )
277         (setq iconpoint (point))
278         (if (not (looking-at "^$"))
279             (open-line 1))
280         (gnus-picons-remove gnus-group-annotations)
281         (setq gnus-group-annotations nil)
282         (setq gnus-group-annotations
283               (gnus-picons-insert-face-if-exists 
284                (concat (file-name-as-directory gnus-picons-database)  
285                        gnus-picons-news-directory)
286                (concat (replace-in-string gnus-newsgroup-name "\\." "/") 
287                        "/unknown")
288                iconpoint t))
289         (add-hook 'gnus-summary-exit-hook 'gnus-picons-remove-all)))))
290
291
292 (defun gnus-picons-insert-face-if-exists (path filename ipoint &optional rev)
293   "Inserts a face at point if I can find one"
294   (let ((pathfile (concat path "/" filename "/face"))
295         (newfilename 
296          (replace-in-string filename 
297                             "[_a-zA-Z0-9-]+/\\([_A-Za-z0-9-]+\\)$" "\\1"))
298         (annotations nil))
299     (if (and rev
300          (not (equal filename newfilename)))
301         (setq annotations (append
302               (gnus-picons-insert-face-if-exists path newfilename ipoint rev)
303                annotations)))
304     (if (eq (length annotations) (length (setq annotations (append
305           (gnus-picons-try-to-find-face (concat pathfile ".xpm") ipoint)
306            annotations))))
307         (setq annotations (append
308                              (gnus-picons-try-to-find-face 
309                               (concat pathfile ".xbm") ipoint)
310                               annotations)))
311     (if (and (not rev)
312          (not (equal filename newfilename)))
313         (setq annotations (append
314               (gnus-picons-insert-face-if-exists path newfilename ipoint rev)
315                annotations)))
316     annotations
317     )
318   )
319   
320 (defun gnus-picons-try-to-find-face (path ipoint)
321   "If PATH exists, display it as a bitmap.  Returns t if succedded."
322   (when (file-exists-p path)
323     (let ((gl (make-glyph path)))
324       (set-glyph-face gl 'default)
325       (list (make-annotation gl ipoint 'text)))))
326
327 (defun gnus-picons-reverse-domain-path (str)
328   "a/b/c/d -> d/c/b/a"
329   (if (equal (replace-in-string str "^[^/]*$" "") "")
330       str
331     (concat (replace-in-string str "^.*/\\([_a-zA-Z0-9-]+\\)$" "\\1") "/"
332             (gnus-picons-reverse-domain-path 
333              (replace-in-string str "^\\(.*\\)/[_a-zA-Z0-9-]+$" "\\1")))))
334
335 (provide 'gnus-picon)
336
337 ;;; gnus-picon.el ends here