emacs/lisp/mh-e/mh-identity.el

325 lines
12 KiB
EmacsLisp

;;; mh-identity.el --- multiple identify support for MH-E -*- lexical-binding: t; -*-
;; Copyright (C) 2002-2024 Free Software Foundation, Inc.
;; Author: Peter S. Galbraith <psg@debian.org>
;; Maintainer: Bill Wohler <wohler@newt.com>
;; Keywords: mail
;; See: mh-e.el
;; This file is part of GNU Emacs.
;; 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 3 of the License, or
;; (at your option) any later version.
;; 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Multiple identity support for MH-E.
;; Used to easily set different fields such as From and Organization,
;; as well as different signature files.
;; Customize the variable `mh-identity-list' and see the Identity menu
;; in MH-Letter mode. The command `mh-insert-identity' can be used
;; to manually insert an identity.
;;; Code:
(require 'mh-e)
(autoload 'mml-insert-tag "mml")
(define-obsolete-variable-alias 'mh-identity-pgg-default-user-id
'mh-identity-gpg-default-user-id "29.1")
(defvar-local mh-identity-gpg-default-user-id nil
"Holds the GPG key ID.
This is normally set as part of an Identity in
`mh-identity-list'.")
(defvar mh-identity-menu nil
"The Identity menu.")
(defalias 'mh-identity-make-menu-no-autoload #'mh-identity-make-menu)
;;;###mh-autoload
(defun mh-identity-make-menu ()
"Build the Identity menu.
This should be called any time `mh-identity-list' or
`mh-auto-fields-list' change."
(easy-menu-define mh-identity-menu mh-letter-mode-map
"MH-E identity menu"
(append
'("Identity")
;; Dynamically render :type corresponding to `mh-identity-list'
;; e.g.:
;; ["Home" (mh-insert-identity "Home")
;; :style radio :active (not (equal mh-identity-local "Home"))
;; :selected (equal mh-identity-local "Home")]
'(["Insert Auto Fields"
(mh-insert-auto-fields) mh-auto-fields-list]
"--")
(mapcar (lambda (arg)
`[,arg (mh-insert-identity ,arg) :style radio
:selected (equal mh-identity-local ,arg)])
(mapcar #'car mh-identity-list))
'(["None"
(mh-insert-identity "None") :style radio
:selected (not mh-identity-local)]
"--"
["Set Default for Session"
(setq mh-identity-default mh-identity-local) t]
["Save as Default"
(customize-save-variable 'mh-identity-default mh-identity-local) t]
["Customize Identities" (customize-variable 'mh-identity-list) t]
))))
;;;###mh-autoload
(defun mh-identity-add-menu ()
"Add the current Identity menu.
See `mh-identity-make-menu'."
(declare (obsolete nil "29.1"))
nil)
(defvar-local mh-identity-local nil
"Buffer-local variable that holds the identity currently in use.")
(defun mh-header-field-delete (field value-only)
"Delete header FIELD, or only its value if VALUE-ONLY is t.
Return t if anything is deleted."
(let ((field-colon (if (string-match "^.*:$" field)
field
(concat field ":"))))
(when (mh-goto-header-field field-colon)
(if (not value-only)
(beginning-of-line)
(forward-char))
(delete-region (point)
(progn (mh-header-field-end)
(if (not value-only) (forward-char 1))
(point)))
t)))
(defvar mh-identity-signature-start nil
"Marker for the beginning of a signature inserted by `mh-insert-identity'.")
(defvar mh-identity-signature-end nil
"Marker for the end of a signature inserted by `mh-insert-identity'.")
(defun mh-identity-field-handler (field)
"Return the handler for header FIELD or nil if none set.
The field name is downcased. If the FIELD begins with the
character \":\", then it must have a special handler defined in
`mh-identity-handlers', else return an error since it is not a
valid header field."
(or (cdr (assoc-string field mh-identity-handlers t))
(and (eq (aref field 0) ?:)
(error "Field %s not found in `mh-identity-handlers'" field))
(cdr (assoc ":default" mh-identity-handlers))
'mh-identity-handler-default))
;;;###mh-autoload
(defun mh-select-identity (default)
"Prompt for and return an identity.
If DEFAULT is non-nil, it will be used if the user doesn't enter a
different identity.
See `mh-identity-list'."
(let (identity)
(setq identity
(completing-read
"Identity: "
(cons '("None")
(mapcar #'list (mapcar #'car mh-identity-list)))
nil t default nil default))
(if (equal identity "None")
nil
identity)))
;;;###mh-autoload
(defun mh-identity-field (identity field)
"Return the specified FIELD of the given IDENTITY.
See `mh-identity-list'."
(let* ((pers-list (cadr (assoc identity mh-identity-list)))
(value (cdr (assoc field pers-list))))
value))
;;;###mh-autoload
(defun mh-insert-identity (identity &optional maybe-insert)
"Insert fields specified by given IDENTITY.
In a program, do not insert fields if MAYBE-INSERT is non-nil,
`mh-identity-default' is non-nil, and fields have already been
inserted.
See `mh-identity-list'."
(interactive
(list (completing-read
"Identity: "
(if mh-identity-local
(cons '("None")
(mapcar #'list (mapcar #'car mh-identity-list)))
(mapcar #'list (mapcar #'car mh-identity-list)))
nil t)
nil))
(when (or (not maybe-insert)
(and (boundp 'mh-identity-default)
mh-identity-default
(not mh-identity-local)))
(save-excursion
;;First remove old settings, if any.
(when mh-identity-local
(let ((pers-list (cadr (assoc mh-identity-local mh-identity-list))))
(while pers-list
(let* ((field (caar pers-list))
(handler (mh-identity-field-handler field)))
(funcall handler field 'remove))
(setq pers-list (cdr pers-list)))))
;; Then insert the replacement
(when (not (equal "None" identity))
(let ((pers-list (cadr (assoc identity mh-identity-list))))
(while pers-list
(let* ((field (caar pers-list))
(value (cdar pers-list))
(handler (mh-identity-field-handler field)))
(funcall handler field 'add value))
(setq pers-list (cdr pers-list))))))
;; Remember what is in use in this buffer
(if (equal "None" identity)
(setq mh-identity-local nil)
(setq mh-identity-local identity))))
;;;###mh-autoload
(defun mh-identity-handler-gpg-identity (_field action &optional value)
"Process header FIELD \":pgg-default-user-id\".
The ACTION is one of `remove' or `add'. If `add', the VALUE is added.
The buffer-local variable `mh-identity-gpg-default-user-id' is set to
VALUE when action `add' is selected."
(cond
((or (equal action 'remove)
(not value)
(string= value ""))
(setq mh-identity-gpg-default-user-id nil))
((equal action 'add)
(setq mh-identity-gpg-default-user-id value))))
;;;###mh-autoload
(defun mh-identity-handler-signature (_field action &optional value)
"Process header FIELD \":signature\".
The ACTION is one of `remove' or `add'. If `add', the VALUE is
added."
(cond
((equal action 'remove)
(when (and (markerp mh-identity-signature-start)
(markerp mh-identity-signature-end))
(delete-region mh-identity-signature-start
mh-identity-signature-end)))
(t
;; Insert "signature". Nil value means to use `mh-signature-file-name'.
(when (not (mh-signature-separator-p)) ;...unless already present
(goto-char (point-max))
(save-restriction
(narrow-to-region (point) (point))
(if (null value)
(mh-insert-signature)
(mh-insert-signature value))
(setq-local mh-identity-signature-start (point-min-marker))
(set-marker-insertion-type mh-identity-signature-start t)
(setq-local mh-identity-signature-end (point-max-marker)))))))
(defvar mh-identity-attribution-verb-start nil
"Marker for the beginning of the attribution verb.")
(defvar mh-identity-attribution-verb-end nil
"Marker for the end of the attribution verb.")
;;;###mh-autoload
(defun mh-identity-handler-attribution-verb (_field action &optional value)
"Process header FIELD \":attribution-verb\".
The ACTION is one of `remove' or `add'. If `add', the VALUE is
added."
(when (and (markerp mh-identity-attribution-verb-start)
(markerp mh-identity-attribution-verb-end))
(delete-region mh-identity-attribution-verb-start
mh-identity-attribution-verb-end)
(goto-char mh-identity-attribution-verb-start)
(cond
((equal action 'remove) ; Replace with default
(mh-identity-insert-attribution-verb nil))
(t ; Insert attribution verb.
(mh-identity-insert-attribution-verb value)))))
;;;###mh-autoload
(defun mh-identity-insert-attribution-verb (value)
"Insert VALUE as attribution verb, setting up delimiting markers.
If VALUE is nil, use `mh-extract-from-attribution-verb'."
(save-restriction
(narrow-to-region (point) (point))
(if (null value)
(insert mh-extract-from-attribution-verb)
(insert value))
(setq-local mh-identity-attribution-verb-start (point-min-marker))
(set-marker-insertion-type mh-identity-attribution-verb-start t)
(setq-local mh-identity-attribution-verb-end (point-max-marker))))
(defun mh-identity-handler-default (field action top &optional value)
"Process header FIELD.
The ACTION is one of `remove' or `add'. If TOP is non-nil, add the
field and its VALUE at the top of the header, else add it at the
bottom of the header. If action is `add', the VALUE is added."
(let ((field-colon (if (string-match "^.*:$" field)
field
(concat field ":"))))
(cond
((equal action 'remove)
(mh-header-field-delete field-colon nil))
(t
(cond
;; No value, remove field
((or (not value)
(string= value ""))
(mh-header-field-delete field-colon nil))
;; Existing field, replace
((mh-header-field-delete field-colon t)
(insert value))
;; Other field, add at end or top
(t
(goto-char (point-min))
(if (not top)
(mh-goto-header-end 0))
(insert field-colon " " value "\n")))))))
;;;###mh-autoload
(defun mh-identity-handler-top (field action &optional value)
"Process header FIELD.
The ACTION is one of `remove' or `add'. If `add', the VALUE is
added. If the field wasn't present, it is added to the top of the
header."
(mh-identity-handler-default field action t value))
;;;###mh-autoload
(defun mh-identity-handler-bottom (field action &optional value)
"Process header FIELD.
The ACTION is one of `remove' or `add'. If `add', the VALUE is
added. If the field wasn't present, it is added to the bottom of
the header."
(mh-identity-handler-default field action nil value))
(provide 'mh-identity)
;; Local Variables:
;; sentence-end-double-space: nil
;; End:
;;; mh-identity.el ends here