2000-11-08 TAKAHASHI Kaoru <kaoru@kaisei.org>
[elisp/wanderlust.git] / wl / wl-folder.el
1 ;;; wl-folder.el -- Folder mode for Wanderlust.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1998,1999,2000 Masahiro MURATA <muse@ba2.so-net.ne.jp>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;;      Masahiro MURATA <muse@ba2.so-net.ne.jp>
8 ;; Keywords: mail, net news
9
10 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
11
12 ;; This program is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; any later version.
16 ;;
17 ;; This program is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 ;; GNU General Public License for more details.
21 ;;
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27
28 ;;; Commentary:
29 ;;
30
31 ;;; Code:
32 ;;
33
34 (require 'elmo-vars)
35 (require 'elmo-util)
36 (require 'elmo2)
37 (require 'wl-vars)
38 (condition-case ()
39     (require 'easymenu) ; needed here.
40   (error))
41
42 (eval-when-compile
43   (require 'cl)
44   (require 'wl-util)
45   (provide 'wl-folder)
46   (require 'wl)
47   (require 'elmo-nntp)
48   (if wl-use-semi
49       (require 'mmelmo))
50   (unless (boundp ':file)
51     (set (make-local-variable ':file) nil))
52   (defun-maybe mmelmo-cleanup-entity-buffers ()))
53
54 (defvar wl-folder-buffer-name "Folder")
55 (defvar wl-folder-entity nil)           ; desktop entity.
56 (defvar wl-folder-group-alist nil)      ; opened or closed
57 (defvar wl-folder-entity-id nil) ; id
58 (defvar wl-folder-entity-hashtb nil)
59 (defvar wl-folder-entity-id-name-hashtb nil)
60 (defvar wl-folder-newsgroups-hashtb nil)
61 (defvar wl-folder-info-alist-modified nil)
62 (defvar wl-folder-completion-func nil)
63
64 (defvar wl-folder-mode-map nil)
65
66 (defvar wl-folder-buffer-disp-summary nil)
67 (defvar wl-folder-buffer-cur-entity-id nil)
68 (defvar wl-folder-buffer-cur-path nil)
69 (defvar wl-folder-buffer-cur-point nil)
70
71 (make-variable-buffer-local 'wl-folder-buffer-disp-summary)
72 (make-variable-buffer-local 'wl-folder-buffer-cur-entity-id)
73 (make-variable-buffer-local 'wl-folder-buffer-cur-path)
74 (make-variable-buffer-local 'wl-folder-buffer-cur-point)
75
76 (defconst wl-folder-entity-regexp "^\\([ ]*\\)\\(\\[[\\+-]\\]\\)?\\([^\\[].+\\):[-*0-9]+/[-*0-9]+/[-*0-9]+")
77 (defconst wl-folder-group-regexp  "^\\([ ]*\\)\\[\\([\\+-]\\)\\]\\(.+\\):[-0-9-]+/[0-9-]+/[0-9-]+\n")
78 ;;                              1:indent 2:opened 3:group-name
79 (defconst wl-folder-unsync-regexp ":[^0\\*][0-9]*/[0-9\\*-]+/[0-9\\*-]+$")
80
81 (defvar wl-folder-mode-menu-spec
82   '("Folder"
83     ["Enter Current Folder" wl-folder-jump-to-current-entity t]
84     ["Prev Folder"          wl-folder-prev-entity t]
85     ["Next Folder"          wl-folder-next-entity t]
86     ["Check Current Folder" wl-folder-check-current-entity t]
87     ["Sync Current Folder"  wl-folder-sync-current-entity t]
88     ["Drop Current Folder" wl-folder-drop-unsync-current-entity t]
89     ["Prefetch Current Folder" wl-folder-prefetch-current-entity t]
90     ["Mark as Read all Current Folder" wl-folder-mark-as-read-all-current-entity t]
91     ["Expire Current Folder" wl-folder-expire-current-entity t]
92     ["Empty trash" wl-folder-empty-trash t]
93     ["Flush queue" wl-folder-flush-queue t]
94     ["Open All" wl-folder-open-all t]
95     ["Open All Unread folder" wl-folder-open-all-unread-folder t]
96     ["Close All" wl-folder-close-all t]
97     ("Folder Manager"
98      ["Add folder" wl-fldmgr-add t]
99      ["Add group" wl-fldmgr-make-group t]
100      ["Copy" wl-fldmgr-copy t]
101      ["Cut" wl-fldmgr-cut t]
102      ["Paste" wl-fldmgr-yank t]
103      ["Set petname" wl-fldmgr-set-petname t]
104      ["Rename" wl-fldmgr-rename t]
105      ["Save" wl-fldmgr-save-folders t]
106      "----"
107      ["Unsubscribe" wl-fldmgr-unsubscribe t]
108      ["Display all" wl-fldmgr-access-display-all t])
109     "----"
110     ["Write a message" wl-draft t]
111     "----"
112     ["Toggle Plug Status" wl-toggle-plugged t]
113     ["Change Plug Status" wl-plugged-change t]
114     "----"
115     ["Save Current Status"  wl-save t]
116     ["Update Satus"         wl-status-update t]
117     ["Exit"                 wl-exit t]
118     ))
119
120 (if wl-on-xemacs
121     (defun wl-folder-setup-mouse ()
122       (define-key wl-folder-mode-map 'button2 'wl-folder-click)
123       (define-key wl-folder-mode-map 'button4 'wl-folder-prev-entity)
124       (define-key wl-folder-mode-map 'button5 'wl-folder-next-entity)
125       (define-key wl-folder-mode-map [(shift button4)]
126         'wl-folder-prev-unread)
127       (define-key wl-folder-mode-map [(shift button5)]
128         'wl-folder-next-unread))
129   (if wl-on-nemacs
130       (defun wl-folder-setup-mouse ())
131     (defun wl-folder-setup-mouse ()
132       (define-key wl-folder-mode-map [mouse-2] 'wl-folder-click)
133       (define-key wl-folder-mode-map [mouse-4] 'wl-folder-prev-entity)
134       (define-key wl-folder-mode-map [mouse-5] 'wl-folder-next-entity)
135       (define-key wl-folder-mode-map [S-mouse-4] 'wl-folder-prev-unread)
136       (define-key wl-folder-mode-map [S-mouse-5] 'wl-folder-next-unread))))
137
138 (if wl-folder-mode-map
139     nil
140   (setq wl-folder-mode-map (make-sparse-keymap))
141   (define-key wl-folder-mode-map " "    'wl-folder-jump-to-current-entity)
142 ;  (define-key wl-folder-mode-map "\M- " 'wl-folder-open-close)
143   (define-key wl-folder-mode-map "/"    'wl-folder-open-close)
144   (define-key wl-folder-mode-map "\C-m" 'wl-folder-jump-to-current-entity)
145   (define-key wl-folder-mode-map "\M-\C-m" 'wl-folder-update-recursive-current-entity)
146   (define-key wl-folder-mode-map "rc"    'wl-folder-mark-as-read-all-region)
147   (define-key wl-folder-mode-map "c"    'wl-folder-mark-as-read-all-current-entity)
148   (define-key wl-folder-mode-map "g"    'wl-folder-goto-folder)
149   (define-key wl-folder-mode-map "j"    'wl-folder-jump-to-current-entity)
150   (define-key wl-folder-mode-map "w"    'wl-draft)
151   (define-key wl-folder-mode-map "W"    'wl-folder-write-current-folder)
152   (define-key wl-folder-mode-map "\C-c\C-o" 'wl-jump-to-draft-buffer)
153   (define-key wl-folder-mode-map "rS"   'wl-folder-sync-region)
154   (define-key wl-folder-mode-map "S"    'wl-folder-sync-current-entity)
155   (define-key wl-folder-mode-map "rs"   'wl-folder-check-region)
156   (define-key wl-folder-mode-map "s"    'wl-folder-check-current-entity)
157   (define-key wl-folder-mode-map "I"    'wl-folder-prefetch-current-entity)
158   (define-key wl-folder-mode-map "D"    'wl-folder-drop-unsync-current-entity)
159   (define-key wl-folder-mode-map "p"    'wl-folder-prev-entity)
160   (define-key wl-folder-mode-map "n"    'wl-folder-next-entity)
161   (define-key wl-folder-mode-map "v"    'wl-folder-toggle-disp-summary)
162   (define-key wl-folder-mode-map "P"    'wl-folder-prev-unread)
163   (define-key wl-folder-mode-map "N"    'wl-folder-next-unread)
164   (define-key wl-folder-mode-map "J"    'wl-folder-jump-folder)
165   (define-key wl-folder-mode-map "f"    'wl-folder-goto-first-unread-folder)
166   (define-key wl-folder-mode-map "o"    'wl-folder-open-all-unread-folder)
167   (define-key wl-folder-mode-map "["    'wl-folder-open-all)
168   (define-key wl-folder-mode-map "]"    'wl-folder-close-all)
169   (define-key wl-folder-mode-map "e"    'wl-folder-expire-current-entity)
170   (define-key wl-folder-mode-map "E"    'wl-folder-empty-trash)
171   (define-key wl-folder-mode-map "F"    'wl-folder-flush-queue)
172   (define-key wl-folder-mode-map "q"    'wl-exit)
173   (define-key wl-folder-mode-map "z"    'wl-folder-suspend)
174   (define-key wl-folder-mode-map "\M-t" 'wl-toggle-plugged)
175   (define-key wl-folder-mode-map "\C-t" 'wl-plugged-change)
176   (define-key wl-folder-mode-map "<"    'beginning-of-buffer)
177   (define-key wl-folder-mode-map ">"    'end-of-buffer)
178   ;; wl-fldmgr
179   (unless wl-on-nemacs
180     (define-key wl-folder-mode-map "m"    'wl-fldmgr-mode-map))
181   (define-key wl-folder-mode-map "*"    'wl-fldmgr-make-multi)
182   (define-key wl-folder-mode-map "+"    'wl-fldmgr-make-group)
183   (define-key wl-folder-mode-map "|"    'wl-fldmgr-make-filter)
184   (define-key wl-folder-mode-map "\M-c" 'wl-fldmgr-copy)
185   (define-key wl-folder-mode-map "\M-w" 'wl-fldmgr-copy-region)
186   (define-key wl-folder-mode-map "\C-k" 'wl-fldmgr-cut)
187   (define-key wl-folder-mode-map "\C-w" 'wl-fldmgr-cut-region)
188   (define-key wl-folder-mode-map "\C-y" 'wl-fldmgr-yank)
189   (define-key wl-folder-mode-map "R"    'wl-fldmgr-rename)
190   (define-key wl-folder-mode-map "u"    'wl-fldmgr-unsubscribe)
191   (define-key wl-folder-mode-map "ru"   'wl-fldmgr-unsubscribe-region)
192   (define-key wl-folder-mode-map "U"    'wl-fldmgr-unsubscribe-region)
193   (define-key wl-folder-mode-map "l"    'wl-fldmgr-access-display-normal)
194   (define-key wl-folder-mode-map "L"    'wl-fldmgr-access-display-all)
195   (define-key wl-folder-mode-map "Z"    'wl-status-update)
196   (define-key wl-folder-mode-map "\C-x\C-s" 'wl-save)
197   (define-key wl-folder-mode-map "\M-s"     'wl-save)
198   (define-key wl-folder-mode-map "\C-xk"    'wl-folder-mimic-kill-buffer)
199   (define-key wl-folder-mode-map "\M-\C-a"
200     'wl-folder-goto-top-of-current-folder)
201   (define-key wl-folder-mode-map "\M-\C-e"
202     'wl-folder-goto-bottom-of-current-folder)
203
204   (wl-folder-setup-mouse)
205   (easy-menu-define
206    wl-folder-mode-menu
207    wl-folder-mode-map
208    "Menu used in Folder mode."
209    wl-folder-mode-menu-spec))
210
211 (defmacro wl-folder-unread-regex (group)
212   (` (concat "^[ ]*.+:[0-9\\*-]+/[^0\\*][0-9]*/[0-9\\*-]+$"
213              (if (, group)
214                  "\\|^[ ]*\\[[+-]\\]"
215                ""))))
216
217 (defmacro wl-folder-buffer-group-p ()
218   (` (save-excursion (beginning-of-line)
219                      (looking-at wl-folder-group-regexp))))
220
221 (defmacro wl-folder-folder-name ()
222   (` (save-excursion
223        (beginning-of-line)
224        (if (or (looking-at "^[ ]*\\[[\\+-]\\]\\(.+\\):[-0-9\\*-]+/[0-9\\*-]+/[0-9\\*-]+\n")
225                (looking-at "^[ ]*\\([^\\[].+\\):.*\n"))
226            (wl-match-buffer 1)))))
227
228 (defmacro wl-folder-entity-name ()
229   (` (save-excursion
230        (beginning-of-line)
231        (if (looking-at "^[ ]*\\([^\\[].+\\):.*\n")
232            (wl-match-buffer 1)))))
233
234 (defun wl-folder-buffer-search-group (group)
235   (re-search-forward
236    (concat
237     "^\\([ \t]*\\)\\[[\\+-]\\]"
238     (regexp-quote group) ":[-0-9-]+/[0-9-]+/[0-9-]+") nil t))
239
240 (defun wl-folder-buffer-search-entity (folder &optional searchname)
241   (let ((search (or searchname (wl-folder-get-petname folder))))
242     (re-search-forward
243      (concat
244       "^[ \t]*"
245       (regexp-quote search) ":[-0-9\\*-]+/[0-9\\*-]+/[0-9\\*-]+") nil t)))
246
247 (defsubst wl-folder-get-folder-name-by-id (entity-id &optional hashtb)
248   (and (numberp entity-id)
249        (elmo-get-hash-val (format "#%d" entity-id)
250                           (or hashtb wl-folder-entity-id-name-hashtb))))
251
252 (defsubst wl-folder-set-id-name (entity-id entity &optional hashtb)
253   (and (numberp entity-id)
254        (elmo-set-hash-val (format "#%d" entity-id)
255                           entity (or hashtb wl-folder-entity-id-name-hashtb))))
256
257 (defmacro wl-folder-get-entity-id (entity)
258   (` (or (get-text-property 0
259                             'wl-folder-entity-id
260                             (, entity))
261          (, entity)))) ;; for nemacs
262
263 (defmacro wl-folder-get-entity-from-buffer (&optional getid)
264   (` (let ((id (get-text-property (point)
265                                   'wl-folder-entity-id)))
266        (if (not id) ;; for nemacs
267            (wl-folder-get-realname (wl-folder-folder-name))
268          (if (, getid)
269              id
270            (wl-folder-get-folder-name-by-id id))))))
271
272 (defmacro wl-folder-entity-exists-p (entity &optional hashtb)
273   (` (let ((sym (intern-soft (, entity)
274                              (or (, hashtb) wl-folder-entity-hashtb))))
275        (and sym (boundp sym)))))
276
277 (defmacro wl-folder-clear-entity-info (entity &optional hashtb)
278   (` (let ((sym (intern-soft (, entity)
279                              (or (, hashtb) wl-folder-entity-hashtb))))
280        (if (boundp sym)
281            (makunbound sym)))))
282
283 (defmacro wl-folder-get-entity-info (entity &optional hashtb)
284   (` (elmo-get-hash-val (, entity) (or (, hashtb) wl-folder-entity-hashtb))))
285
286 (defmacro wl-folder-set-entity-info (entity value &optional hashtb)
287   (` (let* ((hashtb (or (, hashtb) wl-folder-entity-hashtb))
288             (info (wl-folder-get-entity-info (, entity) hashtb)))
289        (elmo-set-hash-val (, entity)
290                           (if (< (length (, value)) 4)
291                               (append (, value) (list (nth 3 info)))
292                             (, value))
293                           hashtb))))
294
295 (defun wl-folder-persistent-p (folder)
296   (or (elmo-get-hash-val folder wl-folder-entity-hashtb) ; on Folder mode.
297       (catch 'found
298         (let ((li wl-save-folder-list))
299           (while li
300             (if (string-match (car li) folder)
301                 (throw 'found t))
302             (setq li (cdr li)))))
303       (not (catch 'found
304              (let ((li wl-no-save-folder-list))
305                (while li
306                  (if (string-match (car li) folder)
307                      (throw 'found t))
308                  (setq li (cdr li))))))))
309
310 (defun wl-folder-prev-entity ()
311   (interactive)
312   (forward-line -1))
313
314 (defun wl-folder-next-entity ()
315   (interactive)
316   (forward-line 1))
317
318 (defun wl-folder-prev-entity-skip-invalid (&optional hereto)
319   "move to previous entity. skip unsubscribed or removed entity."
320   (interactive)
321   (if hereto
322       (end-of-line))
323   (if (re-search-backward wl-folder-entity-regexp nil t)
324       (beginning-of-line)
325     (goto-char (point-min))))
326
327 (defun wl-folder-next-entity-skip-invalid (&optional hereto)
328   "Move to next entity. skip unsubscribed or removed entity."
329   (interactive)
330   (beginning-of-line)
331   (if (not hereto)
332       (forward-line 1))
333   (if (re-search-forward wl-folder-entity-regexp nil t)
334       (beginning-of-line)
335     (goto-char (point-max))))
336
337 (defun wl-folder-search-group-entity-by-name (name entity)
338   (wl-folder-search-entity-by-name name entity 'group))
339
340 (defun wl-folder-search-entity-by-name (name entity &optional type)
341   (let ((entities (list entity))
342         entity-stack)
343     (catch 'done
344       (while entities
345         (setq entity (wl-pop entities))
346         (cond
347          ((consp entity)
348           (if (and (not (eq type 'folder))
349                    (string= name (car entity)))
350               (throw 'done entity))
351           (and entities
352                (wl-push entities entity-stack))
353           (setq entities (nth 2 entity)))
354          ((and (not (eq type 'group))
355                (stringp entity))
356           (if (string= name entity)
357               (throw 'done entity))))
358         (unless entities
359           (setq entities (wl-pop entity-stack)))))))
360
361 (defun wl-folder-search-entity-list-by-name (name entity &optional get-id)
362   (let ((entities (list entity))
363         entity-stack ret-val)
364     (while entities
365       (setq entity (wl-pop entities))
366       (cond
367        ((consp entity)
368         (and entities
369              (wl-push entities entity-stack))
370         (setq entities (nth 2 entity)))
371        ((stringp entity)
372         (if (string= name entity)
373             (wl-append ret-val (if get-id
374                                    (list (wl-folder-get-entity-id entity))
375                                  (list entity))))))
376       (unless entities
377         (setq entities (wl-pop entity-stack))))
378     ret-val))
379
380 (defun wl-folder-get-prev-folder (id &optional unread)
381   (let ((name (if (stringp id)
382                   id
383                 (wl-folder-get-folder-name-by-id id)))
384         entity entity-stack last-entity finfo
385         (entities (list wl-folder-entity)))
386     (catch 'done
387       (while entities
388         (setq entity (wl-pop entities))
389         (cond
390          ((consp entity)
391 ;;        (if (and (string= name (car entity))
392 ;;                 (eq id (wl-folder-get-entity-id (car entity))))
393 ;;            (throw 'done last-entity))
394           (and entities
395                (wl-push entities entity-stack))
396           (setq entities (nth 2 entity)))
397          ((stringp entity)
398           (if (and (string= name entity)
399                    ;; don't use eq, `id' is string on Nemacs.
400                    (equal id (wl-folder-get-entity-id entity)))
401               (throw 'done last-entity))
402           (if (or (not unread)
403                   (and (setq finfo (wl-folder-get-entity-info entity))
404                        (and (nth 0 finfo)(nth 1 finfo))
405                        (> (+ (nth 0 finfo)(nth 1 finfo)) 0)))
406               (setq last-entity entity))))
407         (unless entities
408           (setq entities (wl-pop entity-stack)))))))
409
410 (defun wl-folder-get-next-folder (id &optional unread)
411   (let ((name (if (stringp id)
412                   id
413                 (wl-folder-get-folder-name-by-id id)))
414         entity entity-stack found finfo
415         (entities (list wl-folder-entity)))
416     (catch 'done
417       (while entities
418         (setq entity (wl-pop entities))
419         (cond
420          ((consp entity)
421 ;;;       (if (and (string= name (car entity))
422 ;;;                (eq id (wl-folder-get-entity-id (car entity))))
423 ;;;           (setq found t))
424           (and entities
425                (wl-push entities entity-stack))
426           (setq entities (nth 2 entity)))
427          ((stringp entity)
428           (if found
429               (when (or (not unread)
430                         (and (setq finfo (wl-folder-get-entity-info entity))
431                              (and (nth 0 finfo)(nth 1 finfo))
432                              (> (+ (nth 0 finfo)(nth 1 finfo)) 0)))
433                 (throw 'done entity))
434             (if (and (string= name entity)
435                      ;; don't use eq, `id' is string on Nemacs.
436                      (equal id (wl-folder-get-entity-id entity)))
437                 (setq found t)))))
438         (unless entities
439           (setq entities (wl-pop entity-stack)))))))
440
441 (defun wl-folder-flush-queue ()
442   "Flush queue."
443   (interactive)
444   (let ((cur-buf (current-buffer))
445         (wl-auto-select-first nil)
446         (wl-plugged t)
447         emptied)
448     (if elmo-enable-disconnected-operation
449         (elmo-dop-queue-flush 'force)) ; Try flushing all queue.
450     (if (not (elmo-list-folder wl-queue-folder))
451         (message "No sending queue exists.")
452       (if wl-stay-folder-window
453           (wl-folder-select-buffer
454            (wl-summary-get-buffer-create wl-queue-folder)))
455       (wl-summary-goto-folder-subr wl-queue-folder 'force-update nil)
456       (unwind-protect
457           (wl-draft-queue-flush)
458         (if (get-buffer-window cur-buf)
459             (select-window (get-buffer-window cur-buf)))
460         (set-buffer cur-buf)
461         (if wl-stay-folder-window
462             (wl-folder-toggle-disp-summary 'off wl-queue-folder)
463           (switch-to-buffer cur-buf))))))
464
465 (defun wl-folder-empty-trash ()
466   "Empty trash."
467   (interactive)
468   (let ((cur-buf (current-buffer))
469         (wl-auto-select-first nil)
470         trash-buf emptied)
471     (if wl-stay-folder-window
472         (wl-folder-select-buffer
473          (wl-summary-get-buffer-create wl-trash-folder)))
474     (wl-summary-goto-folder-subr wl-trash-folder 'force-update nil nil t)
475     (setq trash-buf (current-buffer))
476     (unwind-protect
477         (setq emptied (wl-summary-delete-all-msgs))
478       (when emptied
479         (setq wl-thread-entities nil
480               wl-thread-entity-list nil)
481         (if wl-summary-cache-use (wl-summary-save-view-cache))
482         (wl-summary-msgdb-save))
483       (if (get-buffer-window cur-buf)
484           (select-window (get-buffer-window cur-buf)))
485       (set-buffer cur-buf)
486       (if emptied
487           (wl-folder-set-folder-updated wl-trash-folder '(0 0 0)))
488       (if wl-stay-folder-window
489           (wl-folder-toggle-disp-summary 'off wl-trash-folder)
490         (switch-to-buffer cur-buf))
491       (and trash-buf
492            (kill-buffer trash-buf)))))
493
494 (defun wl-folder-goto-top-of-current-folder (&optional arg)
495   "Move backward to the top of the current folder group.
496 Optional argument ARG is repeart count."
497   (interactive "P")
498   (if (re-search-backward
499        "^ *\\[[\\+-]\\]" nil t (if arg (prefix-numeric-value arg)))
500       (beginning-of-line)
501     (goto-char (point-min))))
502
503 (defun wl-folder-goto-bottom-of-current-folder (indent)
504   "Move forward to the bottom of the current folder group."
505   (interactive
506    (let ((indent
507           (save-excursion
508             (beginning-of-line)
509             (if (looking-at "^ *")
510                 (buffer-substring (match-beginning 0)(1- (match-end 0)))
511               ""))))
512      (list indent)))
513   (if (catch 'done
514         (while (re-search-forward "^ *" nil t)
515           (if (<= (length (match-string 0))
516                   (length indent))
517               (throw 'done nil)))
518         (throw 'done t))
519       (goto-char (point-max))))
520
521 (defsubst wl-folder-update-group (entity diffs &optional is-group)
522   (let ((path (wl-folder-get-path
523                wl-folder-entity
524                (wl-folder-get-entity-id entity)
525                entity)))
526     (if (not is-group)
527         ;; delete itself from path
528         (setq path (delete (nth (- (length path) 1) path) path)))
529     (goto-char (point-min))
530     (catch 'done
531       (while path
532         ;; goto the path line.
533         (if (or (eq (car path) 0) ; update desktop
534                 (wl-folder-buffer-search-group
535                  (wl-folder-get-petname
536                   (if (stringp (car path))
537                       (car path)
538                     (wl-folder-get-folder-name-by-id
539                      (car path))))))
540             ;; update it.
541             (wl-folder-update-diff-line diffs)
542           (throw 'done t))
543         (setq path (cdr path))))))
544
545 (defun wl-folder-maybe-load-folder-list (entity)
546   (when (null (caddr entity))
547     (setcdr (cdr entity)
548             (elmo-msgdb-flist-load (car entity)))
549     (when (cddr entity)
550       (let (diffs)
551         (save-excursion
552           (wl-folder-entity-assign-id entity
553                                       wl-folder-entity-id-name-hashtb
554                                       t)
555           (setq diffs (wl-fldmgr-add-entity-hashtb (list entity)))
556           (unless (equal diffs '(0 0 0))
557             (wl-folder-update-group (car entity) diffs t)))))))
558
559 (defsubst wl-folder-force-fetch-p (entity)
560   (cond
561    ((consp wl-force-fetch-folders)
562     (wl-string-match-member entity wl-force-fetch-folders))
563    (t
564     wl-force-fetch-folders)))
565
566 (defun wl-folder-jump-to-current-entity (&optional arg)
567   "Enter the current folder.  If optional ARG exists, update folder list."
568   (interactive "P")
569   (beginning-of-line)
570   (let (entity beg end indent opened fname err fld-name)
571     (cond
572      ((looking-at wl-folder-group-regexp)
573       (save-excursion
574         (setq fname (wl-folder-get-realname (wl-match-buffer 3)))
575         (setq indent (wl-match-buffer 1))
576         (setq opened (wl-match-buffer 2))
577         (if (string= opened "+")
578             (progn
579               (setq entity (wl-folder-search-group-entity-by-name
580                             fname
581                             wl-folder-entity))
582               (setq beg (point))
583               (if arg
584                   (wl-folder-update-recursive-current-entity entity)
585               ;; insert as opened
586               (setcdr (assoc (car entity) wl-folder-group-alist) t)
587               (if (eq 'access (cadr entity))
588                   (wl-folder-maybe-load-folder-list entity))
589               (condition-case errobj
590                   (progn
591                     (if (or (wl-folder-force-fetch-p (car entity))
592                             (and
593                              (eq 'access (cadr entity))
594                              (null (caddr entity))))
595                         (wl-folder-update-newest indent entity)
596                       (wl-folder-insert-entity indent entity))
597                     (wl-highlight-folder-path wl-folder-buffer-cur-path))
598                 (quit
599                  (setq err t)
600                  (setcdr (assoc fname wl-folder-group-alist) nil))
601                 (error
602                  (elmo-display-error errobj t)
603                  (ding)
604                  (setq err t)
605                  (setcdr (assoc fname wl-folder-group-alist) nil)))
606               (if (not err)
607                   (let ((buffer-read-only nil))
608                     (delete-region (save-excursion (beginning-of-line)
609                                                    (point))
610                                    (save-excursion (end-of-line)
611                                                    (+ 1 (point))))))))
612           (setq beg (point))
613           (end-of-line)
614           (save-match-data
615             (setq end
616                   (progn (wl-folder-goto-bottom-of-current-folder indent)
617                          (beginning-of-line)
618                          (point))))
619           (setq entity (wl-folder-search-group-entity-by-name
620                         fname
621                         wl-folder-entity))
622           (let ((buffer-read-only nil))
623             (delete-region beg end))
624           (setcdr (assoc (car entity) wl-folder-group-alist) nil)
625           (wl-folder-insert-entity indent entity) ; insert entity
626           (forward-line -1)
627           (wl-highlight-folder-path wl-folder-buffer-cur-path)
628 ;         (wl-delete-all-overlays)
629 ;         (wl-highlight-folder-current-line)
630           )))
631      ((setq fld-name (wl-folder-entity-name))
632       (if wl-on-nemacs
633           (progn
634             (wl-folder-set-current-entity-id
635              (wl-folder-get-entity-from-buffer))
636             (setq fld-name (wl-folder-get-realname fld-name)))
637         (wl-folder-set-current-entity-id
638          (get-text-property (point) 'wl-folder-entity-id))
639         (setq fld-name (wl-folder-get-folder-name-by-id
640                         wl-folder-buffer-cur-entity-id)))
641       (let ((summary-buf (wl-summary-get-buffer-create fld-name arg))
642             error-selecting)
643         (if wl-stay-folder-window
644             (wl-folder-select-buffer summary-buf)
645           (if (and summary-buf
646                    (get-buffer-window summary-buf))
647               (delete-window)))
648         (wl-summary-goto-folder-subr fld-name
649                                      (wl-summary-get-sync-range fld-name)
650                                      nil arg t)))))
651   (set-buffer-modified-p nil))
652
653 (defun wl-folder-close-entity (entity)
654   (let ((entities (list entity))
655         entity-stack)
656     (while entities
657       (setq entity (wl-pop entities))
658       (cond
659        ((consp entity)
660         (setcdr (assoc (car entity) wl-folder-group-alist) nil)
661         (and entities
662              (wl-push entities entity-stack))
663         (setq entities (nth 2 entity))))
664       (unless entities
665         (setq entities (wl-pop entity-stack))))))
666
667 (defun wl-folder-update-recursive-current-entity (&optional entity)
668   (interactive)
669   (when (wl-folder-buffer-group-p)
670     (cond
671      ((string= (wl-match-buffer 2) "+")
672       (save-excursion
673         (if entity ()
674           (setq entity
675                 (wl-folder-search-group-entity-by-name
676                  (wl-folder-get-realname (wl-match-buffer 3))
677                  wl-folder-entity)))
678         (let ((inhibit-read-only t)
679               (entities (list entity))
680               entity-stack err indent)
681           (while (and entities (not err))
682             (setq entity (wl-pop entities))
683             (cond
684              ((consp entity)
685               (wl-folder-close-entity entity)
686               (setcdr (assoc (car entity) wl-folder-group-alist) t)
687               (unless (wl-folder-buffer-search-group
688                        (wl-folder-get-petname (car entity)))
689                 (error "%s: not found group" (car entity)))
690               (setq indent (wl-match-buffer 1))
691               (if (eq 'access (cadr entity))
692                   (wl-folder-maybe-load-folder-list entity))
693               (beginning-of-line)
694               (setq err nil)
695               (save-excursion
696                 (condition-case errobj
697                     (wl-folder-update-newest indent entity)
698                   (quit
699                    (setq err t)
700                    (setcdr (assoc (car entity) wl-folder-group-alist) nil))
701                   (error
702                    (elmo-display-error errobj t)
703                    (ding)
704                    (setq err t)
705                    (setcdr (assoc (car entity) wl-folder-group-alist) nil)))
706                 (if (not err)
707                     (delete-region (save-excursion (beginning-of-line)
708                                                    (point))
709                                    (save-excursion (end-of-line)
710                                                    (+ 1 (point))))))
711               ;;
712               (and entities
713                    (wl-push entities entity-stack))
714               (setq entities (nth 2 entity))))
715             (unless entities
716               (setq entities (wl-pop entity-stack)))))
717         (set-buffer-modified-p nil)))
718      (t
719       (wl-folder-jump-to-current-entity)))))
720
721 (defun wl-folder-no-auto-check-folder-p (folder)
722   (if (stringp folder)
723       (if (catch 'found
724             (let ((li wl-auto-check-folder-list))
725               (while li
726                 (if (string-match (car li) folder)
727                     (throw 'found t))
728                 (setq li (cdr li)))))
729           nil
730         (catch 'found
731           (let ((li wl-auto-uncheck-folder-list))
732             (while li
733               (if (string-match (car li) folder)
734                   (throw 'found t))     ; no check!
735               (setq li (cdr li))))))))
736
737 (defsubst wl-folder-add-folder-info (pre-value value)
738   (list
739    (+ (or (nth 0 pre-value) 0) (or (nth 0 value) 0))
740    (+ (or (nth 1 pre-value) 0) (or (nth 1 value) 0))
741    (+ (or (nth 2 pre-value) 0) (or (nth 2 value) 0))))
742
743 (defun wl-folder-check-entity (entity &optional auto)
744   "Check unsync message number."
745   (let ((start-pos (point))
746         ret-val)
747     (run-hooks 'wl-folder-check-entity-pre-hook)
748     (if (and (consp entity)             ;; group entity
749              wl-folder-check-async)     ;; very fast
750         (setq ret-val (wl-folder-check-entity-async entity auto))
751       (save-excursion
752         (cond
753          ((consp entity)
754           (let ((flist (if auto
755                            (elmo-delete-if
756                             'wl-folder-no-auto-check-folder-p
757                             (nth 2 entity))
758                          (nth 2 entity)))
759                 (wl-folder-check-entity-pre-hook nil)
760                 (wl-folder-check-entity-hook nil)
761                 new unread all)
762             (while flist
763               (setq ret-val
764                     (wl-folder-add-folder-info
765                      ret-val
766                      (wl-folder-check-entity (car flist))))
767               (setq flist (cdr flist)))
768             ;(wl-folder-buffer-search-entity (car entity))
769             ;(wl-folder-update-line ret-val)
770             ))
771          ((and (stringp entity)
772                (elmo-folder-plugged-p entity))
773           (message "Checking \"%s\"" entity)
774           (setq ret-val (wl-folder-check-one-entity entity))
775           (goto-char start-pos)
776           (sit-for 0))
777          (t
778           (message "Uncheck(unplugged) \"%s\"" entity)))))
779     (if ret-val
780         (message "Checking \"%s\" is done."
781                  (if (consp entity) (car entity) entity)))
782     (run-hooks 'wl-folder-check-entity-hook)
783     ret-val))
784
785 ;; All contained folders are imap4 and persistent flag, then
786 ;; use server diff.
787 (defun wl-folder-use-server-diff-p (folder)
788   (let ((spec (elmo-folder-get-spec folder)))
789     (cond
790      ((eq (car spec) 'multi)
791       (let ((folders (cdr spec)))
792         (catch 'done
793           (while folders
794             (if (wl-folder-use-server-diff-p (car folders))
795                 (throw 'done t))
796             (setq folders (cdr folders)))
797           nil)))
798     ((eq (car spec) 'filter)
799      (wl-folder-use-server-diff-p (nth 2 spec)))
800     ((eq (car spec) 'imap4)
801      (and wl-folder-use-server-diff
802           (elmo-imap4-use-flag-p spec)))
803     (t nil))))
804
805 (defun wl-folder-check-one-entity (entity)
806   (let* ((elmo-use-server-diff (wl-folder-use-server-diff-p entity))
807          (nums (condition-case err
808                    (if (wl-string-match-member entity wl-strict-diff-folders)
809                        (elmo-strict-folder-diff entity)
810                      (elmo-folder-diff entity))
811                  (error
812                   ;; maybe not exist folder.
813                   (if (and (not (memq 'elmo-open-error
814                                       (get (car err) 'error-conditions)))
815                            (not (elmo-folder-exists-p entity)))
816                       (wl-folder-create-subr entity)
817                     (signal (car err) (cdr err))))))
818          unread unsync nomif)
819     (if (and (eq wl-folder-notify-deleted 'sync)
820              (car nums)
821              (or (> 0 (car nums)) (> 0 (cdr nums))))
822         (progn
823           (wl-folder-sync-entity entity)
824           (setq nums (elmo-folder-diff entity)))
825       (unless wl-folder-notify-deleted
826         (setq unsync (if (and (car nums) (> 0 (car nums))) 0 (car nums)))
827         (setq nomif (if (and (car nums) (> 0 (cdr nums))) 0 (cdr nums)))
828         (setq nums (cons unsync nomif)))
829       (wl-folder-entity-hashtb-set wl-folder-entity-hashtb entity
830                                    (list (car nums)
831                                          (setq
832                                           unread
833                                           (or
834                                            ;; If server diff, All unreads are
835                                            ;; treated as unsync.
836                                            (if elmo-use-server-diff 0)
837                                            (elmo-folder-get-info-unread entity)
838                                            (wl-summary-count-unread
839                                             (elmo-msgdb-mark-load
840                                              (elmo-msgdb-expand-path entity))
841                                             entity)))
842                                          (cdr nums))
843                                    (current-buffer)))
844     (setq wl-folder-info-alist-modified t)
845     (sit-for 0)
846     (list (if wl-folder-notify-deleted
847               (car nums)
848             (max (or (car nums) 0))) unread (cdr nums))))
849
850 (defun wl-folder-check-entity-async (entity &optional auto)
851   (let ((elmo-nntp-groups-async t)
852         (elist (if auto
853                    (elmo-delete-if
854                     'wl-folder-no-auto-check-folder-p
855                     (wl-folder-get-entity-list entity))
856                  (wl-folder-get-entity-list entity)))
857         (nntp-connection-keys nil)
858         folder spec-list local-elist net-elist server
859         ret-val)
860     (while elist
861       (if (not (elmo-folder-plugged-p (car elist)))
862           (message "Uncheck \"%s\"" (car elist))
863         (setq spec-list
864               (elmo-folder-get-primitive-spec-list (elmo-string (car elist))))
865         (cond ((assq 'nntp spec-list)
866                (wl-append net-elist (list (car elist)))
867                (while spec-list
868                  (when (eq (caar spec-list) 'nntp)
869                    (when (not (string= server (elmo-nntp-spec-hostname (car spec-list))))
870                      (setq server (elmo-nntp-spec-hostname (car spec-list)))
871                      (message "Checking on \"%s\"" server))
872                    (setq nntp-connection-keys
873                          (elmo-nntp-get-folders-info-prepare
874                           (car spec-list)
875                           nntp-connection-keys)))
876                  (setq spec-list (cdr spec-list))))
877               (t
878                (wl-append local-elist (list (car elist))))))
879       (setq elist (cdr elist)))
880     ;; check local entity at first
881     (while (setq folder (pop local-elist))
882       (if (not (elmo-folder-plugged-p folder))
883           (message "Uncheck \"%s\"" folder)
884         (message "Checking \"%s\"" folder)
885         (setq ret-val
886               (wl-folder-add-folder-info
887                ret-val
888                (wl-folder-check-one-entity folder)))
889         ;;(sit-for 0)
890         ))
891     ;; check network entity at last
892     (when net-elist
893       (elmo-nntp-get-folders-info nntp-connection-keys)
894       (while (setq folder (pop net-elist))
895         (if (not (elmo-folder-plugged-p folder))
896             (message "Uncheck \"%s\"" folder)
897           (message "Checking \"%s\"" folder)
898           (setq ret-val
899                 (wl-folder-add-folder-info
900                  ret-val
901                  (wl-folder-check-one-entity folder)))
902           ;;(sit-for 0)
903           )))
904     ret-val))
905
906 ;;
907 (defun wl-folder-resume-entity-hashtb-by-finfo (entity-hashtb info-alist)
908   "Resume unread info for entity alist."
909   (let (info)
910     (while info-alist
911       (setq info (nth 1 (car info-alist)))
912       (wl-folder-set-entity-info (caar info-alist)
913                                  (list (nth 2 info)(nth 3 info)(nth 1 info))
914                                  entity-hashtb)
915       (setq info-alist (cdr info-alist)))))
916
917 (defun wl-folder-move-path (path)
918   (let ((fp (if (consp path)
919                 path
920               ;; path is entity-id
921               (wl-folder-get-path wl-folder-entity path))))
922     (goto-char (point-min))
923     (while (and fp
924                 (not (eobp)))
925       (when (equal (car fp)
926                    (wl-folder-get-entity-from-buffer t))
927         (setq fp (cdr fp))
928         (setq wl-folder-buffer-cur-point (point)))
929       (forward-line 1))
930     (and wl-folder-buffer-cur-point
931          (goto-char wl-folder-buffer-cur-point))))
932
933 (defun wl-folder-set-current-entity-id (entity-id)
934   (let ((buf (get-buffer wl-folder-buffer-name)))
935     (if buf
936         (save-excursion
937           (set-buffer buf)
938           (setq wl-folder-buffer-cur-entity-id entity-id)
939           (setq wl-folder-buffer-cur-path (wl-folder-get-path wl-folder-entity
940                                                               entity-id))
941           (wl-highlight-folder-path wl-folder-buffer-cur-path)
942           (and wl-folder-move-cur-folder
943                wl-folder-buffer-cur-point
944                (goto-char wl-folder-buffer-cur-point))))
945     (if (eq (current-buffer) buf)
946         (and wl-folder-move-cur-folder
947              wl-folder-buffer-cur-point
948              (goto-char wl-folder-buffer-cur-point)))))
949
950 (defun wl-folder-check-current-entity ()
951   "Check folder at position.
952 If current line is group folder, check all sub entries."
953   (interactive)
954   (let* ((entity-name (wl-folder-get-entity-from-buffer))
955          (group (wl-folder-buffer-group-p))
956          (desktop (string= entity-name wl-folder-desktop-name)))
957     (when entity-name
958       (wl-folder-check-entity
959        (if group
960            (wl-folder-search-group-entity-by-name entity-name
961                                                   wl-folder-entity)
962          entity-name)
963        desktop))))
964
965 (defun wl-folder-sync-entity (entity &optional unread-only)
966   "Synchronize the msgdb of ENTITY."
967   (cond
968    ((consp entity)
969     (let ((flist (nth 2 entity)))
970       (while flist
971         (wl-folder-sync-entity (car flist) unread-only)
972         (setq flist (cdr flist)))))
973    ((stringp entity)
974     (let ((nums (wl-folder-get-entity-info entity))
975           (wl-summary-highlight (if (or (wl-summary-sticky-p entity)
976                                         (wl-summary-always-sticky-folder-p
977                                          entity))
978                                     wl-summary-highlight))
979           wl-auto-select-first new unread)
980       (setq new (or (car nums) 0))
981       (setq unread (or (cadr nums) 0))
982       (if (or (not unread-only)
983               (or (< 0 new) (< 0 unread)))
984           (save-window-excursion
985             (save-excursion
986               (wl-summary-goto-folder-subr entity
987                                            (wl-summary-get-sync-range entity)
988                                            nil nil nil t)
989               (wl-summary-exit))))))))
990
991 (defun wl-folder-sync-current-entity (&optional unread-only)
992   "Synchronize the folder at position.
993 If current line is group folder, check all subfolders."
994   (interactive "P")
995   (save-excursion
996     (let ((entity-name (wl-folder-get-entity-from-buffer))
997           (group (wl-folder-buffer-group-p)))
998       (when (and entity-name
999                  (y-or-n-p (format "Sync %s?" entity-name)))
1000         (wl-folder-sync-entity
1001          (if group
1002              (wl-folder-search-group-entity-by-name entity-name
1003                                                     wl-folder-entity)
1004            entity-name)
1005          unread-only)
1006         (message "Syncing %s is done!" entity-name)))))
1007
1008 (defun wl-folder-mark-as-read-all-entity (entity)
1009   "Mark as read all messages in the ENTITY."
1010   (cond
1011    ((consp entity)
1012     (let ((flist (nth 2 entity)))
1013       (while flist
1014         (wl-folder-mark-as-read-all-entity (car flist))
1015         (setq flist (cdr flist)))))
1016    ((stringp entity)
1017     (let ((nums (wl-folder-get-entity-info entity))
1018           (wl-summary-highlight (if (or (wl-summary-sticky-p entity)
1019                                         (wl-summary-always-sticky-folder-p
1020                                          entity))
1021                                     wl-summary-highlight))
1022           wl-auto-select-first new unread)
1023       (setq new (or (car nums) 0))
1024       (setq unread (or (cadr nums) 0))
1025       (if (or (< 0 new) (< 0 unread))
1026         (save-window-excursion
1027           (save-excursion
1028             (wl-summary-goto-folder-subr entity
1029                                          (wl-summary-get-sync-range entity)
1030                                          nil)
1031             (wl-summary-mark-as-read-all)
1032             (wl-summary-exit)))
1033         (sit-for 0))))))
1034
1035 (defun wl-folder-mark-as-read-all-current-entity ()
1036   "Mark as read all messages in the folder at position.
1037 If current line is group folder, all subfolders are marked."
1038   (interactive)
1039   (save-excursion
1040     (let ((entity-name (wl-folder-get-entity-from-buffer))
1041           (group (wl-folder-buffer-group-p))
1042           summary-buf)
1043       (when (and entity-name
1044                  (y-or-n-p (format "Mark all messages in %s as read?" entity-name)))
1045         (wl-folder-mark-as-read-all-entity
1046          (if group
1047              (wl-folder-search-group-entity-by-name entity-name
1048                                                     wl-folder-entity)
1049            entity-name))
1050         (message "All messages in %s are marked!" entity-name)))))
1051
1052 (defun wl-folder-check-region (beg end)
1053   (interactive "r")
1054   (goto-char beg)
1055   (beginning-of-line)
1056   (setq beg (point))
1057   (goto-char end)
1058   (beginning-of-line)
1059   (setq end (point))
1060   (goto-char beg)
1061   (let ((inhibit-read-only t)
1062         entity)
1063     (while (< (point) end)
1064       ;; normal folder entity
1065       (if (looking-at "^[\t ]*\\([^\\[]+\\):\\(.*\\)\n")
1066           (save-excursion
1067             (setq entity (wl-folder-get-entity-from-buffer))
1068             (if (not (elmo-folder-plugged-p entity))
1069                 (message "Uncheck %s" entity)
1070               (message "Checking %s" entity)
1071               (wl-folder-check-one-entity entity)
1072               (sit-for 0))))
1073       (forward-line 1)))
1074   (message ""))
1075
1076 (defun wl-folder-sync-region (beg end)
1077   (interactive "r")
1078   (goto-char beg)
1079   (beginning-of-line)
1080   (setq beg (point))
1081   (goto-char end)
1082   (end-of-line)
1083   (setq end (point))
1084   (goto-char beg)
1085   (while (< (point) end)
1086     ;; normal folder entity
1087     (if (looking-at "^[\t ]*\\([^\\[]+\\):\\(.*\\)\n")
1088         (save-excursion
1089           (let ((inhibit-read-only t)
1090                 entity)
1091             (setq entity (wl-folder-get-entity-from-buffer))
1092             (wl-folder-sync-entity entity)
1093             (message "Syncing %s is done!" entity)
1094             (sit-for 0))))
1095     (forward-line 1))
1096   (message ""))
1097
1098 (defun wl-folder-mark-as-read-all-region (beg end)
1099   (interactive "r")
1100   (goto-char beg)
1101   (beginning-of-line)
1102   (setq beg (point))
1103   (goto-char end)
1104   (end-of-line)
1105   (setq end (point))
1106   (goto-char beg)
1107   (while (< (point) end)
1108     ;; normal folder entity
1109     (if (looking-at "^[\t ]*\\([^\\[]+\\):\\(.*\\)\n")
1110         (save-excursion
1111           (let ((inhibit-read-only t)
1112                 entity)
1113             (setq entity (wl-folder-get-entity-from-buffer))
1114             (wl-folder-mark-as-read-all-entity entity)
1115             (message "All messages in %s are marked!" entity)
1116             (sit-for 0))))
1117     (forward-line 1))
1118   (message ""))
1119
1120 (defsubst wl-create-access-init-load-p (folder)
1121   (let ((no-load-regexp (when (and
1122                                (not wl-folder-init-load-access-folders)
1123                                wl-folder-init-no-load-access-folders)
1124                           (mapconcat 'identity
1125                                      wl-folder-init-no-load-access-folders
1126                                      "\\|")))
1127         (load-regexp (and wl-folder-init-load-access-folders
1128                           (mapconcat 'identity
1129                                      wl-folder-init-load-access-folders
1130                                      "\\|"))))
1131     (cond (load-regexp (string-match load-regexp folder))
1132           (t (not (and no-load-regexp
1133                        (string-match no-load-regexp folder)))))))
1134
1135 (defun wl-create-access-folder-entity (name)
1136   (let (flists flist)
1137     (when (wl-create-access-init-load-p name)
1138       (setq flists (elmo-msgdb-flist-load name)) ; load flist.
1139       (setq flist (car flists))
1140       (while flist
1141         (when (consp (car flist))
1142           (setcdr (cdar flist)
1143                   (wl-create-access-folder-entity (caar flist))))
1144         (setq flist (cdr flist)))
1145       flists)))
1146
1147 (defun wl-create-folder-entity-from-buffer ()
1148   "Create folder entity recursively."
1149   (cond
1150    ((looking-at "^[ \t]*$")             ; blank line
1151     (goto-char (+ 1(match-end 0)))
1152     'ignore)
1153    ((looking-at "^#.*$")                ; comment
1154     (goto-char (+ 1 (match-end 0)))
1155     'ignore)
1156    ((looking-at "^[\t ]*\\(.+\\)[\t ]*{[\t ]*$") ; group definition
1157     (let (name entity flist)
1158       (setq name (wl-match-buffer 1))
1159       (goto-char (+ 1 (match-end 0)))
1160       (while (setq entity (wl-create-folder-entity-from-buffer))
1161         (unless (eq entity 'ignore)
1162           (wl-append flist (list entity))))
1163       (if (looking-at "^[\t ]*}[\t ]*$") ; end of group
1164           (progn
1165             (goto-char (+ 1 (match-end 0)))
1166             (if (wl-string-assoc name wl-folder-petname-alist)
1167                 (error "%s already defined as petname" name))
1168             (list name 'group flist))
1169         (error "Syntax error in folder definition"))))
1170    ((looking-at "^[\t ]*\\([^\t \n]+\\)[\t ]*/$") ; access it!
1171     (let (name)
1172       (setq name (wl-match-buffer 1))
1173       (goto-char (+ 1 (match-end 0)))
1174 ;      (condition-case ()
1175 ;         (unwind-protect
1176 ;             (setq flist (elmo-list-folders name)))
1177 ;       (error (message "Access to folder %s failed." name)))
1178 ;;       (setq flist (elmo-msgdb-flist-load name)) ; load flist.
1179 ;;       (setq unsublist (nth 1 flist))
1180 ;;       (setq flist (car flist))
1181 ;;       (list name 'access flist unsublist)))
1182       (append (list name 'access) (wl-create-access-folder-entity name))))
1183    ;((looking-at "^[\t ]*\\([^\t \n}]+\\)[\t ]*\\(\"[^\"]*\"\\)?[\t ]*$") ; normal folder entity
1184    ((looking-at "^[\t ]*=[ \t]+\\([^\n]+\\)$"); petname definition
1185     (goto-char (+ 1 (match-end 0)))
1186     (let ((rest (elmo-match-buffer 1))
1187           petname)
1188       (when (string-match "\\(\"[^\"]*\"\\)[\t ]*$" rest)
1189         (setq petname (elmo-delete-char ?\" (elmo-match-string 1 rest)))
1190         (setq rest (substring rest 0 (match-beginning 0))))
1191       (when (string-match "^[\t ]*\\(.*[^\t ]+\\)[\t ]+$" rest)
1192         (wl-folder-append-petname (elmo-match-string 1 rest)
1193                                   petname))
1194       'ignore))
1195    ((looking-at "^[ \t]*}[ \t]*$") ; end of group
1196     nil)
1197    ((looking-at "^.*$") ; normal folder entity
1198     (goto-char (+ 1 (match-end 0)))
1199     (let ((rest (elmo-match-buffer 0))
1200           realname petname)
1201       (if (string-match "\\(\"[^\"]*\"\\)[\t ]*$" rest)
1202           (progn
1203             (setq petname (elmo-delete-char ?\" (elmo-match-string 1 rest)))
1204             (setq rest (substring rest 0 (match-beginning 0)))
1205             (when (string-match "^[\t ]*\\(.*[^\t ]+\\)[\t ]+$" rest)
1206               (wl-folder-append-petname
1207                (setq realname (elmo-match-string 1 rest))
1208                petname)
1209               realname))
1210         (if (string-match "^[\t ]*\\(.+\\)$" rest)
1211             (elmo-match-string 1 rest)
1212           rest))))))
1213
1214 (defun wl-folder-create-folder-entity ()
1215   "Create folder entries."
1216   (let ((tmp-buf (get-buffer-create " *wl-folder-tmp*"))
1217         entity ret-val)
1218     (condition-case ()
1219         (progn
1220           (with-current-buffer tmp-buf
1221             (erase-buffer)
1222             (insert-file-contents wl-folders-file)
1223             (goto-char (point-min))
1224             (while (and (not (eobp))
1225                         (setq entity (wl-create-folder-entity-from-buffer)))
1226               (unless (eq entity 'ignore)
1227                 (wl-append ret-val (list entity)))))
1228           (kill-buffer tmp-buf))
1229       (file-error nil))
1230     (setq ret-val (list wl-folder-desktop-name 'group ret-val))))
1231
1232 (defun wl-folder-entity-assign-id (entity &optional hashtb on-noid)
1233   (let ((hashtb (or hashtb
1234                     (setq wl-folder-entity-id-name-hashtb
1235                           (elmo-make-hash wl-folder-entity-id))))
1236         (entities (list entity))
1237         entity-stack)
1238     (while entities
1239       (setq entity (wl-pop entities))
1240       (cond
1241        ((consp entity)
1242         (when (not (and on-noid
1243                         (get-text-property 0
1244                                            'wl-folder-entity-id
1245                                            (car entity))))
1246           (put-text-property 0 (length (car entity))
1247                              'wl-folder-entity-id
1248                              wl-folder-entity-id
1249                              (car entity))
1250           (wl-folder-set-id-name wl-folder-entity-id
1251                                  (car entity) hashtb))
1252         (and entities
1253              (wl-push entities entity-stack))
1254         (setq entities (nth 2 entity)))
1255        ((stringp entity)
1256         (when (not (and on-noid
1257                         (get-text-property 0
1258                                            'wl-folder-entity-id
1259                                            entity)))
1260           (put-text-property 0 (length entity)
1261                              'wl-folder-entity-id
1262                              wl-folder-entity-id
1263                              entity)
1264           (wl-folder-set-id-name wl-folder-entity-id
1265                                  entity hashtb))))
1266       (setq wl-folder-entity-id (+ 1 wl-folder-entity-id))
1267       (unless entities
1268         (setq entities (wl-pop entity-stack))))))
1269
1270 (defun wl-folder-click (e)
1271   (interactive "e")
1272   (mouse-set-point e)
1273   (beginning-of-line)
1274   (save-excursion
1275     (wl-folder-jump-to-current-entity)))
1276
1277 (defun wl-folder-select-buffer (buffer)
1278   (let ((gbw (get-buffer-window buffer))
1279         ret-val)
1280     (if gbw
1281         (progn (select-window gbw)
1282                (setq ret-val t))
1283       (condition-case ()
1284           (unwind-protect
1285               (split-window-horizontally wl-folder-window-width)
1286             (other-window 1))
1287         (error nil)))
1288     (set-buffer buffer)
1289     (switch-to-buffer buffer)
1290     ret-val
1291     ))
1292
1293 (defun wl-folder-toggle-disp-summary (&optional arg folder)
1294   (interactive)
1295   (if (or (and folder (assoc folder wl-folder-group-alist))
1296           (and (interactive-p) (wl-folder-buffer-group-p)))
1297       (error "This command is not available on Group"))
1298   (beginning-of-line)
1299   (let (wl-auto-select-first)
1300     (cond
1301      ((eq arg 'on)
1302       (setq wl-folder-buffer-disp-summary t))
1303      ((eq arg 'off)
1304       (setq wl-folder-buffer-disp-summary nil)
1305       ;; hide wl-summary window.
1306       (let ((cur-buf (current-buffer))
1307             (summary-buffer (wl-summary-get-buffer folder)))
1308         (wl-folder-select-buffer summary-buffer)
1309         (delete-window)
1310         (select-window (get-buffer-window cur-buf))))
1311      (t
1312       (setq wl-folder-buffer-disp-summary
1313             (not wl-folder-buffer-disp-summary))
1314       (let ((cur-buf (current-buffer))
1315             folder-name)
1316         (when (looking-at "^[ ]*\\([^\\[].+\\):.*\n")
1317           (setq folder-name (wl-folder-get-entity-from-buffer))
1318           (if wl-folder-buffer-disp-summary
1319               (progn
1320                 (wl-folder-select-buffer
1321                  (wl-summary-get-buffer-create folder-name))
1322                 (unwind-protect
1323                     (wl-summary-goto-folder-subr folder-name 'no-sync nil)
1324                   (select-window (get-buffer-window cur-buf))))
1325             (wl-folder-select-buffer (wl-summary-get-buffer folder-name))
1326             (delete-window)
1327             (select-window (get-buffer-window cur-buf)))))))))
1328
1329 (defun wl-folder-prev-unsync ()
1330   "Move cursor to the previous unsync folder."
1331   (interactive)
1332   (let (start-point)
1333     (setq start-point (point))
1334     (beginning-of-line)
1335     (if (re-search-backward wl-folder-unsync-regexp nil t)
1336         (beginning-of-line)
1337       (goto-char start-point)
1338       (message "No more unsync folder"))))
1339
1340 (defun wl-folder-next-unsync (&optional plugged)
1341   "Move cursor to the next unsync."
1342   (interactive)
1343   (let (start-point entity)
1344     (setq start-point (point))
1345     (end-of-line)
1346     (if (catch 'found
1347           (while (re-search-forward wl-folder-unsync-regexp nil t)
1348             (if (or (wl-folder-buffer-group-p)
1349                     (not plugged)
1350                     (setq entity
1351                           (wl-folder-get-realname
1352                            (wl-folder-folder-name)))
1353                     (elmo-folder-plugged-p entity))
1354                 (throw 'found t))))
1355         (beginning-of-line)
1356       (goto-char start-point)
1357       (message "No more unsync folder"))))
1358
1359 (defun wl-folder-prev-unread (&optional group)
1360   "Move cursor to the previous unread folder."
1361   (interactive "P")
1362   (let (start-point)
1363     (setq start-point (point))
1364     (beginning-of-line)
1365     (if (re-search-backward (wl-folder-unread-regex group) nil t)
1366         (progn
1367           (beginning-of-line)
1368           (wl-folder-folder-name))
1369       (goto-char start-point)
1370       (message "No more unread folder")
1371       nil)))
1372
1373 (defun wl-folder-next-unread (&optional group)
1374   "Move cursor to the next unread folder."
1375   (interactive "P")
1376   (let (start-point)
1377     (setq start-point (point))
1378     (end-of-line)
1379     (if (re-search-forward (wl-folder-unread-regex group) nil t)
1380         (progn
1381           (beginning-of-line)
1382           (wl-folder-folder-name))
1383       (goto-char start-point)
1384       (message "No more unread folder")
1385       nil)))
1386
1387 (defun wl-folder-mode ()
1388   "Major mode for Wanderlust Folder.
1389 See info under Wanderlust for full documentation.
1390
1391 Special commands:
1392 \\{wl-folder-mode-map}
1393
1394 Entering Folder mode calls the value of `wl-folder-mode-hook'."
1395   (interactive)
1396   (setq major-mode 'wl-folder-mode)
1397   (setq mode-name "Folder")
1398   (use-local-map wl-folder-mode-map)
1399   (setq buffer-read-only t)
1400   (setq inhibit-read-only nil)
1401   (setq truncate-lines t)
1402   (setq wl-folder-buffer-cur-entity-id nil
1403         wl-folder-buffer-cur-path nil
1404         wl-folder-buffer-cur-point nil)
1405   (wl-mode-line-buffer-identification)
1406   (easy-menu-add wl-folder-mode-menu)
1407   ;; This hook may contain the functions `wl-folder-init-icons' and
1408   ;; `wl-setup-folder' for reasons of system internal to accord
1409   ;; facilities for the Emacs variants.
1410   (run-hooks 'wl-folder-mode-hook))
1411
1412 (defun wl-folder-append-petname (realname petname)
1413   (let (pentry)
1414     ;; check group name.
1415     (if (wl-folder-search-group-entity-by-name petname wl-folder-entity)
1416         (error "%s already defined as group name" petname))
1417     (when (setq pentry (wl-string-assoc realname wl-folder-petname-alist))
1418       (setq wl-folder-petname-alist
1419             (delete pentry wl-folder-petname-alist)))
1420     (wl-append wl-folder-petname-alist
1421                (list (cons realname petname)))))
1422
1423 (defun wl-folder (&optional arg)
1424   (interactive "P")
1425   (let (initialize)
1426 ;;; (delete-other-windows)
1427     (if (get-buffer wl-folder-buffer-name)
1428         (switch-to-buffer  wl-folder-buffer-name)
1429       (switch-to-buffer (get-buffer-create wl-folder-buffer-name))
1430       (wl-folder-mode)
1431       (wl-folder-init)
1432       (set-buffer wl-folder-buffer-name)
1433       (let ((inhibit-read-only t)
1434             (buffer-read-only nil))
1435         (erase-buffer)
1436         (setcdr (assoc (car wl-folder-entity) wl-folder-group-alist) t)
1437         (save-excursion
1438           (wl-folder-insert-entity " " wl-folder-entity)))
1439       (set-buffer-modified-p nil)
1440       ;(sit-for 0)
1441       (setq initialize t))
1442     initialize))
1443
1444 (defun wl-folder-auto-check ()
1445   "Check and update folders in `wl-auto-check-folder-name'."
1446   (interactive)
1447   (when (get-buffer wl-folder-buffer-name)
1448     (switch-to-buffer  wl-folder-buffer-name)
1449     (cond
1450      ((eq wl-auto-check-folder-name 'none))
1451      ((or (consp wl-auto-check-folder-name)
1452           (stringp wl-auto-check-folder-name))
1453       (let ((folder-list (if (consp wl-auto-check-folder-name)
1454                              wl-auto-check-folder-name
1455                            (list wl-auto-check-folder-name)))
1456             entity)
1457         (while folder-list
1458           (if (setq entity (wl-folder-search-entity-by-name
1459                             (car folder-list)
1460                             wl-folder-entity))
1461               (wl-folder-check-entity entity 'auto))
1462           (setq folder-list (cdr folder-list)))))
1463      (t
1464       (wl-folder-check-entity wl-folder-entity 'auto)))))
1465
1466 (defun wl-folder-set-folder-updated (name value)
1467   (save-excursion
1468     (let (buf)
1469       (if (setq buf (get-buffer wl-folder-buffer-name))
1470           (wl-folder-entity-hashtb-set
1471            wl-folder-entity-hashtb name value buf))
1472 ;;;   (elmo-folder-set-info-hashtb (elmo-string name)
1473 ;;;                                nil
1474 ;;;                                (nth 2 value)
1475 ;;;                                (nth 0 value)
1476 ;;;                                (nth 1 value))
1477       (setq wl-folder-info-alist-modified t))))
1478
1479 (defun wl-folder-calc-finfo (entity)
1480   ;; calcurate finfo without inserting.
1481   (let ((entities (list entity))
1482         entity-stack
1483         new unread all nums)
1484     (while entities
1485       (setq entity (wl-pop entities))
1486       (cond
1487        ((consp entity)
1488         (and entities
1489              (wl-push entities entity-stack))
1490         (setq entities (nth 2 entity)))
1491        ((stringp entity)
1492         (setq nums (wl-folder-get-entity-info entity))
1493         (setq new    (+ (or new 0) (or (nth 0 nums) 0)))
1494         (setq unread (+ (or unread 0)
1495                         (or (and (nth 0 nums)(nth 1 nums)
1496                                  (+ (nth 0 nums)(nth 1 nums))) 0)))
1497         (setq all    (+ (or all 0) (or (nth 2 nums) 0)))))
1498       (unless entities
1499         (setq entities (wl-pop entity-stack))))
1500     (list new unread all)))
1501
1502 (defsubst wl-folder-make-save-access-list (list)
1503   (mapcar '(lambda (x)
1504              (cond
1505               ((consp x)
1506                (list (elmo-string (car x)) 'access))
1507               (t
1508                (elmo-string x))))
1509           list))
1510
1511 (defun wl-folder-update-newest (indent entity)
1512   (let (ret-val new unread all)
1513     (cond
1514      ((consp entity)
1515       (let ((inhibit-read-only t)
1516             (buffer-read-only nil)
1517             (flist (nth 2 entity))
1518             (as-opened (cdr (assoc (car entity) wl-folder-group-alist)))
1519             beg
1520             )
1521         (setq beg (point))
1522         (if as-opened
1523             (let (update-flist flist-unsub new-flist removed group-name-end)
1524               (when (and (eq (cadr entity) 'access)
1525                          (elmo-folder-plugged-p (car entity)))
1526                 (message "Fetching folder entries...")
1527                 (when (setq new-flist
1528                             (elmo-list-folders
1529                              (elmo-string (car entity))
1530                              (wl-string-member
1531                               (car entity)
1532                               wl-folder-hierarchy-access-folders)))
1533                   (setq update-flist
1534                         (wl-folder-update-access-group entity new-flist))
1535                   (setq flist (nth 1 update-flist))
1536                   (when (car update-flist) ;; diff
1537                     (setq flist-unsub (nth 2 update-flist))
1538                     (setq removed (nth 3 update-flist))
1539                     (elmo-msgdb-flist-save
1540                      (car entity)
1541                      (list
1542                       (wl-folder-make-save-access-list flist)
1543                       (wl-folder-make-save-access-list flist-unsub)))
1544                     (wl-folder-entity-assign-id
1545                      entity
1546                      wl-folder-entity-id-name-hashtb
1547                      t)
1548                     (setq wl-folder-entity-hashtb
1549                           (wl-folder-create-entity-hashtb
1550                            entity
1551                            wl-folder-entity-hashtb
1552                            t))
1553                     (setq wl-folder-newsgroups-hashtb
1554                           (or
1555                            (wl-folder-create-newsgroups-hashtb
1556                             entity nil)
1557                            wl-folder-newsgroups-hashtb))))
1558                 (message "Fetching folder entries...done"))
1559               (wl-folder-insert-entity indent entity))))))))
1560
1561 (defun wl-folder-insert-entity (indent entity &optional onlygroup)
1562   (let (ret-val new unread all)
1563     (cond
1564      ((consp entity)
1565       (let ((inhibit-read-only t)
1566             (buffer-read-only nil)
1567             (flist (nth 2 entity))
1568             (as-opened (cdr (assoc (car entity) wl-folder-group-alist)))
1569             beg
1570             )
1571 ;;;     (insert indent "[" (if as-opened "-" "+") "]" (car entity) "\n")
1572 ;;;     (save-excursion (forward-line -1)
1573 ;;;                     (wl-highlight-folder-current-line))
1574         (setq beg (point))
1575         (if (and as-opened
1576                  (not onlygroup))
1577             (let (update-flist flist-unsub new-flist removed group-name-end)
1578 ;;;           (when (and (eq (cadr entity) 'access)
1579 ;;;                      newest)
1580 ;;;             (message "fetching folder entries...")
1581 ;;;             (when (setq new-flist
1582 ;;;                         (elmo-list-folders
1583 ;;;                          (elmo-string (car entity))
1584 ;;;                          (wl-string-member
1585 ;;;                           (car entity)
1586 ;;;                           wl-folder-hierarchy-access-folders)
1587 ;;;                          ))
1588 ;;;               (setq update-flist
1589 ;;;                     (wl-folder-update-access-group entity new-flist))
1590 ;;;               (setq flist (nth 1 update-flist))
1591 ;;;               (when (car update-flist) ;; diff
1592 ;;;                 (setq flist-unsub (nth 2 update-flist))
1593 ;;;                 (setq removed (nth 3 update-flist))
1594 ;;;                 (elmo-msgdb-flist-save
1595 ;;;                  (car entity)
1596 ;;;                  (list
1597 ;;;                   (wl-folder-make-save-access-list flist)
1598 ;;;                   (wl-folder-make-save-access-list flist-unsub)))
1599 ;;;                 ;;
1600 ;;;                 ;; reconstruct wl-folder-entity-id-name-hashtb and
1601 ;;;                 ;;           wl-folder-entity-hashtb
1602 ;;;                 ;;
1603 ;;;                 (wl-folder-entity-assign-id
1604 ;;;                  entity
1605 ;;;                  wl-folder-entity-id-name-hashtb
1606 ;;;                  t)
1607 ;;;                 (setq wl-folder-entity-hashtb
1608 ;;;                       (wl-folder-create-entity-hashtb
1609 ;;;                        entity
1610 ;;;                        wl-folder-entity-hashtb
1611 ;;;                        t))
1612 ;;;                 (setq wl-folder-newsgroups-hashtb
1613 ;;;                       (or
1614 ;;;                        (wl-folder-create-newsgroups-hashtb
1615 ;;;                         entity nil)
1616 ;;;                        wl-folder-newsgroups-hashtb))))
1617 ;;;             (message "fetching folder entries...done"))
1618               (insert indent "[" (if as-opened "-" "+") "]"
1619                       (wl-folder-get-petname (car entity)))
1620               (setq group-name-end (point))
1621               (insert ":0/0/0\n")
1622               (put-text-property beg (point) 'wl-folder-entity-id
1623                                  (get-text-property 0 'wl-folder-entity-id
1624                                                     (car entity)))
1625               (when removed
1626                 (setq beg (point))
1627                 (while removed
1628                   (insert indent "  "
1629                           wl-folder-removed-mark
1630                           (if (listp (car removed))
1631                               (concat "[+]" (caar removed))
1632                             (car removed))
1633                           "\n")
1634                   (save-excursion (forward-line -1)
1635                                   (wl-highlight-folder-current-line))
1636                   (setq removed (cdr removed)))
1637                 (remove-text-properties beg (point) '(wl-folder-entity-id)))
1638               (let* ((len (length flist))
1639                      (mes (> len 100))
1640                      (i 0))
1641                 (while flist
1642                   (setq ret-val
1643                         (wl-folder-insert-entity
1644                          (concat indent "  ") (car flist)))
1645                   (setq new    (+ (or new 0) (or (nth 0 ret-val) 0)))
1646                   (setq unread (+ (or unread 0) (or (nth 1 ret-val) 0)))
1647                   (setq all    (+ (or all 0) (or (nth 2 ret-val) 0)))
1648                   (when (and mes
1649                              (> len elmo-display-progress-threshold))
1650                     (setq i (1+ i))
1651                     (elmo-display-progress
1652                      'wl-folder-insert-entity "Inserting group %s..."
1653                      (/ (* i 100) len) (car entity)))
1654                   (setq flist (cdr flist))))
1655               (save-excursion
1656                 (goto-char group-name-end)
1657                 (delete-region (point) (save-excursion (end-of-line)
1658                                                        (point)))
1659                 (insert (format ":%d/%d/%d" (or new 0)
1660                                 (or unread 0) (or all 0)))
1661                 (setq ret-val (list new unread all))
1662                 (wl-highlight-folder-current-line ret-val)))
1663           (setq ret-val (wl-folder-calc-finfo entity))
1664           (insert indent "[" (if as-opened "-" "+") "]"
1665                   (wl-folder-get-petname (car entity))
1666                   (format ":%d/%d/%d"
1667                           (or (nth 0 ret-val) 0)
1668                           (or (nth 1 ret-val) 0)
1669                           (or (nth 2 ret-val) 0))
1670                   "\n")
1671           (put-text-property beg (point) 'wl-folder-entity-id
1672                              (get-text-property 0 'wl-folder-entity-id
1673                                                 (car entity)))
1674           (save-excursion (forward-line -1)
1675                           (wl-highlight-folder-current-line ret-val)))))
1676      ((stringp entity)
1677       (let* ((inhibit-read-only t)
1678              (buffer-read-only nil)
1679              (nums (wl-folder-get-entity-info entity))
1680              beg)
1681         (setq beg (point))
1682         (insert indent (wl-folder-get-petname entity)
1683                 (format ":%s/%s/%s\n"
1684                         (or (setq new (nth 0 nums)) "*")
1685                         (or (setq unread (and (nth 0 nums)(nth 1 nums)
1686                                               (+ (nth 0 nums)(nth 1 nums))))
1687                             "*")
1688                         (or (setq all (nth 2 nums)) "*")))
1689         (put-text-property beg (point) 'wl-folder-entity-id
1690                            (get-text-property 0 'wl-folder-entity-id entity))
1691         (save-excursion (forward-line -1)
1692                         (wl-highlight-folder-current-line nums))
1693         (setq ret-val (list new unread all)))))
1694     (set-buffer-modified-p nil)
1695     ret-val))
1696
1697 (defun wl-folder-check-all ()
1698   (interactive)
1699   (wl-folder-check-entity wl-folder-entity))
1700
1701 (defun wl-folder-entity-hashtb-set (entity-hashtb name value buffer)
1702   (let (cur-val
1703         (new-diff 0)
1704         (unread-diff 0)
1705         (all-diff 0)
1706         diffs
1707         entity-list)
1708     (setq cur-val (wl-folder-get-entity-info name entity-hashtb))
1709     (setq new-diff    (- (or (nth 0 value) 0) (or (nth 0 cur-val) 0)))
1710     (setq unread-diff
1711           (+ new-diff
1712              (- (or (nth 1 value) 0) (or (nth 1 cur-val) 0))))
1713     (setq all-diff    (- (or (nth 2 value) 0) (or (nth 2 cur-val) 0)))
1714     (setq diffs (list new-diff unread-diff all-diff))
1715     (unless (and (nth 0 cur-val)
1716                  (equal diffs '(0 0 0)))
1717       (wl-folder-set-entity-info name value entity-hashtb)
1718       (save-match-data
1719         (save-excursion
1720           (set-buffer buffer)
1721           (setq entity-list (wl-folder-search-entity-list-by-name
1722                              name wl-folder-entity))
1723           (while entity-list
1724             (wl-folder-update-group (car entity-list) diffs)
1725             (setq entity-list (cdr entity-list)))
1726           (goto-char (point-min))
1727           (while (wl-folder-buffer-search-entity name)
1728             (wl-folder-update-line value)))))))
1729
1730 (defun wl-folder-update-unread (folder unread)
1731   (save-window-excursion
1732     (let ((buf (get-buffer wl-folder-buffer-name))
1733           cur-unread
1734           (unread-diff 0)
1735           ;;(fld (elmo-string folder))
1736           value newvalue entity-list)
1737 ;;; Update folder-info
1738 ;;;    (elmo-folder-set-info-hashtb fld nil nil nil unread)
1739       (setq cur-unread (or (nth 1 (wl-folder-get-entity-info folder)) 0))
1740       (setq unread-diff (- (or unread 0) cur-unread))
1741       (setq value (wl-folder-get-entity-info folder))
1742
1743       (setq newvalue (list (nth 0 value)
1744                            unread
1745                            (nth 2 value)))
1746       (wl-folder-set-entity-info folder newvalue)
1747       (setq wl-folder-info-alist-modified t)
1748       (when (and buf
1749                  (not (eq unread-diff 0)))
1750         (save-match-data
1751           (save-excursion
1752             (set-buffer buf)
1753             (save-excursion
1754               (setq entity-list (wl-folder-search-entity-list-by-name
1755                                  folder wl-folder-entity))
1756               (while entity-list
1757                 (wl-folder-update-group (car entity-list) (list 0
1758                                                                 unread-diff
1759                                                                 0))
1760                 (setq entity-list (cdr entity-list)))
1761               (goto-char (point-min))
1762               (while (wl-folder-buffer-search-entity folder)
1763                 (wl-folder-update-line newvalue)))))))))
1764
1765 (defun wl-folder-create-entity-hashtb (entity &optional hashtb reconst)
1766   (let ((hashtb (or hashtb (elmo-make-hash wl-folder-entity-id)))
1767         (entities (list entity))
1768         entity-stack)
1769     (while entities
1770       (setq entity (wl-pop entities))
1771       (cond
1772        ((consp entity)
1773         (and entities
1774              (wl-push entities entity-stack))
1775         (setq entities (nth 2 entity)))
1776        ((stringp entity)
1777         (when (not (and reconst
1778                         (wl-folder-get-entity-info entity)))
1779           (wl-folder-set-entity-info entity
1780                                      nil
1781                                      hashtb))))
1782       (unless entities
1783         (setq entities (wl-pop entity-stack))))
1784     hashtb))
1785
1786 ;; Unsync number is reserved.
1787 ;;(defun wl-folder-reconstruct-entity-hashtb (entity &optional hashtb id-name)
1788 ;;  (let* ((hashtb (or hashtb (elmo-make-hash wl-folder-entity-id)))
1789 ;;       (entities (list entity))
1790 ;;       entity-stack)
1791 ;;    (while entities
1792 ;;      (setq entity (wl-pop entities))
1793 ;;      (cond
1794 ;;       ((consp entity)
1795 ;;      (if id-name
1796 ;;          (wl-folder-set-id-name (wl-folder-get-entity-id (car entity))
1797 ;;                                 (car entity)))
1798 ;;      (and entities
1799 ;;           (wl-push entities entity-stack))
1800 ;;      (setq entities (nth 2 entity))
1801 ;;      )
1802 ;;       ((stringp entity)
1803 ;;      (wl-folder-set-entity-info entity
1804 ;;                                 (wl-folder-get-entity-info entity)
1805 ;;                                 hashtb)
1806 ;;      (if id-name
1807 ;;          (wl-folder-set-id-name (wl-folder-get-entity-id entity)
1808 ;;                                 entity))))
1809 ;;      (unless entities
1810 ;;      (setq entities (wl-pop entity-stack))))
1811 ;;    hashtb))
1812
1813 (defun wl-folder-create-newsgroups-from-nntp-access2 (entity)
1814   (let ((flist (nth 2 entity))
1815         folders)
1816     (and
1817      (setq folders
1818            (delq
1819             nil
1820             (mapcar
1821              '(lambda (fld)
1822                 (if (consp fld)
1823                     (wl-folder-create-newsgroups-from-nntp-access2 fld)
1824                   (nth 1 (elmo-folder-get-spec fld))))
1825              flist)))
1826      (elmo-nntp-make-groups-hashtb folders 1024))
1827     nil))
1828
1829 (defun wl-folder-create-newsgroups-from-nntp-access (entity)
1830   (let ((flist (nth 2 entity))
1831         folders)
1832     (while flist
1833       (wl-append folders
1834                  (cond
1835                   ((consp (car flist))
1836                    (wl-folder-create-newsgroups-from-nntp-access (car flist)))
1837                   (t
1838                    (list (nth 1 (elmo-folder-get-spec (car flist)))))))
1839       (setq flist (cdr flist)))
1840     folders))
1841
1842 (defun wl-folder-create-newsgroups-hashtb (entity &optional is-list info)
1843   (let ((entities (if is-list entity (list entity)))
1844         entity-stack spec-list folders fld make-hashtb)
1845     (and info (message "Creating newsgroups..."))
1846     (while entities
1847       (setq entity (wl-pop entities))
1848       (cond
1849        ((consp entity)
1850         (if (eq (nth 1 entity) 'access)
1851             (when (eq (elmo-folder-get-type (car entity)) 'nntp)
1852               (wl-append folders
1853                          (wl-folder-create-newsgroups-from-nntp-access entity))
1854               (setq make-hashtb t))
1855           (and entities
1856                (wl-push entities entity-stack))
1857           (setq entities (nth 2 entity))))
1858        ((stringp entity)
1859         (setq spec-list (elmo-folder-get-primitive-spec-list entity))
1860         (while spec-list
1861           (when (and (eq (caar spec-list) 'nntp)
1862                      (setq fld (nth 1 (car spec-list))))
1863             (wl-append folders (list (elmo-string fld))))
1864           (setq spec-list (cdr spec-list)))))
1865       (unless entities
1866         (setq entities (wl-pop entity-stack))))
1867     (and info (message "Creating newsgroups...done"))
1868     (if (or folders make-hashtb)
1869         (elmo-nntp-make-groups-hashtb folders))))
1870
1871 (defun wl-folder-get-path (entity target-id &optional string)
1872   (let ((entities (list entity))
1873         entity-stack result-path)
1874     (reverse
1875      (catch 'done
1876        (while entities
1877          (setq entity (wl-pop entities))
1878          (cond
1879           ((consp entity)
1880            (if (and (or (not string) (string= string (car entity)))
1881                     ;; don't use eq, `id' is string on Nemacs.
1882                     (equal target-id (wl-folder-get-entity-id (car entity))))
1883                (throw 'done
1884                       (wl-push target-id result-path))
1885              (wl-push (wl-folder-get-entity-id (car entity)) result-path))
1886            (wl-push entities entity-stack)
1887            (setq entities (nth 2 entity)))
1888           ((stringp entity)
1889            (if (and (or (not string) (string= string entity))
1890                     ;; don't use eq, `id' is string on Nemacs.
1891                     (equal target-id (wl-folder-get-entity-id entity)))
1892                (throw 'done
1893                       (wl-push target-id result-path)))))
1894          (unless entities
1895            (while (and entity-stack
1896                        (not entities))
1897              (setq result-path (cdr result-path))
1898              (setq entities (wl-pop entity-stack)))))))))
1899
1900 (defun wl-folder-create-group-alist (entity)
1901   (if (consp entity)
1902       (let ((flist (nth 2 entity))
1903             (cur-alist (list (cons (car entity) nil)))
1904              append-alist)
1905         (while flist
1906           (if (consp (car flist))
1907               (wl-append append-alist
1908                          (wl-folder-create-group-alist (car flist))))
1909           (setq flist (cdr flist)))
1910         (append cur-alist append-alist))))
1911
1912 (defun wl-folder-init-info-hashtb ()
1913   (let ((info-alist (and wl-folder-info-save
1914                          (elmo-msgdb-finfo-load))))
1915     (elmo-folder-info-make-hashtb
1916      info-alist
1917      wl-folder-entity-hashtb)))
1918 ;;; (wl-folder-resume-entity-hashtb-by-finfo
1919 ;;;  wl-folder-entity-hashtb
1920 ;;;  info-alist)))
1921
1922 (defun wl-folder-cleanup-variables ()
1923   (setq wl-folder-entity nil
1924         wl-folder-entity-hashtb nil
1925         wl-folder-entity-id-name-hashtb nil
1926         wl-folder-group-alist nil
1927         wl-folder-petname-alist nil
1928         wl-folder-newsgroups-hashtb nil
1929         wl-fldmgr-cut-entity-list nil
1930         wl-fldmgr-modified nil
1931         wl-fldmgr-modified-access-list nil
1932         wl-score-cache nil
1933         ))
1934
1935 (defun wl-make-plugged-alist ()
1936   (let ((entity-list (wl-folder-get-entity-list wl-folder-entity))
1937         (add (not wl-reset-plugged-alist)))
1938     (while entity-list
1939       (elmo-folder-set-plugged
1940        (elmo-string (car entity-list)) wl-plugged add)
1941       (setq entity-list (cdr entity-list)))
1942     ;; smtp posting server
1943     (when wl-smtp-posting-server
1944       (elmo-set-plugged wl-plugged
1945                         wl-smtp-posting-server  ; server
1946                         (or (and (boundp 'smtp-service) smtp-service)
1947                             "smtp")     ; port
1948                         nil nil "smtp" add))
1949     ;; nntp posting server
1950     (when wl-nntp-posting-server
1951       (elmo-set-plugged wl-plugged
1952                         wl-nntp-posting-server
1953                         elmo-default-nntp-port
1954                         nil nil "nntp" add))
1955     (run-hooks 'wl-make-plugged-hook)))
1956
1957 (defvar wl-folder-init-func 'wl-local-folder-init)
1958
1959 (defun wl-folder-init ()
1960   "Call `wl-folder-init-func' function."
1961   (interactive)
1962   (funcall wl-folder-init-func))
1963
1964 (defun wl-local-folder-init ()
1965   "Initialize local folder."
1966   (message "Initializing folder...")
1967   (save-excursion
1968     (set-buffer wl-folder-buffer-name)
1969     (let ((entity (wl-folder-create-folder-entity))
1970           (inhibit-read-only t))
1971       (setq wl-folder-entity entity)
1972       (setq wl-folder-entity-id 0)
1973       (wl-folder-entity-assign-id wl-folder-entity)
1974       (setq wl-folder-entity-hashtb
1975             (wl-folder-create-entity-hashtb entity))
1976       (setq wl-folder-group-alist
1977             (wl-folder-create-group-alist entity))
1978       (setq wl-folder-newsgroups-hashtb
1979             (wl-folder-create-newsgroups-hashtb wl-folder-entity))
1980       (wl-folder-init-info-hashtb)))
1981   (message "Initializing folder...done"))
1982
1983 (defun wl-folder-get-realname (petname)
1984   (or (car
1985        (wl-string-rassoc
1986         petname
1987         wl-folder-petname-alist))
1988       petname))
1989
1990 (defun wl-folder-get-petname (folder)
1991   (or (cdr
1992        (wl-string-assoc
1993         folder
1994         wl-folder-petname-alist))
1995       folder))
1996
1997 (defun wl-folder-get-entity-with-petname ()
1998   (let ((alist wl-folder-petname-alist)
1999         (hashtb (copy-sequence wl-folder-entity-hashtb)))
2000     (while alist
2001       (wl-folder-set-entity-info (cdar alist) nil hashtb)
2002       (setq alist (cdr alist)))
2003     hashtb))
2004
2005 (defun wl-folder-get-newsgroups (folder)
2006   "Return Newsgroups field value string for FOLDER newsgroup.
2007 If FOLDER is multi, return comma separated string (cross post)."
2008   (let ((flist (elmo-folder-get-primitive-folder-list folder)) ; multi
2009         newsgroups fld ret)
2010     (while (setq fld (car flist))
2011       (if (setq ret
2012                 (cond ((eq 'nntp (elmo-folder-get-type fld))
2013                        (nth 1 (elmo-folder-get-spec fld)))
2014                       ((eq 'localnews (elmo-folder-get-type fld))
2015                        (elmo-replace-in-string
2016                         (nth 1 (elmo-folder-get-spec fld)) "/" "\\."))))
2017           ;; append newsgroup
2018           (setq newsgroups (if (stringp newsgroups)
2019                                (concat newsgroups "," ret)
2020                              ret)))
2021       (setq flist (cdr flist)))
2022     (list nil nil newsgroups)))
2023
2024 (defun wl-folder-guess-mailing-list-by-refile-rule (folder)
2025   "Return ML address guess by FOLDER.
2026 Use `wl-subscribed-mailing-list' and `wl-refile-rule-alist'.
2027 Don't care multi."
2028   (setq folder (car (elmo-folder-get-primitive-folder-list folder)))
2029   (unless (memq (elmo-folder-get-type folder)
2030                 '(localnews nntp))
2031     (let ((rules wl-refile-rule-alist)
2032           mladdress tokey toalist histkey)
2033       (while rules
2034         (if (or (and (stringp (car (car rules)))
2035                      (string-match "[Tt]o" (car (car rules))))
2036                 (and (listp (car (car rules)))
2037                      (elmo-string-matched-member "to" (car (car rules))
2038                                                  'case-ignore)))
2039             (setq toalist (append toalist (cdr (car rules)))))
2040         (setq rules (cdr rules)))
2041       (setq tokey (car (rassoc folder toalist)))
2042 ;;;     (setq histkey (car (rassoc folder wl-refile-alist)))
2043       ;; case-ignore search `wl-subscribed-mailing-list'
2044       (if (stringp tokey)
2045           (list
2046            (elmo-string-matched-member tokey wl-subscribed-mailing-list t)
2047            nil nil)
2048         nil))))
2049
2050 (defun wl-folder-guess-mailing-list-by-folder-name (folder)
2051   "Return ML address guess by FOLDER name's last hierarchy.
2052 Use `wl-subscribed-mailing-list'.  Don't care multi."
2053   (setq folder (car (elmo-folder-get-primitive-folder-list folder)))
2054   (when (memq (elmo-folder-get-type folder)
2055               '(localdir imap4 maildir))
2056     (let (key mladdress)
2057       (when (string-match "[^\\./]+$" folder)
2058         (setq key (concat "^" (substring folder (match-beginning 0)) "@"))
2059         (setq mladdress
2060               (elmo-string-matched-member
2061                key wl-subscribed-mailing-list 'case-ignore))
2062         (if (stringp mladdress)
2063             (list mladdress nil nil)
2064           nil)))))
2065
2066 (defun wl-folder-update-diff-line (diffs)
2067   (let ((inhibit-read-only t)
2068         (buffer-read-only nil)
2069         cur-new new-new
2070         cur-unread new-unread
2071         cur-all new-all
2072         id)
2073     (save-excursion
2074       (beginning-of-line)
2075       (setq id (get-text-property (point) 'wl-folder-entity-id))
2076       (when (looking-at "^[ ]*\\(.*\\):\\([0-9\\*-]*\\)/\\([0-9\\*-]*\\)/\\([0-9\\*]*\\)")
2077         ;;(looking-at "^[ ]*\\([^\\[].+\\):\\([0-9\\*-]*/[0-9\\*-]*/[0-9\\*]*\\)")
2078         (setq cur-new (string-to-int
2079                        (wl-match-buffer 2)))
2080         (setq cur-unread (string-to-int
2081                           (wl-match-buffer 3)))
2082         (setq cur-all (string-to-int
2083                        (wl-match-buffer 4)))
2084         (delete-region (match-beginning 2)
2085                        (match-end 4))
2086         (goto-char (match-beginning 2))
2087         (insert (format "%s/%s/%s"
2088                         (setq new-new (+ cur-new (nth 0 diffs)))
2089                         (setq new-unread (+ cur-unread (nth 1 diffs)))
2090                         (setq new-all (+ cur-all (nth 2 diffs)))))
2091         (put-text-property (match-beginning 2) (point)
2092                            'wl-folder-entity-id id)
2093         (if wl-use-highlight-mouse-line
2094             (put-text-property (match-beginning 2) (point)
2095                                'mouse-face 'highlight))
2096         (wl-highlight-folder-group-line (list new-new new-unread new-all))
2097         (setq buffer-read-only t)
2098         (set-buffer-modified-p nil)))))
2099
2100 (defun wl-folder-update-line (nums &optional is-group)
2101   (let ((inhibit-read-only t)
2102         (buffer-read-only nil)
2103         id)
2104     (save-excursion
2105       (beginning-of-line)
2106       (setq id (get-text-property (point) 'wl-folder-entity-id))
2107       (if (looking-at "^[ ]*\\(.*\\):\\([0-9\\*-]*/[0-9\\*-]*/[0-9\\*]*\\)")
2108 ;;;       (looking-at "^[ ]*\\([^\\[].+\\):\\([0-9\\*-]*/[0-9\\*-]*/[0-9\\*]*\\)")
2109           (progn
2110             (delete-region (match-beginning 2)
2111                            (match-end 2))
2112             (goto-char (match-beginning 2))
2113             (insert (format "%s/%s/%s"
2114                             (or (nth 0 nums) "*")
2115                             (or (and (nth 0 nums)(nth 1 nums)
2116                                      (+ (nth 0 nums)(nth 1 nums)))
2117                                 "*")
2118                             (or (nth 2 nums) "*")))
2119             (put-text-property (match-beginning 2) (point)
2120                                'wl-folder-entity-id id)
2121             (if is-group
2122                 ;; update only colors
2123                 (wl-highlight-folder-group-line nums)
2124               (wl-highlight-folder-current-line nums))
2125             (set-buffer-modified-p nil))))))
2126
2127 (defun wl-folder-goto-folder (&optional arg)
2128   (interactive "P")
2129   (wl-folder-goto-folder-subr nil arg))
2130
2131 (defun wl-folder-goto-folder-subr (&optional folder sticky)
2132   (beginning-of-line)
2133   (let (summary-buf fld-name entity id error-selecting)
2134 ;;; (setq fld-name (wl-folder-get-entity-from-buffer))
2135 ;;; (if (or (null fld-name)
2136 ;;;         (assoc fld-name wl-folder-group-alist))
2137     (setq fld-name wl-default-folder)
2138     (setq fld-name (or folder
2139                        (wl-summary-read-folder fld-name)))
2140     (if (and (setq entity
2141                    (wl-folder-search-entity-by-name fld-name
2142                                                     wl-folder-entity
2143                                                     'folder))
2144              (setq id (wl-folder-get-entity-id entity)))
2145         (wl-folder-set-current-entity-id id))
2146     (setq summary-buf (wl-summary-get-buffer-create fld-name sticky))
2147     (if wl-stay-folder-window
2148         (wl-folder-select-buffer summary-buf)
2149       (if (and summary-buf
2150                (get-buffer-window summary-buf))
2151           (delete-window)))
2152     (wl-summary-goto-folder-subr fld-name
2153                                  (wl-summary-get-sync-range fld-name)
2154                                  nil sticky t)))
2155
2156 (defun wl-folder-suspend ()
2157   (interactive)
2158   (run-hooks 'wl-folder-suspend-hook)
2159   (wl-folder-info-save)
2160   (wl-crosspost-alist-save)
2161   (wl-kill-buffers
2162    (format "^\\(%s\\)$"
2163            (mapconcat 'identity
2164                       (list (format "%s\\(:.*\\)?"
2165                                     (default-value 'wl-message-buf-name))
2166                             wl-original-buf-name)
2167                       "\\|")))
2168   (if (fboundp 'mmelmo-cleanup-entity-buffers)
2169       (mmelmo-cleanup-entity-buffers))
2170   (bury-buffer wl-folder-buffer-name)
2171   (delete-windows-on wl-folder-buffer-name t))
2172
2173 (defun wl-folder-info-save ()
2174   (when (and wl-folder-info-save
2175              wl-folder-info-alist-modified)
2176     (let ((entities (list wl-folder-entity))
2177           entity entity-stack info-alist info)
2178       (while entities
2179         (setq entity (wl-pop entities))
2180         (cond
2181          ((consp entity)
2182           (and entities
2183                (wl-push entities entity-stack))
2184           (setq entities (nth 2 entity)))
2185          ((stringp entity)
2186           (when (and (setq info (elmo-folder-get-info entity))
2187                      (not (equal info '(nil))))
2188             (wl-append info-alist (list (list (elmo-string entity)
2189                                               (list (nth 3 info)  ;; max
2190                                                     (nth 2 info)  ;; length
2191                                                     (nth 0 info)  ;; new
2192                                                     (nth 1 info)) ;; unread
2193                                               ))))))
2194         (unless entities
2195           (setq entities (wl-pop entity-stack))))
2196       (elmo-msgdb-finfo-save info-alist)
2197       (setq wl-folder-info-alist-modified nil))))
2198
2199 (defun wl-folder-goto-first-unread-folder (&optional arg)
2200   (interactive "P")
2201   (let ((entities (list wl-folder-entity))
2202         entity entity-stack ret-val
2203         first-entity finfo)
2204     (setq first-entity
2205           (catch 'done
2206             (while entities
2207               (setq entity (wl-pop entities))
2208               (cond
2209                ((consp entity)
2210                 (and entities
2211                      (wl-push entities entity-stack))
2212                 (setq entities (nth 2 entity)))
2213                ((stringp entity)
2214                 (if (and (setq finfo (wl-folder-get-entity-info entity))
2215                          (and (nth 0 finfo)(nth 1 finfo))
2216                          (> (+ (nth 0 finfo)(nth 1 finfo)) 0))
2217                     (throw 'done entity))
2218                 (wl-append ret-val (list entity))))
2219               (unless entities
2220                 (setq entities (wl-pop entity-stack))))))
2221     (if first-entity
2222         (progn
2223           (when arg
2224             (wl-folder-jump-folder first-entity)
2225             (sit-for 0))
2226           (wl-folder-goto-folder-subr first-entity))
2227       (message "No unread folder"))))
2228
2229 (defun wl-folder-jump-folder (&optional fld-name noopen)
2230   (interactive)
2231   (if (not fld-name)
2232       (setq fld-name (wl-summary-read-folder wl-default-folder)))
2233   (goto-char (point-min))
2234   (if (not noopen)
2235       (wl-folder-open-folder fld-name))
2236   (and (wl-folder-buffer-search-entity fld-name)
2237        (beginning-of-line)))
2238
2239 (defun wl-folder-get-entity-list (entity)
2240   (let ((entities (list entity))
2241         entity-stack ret-val)
2242     (while entities
2243       (setq entity (wl-pop entities))
2244       (cond
2245        ((consp entity)
2246         (and entities
2247              (wl-push entities entity-stack))
2248         (setq entities (nth 2 entity)))
2249        ((stringp entity)
2250         (wl-append ret-val (list entity))))
2251       (unless entities
2252         (setq entities (wl-pop entity-stack))))
2253     ret-val))
2254
2255 (defun wl-folder-open-unread-folder (entity)
2256   (save-excursion
2257     (let ((alist (wl-folder-get-entity-list entity))
2258           (unread 0)
2259           finfo path-list path id)
2260       (while alist
2261         (when (and (setq finfo (wl-folder-get-entity-info (car alist)))
2262                    (nth 0 finfo) (nth 1 finfo)
2263                    (> (+ (nth 0 finfo)(nth 1 finfo)) 0))
2264           (setq unread (+ unread (+ (nth 0 finfo)(nth 1 finfo))))
2265           (setq id (wl-folder-get-entity-id (car alist)))
2266           (setq path (delete id (wl-folder-get-path
2267                                  wl-folder-entity
2268                                  id
2269                                  (car alist))))
2270           (if (not (member path path-list))
2271               (wl-append path-list (list path))))
2272         (setq alist (cdr alist)))
2273       (while path-list
2274         (wl-folder-open-folder-sub (car path-list))
2275         (setq path-list (cdr path-list)))
2276       (message "%s unread folder"
2277                (if (> unread 0) unread "No")))))
2278
2279 (defun wl-folder-open-unread-current-entity ()
2280   (interactive)
2281   (let ((entity-name (wl-folder-get-entity-from-buffer))
2282         (group (wl-folder-buffer-group-p)))
2283     (when entity-name
2284       (wl-folder-open-unread-folder
2285        (if group
2286            (wl-folder-search-group-entity-by-name entity-name
2287                                                   wl-folder-entity)
2288          entity-name)))))
2289
2290 (defun wl-folder-open-only-unread-folder ()
2291   (interactive)
2292   (let ((id (progn
2293               (wl-folder-prev-entity-skip-invalid t)
2294               (wl-folder-get-entity-from-buffer t))))
2295     (wl-folder-open-all-unread-folder)
2296     (save-excursion
2297       (goto-char (point-max))
2298       (while (and (re-search-backward
2299                    "^[ ]*\\[[-]\\].+:0/0/[0-9-]+" nil t)
2300                   (not (bobp)))
2301         (wl-folder-jump-to-current-entity) ;; close it
2302         ))
2303     (wl-folder-move-path id)
2304     (recenter)))
2305
2306 (defun wl-folder-open-all-unread-folder (&optional arg)
2307   (interactive "P")
2308   (let ((id (progn
2309               (wl-folder-prev-entity-skip-invalid t)
2310               (wl-folder-get-entity-from-buffer t))))
2311     (wl-folder-open-unread-folder wl-folder-entity)
2312     (if (not arg)
2313         (wl-folder-move-path id)
2314       (goto-char (point-min))
2315       (wl-folder-next-unread t))))
2316
2317 (defun wl-folder-open-folder (&optional fld-name)
2318   (interactive)
2319   (if (not fld-name)
2320       (setq fld-name (wl-summary-read-folder wl-default-folder)))
2321   (let* ((id (wl-folder-get-entity-id
2322               (wl-folder-search-entity-by-name fld-name wl-folder-entity
2323                                                'folder)))
2324          (path (and id (wl-folder-get-path wl-folder-entity id))))
2325       (if path
2326           (wl-folder-open-folder-sub path))))
2327
2328 (defun wl-folder-open-folder-sub (path)
2329   (let ((inhibit-read-only t)
2330         (buffer-read-only nil)
2331         indent name entity
2332         err)
2333     (save-excursion
2334       (goto-char (point-min))
2335       (while (and path
2336                   (wl-folder-buffer-search-group
2337                    (wl-folder-get-petname
2338                     (if (stringp (car path))
2339                         (car path)
2340                       (wl-folder-get-folder-name-by-id
2341                        (car path))))))
2342         (beginning-of-line)
2343         (setq path (cdr path))
2344         (if (and (looking-at wl-folder-group-regexp)
2345                  (string= "+" (wl-match-buffer 2)));; closed group
2346             (save-excursion
2347               (setq indent (wl-match-buffer 1))
2348               (setq name (wl-folder-get-realname (wl-match-buffer 3)))
2349               (setq entity (wl-folder-search-group-entity-by-name
2350                             name
2351                             wl-folder-entity))
2352               ;; insert as opened
2353               (setcdr (assoc (car entity) wl-folder-group-alist) t)
2354               (if (eq 'access (cadr entity))
2355                   (wl-folder-maybe-load-folder-list entity))
2356               (wl-folder-insert-entity indent entity)
2357               (delete-region (save-excursion (beginning-of-line)
2358                                              (point))
2359                              (save-excursion (end-of-line)
2360                                              (+ 1 (point)))))))
2361       (set-buffer-modified-p nil))))
2362
2363 (defun wl-folder-open-all-pre ()
2364   (let ((entities (list wl-folder-entity))
2365         entity entity-stack group-entry)
2366     (while entities
2367       (setq entity (wl-pop entities))
2368       (cond
2369        ((consp entity)
2370         (unless (or (not (setq group-entry
2371                                (assoc (car entity) wl-folder-group-alist)))
2372                     (cdr group-entry))
2373           (setcdr group-entry t)
2374           (when (eq 'access (cadr entity))
2375             (wl-folder-maybe-load-folder-list entity)))
2376         (and entities
2377              (wl-push entities entity-stack))
2378         (setq entities (nth 2 entity))))
2379       (unless entities
2380         (setq entities (wl-pop entity-stack))))))
2381
2382 (defun wl-folder-open-all (&optional refresh)
2383   (interactive "P")
2384   (let* ((inhibit-read-only t)
2385          (buffer-read-only nil)
2386          (len (length wl-folder-group-alist))
2387          (i 0)
2388          indent name entity)
2389     (if refresh
2390         (let ((id (progn
2391                     (wl-folder-prev-entity-skip-invalid t)
2392                     (wl-folder-get-entity-from-buffer t)))
2393               (alist wl-folder-group-alist))
2394           (while alist
2395             (setcdr (pop alist) t))
2396           (erase-buffer)
2397           (wl-folder-insert-entity " " wl-folder-entity)
2398           (wl-folder-move-path id))
2399       (message "Opening all folders...")
2400       (wl-folder-open-all-pre)
2401       (save-excursion
2402         (goto-char (point-min))
2403         (while (re-search-forward
2404                 "^\\([ ]*\\)\\[\\([+]\\)\\]\\(.+\\):[-0-9-]+/[0-9-]+/[0-9-]+\n"
2405                 nil t)
2406           (setq indent (wl-match-buffer 1))
2407           (setq name (wl-folder-get-realname (wl-match-buffer 3)))
2408           (setq entity (wl-folder-search-group-entity-by-name
2409                         name
2410                         wl-folder-entity))
2411           ;; insert as opened
2412           (setcdr (assoc (car entity) wl-folder-group-alist) t)
2413           (forward-line -1)
2414           (wl-folder-insert-entity indent entity)
2415           (delete-region (save-excursion (beginning-of-line)
2416                                          (point))
2417                          (save-excursion (end-of-line)
2418                                          (+ 1 (point))))
2419           (when (> len elmo-display-progress-threshold)
2420             (setq i (1+ i))
2421             (if (or (zerop (% i 5)) (= i len))
2422                 (elmo-display-progress
2423                  'wl-folder-open-all "Opening all folders..."
2424                  (/ (* i 100) len)))))
2425         (when (> len elmo-display-progress-threshold)
2426           (elmo-display-progress
2427            'wl-folder-open-all "Opening all folders..." 100))))
2428     (message "Opening all folders...done")
2429     (set-buffer-modified-p nil)))
2430
2431 (defun wl-folder-close-all ()
2432   (interactive)
2433   (let ((inhibit-read-only t)
2434         (buffer-read-only nil)
2435         (alist wl-folder-group-alist)
2436         (id (progn
2437               (wl-folder-prev-entity-skip-invalid t)
2438               (wl-folder-get-entity-from-buffer t))))
2439     (while alist
2440       (setcdr (car alist) nil)
2441       (setq alist (cdr alist)))
2442     (setcdr (assoc (car wl-folder-entity) wl-folder-group-alist) t)
2443     (erase-buffer)
2444     (wl-folder-insert-entity " " wl-folder-entity)
2445     (wl-folder-move-path id)
2446     (recenter)
2447     (set-buffer-modified-p nil)))
2448
2449 (defun wl-folder-open-close ()
2450   "Open or close parent entity."
2451   (interactive)
2452   (save-excursion
2453     (beginning-of-line)
2454     (if (wl-folder-buffer-group-p)
2455         ;; if group (whether opend or closed.)
2456         (wl-folder-jump-to-current-entity)
2457       ;; if folder
2458       (let (indent)
2459         (setq indent (save-excursion
2460                        (re-search-forward "\\([ ]*\\)." nil t)
2461                        (wl-match-buffer 1)))
2462         (while (looking-at indent)
2463           (forward-line -1)))
2464       (wl-folder-jump-to-current-entity))))
2465
2466 (defsubst wl-folder-access-subscribe-p (group folder)
2467   (let (subscr regexp match)
2468     (if (setq subscr (wl-get-assoc-list-value
2469                       wl-folder-access-subscribe-alist
2470                       group))
2471         (progn
2472           (setq regexp (mapconcat 'identity (cdr subscr) "\\|"))
2473           (setq match (string-match regexp folder))
2474           (if (car subscr)
2475               match
2476             (not match)))
2477       t)))
2478
2479 (defun wl-folder-update-access-group (entity new-flist)
2480   (let* ((flist (nth 2 entity))
2481          (unsubscribes (nth 3 entity))
2482          (len (+ (length flist) (length unsubscribes)))
2483          (i 0)
2484          diff new-unsubscribes removes
2485          subscribed-list folder group entry)
2486     ;; check subscribed groups
2487     (while flist
2488       (cond
2489        ((listp (car flist))     ;; group
2490         (setq group (elmo-string (caar flist)))
2491         (cond
2492          ((assoc group new-flist)       ;; found in new-flist
2493           (setq new-flist (delete (assoc group new-flist)
2494                                   new-flist))
2495           (if (wl-folder-access-subscribe-p (car entity) group)
2496               (wl-append subscribed-list (list (car flist)))
2497             (wl-append new-unsubscribes (list (car flist)))
2498             (setq diff t)))
2499          (t
2500           (setq wl-folder-group-alist
2501                 (delete (wl-string-assoc group wl-folder-group-alist)
2502                         wl-folder-group-alist))
2503           (wl-append removes (list (list group))))))
2504        (t                       ;; folder
2505         (setq folder (elmo-string (car flist)))
2506         (cond
2507          ((member folder new-flist)     ;; found in new-flist
2508           (setq new-flist (delete folder new-flist))
2509           (if (wl-folder-access-subscribe-p (car entity) folder)
2510               (wl-append subscribed-list (list (car flist)))
2511             (wl-append new-unsubscribes (list folder))
2512             (setq diff t)))
2513          (t
2514           (wl-append removes (list folder))))))
2515       (when (> len elmo-display-progress-threshold)
2516         (setq i (1+ i))
2517         (if (or (zerop (% i 10)) (= i len))
2518             (elmo-display-progress
2519              'wl-folder-update-access-group "Updating access group..."
2520              (/ (* i 100) len))))
2521       (setq flist (cdr flist)))
2522     ;; check unsubscribed groups
2523     (while unsubscribes
2524       (cond
2525        ((listp (car unsubscribes))
2526         (when (setq entry (assoc (caar unsubscribes) new-flist))
2527           (setq new-flist (delete entry new-flist))
2528           (wl-append new-unsubscribes (list (car unsubscribes)))))
2529        (t
2530         (when (member (car unsubscribes) new-flist)
2531           (setq new-flist (delete (car unsubscribes) new-flist))
2532           (wl-append new-unsubscribes (list (car unsubscribes))))))
2533       (when (> len elmo-display-progress-threshold)
2534         (setq i (1+ i))
2535         (if (or (zerop (% i 10)) (= i len))
2536             (elmo-display-progress
2537              'wl-folder-update-access-group "Updating access group..."
2538              (/ (* i 100) len))))
2539       (setq unsubscribes (cdr unsubscribes)))
2540     ;;
2541     (if (or new-flist removes)
2542         (setq diff t))
2543     (setq new-flist
2544           (mapcar '(lambda (x)
2545                      (cond ((consp x) (list (car x) 'access))
2546                            (t x)))
2547                   new-flist))
2548     ;; check new groups
2549     (let ((new-list new-flist))
2550       (while new-list
2551         (if (not (wl-folder-access-subscribe-p
2552                   (car entity)
2553                   (if (listp (car new-list))
2554                       (caar new-list)
2555                     (car new-list))))
2556             ;; auto unsubscribe
2557             (progn
2558               (wl-append new-unsubscribes (list (car new-list)))
2559               (setq new-flist (delete (car new-list) new-flist)))
2560           (cond
2561            ((listp (car new-list))
2562             ;; check group exists
2563             (if (wl-string-assoc (caar new-list) wl-folder-group-alist)
2564                 (progn
2565                   (message "%s: group already exists." (caar new-list))
2566                   (sit-for 1)
2567                   (wl-append new-unsubscribes (list (car new-list)))
2568                   (setq new-flist (delete (car new-list) new-flist)))
2569               (wl-append wl-folder-group-alist
2570                          (list (cons (caar new-list) nil)))))))
2571         (setq new-list (cdr new-list))))
2572     (if new-flist
2573         (message "%d new folder(s)." (length new-flist))
2574       (message "Updating access group...done"))
2575     (wl-append new-flist subscribed-list)       ;; new is first
2576     (run-hooks 'wl-folder-update-access-group-hook)
2577     (setcdr (cdr entity) (list new-flist new-unsubscribes))
2578     (list diff new-flist new-unsubscribes removes)))
2579
2580 (defun wl-folder-prefetch-entity (entity)
2581   "Prefetch all new messages in the ENTITY."
2582   (cond
2583    ((consp entity)
2584     (let ((flist (nth 2 entity))
2585           (sum-done 0)
2586           (sum-all 0)
2587           result)
2588       (while flist
2589         (setq result (wl-folder-prefetch-entity (car flist)))
2590         (setq sum-done (+ sum-done (car result)))
2591         (setq sum-all (+ sum-all (cdr result)))
2592         (setq flist (cdr flist)))
2593       (message "Prefetched %d/%d message(s) in \"%s\"."
2594                sum-done sum-all
2595                (wl-folder-get-petname (car entity)))
2596       (cons sum-done sum-all)))
2597    ((stringp entity)
2598     (let ((nums (wl-folder-get-entity-info entity))
2599           (wl-summary-highlight (if (or (wl-summary-sticky-p entity)
2600                                         (wl-summary-always-sticky-folder-p
2601                                          entity))
2602                                     wl-summary-highlight))
2603           wl-summary-exit-next-move
2604           wl-auto-select-first ret-val
2605           count)
2606       (setq count (or (car nums) 0))
2607       (setq count (+ count (wl-folder-count-incorporates entity)))
2608       (if (or (null (car nums)) ; unknown
2609               (< 0 count))
2610           (save-window-excursion
2611             (save-excursion
2612               (wl-summary-goto-folder-subr entity
2613                                            (wl-summary-get-sync-range entity)
2614                                            nil)
2615               (setq ret-val (wl-summary-incorporate))
2616               (wl-summary-exit)
2617               ret-val))
2618         (cons 0 0))))))
2619
2620 (defun wl-folder-count-incorporates (folder)
2621   (let ((marks (elmo-msgdb-mark-load (elmo-msgdb-expand-path folder)))
2622         (sum 0))
2623     (while marks
2624       (if (member (cadr (car marks))
2625                   wl-summary-incorporate-marks)
2626           (incf sum))
2627       (setq marks (cdr marks)))
2628     sum))
2629
2630 (defun wl-folder-prefetch-current-entity (&optional no-check)
2631   "Prefetch all uncached messages in the folder at position.
2632 If current line is group folder, all subfolders are prefetched."
2633   (interactive "P")
2634   (save-excursion
2635     (let ((entity-name (wl-folder-get-entity-from-buffer))
2636           (group (wl-folder-buffer-group-p))
2637           wl-folder-check-entity-hook
2638           summary-buf entity)
2639       (when entity-name
2640         (setq entity
2641               (if group
2642                   (wl-folder-search-group-entity-by-name entity-name
2643                                                          wl-folder-entity)
2644                 entity-name))
2645         (if (not no-check)
2646             (wl-folder-check-entity entity))
2647         (wl-folder-prefetch-entity entity)))))
2648
2649 (defun wl-folder-drop-unsync-entity (entity)
2650   "Drop all unsync messages in the ENTITY."
2651   (cond
2652    ((consp entity)
2653     (let ((flist (nth 2 entity)))
2654       (while flist
2655         (wl-folder-drop-unsync-entity (car flist))
2656         (setq flist (cdr flist)))))
2657    ((stringp entity)
2658     (let ((nums (wl-folder-get-entity-info entity))
2659           wl-summary-highlight wl-auto-select-first new)
2660       (setq new (or (car nums) 0))
2661       (if (< 0 new)
2662           (save-window-excursion
2663             (save-excursion
2664               (wl-summary-goto-folder-subr entity 'no-sync nil)
2665               (wl-summary-drop-unsync)
2666               (wl-summary-exit))))))))
2667
2668 (defun wl-folder-drop-unsync-current-entity (&optional force-check)
2669   "Drop all unsync messages in the folder at position.
2670 If current line is group folder, all subfolders are dropped.
2671 If optional arg exists, don't check any folders."
2672   (interactive "P")
2673   (save-excursion
2674     (let ((entity-name (wl-folder-get-entity-from-buffer))
2675           (group (wl-folder-buffer-group-p))
2676           wl-folder-check-entity-hook
2677           summary-buf entity)
2678       (when (and entity-name
2679                  (y-or-n-p (format
2680                             "Drop all unsync messages in %s?" entity-name)))
2681         (setq entity
2682               (if group
2683                   (wl-folder-search-group-entity-by-name entity-name
2684                                                          wl-folder-entity)
2685                 entity-name))
2686         (if (null force-check)
2687             (wl-folder-check-entity entity))
2688         (wl-folder-drop-unsync-entity entity)
2689         (message "All unsync messages in %s are dropped!" entity-name)))))
2690
2691 (defun wl-folder-write-current-folder ()
2692   ""
2693   (interactive)
2694   (unless (wl-folder-buffer-group-p)
2695     (wl-summary-write-current-folder (wl-folder-entity-name))))
2696
2697 (defun wl-folder-mimic-kill-buffer ()
2698   "Kill the current (Folder) buffer with query."
2699   (interactive)
2700   (let ((bufname (read-buffer (format "Kill buffer: (default %s) "
2701                                       (buffer-name))))
2702         wl-interactive-exit)
2703     (if (or (not bufname)
2704             (string-equal bufname "")
2705             (string-equal bufname (buffer-name)))
2706         (wl-exit)
2707       (kill-buffer bufname))))
2708
2709 (defun wl-folder-create-subr (entity)
2710   (if (not (elmo-folder-creatable-p entity))
2711       (error "Folder %s is not found" entity)
2712     (if (y-or-n-p
2713          (format "Folder %s does not exist, create it?"
2714                  entity))
2715         (progn
2716           (setq wl-folder-entity-hashtb
2717                 (wl-folder-create-entity-hashtb
2718                  entity wl-folder-entity-hashtb))
2719           (unless (elmo-create-folder entity)
2720             (error "Create folder failed")))
2721       (error "Folder %s is not created" entity))))
2722
2723 (defun wl-folder-confirm-existence (folder &optional force)
2724   (if force
2725       (unless (elmo-folder-exists-p folder)
2726         (wl-folder-create-subr folder))
2727     (unless (or (wl-folder-entity-exists-p folder)
2728                 (file-exists-p (elmo-msgdb-expand-path folder))
2729                 (elmo-folder-exists-p folder))
2730       (wl-folder-create-subr folder))))
2731
2732 (require 'product)
2733 (product-provide (provide 'wl-folder) (require 'wl-version))
2734
2735 ;;; wl-folder.el ends here