* elmo.el (elmo-folder-synchronize): Don't check important mark in
[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
32 (require 'elmo)
33 (require 'luna)
34
35 (defvar elmo-multi-divide-number 100000
36   "*Multi divider number.")
37
38 ;;; ELMO Multi folder
39 (eval-and-compile
40   (luna-define-class elmo-multi-folder (elmo-folder)
41                      (children divide-number))
42   (luna-define-internal-accessors 'elmo-multi-folder))
43
44 (luna-define-method elmo-folder-initialize ((folder
45                                              elmo-multi-folder)
46                                             name)
47   (elmo-multi-folder-set-children-internal
48    folder
49    (mapcar 'elmo-make-folder (split-string name ",")))
50   (elmo-multi-folder-set-divide-number-internal
51    folder
52    elmo-multi-divide-number)
53   folder)
54
55 (luna-define-method elmo-folder-open-internal ((folder elmo-multi-folder))
56   (dolist (fld (elmo-multi-folder-children-internal folder))
57     (elmo-folder-open-internal fld)))
58
59 (luna-define-method elmo-folder-check ((folder elmo-multi-folder))
60   (dolist (fld (elmo-multi-folder-children-internal folder))
61     (elmo-folder-check fld)))
62
63 (luna-define-method elmo-folder-close-internal ((folder elmo-multi-folder))
64   (dolist (fld (elmo-multi-folder-children-internal folder))
65     (elmo-folder-close-internal fld)))
66
67 (luna-define-method elmo-folder-expand-msgdb-path ((folder
68                                                     elmo-multi-folder))
69   (expand-file-name (elmo-replace-string-as-filename 
70                      (elmo-folder-name-internal folder))
71                     (expand-file-name "multi"
72                                       elmo-msgdb-dir)))
73
74 (luna-define-method elmo-folder-get-primitive-list ((folder elmo-multi-folder))
75   (elmo-flatten
76    (mapcar 
77     'elmo-folder-get-primitive-list
78     (elmo-multi-folder-children-internal folder))))
79
80 (luna-define-method elmo-folder-contains-type ((folder elmo-multi-folder) type)
81   (let ((children (elmo-multi-folder-children-internal folder))
82         match)
83     (while children
84       (when (elmo-folder-contains-type (car children) type)
85         (setq match t)
86         (setq children nil))
87       (setq children (cdr children)))
88     match))
89
90 (luna-define-method elmo-message-use-cache-p ((folder elmo-multi-folder)
91                                              number)
92   (elmo-message-use-cache-p 
93    (nth (- (/ number (elmo-multi-folder-divide-number-internal folder)) 1)
94         (elmo-multi-folder-children-internal folder))
95    (% number (elmo-multi-folder-divide-number-internal folder))))
96
97 (luna-define-method elmo-message-folder ((folder elmo-multi-folder)
98                                          number)
99   (nth (- (/ number (elmo-multi-folder-divide-number-internal folder)) 1)
100        (elmo-multi-folder-children-internal folder)))
101
102 (defun elmo-multi-msgdb (msgdb base)
103   (list (mapcar (function
104                  (lambda (x)
105                    (elmo-msgdb-overview-entity-set-number
106                     x
107                     (+ base
108                        (elmo-msgdb-overview-entity-get-number x)))))
109                 (nth 0 msgdb))
110         (mapcar (function
111                  (lambda (x) (cons
112                               (+ base (car x))
113                               (cdr x))))
114                 (nth 1 msgdb))
115         (mapcar (function
116                  (lambda (x) (cons
117                               (+ base (car x))
118                               (cdr x)))) (nth 2 msgdb))))
119
120 (defun elmo-multi-split-numbers (folder numlist &optional as-is)
121   (let ((numbers (sort numlist '<))
122         (divider (elmo-multi-folder-divide-number-internal folder))
123         (cur-number 0)
124         one-list numbers-list)
125     (while numbers
126       (setq cur-number (+ cur-number 1))
127       (setq one-list nil)
128       (while (and numbers
129                   (eq 0
130                       (/ (- (car numbers)
131                             (* divider cur-number))
132                          divider)))
133         (setq one-list (nconc
134                         one-list
135                         (list
136                          (if as-is
137                              (car numbers)
138                            (% (car numbers)
139                               (* divider cur-number))))))
140         (setq numbers (cdr numbers)))
141       (setq numbers-list (nconc numbers-list (list one-list))))
142     numbers-list))
143
144 (luna-define-method elmo-folder-msgdb-create ((folder elmo-multi-folder)
145                                               numbers new-mark already-mark
146                                               seen-mark important-mark
147                                               seen-list)
148   (let* ((folders (elmo-multi-folder-children-internal folder))
149          overview number-alist mark-alist entity
150          numbers-list
151          cur-number
152          i percent num
153          msgdb)
154     (setq numbers-list (elmo-multi-split-numbers folder numbers))
155     (setq cur-number 0)
156     (while (< cur-number (length folders))
157       (if (nth cur-number numbers-list)
158           (setq msgdb
159                 (elmo-msgdb-append
160                  msgdb
161                  (elmo-multi-msgdb
162                   (elmo-folder-msgdb-create (nth cur-number folders)
163                                             (nth cur-number numbers-list)
164                                             new-mark already-mark
165                                             seen-mark important-mark
166                                             seen-list)
167                   (* (elmo-multi-folder-divide-number-internal folder)
168                      (1+ cur-number))))))
169       (setq cur-number (1+ cur-number)))
170     (elmo-msgdb-sort-by-date msgdb)))
171
172 (luna-define-method elmo-folder-process-crosspost ((folder elmo-multi-folder)
173                                                    &optional
174                                                    number-alist)
175   (let ((number-alists (elmo-multi-split-number-alist
176                         folder
177                         (elmo-msgdb-get-number-alist
178                          (elmo-folder-msgdb folder))))
179         (cur-number 1))
180     (dolist (child (elmo-multi-folder-children-internal folder))
181       (elmo-folder-process-crosspost child (car number-alists))
182       (setq cur-number (+ 1 cur-number)
183             number-alists (cdr number-alists)))))
184
185 (defsubst elmo-multi-folder-append-msgdb (folder append-msgdb)
186   (if append-msgdb
187       (let* ((number-alist (elmo-msgdb-get-number-alist append-msgdb))
188              (all-alist (copy-sequence (append
189                                         (elmo-msgdb-get-number-alist
190                                          (elmo-folder-msgdb folder))
191                                         number-alist)))
192              (cur number-alist)
193              overview to-be-deleted
194              mark-alist same)
195         (while cur
196           (setq all-alist (delq (car cur) all-alist))
197           ;; same message id exists.
198           (if (setq same (rassoc (cdr (car cur)) all-alist))
199               (unless (= (/ (car (car cur))
200                             (elmo-multi-folder-divide-number-internal folder))
201                          (/ (car same) 
202                             (elmo-multi-folder-divide-number-internal folder)))
203                 ;; base is also same...delete it!
204                 (setq to-be-deleted
205                       (append to-be-deleted (list (car (car cur)))))))
206           (setq cur (cdr cur)))
207         (cond ((eq (elmo-folder-process-duplicates-internal folder)
208                    'hide)
209                ;; Hide duplicates.
210                (elmo-msgdb-append-to-killed-list folder to-be-deleted)
211                (setq overview (elmo-delete-if
212                                (lambda (x)
213                                  (memq (elmo-msgdb-overview-entity-get-number
214                                         x)
215                                        to-be-deleted))
216                                (elmo-msgdb-get-overview append-msgdb)))
217                ;; Should be mark as read.
218                (elmo-folder-mark-as-read folder to-be-deleted)
219                (elmo-msgdb-set-overview append-msgdb overview))
220               ((eq (elmo-folder-process-duplicates-internal folder)
221                    'read)
222                ;; Mark as read duplicates.
223                (elmo-folder-mark-as-read folder to-be-deleted))
224               (t
225                ;; Do nothing.
226                (setq to-be-deleted nil)))
227         (elmo-folder-set-msgdb-internal folder
228                                         (elmo-msgdb-append
229                                          (elmo-folder-msgdb folder)
230                                          append-msgdb t))
231         (length to-be-deleted))
232     0))
233
234 (luna-define-method elmo-folder-append-msgdb ((folder elmo-multi-folder)
235                                               append-msgdb)
236   (elmo-multi-folder-append-msgdb folder append-msgdb))
237
238 (defmacro elmo-multi-real-folder-number (folder number)
239   "Returns a cons cell of real FOLDER and NUMBER."
240   (` (cons (nth (- 
241                  (/ (, number)
242                     (elmo-multi-folder-divide-number-internal (, folder)))
243                  1) (elmo-multi-folder-children-internal (, folder)))
244            (% (, number) (elmo-multi-folder-divide-number-internal
245                           (, folder))))))
246
247 (defsubst elmo-multi-find-fetch-strategy (folder entity ignore-cache)
248   (if entity
249       (let ((pair (elmo-multi-real-folder-number
250                    folder
251                    (elmo-msgdb-overview-entity-get-number entity)))
252             (new-entity (elmo-msgdb-copy-overview-entity entity)))
253         (setq new-entity
254               (elmo-msgdb-overview-entity-set-number new-entity (cdr pair)))
255         (elmo-find-fetch-strategy (car pair) new-entity ignore-cache))
256     (elmo-make-fetch-strategy 'entire)))
257
258 (luna-define-method elmo-find-fetch-strategy
259   ((folder elmo-multi-folder)
260    entity &optional ignore-cache)
261   (elmo-multi-find-fetch-strategy folder entity ignore-cache))
262
263 (luna-define-method elmo-message-fetch ((folder elmo-multi-folder)
264                                         number strategy
265                                         &optional section outbuf unseen)
266   (let ((pair (elmo-multi-real-folder-number folder number)))
267     (elmo-message-fetch (car pair) (cdr pair) strategy section outbuf unseen)))
268
269 (luna-define-method elmo-folder-delete-messages ((folder elmo-multi-folder)
270                                                  numbers)
271   (let ((flds (elmo-multi-folder-children-internal folder))
272         one-list-list
273         (cur-number 0))
274     (setq one-list-list (elmo-multi-split-numbers folder numbers))
275     (while (< cur-number (length flds))
276       (elmo-folder-delete-messages (nth cur-number flds)
277                                    (nth cur-number one-list-list))
278       (setq cur-number (+ 1 cur-number)))
279     t))
280
281 (luna-define-method elmo-folder-diff ((folder elmo-multi-folder)
282                                       &optional numbers)
283   (elmo-multi-folder-diff folder numbers))
284
285 (defun elmo-multi-folder-diff (folder numbers)
286   (let ((flds (elmo-multi-folder-children-internal folder))
287         (numbers (mapcar 'car
288                          (elmo-msgdb-number-load
289                           (elmo-folder-msgdb-path folder))))
290         (killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
291         (count 0)
292         (unsync 0)
293         (messages 0)
294         num-list
295         diffs)
296     ;; If first time, dummy numbers is used as current number list.
297     (unless numbers
298       (let ((i 0)
299             (divider (elmo-multi-folder-divide-number-internal folder)))
300         (dolist (folder flds)
301           (setq i (+ i 1))
302           (setq numbers
303                 (cons (* i divider) numbers)))))
304     (setq num-list
305           (elmo-multi-split-numbers folder
306                                     (elmo-uniq-list
307                                      (nconc
308                                       (elmo-number-set-to-number-list killed)
309                                       numbers))))
310     (while flds
311       (setq diffs (nconc diffs (list (elmo-folder-diff
312                                       (car flds)
313                                       (car num-list)))))
314       (setq count (+ 1 count))
315       (setq num-list (cdr num-list))
316       (setq flds (cdr flds)))
317     (while diffs
318       (and (car (car diffs))
319            (setq unsync (+ unsync (car (car diffs)))))
320       (setq messages  (+ messages (cdr (car diffs))))
321       (setq diffs (cdr diffs)))
322     (elmo-folder-set-info-hashtb folder nil messages)
323     (cons unsync messages)))
324
325 (defun elmo-multi-split-number-alist (folder number-alist)
326   (let ((alist (sort (copy-sequence number-alist)
327                      (lambda (pair1 pair2)
328                        (< (car pair1)(car pair2)))))
329         (cur-number 0)
330         one-alist split num)
331     (while alist
332       (setq cur-number (+ cur-number 1))
333       (setq one-alist nil)
334       (while (and alist
335                   (eq 0
336                       (/ (- (setq num (car (car alist)))
337                             (* elmo-multi-divide-number cur-number))
338                          (elmo-multi-folder-divide-number-internal folder))))
339         (setq one-alist (nconc
340                          one-alist
341                          (list
342                           (cons
343                            (% num (* (elmo-multi-folder-divide-number-internal
344                                       folder) cur-number))
345                            (cdr (car alist))))))
346         (setq alist (cdr alist)))
347       (setq split (nconc split (list one-alist))))
348     split))
349
350 (defun elmo-multi-split-mark-alist (folder mark-alist)
351   (let ((cur-number 0)
352         (alist (sort (copy-sequence mark-alist)
353                      (lambda (pair1 pair2)
354                        (< (car pair1)(car pair2)))))
355         one-alist result)
356     (while alist
357       (setq cur-number (+ cur-number 1))
358       (setq one-alist nil)
359       (while (and alist
360                   (eq 0
361                       (/ (- (car (car alist))
362                             (* (elmo-multi-folder-divide-number-internal
363                                 folder) cur-number))
364                          (elmo-multi-folder-divide-number-internal folder))))
365         (setq one-alist (nconc
366                          one-alist
367                          (list
368                           (list (% (car (car alist))
369                                    (* (elmo-multi-folder-divide-number-internal
370                                        folder) cur-number))
371                                 (cadr (car alist))))))
372         (setq alist (cdr alist)))
373       (setq result (nconc result (list one-alist))))
374     result))
375
376 (luna-define-method elmo-folder-list-unreads-internal
377   ((folder elmo-multi-folder) unread-marks &optional mark-alist)
378   (elmo-multi-folder-list-unreads-internal folder unread-marks))
379
380 (defun elmo-multi-folder-list-unreads-internal (folder unread-marks)
381   (let ((folders (elmo-multi-folder-children-internal folder))
382         (mark-alists (elmo-multi-split-mark-alist
383                       folder
384                       (elmo-msgdb-get-mark-alist
385                        (elmo-folder-msgdb folder))))
386         (cur-number 0)
387         unreads
388         all-unreads)
389     (while folders
390       (setq cur-number (+ cur-number 1))
391       (unless (listp (setq unreads
392                            (elmo-folder-list-unreads-internal
393                             (car folders) unread-marks (car mark-alists))))
394         (setq unreads (delq  nil
395                              (mapcar
396                               (lambda (x)
397                                 (if (member (cadr x) unread-marks)
398                                     (car x)))
399                               (car mark-alists)))))
400       (setq all-unreads
401             (nconc all-unreads
402                    (mapcar 
403                     (lambda (x)
404                       (+ x
405                          (* cur-number
406                             (elmo-multi-folder-divide-number-internal
407                              folder))))            
408                     unreads)))
409       (setq mark-alists (cdr mark-alists)
410             folders (cdr folders)))
411     all-unreads))
412
413 (luna-define-method elmo-folder-list-importants-internal
414   ((folder elmo-multi-folder) important-mark)
415   (let ((folders (elmo-multi-folder-children-internal folder))
416         (mark-alists (elmo-multi-split-mark-alist
417                       folder
418                       (elmo-msgdb-get-mark-alist
419                        (elmo-folder-msgdb folder))))
420         (cur-number 0)
421         importants
422         all-importants)
423     (while folders
424       (setq cur-number (+ cur-number 1))
425       (unless (listp (setq importants
426                            (elmo-folder-list-importants-internal
427                             (car folders) important-mark)))
428         (setq importants (delq  nil
429                              (mapcar
430                               (lambda (x)
431                                 (if (string= (cadr x) important-mark)
432                                     (car x)))
433                               (car mark-alists)))))
434       (setq all-importants
435             (nconc all-importants
436                    (mapcar 
437                     (lambda (x)
438                       (+ x
439                          (* cur-number
440                             (elmo-multi-folder-divide-number-internal
441                              folder))))            
442                     importants)))
443       (setq mark-alists (cdr mark-alists)
444             folders (cdr folders)))
445     all-importants))
446
447 (luna-define-method elmo-folder-list-messages-internal
448   ((folder elmo-multi-folder) &optional nohide)
449   (let* ((flds (elmo-multi-folder-children-internal folder))
450          (cur-number 0)
451          list numbers)
452     (while flds
453       (setq cur-number (+ cur-number 1))
454       (setq list (elmo-folder-list-messages-internal (car flds)))
455       (setq numbers
456             (append
457              numbers
458              (if (listp list)
459                  (mapcar
460                   (function
461                    (lambda (x)
462                      (+
463                       (* (elmo-multi-folder-divide-number-internal
464                           folder) cur-number) x)))
465                   list)
466                ;; Use current list.
467                (elmo-delete-if
468                 (lambda (num)
469                   (not
470                    (eq cur-number (/ num
471                                      (elmo-multi-folder-divide-number-internal
472                                       folder)))))
473                 (mapcar
474                  'car
475                  (elmo-msgdb-get-number-alist
476                   (elmo-folder-msgdb folder)))))))
477       (setq flds (cdr flds)))
478     numbers))
479
480 (luna-define-method elmo-folder-exists-p ((folder elmo-multi-folder))
481   (let ((flds (elmo-multi-folder-children-internal folder)))
482     (catch 'exists
483       (while flds
484         (unless (elmo-folder-exists-p (car flds))
485           (throw 'exists nil))
486         (setq flds (cdr flds)))
487       t)))
488
489 (luna-define-method elmo-folder-creatable-p ((folder elmo-multi-folder))
490   (let ((flds (elmo-multi-folder-children-internal folder)))
491     (catch 'creatable
492       (while flds
493         (when (and (elmo-folder-creatable-p (car flds))
494                    (not (elmo-folder-exists-p (car flds))))
495           ;; If folder already exists, don't to `creatable'.
496           ;; Because this function is called, when folder doesn't exists.
497           (throw 'creatable t))
498         (setq flds (cdr flds)))
499       nil)))
500
501 (luna-define-method elmo-folder-create ((folder elmo-multi-folder))
502   (let ((flds (elmo-multi-folder-children-internal folder)))
503     (catch 'create
504       (while flds
505         (unless (or (elmo-folder-exists-p (car flds))
506                     (elmo-folder-create (car flds)))
507           (throw 'create nil))
508         (setq flds (cdr flds)))
509       t)))
510
511 (luna-define-method elmo-folder-search ((folder elmo-multi-folder)
512                                         condition &optional numlist)
513   (let* ((flds (elmo-multi-folder-children-internal folder))
514          (cur-number 0)
515          numlist-list cur-numlist ; for filtered search.
516          ret-val)
517     (if numlist
518         (setq numlist-list
519               (elmo-multi-split-numbers folder numlist t)))
520     (while flds
521       (setq cur-number (+ cur-number 1))
522       (when numlist
523         (setq cur-numlist (car numlist-list))
524         (if (null cur-numlist)
525             ;; t means filter all.
526             (setq cur-numlist t)))
527       (setq ret-val (append
528                      ret-val
529                      (elmo-list-filter
530                       cur-numlist
531                       (mapcar
532                        (function
533                         (lambda (x)
534                           (+
535                            (* (elmo-multi-folder-divide-number-internal
536                                folder) cur-number) x)))
537                        (elmo-folder-search
538                         (car flds) condition)))))
539       (when numlist
540         (setq numlist-list (cdr numlist-list)))
541       (setq flds (cdr flds)))
542     ret-val))
543
544 (luna-define-method elmo-message-use-cache-p ((folder elmo-multi-folder)
545                                               number)
546   (let ((pair (elmo-multi-real-folder-number folder number)))
547     (elmo-message-use-cache-p (car pair) (cdr pair))))
548
549 (luna-define-method elmo-message-file-p ((folder elmo-multi-folder) number)
550   (let ((pair (elmo-multi-real-folder-number folder number)))
551     (elmo-message-file-p (car pair) (cdr pair))))
552
553 (luna-define-method elmo-message-file-name ((folder elmo-multi-folder) number)
554   (let ((pair (elmo-multi-real-folder-number folder number)))
555     (elmo-message-file-name (car pair) (cdr pair))))
556   
557 (luna-define-method elmo-folder-plugged-p ((folder elmo-multi-folder))
558   (let ((flds (elmo-multi-folder-children-internal folder)))
559     (catch 'plugged
560       (while flds
561         (unless (elmo-folder-plugged-p (car flds))
562           (throw 'plugged nil))
563         (setq flds (cdr flds)))
564       t)))
565
566 (luna-define-method elmo-folder-set-plugged ((folder elmo-multi-folder)
567                                              plugged add)
568   (let ((flds  (elmo-multi-folder-children-internal folder)))
569     (dolist (fld flds)
570       (elmo-folder-set-plugged fld plugged add))))
571
572 (defun elmo-multi-folder-numbers-list-assoc (folder folder-numbers)
573   (let (ent)
574     (while folder-numbers
575       (when (string= (elmo-folder-name-internal (car (car folder-numbers)))
576                      (elmo-folder-name-internal folder))
577         (setq ent (car folder-numbers)
578               folder-numbers nil))
579       (setq folder-numbers (cdr folder-numbers)))
580     ent))
581
582 (defun elmo-multi-make-folder-numbers-list (folder msgs)
583   (let ((msg-list msgs)
584         pair fld-list
585         ret-val)
586     (while msg-list
587       (when (and (numberp (car msg-list))
588                  (> (car msg-list) 0))
589         (setq pair (elmo-multi-real-folder-number folder (car msg-list)))
590         (if (setq fld-list (elmo-multi-folder-numbers-list-assoc
591                             (car pair)
592                             ret-val))
593             (setcdr fld-list (cons (cdr pair) (cdr fld-list)))
594           (setq ret-val (cons (cons (car pair) (list (cdr pair))) ret-val))))
595       (setq msg-list (cdr msg-list)))
596     ret-val))
597
598 (luna-define-method elmo-folder-mark-as-important ((folder elmo-multi-folder)
599                                                    numbers)
600   (dolist (folder-numbers (elmo-multi-make-folder-numbers-list folder numbers))
601     (elmo-folder-mark-as-important (car folder-numbers)
602                                    (cdr folder-numbers)))
603   t)
604   
605 (luna-define-method elmo-folder-unmark-important ((folder elmo-multi-folder)
606                                                   numbers)
607   (dolist (folder-numbers (elmo-multi-make-folder-numbers-list folder numbers))
608     (elmo-folder-unmark-important (car folder-numbers)
609                                   (cdr folder-numbers)))
610   t)
611
612 (luna-define-method elmo-folder-mark-as-read ((folder elmo-multi-folder)
613                                               numbers)
614   (dolist (folder-numbers (elmo-multi-make-folder-numbers-list folder numbers))
615     (elmo-folder-mark-as-read (car folder-numbers)
616                               (cdr folder-numbers)))
617   t)
618
619 (luna-define-method elmo-folder-unmark-read ((folder elmo-multi-folder)
620                                              numbers)
621   (dolist (folder-numbers (elmo-multi-make-folder-numbers-list folder numbers))
622     (elmo-folder-unmark-read (car folder-numbers)
623                              (cdr folder-numbers)))
624   t)
625
626 (require 'product)
627 (product-provide (provide 'elmo-multi) (require 'elmo-version))
628
629 ;;; elmo-multi.el ends here