gmm-utils.el 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. ;;; gmm-utils.el --- Utility functions for Gnus, Message and MML
  2. ;; Copyright (C) 2006-2012 Free Software Foundation, Inc.
  3. ;; Author: Reiner Steib <reiner.steib@gmx.de>
  4. ;; Keywords: news
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This library provides self-contained utility functions. The functions are
  18. ;; used in Gnus, Message and MML, but within this library there are no
  19. ;; dependencies on Gnus, Message, or MML.
  20. ;;; Code:
  21. (defgroup gmm nil
  22. "Utility functions for Gnus, Message and MML."
  23. :prefix "gmm-"
  24. :version "22.1" ;; Gnus 5.10.9
  25. :group 'lisp)
  26. ;; Helper functions from `gnus-utils.el': gmm-verbose, gmm-message, gmm-error
  27. (defcustom gmm-verbose 7
  28. "Integer that says how verbose gmm should be.
  29. The higher the number, the more messages will flash to say what
  30. it did. At zero, it will be totally mute; at five, it will
  31. display most important messages; and at ten, it will keep on
  32. jabbering all the time."
  33. :type 'integer
  34. :group 'gmm)
  35. ;;;###autoload
  36. (defun gmm-regexp-concat (regexp)
  37. "Potentially concat a list of regexps into a single one.
  38. The concatenation is done with logical ORs."
  39. (cond ((null regexp)
  40. nil)
  41. ((stringp regexp)
  42. regexp)
  43. ((listp regexp)
  44. (mapconcat (lambda (elt) (concat "\\(" elt "\\)"))
  45. regexp
  46. "\\|"))))
  47. ;;;###autoload
  48. (defun gmm-message (level &rest args)
  49. "If LEVEL is lower than `gmm-verbose' print ARGS using `message'.
  50. Guideline for numbers:
  51. 1 - error messages
  52. 3 - non-serious error messages
  53. 5 - messages for things that take a long time
  54. 7 - not very important messages on stuff
  55. 9 - messages inside loops."
  56. (if (<= level gmm-verbose)
  57. (apply 'message args)
  58. ;; We have to do this format thingy here even if the result isn't
  59. ;; shown - the return value has to be the same as the return value
  60. ;; from `message'.
  61. (apply 'format args)))
  62. ;;;###autoload
  63. (defun gmm-error (level &rest args)
  64. "Beep an error if LEVEL is equal to or less than `gmm-verbose'.
  65. ARGS are passed to `message'."
  66. (when (<= (floor level) gmm-verbose)
  67. (apply 'message args)
  68. (ding)
  69. (let (duration)
  70. (when (and (floatp level)
  71. (not (zerop (setq duration (* 10 (- level (floor level)))))))
  72. (sit-for duration))))
  73. nil)
  74. ;;;###autoload
  75. (defun gmm-widget-p (symbol)
  76. "Non-nil if SYMBOL is a widget."
  77. (get symbol 'widget-type))
  78. (autoload 'widget-create-child-value "wid-edit")
  79. (autoload 'widget-convert "wid-edit")
  80. (autoload 'widget-default-get "wid-edit")
  81. ;; Copy of the `nnmail-lazy' code from `nnmail.el':
  82. (define-widget 'gmm-lazy 'default
  83. "Base widget for recursive datastructures.
  84. This is a copy of the `lazy' widget in Emacs 22.1 provided for compatibility."
  85. :format "%{%t%}: %v"
  86. :convert-widget 'widget-value-convert-widget
  87. :value-create (lambda (widget)
  88. (let ((value (widget-get widget :value))
  89. (type (widget-get widget :type)))
  90. (widget-put widget :children
  91. (list (widget-create-child-value
  92. widget (widget-convert type) value)))))
  93. :value-delete 'widget-children-value-delete
  94. :value-get (lambda (widget)
  95. (widget-value (car (widget-get widget :children))))
  96. :value-inline (lambda (widget)
  97. (widget-apply (car (widget-get widget :children))
  98. :value-inline))
  99. :default-get (lambda (widget)
  100. (widget-default-get
  101. (widget-convert (widget-get widget :type))))
  102. :match (lambda (widget value)
  103. (widget-apply (widget-convert (widget-get widget :type))
  104. :match value))
  105. :validate (lambda (widget)
  106. (widget-apply (car (widget-get widget :children)) :validate)))
  107. ;; Note: The format of `gmm-tool-bar-item' may change if some future Emacs
  108. ;; version will provide customizable tool bar buttons using a different
  109. ;; interface.
  110. ;; TODO: Extend API so that the "Command" entry can be a function or a plist.
  111. ;; In case of a list it should have the format...
  112. ;;
  113. ;; (:none command-without-modifier
  114. ;; :shift command-with-shift-pressed
  115. ;; :control command-with-ctrl-pressed
  116. ;; :control-shift command-with-control-and-shift-pressed
  117. ;; ;; mouse-2 and mouse-3 can't be used in Emacs yet.
  118. ;; :mouse-2 command-on-mouse-2-press
  119. ;; :mouse-3 command-on-mouse-3-press) ;; typically a menu of related commands
  120. ;;
  121. ;; Combinations of mouse-[23] plus shift and/or control might be overkill.
  122. ;;
  123. ;; Then use (plist-get rs-command :none), (plist-get rs-command :shift)
  124. (define-widget 'gmm-tool-bar-item (if (gmm-widget-p 'lazy) 'lazy 'gmm-lazy)
  125. "Tool bar list item."
  126. :tag "Tool bar item"
  127. :type '(choice
  128. (list :tag "Command and Icon"
  129. (function :tag "Command")
  130. (string :tag "Icon file")
  131. (choice
  132. (const :tag "Default map" nil)
  133. ;; Note: Usually we need non-nil attributes if map is t.
  134. (const :tag "No menu" t)
  135. (sexp :tag "Other map"))
  136. (plist :inline t :tag "Properties"))
  137. (list :tag "Separator"
  138. (const :tag "No command" gmm-ignore)
  139. (string :tag "Icon file")
  140. (const :tag "No map")
  141. (plist :inline t :tag "Properties"))))
  142. (define-widget 'gmm-tool-bar-zap-list (if (gmm-widget-p 'lazy) 'lazy 'gmm-lazy)
  143. "Tool bar zap list."
  144. :tag "Tool bar zap list"
  145. :type '(choice (const :tag "Zap all" t)
  146. (const :tag "Keep all" nil)
  147. (list
  148. ;; :value
  149. ;; Work around (bug in customize?), see
  150. ;; <news:v9is48jrj1.fsf@marauder.physik.uni-ulm.de>
  151. ;; (new-file open-file dired kill-buffer write-file
  152. ;; print-buffer customize help)
  153. (set :inline t
  154. (const new-file)
  155. (const open-file)
  156. (const dired)
  157. (const kill-buffer)
  158. (const save-buffer)
  159. (const write-file)
  160. (const undo)
  161. (const cut)
  162. (const copy)
  163. (const paste)
  164. (const search-forward)
  165. (const print-buffer)
  166. (const customize)
  167. (const help))
  168. (repeat :inline t
  169. :tag "Other"
  170. (symbol :tag "Icon item")))))
  171. ;; (defun gmm-color-cells (&optional display)
  172. ;; "Return the number of color cells supported by DISPLAY.
  173. ;; Compatibility function."
  174. ;; ;; `display-color-cells' doesn't return more than 256 even if color depth is
  175. ;; ;; > 8 in Emacs 21.
  176. ;; ;;
  177. ;; ;; Feel free to add proper XEmacs support.
  178. ;; (let* ((cells (and (fboundp 'display-color-cells)
  179. ;; (display-color-cells display)))
  180. ;; (plane (and (fboundp 'x-display-planes)
  181. ;; (ash 1 (x-display-planes))))
  182. ;; (none -1))
  183. ;; (max (if (integerp cells) cells none)
  184. ;; (if (integerp plane) plane none))))
  185. (defcustom gmm-tool-bar-style
  186. (if (and (boundp 'tool-bar-mode)
  187. tool-bar-mode
  188. (and (fboundp 'display-visual-class)
  189. (not (memq (display-visual-class)
  190. (list 'static-gray 'gray-scale
  191. 'static-color 'pseudo-color)))))
  192. 'gnome
  193. 'retro)
  194. "Preferred tool bar style."
  195. :type '(choice (const :tag "GNOME style" gnome)
  196. (const :tag "Retro look" retro))
  197. :group 'gmm)
  198. (defvar tool-bar-map)
  199. ;;;###autoload
  200. (defun gmm-tool-bar-from-list (icon-list zap-list default-map)
  201. "Make a tool bar from ICON-LIST.
  202. Within each entry of ICON-LIST, the first element is a menu
  203. command, the second element is an icon file name and the third
  204. element is a test function. You can use \\[describe-key]
  205. <menu-entry> to find out the name of a menu command. The fourth
  206. and all following elements are passed as the PROPS argument to the
  207. function `tool-bar-local-item'.
  208. If ZAP-LIST is a list, remove those item from the default
  209. `tool-bar-map'. If it is t, start with a new sparse map. You
  210. can use \\[describe-key] <icon> to find out the name of an icon
  211. item. When \\[describe-key] <icon> shows \"<tool-bar> <new-file>
  212. runs the command find-file\", then use `new-file' in ZAP-LIST.
  213. DEFAULT-MAP specifies the default key map for ICON-LIST."
  214. (let (;; For Emacs 21, we must let-bind `tool-bar-map'. In Emacs 22, we
  215. ;; could use some other local variable.
  216. (tool-bar-map (if (eq zap-list t)
  217. (make-sparse-keymap)
  218. (copy-keymap tool-bar-map))))
  219. (when (listp zap-list)
  220. ;; Zap some items which aren't relevant for this mode and take up space.
  221. (dolist (key zap-list)
  222. (define-key tool-bar-map (vector key) nil)))
  223. (mapc (lambda (el)
  224. (let ((command (car el))
  225. (icon (nth 1 el))
  226. (fmap (or (nth 2 el) default-map))
  227. (props (cdr (cdr (cdr el)))) )
  228. ;; command may stem from different from-maps:
  229. (cond ((eq command 'gmm-ignore)
  230. ;; The dummy `gmm-ignore', see `gmm-tool-bar-item'
  231. ;; widget. Suppress tooltip by adding `:enable nil'.
  232. (if (fboundp 'tool-bar-local-item)
  233. (apply 'tool-bar-local-item icon nil nil
  234. tool-bar-map :enable nil props)
  235. ;; (tool-bar-local-item ICON DEF KEY MAP &rest PROPS)
  236. ;; (tool-bar-add-item ICON DEF KEY &rest PROPS)
  237. (apply 'tool-bar-add-item icon nil nil :enable nil props)))
  238. ((equal fmap t) ;; Not a menu command
  239. (apply 'tool-bar-local-item
  240. icon command
  241. (intern icon) ;; reuse icon or fmap here?
  242. tool-bar-map props))
  243. (t ;; A menu command
  244. (apply 'tool-bar-local-item-from-menu
  245. ;; (apply 'tool-bar-local-item icon def key
  246. ;; tool-bar-map props)
  247. command icon tool-bar-map (symbol-value fmap)
  248. props)))
  249. t))
  250. (if (symbolp icon-list)
  251. (eval icon-list)
  252. icon-list))
  253. tool-bar-map))
  254. (defmacro defun-gmm (name function arg-list &rest body)
  255. "Create function NAME.
  256. If FUNCTION exists, then NAME becomes an alias for FUNCTION.
  257. Otherwise, create function NAME with ARG-LIST and BODY."
  258. (let ((defined-p (fboundp function)))
  259. (if defined-p
  260. `(defalias ',name ',function)
  261. `(defun ,name ,arg-list ,@body))))
  262. (defun-gmm gmm-image-search-load-path
  263. image-search-load-path (file &optional path)
  264. "Emacs 21 and XEmacs don't have `image-search-load-path'.
  265. This function returns nil on those systems."
  266. nil)
  267. ;; Cf. `mh-image-load-path-for-library' in `mh-compat.el'.
  268. (defun-gmm gmm-image-load-path-for-library
  269. image-load-path-for-library (library image &optional path no-error)
  270. "Return a suitable search path for images used by LIBRARY.
  271. It searches for IMAGE in `image-load-path' (excluding
  272. \"`data-directory'/images\") and `load-path', followed by a path
  273. suitable for LIBRARY, which includes \"../../etc/images\" and
  274. \"../etc/images\" relative to the library file itself, and then
  275. in \"`data-directory'/images\".
  276. Then this function returns a list of directories which contains
  277. first the directory in which IMAGE was found, followed by the
  278. value of `load-path'. If PATH is given, it is used instead of
  279. `load-path'.
  280. If NO-ERROR is non-nil and a suitable path can't be found, don't
  281. signal an error. Instead, return a list of directories as before,
  282. except that nil appears in place of the image directory.
  283. Here is an example that uses a common idiom to provide
  284. compatibility with versions of Emacs that lack the variable
  285. `image-load-path':
  286. ;; Shush compiler.
  287. (defvar image-load-path)
  288. (let* ((load-path (image-load-path-for-library \"mh-e\" \"mh-logo.xpm\"))
  289. (image-load-path (cons (car load-path)
  290. (when (boundp 'image-load-path)
  291. image-load-path))))
  292. (mh-tool-bar-folder-buttons-init))"
  293. (unless library (error "No library specified"))
  294. (unless image (error "No image specified"))
  295. (let (image-directory image-directory-load-path)
  296. ;; Check for images in image-load-path or load-path.
  297. (let ((img image)
  298. (dir (or
  299. ;; Images in image-load-path.
  300. (gmm-image-search-load-path image) ;; "gmm-" prefix!
  301. ;; Images in load-path.
  302. (locate-library image)))
  303. parent)
  304. ;; Since the image might be in a nested directory (for
  305. ;; example, mail/attach.pbm), adjust `image-directory'
  306. ;; accordingly.
  307. (when dir
  308. (setq dir (file-name-directory dir))
  309. (while (setq parent (file-name-directory img))
  310. (setq img (directory-file-name parent)
  311. dir (expand-file-name "../" dir))))
  312. (setq image-directory-load-path dir))
  313. ;; If `image-directory-load-path' isn't Emacs's image directory,
  314. ;; it's probably a user preference, so use it. Then use a
  315. ;; relative setting if possible; otherwise, use
  316. ;; `image-directory-load-path'.
  317. (cond
  318. ;; User-modified image-load-path?
  319. ((and image-directory-load-path
  320. (not (equal image-directory-load-path
  321. (file-name-as-directory
  322. (expand-file-name "images" data-directory)))))
  323. (setq image-directory image-directory-load-path))
  324. ;; Try relative setting.
  325. ((let (library-name d1ei d2ei)
  326. ;; First, find library in the load-path.
  327. (setq library-name (locate-library library))
  328. (if (not library-name)
  329. (error "Cannot find library %s in load-path" library))
  330. ;; And then set image-directory relative to that.
  331. (setq
  332. ;; Go down 2 levels.
  333. d2ei (file-name-as-directory
  334. (expand-file-name
  335. (concat (file-name-directory library-name) "../../etc/images")))
  336. ;; Go down 1 level.
  337. d1ei (file-name-as-directory
  338. (expand-file-name
  339. (concat (file-name-directory library-name) "../etc/images"))))
  340. (setq image-directory
  341. ;; Set it to nil if image is not found.
  342. (cond ((file-exists-p (expand-file-name image d2ei)) d2ei)
  343. ((file-exists-p (expand-file-name image d1ei)) d1ei)))))
  344. ;; Use Emacs's image directory.
  345. (image-directory-load-path
  346. (setq image-directory image-directory-load-path))
  347. (no-error
  348. (message "Could not find image %s for library %s" image library))
  349. (t
  350. (error "Could not find image %s for library %s" image library)))
  351. ;; Return an augmented `path' or `load-path'.
  352. (nconc (list image-directory)
  353. (delete image-directory (copy-sequence (or path load-path))))))
  354. (defun gmm-customize-mode (&optional mode)
  355. "Customize customization group for MODE.
  356. If mode is nil, use `major-mode' of the current buffer."
  357. (interactive)
  358. (customize-group
  359. (or mode
  360. (intern (let ((mode (symbol-name major-mode)))
  361. (string-match "^\\(.+\\)-mode$" mode)
  362. (match-string 1 mode))))))
  363. (defun gmm-write-region (start end filename &optional append visit
  364. lockname mustbenew)
  365. "Compatibility function for `write-region'.
  366. In XEmacs, the seventh argument of `write-region' specifies the
  367. coding-system."
  368. (if (and mustbenew (featurep 'xemacs))
  369. (if (file-exists-p filename)
  370. (signal 'file-already-exists (list "File exists" filename))
  371. (write-region start end filename append visit lockname))
  372. (write-region start end filename append visit lockname mustbenew)))
  373. (provide 'gmm-utils)
  374. ;;; gmm-utils.el ends here