* wl-mime.el (toplevel): Require wl-vars.
[elisp/wanderlust.git] / elmo / elmo-multi.el
1 ;;; elmo-multi.el --- Multiple Folder Interface for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14 ;;
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24 ;;
25
26 ;;; Commentary:
27 ;;
28
29 ;;; Code:
30 ;;
31 (eval-when-compile (require 'cl))
32
33 (require 'elmo)
34 (require 'luna)
35
36 (defvar elmo-multi-divide-number 100000
37   "*Multi divider number.")
38
39 ;;; ELMO Multi folder
40 (eval-and-compile
41   (luna-define-class elmo-multi-folder (elmo-folder)
42                      (children divide-number))
43   (luna-define-internal-accessors 'elmo-multi-folder))
44
45 (defmacro elmo-multi-real-folder-number (folder number)
46   "Returns a cons cell of real FOLDER and NUMBER."
47   (` (cons (nth (-
48                  (/ (, number)
49                     (elmo-multi-folder-divide-number-internal (, folder)))
50                  1) (elmo-multi-folder-children-internal (, folder)))
51            (% (, number) (elmo-multi-folder-divide-number-internal
52                           (, folder))))))
53
54 (luna-define-method elmo-folder-initialize ((folder
55                                              elmo-multi-folder)
56                                             name)
57   (while (> (length (car (setq name (elmo-parse-token name ",")))) 0)
58     (elmo-multi-folder-set-children-internal
59      folder
60      (nconc (elmo-multi-folder-children-internal
61              folder)
62             (list (elmo-make-folder (car name)))))
63     (setq name (cdr name))
64     (when (and (> (length name) 0)
65                (eq (aref name 0) ?,))
66       (setq name (substring name 1))))
67   (elmo-multi-folder-set-divide-number-internal
68    folder
69    elmo-multi-divide-number)
70   folder)
71
72 (luna-define-method elmo-folder-open-internal ((folder elmo-multi-folder))
73   (dolist (fld (elmo-multi-folder-children-internal folder))
74     (elmo-folder-open-internal fld)))
75
76 (luna-define-method elmo-folder-check ((folder elmo-multi-folder))
77   (dolist (fld (elmo-multi-folder-children-internal folder))
78     (elmo-folder-check fld)))
79
80 (luna-define-method elmo-folder-close-internal ((folder elmo-multi-folder))
81   (dolist (fld (elmo-multi-folder-children-internal folder))
82     (elmo-folder-close-internal fld)))
83
84 (luna-define-method elmo-folder-close :after ((folder elmo-multi-folder))
85   (dolist (fld (elmo-multi-folder-children-internal folder))
86     (elmo-folder-set-msgdb-internal fld nil)))
87
88 (luna-define-method elmo-folder-synchronize ((folder elmo-multi-folder)
89                                              &optional
90                                              disable-killed
91                                              ignore-msgdb
92                                              no-check
93                                              mask)
94   (if mask
95       (dolist (element (elmo-multi-split-numbers folder mask))
96         (when (cdr element)
97           (elmo-folder-synchronize (car element)
98                                    disable-killed
99                                    ignore-msgdb
100                                    no-check
101                                    (cdr element))))
102     (dolist (fld (elmo-multi-folder-children-internal folder))
103       (elmo-folder-synchronize fld disable-killed ignore-msgdb no-check)))
104   0)
105
106 (luna-define-method elmo-folder-expand-msgdb-path ((folder
107                                                     elmo-multi-folder))
108   (expand-file-name (elmo-replace-string-as-filename
109                      (elmo-folder-name-internal folder))
110                     (expand-file-name "multi"
111                                       elmo-msgdb-directory)))
112
113 (luna-define-method elmo-folder-newsgroups ((folder elmo-multi-folder))
114   (apply  #'nconc
115           (mapcar
116            'elmo-folder-newsgroups
117            (elmo-multi-folder-children-internal folder))))
118
119 (luna-define-method elmo-folder-get-primitive-list ((folder elmo-multi-folder))
120   (elmo-flatten
121    (mapcar
122     'elmo-folder-get-primitive-list
123     (elmo-multi-folder-children-internal folder))))
124
125 (luna-define-method elmo-folder-contains-type ((folder elmo-multi-folder) type)
126   (let ((children (elmo-multi-folder-children-internal folder))
127         match)
128     (while children
129       (when (elmo-folder-contains-type (car children) type)
130         (setq match t)
131         (setq children nil))
132       (setq children (cdr children)))
133     match))
134
135 (luna-define-method elmo-message-folder ((folder elmo-multi-folder)
136                                          number)
137   (nth (- (/ number (elmo-multi-folder-divide-number-internal folder)) 1)
138        (elmo-multi-folder-children-internal folder)))
139
140 (luna-define-method elmo-message-cached-p ((folder elmo-multi-folder) number)
141   (let ((pair (elmo-multi-real-folder-number folder number)))
142     (elmo-message-cached-p (car pair) (cdr pair))))
143
144 (luna-define-method elmo-message-set-cached ((folder elmo-multi-folder)
145                                              number cached)
146   (let ((pair (elmo-multi-real-folder-number folder number)))
147     (elmo-message-set-cached (car pair) (cdr pair) cached)))
148
149 (luna-define-method elmo-find-fetch-strategy ((folder elmo-multi-folder)
150                                               number
151                                               &optional
152                                               ignore-cache
153                                               require-entireness)
154   (let ((pair (elmo-multi-real-folder-number folder number)))
155     (elmo-find-fetch-strategy (car pair)
156                               (cdr pair)
157                               ignore-cache
158                               require-entireness)))
159
160 (luna-define-method elmo-message-number ((folder elmo-multi-folder)
161                                          message-id)
162   (let ((children (elmo-multi-folder-children-internal folder))
163         match)
164     (while children
165       (when (setq match (elmo-message-number (car children) message-id))
166         (setq children nil))
167       (setq children (cdr children)))
168     match))
169
170 (luna-define-method elmo-message-entity ((folder elmo-multi-folder) key)
171   (cond
172    ((numberp key)
173     (let* ((pair (elmo-multi-real-folder-number folder key))
174            (entity (elmo-message-entity (car pair) (cdr pair))))
175       (when entity
176         (elmo-message-entity-set-number (elmo-message-copy-entity entity)
177                                         key))))
178    ((stringp key)
179     (let ((children (elmo-multi-folder-children-internal folder))
180           (cur-number 0)
181           match)
182       (while children
183         (setq cur-number (+ cur-number 1))
184         (when (setq match (elmo-message-entity (car children) key))
185           (setq match (elmo-message-copy-entity match))
186           (elmo-message-entity-set-number
187            match
188            (+ (* (elmo-multi-folder-divide-number-internal folder)
189                  cur-number)
190               (elmo-message-entity-number match)))
191           (setq children nil))
192         (setq children (cdr children)))
193       match))))
194
195 (luna-define-method elmo-message-entity-parent ((folder
196                                                  elmo-multi-folder) entity)
197   (elmo-message-entity
198    folder
199    (elmo-message-entity-field entity 'references)))
200
201 (luna-define-method elmo-message-field ((folder elmo-multi-folder)
202                                         number field)
203   (let ((pair (elmo-multi-real-folder-number folder number)))
204     (elmo-message-field (car pair) (cdr pair) field)))
205
206 (luna-define-method elmo-message-flag-available-p ((folder
207                                                     elmo-multi-folder) number
208                                                     flag)
209   (let ((pair (elmo-multi-real-folder-number folder number)))
210     (elmo-message-flag-available-p (car pair) (cdr pair) flag)))
211
212 (luna-define-method elmo-message-flags ((folder elmo-multi-folder) number)
213   (let ((pair (elmo-multi-real-folder-number folder number)))
214     (elmo-message-flags (car pair) (cdr pair))))
215
216 (defun elmo-multi-split-numbers (folder numlist &optional as-is)
217   (let ((numbers (sort numlist '<))
218         (folders (elmo-multi-folder-children-internal folder))
219         (divider (elmo-multi-folder-divide-number-internal folder))
220         (cur-number 0)
221         one-list numbers-list)
222     (while numbers
223       (setq one-list (list (nth cur-number folders)))
224       (setq cur-number (+ cur-number 1))
225       (while (and numbers
226                   (eq 0
227                       (/ (- (car numbers)
228                             (* divider cur-number))
229                          divider)))
230         (setq one-list (nconc
231                         one-list
232                         (list
233                          (if as-is
234                              (car numbers)
235                            (% (car numbers)
236                               (* divider cur-number))))))
237         (setq numbers (cdr numbers)))
238       (setq numbers-list (nconc numbers-list (list one-list))))
239     numbers-list))
240
241 (luna-define-method elmo-folder-process-crosspost ((folder elmo-multi-folder))
242   (dolist (child (elmo-multi-folder-children-internal folder))
243     (elmo-folder-process-crosspost child)))
244
245 (luna-define-method elmo-message-fetch ((folder elmo-multi-folder)
246                                         number strategy
247                                         &optional unseen section)
248   (let ((pair (elmo-multi-real-folder-number folder number)))
249     (when (elmo-message-fetch (car pair) (cdr pair)
250                               strategy unseen section)
251       (unless unseen
252         (elmo-folder-notify-event folder 'flag-changed (list number)))
253       t)))
254
255 (luna-define-method elmo-folder-delete-messages ((folder elmo-multi-folder)
256                                                  numbers)
257   (dolist (element (elmo-multi-split-numbers folder numbers))
258     (when (cdr element)
259       (elmo-folder-delete-messages (car element) (cdr element))))
260   t)
261
262 (luna-define-method elmo-folder-detach-messages ((folder elmo-multi-folder)
263                                                  numbers)
264   (dolist (element (elmo-multi-split-numbers folder numbers))
265     (when (cdr element)
266       (elmo-folder-detach-messages (car element) (cdr element))))
267   t)
268
269 (luna-define-method elmo-folder-diff ((folder elmo-multi-folder))
270   (elmo-multi-folder-diff folder))
271
272 (defun elmo-multi-folder-diff (folder)
273   (let ((flds (elmo-multi-folder-children-internal folder))
274         (news 0)
275         (unreads 0)
276         (alls 0)
277         no-unreads diff)
278     (while flds
279       (setq diff (elmo-folder-diff (car flds)))
280       (cond
281        ((consp (cdr diff)) ; (new unread all)
282         (setq news    (+ news (nth 0 diff))
283               unreads (+ unreads (nth 1 diff))
284               alls    (+ alls (nth 2 diff))))
285        (t
286         (setq no-unreads t)
287         (setq news    (+ news (car diff))
288               alls    (+ alls (cdr diff)))))
289       (setq flds (cdr flds)))
290     (if no-unreads
291         (cons news alls)
292       (list news unreads alls))))
293
294 (luna-define-method elmo-folder-list-messages
295   ((folder elmo-multi-folder) &optional visible-only in-msgdb)
296   (let* ((flds (elmo-multi-folder-children-internal folder))
297          (cur-number 0)
298          list numbers)
299     (while flds
300       (setq cur-number (+ cur-number 1))
301       (setq list (elmo-folder-list-messages (car flds) visible-only in-msgdb))
302       (setq numbers
303             (nconc
304              numbers
305              (mapcar
306               (function
307                (lambda (x)
308                  (+
309                   (* (elmo-multi-folder-divide-number-internal
310                       folder) cur-number) x)))
311               list)))
312       (setq flds (cdr flds)))
313     numbers))
314
315 (luna-define-method elmo-folder-exists-p ((folder elmo-multi-folder))
316   (let ((flds (elmo-multi-folder-children-internal folder)))
317     (catch 'exists
318       (while flds
319         (unless (elmo-folder-exists-p (car flds))
320           (throw 'exists nil))
321         (setq flds (cdr flds)))
322       t)))
323
324 (luna-define-method elmo-folder-creatable-p ((folder elmo-multi-folder))
325   (let ((flds (elmo-multi-folder-children-internal folder)))
326     (catch 'creatable
327       (while flds
328         (when (and (elmo-folder-creatable-p (car flds))
329                    (not (elmo-folder-exists-p (car flds))))
330           ;; If folder already exists, don't to `creatable'.
331           ;; Because this function is called, when folder doesn't exists.
332           (throw 'creatable t))
333         (setq flds (cdr flds)))
334       nil)))
335
336 (luna-define-method elmo-folder-create ((folder elmo-multi-folder))
337   (let ((flds (elmo-multi-folder-children-internal folder)))
338     (catch 'create
339       (while flds
340         (unless (or (elmo-folder-exists-p (car flds))
341                     (elmo-folder-create (car flds)))
342           (throw 'create nil))
343         (setq flds (cdr flds)))
344       t)))
345
346 (luna-define-method elmo-folder-search ((folder elmo-multi-folder)
347                                         condition &optional numbers)
348   (let* ((flds (elmo-multi-folder-children-internal folder))
349          (cur-number 0)
350          numlist
351          matches)
352     (setq numbers (or numbers
353                       (elmo-folder-list-messages folder)))
354     (while flds
355       (setq cur-number (+ cur-number 1))
356       (setq matches (append matches
357                             (mapcar
358                              (function
359                               (lambda (x)
360                                 (+
361                                  (* (elmo-multi-folder-divide-number-internal
362                                      folder)
363                                     cur-number)
364                                  x)))
365                              (elmo-folder-search
366                               (car flds) condition))))
367       (setq flds (cdr flds)))
368     (elmo-list-filter numbers matches)))
369
370 (luna-define-method elmo-message-use-cache-p ((folder elmo-multi-folder)
371                                               number)
372   (let ((pair (elmo-multi-real-folder-number folder number)))
373     (elmo-message-use-cache-p (car pair) (cdr pair))))
374
375 (luna-define-method elmo-message-file-p ((folder elmo-multi-folder) number)
376   (let ((pair (elmo-multi-real-folder-number folder number)))
377     (elmo-message-file-p (car pair) (cdr pair))))
378
379 (luna-define-method elmo-message-file-name ((folder elmo-multi-folder) number)
380   (let ((pair (elmo-multi-real-folder-number folder number)))
381     (elmo-message-file-name (car pair) (cdr pair))))
382
383 (luna-define-method elmo-folder-plugged-p ((folder elmo-multi-folder))
384   (let ((flds (elmo-multi-folder-children-internal folder)))
385     (catch 'plugged
386       (while flds
387         (unless (elmo-folder-plugged-p (car flds))
388           (throw 'plugged nil))
389         (setq flds (cdr flds)))
390       t)))
391
392 (luna-define-method elmo-folder-set-plugged ((folder elmo-multi-folder)
393                                              plugged add)
394   (let ((flds  (elmo-multi-folder-children-internal folder)))
395     (dolist (fld flds)
396       (elmo-folder-set-plugged fld plugged add))))
397
398 (defun elmo-multi-folder-numbers-list-assoc (folder folder-numbers)
399   (let (ent)
400     (while folder-numbers
401       (when (string= (elmo-folder-name-internal (car (car folder-numbers)))
402                      (elmo-folder-name-internal folder))
403         (setq ent (car folder-numbers)
404               folder-numbers nil))
405       (setq folder-numbers (cdr folder-numbers)))
406     ent))
407
408 (defun elmo-multi-make-folder-numbers-list (folder msgs)
409   (let ((msg-list msgs)
410         pair fld-list
411         ret-val)
412     (while msg-list
413       (when (and (numberp (car msg-list))
414                  (> (car msg-list) 0))
415         (setq pair (elmo-multi-real-folder-number folder (car msg-list)))
416         (if (setq fld-list (elmo-multi-folder-numbers-list-assoc
417                             (car pair)
418                             ret-val))
419             (setcdr fld-list (cons (cdr pair) (cdr fld-list)))
420           (setq ret-val (cons (cons (car pair) (list (cdr pair))) ret-val))))
421       (setq msg-list (cdr msg-list)))
422     ret-val))
423
424 (luna-define-method elmo-folder-set-flag ((folder elmo-multi-folder)
425                                           numbers
426                                           flag
427                                           &optional is-local)
428   (dolist (pair (elmo-multi-make-folder-numbers-list folder numbers))
429     (elmo-folder-set-flag (car pair) (cdr pair) flag is-local))
430   (elmo-folder-notify-event folder 'flag-changed numbers))
431
432 (luna-define-method elmo-folder-unset-flag ((folder elmo-multi-folder)
433                                             numbers
434                                             flag
435                                             &optional is-local)
436   (dolist (pair (elmo-multi-make-folder-numbers-list folder numbers))
437     (ignore-errors
438      (elmo-folder-unset-flag (car pair) (cdr pair) flag is-local)))
439   (elmo-folder-notify-event folder 'flag-changed numbers))
440
441 (luna-define-method elmo-folder-list-flagged ((folder elmo-multi-folder)
442                                               flag
443                                               &optional in-msgdb)
444   (let ((cur-number 0)
445         numbers)
446     (dolist (child (elmo-multi-folder-children-internal folder))
447       (setq cur-number (+ cur-number 1)
448             numbers
449             (nconc
450              numbers
451              (mapcar
452               (function
453                (lambda (x)
454                  (+
455                   (* (elmo-multi-folder-divide-number-internal folder)
456                      cur-number) x)))
457               (elmo-folder-list-flagged child flag in-msgdb)))))
458     numbers))
459
460 (luna-define-method elmo-folder-commit ((folder elmo-multi-folder))
461   (dolist (child (elmo-multi-folder-children-internal folder))
462     (elmo-folder-commit child)))
463
464 (luna-define-method elmo-folder-length ((folder elmo-multi-folder))
465   (let ((sum 0))
466     (dolist (child (elmo-multi-folder-children-internal folder))
467       (setq sum (+ sum (elmo-folder-length child))))
468     sum))
469
470 (luna-define-method elmo-folder-count-flags ((folder elmo-multi-folder))
471   (let (flag-alist element)
472     (dolist (child (elmo-multi-folder-children-internal folder))
473       (dolist (pair (elmo-folder-count-flags child))
474         (if (setq element (assq (car pair) flag-alist))
475             (setcdr element (+ (cdr element) (cdr pair)))
476           (setq flag-alist (cons pair flag-alist)))))
477     flag-alist))
478
479 (require 'product)
480 (product-provide (provide 'elmo-multi) (require 'elmo-version))
481
482 ;;; elmo-multi.el ends here