(NNTP): Fix description of nntp-open-connection-function.
[gnus] / lisp / gnus-diary.el
index c329d0d..ae752a4 100644 (file)
@@ -1,30 +1,29 @@
-;;; gnus-diary.el --- Wrapper around the NNDiary Gnus backend
+;;; gnus-diary.el --- Wrapper around the NNDiary Gnus back end
 
-;; Copyright (C) 1999 Didier Verna.
-
-;; PRCS: $Id: gnus-diary.el 1.8 Tue, 04 Sep 2001 11:32:13 +0200 didier $
+;; Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+;; Copyright (C) 1999, 2000, 2001 Didier Verna.
 
 ;; Author:        Didier Verna <didier@xemacs.org>
 ;; Maintainer:    Didier Verna <didier@xemacs.org>
-;; Created:       Tue Jul 20 10:42:55 1999 under XEmacs 21.2 (beta 18)
-;; Last Revision: Wed Aug  8 14:38:14 2001
+;; Created:       Tue Jul 20 10:42:55 1999
 ;; Keywords:      calendar mail news
 
-;; This file is part of NNDiary.
+;; This file is part of GNU Emacs.
 
-;; NNDiary 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 2 of the License, or
-;; (at your option) any later version.
+;; GNU Emacs 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 2 of the License,
+;; or (at your option) any later version.
 
-;; NNDiary 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.
+;; GNU Emacs 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.
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+;; MA 02110-1301, USA.
 
 
 ;;; Commentary:
 ;; Description:
 ;; ===========
 
-;; Gnus-Diary is a wrapper around the NNDiary Gnus backend.  It is here to
-;; make your nndiary-user life easier in differnet ways.  So, you don't have
-;; to use it if you don't want to.  But, really, you should.
-
-;; Gnus-Diary offers the following improvements on top of the NNDiary backend:
-
-;; - A nice summary line format:
-;;   Displaying diary messages in standard summary line format (usually
-;;   something like "<From Joe>: <Subject>") is pretty useless.  Most of the
-;;   time, you're the one who wrote the message, and you mostly want to see
-;;   the event's date.  Gnus-Diary offers you a nice summary line format which
-;;   will do this.  By default, a summary line will appear like this:
-;;
-;;     <Event Date>: <Subject> <Remaining time>
-;;
-;;   for example, here's how Joe's birthday is displayed in my
-;;   "nndiary:birhdays" summary buffer (the message is expirable, but will
-;;   never be deleted, as it specifies a regular event):
-;;
-;;   E  Sat, Sep 22 01, 12:00: Joe's birthday (in 6 months, 1 week)
-
-;;  - More article sorting functions:
-;;    Gnus-Diary adds a new sorting function called
-;;    `gnus-summary-sort-by-schedule'.  This function lets you organize your
-;;    diary summary buffers from the closest event to the farthest one.
-
-;;  - Automatic generation of diary group parameters:
-;;    When you create a new diary group, or visit one, Gnus-Diary checks your
-;;    group parameters, and if needed, sets the summary line format to the
-;;    diary-specific value, adds the diary-specific sorting functions, and
-;;    also adds the different `X-Diary-*' headers to the group's
-;;    posting-style.  It is then easier to send a diary message, because if
-;;    you use `C-u a' or `C-u m' on a diary group to prepare a message, these
-;;    headers will be inserted automatically (but not filled with proper
-;;    values yet).
-
-
-;; Usage:
-;; =====
-
-;; 0/ Don't use any `gnus-user-format-function-[d|D]'.  Gnus-Diary provides
-;;    both of these (sorry if you used them before).
-;; 1/ Add '(require 'gnus-diary) to your gnusrc file.
-;; 2/ Customize your gnus-diary options to suit your needs.
-
+;; gnus-diary is a utility toolkit used on top of the nndiary back end. It is
+;; now fully documented in the Gnus manual.
 
 
 ;; Bugs / Todo:
 ;; ===========
 
