diff lisp/vm/vm-thread.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 859a2309aef8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/vm/vm-thread.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,236 @@
+;;; Thread support for VM
+;;; Copyright (C) 1994 Kyle E. Jones
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 1, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(provide 'vm-thread)
+
+(defun vm-toggle-threads-display ()
+  "Toggle the threads display on and off.
+When the threads display is on, the folder will be sorted by
+thread and thread indentation (via the %I summary format specifier)
+will be visible."
+  (interactive)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-set-summary-redo-start-point t)
+  (setq vm-summary-show-threads (not vm-summary-show-threads))
+  (if vm-summary-show-threads
+      (vm-sort-messages "thread")
+    (vm-sort-messages "physical-order")))
+
+(defun vm-build-threads (message-list)
+  (if (null vm-thread-obarray)
+      (setq vm-thread-obarray (make-vector 641 0)
+	    vm-thread-subject-obarray (make-vector 641 0)))
+  (let ((mp (or message-list vm-message-list))
+	(n 0)
+	;; Just for laughs, make the update interval vary.
+	(modulus (+ (% (vm-abs (random)) 11) 40))
+	;; no need to schedule reindents of reparented messages
+	;; unless there were already messages present.
+	(schedule-reindents message-list)
+	parent parent-sym id id-sym date)
+    (while mp
+      (setq parent (vm-th-parent (car mp))
+	    id (vm-su-message-id (car mp))
+	    id-sym (intern id vm-thread-obarray)
+	    date (vm-so-sortable-datestring (car mp)))
+      (put id-sym 'messages (cons (car mp) (get id-sym 'messages)))
+      (if (and (null (cdr (get id-sym 'messages)))
+	       schedule-reindents)
+	  (vm-thread-mark-for-summary-update (get id-sym 'children)))
+      (if parent
+	  (progn
+	    (setq parent-sym (intern parent vm-thread-obarray))
+	    (if (not (boundp id-sym))
+		(set id-sym parent-sym))
+	    (put parent-sym 'children
+		 (cons (car mp) (get parent-sym 'children))))
+	(set id-sym nil))
+      ;; we need to make sure the asets below are an atomic group.
+      (if vm-thread-using-subject
+	  (let* ((inhibit-quit t)
+		 (subject (vm-so-sortable-subject (car mp)))
+		 (subject-sym (intern subject vm-thread-subject-obarray)))
+	    (if (not (boundp subject-sym))
+		(set subject-sym
+		     (vector id-sym (vm-so-sortable-datestring (car mp))
+			     nil (list (car mp))))
+	      (aset (symbol-value subject-sym) 3
+		    (cons (car mp) (aref (symbol-value subject-sym) 3)))
+	      (if (string< date (aref (symbol-value subject-sym) 1))
+		  (let* ((vect (symbol-value subject-sym))
+			 (i-sym (aref vect 0)))
+		    (if (or (not (boundp i-sym))
+			    (null (symbol-value i-sym)))
+			(aset vect 2 (append (get i-sym 'messages)
+					     (aref vect 2))))
+		    (aset vect 0 id-sym)
+		    (aset vect 1 date)
+		    ;; this loops _and_ recurses and I'm worried
+		    ;; about it going into a spin someday.  So I
+		    ;; unblock interrupts here.  It's not critical
+		    ;; that it finish... the summary will just be out
+		    ;; of sync.
+		    (if schedule-reindents
+			(let ((inhibit-quit nil))
+			  (vm-thread-mark-for-summary-update (aref vect 2)))))
+		(if (null parent)
+		    (aset (symbol-value subject-sym) 2
+			  (cons (car mp)
+				(aref (symbol-value subject-sym) 2))))))))
+      (setq mp (cdr mp) n (1+ n))
+      (if (zerop (% n modulus))
+	  (vm-unsaved-message "Building threads... %d" n)))
+    (if (> n modulus)
+	(vm-unsaved-message "Building threads... done"))))
+
+(defun vm-thread-mark-for-summary-update (message-list)
+  (while message-list
+    (vm-mark-for-summary-update (car message-list) t)
+    (vm-set-thread-list-of (car message-list) nil)
+    (vm-set-thread-indentation-of (car message-list) nil)
+    (vm-thread-mark-for-summary-update
+     (get (intern (vm-su-message-id (car message-list))
+		  vm-thread-obarray)
+	  'children))
+    (setq message-list (cdr message-list))))
+
+(defun vm-thread-list (message)
+  (let ((done nil)
+	(m message)
+	thread-list id-sym subject-sym loop-sym root-date)
+    (save-excursion
+      (set-buffer (vm-buffer-of m))
+      (setq id-sym (intern (vm-su-message-id m) vm-thread-obarray)
+	    thread-list (list id-sym))
+      (fillarray vm-thread-loop-obarray 0)
+      (while (not done)
+	(setq loop-sym (intern (symbol-name id-sym) vm-thread-loop-obarray))
+	(if (boundp loop-sym)
+	    ;; loop detected, bail...
+	    (setq done t
+		  thread-list (cdr thread-list))
+	  (set loop-sym t)
+	  (if (and (boundp id-sym) (symbol-value id-sym))
+	      (progn
+		(setq id-sym (symbol-value id-sym)
+		      thread-list (cons id-sym thread-list)
+		      m (car (get id-sym 'messages))))
+	    (if (null m)
+		(setq done t)
+	      (if (null vm-thread-using-subject)
+		  nil
+		(setq subject-sym
+		      (intern (vm-so-sortable-subject m)
+			      vm-thread-subject-obarray))
+		(if (or (not (boundp subject-sym))
+			(eq (aref (symbol-value subject-sym) 0) id-sym))
+		    (setq done t)
+		  (setq id-sym (aref (symbol-value subject-sym) 0)
+			thread-list (cons id-sym thread-list)
+			m (car (get id-sym 'messages)))))))))
+      ;; save the date of the oldest message in this thread
+      (setq root-date (get id-sym 'oldest-date))
+      (if (or (null root-date)
+	      (string< (vm-so-sortable-datestring message) root-date))
+	  (put id-sym 'oldest-date (vm-so-sortable-datestring message)))
+      thread-list )))
+
+;; remove message struct from thread data.
+;;
+;; optional second arg non-nil means forget information that
+;; might be different if the mesage contents changed.
+;;
+;; message must be a real message
+(defun vm-unthread-message (message &optional message-changing)
+  (save-excursion
+    (let ((mp (cons message (vm-virtual-messages-of message)))
+	  id-sym subject-sym vect p-sym)
+      (while mp
+	(let ((inhibit-quit t))
+	  (vm-set-thread-list-of (car mp) nil)
+	  (vm-set-thread-indentation-of (car mp) nil)
+	  (set-buffer (vm-buffer-of (car mp)))
+	  (setq id-sym (intern (vm-su-message-id (car mp)) vm-thread-obarray)
+		subject-sym (intern (vm-so-sortable-subject (car mp))
+				    vm-thread-subject-obarray))
+	  (if (boundp id-sym)
+	      (progn
+		(put id-sym 'messages (delq (car mp) (get id-sym 'messages)))
+		(vm-thread-mark-for-summary-update (get id-sym 'children))
+		(setq p-sym (symbol-value id-sym))
+		(and p-sym (put p-sym 'children
+				(delq (car mp) (get p-sym 'children))))
+		(if message-changing
+		    (set id-sym nil))))
+	  (if (and (boundp subject-sym) (setq vect (symbol-value subject-sym)))
+	      (if (not (eq id-sym (aref vect 0)))
+		  (aset vect 2 (delq (car mp) (aref vect 2)))
+		(if message-changing
+		    (if (null (cdr (aref vect 3)))
+			(makunbound subject-sym)
+		      (let ((p (aref vect 3))
+			    oldest-msg oldest-date children)
+			(setq oldest-msg (car p)
+			      oldest-date (vm-so-sortable-datestring (car p))
+			      p (cdr p))
+			(while p
+			  (if (and (string-lessp (vm-so-sortable-datestring (car p))
+						 oldest-date)
+				   (not (eq (car mp) (car p))))
+			      (setq oldest-msg (car p)
+				    oldest-date (vm-so-sortable-datestring (car p))))
+			  (setq p (cdr p)))
+			(aset vect 0 (intern (vm-su-message-id oldest-msg)
+					     vm-thread-obarray))
+			(aset vect 1 oldest-date)
+			(setq children (delq oldest-msg (aref vect 2)))
+			(aset vect 2 children)
+			(aset vect 3 (delq (car mp) (aref vect 3)))
+			;; I'm not sure there aren't situations
+			;; where this might loop forever.
+			(let ((inhibit-quit nil))
+			  (vm-thread-mark-for-summary-update children))))))))
+	  (setq mp (cdr mp))))))
+
+(defun vm-th-parent (m)
+  (or (vm-parent-of m)
+      (vm-set-parent-of
+       m
+       (or (let (references)
+	     (setq references (vm-get-header-contents m "References:"))
+	     (and references
+		  (car (vm-last
+			(vm-parse references "[^<]*\\(<[^>]+>\\)")))))
+	   (let (in-reply-to)
+	     (setq in-reply-to (vm-get-header-contents m "In-Reply-To:"))
+	     (and in-reply-to
+		  (car (vm-parse in-reply-to "[^<]*\\(<[^>]+>\\)"))))))))
+
+(defun vm-th-thread-indentation (m)
+  (or (vm-thread-indentation-of m)
+      (let ((p (vm-th-thread-list m)))
+	(while (and p (null (get (car p) 'messages)))
+	  (setq p (cdr p)))
+	(vm-set-thread-indentation-of m (1- (length p)))
+	(vm-thread-indentation-of m))))
+
+(defun vm-th-thread-list (m)
+  (or (vm-thread-list-of m)
+      (progn
+	(vm-set-thread-list-of m (vm-thread-list m))
+	(vm-thread-list-of m))))