Mercurial > hg > xemacs-beta
diff lisp/vm/vm-sort.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 441bb1e64a06 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/vm/vm-sort.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,555 @@ +;;; Sorting and moving messages inside VM +;;; Copyright (C) 1993, 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-sort) + +(defun vm-move-message-forward (count) + "Move a message forward in a VM folder. +Prefix arg COUNT causes the current message to be moved COUNT messages forward. +A negative COUNT causes movement to be backward instead of forward. +COUNT defaults to 1. The current message remains selected after being +moved. + +If vm-move-messages-physically is non-nil, the physical copy of +the message in the folder is moved. A nil value means just +change the presentation order and leave the physical order of +the folder undisturbed." + (interactive "p") + (vm-follow-summary-cursor) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (if vm-move-messages-physically + (vm-error-if-folder-read-only)) + (vm-display nil nil '(vm-move-message-forward + vm-move-message-backward + vm-move-message-forward-physically + vm-move-message-backward-physically) + (list this-command)) + (let* ((ovmp vm-message-pointer) vmp-prev ovmp-prev + (vm-message-pointer vm-message-pointer) + (direction (if (> count 0) 'forward 'backward)) + (count (vm-abs count))) + (while (not (zerop count)) + (vm-move-message-pointer direction) + (vm-decrement count)) + (if (> (string-to-int (vm-number-of (car vm-message-pointer))) + (string-to-int (vm-number-of (car ovmp)))) + (setq vm-message-pointer (cdr vm-message-pointer))) + (if (eq vm-message-pointer ovmp) + () + (if (null vm-message-pointer) + (setq vmp-prev (vm-last vm-message-list)) + (setq vmp-prev (vm-reverse-link-of (car vm-message-pointer)))) + (setq ovmp-prev (vm-reverse-link-of (car ovmp))) + ;; lock out interrupts to preserve message list integrity. + (let ((inhibit-quit t)) + (if ovmp-prev + (progn + (setcdr ovmp-prev (cdr ovmp)) + (and (cdr ovmp) + (vm-set-reverse-link-of (car (cdr ovmp)) ovmp-prev))) + (setq vm-message-list (cdr ovmp)) + (vm-set-reverse-link-of (car vm-message-list) nil)) + (if vmp-prev + (progn + (setcdr vmp-prev ovmp) + (vm-set-reverse-link-of (car ovmp) vmp-prev)) + (setq vm-message-list ovmp) + (vm-set-reverse-link-of (car vm-message-list) nil)) + (setcdr ovmp vm-message-pointer) + (and vm-message-pointer + (vm-set-reverse-link-of (car vm-message-pointer) ovmp)) + (if (and vm-move-messages-physically + (not (eq major-mode 'vm-virtual-mode))) + (vm-physically-move-message (car ovmp) (car vm-message-pointer))) + (setq vm-ml-sort-keys nil) + (if (not vm-folder-read-only) + (progn + (setq vm-message-order-changed t) + (vm-set-buffer-modified-p t) + (vm-clear-modification-flag-undos)))) + (cond ((null ovmp-prev) + (setq vm-numbering-redo-start-point vm-message-list + vm-numbering-redo-end-point vm-message-pointer + vm-summary-pointer (car vm-message-list))) + ((null vmp-prev) + (setq vm-numbering-redo-start-point vm-message-list + vm-numbering-redo-end-point (cdr ovmp-prev) + vm-summary-pointer (car ovmp-prev))) + ((or (not vm-message-pointer) + (< (string-to-int (vm-number-of (car ovmp-prev))) + (string-to-int (vm-number-of (car vm-message-pointer))))) + (setq vm-numbering-redo-start-point (cdr ovmp-prev) + vm-numbering-redo-end-point (cdr ovmp) + vm-summary-pointer (car (cdr ovmp-prev)))) + (t + (setq vm-numbering-redo-start-point ovmp + vm-numbering-redo-end-point (cdr ovmp-prev) + vm-summary-pointer (car ovmp-prev)))) + (if vm-summary-buffer + (let (list mp) + (vm-copy-local-variables vm-summary-buffer 'vm-summary-pointer) + (setq vm-need-summary-pointer-update t) + (setq mp vm-numbering-redo-start-point) + (while (not (eq mp vm-numbering-redo-end-point)) + (vm-mark-for-summary-update (car mp)) + (setq list (cons (car mp) list) + mp (cdr mp))) + (vm-mapc + (function + (lambda (m p) + (vm-set-su-start-of m (car p)) + (vm-set-su-end-of m (car (cdr p))))) + (setq list (nreverse list)) + (sort + (mapcar + (function + (lambda (p) + (list (vm-su-start-of p) (vm-su-end-of p)))) + list) + (function + (lambda (p q) + (< (car p) (car q)))))))))) + (if vm-move-messages-physically + ;; clip region is messed up + (vm-preview-current-message) + (vm-update-summary-and-mode-line))) + +(defun vm-move-message-backward (count) + "Move a message backward in a VM folder. +Prefix arg COUNT causes the current message to be moved COUNT +messages backward. A negative COUNT causes movement to be +forward instead of backward. COUNT defaults to 1. The current +message remains selected after being moved. + +If vm-move-messages-physically is non-nil, the physical copy of +the message in the folder is moved. A nil value means just +change the presentation order and leave the physical order of +the folder undisturbed." + (interactive "p") + (vm-move-message-forward (- count))) + +(defun vm-move-message-forward-physically (count) + "Like vm-move-message-forward but always move the message physically." + (interactive "p") + (let ((vm-move-messages-physically t)) + (vm-move-message-forward count))) + +(defun vm-move-message-backward-physically (count) + "Like vm-move-message-backward but always move the message physically." + (interactive "p") + (let ((vm-move-messages-physically t)) + (vm-move-message-backward count))) + +;; move message m to be before m-dest +;; and fix up the location markers afterwards. +;; m better not equal m-dest. +;; of m-dest is nil, move m to the end of buffer. +;; +;; consider carefully the effects of insertion on markers +;; and variables containg markers before you modify this code. +(defun vm-physically-move-message (m m-dest) + (save-excursion + (vm-save-restriction + (widen) + + ;; Make sure vm-headers-of and vm-text-of are non-nil in + ;; their slots before we try to move them. (Simply + ;; referencing the slot with their slot function is + ;; sufficient to guarantee this.) Otherwise, they be + ;; initialized in the middle of the message move and get the + ;; offset applied to them twice by way of a relative offset + ;; from one of the other location markers that has already + ;; been moved. + ;; + ;; Also, and more importantly, vm-vheaders-of might run + ;; vm-reorder-message-headers, which can add text to + ;; message. This MUST NOT happen after offsets have been + ;; computed for the message move or varying levels of chaos + ;; will ensue. In the case of BABYL files, where + ;; vm-reorder-message-headers can add a lot of new text, + ;; folder curroption can be massive. + (vm-text-of m) + (vm-vheaders-of m) + + (let ((dest-start (if m-dest (vm-start-of m-dest) (point-max))) + (buffer-read-only nil) + offset doomed-start doomed-end) + (goto-char dest-start) + (insert-buffer-substring (current-buffer) (vm-start-of m) (vm-end-of m)) + (setq doomed-start (marker-position (vm-start-of m)) + doomed-end (marker-position (vm-end-of m)) + offset (- (vm-start-of m) dest-start)) + (set-marker (vm-start-of m) (- (vm-start-of m) offset)) + (set-marker (vm-headers-of m) (- (vm-headers-of m) offset)) + (set-marker (vm-text-end-of m) (- (vm-text-end-of m) offset)) + (set-marker (vm-end-of m) (- (vm-end-of m) offset)) + (set-marker (vm-text-of m) (- (vm-text-of m) offset)) + (set-marker (vm-vheaders-of m) (- (vm-vheaders-of m) offset)) + ;; now fix the start of m-dest since it didn't + ;; move forward with its message. + (and m-dest (set-marker (vm-start-of m-dest) (vm-end-of m))) + ;; delete the old copy of the message + (delete-region doomed-start doomed-end))))) + +(defun vm-so-sortable-datestring (m) + (or (vm-sortable-datestring-of m) + (progn + (vm-set-sortable-datestring-of + m + (timezone-make-date-sortable + (or (vm-get-header-contents m "Date:") + (vm-grok-From_-date m) + "Thu, 1 Jan 1970 00:00:00 GMT") + "GMT" "GMT")) + (vm-sortable-datestring-of m)))) + +(defun vm-so-sortable-subject (m) + (or (vm-sortable-subject-of m) + (progn + (vm-set-sortable-subject-of + m + (let ((case-fold-search t) + (subject (vm-su-subject m))) + (if (and vm-subject-ignored-prefix + (string-match vm-subject-ignored-prefix subject) + (zerop (match-beginning 0))) + (setq subject (substring subject (match-end 0)))) + (if (and vm-subject-ignored-suffix + (string-match vm-subject-ignored-suffix subject) + (= (match-end 0) (length subject))) + (setq subject (substring subject 0 (match-beginning 0)))) + subject )) + (vm-sortable-subject-of m)))) + +(defun vm-sort-messages (keys &optional lets-get-physical) + "Sort message in a folder by the specified KEYS. +You may sort by more than one particular message key. If +messages compare equal by the first key, the second key will be +compared and so on. When called interactively the keys will be +read from the minibuffer. Valid keys are + +\"date\" \"reversed-date\" +\"author\" \"reversed-author\" +\"subject\" \"reversed-subject\" +\"recipients\" \"reversed-recipients\" +\"line-count\" \"reversed-line-count\" +\"byte-count\" \"reversed-byte-count\" +\"physical-order\" \"reversed-physical-order\" + +Optional second arg (prefix arg interactively) means the sort +should change the physical order of the messages in the folder. +Normally VM changes presentation order only, leaving the +folder in the order in which the messages arrived." + (interactive + (let ((last-command last-command) + (this-command this-command)) + (list (vm-read-string (if (or current-prefix-arg + vm-move-messages-physically) + "Physically sort messages by: " + "Sort messages by: ") + vm-supported-sort-keys t) + current-prefix-arg))) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + ;; only squawk if interactive. The thread display uses this + ;; function and doesn't expect errors. + (if (interactive-p) + (vm-error-if-folder-empty)) + ;; ditto + (if (and (interactive-p) (or vm-move-messages-physically lets-get-physical)) + (vm-error-if-folder-read-only)) + (vm-display nil nil '(vm-sort-messages) '(vm-sort-messages)) + (let (key-list key-funcs key ml-keys + physical-order-list old-message-list new-message-list mp-old mp-new + old-start + doomed-start doomed-end offset + (order-did-change nil) + virtual + physical) + (setq key-list (vm-parse keys "[ \t]*\\([^ \t]+\\)") + ml-keys (and key-list (mapconcat (function identity) key-list "/")) + key-funcs nil + old-message-list vm-message-list + virtual (eq major-mode 'vm-virtual-mode) + physical (and (or lets-get-physical + vm-move-messages-physically) + (not vm-folder-read-only) + (not virtual))) + (or key-list (error "No sort keys specified.")) + (while key-list + (setq key (car key-list)) + (cond ((equal key "thread") + (vm-build-threads-if-unbuilt) + (setq key-funcs (cons 'vm-sort-compare-thread key-funcs))) + ((equal key "author") + (setq key-funcs (cons 'vm-sort-compare-author key-funcs))) + ((equal key "reversed-author") + (setq key-funcs (cons 'vm-sort-compare-author-r key-funcs))) + ((equal key "date") + (setq key-funcs (cons 'vm-sort-compare-date key-funcs))) + ((equal key "reversed-date") + (setq key-funcs (cons 'vm-sort-compare-date-r key-funcs))) + ((equal key "subject") + (setq key-funcs (cons 'vm-sort-compare-subject key-funcs))) + ((equal key "reversed-subject") + (setq key-funcs (cons 'vm-sort-compare-subject-r key-funcs))) + ((equal key "recipients") + (setq key-funcs (cons 'vm-sort-compare-recipients key-funcs))) + ((equal key "reversed-recipients") + (setq key-funcs (cons 'vm-sort-compare-recipients-r key-funcs))) + ((equal key "byte-count") + (setq key-funcs (cons 'vm-sort-compare-byte-count key-funcs))) + ((equal key "reversed-byte-count") + (setq key-funcs (cons 'vm-sort-compare-byte-count-r key-funcs))) + ((equal key "line-count") + (setq key-funcs (cons 'vm-sort-compare-line-count key-funcs))) + ((equal key "reversed-line-count") + (setq key-funcs (cons 'vm-sort-compare-line-count-r key-funcs))) + ((equal key "physical-order") + (setq key-funcs (cons 'vm-sort-compare-physical-order key-funcs))) + ((equal key "reversed-physical-order") + (setq key-funcs (cons 'vm-sort-compare-physical-order-r key-funcs))) + (t (error "Unknown key: %s" key))) + (setq key-list (cdr key-list))) + (vm-unsaved-message "Sorting...") + (let ((vm-key-functions (nreverse key-funcs))) + (setq new-message-list (sort (copy-sequence old-message-list) + 'vm-sort-compare-xxxxxx)) + ;; only need to do this sort if we're going to physically + ;; move messages later. + (if physical + (setq vm-key-functions '(vm-sort-compare-physical-order) + physical-order-list (sort (copy-sequence old-message-list) + 'vm-sort-compare-xxxxxx)))) + (vm-unsaved-message "Sorting... done") + (let ((inhibit-quit t)) + (setq mp-old old-message-list + mp-new new-message-list) + (while mp-new + (if (eq (car mp-old) (car mp-new)) + (setq mp-old (cdr mp-old) + mp-new (cdr mp-new)) + (setq order-did-change t) + ;; unless a full redo has been requested, the numbering + ;; start point now points to a cons in the old message + ;; list. therefore we just change the variable + ;; directly to avoid the list scan that + ;; vm-set-numbering-redo-start-point does. + (cond ((not (eq vm-numbering-redo-start-point t)) + (setq vm-numbering-redo-start-point mp-new + vm-numbering-redo-end-point nil))) + (if vm-summary-buffer + (progn + (setq vm-need-summary-pointer-update t) + ;; same logic as numbering reset above... + (cond ((not (eq vm-summary-redo-start-point t)) + (setq vm-summary-redo-start-point mp-new))) + ;; start point of this message's summary is now + ;; wrong relative to where it is in the + ;; message list. fix it and the summary rebuild + ;; will take care of the rest. + (vm-set-su-start-of (car mp-new) + (vm-su-start-of (car mp-old))))) + (setq mp-new nil))) + (if (and order-did-change physical) + (let ((buffer-read-only nil)) + ;; the folder is being physically ordered so we don't + ;; need a message order header to be stuffed, nor do + ;; we need to retain one in the folder buffer. so we + ;; strip out any existing message order header and + ;; say there are no changes to prevent a message + ;; order header from being stuffed later. + (vm-remove-message-order) + (setq vm-message-order-changed nil) + (vm-unsaved-message "Moving messages... ") + (widen) + (setq mp-old physical-order-list + mp-new new-message-list) + (setq old-start (vm-start-of (car mp-old))) + (while mp-new + (if (< (vm-start-of (car mp-old)) old-start) + ;; already moved this message + (setq mp-old (cdr mp-old)) + (if (eq (car mp-old) (car mp-new)) + (setq mp-old (cdr mp-old) + mp-new (cdr mp-new)) + ;; move message + (vm-physically-move-message (car mp-new) (car mp-old)) + ;; record start position. if vm-start-of + ;; mp-old ever becomes less than old-start + ;; we're running into messages that have + ;; already been moved. + (setq old-start (vm-start-of (car mp-old))) + ;; move mp-new but not mp-old because we moved + ;; mp-old down one message by inserting a + ;; message in front of it. + (setq mp-new (cdr mp-new))))) + (vm-unsaved-message "Moving messages... done") + (vm-set-buffer-modified-p t) + (vm-clear-modification-flag-undos)) + (if (and order-did-change (not vm-folder-read-only)) + (progn + (setq vm-message-order-changed t) + (vm-set-buffer-modified-p t) + (vm-clear-modification-flag-undos)))) + (setq vm-ml-sort-keys ml-keys) + (intern (buffer-name) vm-buffers-needing-display-update) + (cond (order-did-change + (setq vm-message-list new-message-list) + (vm-reverse-link-messages) + (if vm-message-pointer + (setq vm-message-pointer + (or (cdr (vm-reverse-link-of (car vm-message-pointer))) + vm-message-list))) + (if vm-last-message-pointer + (setq vm-last-message-pointer + (or (cdr (vm-reverse-link-of + (car vm-last-message-pointer))) + vm-message-list)))))) + (if (and vm-message-pointer + order-did-change + (or lets-get-physical vm-move-messages-physically)) + ;; clip region is most likely messed up + (vm-preview-current-message) + (vm-update-summary-and-mode-line)))) + +(defun vm-sort-compare-xxxxxx (m1 m2) + (let ((key-funcs vm-key-functions) result) + (while (and key-funcs + (eq '= (setq result (funcall (car key-funcs) m1 m2)))) + (setq key-funcs (cdr key-funcs))) + (and key-funcs result) )) + +(defun vm-sort-compare-thread (m1 m2) + (let ((list1 (vm-th-thread-list m1)) + (list2 (vm-th-thread-list m2))) + (catch 'done + (if (not (eq (car list1) (car list2))) + (let ((date1 (get (car list1) 'oldest-date)) + (date2 (get (car list2) 'oldest-date))) + (cond ((string-lessp date1 date2) t) + ((string-equal date1 date2) '=) + (t nil))) + (while (and list1 list2) + (cond ((string-lessp (car list1) (car list2)) (throw 'done t)) + ((not (string-equal (car list1) (car list2))) + (throw 'done nil))) + (setq list1 (cdr list1) + list2 (cdr list2))) + (cond ((and list1 (not list2)) nil) + ((and list2 (not list1)) t) + (t '=)))))) + +(defun vm-sort-compare-author (m1 m2) + (let ((s1 (vm-su-from m1)) + (s2 (vm-su-from m2))) + (cond ((string-lessp s1 s2) t) + ((string-equal s1 s2) '=) + (t nil)))) + +(defun vm-sort-compare-author-r (m1 m2) + (let ((s1 (vm-su-from m1)) + (s2 (vm-su-from m2))) + (cond ((string-lessp s1 s2) nil) + ((string-equal s1 s2) '=) + (t t)))) + +(defun vm-sort-compare-date (m1 m2) + (let ((s1 (vm-so-sortable-datestring m1)) + (s2 (vm-so-sortable-datestring m2))) + (cond ((string-lessp s1 s2) t) + ((string-equal s1 s2) '=) + (t nil)))) + +(defun vm-sort-compare-date-r (m1 m2) + (let ((s1 (vm-so-sortable-datestring m1)) + (s2 (vm-so-sortable-datestring m2))) + (cond ((string-lessp s1 s2) nil) + ((string-equal s1 s2) '=) + (t t)))) + +(defun vm-sort-compare-recipients (m1 m2) + (let ((s1 (vm-su-to m1)) + (s2 (vm-su-to m2))) + (cond ((string-lessp s1 s2) t) + ((string-equal s1 s2) '=) + (t nil)))) + +(defun vm-sort-compare-recipients-r (m1 m2) + (let ((s1 (vm-su-to m1)) + (s2 (vm-su-to m2))) + (cond ((string-lessp s1 s2) nil) + ((string-equal s1 s2) '=) + (t t)))) + +(defun vm-sort-compare-subject (m1 m2) + (let ((s1 (vm-so-sortable-subject m1)) + (s2 (vm-so-sortable-subject m2))) + (cond ((string-lessp s1 s2) t) + ((string-equal s1 s2) '=) + (t nil)))) + +(defun vm-sort-compare-subject-r (m1 m2) + (let ((s1 (vm-so-sortable-subject m1)) + (s2 (vm-so-sortable-subject m2))) + (cond ((string-lessp s1 s2) nil) + ((string-equal s1 s2) '=) + (t t)))) + +(defun vm-sort-compare-line-count (m1 m2) + (let ((n1 (string-to-int (vm-su-line-count m1))) + (n2 (string-to-int (vm-su-line-count m2)))) + (cond ((< n1 n2) t) + ((= n1 n2) '=) + (t nil)))) + +(defun vm-sort-compare-line-count-r (m1 m2) + (let ((n1 (string-to-int (vm-su-line-count m1))) + (n2 (string-to-int (vm-su-line-count m2)))) + (cond ((> n1 n2) t) + ((= n1 n2) '=) + (t nil)))) + +(defun vm-sort-compare-byte-count (m1 m2) + (let ((n1 (string-to-int (vm-su-byte-count m1))) + (n2 (string-to-int (vm-su-byte-count m2)))) + (cond ((< n1 n2) t) + ((= n1 n2) '=) + (t nil)))) + +(defun vm-sort-compare-byte-count-r (m1 m2) + (let ((n1 (string-to-int (vm-su-byte-count m1))) + (n2 (string-to-int (vm-su-byte-count m2)))) + (cond ((> n1 n2) t) + ((= n1 n2) '=) + (t nil)))) + +(defun vm-sort-compare-physical-order (m1 m2) + (let ((n1 (vm-start-of m1)) + (n2 (vm-start-of m2))) + (cond ((< n1 n2) t) + ((= n1 n2) '=) + (t nil)))) + +(defun vm-sort-compare-physical-order-r (m1 m2) + (let ((n1 (vm-start-of m1)) + (n2 (vm-start-of m2))) + (cond ((> n1 n2) t) + ((= n1 n2) '=) + (t nil))))