diff lisp/gnus/gnus-picon.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/gnus/gnus-picon.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,362 @@
+;;; gnus-picon.el --- displaying pretty icons in Gnus
+;; Copyright (C) 1996 Free Software Foundation, Inc.
+
+;; Author: Wes Hardaker <hardaker@ece.ucdavis.edu>
+;; Keywords: news xpm annotation glyph faces
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Usage:
+;;     - You must have XEmacs (19.12 or above I think) to use this.
+;;     - Read the variable descriptions below.
+;;
+;;     - chose a setup:
+;;
+;;       1) display the icons in its own buffer:
+;;
+;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
+;;          (add-hook 'gnus-summary-prepare-hook 'gnus-group-display-picons t)
+;;          (setq gnus-picons-display-where 'picons)
+;;
+;;          Then add the picons buffer to your display configuration:
+;;          The picons buffer needs to be at least 48 pixels high,
+;;          which for me is 5 lines:
+;;
+;;          (gnus-add-configuration
+;;           '(article (vertical 1.0 
+;;                             (group 6)
+;;                             (picons 5)
+;;                             (summary .25 point)
+;;                             (article 1.0))))
+;;
+;;          (gnus-add-configuration
+;;           '(summary (vertical 1.0 (group 6)
+;;                      (picons 5)
+;;                      (summary 1.0 point))))
+;;
+;;       2) display the icons in the summary buffer
+;;
+;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
+;;          (add-hook 'gnus-summary-prepare-hook 'gnus-group-display-picons t)
+;;          (setq gnus-picons-display-where 'summary)
+;;
+;;       3) display the icons in the article buffer
+;;
+;;          (add-hook 'gnus-article-display-hook 'gnus-article-display-picons t)
+;;          (add-hook 'gnus-article-prepare-hook 'gnus-group-display-picons t)
+;;          (setq gnus-picons-display-where 'article)
+;;
+;;
+;; Warnings:
+;;     - I'm not even close to being a lisp expert.
+;;     - The 't' (append) flag MUST be in the add-hook line
+;;
+;; TODO:
+;;     - Remove the TODO section in the headers.
+;;
+
+;;; Code:
+
+(require 'xpm)
+(require 'annotations)
+(eval-when-compile (require 'cl))
+
+(defvar gnus-picons-buffer "*Icon Buffer*"
+  "Buffer name to display the icons in if gnus-picons-display-where is 'picons.")
+
+(defvar gnus-picons-display-where 'picons
+  "Where to display the group and article icons.")
+
+(defvar gnus-picons-database "/usr/local/faces"
+  "Defines the location of the faces database.  
+For information on obtaining this database of pretty pictures, please
+see http://www.cs.indiana.edu/picons/ftp/index.html" )
+
+(defvar gnus-picons-news-directory "news"
+  "Sub-directory of the faces database containing the icons for newsgroups."
+)
+
+(defvar gnus-picons-user-directories '("local" "users" "usenix" "misc/MISC")
+  "List of directories to search for user faces."
+)
+
+(defvar gnus-picons-domain-directories '("domains")
+  "List of directories to search for domain faces.  
+Some people may want to add \"unknown\" to this list."
+)
+
+(defvar gnus-picons-x-face-file-name 
+  (format "/tmp/picon-xface.%s.xbm" (user-login-name))
+  "The name of the file in which to store the converted X-face header.")
+
+(defvar gnus-picons-convert-x-face (format "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | pbmtoxbm > %s" gnus-picons-x-face-file-name)
+  "Command to convert the x-face header into a xbm file."
+)
+
+(defvar gnus-picons-file-suffixes
+  (when (featurep 'x)
+    (let ((types (list "xbm")))
+      (when (featurep 'gif)
+	(push "gif" types))
+      (when (featurep 'xpm)
+	(push "xpm" types))
+      types))
+  "List of suffixes on picon file names to try.")
+
+(defvar gnus-picons-display-article-move-p t
+  "*Whether to move point to first empty line when displaying picons.
+This has only an effect if `gnus-picons-display-where' hs value article.")
+
+;;; Internal variables.
+       
+(defvar gnus-group-annotations nil)
+(defvar gnus-article-annotations nil)
+(defvar gnus-x-face-annotations nil)
+
+(defun gnus-picons-remove (plist)
+  (let ((listitem (car plist)))
+    (while (setq listitem (car plist))
+      (if (annotationp listitem)
+          (delete-annotation listitem))
+      (setq plist (cdr plist))))
+  )
+
+(defun gnus-picons-remove-all ()
+  "Removes all picons from the Gnus display(s)."
+  (interactive)
+  (gnus-picons-remove gnus-article-annotations)
+  (gnus-picons-remove gnus-group-annotations)
+  (gnus-picons-remove gnus-x-face-annotations)
+  (setq gnus-article-annotations nil
+        gnus-group-annotations nil
+	gnus-x-face-annotations nil)
+  (if (bufferp gnus-picons-buffer)
+      (kill-buffer gnus-picons-buffer))
+  )
+
+(defun gnus-get-buffer-name (variable)
+  "Returns the buffer name associated with the contents of a variable."
+  (cond ((symbolp variable)
+         (let ((newvar (cdr (assq variable gnus-window-to-buffer))))
+           (cond ((symbolp newvar)
+                  (symbol-value newvar))
+                 ((stringp newvar) newvar))))
+        ((stringp variable)
+         variable)))
+
+(defun gnus-picons-article-display-x-face ()
+  "Display the x-face header bitmap in the 'gnus-picons-display-where buffer."
+  ;; delete any old ones.
+  (gnus-picons-remove gnus-x-face-annotations)
+  (setq gnus-x-face-annotations nil)
+  ;; display the new one.
+  (let ((gnus-article-x-face-command 'gnus-picons-display-x-face))
+    (gnus-article-display-x-face)))
+
+(defun gnus-picons-display-x-face (beg end)
+  "Function to display the x-face header in the picons window.
+To use:  (setq gnus-article-x-face-command 'gnus-picons-display-x-face)"
+  (interactive)
+  ;; convert the x-face header to a .xbm file
+  (let ((process-connection-type nil)
+	(process nil))
+    (process-kill-without-query
+     (setq process (start-process
+		    "gnus-x-face" nil shell-file-name shell-command-switch
+		    gnus-picons-convert-x-face)))
+    (process-send-region "gnus-x-face" beg end)
+    (process-send-eof "gnus-x-face")
+    ;; wait for it.
+    (while (not (equal (process-status process) 'exit))
+      (sleep-for .1)))
+  ;; display it
+  (save-excursion
+    (set-buffer (get-buffer-create (gnus-get-buffer-name 
+				    gnus-picons-display-where)))
+    (gnus-add-current-to-buffer-list)
+    (goto-char (point-min))
+    (let (buffer-read-only)
+      (unless (eolp)
+	(push (make-annotation "\n" (point) 'text)
+	      gnus-x-face-annotations))
+      ;; append the annotation to gnus-article-annotations for deletion.
+      (setq gnus-x-face-annotations 
+	    (append
+	     (gnus-picons-try-to-find-face gnus-picons-x-face-file-name t)
+	     gnus-x-face-annotations)))
+    ;; delete the tmp file
+    (delete-file gnus-picons-x-face-file-name)))
+
+(defun gnus-article-display-picons ()
+  "Display faces for an author and his/her domain in gnus-picons-display-where."
+  (interactive)
+  (let (from at-idx databases)
+    (when (and (featurep 'xpm) 
+	       (or (not (fboundp 'device-type)) (equal (device-type) 'x))
+	       (setq from (mail-fetch-field "from"))
+	       (setq from (downcase (cadr (mail-extract-address-components
+					   from)))
+		     at-idx (string-match "@" from)))
+      (save-excursion
+	(let ((username (substring from 0 at-idx))
+	      (addrs (nreverse
+		      (message-tokenize-header (substring from (1+ at-idx))
+					       "."))))
+	  (set-buffer (get-buffer-create
+		       (gnus-get-buffer-name gnus-picons-display-where)))
+	  (gnus-add-current-to-buffer-list)
+	  (goto-char (point-min))
+	  (if (and (eq gnus-picons-display-where 'article)
+		   gnus-picons-display-article-move-p)
+	      (when (search-forward "\n\n" nil t)
+		(forward-line -1))
+	    (unless (eolp)
+	      (push (make-annotation "\n" (point) 'text)
+		    gnus-article-annotations)))
+	    
+	  (gnus-picons-remove gnus-article-annotations)
+	  (setq gnus-article-annotations nil)
+
+	  (setq databases (append gnus-picons-user-directories
+				  gnus-picons-domain-directories))
+	  (while databases
+	    (setq gnus-article-annotations
+		  (nconc (gnus-picons-insert-face-if-exists
+			  (car databases)
+			  addrs
+			  "unknown")
+			 (gnus-picons-insert-face-if-exists
+			  (car databases)
+			  addrs
+			  (downcase username) t)
+			 gnus-article-annotations))
+	    (setq databases (cdr databases)))
+	  (add-hook 'gnus-summary-exit-hook 'gnus-picons-remove-all))))))
+
+(defun gnus-group-display-picons ()
+  "Display icons for the group in the gnus-picons-display-where buffer." 
+  (interactive)
+  (when (and (featurep 'xpm) 
+	     (or (not (fboundp 'device-type)) (equal (device-type) 'x)))
+    (save-excursion
+      (set-buffer (get-buffer-create
+		   (gnus-get-buffer-name gnus-picons-display-where)))
+      (gnus-add-current-to-buffer-list)
+      (goto-char (point-min))
+      (if (and (eq gnus-picons-display-where 'article)
+	       gnus-picons-display-article-move-p)
+	  (if (search-forward "\n\n" nil t)
+	      (forward-line -1))
+	(unless (eolp)
+	  (push (make-annotation "\n" (point) 'text)
+		gnus-group-annotations)))
+      (cond 
+       ((listp gnus-group-annotations)
+	(mapcar 'delete-annotation gnus-group-annotations)
+	(setq gnus-group-annotations nil))
+       ((annotationp gnus-group-annotations)
+	(delete-annotation gnus-group-annotations)
+	(setq gnus-group-annotations nil)))
+      (gnus-picons-remove gnus-group-annotations)
+      (setq gnus-group-annotations
+	    (gnus-picons-insert-face-if-exists
+	     gnus-picons-news-directory
+	     (message-tokenize-header gnus-newsgroup-name ".")
+	     "unknown"))
+      (add-hook 'gnus-summary-exit-hook 'gnus-picons-remove-all))))
+
+(defsubst gnus-picons-try-suffixes (file)
+  (let ((suffixes gnus-picons-file-suffixes)
+	f)
+    (while (and suffixes
+		(not (file-exists-p (setq f (concat file (pop suffixes))))))
+      (setq f nil))
+    f))
+
+(defun gnus-picons-insert-face-if-exists (database addrs filename &optional
+						   nobar-p)
+  "Inserts a face at point if I can find one"
+  ;; '(gnus-picons-insert-face-if-exists
+  ;     "Database" '("edu" "indiana" "cs") "Name")
+  ;; looks for:
+  ;;  1. edu/indiana/cs/Name 
+  ;;  2. edu/indiana/Name 
+  ;;  3. edu/Name
+  ;; '(gnus-picons-insert-face-if-exists
+  ;;     "Database/MISC" '("edu" "indiana" "cs") "Name")
+  ;; looks for:
+  ;;  1. MISC/Name
+  ;; The special treatment of MISC doesn't conform with the conventions for
+  ;; picon databases, but otherwise we would always see the MISC/unknown face.
+  (let ((bar (and (not nobar-p)
+		  (annotations-in-region 
+		   (point) (min (point-max) (1+ (point)))
+		   (current-buffer))))
+	(path (concat (file-name-as-directory gnus-picons-database)
+		      database "/"))
+	picons found bar-ann)
+    (if (string-match "/MISC" database)
+	(setq addrs '("")))
+    (while (and addrs
+		(file-accessible-directory-p path))
+      (setq path (concat path (pop addrs) "/"))
+      (when (setq found
+		  (gnus-picons-try-suffixes
+		   (concat path filename "/face.")))
+	(when bar
+	  (setq bar-ann (gnus-picons-try-to-find-face 
+			 (concat gnus-xmas-glyph-directory "bar.xbm")))
+	  (when bar-ann
+	    (setq picons (nconc picons bar-ann))
+	    (setq bar nil)))
+	(setq picons (nconc (gnus-picons-try-to-find-face found)
+			    picons))))
+    (nreverse picons)))
+
+(defvar gnus-picons-glyph-alist nil)
+      
+(defun gnus-picons-try-to-find-face (path &optional xface-p)
+  "If PATH exists, display it as a bitmap.  Returns t if succedded."
+  (let ((glyph (and (not xface-p)
+		    (cdr (assoc path gnus-picons-glyph-alist)))))
+    (when (or glyph (file-exists-p path))
+      (unless glyph
+	(setq glyph (make-glyph path))
+	(unless xface-p
+	  (push (cons path glyph) gnus-picons-glyph-alist))
+	(set-glyph-face glyph 'default))
+      (nconc
+       (list (make-annotation glyph (point) 'text))
+       (when (eq major-mode 'gnus-article-mode)
+	 (list (make-annotation " " (point) 'text)))))))
+
+(defun gnus-picons-reverse-domain-path (str)
+  "a/b/c/d -> d/c/b/a"
+  (mapconcat 'downcase (nreverse (message-tokenize-header str "/")) "/"))
+
+(gnus-add-shutdown 'gnus-picons-close 'gnus)
+
+(defun gnus-picons-close ()
+  "Shut down the picons."
+  (setq gnus-picons-glyph-alist nil))
+
+(provide 'gnus-picon)
+
+;;; gnus-picon.el ends here