-;; * Provide `gnus-group-diary-mail' and `gnus-group-post-diary-news' (or
-;;   something like that), that would do just like `gnus-group-mail' and
-;;   `gnus-group-post-news', but also prompt for diary header values with
-;;   completion etc.
-;; * Maybe not actually: we could just have a function that converts *any*
-;;   message to a diary one, by prompting the schedule. You could then forward
-;;   a message and make it a diary one etc.
 
 ;;; Code:
 
 (require 'nndiary)
+(require 'message)
+(require 'gnus-art)
 
 (defgroup gnus-diary nil
-  "Utilities on top of the nndiary backend for Gnus.")
+  "Utilities on top of the nndiary back end for Gnus."
+  :version "22.1"
+  :group 'gnus)
 
-(defcustom gnus-diary-summary-line-format "%U%R%z%I %uD: %(%s%) (%ud)\n"
+(defcustom gnus-diary-summary-line-format "%U%R%z %uD: %(%s%) (%ud)\n"
   "*Summary line format for nndiary groups."
   :type 'string
   :group 'gnus-diary
@@ -128,7 +81,18 @@ There are currently two built-in format functions:
   :group 'gnus-diary)
 
 (defconst gnus-diary-version nndiary-version
-  "Current Diary backend version.")
+  "Current Diary back end version.")
+
+
+;; Compatibility functions ==================================================
+
+(eval-and-compile
+  (if (fboundp 'kill-entire-line)
+      (defalias 'gnus-diary-kill-entire-line 'kill-entire-line)
+    (defun gnus-diary-kill-entire-line ()
+      (beginning-of-line)
+      (let ((kill-whole-line t))
+       (kill-line)))))
 
 
 ;; Summary line format ======================================================
@@ -137,7 +101,7 @@ There are currently two built-in format functions:
   (if (null delay)
       "maintenant!"
     ;; Keep only a precision of two degrees
-    (and (> (length delay) 1) (setf (nthcdr 2 delay) nil))
+    (and (> (length delay) 1) (setcdr (cdr delay) nil))
     (concat (if past "il y a " "dans ")
            (let ((str "")
                  del)
@@ -167,7 +131,7 @@ There are currently two built-in format functions:
   (if (null delay)
       "now!"
     ;; Keep only a precision of two degrees
-    (and (> (length delay) 1) (setf (nthcdr 2 delay) nil))
+    (and (> (length delay) 1) (setcdr (cdr delay) nil))
     (concat (unless past "in ")
            (let ((str "")
                  del)
@@ -188,7 +152,7 @@ There are currently two built-in format functions:
      (let ((head (cdr (assoc (intern (format "X-Diary-%s" (car elt)))
                             headers))))
        (when head
-        (nndiary-parse-schedule-value head (cadr elt) (caddr elt)))))
+        (nndiary-parse-schedule-value head (cadr elt) (car (cddr elt))))))
    nndiary-headers))
 
 ;; #### NOTE: Gnus sometimes gives me a HEADER not corresponding to any
@@ -249,7 +213,7 @@ There are currently two built-in format functions:
         (o1 (nndiary-next-occurence s1 now))
         (o2 (nndiary-next-occurence s2 now)))
     (if (and (= (car o1) (car o2)) (= (cadr o1) (cadr o2)))
-        (< (mail-header-number h1) (mail-header-number h2))
+       (< (mail-header-number h1) (mail-header-number h2))
       (time-less-p o1 o2))))
 
 
@@ -263,6 +227,7 @@ Optional prefix (or REVERSE argument) means sort in reverse order."
   (interactive "P")
   (gnus-summary-sort 'schedule reverse))
 
+(defvar gnus-summary-misc-menu) ;; Avoid byte compiler warning.
 (add-hook 'gnus-summary-menu-hook
          (lambda ()
            (easy-menu-add-item gnus-summary-misc-menu
@@ -274,6 +239,8 @@ Optional prefix (or REVERSE argument) means sort in reverse order."
                                     'nndiary)]
                                "Sort by number")))
 
+
+
 ;; Group parameters autosetting =============================================
 
 (defun gnus-diary-update-group-parameters (group)
@@ -310,7 +277,7 @@ Optional prefix (or REVERSE argument) means sort in reverse order."
     ))
 
 ;; Called when a group is subscribed. This is needed because groups created
-;; because of mail splitting are *not* created with the backend function.
+;; because of mail splitting are *not* created with the back end function.
 ;; Thus, `nndiary-request-create-group-hooks' is inoperative.
 (defun gnus-diary-maybe-update-group-parameters (group)
   (when (eq (car (gnus-find-method-for-group group)) 'nndiary)
@@ -325,11 +292,118 @@ Optional prefix (or REVERSE argument) means sort in reverse order."
 (add-hook 'gnus-subscribe-newsgroup-hooks
          'gnus-diary-maybe-update-group-parameters)
 
+
+;; Diary Message Checking ===================================================
+
+(defvar gnus-diary-header-value-history nil
+  ;; History variable for header value prompting
+  )
+
+(defun gnus-diary-narrow-to-headers ()
+  "Narrow the current buffer to the header part.
+Point is left at the beginning of the region.
+The buffer is assumed to contain a message, but the format is unknown."
+  (cond ((eq major-mode 'message-mode)
+        (message-narrow-to-headers))
+       (t
+        (goto-char (point-min))
+        (when (search-forward "\n\n" nil t)
+          (narrow-to-region (point-min) (- (point) 1))
+          (goto-char (point-min))))
+       ))
+
+(defun gnus-diary-add-header (str)
+  "Add a header to the current buffer.
+The buffer is assumed to contain a message, but the format is unknown."
+  (cond ((eq major-mode 'message-mode)
+        (message-add-header str))
+       (t
+        (save-restriction
+          (gnus-diary-narrow-to-headers)
+          (goto-char (point-max))
+          (if (string-match "\n$" str)
+              (insert str)
+            (insert str ?\n))))
+       ))
+
+(defun gnus-diary-check-message (arg)
+  "Ensure that the current message is a valid for NNDiary.
+This function checks that all NNDiary required headers are present and
+valid, and prompts for values / correction otherwise.
+
+If ARG (or prefix) is non-nil, force prompting for all fields."
+  (interactive "P")
+  (save-excursion
+    (mapcar
+     (lambda (head)
+       (let ((header (concat "X-Diary-" (car head)))
+            (ask arg)
+            value invalid)
+        ;; First, try to find the header, and checks for validity:
+        (save-restriction
+          (gnus-diary-narrow-to-headers)
+          (when (re-search-forward (concat "^" header ":") nil t)
+            (unless (eq (char-after) ? )
+              (insert " "))
+            (setq value (buffer-substring (point) (point-at-eol)))
+            (and (string-match "[ \t]*\\([^ \t]+\\)[ \t]*" value)
+                 (setq value (match-string 1 value)))
+            (condition-case ()
+                (nndiary-parse-schedule-value value
+                                              (nth 1 head) (nth 2 head))
+              (t
+               (setq invalid t)))
+            ;; #### NOTE: this (along with the `gnus-diary-add-header'
+            ;; function) could be rewritten in a better way, in particular
+            ;; not to blindly remove an already present header and reinsert
+            ;; it somewhere else afterwards.
+            (when (or ask invalid)
+              (gnus-diary-kill-entire-line))
+            ))
+        ;; Now, loop until a valid value is provided:
+        (while (or ask (not value) invalid)
+          (let ((prompt (concat (and invalid
+                                     (prog1 "(current value invalid) "
+                                       (beep)))
+                                header ": ")))
+            (setq value
+                  (if (listp (nth 1 head))
+                      (completing-read prompt (cons '("*" nil) (nth 1 head))
+                                       nil t value
+                                       gnus-diary-header-value-history)
+                    (read-string prompt value
+                                 gnus-diary-header-value-history))))
+          (setq ask nil)
+          (setq invalid nil)
+          (condition-case ()
+              (nndiary-parse-schedule-value value
+                                            (nth 1 head) (nth 2 head))
+            (t
+             (setq invalid t))))
+        (gnus-diary-add-header (concat header ": " value))
+        ))
+     nndiary-headers)
+    ))
+
+(add-hook 'nndiary-request-accept-article-hooks
+         (lambda () (gnus-diary-check-message nil)))
+
+(define-key message-mode-map "\C-cDc" 'gnus-diary-check-message)
+(define-key gnus-article-edit-mode-map "\C-cDc" 'gnus-diary-check-message)
+
+
+;; The end ==================================================================
+
 (defun gnus-diary-version ()
-  "Current Diary backend version."
+  "Current Diary back end version."
   (interactive)
   (message "NNDiary version %s" nndiary-version))
 
+(define-key message-mode-map "\C-cDv" 'gnus-diary-version)
+(define-key gnus-article-edit-mode-map "\C-cDv" 'gnus-diary-version)
+
+
 (provide 'gnus-diary)
 
+;;; arch-tag: 98467e70-337e-4ddc-b92d-45d403ff1b4b
 ;;; gnus-diary.el ends here