Mercurial > hg > xemacs-beta
diff lisp/vm/vm-motion.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-motion.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,458 @@ +;;; Commands to move around in a VM folder +;;; Copyright (C) 1989, 1990, 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-motion) + +(defun vm-record-and-change-message-pointer (old new) + (intern (buffer-name) vm-buffers-needing-display-update) + (setq vm-last-message-pointer old + vm-message-pointer new + vm-need-summary-pointer-update t)) + +(defun vm-goto-message (n) + "Go to the message numbered N. +Interactively N is the prefix argument. If no prefix arg is provided +N is prompted for in the minibuffer. + +If vm-follow-summary-cursor is non-nil this command will go to +the message under the cursor in the summary buffer if the summary +window is selected. This only happens if no prefix argument is +given." + (interactive + (list + (cond (current-prefix-arg (prefix-numeric-value current-prefix-arg)) + ((vm-follow-summary-cursor) nil) + (t + (let ((last-command last-command) + (this-command this-command)) + (vm-read-number "Go to message: ")))))) + (if (null n) + () ; nil means work has been done already + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (vm-display nil nil '(vm-goto-message) '(vm-goto-message)) + (let ((cons (nthcdr (1- n) vm-message-list))) + (if (null cons) + (error "No such message.")) + (if (eq vm-message-pointer cons) + (vm-preview-current-message) + (vm-record-and-change-message-pointer vm-message-pointer cons) + (vm-preview-current-message))))) + +(defun vm-goto-message-last-seen () + "Go to the message last previewed." + (interactive) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (vm-display nil nil '(vm-goto-message-last-seen) + '(vm-goto-message-last-seen)) + (if vm-last-message-pointer + (progn + (vm-record-and-change-message-pointer vm-message-pointer + vm-last-message-pointer) + (vm-preview-current-message)))) + +(defun vm-goto-parent-message () + "Go to the parent of the current message." + (interactive) + (vm-follow-summary-cursor) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-error-if-folder-empty) + (vm-build-threads-if-unbuilt) + (vm-display nil nil '(vm-goto-parent-message) + '(vm-goto-parent-message)) + (let ((list (vm-th-thread-list (car vm-message-pointer))) + message) + (if (null (cdr list)) + (message "Message has no parent.") + (while (cdr (cdr list)) + (setq list (cdr list))) + (setq message (car (get (car list) 'messages))) + (if (null message) + (message "Parent message is not in this folder.") + (vm-record-and-change-message-pointer vm-message-pointer + (memq message vm-message-list)) + (vm-preview-current-message))))) + +(defun vm-check-count (count) + (if (>= count 0) + (if (< (length vm-message-pointer) count) + (signal 'end-of-folder nil)) + (if (< (1+ (- (length vm-message-list) (length vm-message-pointer))) + (vm-abs count)) + (signal 'beginning-of-folder nil)))) + +(defun vm-move-message-pointer (direction) + (let ((mp vm-message-pointer)) + (if (eq direction 'forward) + (progn + (setq mp (cdr mp)) + (if (null mp) + (if vm-circular-folders + (setq mp vm-message-list) + (signal 'end-of-folder nil)))) + (setq mp (vm-reverse-link-of (car mp))) + (if (null mp) + (if vm-circular-folders + (setq mp (vm-last vm-message-list)) + (signal 'beginning-of-folder nil)))) + (setq vm-message-pointer mp))) + +(defun vm-should-skip-message (mp &optional skip-dogmatically) + (if skip-dogmatically + (or (and vm-skip-deleted-messages + (vm-deleted-flag (car mp))) + (and vm-skip-read-messages + (or (vm-deleted-flag (car mp)) + (not (or (vm-new-flag (car mp)) + (vm-unread-flag (car mp)))))) + (and (eq last-command 'vm-next-command-uses-marks) + (null (vm-mark-of (car mp))))) + (or (and (eq vm-skip-deleted-messages t) + (vm-deleted-flag (car mp))) + (and (eq vm-skip-read-messages t) + (or (vm-deleted-flag (car mp)) + (not (or (vm-new-flag (car mp)) + (vm-unread-flag (car mp)))))) + (and (eq last-command 'vm-next-command-uses-marks) + (null (vm-mark-of (car mp))))))) + +(defun vm-next-message (&optional count retry signal-errors) + "Go forward one message and preview it. +With prefix arg (optional first argument) COUNT, go forward COUNT +messages. A negative COUNT means go backward. If the absolute +value of COUNT is greater than 1, then the values of the variables +vm-skip-deleted-messages and vm-skip-read-messages are ignored. + +When invoked on marked messages (via vm-next-command-uses-marks) +this command 'sees' marked messages as it moves." + ;; second arg RETRY non-nil means retry a failed move, giving + ;; not nil-or-t values of the vm-skip variables a chance to + ;; work. + ;; + ;; third arg SIGNAL-ERRORS non-nil means that if after + ;; everything we still have bashed into the end or beginning of + ;; folder before completing the move, signal + ;; beginning-of-folder or end-of-folder. Otherwise no error + ;; will be signaled. + ;; + ;; Note that interactively all args are 1, so error signaling + ;; and retries apply to all interactive moves. + (interactive "p\np\np") + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + ;; include other commands that call vm-next-message so that the + ;; correct window configuration is applied for these particular + ;; non-interactive calls. + (vm-display nil nil '(vm-next-message + vm-delete-message + vm-undelete-message + vm-scroll-forward) + (list this-command)) + (and signal-errors (vm-error-if-folder-empty)) + (or count (setq count 1)) + (let ((oldmp vm-message-pointer) + (use-marks (eq last-command 'vm-next-command-uses-marks)) + (error) + (direction (if (> count 0) 'forward 'backward)) + (count (vm-abs count))) + (cond + ((null vm-message-pointer) + (setq vm-message-pointer vm-message-list)) + ((/= count 1) + (condition-case () + (let ((oldmp oldmp)) + (while (not (zerop count)) + (vm-move-message-pointer direction) + (if (and use-marks (null (vm-mark-of (car vm-message-pointer)))) + (progn + (while (and (not (eq vm-message-pointer oldmp)) + (null (vm-mark-of (car vm-message-pointer)))) + (vm-move-message-pointer direction)) + (if (eq vm-message-pointer oldmp) + ;; terminate the loop + (setq count 1) + ;; reset for next pass + (setq oldmp vm-message-pointer)))) + (vm-decrement count))) + (beginning-of-folder (setq error 'beginning-of-folder)) + (end-of-folder (setq error 'end-of-folder)))) + (t + (condition-case () + (progn + (vm-move-message-pointer direction) + (while (and (not (eq oldmp vm-message-pointer)) + (vm-should-skip-message vm-message-pointer t)) + (vm-move-message-pointer direction)) + ;; Retry the move if we've gone a complete circle and + ;; retries are allowed and there are other messages + ;; besides this one. + (and (eq vm-message-pointer oldmp) retry (cdr vm-message-list) + (progn + (vm-move-message-pointer direction) + (while (and (not (eq oldmp vm-message-pointer)) + (vm-should-skip-message vm-message-pointer)) + (vm-move-message-pointer direction))))) + (beginning-of-folder + ;; we bumped into the beginning of the folder without finding + ;; a suitable stopping point; retry the move if we're allowed. + (setq vm-message-pointer oldmp) + ;; if the retry fails, we make sure the message pointer + ;; is restored to its old value. + (if retry + (setq vm-message-pointer + (condition-case () + (let ((vm-message-pointer vm-message-pointer)) + (vm-move-message-pointer direction) + (while (vm-should-skip-message vm-message-pointer) + (vm-move-message-pointer direction)) + vm-message-pointer ) + (beginning-of-folder + (setq error 'beginning-of-folder) + oldmp ))) + (setq error 'beginning-of-folder))) + (end-of-folder + ;; we bumped into the end of the folder without finding + ;; a suitable stopping point; retry the move if we're allowed. + (setq vm-message-pointer oldmp) + ;; if the retry fails, we make sure the message pointer + ;; is restored to its old value. + (if retry + (setq vm-message-pointer + (condition-case () + (let ((vm-message-pointer vm-message-pointer)) + (vm-move-message-pointer direction) + (while (vm-should-skip-message vm-message-pointer) + (vm-move-message-pointer direction)) + vm-message-pointer ) + (end-of-folder + (setq error 'end-of-folder) + oldmp ))) + (setq error 'end-of-folder)))))) + (if (not (eq vm-message-pointer oldmp)) + (progn + (vm-record-and-change-message-pointer oldmp vm-message-pointer) + (vm-preview-current-message))) + (and error signal-errors + (signal error nil)))) + +(defun vm-previous-message (&optional count retry signal-errors) + "Go back one message and preview it. +With prefix arg COUNT, go backward COUNT messages. A negative COUNT +means go forward. If the absolute value of COUNT > 1 the values of the +variables vm-skip-deleted-messages and vm-skip-read-messages are +ignored." + (interactive "p\np\np") + (or count (setq count 1)) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-display nil nil '(vm-previous-message) '(vm-previous-message)) + (vm-next-message (- count) retry signal-errors)) + +(defun vm-next-message-no-skip (&optional count) + "Like vm-next-message but will not skip deleted or read messages." + (interactive "p") + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-display nil nil '(vm-Next-message) '(vm-Next-message)) + (let ((vm-skip-deleted-messages nil) + (vm-skip-read-messages nil)) + (vm-next-message count nil t))) +;; backward compatibility +(fset 'vm-Next-message 'vm-next-message-no-skip) + +(defun vm-previous-message-no-skip (&optional count) + "Like vm-previous-message but will not skip deleted or read messages." + (interactive "p") + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-display nil nil '(vm-Previous-message) '(vm-Previous-message)) + (let ((vm-skip-deleted-messages nil) + (vm-skip-read-messages nil)) + (vm-previous-message count))) +;; backward compatibility +(fset 'vm-Previous-message 'vm-previous-message-no-skip) + +(defun vm-next-unread-message () + "Move forward to the nearest new or unread message, if there is one." + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-display nil nil '(vm-next-unread-message) '(vm-next-unread-message)) + (condition-case () + (let ((vm-skip-read-messages t) + (oldmp vm-message-pointer)) + (vm-next-message 1 nil t) + ;; in case vm-circular-folders is non-nil + (and (eq vm-message-pointer oldmp) (signal 'end-of-folder nil))) + (end-of-folder (message "No next unread message")))) + +(defun vm-previous-unread-message () + "Move backward to the nearest new or unread message, if there is one." + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-display nil nil '(vm-previous-unread-message) + '(vm-previous-unread-message)) + (condition-case () + (let ((vm-skip-read-messages t) + (oldmp vm-message-pointer)) + (vm-previous-message) + ;; in case vm-circular-folders is non-nil + (and (eq vm-message-pointer oldmp) (signal 'beginning-of-folder nil))) + (beginning-of-folder (message "No previous unread message")))) + +(defun vm-next-message-same-subject () + "Move forward to the nearest message with the same subject. +vm-subject-ignored-prefix and vm-subject-ignored-suffix will apply +to the subject comparisons." + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-display nil nil '(vm-next-message-same-subject) + '(vm-next-message-same-subject)) + (let ((oldmp vm-message-pointer) + (done nil) + (subject (vm-so-sortable-subject (car vm-message-pointer)))) + (condition-case () + (progn + (while (not done) + (vm-move-message-pointer 'forward) + (if (eq oldmp vm-message-pointer) + (signal 'end-of-folder nil)) + (if (equal subject + (vm-so-sortable-subject (car vm-message-pointer))) + (setq done t))) + (vm-record-and-change-message-pointer oldmp vm-message-pointer) + (vm-preview-current-message)) + (end-of-folder + (setq vm-message-pointer oldmp) + (message "No next message with the same subject"))))) + +(defun vm-previous-message-same-subject () + "Move backward to the nearest message with the same subject. +vm-subject-ignored-prefix and vm-subject-ignored-suffix will apply +to the subject comparisons." + (interactive) + (if (interactive-p) + (vm-follow-summary-cursor)) + (vm-select-folder-buffer) + (vm-check-for-killed-summary) + (vm-display nil nil '(vm-previous-message-same-subject) + '(vm-previous-message-same-subject)) + (let ((oldmp vm-message-pointer) + (done nil) + (subject (vm-so-sortable-subject (car vm-message-pointer)))) + (condition-case () + (progn + (while (not done) + (vm-move-message-pointer 'backward) + (if (eq oldmp vm-message-pointer) + (signal 'beginning-of-folder nil)) + (if (equal subject + (vm-so-sortable-subject (car vm-message-pointer))) + (setq done t))) + (vm-record-and-change-message-pointer oldmp vm-message-pointer) + (vm-preview-current-message)) + (beginning-of-folder + (setq vm-message-pointer oldmp) + (message "No previous message with the same subject"))))) + +(defun vm-find-first-unread-message (new) + (let (mp unread-mp) + (setq mp vm-message-list) + (if new + (while mp + (if (and (vm-new-flag (car mp)) (not (vm-deleted-flag (car mp)))) + (setq unread-mp mp mp nil) + (setq mp (cdr mp)))) + (while mp + (if (and (or (vm-new-flag (car mp)) (vm-unread-flag (car mp))) + (not (vm-deleted-flag (car mp)))) + (setq unread-mp mp mp nil) + (setq mp (cdr mp))))) + unread-mp )) + +(defun vm-thoughtfully-select-message () + (let ((new (and vm-jump-to-new-messages (vm-find-first-unread-message t))) + (unread (and vm-jump-to-unread-messages + (vm-find-first-unread-message nil))) + fix mp) + (if (null vm-message-pointer) + (setq fix (vm-last vm-message-list))) + (setq mp (or new unread fix)) + (if (and mp (not (eq mp vm-message-pointer))) + (progn + (vm-record-and-change-message-pointer vm-message-pointer mp) + mp ) + nil ))) + +(defun vm-follow-summary-cursor () + (and vm-follow-summary-cursor (eq major-mode 'vm-summary-mode) + (let ((point (point)) + message-pointer message-list mp) + (save-excursion + (set-buffer vm-mail-buffer) + (setq message-pointer vm-message-pointer + message-list vm-message-list)) + (cond ((or (null message-pointer) + (and (>= point (vm-su-start-of (car message-pointer))) + (< point (vm-su-end-of (car message-pointer))))) + nil ) + ;; the position at eob belongs to the last message + ((and (eobp) (= (vm-su-end-of (car message-pointer)) point)) + nil ) + ;; make the position at eob belong to the last message + ((eobp) + (setq mp (vm-last message-pointer)) + (save-excursion + (set-buffer vm-mail-buffer) + (vm-record-and-change-message-pointer vm-message-pointer mp) + (vm-preview-current-message) + ;; return non-nil so the caller will know that + ;; a new message was selected. + t )) + (t + (if (< point (vm-su-start-of (car message-pointer))) + (setq mp message-list) + (setq mp (cdr message-pointer) message-pointer nil)) + (while (and (not (eq mp message-pointer)) + (>= point (vm-su-end-of (car mp)))) + (setq mp (cdr mp))) + (if (not (eq mp message-pointer)) + (save-excursion + (set-buffer vm-mail-buffer) + (vm-record-and-change-message-pointer + vm-message-pointer mp) + (vm-preview-current-message) + ;; return non-nil so the caller will know that + ;; a new message was selected. + t )))))))