2000-12-26 Yuuichi Teranishi <teranisi@gohome.org>
[elisp/wanderlust.git] / utils / bbdb-wl.el
1 ;;; bbdb-wl.el -- BBDB interface to Wanderlust
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, news, database
7
8 ;;; Commentary:
9 ;;
10 ;;  Insert the following lines in your ~/.wl
11 ;; 
12 ;;  (require 'bbdb-wl)
13 ;;  (bbdb-wl-setup)
14
15 ;;; Code:
16 ;;
17
18 ;; bbdb setup.
19 (eval-when-compile
20   (require 'mime-setup)
21   (require 'elmo-vars)
22   (require 'elmo-util)
23   (require 'bbdb)
24   (require 'wl-summary)
25   (require 'wl-message)
26   (require 'wl-draft)
27   (require 'wl-address)
28   (defvar bbdb-pop-up-elided-display nil)
29   (or (fboundp 'bbdb-extract-field-value-internal)
30       (defun bbdb-extract-field-value-internal (field))))
31
32 (defvar bbdb-wl-get-update-record-hook nil)
33
34 (defun bbdb-wl-setup ()
35   (require 'bbdb)
36   (add-hook 'wl-message-redisplay-hook 'bbdb-wl-get-update-record)
37   (add-hook 'wl-summary-exit-hook 'bbdb-wl-hide-bbdb-buffer)
38   (add-hook 'wl-message-window-deleted-hook 'bbdb-wl-hide-bbdb-buffer)
39   (add-hook 'wl-exit-hook 'bbdb-wl-exit)
40   (add-hook 'wl-summary-toggle-disp-off-hook 'bbdb-wl-hide-bbdb-buffer)
41   (add-hook 'wl-summary-toggle-disp-folder-on-hook 'bbdb-wl-hide-bbdb-buffer)
42   (add-hook 'wl-summary-toggle-disp-folder-off-hook 'bbdb-wl-hide-bbdb-buffer)
43   (add-hook 'wl-summary-toggle-disp-folder-message-resumed-hook
44             'bbdb-wl-show-bbdb-buffer)
45   (add-hook 'wl-summary-mode-hook
46             (function
47              (lambda ()
48                (define-key (current-local-map) ":" 'bbdb-wl-show-sender)
49                (define-key (current-local-map) ";" 'bbdb-wl-edit-notes))))
50   (add-hook 'wl-summary-exit-hook 'bbdb-flush-all-caches)
51   (add-hook 'wl-summary-exec-hook 'bbdb-flush-all-caches)
52   (add-hook 'wl-mail-setup-hook
53             (function
54              (lambda ()
55 ;;;            (local-set-key "\M-\t" 'bbdb-complete-name)
56                (define-key (current-local-map) "\M-\t" 'bbdb-complete-name)
57                ))))
58
59 (defun bbdb-wl-exit ()
60   (let (bbdb-buf)
61     (if (setq bbdb-buf (get-buffer bbdb-buffer-name))
62         (kill-buffer bbdb-buf)))
63   (bbdb-save-db t))
64
65 (defun bbdb-wl-get-update-record ()
66   (set-buffer (wl-message-get-original-buffer))
67   (bbdb-wl-update-record)
68   (run-hooks 'bbdb-wl-get-update-record-hook))
69
70 (defun bbdb-wl-hide-bbdb-buffer ()
71   (let (bbdb-buf bbdb-win)
72     (if (setq bbdb-buf (get-buffer bbdb-buffer-name))
73         (if (setq bbdb-win (get-buffer-window bbdb-buf))
74             (delete-window bbdb-win)))))
75
76 (defun bbdb-wl-show-bbdb-buffer ()
77   (save-selected-window
78     (if (get-buffer-window bbdb-buffer-name)
79         nil
80       (let ((mes-win (get-buffer-window
81                       (save-excursion
82                         (if (buffer-live-p  wl-current-summary-buffer)
83                             (set-buffer wl-current-summary-buffer))
84                         wl-message-buf-name)))
85             (cur-win (selected-window))
86             (b (current-buffer)))
87         (and mes-win (select-window mes-win))
88         (let ((size (min
89                      (- (window-height mes-win)
90                         window-min-height 1)
91                      (- (window-height mes-win)
92                         (max window-min-height
93                              (1+ bbdb-pop-up-target-lines))))))
94           (split-window mes-win (if (> size 0) size window-min-height)))
95         ;; goto the bottom of the two...
96         (select-window (next-window))
97         ;; make it display *BBDB*...
98         (let ((pop-up-windows nil))
99           (switch-to-buffer (get-buffer-create bbdb-buffer-name)))))))
100
101 (defun bbdb-wl-from-func (string)
102   "A candidate From field STRING.  For `wl-summary-from-func'."
103   (let ((hit (bbdb-search-simple nil (wl-address-header-extract-address
104                                       string)))
105         first-name last-name from-str)
106     (if hit
107         (progn
108           (setq first-name (aref hit 0))
109           (setq last-name (aref hit 1))
110           (cond ((and (null first-name)
111                       (null last-name))
112                  (setq from-str string))
113                 ((and first-name last-name)
114                  (setq from-str (concat first-name " " last-name)))
115                 ((or first-name last-name)
116                  (setq from-str (or first-name last-name))))
117           from-str)
118       string)))
119
120 (defun bbdb-wl-update-record (&optional offer-to-create)
121   "Returns the record corresponding to the current WL message,
122 creating or modifying it as necessary.  A record will be created if
123 bbdb/mail-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
124 the user confirms the creation."
125   (save-excursion
126     (if bbdb-use-pop-up
127         (bbdb-wl-pop-up-bbdb-buffer offer-to-create)
128       (let ((key
129              (save-excursion
130                (set-buffer
131                 (save-excursion
132                   (if (buffer-live-p wl-current-summary-buffer)
133                       (set-buffer wl-current-summary-buffer))
134                   wl-message-buf-name))
135                (intern (format
136                         "%s-%d"
137                         wl-current-summary-buffer
138                         wl-message-buffer-cur-number)))))
139         (or (bbdb-message-cache-lookup key nil)
140             (and key
141                  (let* ((from (or (std11-field-body "From") ""))
142                         (addr (and from
143                                    (nth 1 (std11-extract-address-components
144                                            from)))))
145                    (if (or (null from)
146                            (null addr)
147                            (string-match (bbdb-user-mail-names) addr))
148                        (setq from (or (std11-field-body "To") from)))
149                    (with-temp-buffer ; to keep raw buffer unibyte.
150                      (elmo-set-buffer-multibyte
151                       default-enable-multibyte-characters)
152                      (setq from (eword-decode-string
153                                  (decode-mime-charset-string
154                                   from
155                                   wl-mime-charset))))
156                    (if from
157                        (bbdb-encache-message
158                         key
159                         (bbdb-annotate-message-sender
160                          from t
161                          (or (bbdb-invoke-hook-for-value
162                               bbdb/mail-auto-create-p)
163                              offer-to-create)
164                          offer-to-create))))))))))
165
166 (defun bbdb-wl-annotate-sender (string)
167   "Add a line to the end of the Notes field of the BBDB record
168 corresponding to the sender of this message."
169   (interactive (list (if bbdb-readonly-p
170                          (error "The Insidious Big Brother Database is read-only")
171                        (read-string "Comments: "))))
172   (set-buffer (wl-message-get-original-buffer))
173   (bbdb-annotate-notes (bbdb-wl-update-record t) string))
174
175 (defun bbdb-wl-edit-notes (&optional arg)
176   "Edit the notes field or (with a prefix arg) a user-defined field
177 of the BBDB record corresponding to the sender of this message."
178   (interactive "P")
179   (wl-summary-redisplay)
180   (set-buffer (wl-message-get-original-buffer))
181   (let ((record (or (bbdb-wl-update-record t) (error ""))))
182     (bbdb-display-records (list record))
183     (if arg
184         (bbdb-record-edit-property record nil t)
185       (bbdb-record-edit-notes record t))))
186
187 (defun bbdb-wl-show-sender ()
188   "Display the contents of the BBDB for the sender of this message.
189 This buffer will be in `bbdb-mode', with associated keybindings."
190   (interactive)
191   (wl-summary-redisplay)
192   (set-buffer (wl-message-get-original-buffer))
193   (let ((record (bbdb-wl-update-record t))
194         bbdb-win)
195     (if record
196         (progn
197           (bbdb-wl-pop-up-bbdb-buffer)
198           (bbdb-display-records (list record)))
199       (error "Unperson"))
200     (setq bbdb-win (get-buffer-window (get-buffer bbdb-buffer-name)))
201     (and bbdb-win
202          (select-window bbdb-win))))
203
204
205 (defun bbdb-wl-pop-up-bbdb-buffer (&optional offer-to-create)
206   "Make the *BBDB* buffer be displayed along with the WL window(s),
207 displaying the record corresponding to the sender of the current message."
208   (if (get-buffer-window bbdb-buffer-name)
209       nil
210     (let ((mes-win (get-buffer-window
211                     (save-excursion
212                       (if (buffer-live-p  wl-current-summary-buffer)
213                           (set-buffer wl-current-summary-buffer))
214                       wl-message-buf-name)))
215           (cur-win (selected-window))
216           (b (current-buffer)))
217       (and mes-win
218            (select-window mes-win))
219       (let ((size (min
220                    (- (window-height mes-win)
221                       window-min-height 1)
222                    (- (window-height mes-win)
223                       (max window-min-height
224                            (1+ bbdb-pop-up-target-lines))))))
225         (split-window mes-win (if (> size 0) size window-min-height)))
226       ;; goto the bottom of the two...
227       (select-window (next-window))
228       ;; make it display *BBDB*...
229       (let ((pop-up-windows nil))
230         (switch-to-buffer (get-buffer-create bbdb-buffer-name)))
231       ;; select the original window we were in...
232       (select-window cur-win)
233       ;; and make sure the current buffer is correct as well.
234       (set-buffer b)))
235   (let ((bbdb-gag-messages t)
236         (bbdb-use-pop-up nil)
237         (bbdb-electric-p nil))
238     (let ((record (bbdb-wl-update-record offer-to-create))
239           (bbdb-elided-display (bbdb-pop-up-elided-display))
240           (b (current-buffer)))
241       (bbdb-display-records (if record (list record) nil))
242       (set-buffer b)
243       record)))
244
245 (defun bbdb-wl-send-mail-internal (&optional to subj records)
246   (unwind-protect
247       (wl-draft (wl-address-header-extract-address to) "" (or subj ""))
248     (condition-case nil (delete-other-windows) (error))))
249
250 ;;; @ bbdb-extract-field-value -- stolen from tm-bbdb.
251 ;;;
252 (and (not (fboundp 'bbdb-extract-field-value-internal))
253 ;;;  (not (fboundp 'PLEASE_REPLACE_WITH_SEMI-BASED_MIME-BBDB)) ;; mime-bbdb
254     (progn
255 ;;;   (require 'bbdb-hooks) ; not provided.
256 ;;;   (or (fboundp 'bbdb-extract-field-value) ; defined as autoload
257       (or (fboundp 'bbdb-header-start)
258           (load "bbdb-hooks"))
259       (fset 'bbdb-extract-field-value-internal
260             (cond
261              ((fboundp 'tm:bbdb-extract-field-value)
262               (symbol-function 'tm:bbdb-extract-field-value))
263              (t (symbol-function 'bbdb-extract-field-value))))
264       (defun bbdb-extract-field-value (field)
265         (let ((value (bbdb-extract-field-value-internal field)))
266           (with-temp-buffer ; to keep raw buffer unibyte.
267             (elmo-set-buffer-multibyte
268              default-enable-multibyte-characters)
269             (and value
270                  (eword-decode-string value)))))
271       ))
272
273
274 (provide 'bbdb-wl)
275
276 ;;; bbdb-wl.el ends here