* WL-ELS (ELMO-MODULES): Added elmo-signal.
[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 'elmo-signal)
35 (require 'luna)
36
37 (defvar elmo-multi-divide-number 100000
38   "*Multi divider number.")
39
40 ;;; ELMO Multi folder
41 (eval-and-compile
42   (luna-define-class elmo-multi-folder (elmo-folder)
43                      (children divide-number))
44   (luna-define-internal-accessors 'elmo-multi-folder))
45
46 (defmacro elmo-multi-real-folder-number (folder number)
47   "Returns a cons cell of real FOLDER and NUMBER."
48   (` (cons (nth (-
49                  (/ (, number)
50                     (elmo-multi-folder-divide-number-internal (, folder)))
51                  1) (elmo-multi-folder-children-internal (, folder)))
52            (% (, number) (elmo-multi-folder-divide-number-internal
53                           (, folder))))))
54
55 (luna-define-method elmo-folder-initialize ((folder
56                                              elmo-multi-folder)
57                                             name)
58   (while (> (length (car (setq name (elmo-parse-token name ",")))) 0)
59     (elmo-multi-folder-set-children-internal
60      folder
61      (nconc (elmo-multi-folder-children-internal
62              folder)
63             (list (elmo-make-folder (car name)))))
64     (setq name (cdr name))
65     (when (and (> (length name) 0)
66                (eq (aref name 0) ?,))
67       (setq name (substring name 1))))
68   (elmo-multi-folder-set-divide-number-internal
69    folder
70    elmo-multi-divide-number)
71   (elmo-multi-connect-signals folder)
72   folder)
73
74 (defun elmo-multi-connect-signals (folder)
75   (elmo-connect-signal
76    nil 'flag-changing folder
77    (elmo-define-signal-handler (folder child number old-flags new-flags)
78      (elmo-emit-signal 'flag-changing folder
79                        (car (elmo-multi-map-numbers folder child (list number)))
80                        old-flags new-flags))
81    (elmo-define-signal-filter (folder sender)
82      (memq sender (elmo-multi-folder-children-internal folder))))
83   (elmo-connect-signal
84    nil 'flag-changed folder
85    (elmo-define-signal-handler (folder child numbers)
86      (elmo-emit-signal 'flag-changed folder
87                        (elmo-multi-map-numbers folder child numbers)))
88    (elmo-define-signal-filter (folder sender)
89      (memq sender (elmo-multi-folder-children-internal folder))))
90   (elmo-connect-signal
91    nil 'cache-changed folder
92    (elmo-define-signal-handler (folder child number)
93      (elmo-emit-signal
94       'flag-changed folder
95       (car (elmo-multi-map-numbers folder child (list number)))))
96    (elmo-define-signal-filter (folder sender)
97      (memq sender (elmo-multi-folder-children-internal folder)))))
98
99 (defun elmo-multi-map-numbers (folder child numbers)
100   (let ((multi (catch 'found
101                  (let ((children (elmo-multi-folder-children-internal folder))
102                        (index 0))
103                    (while children
104                      (setq index (1+ index))
105                      (when (eq (car children) child)
106                        (throw 'found index))
107                      (setq children (cdr children)))))))
108     (when multi
109       (let ((offset (* (elmo-multi-folder-divide-number-internal folder)
110                        multi)))
111       (mapcar (lambda (number) (+ offset number))
112               numbers)))))
113
114
115 (luna-define-method elmo-folder-open-internal ((folder elmo-multi-folder))
116   (dolist (fld (elmo-multi-folder-children-internal folder))
117     (elmo-folder-open-internal fld)))
118
119 (luna-define-method elmo-folder-check ((folder elmo-multi-folder))
120   (dolist (fld (elmo-multi-folder-children-internal folder))
121     (elmo-folder-check fld)))
122
123 (luna-define-method elmo-folder-close-internal ((folder elmo-multi-folder))
124   (dolist (fld (elmo-multi-folder-children-internal folder))
125     (elmo-folder-close-internal fld)))
126
127 (luna-define-method elmo-folder-close :after ((folder elmo-multi-folder))
128   (dolist (fld (elmo-multi-folder-children-internal folder))
129     (elmo-folder-set-msgdb-internal fld nil)))
130
131 (luna-define-method elmo-folder-synchronize ((folder elmo-multi-folder)
132                                              &optional
133                                              disable-killed
134                                              ignore-msgdb
135                                              no-check
136                                              mask)
137   (if mask
138       (dolist (element (elmo-multi-split-numbers folder mask))
139         (when (cdr element)
140           (elmo-folder-synchronize (car element)
141                                    disable-killed
142                                    ignore-msgdb
143                                    no-check
144                                    (cdr element))))
145     (dolist (fld (elmo-multi-folder-children-internal folder))
146       (elmo-folder-synchronize fld disable-killed ignore-msgdb no-check)))
147   0)
148
149 (luna-define-method elmo-folder-expand-msgdb-path ((folder
150                                                     elmo-multi-folder))
151   (expand-file-name (elmo-replace-string-as-filename
152                      (elmo-folder-name-internal folder))
153                     (expand-file-name "multi"
154                                       elmo-msgdb-directory)))
155
156 (luna-define-method elmo-folder-newsgroups ((folder elmo-multi-folder))
157   (apply  #'nconc
158           (mapcar
159            'elmo-folder-newsgroups
160            (elmo-multi-folder-children-internal folder))))
161
162 (luna-define-method elmo-folder-get-primitive-list ((folder elmo-multi-folder))
163   (elmo-flatten
164    (mapcar
165     'elmo-folder-get-primitive-list
166     (elmo-multi-folder-children-internal folder))))
167
168 (luna-define-method elmo-folder-contains-type ((folder elmo-multi-folder) type)
169   (let ((children (elmo-multi-folder-children-internal folder))
170         match)
171     (while children
172       (when (elmo-folder-contains-type (car children) type)
173         (setq match t)
174         (setq children nil))
175       (setq children (cdr children)))
176     match))
177
178 (luna-define-method elmo-message-folder ((folder elmo-multi-folder)
179                                          number)
180   (nth (- (/ number (elmo-multi-folder-divide-number-internal folder)) 1)
181        (elmo-multi-folder-children-internal folder)))
182
183 (luna-define-method elmo-message-cached-p ((folder elmo-multi-folder) number)
184   (let ((pair (elmo-multi-real-folder-number folder number)))
185     (elmo-message-cached-p (car pair) (cdr pair))))
186
187 (luna-define-method elmo-message-set-cached ((folder elmo-multi-folder)
188                                              number cached)
189   (let ((pair (elmo-multi-real-folder-number folder number)))
190     (elmo-message-set-cached (car pair) (cdr pair) cached)))
191
192 (luna-define-method elmo-find-fetch-strategy ((folder elmo-multi-folder)
193                                               number
194                                               &optional
195                                               ignore-cache
196                                               require-entireness)
197   (let ((pair (elmo-multi-real-folder-number folder number)))
198     (elmo-find-fetch-strategy (car pair)
199                               (cdr pair)
200                               ignore-cache
201                               require-entireness)))
202
203 (luna-define-method elmo-message-number ((folder elmo-multi-folder)
204                                          message-id)
205   (let ((children (elmo-multi-folder-children-internal folder))
206         match)
207     (while children
208       (when (setq match (elmo-message-number (car children) message-id))
209         (setq children nil))
210       (setq children (cdr children)))
211     match))
212
213 (luna-define-method elmo-message-entity ((folder elmo-multi-folder) key)
214   (cond
215    ((numberp key)
216     (let* ((pair (elmo-multi-real-folder-number folder key))
217            (entity (elmo-message-entity (car pair) (cdr pair))))
218       (when entity
219         (elmo-message-entity-set-number (elmo-message-copy-entity entity)
220                                         key))))
221    ((stringp key)
222     (let ((children (elmo-multi-folder-children-internal folder))
223           (cur-number 0)
224           match)
225       (while children
226         (setq cur-number (+ cur-number 1))
227         (when (setq match (elmo-message-entity (car children) key))
228           (setq match (elmo-message-copy-entity match))
229           (elmo-message-entity-set-number
230            match
231            (+ (* (elmo-multi-folder-divide-number-internal folder)
232                  cur-number)
233               (elmo-message-entity-number match)))
234           (setq children nil))
235         (setq children (cdr children)))
236       match))))
237
238 (luna-define-method elmo-message-entity-parent ((folder
239                                                  elmo-multi-folder) entity)
240   (elmo-message-entity
241    folder
242    (elmo-message-entity-field entity 'references)))
243
244 (luna-define-method elmo-message-field ((folder elmo-multi-folder)
245                                         number field)
246   (let ((pair (elmo-multi-real-folder-number folder number)))
247     (elmo-message-field (car pair) (cdr pair) field)))
248
249 (luna-define-method elmo-message-flag-available-p ((folder
250                                                     elmo-multi-folder) number
251                                                     flag)
252   (let ((pair (elmo-multi-real-folder-number folder number)))
253     (elmo-message-flag-available-p (car pair) (cdr pair) flag)))
254
255 (luna-define-method elmo-message-flags ((folder elmo-multi-folder) number)
256   (let ((pair (elmo-multi-real-folder-number folder number)))
257     (elmo-message-flags (car pair) (cdr pair))))
258
259 (defun elmo-multi-split-numbers (folder numlist &optional as-is)
260   (let ((numbers (sort numlist '<))
261         (folders (elmo-multi-folder-children-internal folder))
262         (divider (elmo-multi-folder-divide-number-internal folder))
263         (cur-number 0)
264         one-list numbers-list)
265     (while numbers
266       (setq one-list (list (nth cur-number folders)))
267       (setq cur-number (+ cur-number 1))
268       (while (and numbers
269                   (eq 0
270                       (/ (- (car numbers)
271                             (* divider cur-number))
272                          divider)))
273         (setq one-list (nconc
274                         one-list
275                         (list
276                          (if as-is
277                              (car numbers)
278                            (% (car numbers)
279                               (* divider cur-number))))))
280         (setq numbers (cdr numbers)))
281       (setq numbers-list (nconc numbers-list (list one-list))))
282     numbers-list))
283
284 (luna-define-method elmo-folder-process-crosspost ((folder elmo-multi-folder))
285   (dolist (child (elmo-multi-folder-children-internal folder))
286     (elmo-folder-process-crosspost child)))
287
288 (luna-define-method elmo-message-fetch ((folder elmo-multi-folder)
289                                         number strategy
290                                         &optional unseen section)
291   (let ((pair (elmo-multi-real-folder-number folder number)))
292     (elmo-message-fetch (car pair) (cdr pair) strategy unseen section)))
293
294 (luna-define-method elmo-folder-delete-messages ((folder elmo-multi-folder)
295                                                  numbers)
296   (dolist (element (elmo-multi-split-numbers folder numbers))
297     (when (cdr element)
298       (elmo-folder-delete-messages (car element) (cdr element))))
299   t)
300
301 (luna-define-method elmo-folder-detach-messages ((folder elmo-multi-folder)
302                                                  numbers)
303   (dolist (element (elmo-multi-split-numbers folder numbers))
304     (when (cdr element)
305       (elmo-folder-detach-messages (car element) (cdr element))))
306   t)
307
308 (luna-define-method elmo-folder-diff ((folder elmo-multi-folder))
309   (elmo-multi-folder-diff folder))
310
311 (defun elmo-multi-folder-diff (folder)
312   (let ((news 0)
313         (unreads 0)
314         (alls 0)
315         diff value)
316     (dolist (child (elmo-multi-folder-children-internal folder))
317       (setq diff (elmo-folder-diff child))
318       (setq news    (and news
319                          (setq value (elmo-diff-new diff))
320                          (+ news value))
321             unreads (and unreads
322                          (setq value (elmo-diff-unread diff))
323                          (+ unreads value))
324             alls    (and alls
325                          (setq value (elmo-diff-all diff))
326                          (+ alls value))))
327     (if unreads
328         (list news unreads alls)
329       (cons news alls))))
330
331 (luna-define-method elmo-folder-list-messages
332   ((folder elmo-multi-folder) &optional visible-only in-msgdb)
333   (let* ((flds (elmo-multi-folder-children-internal folder))
334          (cur-number 0)
335          list numbers)
336     (while flds
337       (setq cur-number (+ cur-number 1))
338       (setq list (elmo-folder-list-messages (car flds) visible-only in-msgdb))
339       (setq numbers
340             (nconc
341              numbers
342              (mapcar
343               (function
344                (lambda (x)
345                  (+
346                   (* (elmo-multi-folder-divide-number-internal
347                       folder) cur-number) x)))
348               list)))
349       (setq flds (cdr flds)))
350     numbers))
351
352 (luna-define-method elmo-folder-exists-p ((folder elmo-multi-folder))
353   (let ((flds (elmo-multi-folder-children-internal folder)))
354     (catch 'exists
355       (while flds
356         (unless (elmo-folder-exists-p (car flds))
357           (throw 'exists nil))
358         (setq flds (cdr flds)))
359       t)))
360
361 (luna-define-method elmo-folder-creatable-p ((folder elmo-multi-folder))
362   (let ((flds (elmo-multi-folder-children-internal folder)))
363     (catch 'creatable
364       (while flds
365         (when (and (elmo-folder-creatable-p (car flds))
366                    (not (elmo-folder-exists-p (car flds))))
367           ;; If folder already exists, don't to `creatable'.
368           ;; Because this function is called, when folder doesn't exists.
369           (throw 'creatable t))
370         (setq flds (cdr flds)))
371       nil)))
372
373 (luna-define-method elmo-folder-create ((folder elmo-multi-folder))
374   (let ((flds (elmo-multi-folder-children-internal folder)))
375     (catch 'create
376       (while flds
377         (unless (or (elmo-folder-exists-p (car flds))
378                     (elmo-folder-create (car flds)))
379           (throw 'create nil))
380         (setq flds (cdr flds)))
381       t)))
382
383 (luna-define-method elmo-folder-search ((folder elmo-multi-folder)
384                                         condition &optional numbers)
385   (let* ((flds (elmo-multi-folder-children-internal folder))
386          (cur-number 0)
387          numlist
388          matches)
389     (setq numbers (or numbers
390                       (elmo-folder-list-messages folder)))
391     (while flds
392       (setq cur-number (+ cur-number 1))
393       (setq matches (append matches
394                             (mapcar
395                              (function
396                               (lambda (x)
397                                 (+
398                                  (* (elmo-multi-folder-divide-number-internal
399                                      folder)
400                                     cur-number)
401                                  x)))
402                              (elmo-folder-search
403                               (car flds) condition))))
404       (setq flds (cdr flds)))
405     (elmo-list-filter numbers matches)))
406
407 (luna-define-method elmo-message-use-cache-p ((folder elmo-multi-folder)
408                                               number)
409   (let ((pair (elmo-multi-real-folder-number folder number)))
410     (elmo-message-use-cache-p (car pair) (cdr pair))))
411
412 (luna-define-method elmo-message-file-p ((folder elmo-multi-folder) number)
413   (let ((pair (elmo-multi-real-folder-number folder number)))
414     (elmo-message-file-p (car pair) (cdr pair))))
415
416 (luna-define-method elmo-message-file-name ((folder elmo-multi-folder) number)
417   (let ((pair (elmo-multi-real-folder-number folder number)))
418     (elmo-message-file-name (car pair) (cdr pair))))
419
420 (luna-define-method elmo-folder-plugged-p ((folder elmo-multi-folder))
421   (let ((flds (elmo-multi-folder-children-internal folder)))
422     (catch 'plugged
423       (while flds
424         (unless (elmo-folder-plugged-p (car flds))
425           (throw 'plugged nil))
426         (setq flds (cdr flds)))
427       t)))
428
429 (luna-define-method elmo-folder-set-plugged ((folder elmo-multi-folder)
430                                              plugged add)
431   (let ((flds  (elmo-multi-folder-children-internal folder)))
432     (dolist (fld flds)
433       (elmo-folder-set-plugged fld plugged add))))
434
435 (defun elmo-multi-folder-numbers-list-assoc (folder folder-numbers)
436   (let (ent)
437     (while folder-numbers
438       (when (string= (elmo-folder-name-internal (car (car folder-numbers)))
439                      (elmo-folder-name-internal folder))
440         (setq ent (car folder-numbers)
441               folder-numbers nil))
442       (setq folder-numbers (cdr folder-numbers)))
443     ent))
444
445 (defun elmo-multi-make-folder-numbers-list (folder msgs)
446   (let ((msg-list msgs)
447         pair fld-list
448         ret-val)
449     (while msg-list
450       (when (and (numberp (car msg-list))
451                  (> (car msg-list) 0))
452         (setq pair (elmo-multi-real-folder-number folder (car msg-list)))
453         (if (setq fld-list (elmo-multi-folder-numbers-list-assoc
454                             (car pair)
455                             ret-val))
456             (setcdr fld-list (cons (cdr pair) (cdr fld-list)))
457           (setq ret-val (cons (cons (car pair) (list (cdr pair))) ret-val))))
458       (setq msg-list (cdr msg-list)))
459     ret-val))
460
461 (luna-define-method elmo-folder-set-flag ((folder elmo-multi-folder)
462                                           numbers
463                                           flag
464                                           &optional is-local)
465   (dolist (pair (elmo-multi-make-folder-numbers-list folder numbers))
466     (elmo-folder-set-flag (car pair) (cdr pair) flag is-local)))
467
468 (luna-define-method elmo-folder-unset-flag ((folder elmo-multi-folder)
469                                             numbers
470                                             flag
471                                             &optional is-local)
472   (dolist (pair (elmo-multi-make-folder-numbers-list folder numbers))
473     (ignore-errors
474      (elmo-folder-unset-flag (car pair) (cdr pair) flag is-local))))
475
476 (luna-define-method elmo-folder-list-flagged ((folder elmo-multi-folder)
477                                               flag
478                                               &optional in-msgdb)
479   (let ((cur-number 0)
480         numbers)
481     (dolist (child (elmo-multi-folder-children-internal folder))
482       (setq cur-number (+ cur-number 1)
483             numbers
484             (nconc
485              numbers
486              (mapcar
487               (function
488                (lambda (x)
489                  (+
490                   (* (elmo-multi-folder-divide-number-internal folder)
491                      cur-number) x)))
492               (elmo-folder-list-flagged child flag in-msgdb)))))
493     numbers))
494
495 (luna-define-method elmo-folder-commit ((folder elmo-multi-folder))
496   (dolist (child (elmo-multi-folder-children-internal folder))
497     (elmo-folder-commit child)))
498
499 (luna-define-method elmo-folder-length ((folder elmo-multi-folder))
500   (let ((sum 0))
501     (dolist (child (elmo-multi-folder-children-internal folder))
502       (setq sum (+ sum (elmo-folder-length child))))
503     sum))
504
505 (luna-define-method elmo-folder-count-flags ((folder elmo-multi-folder))
506   (let (flag-alist element)
507     (dolist (child (elmo-multi-folder-children-internal folder))
508       (dolist (pair (elmo-folder-count-flags child))
509         (if (setq element (assq (car pair) flag-alist))
510             (setcdr element (+ (cdr element) (cdr pair)))
511           (setq flag-alist (cons pair flag-alist)))))
512     flag-alist))
513
514 (require 'product)
515 (product-provide (provide 'elmo-multi) (require 'elmo-version))
516
517 ;;; elmo-multi.el ends here