* Synch up with wl-2_4 and merged following changes.
[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-wl-extract-field-value-internal)
30 ;;      (defun bbdb-wl-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-get-petname (from)
102   "For `wl-summary-get-petname-func'."
103   (let* ((address (wl-address-header-extract-address from))
104          (record (bbdb-search-simple nil address)))
105     (and record
106          (or (bbdb-record-name record)
107              (car (bbdb-record-name record))))))
108
109 (defun bbdb-wl-from-func (string)
110   "A candidate From field STRING.  For `wl-summary-from-func'."
111   (let ((hit (bbdb-search-simple nil (wl-address-header-extract-address
112                                       string)))
113         first-name last-name from-str)
114     (if hit
115         (progn
116           (setq first-name (aref hit 0))
117           (setq last-name (aref hit 1))
118           (cond ((and (null first-name)
119                       (null last-name))
120                  (setq from-str string))
121                 ((and first-name last-name)
122                  (setq from-str (concat first-name " " last-name)))
123                 ((or first-name last-name)
124                  (setq from-str (or first-name last-name))))
125           from-str)
126       string)))
127
128 (defun bbdb-wl-update-record (&optional offer-to-create)
129   "Returns the record corresponding to the current WL message,
130 creating or modifying it as necessary.  A record will be created if
131 bbdb/mail-auto-create-p is non-nil, or if OFFER-TO-CREATE is true and
132 the user confirms the creation."
133   (save-excursion
134     (if bbdb-use-pop-up
135         (bbdb-wl-pop-up-bbdb-buffer offer-to-create)
136       (let ((key
137              (save-excursion
138                (set-buffer
139                 (save-excursion
140                   (if (buffer-live-p wl-current-summary-buffer)
141                       (set-buffer wl-current-summary-buffer))
142                   wl-message-buf-name))
143                (intern (format
144                         "%s-%d"
145                         wl-current-summary-buffer
146                         wl-message-buffer-cur-number)))))
147         (or (bbdb-message-cache-lookup key nil)
148             (and key
149                  (let* ((from (or (std11-field-body "From") ""))
150                         (addr (and from
151                                    (nth 1 (std11-extract-address-components
152                                            from)))))
153                    (if (or (null from)
154                            (null addr)
155                            (string-match (bbdb-user-mail-names) addr))
156                        (setq from (or (std11-field-body "To") from)))
157                    (with-temp-buffer ; to keep raw buffer unibyte.
158                      (elmo-set-buffer-multibyte
159                       default-enable-multibyte-characters)
160                      (setq from (eword-decode-string
161                                  (decode-mime-charset-string
162                                   from
163                                   wl-mime-charset))))
164                    (if from
165                        (bbdb-encache-message
166                         key
167                         (bbdb-annotate-message-sender
168                          from t
169                          (or (bbdb-invoke-hook-for-value
170                               bbdb/mail-auto-create-p)
171                              offer-to-create)
172                          offer-to-create))))))))))
173
174 (defun bbdb-wl-annotate-sender (string)
175   "Add a line to the end of the Notes field of the BBDB record
176 corresponding to the sender of this message."
177   (interactive (list (if bbdb-readonly-p
178                          (error "The Insidious Big Brother Database is read-only")
179                        (read-string "Comments: "))))
180   (set-buffer (wl-message-get-original-buffer))
181   (bbdb-annotate-notes (bbdb-wl-update-record t) string))
182
183 (defun bbdb-wl-edit-notes (&optional arg)
184   "Edit the notes field or (with a prefix arg) a user-defined field
185 of the BBDB record corresponding to the sender of this message."
186   (interactive "P")
187   (wl-summary-redisplay)
188   (set-buffer (wl-message-get-original-buffer))
189   (let ((record (or (bbdb-wl-update-record t) (error ""))))
190     (bbdb-display-records (list record))
191     (if arg
192         (bbdb-record-edit-property record nil t)
193       (bbdb-record-edit-notes record t))))
194
195 (defun bbdb-wl-show-sender ()
196   "Display the contents of the BBDB for the sender of this message.
197 This buffer will be in `bbdb-mode', with associated keybindings."
198   (interactive)
199   (wl-summary-redisplay)
200   (set-buffer (wl-message-get-original-buffer))
201   (let ((record (bbdb-wl-update-record t))
202         bbdb-win)
203     (if record
204         (progn
205           (bbdb-wl-pop-up-bbdb-buffer)
206           (bbdb-display-records (list record)))
207       (error "Unperson"))
208     (setq bbdb-win (get-buffer-window (get-buffer bbdb-buffer-name)))
209     (and bbdb-win
210          (select-window bbdb-win))))
211
212
213 (defun bbdb-wl-pop-up-bbdb-buffer (&optional offer-to-create)
214   "Make the *BBDB* buffer be displayed along with the WL window(s),
215 displaying the record corresponding to the sender of the current message."
216   (if (get-buffer-window bbdb-buffer-name)
217       nil
218     (let ((mes-win (get-buffer-window
219                     (save-excursion
220                       (if (buffer-live-p  wl-current-summary-buffer)
221                           (set-buffer wl-current-summary-buffer))
222                       wl-message-buf-name)))
223           (cur-win (selected-window))
224           (b (current-buffer)))
225       (and mes-win
226            (select-window mes-win))
227       (let ((size (min
228                    (- (window-height mes-win)
229                       window-min-height 1)
230                    (- (window-height mes-win)
231                       (max window-min-height
232                            (1+ bbdb-pop-up-target-lines))))))
233         (split-window mes-win (if (> size 0) size window-min-height)))
234       ;; goto the bottom of the two...
235       (select-window (next-window))
236       ;; make it display *BBDB*...
237       (let ((pop-up-windows nil))
238         (switch-to-buffer (get-buffer-create bbdb-buffer-name)))
239       ;; select the original window we were in...
240       (select-window cur-win)
241       ;; and make sure the current buffer is correct as well.
242       (set-buffer b)))
243   (let ((bbdb-gag-messages t)
244         (bbdb-use-pop-up nil)
245         (bbdb-electric-p nil))
246     (let ((record (bbdb-wl-update-record offer-to-create))
247           (bbdb-elided-display (bbdb-pop-up-elided-display))
248           (b (current-buffer)))
249       (bbdb-display-records (if record (list record) nil))
250       (set-buffer b)
251       record)))
252
253 (defun bbdb-wl-send-mail-internal (&optional to subj records)
254   (unwind-protect
255       (wl-draft (wl-address-header-extract-address to) "" (or subj ""))
256     (condition-case nil (delete-other-windows) (error))))
257
258 ;;; @ bbdb-extract-field-value -- stolen from tm-bbdb.
259 ;;;
260 (and (not (fboundp 'bbdb-wl-extract-field-value-internal))
261 ;;;  (not (fboundp 'PLEASE_REPLACE_WITH_SEMI-BASED_MIME-BBDB)) ;; mime-bbdb
262     (progn
263       (if (and (string< bbdb-version "1.58")
264                ;; (not (fboundp 'bbdb-extract-field-value) ; defined as autoload
265                (not (fboundp 'bbdb-header-start)))
266           (load "bbdb-hooks")
267         (require 'bbdb-hooks))
268       (fset 'bbdb-wl-extract-field-value-internal
269             (cond
270              ((fboundp 'tm:bbdb-extract-field-value)
271               (symbol-function 'tm:bbdb-extract-field-value))
272              (t (symbol-function 'bbdb-extract-field-value))))
273       (defun bbdb-extract-field-value (field)
274         (let ((value (bbdb-wl-extract-field-value-internal field)))
275           (with-temp-buffer ; to keep raw buffer unibyte.
276             (elmo-set-buffer-multibyte
277              default-enable-multibyte-characters)
278             (and value
279                  (eword-decode-string value)))))
280       ))
281
282
283 (provide 'bbdb-wl)
284
285 ;;; bbdb-wl.el ends here