Removed trailing whitespaces.
[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 ret-val)
283     (if parent
284         (setq brothers (wl-thread-entity-get-children parent))
285       (setq brothers wl-thread-entity-list))
286     (while (and brothers
287                 (not (eq (wl-thread-entity-get-number entity)
288                          (car brothers))))
289       (wl-append ret-val (list (car brothers)))
290       (setq brothers (cdr brothers)))
291     ret-val))
292
293 (defun wl-thread-entity-get-younger-brothers (entity &optional parent)
294   (let* ((parent (or parent
295                      (wl-thread-entity-get-parent-entity entity)))
296          (brothers (wl-thread-entity-get-children parent)))
297     (if parent
298         (cdr (memq (wl-thread-entity-get-number entity)
299                    brothers))
300       ;; top!!
301       (cdr (memq (car entity) wl-thread-entity-list)))))
302
303 (defun wl-thread-jump-to-msg (&optional number)
304   (interactive)
305   (let ((num (or number
306                  (string-to-int
307                   (read-from-minibuffer "Jump to Message(No.): ")))))
308     (wl-thread-entity-force-open (wl-thread-get-entity num))
309     (wl-summary-jump-to-msg num)))
310
311 (defun wl-thread-close-all ()
312   "Close all top threads."
313   (interactive)
314   (message "Closing all threads...")
315   (save-excursion
316     (let ((entities wl-thread-entity-list)
317           (cur 0)
318           (len (length wl-thread-entity-list)))
319       (while entities
320         (when (and (wl-thread-entity-get-opened (wl-thread-get-entity
321                                                  (car entities)))
322                    (wl-thread-entity-get-children (wl-thread-get-entity
323                                                    (car entities))))
324           (wl-summary-jump-to-msg (car entities))
325           (wl-thread-open-close))
326         (when (> len elmo-display-progress-threshold)
327           (setq cur (1+ cur))
328           (if (or (zerop (% cur 5)) (= cur len))
329               (elmo-display-progress
330                'wl-thread-close-all "Closing all threads..."
331                (/ (* cur 100) len))))
332         (setq entities (cdr entities)))))
333   (message "Closing all threads...done"))
334
335 (defun wl-thread-open-all ()
336   "Open all threads."
337   (interactive)
338   (message "Opening all threads...")
339   (save-excursion
340     (goto-char (point-min))
341     (let ((len (count-lines (point-min) (point-max)))
342           (cur 0)
343           entity)
344       (while (not (eobp))
345         (if (wl-thread-entity-get-opened
346              (setq entity (wl-thread-get-entity
347                            (wl-summary-message-number))))
348             (forward-line 1)
349           (wl-thread-force-open)
350           (wl-thread-goto-bottom-of-sub-thread))
351         (when (> len elmo-display-progress-threshold)
352           (setq cur (1+ cur))
353           (elmo-display-progress
354            'wl-thread-open-all "Opening all threads..."
355            (/ (* cur 100) len)))))
356     ;; Make sure to be 100%.
357     (elmo-display-progress
358      'wl-thread-open-all "Opening all threads..."
359      100))
360   (message "Opening all threads...done"))
361
362 (defun wl-thread-open-all-unread ()
363   (interactive)
364   (dolist (number (elmo-folder-list-flagged wl-summary-buffer-elmo-folder
365                                             'digest 'in-msgdb))
366     (wl-thread-entity-force-open (wl-thread-get-entity number))))
367
368 (defsubst wl-thread-maybe-get-children-num (msg)
369   (let ((entity (wl-thread-get-entity msg)))
370     (if (not (wl-thread-entity-get-opened entity))
371         (wl-thread-entity-get-children-num entity))))
372
373 (defsubst wl-thread-update-line-on-buffer-sub (entity msg &optional parent-msg)
374   (let* ((entity (or entity (wl-thread-get-entity msg)))
375          (parent-msg (or parent-msg (wl-thread-entity-get-parent entity)))
376          (buffer-read-only nil)
377          (inhibit-read-only t)
378          message-entity temp-mark summary-line invisible-top dest-pair)
379     (if (wl-thread-delete-line-from-buffer msg)
380         (progn
381           (cond
382            ((memq msg wl-summary-buffer-target-mark-list)
383             (setq temp-mark "*"))
384            ((setq temp-mark (wl-summary-registered-temp-mark msg))
385             (setq dest-pair (cons (nth 0 temp-mark)(nth 2 temp-mark))
386                   temp-mark (nth 1 temp-mark)))
387            (t (setq temp-mark (wl-summary-get-score-mark msg))))
388           (when (setq message-entity
389                       (elmo-message-entity wl-summary-buffer-elmo-folder
390                                            msg))
391             (wl-summary-insert-line
392              (wl-summary-create-line
393               message-entity
394               (elmo-message-entity wl-summary-buffer-elmo-folder
395                                    parent-msg)
396               temp-mark
397               (elmo-message-flags wl-summary-buffer-elmo-folder
398                                   msg)
399               (elmo-message-cached-p wl-summary-buffer-elmo-folder
400                                      msg)
401               (if wl-thread-insert-force-opened
402                   nil
403                 (wl-thread-maybe-get-children-num msg))
404               (wl-thread-make-indent-string entity)
405               (wl-thread-entity-get-linked entity)))
406             (if dest-pair
407                 (wl-summary-print-argument (car dest-pair)
408                                            (cdr dest-pair)))))
409       ;; insert thread (moving thread)
410       (if (not (setq invisible-top
411                      (wl-thread-entity-parent-invisible-p entity)))
412           (wl-summary-update-thread
413            (elmo-message-entity wl-summary-buffer-elmo-folder msg)
414            entity
415            (and parent-msg
416                 (elmo-message-entity wl-summary-buffer-elmo-folder
417                                      parent-msg)))
418         ;; currently invisible.. update closed line.
419         (wl-thread-update-children-number invisible-top)))))
420
421 (defun wl-thread-update-line-on-buffer (&optional msg parent-msg updates)
422   (interactive)
423   (let ((msgs (list (or msg (wl-summary-message-number))))
424         entity children msgs-stack)
425    (while msgs
426     (setq msg (wl-pop msgs))
427     (setq updates (and updates (delete msg updates)))
428     (setq entity (wl-thread-get-entity msg))
429     (wl-thread-update-line-on-buffer-sub entity msg parent-msg)
430     ;;
431     (setq children (wl-thread-entity-get-children entity))
432     (if children
433         ;; update children
434         (when (wl-thread-entity-get-opened entity)
435           (wl-push msgs msgs-stack)
436           (setq parent-msg msg
437                 msgs children))
438       (unless msgs
439         (while (and (null msgs) msgs-stack)
440           (setq msgs (wl-pop msgs-stack)))
441         (when msgs
442           (setq parent-msg
443                 (wl-thread-entity-get-number
444                  (wl-thread-entity-get-parent-entity
445                   (wl-thread-get-entity (car msgs)))))))))
446    updates))
447
448 (defun wl-thread-update-line-msgs (msgs &optional no-msg)
449   (wl-delete-all-overlays)
450   (let ((i 0)
451         (updates msgs)
452         len)
453 ;;; (while msgs
454 ;;;   (setq updates
455 ;;;         (append updates
456 ;;;                 (wl-thread-get-children-msgs (car msgs))))
457 ;;;   (setq msgs (cdr msgs)))
458 ;;; (setq updates (elmo-uniq-list updates))
459     (setq len (length updates))
460     (while updates
461       (wl-thread-update-line-on-buffer-sub nil (car updates))
462       (setq updates (cdr updates))
463       (when (and (not no-msg)
464                  (> len elmo-display-progress-threshold))
465         (setq i (1+ i))
466         (if (or (zerop (% i 5)) (= i len))
467             (elmo-display-progress
468              'wl-thread-update-line-msgs "Updating deleted thread..."
469              (/ (* i 100) len)))))))
470
471 (defun wl-thread-delete-line-from-buffer (msg)
472   "Simply delete msg line."
473   (let (beg)
474     (if (wl-summary-jump-to-msg msg)
475         (progn
476           (setq beg (point))
477           (forward-line 1)
478           (delete-region beg (point))
479           t)
480       nil)))
481
482 (defun wl-thread-cleanup-symbols (msgs)
483   (let (entity)
484     (while msgs
485       (when (setq entity (wl-thread-get-entity (car msgs)))
486         ;; delete entity.
487         (setq wl-thread-entities (delq entity wl-thread-entities))
488         ;; free symbol.
489         (elmo-clear-hash-val (format "#%d" (car msgs))
490                              wl-thread-entity-hashtb))
491       (setq msgs (cdr msgs)))))
492
493 (defun wl-thread-get-exist-children (msg &optional include-self)
494   (let ((msgs (list msg))
495         msgs-stack children
496         entity ret-val)
497     (while msgs
498       (setq children (wl-thread-entity-get-children
499                       (setq entity (wl-thread-get-entity (car msgs)))))
500       (when (elmo-message-entity wl-summary-buffer-elmo-folder (car msgs))
501         (wl-append ret-val (list (car msgs)))
502         (setq children nil))
503       (setq msgs (cdr msgs))
504       (if (null children)
505           (while (and (null msgs) msgs-stack)
506             (setq msgs (wl-pop msgs-stack)))
507         (wl-push msgs msgs-stack)
508         (setq msgs children)))
509     (unless include-self (setq ret-val (delq msg ret-val)))
510     ret-val))
511
512 (defun wl-thread-delete-message (msg &optional deep update)
513   "Delete MSG from entity and buffer."
514   (save-excursion
515     (let ((entity (wl-thread-get-entity msg))
516           top-child top-entity update-msgs invisible-top)
517       (setq wl-summary-buffer-number-list
518             (delq msg wl-summary-buffer-number-list))
519       (when entity
520         (let ((parent (wl-thread-entity-get-parent-entity entity)))
521           (if parent
522               ;; has parent.
523               (let (children
524                     (older-brothers (wl-thread-entity-get-older-brothers
525                                      entity parent))
526                     (younger-brothers (wl-thread-entity-get-younger-brothers
527                                        entity parent)))
528                 (unless deep
529                   (setq children (wl-thread-entity-get-children entity))
530                   (wl-thread-reparent-children
531                    children (wl-thread-entity-get-number parent))
532                   (setq update-msgs
533                         (apply (function nconc)
534                                update-msgs
535                                (mapcar
536                                 (function
537                                  (lambda (message)
538                                    (wl-thread-get-children-msgs message t)))
539                                 children))))
540                 (wl-thread-entity-set-children
541                  parent (append older-brothers children younger-brothers))
542                 ;; If chidren and younger-brothers do not exist,
543                 ;; update nearly older brother.
544                 (when (and older-brothers
545                            (not younger-brothers)
546                            (not children))
547                   (wl-append
548                    update-msgs
549                    (wl-thread-get-children-msgs (car (last older-brothers))))))
550             ;; top...oldest child becomes top.
551             (unless deep
552               (let ((children (wl-thread-entity-get-children entity)))
553                 (when children
554                   (setq top-child (car children)
555                         children (cdr children))
556                   (setq top-entity (wl-thread-get-entity top-child))
557                   (wl-thread-entity-set-parent top-entity nil)
558                   (wl-thread-entity-set-linked top-entity nil)
559                   (wl-append update-msgs
560                              (wl-thread-get-children-msgs top-child t)))
561                 (when children
562                   (wl-thread-entity-set-children
563                    top-entity
564                    (append
565                     (wl-thread-entity-get-children top-entity)
566                     children))
567                   (wl-thread-reparent-children children top-child)
568                   (wl-append update-msgs children))))
569             ;; delete myself from top list.
570             (let ((match (memq msg wl-thread-entity-list)))
571               (when match
572                 (if top-child
573                     (setcar match top-child)
574                   (setq wl-thread-entity-list
575                         (delq msg wl-thread-entity-list))))))))
576       ;;
577       (if deep
578           ;; delete thread on buffer
579           (when (wl-summary-jump-to-msg msg)
580             (let ((beg (point)))
581               (wl-thread-goto-bottom-of-sub-thread)
582               (delete-region beg (point))))
583         ;; delete myself from buffer.
584         (unless (wl-thread-delete-line-from-buffer msg)
585           ;; jump to suitable point.
586           ;; just upon the oldest younger-brother of my top.
587           (setq invisible-top
588                 (car (wl-thread-entity-parent-invisible-p entity)))
589           (if invisible-top
590               (progn
591                 (wl-append update-msgs (list invisible-top))
592                 (wl-summary-jump-to-msg invisible-top))
593             (goto-char (point-max))))
594
595         ;; insert children if thread is closed or delete top.
596         (when (or top-child
597                   (not (wl-thread-entity-get-opened entity)))
598           (let (next-top insert-msgs ent grandchildren)
599             (if top-child
600                 (progn
601                   (setq insert-msgs (wl-thread-get-exist-children
602                                      top-child 'include-self))
603                   (setq next-top (car insert-msgs))
604                   (setq ent (wl-thread-get-entity next-top))
605                   (when (and
606                          (wl-thread-entity-get-opened entity) ;; open
607                          (not (wl-thread-entity-get-opened ent)) ;; close
608                          (setq grandchildren
609                                (wl-thread-entity-get-children ent))
610                          (wl-summary-jump-to-msg next-top))
611                     (forward-line 1)
612                     (setq insert-msgs (append (cdr insert-msgs) grandchildren)))
613                   (when top-entity (wl-thread-entity-set-opened top-entity t))
614                   (when ent (wl-thread-entity-set-opened ent t)))
615               (when (not invisible-top)
616                 (setq insert-msgs (wl-thread-get-exist-children msg))
617                 ;; First msg always opened, because first msg maybe becomes top.
618                 (if (setq ent (wl-thread-get-entity (car insert-msgs)))
619                     (wl-thread-entity-set-opened ent t))))
620             ;; insert children
621             (while insert-msgs
622               ;; if no exists in summary, insert entity.
623               (when (and (car insert-msgs)
624                          (not (wl-summary-jump-to-msg (car insert-msgs))))
625                 (setq ent (wl-thread-get-entity (car insert-msgs)))
626                 (wl-thread-insert-entity 0 ; no mean now...
627                                          ent entity nil))
628               (setq insert-msgs (cdr insert-msgs))))))
629       (if update
630           ;; modify buffer.
631           (while update-msgs
632             (wl-thread-update-line-on-buffer-sub nil (pop update-msgs)))
633         ;; don't update buffer
634         update-msgs)))) ; return value
635
636 (defun wl-thread-insert-message (message-entity
637                                  msg parent-msg &optional update linked)
638   "Insert MSG to the entity.
639 When optional argument UPDATE is non-nil,
640 Message is inserted to the summary buffer."
641   (let ((parent (wl-thread-get-entity parent-msg))
642         child-entity invisible-top)
643 ;;; Update the thread view...not implemented yet.
644 ;;;  (when force-insert
645 ;;;    (if parent
646 ;;;       (wl-thread-entity-force-open parent))
647     (when (and wl-summary-max-thread-depth parent)
648       (let ((cur parent)
649             (depth 0))
650         (while cur
651           (incf depth)
652           (setq cur (wl-thread-entity-get-parent-entity cur)))
653         (when (> depth wl-summary-max-thread-depth)
654           (setq parent nil
655                 parent-msg nil))))
656     (if parent
657         ;; insert as children.
658         (wl-thread-entity-insert-as-children
659          parent
660          (setq child-entity (wl-thread-create-entity
661                              msg (nth 0 parent) nil linked)))
662       ;; insert as top message.
663       (wl-thread-entity-insert-as-top
664        (wl-thread-create-entity msg nil)))
665     (if update
666         (if (not (setq invisible-top
667                        (wl-thread-entity-parent-invisible-p child-entity)))
668             ;; visible.
669             (progn
670               (wl-summary-update-thread
671                message-entity
672                child-entity
673                (elmo-message-entity wl-summary-buffer-elmo-folder
674                                     parent-msg))
675               (when parent
676                 ;; use thread structure.
677                 ;;(wl-thread-entity-get-nearly-older-brother
678                 ;; child-entity parent))) ; return value
679                 (wl-thread-entity-get-number parent))) ; return value
680 ;;;           (setq beg (point))
681 ;;;           (wl-thread-goto-bottom-of-sub-thread)
682 ;;;           (wl-thread-update-indent-string-region beg (point)))
683           ;; currently invisible.. update closed line.
684           (wl-thread-update-children-number invisible-top)
685           nil))))
686
687 ;(defun wl-thread-get-parent-list (msgs)
688 ;  ;; return ancestors
689 ;  (let* ((msgs2 msgs)
690 ;        myself)
691 ;    (while msgs2
692 ;      (setq myself (car msgs2)
693 ;           msgs2 (cdr msgs2))
694 ;      (while (not (eq myself (car msgs2)))
695 ;       (if (wl-thread-descendant-p myself (car msgs2))
696 ;           (setq msgs (delq (car msgs2) msgs)))
697 ;       (setq msgs2 (or (cdr msgs2) msgs)))
698 ;      (setq msgs2 (cdr msgs2)))
699 ;    msgs))
700
701 (defun wl-thread-get-parent-list (msgs)
702   ;; return connected ancestors
703   (let ((ptr msgs)
704         parent ret)
705     (while (car ptr)
706       (setq parent (wl-thread-entity-get-parent (wl-thread-get-entity (car ptr))))
707       (when (or (not parent)
708                 (not (memq parent msgs)))
709         (setq ret (append ret (list (car ptr)))))
710       (setq ptr (cdr ptr)))
711     ret))
712
713 (defun wl-thread-update-indent-string-thread (top-list)
714   (let* ((top-list (wl-thread-get-parent-list top-list))
715          (num (length top-list))
716          (i 0)
717          beg)
718     (while top-list
719       (when (> num elmo-display-progress-threshold)
720         (setq i (1+ i))
721         (when (or (zerop (% i 5)) (= i num))
722           (elmo-display-progress
723            'wl-thread-update-indent-string-thread
724            "Updating thread indent..."
725            (/ (* i 100) num))))
726       (when (car top-list)
727         (wl-summary-jump-to-msg (car top-list))
728         (setq beg (point))
729         (wl-thread-goto-bottom-of-sub-thread)
730         (wl-thread-update-indent-string-region beg (point)))
731       (setq top-list (cdr top-list)))
732     (message "Updating thread indent...done")))
733
734 (defun wl-thread-update-children-number (entity)
735   "Update the children number."
736   (wl-thread-update-line-on-buffer (wl-thread-entity-get-number entity)))
737
738 ;;
739 ;; Thread oriented commands.
740 ;;
741 (defun wl-thread-call-region-func (func &optional arg)
742   (save-excursion
743     (if arg
744         (wl-summary-goto-top-of-current-thread)
745       (beginning-of-line))
746     (let ((beg (point)))
747       (wl-thread-goto-bottom-of-sub-thread)
748       (funcall func beg (point)))))
749
750 (defun wl-thread-prefetch (&optional arg)
751   (interactive "P")
752   (wl-thread-call-region-func 'wl-summary-prefetch-region arg))
753
754 (defun wl-thread-mark-as-read (&optional arg)
755   (interactive "P")
756   (wl-thread-call-region-func 'wl-summary-mark-as-read-region arg))
757
758 (defun wl-thread-mark-as-unread (&optional arg)
759   (interactive "P")
760   (wl-thread-call-region-func 'wl-summary-mark-as-unread-region arg))
761
762 (defun wl-thread-mark-as-important (&optional arg)
763   (interactive "P")
764   (wl-thread-call-region-func 'wl-summary-mark-as-important-region arg))
765
766 (defun wl-thread-set-flags (&optional arg)
767   (interactive "P")
768   (wl-thread-call-region-func 'wl-summary-set-flags-region arg))
769
770 (defun wl-thread-mark-as-answered (&optional arg)
771   (interactive "P")
772   (wl-thread-call-region-func 'wl-summary-mark-as-answered-region arg))
773
774 (defun wl-thread-unmark (&optional arg)
775   (interactive "P")
776   (wl-thread-call-region-func 'wl-summary-unmark-region arg))
777
778 (defun wl-thread-exec (&optional arg)
779   (interactive "P")
780   (wl-thread-call-region-func 'wl-summary-exec-region arg))
781
782 (defun wl-thread-save (&optional arg)
783   (interactive "P")
784   (wl-thread-call-region-func 'wl-summary-save-region arg))
785
786 (defun wl-thread-force-open (&optional msg-num)
787   "force open current folder"
788   (if msg-num
789       (wl-summary-jump-to-msg msg-num))
790   (let ((wl-thread-insert-force-opened t))
791     (wl-thread-open-close)))
792
793 (defun wl-thread-entity-force-open (entity)
794   (let ((wl-thread-insert-force-opened t)
795         notopen)
796     (if (null (wl-thread-entity-get-parent entity))
797         ;; top!!
798         (if (and (not (wl-thread-entity-get-opened entity))
799                  (wl-thread-entity-get-children entity))
800             (wl-thread-force-open (wl-thread-entity-get-number entity)))
801       (if (setq notopen (wl-thread-entity-parent-invisible-p entity))
802           (wl-thread-force-open (wl-thread-entity-get-number notopen))))))
803
804 (defun wl-thread-insert-top ()
805   (let ((elist wl-thread-entity-list)
806         (len (length wl-thread-entity-list))
807         (cur 0))
808     (wl-delete-all-overlays)
809     (while elist
810       (wl-thread-insert-entity
811        0
812        (wl-thread-get-entity (car elist))
813        nil
814        len)
815       (setq elist (cdr elist))
816       (when (> len elmo-display-progress-threshold)
817         (setq cur (1+ cur))
818         (if (or (zerop (% cur 2)) (= cur len))
819             (elmo-display-progress
820              'wl-thread-insert-top "Inserting message..."
821              (/ (* cur 100) len)))))))
822
823 (defsubst wl-thread-insert-entity-sub (indent entity parent-entity all)
824   (let (msg-num
825         message-entity
826         temp-mark
827         summary-line)
828     (when (setq msg-num (wl-thread-entity-get-number entity))
829       (unless all ; all...means no temp-mark.
830         (cond ((memq msg-num wl-summary-buffer-target-mark-list)
831                (setq temp-mark "*"))
832               ((setq temp-mark (wl-summary-registered-temp-mark msg-num))
833                (setq temp-mark (nth 1 temp-mark)))))
834       (unless temp-mark
835         (setq temp-mark (wl-summary-get-score-mark msg-num)))
836       (setq message-entity
837             (elmo-message-entity wl-summary-buffer-elmo-folder
838                                  (nth 0 entity)))
839 ;;;   (wl-delete-all-overlays)
840       (when message-entity
841         (wl-summary-insert-line
842          (wl-summary-create-line
843           message-entity
844           (elmo-message-entity wl-summary-buffer-elmo-folder
845                                (nth 0 parent-entity))
846           temp-mark
847           (elmo-message-flags wl-summary-buffer-elmo-folder
848                               msg-num)
849           (elmo-message-cached-p wl-summary-buffer-elmo-folder
850                                  msg-num)
851           (if wl-thread-insert-force-opened
852               nil
853             (wl-thread-maybe-get-children-num msg-num))
854           (wl-thread-make-indent-string entity)
855           (wl-thread-entity-get-linked entity)))))))
856
857 (defun wl-thread-insert-entity (indent entity parent-entity all)
858   "Insert thread entity in current buffer."
859   (let ((msgs (list (car entity)))
860         children msgs-stack)
861     (while msgs
862       (wl-thread-insert-entity-sub indent entity parent-entity all)
863       (setq msgs (cdr msgs))
864       (setq children (nth 2 entity))
865       (if children
866           ;; insert children
867           (when (or wl-thread-insert-force-opened
868                     (wl-thread-entity-get-opened entity))
869             (wl-thread-entity-set-opened entity t)
870             (wl-push msgs msgs-stack)
871             (setq msgs children
872                   indent (1+ indent)
873                   parent-entity entity)))
874       (unless msgs
875         (while (and (null msgs) msgs-stack)
876           (setq msgs (wl-pop msgs-stack))
877           (setq indent (1- indent)))
878         (when msgs
879           (setq entity (wl-thread-get-entity (car msgs)))
880           (setq parent-entity (wl-thread-entity-get-parent-entity entity))))
881       (setq entity (wl-thread-get-entity (car msgs))))))
882
883 (defun wl-thread-descendant-p (mynumber number)
884   (let ((cur (wl-thread-get-entity number))
885         num)
886     (catch 'done
887       (while cur
888         (setq cur (wl-thread-entity-get-parent-entity cur))
889         (if (null (setq num (wl-thread-entity-get-number cur))) ; top!
890             (throw 'done nil))
891         (if (and num
892                  (eq mynumber (wl-thread-entity-get-number cur)))
893             (throw 'done t)))
894       nil)))
895
896 ;; (defun wl-thread-goto-bottom-of-sub-thread ()
897 ;;   (interactive)
898 ;;   (let ((depth (wl-thread-get-depth-of-current-line)))
899 ;;     (forward-line 1)
900 ;;     (while (and (not (eobp))
901 ;;              (> (wl-thread-get-depth-of-current-line)
902 ;;                 depth))
903 ;;       (forward-line 1))
904 ;;     (beginning-of-line)))
905
906 (defun wl-thread-goto-bottom-of-sub-thread (&optional msg)
907   (interactive)
908   (let ((mynumber (or msg (wl-summary-message-number))))
909     (forward-line 1)
910     (while (wl-thread-descendant-p mynumber (wl-summary-message-number))
911       (forward-line 1))
912     (beginning-of-line)))
913
914 (defun wl-thread-remove-argument-region (beg end)
915   (save-excursion
916     (save-restriction
917       (narrow-to-region beg end)
918       (goto-char (point-min))
919       (while (not (eobp))
920         (wl-summary-remove-argument)
921         (forward-line 1)))))
922
923 (defun wl-thread-print-argument-region (beg end)
924   (if wl-summary-buffer-temp-mark-list
925       (save-excursion
926         (save-restriction
927           (narrow-to-region beg end)
928           (goto-char (point-min))
929           (while (not (eobp))
930             (let ((num (wl-summary-message-number))
931                   temp-mark pair)
932               (when (and (setq temp-mark
933                                (wl-summary-registered-temp-mark num))
934                          (nth 2 temp-mark)
935                          (setq pair (cons (nth 0 temp-mark)(nth 2 temp-mark))))
936                 (wl-summary-print-argument (car pair) (cdr pair))))
937             (forward-line 1))))))
938
939 (defsubst wl-thread-get-children-msgs (msg &optional visible-only)
940   (let ((msgs (list msg))
941         msgs-stack children
942         entity ret-val)
943     (while msgs
944       (wl-append ret-val (list (car msgs)))
945       (setq children (wl-thread-entity-get-children
946                       (setq entity (wl-thread-get-entity (car msgs)))))
947       (if (and visible-only
948                (not (wl-thread-entity-get-opened entity)))
949           (setq children nil))
950       (setq msgs (cdr msgs))
951       (if (null children)
952           (while (and (null msgs) msgs-stack)
953             (setq msgs (wl-pop msgs-stack)))
954         (wl-push msgs msgs-stack)
955         (setq msgs children)))
956     ret-val))
957
958 (defun wl-thread-get-children-msgs-uncached (msg &optional uncached-marks)
959   (let ((children-msgs (wl-thread-get-children-msgs msg))
960         mark uncached-list)
961     (while children-msgs
962       (if (and (not (eq msg (car children-msgs))) ; except itself
963                (or (and uncached-marks
964                         (setq mark (wl-summary-message-mark
965                                     wl-summary-buffer-elmo-folder
966                                     (car children-msgs)))
967                         (member mark uncached-marks))
968                    (and (not uncached-marks)
969                         (null (elmo-file-cache-exists-p
970                                (elmo-message-field
971                                 wl-summary-buffer-elmo-folder
972                                 (car children-msgs)
973                                 'message-id))))))
974           (wl-append uncached-list (list (car children-msgs))))
975       (setq children-msgs (cdr children-msgs)))
976     uncached-list))
977
978 (defun wl-thread-get-children-msgs-with-mark (msg mark)
979   (let ((children-msgs (wl-thread-get-children-msgs msg))
980         (check-func (cond ((string= mark "o")
981                            'wl-summary-msg-marked-as-refiled)
982                           ((string= mark "O")
983                            'wl-summary-msg-marked-as-copied)
984                           ((string= mark "D")
985                            'wl-summary-msg-marked-as-deleted)
986                           ((string= mark "*")
987                            'wl-summary-msg-marked-as-target)))
988         ret-val)
989     (while children-msgs
990       (if (funcall check-func (car children-msgs))
991           (wl-append ret-val (list (car children-msgs))))
992       (setq children-msgs (cdr children-msgs)))
993     ret-val))
994
995 (defun wl-thread-close (entity)
996   (let (depth beg)
997     (wl-thread-entity-set-opened entity nil)
998     (setq depth (wl-thread-get-depth-of-current-line))
999     (beginning-of-line)
1000     (setq beg (point))
1001     (wl-thread-goto-bottom-of-sub-thread)
1002     (wl-thread-remove-argument-region beg
1003                                       (point))
1004     (forward-char -1)   ;; needed for mouse-face.
1005     (delete-region beg (point))
1006     (wl-thread-insert-entity (- depth 1)
1007                              entity
1008                              (wl-thread-get-entity
1009                               (nth 3 entity))
1010                              nil)
1011     (delete-char 1) ; delete '\n'
1012     (wl-thread-print-argument-region beg (point))))
1013
1014 (defun wl-thread-open (entity)
1015   (let (depth beg)
1016     (beginning-of-line)
1017     (setq beg (point))
1018     (setq depth (wl-thread-get-depth-of-current-line))
1019     (end-of-line)
1020     (delete-region beg (point))
1021     (wl-thread-entity-set-opened entity t)
1022     (wl-thread-insert-entity depth ;(- depth 1)
1023                              entity
1024                              (wl-thread-get-entity
1025                               (nth 3 entity)) nil)
1026     (delete-char 1) ; delete '\n'
1027     (wl-thread-print-argument-region beg (point))))
1028
1029 (defun wl-thread-open-close (&optional force-open)
1030   (interactive "P")
1031   (when (eq wl-summary-buffer-view 'thread)
1032 ;;; (if (equal wl-thread-top-entity '(nil t nil nil))
1033 ;;;     (error "There's no thread structure"))
1034     (save-excursion
1035       (let ((inhibit-read-only t)
1036             (buffer-read-only nil)
1037             (wl-thread-insert-force-opened
1038              (or wl-thread-insert-force-opened
1039                  force-open))
1040             msg entity parent)
1041         (setq msg (wl-summary-message-number))
1042         (setq entity (wl-thread-get-entity msg))
1043         (if (wl-thread-entity-get-opened entity)
1044             ;; if already opened, close its child!
1045           (if (wl-thread-entity-get-children entity)
1046               (wl-thread-close entity)
1047             ;; opened, but has no children, close its parent!
1048             (when (setq parent (wl-thread-entity-get-parent entity))
1049               (wl-summary-jump-to-msg parent)
1050               (wl-thread-close
1051                (wl-thread-get-entity (wl-summary-message-number)))))
1052           ;; if closed (or it is just a thread bottom message)
1053           ;; has children, open it!
1054           (if (wl-thread-entity-get-children entity)
1055               (wl-thread-open entity)
1056             ;; closed, and has no children, close its parent!
1057             (setq msg (or (wl-thread-entity-get-parent entity)
1058                           (wl-thread-entity-get-number entity)))
1059             (when msg
1060               (wl-summary-jump-to-msg msg)
1061               (wl-thread-close
1062                (wl-thread-get-entity (wl-summary-message-number)))))))
1063       (when wl-summary-lazy-highlight
1064         (wl-highlight-summary-window))
1065       (wl-summary-set-message-modified)
1066       (set-buffer-modified-p nil))))
1067
1068 (defun wl-thread-get-depth-of-current-line ()
1069   (let ((entity (wl-thread-get-entity (wl-summary-message-number)))
1070         (depth 0)
1071         number)
1072     (while (setq number (wl-thread-entity-get-parent entity))
1073       (incf depth)
1074       (setq entity (wl-thread-get-entity number)))
1075     depth))
1076
1077 (defun wl-thread-update-indent-string-region (beg end)
1078   (interactive "r")
1079   (save-excursion
1080     (goto-char beg)
1081     (while (< (point) end)
1082       (save-excursion
1083         (wl-thread-update-line-on-buffer-sub nil (wl-summary-message-number)))
1084       (forward-line 1))))
1085
1086 (defsubst wl-thread-make-indent-string (entity)
1087   (let ((cur entity)
1088         (ret-val "")
1089         (space-str (wl-repeat-string wl-thread-space-str-internal
1090                                      (- wl-thread-indent-level-internal 1)))
1091         parent)
1092     (when (wl-thread-entity-get-number
1093            (setq parent (wl-thread-entity-get-parent-entity cur)))
1094       (if (wl-thread-entity-get-younger-brothers cur)
1095           (setq ret-val wl-thread-have-younger-brother-str-internal)
1096         (setq ret-val wl-thread-youngest-child-str-internal))
1097       (setq ret-val (concat ret-val
1098                             (wl-repeat-string
1099                              wl-thread-horizontal-str-internal
1100                              (- wl-thread-indent-level-internal 1))))
1101       (setq cur parent)
1102       (while (wl-thread-entity-get-number
1103               (wl-thread-entity-get-parent-entity cur))
1104         (if (wl-thread-entity-get-younger-brothers cur)
1105             (setq ret-val (concat wl-thread-vertical-str-internal
1106                                   space-str
1107                                   ret-val))
1108           (setq ret-val (concat wl-thread-space-str-internal
1109                                 space-str
1110                                 ret-val)))
1111         (setq cur (wl-thread-entity-get-parent-entity cur))))
1112     ret-val))
1113
1114 (defun wl-thread-set-parent (&optional parent-number)
1115   "Set current message's parent interactively."
1116   (interactive)
1117   (let ((number (wl-summary-message-number))
1118         (dst-parent (if (interactive-p)
1119                         (read-from-minibuffer "Parent Message (No.): ")))
1120         entity dst-parent-entity src-parent children
1121         update-msgs
1122         buffer-read-only)
1123     (if (string= dst-parent "")
1124         (setq dst-parent nil)
1125       (if (interactive-p)
1126           (setq dst-parent (string-to-int dst-parent))
1127         (setq dst-parent parent-number)))
1128     (if (and dst-parent
1129              (memq dst-parent (wl-thread-get-children-msgs number)))
1130         (error "Parent is children or myself"))
1131     (setq entity (wl-thread-get-entity number))
1132     (when (and number entity)
1133       ;; delete thread
1134       (setq update-msgs (wl-thread-delete-message number 'deep))
1135       ;; insert as child at new parent
1136       (setq dst-parent-entity (wl-thread-get-entity dst-parent))
1137       (if dst-parent-entity
1138           (progn
1139             (if (setq children
1140                       (wl-thread-entity-get-children dst-parent-entity))
1141                 (wl-append update-msgs
1142                            (wl-thread-get-children-msgs
1143                             (car (last children)) t)))
1144             (wl-thread-entity-set-children
1145              dst-parent-entity
1146              (append children (list number)))
1147             (wl-thread-entity-set-linked entity t))
1148         ;; insert as top
1149         (wl-append wl-thread-entity-list (list number))
1150         (wl-thread-entity-set-linked entity nil))
1151
1152       ;; update my thread
1153       (wl-append update-msgs (wl-thread-get-children-msgs number t))
1154       (setq update-msgs (elmo-uniq-list update-msgs))
1155       (wl-thread-entity-set-parent entity dst-parent)
1156       ;; update thread on buffer
1157       (wl-thread-make-number-list)
1158       (wl-thread-update-line-msgs update-msgs t))))
1159
1160 (require 'product)
1161 (product-provide (provide 'wl-thread) (require 'wl-version))
1162
1163 ;;; wl-thread.el ends here