elmo-mark branch is merged.
[elisp/wanderlust.git] / wl / wl-thread.el
1 ;;; wl-thread.el --- Thread display modules 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 'wl-summary)
35 (require 'wl-highlight)
36 (eval-when-compile (require 'cl))
37
38 ;; buffer local variables.
39 ;;(defvar wl-thread-top-entity '(nil t nil nil)) ; top entity
40 (defvar wl-thread-tops nil)             ; top number list (number)
41 (defvar wl-thread-entities nil)
42 (defvar wl-thread-entity-list nil)      ; entity list
43 (defvar wl-thread-entity-hashtb nil)    ; obarray
44
45 (make-variable-buffer-local 'wl-thread-entity-hashtb)
46 (make-variable-buffer-local 'wl-thread-entities)     ; ".wl-thread-entity"
47 (make-variable-buffer-local 'wl-thread-entity-list)  ; ".wl-thread-entity-list"
48
49 ;;; global flag
50 (defvar wl-thread-insert-force-opened nil)
51
52 ;;;;;; each entity is (number opened-or-not children parent) ;;;;;;;
53
54 (defun wl-thread-resume-entity (fld)
55   (let (entities top-list)
56     (setq entities (wl-summary-load-file-object
57                     (expand-file-name wl-thread-entity-file
58                                       (elmo-folder-msgdb-path fld))))
59     (setq top-list
60           (wl-summary-load-file-object
61            (expand-file-name wl-thread-entity-list-file
62                              (elmo-folder-msgdb-path fld))))
63     (message "Resuming thread structure...")
64     ;; set obarray value.
65     (setq wl-thread-entity-hashtb (elmo-make-hash (* (length entities) 2)))
66     ;; set buffer local variables.
67     (setq wl-thread-entities entities)
68     (setq wl-thread-entity-list top-list)
69     (while entities
70       (elmo-set-hash-val (format "#%d" (car (car entities))) (car entities)
71                          wl-thread-entity-hashtb)
72       (setq entities (cdr entities)))
73     (wl-thread-make-number-list)
74     (message "Resuming thread structure...done")))
75
76 (defun wl-thread-make-number-list ()
77   "Make `wl-summary-buffer-number-list', a list of message numbers."
78   (let* ((node (wl-thread-get-entity (car wl-thread-entity-list)))
79          (children (wl-thread-entity-get-children node))
80          parent sibling)
81     (setq wl-summary-buffer-number-list (list (car wl-thread-entity-list)))
82     (while children
83       (wl-thread-entity-make-number-list-from-children
84        (wl-thread-get-entity (car children)))
85       (setq children (cdr children)))
86     (while node
87       (setq parent (wl-thread-entity-get-parent-entity node)
88             sibling (wl-thread-entity-get-younger-brothers
89                      node parent))
90       (while sibling
91         (wl-thread-entity-make-number-list-from-children
92          (wl-thread-get-entity (car sibling)))
93         (setq sibling (cdr sibling)))
94       (setq node parent))
95     (setq wl-summary-buffer-number-list (nreverse
96                                          wl-summary-buffer-number-list))))
97
98 (defun wl-thread-entity-make-number-list-from-children (entity)
99   (let ((msgs (list (car entity)))
100         msgs-stack children)
101     (while msgs
102       (setq wl-summary-buffer-number-list (cons (car entity)
103                                                 wl-summary-buffer-number-list))
104       (setq msgs (cdr msgs))
105       (setq children (wl-thread-entity-get-children entity))
106       (if children
107           (progn
108             (wl-push msgs msgs-stack)
109             (setq msgs children))
110         (unless msgs
111           (while (and (null msgs) msgs-stack)
112             (setq msgs (wl-pop msgs-stack)))))
113       (setq entity (wl-thread-get-entity (car msgs))))))
114
115 (defun wl-thread-save-entity (dir)
116   (wl-thread-save-entities dir)
117   (wl-thread-save-top-list dir))
118
119 (defun wl-thread-save-top-list (dir)
120   (let ((top-file (expand-file-name wl-thread-entity-list-file dir))
121         (entity wl-thread-entity-list)
122         (tmp-buffer (get-buffer-create " *wl-thread-save-top-list*"))
123         print-length)
124     (save-excursion
125       (set-buffer tmp-buffer)
126       (erase-buffer)
127       (when (file-writable-p top-file)
128         (prin1 entity tmp-buffer)
129         (princ "\n" tmp-buffer)
130         (write-region (point-min) (point-max) top-file nil 'no-msg)
131         (kill-buffer tmp-buffer)))))
132
133 (defun wl-thread-save-entities (dir)
134   (let ((top-file (expand-file-name wl-thread-entity-file dir))
135         (entities wl-thread-entities)
136         (tmp-buffer (get-buffer-create " *wl-thread-save-entities*"))
137         print-length print-level)
138     (save-excursion
139       (set-buffer tmp-buffer)
140       (erase-buffer)
141       (when (file-writable-p top-file)
142         (prin1 entities tmp-buffer)
143         (princ "\n" tmp-buffer)
144         (write-region (point-min) (point-max) top-file nil 'no-msg)
145         (kill-buffer tmp-buffer)))))
146
147 (defsubst wl-thread-entity-get-number (entity)
148   (nth 0 entity))
149 (defsubst wl-thread-entity-get-opened (entity)
150   (nth 1 entity))
151 (defsubst wl-thread-entity-get-children (entity)
152   (nth 2 entity))
153 (defsubst wl-thread-entity-get-parent (entity)
154   (nth 3 entity))
155 (defsubst wl-thread-entity-get-linked (entity)
156   (nth 4 entity))
157
158 (defsubst wl-thread-create-entity (num parent &optional opened linked)
159   (list num (or opened wl-thread-insert-opened) nil parent linked))
160
161 (defsubst wl-thread-get-entity (num)
162   (and num
163        (elmo-get-hash-val (format "#%d" num) wl-thread-entity-hashtb)))
164
165 (defsubst wl-thread-entity-set-parent (entity parent)
166   (setcar (cdddr entity) parent)
167   entity)
168
169 (defsubst wl-thread-entity-set-children (entity children)
170   (setcar (cddr entity) children))
171
172 (defsubst wl-thread-entity-set-linked (entity linked)
173   (if (cddddr entity)
174       (setcar (cddddr entity) linked)
175     (nconc entity (list linked)))
176   entity)
177
178 (defsubst wl-thread-reparent-children (children parent)
179   (while children
180     (wl-thread-entity-set-parent
181      (wl-thread-get-entity (car children)) parent)
182     (wl-thread-entity-set-linked
183      (wl-thread-get-entity (car children)) t)
184     (setq children (cdr children))))
185
186 (defsubst wl-thread-entity-insert-as-top (entity)
187   (when (and entity
188              (car entity))
189     (wl-append wl-thread-entity-list (list (car entity)))
190     (setq wl-thread-entities (cons entity wl-thread-entities))
191     (setq wl-summary-buffer-number-list
192           (nconc wl-summary-buffer-number-list (list (car entity))))
193     (elmo-set-hash-val (format "#%d" (car entity)) entity
194                        wl-thread-entity-hashtb)))
195
196 (defsubst wl-thread-entity-insert-as-children (to entity)
197   (let ((children (wl-thread-entity-get-children to))
198         curp curc)
199     (setq curp to)
200     (elmo-list-insert wl-summary-buffer-number-list
201                       (wl-thread-entity-get-number entity)
202                       (progn
203                         (while (setq curc
204                                      (wl-thread-entity-get-children curp))
205                           (setq curp (wl-thread-get-entity
206                                       (nth (- (length curc) 1)
207                                            curc))))
208                         (wl-thread-entity-get-number curp)))
209     (setcar (cddr to) (wl-append children
210                                  (list (car entity))))
211     (setq wl-thread-entities (cons entity wl-thread-entities))
212     (elmo-set-hash-val (format "#%d" (car entity)) entity
213                        wl-thread-entity-hashtb)))
214
215 (defsubst wl-thread-entity-set-opened (entity opened)
216   (setcar (cdr entity) opened))
217
218 (defsubst wl-thread-entity-get-children-num (entity)
219   (let (children
220         ret-val msgs-stack
221         (msgs (list (car entity))))
222    (while msgs
223      (setq msgs (cdr msgs))
224      (setq children (wl-thread-entity-get-children entity))
225      (if (null children)
226          (while (and (null msgs) msgs-stack)
227            (setq msgs (wl-pop msgs-stack)))
228        (setq ret-val (+ (or ret-val 0) (length children)))
229        (wl-push msgs msgs-stack)
230        (setq msgs children))
231      (setq entity (wl-thread-get-entity (car msgs))))
232    ret-val))
233
234 (defsubst wl-thread-entity-get-descendant (entity)
235   (let (children
236         ret-val msgs-stack
237         (msgs (list (car entity))))
238    (while msgs
239      (setq msgs (cdr msgs))
240      (setq children (wl-thread-entity-get-children entity))
241      (if (null children)
242          (while (and (null msgs) msgs-stack)
243            (setq msgs (wl-pop msgs-stack)))
244        (setq ret-val (append ret-val (copy-sequence children)))
245        (wl-push msgs msgs-stack)
246        (setq msgs children))
247      (setq entity (wl-thread-get-entity (car msgs))))
248    ret-val))
249
250 (defsubst wl-thread-entity-get-parent-entity (entity)
251   (wl-thread-get-entity (wl-thread-entity-get-parent entity)))
252
253 (defun wl-thread-entity-get-top-entity (entity)
254   (let ((cur-entity entity)
255         p-num)
256     (while (setq p-num (wl-thread-entity-get-parent cur-entity))
257       (setq cur-entity (wl-thread-get-entity p-num)))
258     cur-entity))
259
260 (defun wl-thread-entity-parent-invisible-p (entity)
261   "If parent of ENTITY is invisible, the top invisible ancestor entity of
262 ENTITY is returned."
263   (let ((cur-entity entity)
264         top)
265     (catch 'done
266       (while (setq cur-entity (wl-thread-entity-get-parent-entity
267                                cur-entity))
268         (if (null (wl-thread-entity-get-number cur-entity))
269             (throw 'done nil)
270           (when (not (wl-thread-entity-get-opened cur-entity))
271             (setq top cur-entity)))))
272     top))
273
274 (defun wl-thread-entity-get-nearly-older-brother (entity &optional parent)
275   (let ((brothers (wl-thread-entity-get-older-brothers entity parent)))
276     (when brothers
277       (car (last brothers)))))
278
279 (defun wl-thread-entity-get-older-brothers (entity &optional parent)
280   (let* ((parent (or parent
281                      (wl-thread-entity-get-parent-entity entity)))
282          (brothers (wl-thread-entity-get-children parent))
283          ret-val)
284     (if parent
285         brothers
286       (setq brothers wl-thread-entity-list))
287     (while (and brothers
288                 (not (eq (wl-thread-entity-get-number entity)
289                          (car brothers))))
290       (wl-append ret-val (list (car brothers)))
291       (setq brothers (cdr brothers)))
292     ret-val))
293
294 (defun wl-thread-entity-get-younger-brothers (entity &optional parent)
295   (let* ((parent (or parent
296                      (wl-thread-entity-get-parent-entity entity)))
297          (brothers (wl-thread-entity-get-children parent)))
298     (if parent
299         (cdr (memq (wl-thread-entity-get-number entity)
300                    brothers))
301       ;; top!!
302       (cdr (memq (car entity) wl-thread-entity-list)))))
303
304 (defun wl-thread-jump-to-msg (&optional number)
305   (interactive)
306   (let ((num (or number
307                  (string-to-int
308                   (read-from-minibuffer "Jump to Message(No.): ")))))
309     (wl-thread-entity-force-open (wl-thread-get-entity num))
310     (wl-summary-jump-to-msg num)))
311
312 (defun wl-thread-close-all ()
313   "Close all top threads."
314   (interactive)
315   (message "Closing all threads...")
316   (save-excursion
317     (let ((entities wl-thread-entity-list)
318           (cur 0)
319           (len (length wl-thread-entity-list)))
320       (while entities
321         (when (and (wl-thread-entity-get-opened (wl-thread-get-entity
322                                                  (car entities)))
323                    (wl-thread-entity-get-children (wl-thread-get-entity
324                                                    (car entities))))
325           (wl-summary-jump-to-msg (car entities))
326           (wl-thread-open-close))
327         (when (> len elmo-display-progress-threshold)
328           (setq cur (1+ cur))
329           (if (or (zerop (% cur 5)) (= cur len))
330               (elmo-display-progress
331                'wl-thread-close-all "Closing all threads..."
332                (/ (* cur 100) len))))
333         (setq entities (cdr entities)))))
334   (message "Closing all threads...done"))
335
336 (defun wl-thread-open-all ()
337   "Open all threads."
338   (interactive)
339   (message "Opening all threads...")
340   (save-excursion
341     (goto-char (point-min))
342     (let ((len (count-lines (point-min) (point-max)))
343           (cur 0)
344           entity)
345       (while (not (eobp))
346         (if (wl-thread-entity-get-opened
347              (setq entity (wl-thread-get-entity
348                            (wl-summary-message-number))))
349             (forward-line 1)
350           (wl-thread-force-open)
351           (wl-thread-goto-bottom-of-sub-thread))
352         (when (> len elmo-display-progress-threshold)
353           (setq cur (1+ cur))
354           (elmo-display-progress
355            'wl-thread-open-all "Opening all threads..."
356            (/ (* cur 100) len)))))
357     ;; Make sure to be 100%.
358     (elmo-display-progress
359      'wl-thread-open-all "Opening all threads..."
360      100))
361   (message "Opening all threads...done"))
362
363 (defun wl-thread-open-all-unread ()
364   (interactive)
365   (dolist (number (elmo-folder-list-flagged wl-summary-buffer-elmo-folder
366                                             'digest 'in-msgdb))
367     (wl-thread-entity-force-open (wl-thread-get-entity number))))
368
369 (defsubst wl-thread-maybe-get-children-num (msg)
370   (let ((entity (wl-thread-get-entity msg)))
371     (if (not (wl-thread-entity-get-opened entity))
372         (wl-thread-entity-get-children-num entity))))
373
374 (defsubst wl-thread-update-line-on-buffer-sub (entity msg &optional parent-msg)
375   (let* ((entity (or entity (wl-thread-get-entity msg)))
376          (parent-msg (or parent-msg (wl-thread-entity-get-parent entity)))
377          (buffer-read-only nil)
378          (inhibit-read-only t)
379          message-entity temp-mark summary-line invisible-top dest-pair)
380     (if (wl-thread-delete-line-from-buffer msg)
381         (progn
382           (cond
383            ((memq msg wl-summary-buffer-target-mark-list)
384             (setq temp-mark "*"))
385            ((setq temp-mark (wl-summary-registered-temp-mark msg))
386             (setq dest-pair (cons (nth 0 temp-mark)(nth 2 temp-mark))
387                   temp-mark (nth 1 temp-mark)))
388            (t (setq temp-mark (wl-summary-get-score-mark msg))))
389           (when (setq message-entity
390                       (elmo-message-entity wl-summary-buffer-elmo-folder
391                                            msg))
392             (wl-summary-insert-line 
393              (wl-summary-create-line
394               message-entity
395               (elmo-message-entity wl-summary-buffer-elmo-folder
396                                    parent-msg)
397               temp-mark
398               (elmo-message-mark wl-summary-buffer-elmo-folder msg)
399               (if wl-thread-insert-force-opened
400                   nil
401                 (wl-thread-maybe-get-children-num msg))
402               (wl-thread-make-indent-string entity)
403               (wl-thread-entity-get-linked entity)))
404             (if dest-pair
405                 (wl-summary-print-argument (car dest-pair)
406                                            (cdr dest-pair)))))
407       ;; insert thread (moving thread)
408       (if (not (setq invisible-top
409                      (wl-thread-entity-parent-invisible-p entity)))
410           (wl-summary-update-thread
411            (elmo-message-entity wl-summary-buffer-elmo-folder msg)
412            entity
413            (and parent-msg
414                 (elmo-message-entity wl-summary-buffer-elmo-folder
415                                      parent-msg)))
416         ;; currently invisible.. update closed line.
417         (wl-thread-update-children-number invisible-top)))))
418
419 (defun wl-thread-update-line-on-buffer (&optional msg parent-msg updates)
420   (interactive)
421   (let ((msgs (list (or msg (wl-summary-message-number))))
422         entity children msgs-stack)
423    (while msgs
424     (setq msg (wl-pop msgs))
425     (setq updates (and updates (delete msg updates)))
426     (setq entity (wl-thread-get-entity msg))
427     (wl-thread-update-line-on-buffer-sub entity msg parent-msg)
428     ;;
429     (setq children (wl-thread-entity-get-children entity))
430     (if children
431         ;; update children
432         (when (wl-thread-entity-get-opened entity)
433           (wl-push msgs msgs-stack)
434           (setq parent-msg msg
435                 msgs children))
436       (unless msgs
437         (while (and (null msgs) msgs-stack)
438           (setq msgs (wl-pop msgs-stack)))
439         (when msgs
440           (setq parent-msg
441                 (wl-thread-entity-get-number
442                  (wl-thread-entity-get-parent-entity
443                   (wl-thread-get-entity (car msgs)))))))))
444    updates))
445
446 (defun wl-thread-update-line-msgs (msgs &optional no-msg)
447   (wl-delete-all-overlays)
448   (let ((i 0)
449         (updates msgs)
450         len)
451 ;;; (while msgs
452 ;;;   (setq updates
453 ;;;         (append updates
454 ;;;                 (wl-thread-get-children-msgs (car msgs))))
455 ;;;   (setq msgs (cdr msgs)))
456 ;;; (setq updates (elmo-uniq-list updates))
457     (setq len (length updates))
458     (while updates
459       (wl-thread-update-line-on-buffer-sub nil (car updates))
460       (setq updates (cdr updates))
461       (when (and (not no-msg)
462                  (> len elmo-display-progress-threshold))
463         (setq i (1+ i))
464         (if (or (zerop (% i 5)) (= i len))
465             (elmo-display-progress
466              'wl-thread-update-line-msgs "Updating deleted thread..."
467              (/ (* i 100) len)))))))
468
469 (defun wl-thread-delete-line-from-buffer (msg)
470   "Simply delete msg line."
471   (let (beg)
472     (if (wl-summary-jump-to-msg msg)
473         (progn
474           (setq beg (point))
475           (forward-line 1)
476           (delete-region beg (point))
477           t)
478       nil)))
479
480 (defun wl-thread-cleanup-symbols (msgs)
481   (let (entity)
482     (while msgs
483       (when (setq entity (wl-thread-get-entity (car msgs)))
484         ;; delete entity.
485         (setq wl-thread-entities (delq entity wl-thread-entities))
486         ;; free symbol.
487         (elmo-clear-hash-val (format "#%d" (car msgs))
488                              wl-thread-entity-hashtb))
489       (setq msgs (cdr msgs)))))
490
491 (defun wl-thread-get-exist-children (msg &optional include-self)
492   (let ((msgs (list msg))
493         msgs-stack children
494         entity ret-val)
495     (while msgs
496       (setq children (wl-thread-entity-get-children
497                       (setq entity (wl-thread-get-entity (car msgs)))))
498       (when (elmo-message-entity wl-summary-buffer-elmo-folder (car msgs))
499         (wl-append ret-val (list (car msgs)))
500         (setq children nil))
501       (setq msgs (cdr msgs))
502       (if (null children)
503           (while (and (null msgs) msgs-stack)
504             (setq msgs (wl-pop msgs-stack)))
505         (wl-push msgs msgs-stack)
506         (setq msgs children)))
507     (unless include-self (setq ret-val (delq msg ret-val)))
508     ret-val))
509
510 (defun wl-thread-delete-message (msg &optional deep update)
511   "Delete MSG from entity and buffer."
512   (save-excursion
513     (let* ((entity (wl-thread-get-entity msg))
514            children older-brothers younger-brothers top-child ;;grandchildren
515            top-entity parent update-msgs beg invisible-top)
516       (setq wl-summary-buffer-number-list
517             (delq msg wl-summary-buffer-number-list))         
518       (when entity
519         (setq parent (wl-thread-entity-get-parent-entity entity))
520         (if parent
521             (progn
522 ;;; has parent.
523 ;;;           (setq brothers (wl-thread-entity-get-children parent))
524               (setq older-brothers (wl-thread-entity-get-older-brothers
525                                     entity parent))
526               (setq younger-brothers (wl-thread-entity-get-younger-brothers
527                                       entity parent))
528               ;;
529               (unless deep
530                 (setq children (wl-thread-entity-get-children entity))
531                 (wl-thread-reparent-children
532                  children (wl-thread-entity-get-number parent))
533                 (setq update-msgs
534                       (apply (function nconc)
535                              update-msgs
536                              (mapcar
537                               (function
538                                (lambda (message)
539                                  (wl-thread-get-children-msgs message t)))
540                               children))))
541               (wl-thread-entity-set-children
542                parent (append older-brothers children younger-brothers))
543               ;; If chidren and younger-brothers not exists,
544               ;; update nearly older brother.
545               (when (and older-brothers
546                          (not younger-brothers)
547                          (not children))
548                 (wl-append
549                  update-msgs
550                  (wl-thread-get-children-msgs (car (last older-brothers))))))
551
552           ;; top...oldest child becomes top.
553           (unless deep
554             (setq children (wl-thread-entity-get-children entity))
555             (when children
556               (setq top-child (car children)
557                     children (cdr children))
558               (setq top-entity (wl-thread-get-entity top-child))
559               (wl-thread-entity-set-parent top-entity nil)
560               (wl-thread-entity-set-linked top-entity nil)
561               (wl-append update-msgs
562                          (wl-thread-get-children-msgs top-child t)))
563             (when children
564               (wl-thread-entity-set-children
565                top-entity
566                (append
567                 (wl-thread-entity-get-children top-entity)
568                 children))
569               (wl-thread-reparent-children children top-child)
570               (wl-append update-msgs children)))
571           ;; delete myself from top list.
572           (setq older-brothers (wl-thread-entity-get-older-brothers
573                                 entity nil))
574           (setq younger-brothers (wl-thread-entity-get-younger-brothers
575                                   entity nil))
576           (setq wl-thread-entity-list
577                 (append (append older-brothers
578                                 (and top-child (list top-child)))
579                         younger-brothers))))
580
581       (if deep
582           ;; delete thread on buffer
583           (when (wl-summary-jump-to-msg msg)
584             (setq beg (point))
585             (wl-thread-goto-bottom-of-sub-thread)
586             (delete-region beg (point)))
587         ;; delete myself from buffer.
588         (unless (wl-thread-delete-line-from-buffer msg)
589           ;; jump to suitable point.
590           ;; just upon the oldest younger-brother of my top.
591           (setq invisible-top
592                 (car (wl-thread-entity-parent-invisible-p entity)))
593           (if invisible-top
594               (progn
595                 (wl-append update-msgs (list invisible-top))
596                 (wl-summary-jump-to-msg invisible-top))
597             (goto-char (point-max))))
598
599         ;; insert children if thread is closed or delete top.
600         (when (or top-child
601                   (not (wl-thread-entity-get-opened entity)))
602           (let* (next-top insert-msgs ent e grandchildren)
603             (if top-child
604                 (progn
605                   (setq insert-msgs (wl-thread-get-exist-children
606                                      top-child 'include-self))
607                   (setq next-top (car insert-msgs))
608                   (setq ent (wl-thread-get-entity next-top))
609                   (when (and
610                          (wl-thread-entity-get-opened entity) ;; open
611                          (not (wl-thread-entity-get-opened ent)) ;; close
612                          (setq grandchildren
613                                (wl-thread-entity-get-children ent))
614                          (wl-summary-jump-to-msg next-top))
615                     (forward-line 1)
616                     (setq insert-msgs (append (cdr insert-msgs) grandchildren)))
617                   (when top-entity (wl-thread-entity-set-opened top-entity t))
618                   (when ent (wl-thread-entity-set-opened ent t)))
619               (when (not invisible-top)
620                 (setq insert-msgs (wl-thread-get-exist-children msg))
621                 ;; First msg always opened, because first msg maybe becomes top.
622                 (if (setq ent (wl-thread-get-entity (car insert-msgs)))
623                     (wl-thread-entity-set-opened ent t))))
624             ;; insert children
625             (while insert-msgs
626               ;; if no exists in summary, insert entity.
627               (when (and (car insert-msgs)
628                          (not (wl-summary-jump-to-msg (car insert-msgs))))
629                 (setq ent (wl-thread-get-entity (car insert-msgs)))
630                 (wl-thread-insert-entity 0 ; no mean now...
631                                          ent entity nil))
632               (setq insert-msgs (cdr insert-msgs))))))
633       (if update
634           ;; modify buffer.
635           (while update-msgs
636             (wl-thread-update-line-on-buffer-sub nil (pop update-msgs)))
637         ;; don't update buffer
638         update-msgs)))) ; return value
639
640 (defun wl-thread-insert-message (overview-entity
641                                  msg parent-msg &optional update linked)
642   "Insert MSG to the entity.
643 When optional argument UPDATE is non-nil,
644 Message is inserted to the summary buffer."
645   (let ((parent (wl-thread-get-entity parent-msg))
646         (depth 0) cur
647         child-entity invisible-top)
648 ;;; Update the thread view...not implemented yet.
649 ;;;  (when force-insert
650 ;;;    (if parent
651 ;;;       (wl-thread-entity-force-open parent))
652     (when (and wl-summary-max-thread-depth parent)
653       (setq cur parent)
654       (while cur
655         (incf depth)
656         (setq cur (wl-thread-entity-get-parent-entity cur)))
657       (when (> depth wl-summary-max-thread-depth)
658         (setq parent nil)))
659     (if parent
660         ;; insert as children.
661         (wl-thread-entity-insert-as-children
662          parent
663          (setq child-entity (wl-thread-create-entity
664                              msg (nth 0 parent) nil linked)))
665       ;; insert as top message.
666       (wl-thread-entity-insert-as-top
667        (wl-thread-create-entity msg nil)))
668     (if update
669         (if (not (setq invisible-top
670                        (wl-thread-entity-parent-invisible-p child-entity)))
671             ;; visible.
672             (progn
673               (wl-summary-update-thread
674                overview-entity
675                child-entity
676                (elmo-message-entity wl-summary-buffer-elmo-folder
677                                     parent-msg))
678               (when parent
679                 ;; use thread structure.
680                 ;;(wl-thread-entity-get-nearly-older-brother
681                 ;; child-entity parent))) ; return value
682                 (wl-thread-entity-get-number parent))) ; return value
683 ;;;           (setq beg (point))
684 ;;;           (wl-thread-goto-bottom-of-sub-thread)
685 ;;;           (wl-thread-update-indent-string-region beg (point)))
686           ;; currently invisible.. update closed line.
687           (wl-thread-update-children-number invisible-top)
688           nil))))
689
690 (defun wl-thread-get-parent-list (msgs)
691   (let* ((msgs2 msgs)
692          myself)
693     (while msgs2
694       (setq myself (car msgs2)
695             msgs2 (cdr msgs2))
696       (while (not (eq myself (car msgs2)))
697         (if (wl-thread-descendant-p myself (car msgs2))
698             (setq msgs (delq (car msgs2) msgs)))
699         (setq msgs2 (or (cdr msgs2) msgs)))
700       (setq msgs2 (cdr msgs2)))
701     msgs))
702
703 (defun wl-thread-update-indent-string-thread (top-list)
704   (let ((top-list (wl-thread-get-parent-list top-list))
705         beg)
706     (while top-list
707       (when (car top-list)
708         (wl-summary-jump-to-msg (car top-list))
709         (setq beg (point))
710         (wl-thread-goto-bottom-of-sub-thread)
711         (wl-thread-update-indent-string-region beg (point)))
712       (setq top-list (cdr top-list)))))
713
714 (defun wl-thread-update-children-number (entity)
715   "Update the children number."
716   (wl-thread-update-line-on-buffer (wl-thread-entity-get-number entity)))
717
718 ;;
719 ;; Thread oriented commands.
720 ;;
721 (defun wl-thread-call-region-func (func &optional arg)
722   (save-excursion
723     (if arg
724         (wl-summary-goto-top-of-current-thread)
725       (beginning-of-line))
726     (let ((beg (point)))
727       (wl-thread-goto-bottom-of-sub-thread)
728       (funcall func beg (point)))))
729
730 (defun wl-thread-prefetch (&optional arg)
731   (interactive "P")
732   (wl-thread-call-region-func 'wl-summary-prefetch-region arg))
733
734 (defun wl-thread-msg-mark-as-important (msg)
735   "Set mark as important for invisible MSG. Modeline is not changed."
736   (let ((folder wl-summary-buffer-elmo-folder)
737         cur-mark)
738     (setq cur-mark (elmo-message-mark folder msg))
739     (elmo-folder-mark-as-important folder (list msg))
740     (wl-summary-set-mark-modified)))
741
742 (defun wl-thread-mark-as-read (&optional arg)
743   (interactive "P")
744   (wl-thread-call-region-func 'wl-summary-mark-as-read-region arg))
745
746 (defun wl-thread-mark-as-unread (&optional arg)
747   (interactive "P")
748   (wl-thread-call-region-func 'wl-summary-mark-as-unread-region arg))
749
750 (defun wl-thread-mark-as-important (&optional arg)
751   (interactive "P")
752   (wl-thread-call-region-func 'wl-summary-mark-as-important-region arg))
753
754 (defun wl-thread-unmark (&optional arg)
755   (interactive "P")
756   (wl-thread-call-region-func 'wl-summary-unmark-region arg))
757
758 (defun wl-thread-exec (&optional arg)
759   (interactive "P")
760   (wl-thread-call-region-func 'wl-summary-exec-region arg))
761
762 (defun wl-thread-save (&optional arg)
763   (interactive "P")
764   (wl-thread-call-region-func 'wl-summary-save-region arg))
765
766 (defun wl-thread-force-open (&optional msg-num)
767   "force open current folder"
768   (if msg-num
769       (wl-summary-jump-to-msg msg-num))
770   (let ((wl-thread-insert-force-opened t))
771     (wl-thread-open-close)))
772
773 (defun wl-thread-entity-force-open (entity)
774   (let ((wl-thread-insert-force-opened t)
775         notopen)
776     (if (null (wl-thread-entity-get-parent entity))
777         ;; top!!
778         (if (and (not (wl-thread-entity-get-opened entity))
779                  (wl-thread-entity-get-children entity))
780             (wl-thread-force-open (wl-thread-entity-get-number entity)))
781       (if (setq notopen (wl-thread-entity-parent-invisible-p entity))
782           (wl-thread-force-open (wl-thread-entity-get-number notopen))))))
783
784 (defun wl-thread-insert-top ()
785   (let ((elist wl-thread-entity-list)
786         (len (length wl-thread-entity-list))
787         (cur 0))
788     (wl-delete-all-overlays)
789     (while elist
790       (wl-thread-insert-entity
791        0
792        (wl-thread-get-entity (car elist))
793        nil
794        len)
795       (setq elist (cdr elist))
796       (when (> len elmo-display-progress-threshold)
797         (setq cur (1+ cur))
798         (if (or (zerop (% cur 2)) (= cur len))
799             (elmo-display-progress
800              'wl-thread-insert-top "Inserting message..."
801              (/ (* cur 100) len)))))))
802
803 (defsubst wl-thread-insert-entity-sub (indent entity parent-entity all)
804   (let (msg-num
805         message-entity
806         temp-mark
807         summary-line)
808     (when (setq msg-num (wl-thread-entity-get-number entity))
809       (unless all ; all...means no temp-mark.
810         (cond ((memq msg-num wl-summary-buffer-target-mark-list)
811                (setq temp-mark "*"))
812               ((setq temp-mark (wl-summary-registered-temp-mark msg-num))
813                (setq temp-mark (nth 1 temp-mark)))))
814       (unless temp-mark
815         (setq temp-mark (wl-summary-get-score-mark msg-num)))
816       (setq message-entity
817             (elmo-message-entity wl-summary-buffer-elmo-folder
818                                  (nth 0 entity)))
819 ;;;   (wl-delete-all-overlays)
820       (when message-entity
821         (wl-summary-insert-line
822          (wl-summary-create-line
823           message-entity
824           (elmo-message-entity wl-summary-buffer-elmo-folder
825                                (nth 0 parent-entity))
826           temp-mark
827           (elmo-message-mark wl-summary-buffer-elmo-folder msg-num)
828           (if wl-thread-insert-force-opened
829               nil
830             (wl-thread-maybe-get-children-num msg-num))
831           (wl-thread-make-indent-string entity)
832           (wl-thread-entity-get-linked entity)))))))
833
834 (defun wl-thread-insert-entity (indent entity parent-entity all)
835   "Insert thread entity in current buffer."
836   (let ((msgs (list (car entity)))
837         children msgs-stack)
838     (while msgs
839       (wl-thread-insert-entity-sub indent entity parent-entity all)
840       (setq msgs (cdr msgs))
841       (setq children (nth 2 entity))
842       (if children
843           ;; insert children
844           (when (or wl-thread-insert-force-opened
845                     (wl-thread-entity-get-opened entity))
846             (wl-thread-entity-set-opened entity t)
847             (wl-push msgs msgs-stack)
848             (setq msgs children
849                   indent (1+ indent)
850                   parent-entity entity)))
851       (unless msgs
852         (while (and (null msgs) msgs-stack)
853           (setq msgs (wl-pop msgs-stack))
854           (setq indent (1- indent)))
855         (when msgs
856           (setq entity (wl-thread-get-entity (car msgs)))
857           (setq parent-entity (wl-thread-entity-get-parent-entity entity))))
858       (setq entity (wl-thread-get-entity (car msgs))))))
859
860 (defun wl-thread-descendant-p (mynumber number)
861   (let ((cur (wl-thread-get-entity number))
862         num)
863     (catch 'done
864       (while cur
865         (setq cur (wl-thread-entity-get-parent-entity cur))
866         (if (null (setq num (wl-thread-entity-get-number cur))) ; top!
867             (throw 'done nil))
868         (if (and num
869                  (eq mynumber (wl-thread-entity-get-number cur)))
870             (throw 'done t)))
871       nil)))
872
873 ;; (defun wl-thread-goto-bottom-of-sub-thread ()
874 ;;   (interactive)
875 ;;   (let ((depth (wl-thread-get-depth-of-current-line)))
876 ;;     (forward-line 1)
877 ;;     (while (and (not (eobp))
878 ;;              (> (wl-thread-get-depth-of-current-line)
879 ;;                 depth))
880 ;;       (forward-line 1))
881 ;;     (beginning-of-line)))
882
883 (defun wl-thread-goto-bottom-of-sub-thread (&optional msg)
884   (interactive)
885   (let ((mynumber (or msg (wl-summary-message-number))))
886     (forward-line 1)
887     (while (wl-thread-descendant-p mynumber (wl-summary-message-number))
888       (forward-line 1))
889     (beginning-of-line)))
890
891 (defun wl-thread-remove-argument-region (beg end)
892   (save-excursion
893     (save-restriction
894       (narrow-to-region beg end)
895       (goto-char (point-min))
896       (while (not (eobp))
897         (wl-summary-remove-argument)
898         (forward-line 1)))))
899
900 (defun wl-thread-print-argument-region (beg end)
901   (if wl-summary-buffer-temp-mark-list
902       (save-excursion
903         (save-restriction
904           (narrow-to-region beg end)
905           (goto-char (point-min))
906           (while (not (eobp))
907             (let ((num (wl-summary-message-number))
908                   temp-mark pair)
909               (when (and (setq temp-mark
910                                (wl-summary-registered-temp-mark num))
911                          (nth 2 temp-mark)
912                          (setq pair (cons (nth 0 temp-mark)(nth 2 temp-mark))))
913                 (wl-summary-print-argument (car pair) (cdr pair))))
914             (forward-line 1))))))
915
916 (defsubst wl-thread-get-children-msgs (msg &optional visible-only)
917   (let ((msgs (list msg))
918         msgs-stack children
919         entity ret-val)
920     (while msgs
921       (wl-append ret-val (list (car msgs)))
922       (setq children (wl-thread-entity-get-children
923                       (setq entity (wl-thread-get-entity (car msgs)))))
924       (if (and visible-only
925                (not (wl-thread-entity-get-opened entity)))
926           (setq children nil))
927       (setq msgs (cdr msgs))
928       (if (null children)
929           (while (and (null msgs) msgs-stack)
930             (setq msgs (wl-pop msgs-stack)))
931         (wl-push msgs msgs-stack)
932         (setq msgs children)))
933     ret-val))
934
935 (defun wl-thread-get-children-msgs-uncached (msg &optional uncached-marks)
936   (let ((children-msgs (wl-thread-get-children-msgs msg))
937         mark uncached-list)
938     (while children-msgs
939       (if (and (not (eq msg (car children-msgs))) ; except itself
940                (or (and uncached-marks
941                         (setq mark (elmo-message-mark
942                                     wl-summary-buffer-elmo-folder
943                                     (car children-msgs)))
944                         (member mark uncached-marks))
945                    (and (not uncached-marks)
946                         (null (elmo-file-cache-exists-p
947                                (elmo-message-field
948                                 wl-summary-buffer-elmo-folder
949                                 (car children-msgs)
950                                 'message-id))))))
951           (wl-append uncached-list (list (car children-msgs))))
952       (setq children-msgs (cdr children-msgs)))
953     uncached-list))
954
955 (defun wl-thread-get-children-msgs-with-mark (msg mark)
956   (let ((children-msgs (wl-thread-get-children-msgs msg))
957         (check-func (cond ((string= mark "o")
958                            'wl-summary-msg-marked-as-refiled)
959                           ((string= mark "O")
960                            'wl-summary-msg-marked-as-copied)
961                           ((string= mark "D")
962                            'wl-summary-msg-marked-as-deleted)
963                           ((string= mark "*")
964                            'wl-summary-msg-marked-as-target)))
965         ret-val)
966     (while children-msgs
967       (if (funcall check-func (car children-msgs))
968           (wl-append ret-val (list (car children-msgs))))
969       (setq children-msgs (cdr children-msgs)))
970     ret-val))
971
972 (defun wl-thread-close (entity)
973   (let (depth beg)
974     (wl-thread-entity-set-opened entity nil)
975     (setq depth (wl-thread-get-depth-of-current-line))
976     (beginning-of-line)
977     (setq beg (point))
978     (wl-thread-goto-bottom-of-sub-thread)
979     (wl-thread-remove-argument-region beg
980                                       (point))
981     (forward-char -1)   ;; needed for mouse-face.
982     (delete-region beg (point))
983     (wl-thread-insert-entity (- depth 1)
984                              entity
985                              (wl-thread-get-entity
986                               (nth 3 entity))
987                              nil)
988     (delete-char 1) ; delete '\n'
989     (wl-thread-print-argument-region beg (point))))
990
991 (defun wl-thread-open (entity)
992   (let (depth beg)
993     (beginning-of-line)
994     (setq beg (point))
995     (setq depth (wl-thread-get-depth-of-current-line))
996     (end-of-line)
997     (delete-region beg (point))
998     (wl-thread-entity-set-opened entity t)
999     (wl-thread-insert-entity depth ;(- depth 1)
1000                              entity
1001                              (wl-thread-get-entity
1002                               (nth 3 entity)) nil)
1003     (delete-char 1) ; delete '\n'
1004     (wl-thread-print-argument-region beg (point))))
1005
1006 (defun wl-thread-open-close (&optional force-open)
1007   (interactive "P")
1008   (when (eq wl-summary-buffer-view 'thread)
1009 ;;; (if (equal wl-thread-top-entity '(nil t nil nil))
1010 ;;;     (error "There's no thread structure"))
1011     (save-excursion
1012       (let ((inhibit-read-only t)
1013             (buffer-read-only nil)
1014             (wl-thread-insert-force-opened
1015              (or wl-thread-insert-force-opened
1016                  force-open))
1017             msg entity parent)
1018         (setq msg (wl-summary-message-number))
1019         (setq entity (wl-thread-get-entity msg))
1020         (if (wl-thread-entity-get-opened entity)
1021             ;; if already opened, close its child!
1022           (if (wl-thread-entity-get-children entity)
1023               (wl-thread-close entity)
1024             ;; opened, but has no children, close its parent!
1025             (when (setq parent (wl-thread-entity-get-parent entity))
1026               (wl-summary-jump-to-msg parent)
1027               (wl-thread-close
1028                (wl-thread-get-entity (wl-summary-message-number)))))
1029           ;; if closed (or it is just a thread bottom message)
1030           ;; has children, open it!
1031           (if (wl-thread-entity-get-children entity)
1032               (wl-thread-open entity)
1033             ;; closed, and has no children, close its parent!
1034             (setq msg (or (wl-thread-entity-get-parent entity)
1035                           (wl-thread-entity-get-number entity)))
1036             (when msg
1037               (wl-summary-jump-to-msg msg)
1038               (wl-thread-close
1039                (wl-thread-get-entity (wl-summary-message-number)))))))
1040       (when wl-summary-lazy-highlight
1041         (wl-highlight-summary-window))
1042       (wl-summary-set-message-modified)
1043       (set-buffer-modified-p nil))))
1044
1045 (defun wl-thread-get-depth-of-current-line ()
1046   (let ((entity (wl-thread-get-entity (wl-summary-message-number)))
1047         (depth 0)
1048         number)
1049     (while (setq number (wl-thread-entity-get-parent entity))
1050       (incf depth)
1051       (setq entity (wl-thread-get-entity number)))
1052     depth))
1053   
1054 (defun wl-thread-update-indent-string-region (beg end)
1055   (interactive "r")
1056   (save-excursion
1057     (goto-char beg)
1058     (while (< (point) end)
1059       (save-excursion
1060         (wl-thread-update-line-on-buffer-sub nil (wl-summary-message-number)))
1061       (forward-line 1))))
1062
1063 (defsubst wl-thread-make-indent-string (entity)
1064   (let ((cur entity)
1065         (ret-val "")
1066         (space-str (wl-repeat-string wl-thread-space-str-internal
1067                                      (- wl-thread-indent-level-internal 1)))
1068         parent)
1069     (when (wl-thread-entity-get-number
1070            (setq parent (wl-thread-entity-get-parent-entity cur)))
1071       (if (wl-thread-entity-get-younger-brothers cur)
1072           (setq ret-val wl-thread-have-younger-brother-str-internal)
1073         (setq ret-val wl-thread-youngest-child-str-internal))
1074       (setq ret-val (concat ret-val
1075                             (wl-repeat-string
1076                              wl-thread-horizontal-str-internal
1077                              (- wl-thread-indent-level-internal 1))))
1078       (setq cur parent)
1079       (while (wl-thread-entity-get-number
1080               (wl-thread-entity-get-parent-entity cur))
1081         (if (wl-thread-entity-get-younger-brothers cur)
1082             (setq ret-val (concat wl-thread-vertical-str-internal
1083                                   space-str
1084                                   ret-val))
1085           (setq ret-val (concat wl-thread-space-str-internal
1086                                 space-str
1087                                 ret-val)))
1088         (setq cur (wl-thread-entity-get-parent-entity cur))))
1089     ret-val))
1090
1091 (defun wl-thread-set-parent (&optional parent-number)
1092   "Set current message's parent interactively."
1093   (interactive)
1094   (let ((number (wl-summary-message-number))
1095         (dst-parent (if (interactive-p)
1096                         (read-from-minibuffer "Parent Message (No.): ")))
1097         entity dst-parent-entity src-parent children
1098         update-msgs
1099         buffer-read-only)
1100     (if (string= dst-parent "")
1101         (setq dst-parent nil)
1102       (if (interactive-p)
1103           (setq dst-parent (string-to-int dst-parent))
1104         (setq dst-parent parent-number)))
1105     (if (and dst-parent
1106              (memq dst-parent (wl-thread-get-children-msgs number)))
1107         (error "Parent is children or myself"))
1108     (setq entity (wl-thread-get-entity number))
1109     (when (and number entity)
1110       ;; delete thread
1111       (setq update-msgs (wl-thread-delete-message number 'deep))
1112       ;; insert as child at new parent
1113       (setq dst-parent-entity (wl-thread-get-entity dst-parent))
1114       (if dst-parent-entity
1115           (progn
1116             (if (setq children
1117                       (wl-thread-entity-get-children dst-parent-entity))
1118                 (wl-append update-msgs
1119                            (wl-thread-get-children-msgs
1120                             (car (last children)) t)))
1121             (wl-thread-entity-set-children
1122              dst-parent-entity
1123              (append children (list number)))
1124             (wl-thread-entity-set-linked entity t))
1125         ;; insert as top
1126         (wl-append wl-thread-entity-list (list number))
1127         (wl-thread-entity-set-linked entity nil))
1128
1129       ;; update my thread
1130       (wl-append update-msgs (wl-thread-get-children-msgs number t))
1131       (setq update-msgs (elmo-uniq-list update-msgs))
1132       (wl-thread-entity-set-parent entity dst-parent)
1133       ;; update thread on buffer
1134       (wl-thread-make-number-list)
1135       (wl-thread-update-line-msgs update-msgs t))))
1136
1137 (require 'product)
1138 (product-provide (provide 'wl-thread) (require 'wl-version))
1139
1140 ;;; wl-thread.el ends here