I decided to use killer combination emacs + org mode + latex to take my lessons at school, the only thing that force me to use paper and not be an ecological student is for drawing and painting schemas...

I am aware that there are some mode that let you draw something in ascii some are able to use the mouse (like integrated artist-mode), and export it as a real picture, but it doesn't appeal me, programming picture too.

What would be the best solution for me is to quickly run a painting application from emacs which will export by default the picture in a location that emacs would know so the links to the image would be inserted automatically.

I am sure it is possible, I think it doesn't require that much lisp knowledge, but I still need some help...

I know I can run an external command with exec, I may be able to pass the location in argument of the painting program and inserting the link won't be too difficult.

I need your help for managing images name.

How can I get different default names for each picture ?

Would it be possible to recover the name so I can save it to another name ?

And How can I reopen the application to modify the picture ? (like C-c C-c is able to reevaluate source code... )

I have never coded in lisp, but after configurating a bit emacs it doesn't seem to difficult...

I am excited about lisp coding ! I need your help though.

ps: I am not aware of extensions that do the trick, tell me if there are some.

  • There's something that goes against the general pattern of Org use in what you describe, exactly: Org documents are meant to be reproducible, i.e. another person using your document needs to be able to achieve the same results. Once it depends on how you operate another program, that information is lost from the document. I'm not saying you can't do what you want, only that it's not the way it's typically used. As for the drawing program: you may specify what kind of output do you expect (diagrams, photo-quality images, doodles etc). and the platform, whether you are willing to buy one.
    – wvxvw
    Dec 12 '15 at 12:14
  • As for the file handling / creating unique names: gnu.org/software/emacs/manual/html_node/elisp/… here's a bit of the manual that may get you started.
    – wvxvw
    Dec 12 '15 at 12:16

The following code gives you almost what you want in a gnu/linux or cygwin environment. (I do not know whether it works with nt-emacs.) Adaption to other environments should be easy.

  1. How can I get different default names for each picture?

    That is the only caveat. You have to input the file name for yourself. For an example input [[file:Images/test.svg]]. The advantage is that you can choose a sane file name and you know exactly where the file resides. It is not necessary that the file exists. The option org-mouse-w32-app-templates provides some minimal contents for files that must exist when the application starts. There is only something for svg-files yet. You can easily add other types if you want.

  2. Would it be possible to recover the name so I can save it to another name?

    That is no problem with 1.

  3. And How can I reopen the application to modify the picture ? (like C-c C-c is able to reevaluate source code... ).

    That is what the code really does for you. The 3rd mouse button on the link opens a context menu where you can choose to start the windows application for that link. (You can further populate the context menu with useful items yourself if you want.) Here at my place inkscape is opened in this way. Emacs waits until the application is finished. If the file matches a regular expression for image files the images in the current org-buffer are updated thru the process-sentinel org-mouse-app-update-images.

EDIT 1: Now it should be more system independent.

(require 'org)

(add-hook 'org-mode-hook '(lambda ()
                (org-defkey org-mouse-map [mouse-3] 'org-mouse-3-menu)))

(defun org-mouse-3-menu (event)
  "Context menu for org-links."
  (interactive "e")
  (let ((selection (x-popup-menu
             "Org File Menu"
              '("Registered Application" . org-mouse-app)
              ;; other nice applications go here
    (and selection
     (with-current-buffer (window-buffer (posn-window (event-start event)))
       (call-interactively selection)))))

(defcustom org-mouse-app-templates
  '(("svg" . "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\" \"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\"><svg width=\"100%\" height=\"100%\"></svg>"))
  "Some applications bark if they are called with the name of a non-existing file.
This list associates file extensions of non-existing files to some minimal content."
  :type '(alist :key-type string :value-type string)
  :group 'org)

(defvar org-buf)

(defun org-mouse-app-update-images (proc action)
  "Process sentinel for `org-mouse-app' updates images in org-buffer."
  (with-current-buffer (process-buffer proc)
    (when (and (boundp 'org-buf)
           (buffer-live-p org-buf))
      (with-current-buffer org-buf

(defcustom org-mouse-app-alist '((cygwin "cygstart" "-w") ;; option -w: wait for the application to finish
                 (gnu/linux "xdg-open")
                 (windows-nt "start")
                 (t "xdg-open"))
  "Rules for starting default applications.
Association list. Each element is a cons with the system type as `car'
and the list of the command and its first arguments.
The name of the file to be opened is appended to the list.
System type `t' stands for all systems not explicitly contained in the list."
  :type '(alist :key symbol :value (list string))
  :group 'org)

(defun org-mouse-app (event)
  "Start registered application for file at point."
  (interactive "e")
  (set-buffer (window-buffer (posn-window (event-end event))))
  (let* ((file (save-excursion
        (goto-char (posn-point (event-end event)))
        (substitute-in-file-name (nth 1 (org-link-at-point)))))
    (ext (file-name-extension file)))
    (unless (file-exists-p file)
      (let ((content (assoc-string ext org-mouse-app-templates t)))
    (when content
        (insert (cdr content))
        (write-region (point-min) (point-max) file)
    (select-window (posn-window (event-end event)))
    (let ((proc (apply 'start-process (append (list (concat "App:" file)
                            (concat " *App:" file))
                          (cdr (or (assoc-string system-type org-mouse-app-alist)
                               (assoc-string t org-mouse-app-alist)))
                          (list (expand-file-name (encode-coding-string (file-name-sans-versions file t)
                                                (or file-name-coding-system
      (when (string-match (image-file-name-regexp) file)
    (let ((current-buf (current-buffer)))
      (with-current-buffer (process-buffer proc)
        (setq-local org-buf current-buf)
        (set-process-sentinel proc #'org-mouse-app-update-images)))))))

(defun org-link-at-point ()
  "Return list (type path link) of org-link at point."
  (let (type path link line search (pos (point)))
    (catch 'match
    (skip-chars-forward "^]\n\r")
    (when (org-in-regexp org-bracket-link-regexp)
      (setq link (org-link-unescape (org-match-string-no-properties 1)))
      (while (string-match " *\n *" link)
        (setq link (replace-match " " t t link)))
      (setq link (org-link-expand-abbrev link))
       ((or (file-name-absolute-p link)
        (string-match "^\\.\\.?/" link))
        (setq type "file" path link))
       ((string-match org-link-re-with-space3 link)
        (setq type (match-string 1 link) path (match-string 2 link)))
       (t (setq type "thisfile" path link)))
      (throw 'match t)))

      (when (get-text-property (point) 'org-linked-text)
    (setq type "thisfile"
          pos (if (get-text-property (1+ (point)) 'org-linked-text)
              (1+ (point)) (point))
          path (buffer-substring
            (previous-single-property-change pos 'org-linked-text)
            (next-single-property-change pos 'org-linked-text)))
    (throw 'match t))

    (when (or (org-in-regexp org-angle-link-re)
          (org-in-regexp org-plain-link-re))
      (setq type (match-string 1) path (match-string 2))
      (throw 'match t)))
    (when (org-in-regexp (org-re "\\(:[[:alnum:]_@:]+\\):[ \t]*$"))
      (setq type "tags"
        path (match-string 1))
      (while (string-match ":" path)
        (setq path (replace-match "+" t t path)))
      (throw 'match t)))
      (when (org-in-regexp "<\\([^><\n]+\\)>")
    (setq type "tree-match"
          path (match-string 1))
    (throw 'match t)))
    (unless path
      (error "No link found"))
    (list type (substring-no-properties path) (substring-no-properties link))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.