2

Suppose I want to add a small sketch to a org-mode document (and possibly a LaTeX one). I'd like to type [[file:my_sketch.png]], and when the file my_sketch doesn't exist, offer to open an application (some paint equivalent) to create it. Do you know of an org-mode package providing this?

It would even be better if it were to work also in a LaTeX file with a \includegraphics{my_sketch.png} line.

1

An alternative approach is to modify what clicking on the file link does. That way you choose when to open it in the editing program. Below I add a menu to the file link with completion that gives you an option to open it in inkscape. Apparently you cannot open a nonexistent file in inkscape, so there is some code to create one from a template first.

(defvar template-svg nil
  "Blank document for inkscape. You cannot create a file at the
  command line, so we put this template in and open it.")

(setq template-svg "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<svg
   xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
   xmlns:cc=\"http://creativecommons.org/ns#\"
   xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"
   xmlns:svg=\"http://www.w3.org/2000/svg\"
   xmlns=\"http://www.w3.org/2000/svg\"
   xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"
   xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"
   width=\"744.09448819\"
   height=\"1052.3622047\"
   id=\"svg2\"
   version=\"1.1\"
   inkscape:version=\"0.92.2 (5c3e80d, 2017-08-06)\"
   sodipodi:docname=\"some-sketch.svg\">
  <defs
     id=\"defs4\" />
  <sodipodi:namedview
     id=\"base\"
     pagecolor=\"#ffffff\"
     bordercolor=\"#666666\"
     borderopacity=\"1.0\"
     inkscape:pageopacity=\"0.0\"
     inkscape:pageshadow=\"2\"
     inkscape:zoom=\"0.35\"
     inkscape:cx=\"375\"
     inkscape:cy=\"520\"
     inkscape:document-units=\"px\"
     inkscape:current-layer=\"layer1\"
     showgrid=\"false\"
     inkscape:window-width=\"460\"
     inkscape:window-height=\"438\"
     inkscape:window-x=\"871\"
     inkscape:window-y=\"33\"
     inkscape:window-maximized=\"0\" />
  <metadata
     id=\"metadata7\">
    <rdf:RDF>
      <cc:Work
         rdf:about=\"\">
        <dc:format>image/svg+xml</dc:format>
        <dc:type
           rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />
        <dc:title></dc:title>
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label=\"Layer 1\"
     inkscape:groupmode=\"layer\"
     id=\"layer1\" />
</svg>")


(defun inkscape-open (path)
  "Open the path in inkscape"
  (interactive)
  (unless (f-ext-p path "svg") (error "Must be an svg file."))
  (unless (file-exists-p path)
    (with-temp-file path
      (insert template-svg)))
  (shell-command (format "inkscape %s &" path)))


(org-link-set-parameters
 "file"
 :follow (lambda (path)
       (let ((actions '(("find-file" . find-file)
                ("edit in inkscape" . inkscape-open))))
         (funcall (cdr (assoc (completing-read "Action: " actions) actions)) path))))
  • Uups. I would never modify the :follow parameter for such a general url protocol like file:. If you need special handling define your own protocol. I considered that myself but it requires a quite complicated new definition for inline-display of images (see emacs.stackexchange.com/a/38109/2370). Furthermore ivy-read is not available in emacs -Q. You need to add a require or to specify installation instructions. – Tobias Jan 26 '18 at 16:50
  • I changed it to completing-read. Otherwise, to each their own. It is customizable so that you can customize it. I also considered a special link for it and ran into the same inline-image display complexity. – John Kitchin Jan 26 '18 at 18:32
  • It's exactly what I needed, I'm using it for png files with kolourpaint. It can create an empty file from the command line, so no need for a png template. – wilk Jan 31 '18 at 9:18
1

You could simply electrify the closing bracket. I.e., rebind the org-key ] to a command org+-electric-closing-bracket that checks whether there is an file url before point and open the application if the file is missing.

(defcustom org+-missing-link-target-program "gimp"
  "Program for creating files for link targets."
  :group 'org-link
  :type 'string)

(defcustom org+-link-target-re "^file:\\(.*\\)$"
  "Regexp identifying file link targets."
  :group 'org-link
  :type 'regexp)

(defun org+-electric-closing-bracket (n)
  "Insert current character and send prefix-arg greater element, check whether we are at a bracketed link
   Should be bound to ?.
In that case start org+-missing-link-target-program "
  (interactive "p")
  (org-self-insert-command n)
  (when (looking-back org-bracket-link-regexp (line-beginning-position))
    (let* ((url (match-string 1))
       (descr (match-string 2))
       (fname (or (and (string-match org+-link-target-re url) (match-string 1 url))
              (and (string-match "`file:\\(.*\\)$" descr) (match-string 1 descr)))))
      (when (and (> (length fname) 0)
         (null (file-exists-p fname))
         (y-or-n-p (format "File \"%s\" missing.  Create with \"%s\"? " fname org+-missing-link-target-program)))
    (let ((fdir (file-name-directory fname)))
      (when (or (null fdir) ;; no directory component
            (file-exists-p fdir)
            (when (y-or-n-p (format "Directory \"%s\" missing. Create? " fdir))
              (mkdir fdir t)
              t))
        (let ((buf (get-buffer-create "*org process*")))
          (start-process "*org process*" buf org+-missing-link-target-program fname))))))))

(defun org+-eletrify-closing-bracket ()
  "Setup closing bracket for creating missing files."
  (local-set-key (kbd "]") #'org+-electric-closing-bracket))

(add-hook 'org-mode-hook #'org+-eletrify-closing-bracket)

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.