PageRenderTime 160ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/aquamacs/src/site-lisp/macosx/mac-extra-functions.el

http://github.com/davidswelt/aquamacs-emacs
Emacs Lisp | 509 lines | 310 code | 77 blank | 122 comment | 4 complexity | 7785a058b82cdffef92f6248ce160fe8 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0, GPL-2.0, AGPL-3.0
  1. ; Mac extra functions
  2. ;;
  3. ;; Functions specific to use of Emacs on Mac OS X
  4. ;;
  5. ;; Author: David Reitter, david.reitter@gmail.com
  6. ;; Maintainer: David Reitter
  7. ;; Keywords: aquamacs
  8. ;; This file is part of Aquamacs Emacs
  9. ;; http://www.aquamacs.org/
  10. ;; Aquamacs Emacs is free software; you can redistribute it and/or modify
  11. ;; it under the terms of the GNU General Public License as published by
  12. ;; the Free Software Foundation; either version 3, or (at your option)
  13. ;; any later version.
  14. ;; Aquamacs Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;; GNU General Public License for more details.
  18. ;; You should have received a copy of the GNU General Public License
  19. ;; along with GNU Emacs; see the file COPYING. If not, write to the
  20. ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  21. ;; Boston, MA 02111-1307, USA.
  22. ;; Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 David Reitter
  23. ;; The following function needs to be loaded at runtime.
  24. (eval-when-compile (require 'aquamacs-macros))
  25. (defun aquamacs-mac-initialize ()
  26. (defvar aquamacs-mac-application-bundle-directory
  27. (if invocation-directory
  28. (replace-regexp-in-string
  29. "/Contents/MacOS" ""
  30. (directory-file-name (file-name-directory
  31. (file-truename invocation-directory))))
  32. "/Applications/Aquamacs.app")
  33. "The path to the Aquamacs application bundle.")) ;; default
  34. (defun aquamacs-delete-temp-url-files ()
  35. (shell-command "rm -f /tmp/aquamacs-* 2>/dev/null" 'shut-up))
  36. (defun browse-url-safari (url &optional new-window)
  37. "Open URL in a new Safari window."
  38. (interactive (browse-url-interactive-arg "URL: "))
  39. (unless
  40. (string= ""
  41. (shell-command-to-string
  42. (concat "open -a Safari " url)))
  43. (message "Starting Safari...")
  44. (start-process (concat "open -a Safari " url) nil "open -a Safari " url)
  45. (message "Starting Safari... done")))
  46. (defun mac-resources-path ()
  47. (substring data-directory 0 -4))
  48. ;; File Open / Save
  49. ;; To do: present those panels as sheets
  50. ;; using extra events to handle OK / cancel
  51. (defun mac-key-open-file (&optional filename &rest _wildcards)
  52. "Open a file, selecting file by dialog"
  53. (interactive)
  54. (unless filename
  55. (setq filename (ns-read-file-name "Select File to Load" nil t nil)))
  56. (if filename (find-file-existing filename)))
  57. (defun mac-key-open-file-other-frame (&optional filename &rest ignored)
  58. "Open a file in new frame, selecting file by dialog"
  59. (interactive)
  60. (let ((one-buffer-one-frame-mode t))
  61. (mac-key-open-file filename)))
  62. (defun mac-key-save-file ()
  63. (interactive)
  64. "Save buffer. If needed, select file by dialog"
  65. (if buffer-file-name
  66. (save-buffer)
  67. (mac-key-save-file-as)))
  68. (defun mac-key-save-file-as (&optional filename)
  69. "Save buffer to a file, selecting file by dialog.
  70. Displays sheet. File is saved once user has dismissed sheet."
  71. (interactive)
  72. (ns-popup-save-panel "Select File to Save Buffer" default-directory (if buffer-file-name (file-name-nondirectory buffer-file-name) "Untitled")))
  73. ;; when saving a file, set its creator code
  74. (defcustom aquamacs-set-creator-codes-after-writing-files t
  75. "Set creator and type when a file is written.
  76. If t, the creator and type code of a file are set when it is
  77. written. Visited files will retain their code, while new files
  78. will be set to EMAx. If set to `force', the creator code is
  79. always set to EMAx and the type code is always set to TEXT, no
  80. matter what is was when the file was visited. This way, Aquamacs will
  81. open the files it writes when opened per double-click in
  82. Finder. "
  83. :type '(radio (const :tag "Yes" t)
  84. (const :tag "No" nil)
  85. (other :tag "Always set to EMAx" force))
  86. :group 'Aquamacs
  87. ;; :require mac-extra-functions
  88. ;; no require, because if set in customizations, it's set to nil
  89. ;; in which case not loading this package doesn't have a negative
  90. ;; effect
  91. )
  92. ;; the following requires the non-Emacs function
  93. ;; mac-set-creator to be compiled in
  94. (defvar mac-file-creator nil
  95. "Creator of file loaded in buffer (if any was set)")
  96. (defvar mac-file-type nil
  97. "Type of file loaded in buffer (if any was set)")
  98. ;; (add-hook 'find-file-hook 'mac-read-file-creator-and-type)
  99. ;; (add-hook 'after-save-hook 'mac-set-creator-type-codes-for-file)
  100. ;; (mac-get-file-creator "~/aaa")
  101. (defun mac-read-file-creator-and-type ()
  102. ;; initialize creator code for the file that was loaded.
  103. ;; called from `find-file-hook'
  104. (and buffer-file-name
  105. (not (file-remote-p buffer-file-name))
  106. (file-readable-p buffer-file-name) ;; do not set creator/type if file new
  107. (fboundp 'mac-get-file-creator)
  108. (let ((creator (mac-get-file-creator buffer-file-name))
  109. (type (mac-get-file-type buffer-file-name))
  110. (all-zeros (make-string 4 ?\000)))
  111. (if (or (null creator) (equal creator all-zeros))
  112. (set (make-local-variable 'mac-file-creator) 'none)
  113. (set (make-local-variable 'mac-file-creator) creator))
  114. (if (or (null type) (equal type all-zeros))
  115. (set (make-local-variable 'mac-file-type) 'none)
  116. (set (make-local-variable 'mac-file-type) type)))))
  117. (defun mac-set-creator-type-codes-for-file ()
  118. (when (and aquamacs-set-creator-codes-after-writing-files
  119. buffer-file-name
  120. (not (file-remote-p buffer-file-name))
  121. (fboundp 'mac-set-file-creator) (fboundp 'mac-set-file-type))
  122. (cond
  123. ;; always set if configured so
  124. ((eq aquamacs-set-creator-codes-after-writing-files 'force)
  125. (mac-set-file-type buffer-file-name "TEXT"))
  126. ((eq mac-file-type 'none) nil) ;; do not set if not set originally
  127. ;; set to TEXT if a newly created file
  128. ;; or leave untouched otherwise
  129. (t (mac-set-file-type buffer-file-name (or mac-file-type "TEXT"))))
  130. (cond
  131. ((eq aquamacs-set-creator-codes-after-writing-files 'force)
  132. (mac-set-file-creator buffer-file-name "EMAx"))
  133. ((eq mac-file-creator 'none) nil)
  134. (t (mac-set-file-creator buffer-file-name (or mac-file-creator "EMAx"))))
  135. (mac-read-file-creator-and-type)))
  136. ;; (do-applescript (format "try
  137. ;; tell application \"Finder\"
  138. ;; set the creator type of POSIX file \"%s\" to \"EMAx\"
  139. ;; end tell
  140. ;; end try" buffer-file-name)]
  141. ;; copied here from osx-key-mode.el by Seiji Zenitani
  142. ;; modified to work with OS X 10.4 by David Reitter
  143. (defun mac-key-show-in-finder (&optional file)
  144. "Show the open buffer in Finder"
  145. (interactive)
  146. (if (stringp (or file (buffer-file-name)))
  147. (do-applescript
  148. (format "
  149. tell application \"Finder\"
  150. activate
  151. try
  152. select posix file \"%s\"
  153. on error
  154. beep
  155. end try
  156. end tell"
  157. (if (eq selection-coding-system 'sjis-mac)
  158. (replace-regexp-in-string
  159. "\\\\" "\\\\\\\\"
  160. (encode-coding-string
  161. (or file (buffer-file-name))
  162. selection-coding-system))
  163. (encode-coding-string
  164. (or file (buffer-file-name))
  165. selection-coding-system))
  166. ))
  167. (message "No existing file shown in buffer!")
  168. ))
  169. ; (mac-key-show-in-finder "/tmp/")
  170. (defvar aquamacs-mac-add-standard-directories-added-flag nil)
  171. ; (setq aquamacs-mac-add-standard-directories-added-flag nil)
  172. ; (setq normal-top-level-add-subdirs-inode-list nil)
  173. ; (mac-add-standard-directories)
  174. (defun mac-add-standard-directories ()
  175. ;; Add standard directories and automatically add their subdirectories.
  176. ;; this idea blatantly copied and adapted from Martin Schwenke (meltin.net)
  177. (if (not aquamacs-mac-add-standard-directories-added-flag)
  178. (let ((ddir default-directory)
  179. (directories '("/Library/Application Support/Emacs"
  180. ;"/Library/Application Support/Emacs/site-lisp"
  181. "/Library/Application Support/Aquamacs Emacs"
  182. "~/Library/Application Support/Emacs"
  183. ;"~/Library/Application Support/Emacs/site-lisp"
  184. "~/Library/Application Support/Aquamacs Emacs"
  185. "/Library/Preferences/Emacs" ; for all Emacsen
  186. "/Library/Preferences/Aquamacs Emacs" ; for Aquamacs
  187. "~/Library/Preferences/Emacs" ; for all Emacsen (user-specific):
  188. "~/Library/Preferences/Aquamacs Emacs" ; for Aquamacs (user-specific)
  189. )))
  190. (setq aquamacs-mac-add-standard-directories-added-flag t)
  191. (mapc (lambda (dir)
  192. (let* ((xdir (directory-file-name (expand-file-name dir)))
  193. (default-directory xdir))
  194. (and xdir
  195. (not (file-exists-p ".nosearch"))
  196. (not (file-exists-p ".ignore")) ;; backwards compatiblity (Aq2.3 and lower)
  197. (add-to-list 'load-path xdir) ;; add at beginning of list
  198. ;; Now add subdirectories.
  199. (condition-case nil
  200. ;; this will insert at the position of the
  201. ;; current directory (`default-directory'),
  202. ;; which we have canonicalized so that
  203. ;; its position is correctly determined
  204. (normal-top-level-add-subdirs-to-load-path)
  205. (error nil)))))
  206. directories)
  207. ;; remove Enhanced Carbon Emacs plugin
  208. (let ((case-fold-search nil))
  209. (mapc (lambda (dir)
  210. (when (string-match "/ec-emacs" dir)
  211. (message "Enhanced Carbon Emacs plugin at %s is incompatible. Not loaded."
  212. dir)
  213. (setq load-path (delete dir load-path))))
  214. load-path ; modified during iteration
  215. ))
  216. (setq default-directory ddir) ; restore
  217. )))
  218. (defun aq-flat-concat (list)
  219. "Produces a list of all non-nil elements of list."
  220. (let ((c (car-safe list))
  221. (d (cdr-safe list)))
  222. (if c
  223. (if d
  224. (cons c (aq-flat-concat d))
  225. (list c))
  226. (if d
  227. (aq-flat-concat d)
  228. nil))))
  229. (defvar environment-temp-file nil)
  230. ;; (setq shell-file-name "/bin/bash")
  231. ;; (let ((debug-on-error)) (mac-read-environment-vars-from-shell))
  232. ;; Reading the environment variables is complex, primarily due to
  233. ;; bugs in OS X. On some systems, starting the login shell and
  234. ;; printing all variables takes an hour, so we need to have a
  235. ;; timeout. However, starting the process asynchronuously using
  236. ;; `start-process' fails as well on some other systems. Hence the
  237. ;; need to run it with `call-process' and "&", storing the output in
  238. ;; a temporary file.
  239. ;; dr. 07/2008
  240. (defun mac-read-environment-vars-from-shell ()
  241. "Import the environment from the system's default login shell
  242. specified in `shell-file-name'."
  243. (setq environment-temp-file (make-temp-file "envvar-"))
  244. ;; running the shell with -l (to load the environment)
  245. (let ((default-directory "~/")) ; ensure it can be executed
  246. (message "Shell: %s" shell-file-name)
  247. (let* ((coding-system-for-write 'raw-text-unix)
  248. (shell (or shell-file-name "/bin/bash")) ;; can shell-file-name be nil?
  249. (command (format "printenv >%s.tmp; mv %s.tmp %s"
  250. environment-temp-file
  251. environment-temp-file
  252. environment-temp-file)))
  253. (if (string-match ".*/\\(ba\\|z\\)sh" shell)
  254. (call-process shell nil
  255. 0 nil
  256. "-l" "-c" command)
  257. (if (or (string-match ".*/\\tcsh" shell)
  258. (string-match ".*/ksh" shell))
  259. (call-process shell nil
  260. 0 nil
  261. ;; we can't start tcsh as a login shell
  262. ;; because it doesn't accept -l in combination
  263. ;; with a command.
  264. ;; call-process-region wouldn't work because it's
  265. ;; not interactive.
  266. "-c" command)
  267. (message "Could not retrieve login shell environment with login shell: %s" shell)
  268. ;; won't work for csh, because it doesn't take -l -c ...
  269. )))))
  270. ;; we call the process asynchronuously
  271. ;; using start-process does not work for unknown reasons:
  272. ;; sometimes it doesn't get the environment.
  273. ;; (mac-read-environment-vars-from-shell)
  274. ;; (sit-for 1)
  275. ;; (mac-read-environment-vars-from-shell-2)
  276. ;; (insert (getenv "TEST"))
  277. (defun mac-read-environment-vars-from-shell-2 ()
  278. "Reads temporary file if it exists."
  279. (if (and environment-temp-file (file-readable-p environment-temp-file))
  280. (prog1
  281. (with-temp-buffer
  282. (condition-case nil
  283. (progn
  284. (insert-file-contents-literally environment-temp-file nil)
  285. (delete-file environment-temp-file))
  286. (error nil))
  287. (protect ;; set-env can throw errors wrt. coding system
  288. (let ((num 0))
  289. (if (eq (buffer-size) 0)
  290. (message "Warning: Login shell did not return environment.")
  291. (goto-char (point-min))
  292. (while (re-search-forward "^[A-Za-z_0-9]+=()\s*[^\x]*?
  293. \s*}\s*$" nil t)
  294. (replace-match "..." nil nil))
  295. (goto-char (point-min))
  296. (while (search-forward-regexp "^\\(LC_ALL\\|LC_CTYPE\\|LANG\\)=\\(.*\\)$" nil t)
  297. (when (member (match-string 1) '("LC_ALL" "LANG"))
  298. (setenv (match-string 1) (match-string 2))))
  299. ;; init correct locale
  300. (set-locale-environment)
  301. ;; decode buffer (because setenv wants it this way!)
  302. (decode-coding-region (point-min) (point-max) locale-coding-system)
  303. (while (search-forward-regexp "^\\([A-Za-z_0-9]+\\)=\\(.*\\)$" nil t)
  304. (setq num (1+ num))
  305. (setenv (match-string 1)
  306. (if (equal (match-string 1) "PATH") ;; this is probably not needed.
  307. (concat (match-string 2) ":" (getenv "PATH"))
  308. (match-string 2)))))
  309. (message "%d environment variables imported from login shell (%s)."
  310. num shell-file-name)
  311. (mac-post-environment-vars-function)
  312. num)))
  313. nil)))
  314. (defun mac-post-environment-vars-function ()
  315. (mac-add-path-to-exec-path)
  316. (mac-add-local-directory-to-exec-path) ;; needed for CocoAspell
  317. ;; inferior workaround, until mac.c is fixed not to set INFOPATH any longer
  318. ;; do we still need this?
  319. ;; nsterm.m does set INFOPATH.
  320. (if (equal (concat (mac-resources-path)
  321. "info:")
  322. (getenv "INFOPATH"))
  323. (setenv "INFOPATH"))
  324. ;; when INFOPATH is set from outside, it will only load INFOPATH
  325. (let ((extra-dirs (list
  326. "~/Library/Application Support/Emacs/info"
  327. "/Library/Application Support/Emacs/info"
  328. (concat (mac-resources-path)
  329. "lisp/aquamacs/edit-modes/info")
  330. (concat (mac-resources-path)
  331. "info"))))
  332. (setq Info-default-directory-list (append extra-dirs
  333. Info-default-directory-list
  334. ))
  335. (setq Info-directory-list nil) ; force reinitialization
  336. (when (getenv "INFOPATH")
  337. (setenv "INFOPATH" (apply 'concat (getenv "INFOPATH")
  338. (mapcar (lambda (x) (concat ":" x))
  339. extra-dirs))))))
  340. (defun mac-add-path-to-exec-path ()
  341. "Add elements from environment variable `PATH' to `exec-path'."
  342. (let ((l (split-string (getenv "PATH") ":")))
  343. (mapc
  344. (lambda (p)
  345. (unless (member p l)
  346. (nconc l (list p))))
  347. exec-path)
  348. (setq exec-path l)))
  349. (defun mac-add-local-directory-to-exec-path ()
  350. "Add /usr/locaL/bin to `exec-path'"
  351. (add-to-list 'exec-path "/usr/local/bin"))
  352. ;; according to Apple's guidelines, we should
  353. ;; always go for "untitled", "untitled 2", ...
  354. (defun mac-new-buffer-name (name &optional n)
  355. (if (not (get-buffer name))
  356. name
  357. (setq n (if n (+ n 1) 2))
  358. (let ((new-name (concat name " " (int-to-string n))))
  359. (if (not (get-buffer new-name))
  360. new-name
  361. (mac-new-buffer-name name n)
  362. ))
  363. )
  364. )
  365. (defun aq-run-python-command (cmd)
  366. (let ((f (make-temp-file "emacs-command")))
  367. (let ((coding-system-for-write 'no-conversion))
  368. (write-region
  369. cmd nil f nil 'shut-up))
  370. (call-process "python" f (list (get-buffer "*Messages*") t))
  371. (condition-case nil
  372. (delete-file (car f))
  373. (error nil))))
  374. ; Call up help book
  375. (defun aquamacs-help-book-name ()
  376. (format "Aquamacs Help (%s)"
  377. aquamacs-version))
  378. (defun aquamacs-manual-name (manual)
  379. ;; This assumes that book name and book folder are same
  380. ;; Alternatively, we could read our own Info.plist
  381. ;; or have the Makefile store this somewhere in loadefs.
  382. ;; (let ((manual-version
  383. ;; (with-temp-buffer
  384. ;; (insert-file-contents-literally
  385. ;; (concat aquamacs-mac-application-bundle-directory
  386. ;; (format "/Contents/Resources/English.lproj/%s/VERSION"
  387. ;; manual)))
  388. ;; (buffer-substring (point-min) (1- (point-max))))))
  389. ;; (format "%s (%s)"
  390. ;; manual
  391. ;; manual-version))
  392. ;; There is only one Help Book
  393. (aquamacs-help-book-name))
  394. (defun aquamacs-user-help ()
  395. "Show the Aquamacs Help."
  396. (interactive)
  397. (ns-open-help-anchor "AquamacsIndex" (aquamacs-help-book-name)))
  398. (defun aquamacs-emacs-manual ()
  399. "Show the Emacs Manual"
  400. (interactive)
  401. (ns-open-help-anchor "EmacsManualIndex" (aquamacs-manual-name "Emacs Manual")))
  402. (defun aquamacs-elisp-reference ()
  403. "Show the Emacs Lisp Reference"
  404. (interactive)
  405. (ns-open-help-anchor "EmacsLispReferenceIndex" (aquamacs-manual-name "Emacs Lisp Reference")))
  406. ;; it's imporant to make sure that the following are in the Info.plist file:
  407. ;; <key>CFBundleHelpBookFolder</key>
  408. ;; <array>
  409. ;; <string>Aquamacs Help</string>
  410. ;; <string>Emacs Manual</string>
  411. ;; </array>
  412. ;; <key>CFBundleHelpBookName</key>
  413. ;; <array>
  414. ;; <string>Aquamacs Help (VERSION)</string>
  415. ;; <string>Emacs Manual</string>
  416. ;; </array>
  417. ; Call up help book
  418. ; (aquamacs-show-change-log)
  419. (defun aquamacs-show-change-log ()
  420. (interactive)
  421. (ns-open-help-anchor "changelog-top"
  422. (aquamacs-help-book-name)))
  423. (defun gmail-mailclient-p ()
  424. "non-nil if Gmail notifier is detected
  425. Returns `error' if an error occurs.
  426. Mac OS X only (Aquamacs)."
  427. (condition-case nil
  428. (with-temp-buffer
  429. (shell-command "defaults read com.apple.LaunchServices | grep -a1 \"LSHandlerURLScheme = mailto\"" t)
  430. (string-match "gmailnotifier" (buffer-string)))
  431. (error 'error)))
  432. (provide 'mac-extra-functions)