org-freemind.el 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
  1. ;;; org-freemind.el --- Export Org files to freemind
  2. ;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.
  3. ;; Author: Lennart Borgman (lennart O borgman A gmail O com)
  4. ;; Keywords: outlines, hypermedia, calendar, wp
  5. ;; Homepage: http://orgmode.org
  6. ;; Version: 7.01trans
  7. ;;
  8. ;; This file is part of GNU Emacs.
  9. ;;
  10. ;; GNU 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 of the License, or
  13. ;; (at your option) any later version.
  14. ;; GNU 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. If not, see <http://www.gnu.org/licenses/>.
  20. ;; --------------------------------------------------------------------
  21. ;; Features that might be required by this library:
  22. ;;
  23. ;; `backquote', `bytecomp', `cl', `easymenu', `font-lock',
  24. ;; `noutline', `org', `org-compat', `org-faces', `org-footnote',
  25. ;; `org-list', `org-macs', `org-src', `outline', `syntax',
  26. ;; `time-date', `xml'.
  27. ;;
  28. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  29. ;;
  30. ;;; Commentary:
  31. ;;
  32. ;; This file tries to implement some functions useful for
  33. ;; transformation between org-mode and FreeMind files.
  34. ;;
  35. ;; Here are the commands you can use:
  36. ;;
  37. ;; M-x `org-freemind-from-org-mode'
  38. ;; M-x `org-freemind-from-org-mode-node'
  39. ;; M-x `org-freemind-from-org-sparse-tree'
  40. ;;
  41. ;; M-x `org-freemind-to-org-mode'
  42. ;;
  43. ;; M-x `org-freemind-show'
  44. ;;
  45. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  46. ;;
  47. ;;; Change log:
  48. ;;
  49. ;; 2009-02-15: Added check for next level=current+1
  50. ;; 2009-02-21: Fixed bug in `org-freemind-to-org-mode'.
  51. ;; 2009-10-25: Added support for `org-odd-levels-only'.
  52. ;; Added y/n question before showing in FreeMind.
  53. ;; 2009-11-04: Added support for #+BEGIN_HTML.
  54. ;;
  55. ;;
  56. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  57. ;;
  58. ;; This program is free software; you can redistribute it and/or
  59. ;; modify it under the terms of the GNU General Public License as
  60. ;; published by the Free Software Foundation; either version 2, or
  61. ;; (at your option) any later version.
  62. ;;
  63. ;; This program is distributed in the hope that it will be useful,
  64. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  65. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  66. ;; General Public License for more details.
  67. ;;
  68. ;; You should have received a copy of the GNU General Public License
  69. ;; along with this program; see the file COPYING. If not, write to
  70. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  71. ;; Floor, Boston, MA 02110-1301, USA.
  72. ;;
  73. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  74. ;;
  75. ;;; Code:
  76. (require 'xml)
  77. (require 'org)
  78. (require 'rx)
  79. (require 'org-exp)
  80. (eval-when-compile (require 'cl))
  81. ;; Fix-me: I am not sure these are useful:
  82. ;;
  83. ;; (defcustom org-freemind-main-fgcolor "black"
  84. ;; "Color of main node's text."
  85. ;; :type 'color
  86. ;; :group 'freemind)
  87. ;; (defcustom org-freemind-main-color "black"
  88. ;; "Background color of main node."
  89. ;; :type 'color
  90. ;; :group 'freemind)
  91. ;; (defcustom org-freemind-child-fgcolor "black"
  92. ;; "Color of child nodes' text."
  93. ;; :type 'color
  94. ;; :group 'freemind)
  95. ;; (defcustom org-freemind-child-color "black"
  96. ;; "Background color of child nodes."
  97. ;; :type 'color
  98. ;; :group 'freemind)
  99. (defvar org-freemind-node-style nil "Internal use.")
  100. (defcustom org-freemind-node-styles nil
  101. "Styles to apply to node.
  102. NOT READY YET."
  103. :type '(repeat
  104. (list :tag "Node styles for file"
  105. (regexp :tag "File name")
  106. (repeat
  107. (list :tag "Node"
  108. (regexp :tag "Node name regexp")
  109. (set :tag "Node properties"
  110. (list :format "%v" (const :format "" node-style)
  111. (choice :tag "Style"
  112. :value bubble
  113. (const bubble)
  114. (const fork)))
  115. (list :format "%v" (const :format "" color)
  116. (color :tag "Color" :value "red"))
  117. (list :format "%v" (const :format "" background-color)
  118. (color :tag "Background color" :value "yellow"))
  119. (list :format "%v" (const :format "" edge-color)
  120. (color :tag "Edge color" :value "green"))
  121. (list :format "%v" (const :format "" edge-style)
  122. (choice :tag "Edge style" :value bezier
  123. (const :tag "Linear" linear)
  124. (const :tag "Bezier" bezier)
  125. (const :tag "Sharp Linear" sharp-linear)
  126. (const :tag "Sharp Bezier" sharp-bezier)))
  127. (list :format "%v" (const :format "" edge-width)
  128. (choice :tag "Edge width" :value thin
  129. (const :tag "Parent" parent)
  130. (const :tag "Thin" thin)
  131. (const 1)
  132. (const 2)
  133. (const 4)
  134. (const 8)))
  135. (list :format "%v" (const :format "" italic)
  136. (const :tag "Italic font" t))
  137. (list :format "%v" (const :format "" bold)
  138. (const :tag "Bold font" t))
  139. (list :format "%v" (const :format "" font-name)
  140. (string :tag "Font name" :value "SansSerif"))
  141. (list :format "%v" (const :format "" font-size)
  142. (integer :tag "Font size" :value 12)))))))
  143. :group 'freemind)
  144. ;;;###autoload
  145. (defun org-export-as-freemind (arg &optional hidden ext-plist
  146. to-buffer body-only pub-dir)
  147. (interactive "P")
  148. (let* ((opt-plist (org-combine-plists (org-default-export-plist)
  149. ext-plist
  150. (org-infile-export-plist)))
  151. (region-p (org-region-active-p))
  152. (rbeg (and region-p (region-beginning)))
  153. (rend (and region-p (region-end)))
  154. (subtree-p
  155. (if (plist-get opt-plist :ignore-subtree-p)
  156. nil
  157. (when region-p
  158. (save-excursion
  159. (goto-char rbeg)
  160. (and (org-at-heading-p)
  161. (>= (org-end-of-subtree t t) rend))))))
  162. (opt-plist (setq org-export-opt-plist
  163. (if subtree-p
  164. (org-export-add-subtree-options opt-plist rbeg)
  165. opt-plist)))
  166. (bfname (buffer-file-name (or (buffer-base-buffer) (current-buffer))))
  167. (filename (concat (file-name-as-directory
  168. (or pub-dir
  169. (org-export-directory :ascii opt-plist)))
  170. (file-name-sans-extension
  171. (or (and subtree-p
  172. (org-entry-get (region-beginning)
  173. "EXPORT_FILE_NAME" t))
  174. (file-name-nondirectory bfname)))
  175. ".mm")))
  176. (when (file-exists-p filename)
  177. (delete-file filename))
  178. (cond
  179. (subtree-p
  180. (org-freemind-from-org-mode-node (line-number-at-pos rbeg)
  181. filename))
  182. (t (org-freemind-from-org-mode bfname filename)))))
  183. ;;;###autoload
  184. (defun org-freemind-show (mm-file)
  185. "Show file MM-FILE in FreeMind."
  186. (interactive
  187. (list
  188. (save-match-data
  189. (let ((name (read-file-name "FreeMind file: "
  190. nil nil nil
  191. (if (buffer-file-name)
  192. (file-name-nondirectory (buffer-file-name))
  193. "")
  194. ;; Fix-me: Is this an Emacs bug?
  195. ;; This predicate function is never
  196. ;; called.
  197. (lambda (fn)
  198. (string-match "^mm$" (file-name-extension fn))))))
  199. (setq name (expand-file-name name))
  200. name))))
  201. (org-open-file mm-file))
  202. (defconst org-freemind-org-nfix "--org-mode: ")
  203. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  204. ;;; Format converters
  205. (defun org-freemind-escape-str-from-org (org-str)
  206. "Do some html-escaping of ORG-STR and return the result.
  207. The characters \"&<> will be escaped."
  208. (let ((chars (append org-str nil))
  209. (fm-str ""))
  210. (dolist (cc chars)
  211. (setq fm-str
  212. (concat fm-str
  213. (if (< cc 256)
  214. (cond
  215. ((= cc ?\") "&quot;")
  216. ((= cc ?\&) "&amp;")
  217. ((= cc ?\<) "&lt;")
  218. ((= cc ?\>) "&gt;")
  219. (t (char-to-string cc)))
  220. ;; Formatting as &#number; is maybe needed
  221. ;; according to a bug report from kazuo
  222. ;; fujimoto, but I have now instead added a xml
  223. ;; processing instruction saying that the mm
  224. ;; file is utf-8:
  225. ;;
  226. ;; (format "&#x%x;" (- cc ;; ?\x800))
  227. (format "&#x%x;" (encode-char cc 'ucs))
  228. ))))
  229. fm-str))
  230. ;;(org-freemind-unescape-str-to-org "&#x6d;A&#x224C;B&lt;C&#x3C;&#x3D;")
  231. ;;(org-freemind-unescape-str-to-org "&#x3C;&lt;")
  232. (defun org-freemind-unescape-str-to-org (fm-str)
  233. "Do some html-unescaping of FM-STR and return the result.
  234. This is the opposite of `org-freemind-escape-str-from-org' but it
  235. will also unescape &#nn;."
  236. (let ((org-str fm-str))
  237. (setq org-str (replace-regexp-in-string "&quot;" "\"" org-str))
  238. (setq org-str (replace-regexp-in-string "&amp;" "&" org-str))
  239. (setq org-str (replace-regexp-in-string "&lt;" "<" org-str))
  240. (setq org-str (replace-regexp-in-string "&gt;" ">" org-str))
  241. (setq org-str (replace-regexp-in-string
  242. "&#x\\([a-f0-9]\\{2,4\\}\\);"
  243. (lambda (m)
  244. (char-to-string
  245. (+ (string-to-number (match-string 1 m) 16)
  246. 0 ;?\x800 ;; What is this for? Encoding?
  247. )))
  248. org-str))))
  249. ;; (org-freemind-test-escape)
  250. (defun org-freemind-test-escape ()
  251. (let* ((str1 "a quote: \", an amp: &, lt: <; over 256: öåäÖÅÄ")
  252. (str2 (org-freemind-escape-str-from-org str1))
  253. (str3 (org-freemind-unescape-str-to-org str2))
  254. )
  255. (unless (string= str1 str3)
  256. (error "str3=%s" str3))
  257. ))
  258. (defun org-freemind-convert-links-from-org (org-str)
  259. "Convert org links in ORG-STR to FreeMind links and return the result."
  260. (let ((fm-str (replace-regexp-in-string
  261. (rx (not (any "[\""))
  262. (submatch
  263. "http"
  264. (opt ?\s)
  265. "://"
  266. (1+
  267. (any "-%.?@a-zA-Z0-9()_/:~=&#"))))
  268. "[[\\1][\\1]]"
  269. org-str)))
  270. (replace-regexp-in-string (rx "[["
  271. (submatch (*? nonl))
  272. "]["
  273. (submatch (*? nonl))
  274. "]]")
  275. "<a href=\"\\1\">\\2</a>"
  276. fm-str)))
  277. ;;(org-freemind-convert-links-to-org "<a href=\"http://www.somewhere/\">link-text</a>")
  278. (defun org-freemind-convert-links-to-org (fm-str)
  279. "Convert FreeMind links in FM-STR to org links and return the result."
  280. (let ((org-str (replace-regexp-in-string
  281. (rx "<a"
  282. space
  283. (0+
  284. (0+ (not (any ">")))
  285. space)
  286. "href=\""
  287. (submatch (0+ (not (any "\""))))
  288. "\""
  289. (0+ (not (any ">")))
  290. ">"
  291. (submatch (0+ (not (any "<"))))
  292. "</a>")
  293. "[[\\1][\\2]]"
  294. fm-str)))
  295. org-str))
  296. ;; Fix-me:
  297. ;;(defun org-freemind-convert-drawers-from-org (text)
  298. ;; )
  299. ;; (org-freemind-test-links)
  300. ;; (defun org-freemind-test-links ()
  301. ;; (let* ((str1 "[[http://www.somewhere/][link-text]")
  302. ;; (str2 (org-freemind-convert-links-from-org str1))
  303. ;; (str3 (org-freemind-convert-links-to-org str2))
  304. ;; )
  305. ;; (unless (string= str1 str3)
  306. ;; (error "str3=%s" str3))
  307. ;; ))
  308. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  309. ;;; Org => FreeMind
  310. (defun org-freemind-convert-text-p (text)
  311. "Convert TEXT to html with <p> paragraphs."
  312. (setq text (org-freemind-escape-str-from-org text))
  313. (setq text (replace-regexp-in-string (rx "\n" (0+ blank) "\n") "</p><p>\n" text))
  314. ;;(setq text (replace-regexp-in-string (rx bol (1+ blank) eol) "" text))
  315. ;;(setq text (replace-regexp-in-string (rx bol (1+ blank)) "<br />" text))
  316. (setq text (replace-regexp-in-string "\n" "<br />" text))
  317. (concat "<p>"
  318. (org-freemind-convert-links-from-org text)
  319. "</p>\n"))
  320. (defun org-freemind-org-text-to-freemind-subnode/note (node-name start end drawers-regexp)
  321. "Convert text part of org node to FreeMind subnode or note.
  322. Convert the text part of the org node named NODE-NAME. The text
  323. is in the current buffer between START and END. Drawers matching
  324. DRAWERS-REGEXP are converted to FreeMind notes."
  325. ;; fix-me: doc
  326. (let ((text (buffer-substring-no-properties start end))
  327. (node-res "")
  328. (note-res ""))
  329. (save-match-data
  330. ;;(setq text (org-freemind-escape-str-from-org text))
  331. ;; First see if there is something that should be moved to the
  332. ;; note part:
  333. (let (drawers)
  334. (while (string-match drawers-regexp text)
  335. (setq drawers (cons (match-string 0 text) drawers))
  336. (setq text
  337. (concat (substring text 0 (match-beginning 0))
  338. (substring text (match-end 0))))
  339. )
  340. (when drawers
  341. (dolist (drawer drawers)
  342. (let ((lines (split-string drawer "\n")))
  343. (dolist (line lines)
  344. (setq note-res (concat
  345. note-res
  346. org-freemind-org-nfix line "<br />\n")))
  347. ))))
  348. (when (> (length note-res) 0)
  349. (setq note-res (concat
  350. "<richcontent TYPE=\"NOTE\"><html>\n"
  351. "<head>\n"
  352. "</head>\n"
  353. "<body>\n"
  354. note-res
  355. "</body>\n"
  356. "</html>\n"
  357. "</richcontent>\n"))
  358. )
  359. ;; There is always an LF char:
  360. (when (> (length text) 1)
  361. (setq node-res (concat
  362. "<node style=\"bubble\" background_color=\"#eeee00\">\n"
  363. "<richcontent TYPE=\"NODE\"><html>\n"
  364. "<head>\n"
  365. "<style type=\"text/css\">\n"
  366. "<!--\n"
  367. "p { margin-top: 0 }\n"
  368. "-->\n"
  369. "</style>\n"
  370. "</head>\n"
  371. "<body>\n"))
  372. (let ((begin-html-mark (regexp-quote "#+BEGIN_HTML"))
  373. (end-html-mark (regexp-quote "#+END_HTML"))
  374. head
  375. end-pos
  376. end-pos-match
  377. )
  378. ;; Take care of #+BEGIN_HTML - #+END_HTML
  379. (while (string-match begin-html-mark text)
  380. (setq head (substring text 0 (match-beginning 0)))
  381. (setq end-pos-match (match-end 0))
  382. (setq node-res (concat node-res
  383. (org-freemind-convert-text-p head)))
  384. (setq text (substring text end-pos-match))
  385. (setq end-pos (string-match end-html-mark text))
  386. (if end-pos
  387. (setq end-pos-match (match-end 0))
  388. (message "org-freemind: Missing #+END_HTML")
  389. (setq end-pos (length text))
  390. (setq end-pos-match end-pos))
  391. (setq node-res (concat node-res
  392. (substring text 0 end-pos)))
  393. (setq text (substring text end-pos-match)))
  394. (setq node-res (concat node-res
  395. (org-freemind-convert-text-p text))))
  396. (setq node-res (concat
  397. node-res
  398. "</body>\n"
  399. "</html>\n"
  400. "</richcontent>\n"
  401. ;; Put a note that this is for the parent node
  402. "<richcontent TYPE=\"NOTE\"><html>"
  403. "<head>"
  404. "</head>"
  405. "<body>"
  406. "<p>"
  407. "-- This is more about \"" node-name "\" --"
  408. "</p>"
  409. "</body>"
  410. "</html>"
  411. "</richcontent>\n"
  412. "</node>\n" ;; ok
  413. )))
  414. (list node-res note-res))))
  415. (defun org-freemind-write-node (mm-buffer drawers-regexp num-left-nodes base-level current-level next-level this-m2 this-node-end this-children-visible next-node-start next-has-some-visible-child)
  416. (let* (this-icons
  417. this-bg-color
  418. this-m2-escaped
  419. this-rich-node
  420. this-rich-note
  421. )
  422. (when (string-match "TODO" this-m2)
  423. (setq this-m2 (replace-match "" nil nil this-m2))
  424. (add-to-list 'this-icons "button_cancel")
  425. (setq this-bg-color "#ffff88")
  426. (when (string-match "\\[#\\(.\\)\\]" this-m2)
  427. (let ((prior (string-to-char (match-string 1 this-m2))))
  428. (setq this-m2 (replace-match "" nil nil this-m2))
  429. (cond
  430. ((= prior ?A)
  431. (add-to-list 'this-icons "full-1")
  432. (setq this-bg-color "#ff0000"))
  433. ((= prior ?B)
  434. (add-to-list 'this-icons "full-2")
  435. (setq this-bg-color "#ffaa00"))
  436. ((= prior ?C)
  437. (add-to-list 'this-icons "full-3")
  438. (setq this-bg-color "#ffdd00"))
  439. ((= prior ?D)
  440. (add-to-list 'this-icons "full-4")
  441. (setq this-bg-color "#ffff00"))
  442. ((= prior ?E)
  443. (add-to-list 'this-icons "full-5"))
  444. ((= prior ?F)
  445. (add-to-list 'this-icons "full-6"))
  446. ((= prior ?G)
  447. (add-to-list 'this-icons "full-7"))
  448. ))))
  449. (setq this-m2 (org-trim this-m2))
  450. (setq this-m2-escaped (org-freemind-escape-str-from-org this-m2))
  451. (let ((node-notes (org-freemind-org-text-to-freemind-subnode/note
  452. this-m2-escaped
  453. this-node-end
  454. (1- next-node-start)
  455. drawers-regexp)))
  456. (setq this-rich-node (nth 0 node-notes))
  457. (setq this-rich-note (nth 1 node-notes)))
  458. (with-current-buffer mm-buffer
  459. (insert "<node text=\"" this-m2-escaped "\"")
  460. (org-freemind-get-node-style this-m2)
  461. (when (> next-level current-level)
  462. (unless (or this-children-visible
  463. next-has-some-visible-child)
  464. (insert " folded=\"true\"")))
  465. (when (and (= current-level (1+ base-level))
  466. (> num-left-nodes 0))
  467. (setq num-left-nodes (1- num-left-nodes))
  468. (insert " position=\"left\""))
  469. (when this-bg-color
  470. (insert " background_color=\"" this-bg-color "\""))
  471. (insert ">\n")
  472. (when this-icons
  473. (dolist (icon this-icons)
  474. (insert "<icon builtin=\"" icon "\"/>\n")))
  475. )
  476. (with-current-buffer mm-buffer
  477. (when this-rich-note (insert this-rich-note))
  478. (when this-rich-node (insert this-rich-node))))
  479. num-left-nodes)
  480. (defun org-freemind-check-overwrite (file interactively)
  481. "Check if file FILE already exists.
  482. If FILE does not exists return t.
  483. If INTERACTIVELY is non-nil ask if the file should be replaced
  484. and return t/nil if it should/should not be replaced.
  485. Otherwise give an error say the file exists."
  486. (if (file-exists-p file)
  487. (if interactively
  488. (y-or-n-p (format "File %s exists, replace it? " file))
  489. (error "File %s already exists" file))
  490. t))
  491. (defvar org-freemind-node-pattern (rx bol
  492. (submatch (1+ "*"))
  493. (1+ space)
  494. (submatch (*? nonl))
  495. eol))
  496. (defun org-freemind-look-for-visible-child (node-level)
  497. (save-excursion
  498. (save-match-data
  499. (let ((found-visible-child nil))
  500. (while (and (not found-visible-child)
  501. (re-search-forward org-freemind-node-pattern nil t))
  502. (let* ((m1 (match-string-no-properties 1))
  503. (level (length m1)))
  504. (if (>= node-level level)
  505. (setq found-visible-child 'none)
  506. (unless (get-char-property (line-beginning-position) 'invisible)
  507. (setq found-visible-child 'found)))))
  508. (eq found-visible-child 'found)
  509. ))))
  510. (defun org-freemind-goto-line (line)
  511. "Go to line number LINE."
  512. (save-restriction
  513. (widen)
  514. (goto-char (point-min))
  515. (forward-line (1- line))))
  516. (defun org-freemind-write-mm-buffer (org-buffer mm-buffer node-at-line)
  517. (with-current-buffer org-buffer
  518. (dolist (node-style org-freemind-node-styles)
  519. (when (org-string-match-p (car node-style) buffer-file-name)
  520. (setq org-freemind-node-style (cadr node-style))))
  521. ;;(message "org-freemind-node-style =%s" org-freemind-node-style)
  522. (save-match-data
  523. (let* ((drawers (copy-sequence org-drawers))
  524. drawers-regexp
  525. (num-top1-nodes 0)
  526. (num-top2-nodes 0)
  527. num-left-nodes
  528. (unclosed-nodes 0)
  529. (odd-only org-odd-levels-only)
  530. (first-time t)
  531. (current-level 1)
  532. base-level
  533. prev-node-end
  534. rich-text
  535. unfinished-tag
  536. node-at-line-level
  537. node-at-line-last)
  538. (with-current-buffer mm-buffer
  539. (erase-buffer)
  540. (insert "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
  541. (insert "<map version=\"0.9.0\">\n")
  542. (insert "<!-- To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net -->\n"))
  543. (save-excursion
  544. ;; Get special buffer vars:
  545. (goto-char (point-min))
  546. (while (re-search-forward (rx bol "#+DRAWERS:") nil t)
  547. (let ((dr-txt (buffer-substring-no-properties (match-end 0) (line-end-position))))
  548. (setq drawers (append drawers (split-string dr-txt) nil))))
  549. (setq drawers-regexp
  550. (concat (rx bol (0+ blank) ":")
  551. (regexp-opt drawers)
  552. (rx ":" (0+ blank)
  553. "\n"
  554. (*? anything)
  555. "\n"
  556. (0+ blank)
  557. ":END:"
  558. (0+ blank)
  559. eol)
  560. ))
  561. (if node-at-line
  562. ;; Get number of top nodes and last line for this node
  563. (progn
  564. (org-freemind-goto-line node-at-line)
  565. (unless (looking-at org-freemind-node-pattern)
  566. (error "No node at line %s" node-at-line))
  567. (setq node-at-line-level (length (match-string-no-properties 1)))
  568. (forward-line)
  569. (setq node-at-line-last
  570. (catch 'last-line
  571. (while (re-search-forward org-freemind-node-pattern nil t)
  572. (let* ((m1 (match-string-no-properties 1))
  573. (level (length m1)))
  574. (if (<= level node-at-line-level)
  575. (progn
  576. (beginning-of-line)
  577. (throw 'last-line (1- (point))))
  578. (if (= level (1+ node-at-line-level))
  579. (setq num-top2-nodes (1+ num-top2-nodes))))))))
  580. (setq current-level node-at-line-level)
  581. (setq num-top1-nodes 1)
  582. (org-freemind-goto-line node-at-line))
  583. ;; First get number of top nodes
  584. (goto-char (point-min))
  585. (while (re-search-forward org-freemind-node-pattern nil t)
  586. (let* ((m1 (match-string-no-properties 1))
  587. (level (length m1)))
  588. (if (= level 1)
  589. (setq num-top1-nodes (1+ num-top1-nodes))
  590. (if (= level 2)
  591. (setq num-top2-nodes (1+ num-top2-nodes))))))
  592. ;; If there is more than one top node we need to insert a node
  593. ;; to keep them together.
  594. (goto-char (point-min))
  595. (when (> num-top1-nodes 1)
  596. (setq num-top2-nodes num-top1-nodes)
  597. (setq current-level 0)
  598. (let ((orig-name (if buffer-file-name
  599. (file-name-nondirectory (buffer-file-name))
  600. (buffer-name))))
  601. (with-current-buffer mm-buffer
  602. (insert "<node text=\"" orig-name "\" background_color=\"#00bfff\">\n"
  603. ;; Put a note that this is for the parent node
  604. "<richcontent TYPE=\"NOTE\"><html>"
  605. "<head>"
  606. "</head>"
  607. "<body>"
  608. "<p>"
  609. org-freemind-org-nfix "WHOLE FILE"
  610. "</p>"
  611. "</body>"
  612. "</html>"
  613. "</richcontent>\n")))))
  614. (setq num-left-nodes (floor num-top2-nodes 2))
  615. (setq base-level current-level)
  616. (let (this-m2
  617. this-node-end
  618. this-children-visible
  619. next-m2
  620. next-node-start
  621. next-level
  622. next-has-some-visible-child
  623. next-children-visible
  624. )
  625. (while (and
  626. (re-search-forward org-freemind-node-pattern nil t)
  627. (if node-at-line-last (<= (point) node-at-line-last) t)
  628. )
  629. (let* ((next-m1 (match-string-no-properties 1))
  630. (next-node-end (match-end 0))
  631. )
  632. (setq next-node-start (match-beginning 0))
  633. (setq next-m2 (match-string-no-properties 2))
  634. (setq next-level (length next-m1))
  635. (setq next-children-visible
  636. (not (eq 'outline
  637. (get-char-property (line-end-position) 'invisible))))
  638. (setq next-has-some-visible-child
  639. (if next-children-visible t
  640. (org-freemind-look-for-visible-child next-level)))
  641. (when this-m2
  642. (setq num-left-nodes (org-freemind-write-node mm-buffer drawers-regexp num-left-nodes base-level current-level next-level this-m2 this-node-end this-children-visible next-node-start next-has-some-visible-child)))
  643. (when (if (= num-top1-nodes 1) (> current-level base-level) t)
  644. (while (>= current-level next-level)
  645. (with-current-buffer mm-buffer
  646. (insert "</node>\n")
  647. (setq current-level
  648. (- current-level (if odd-only 2 1))))))
  649. (setq this-node-end (1+ next-node-end))
  650. (setq this-m2 next-m2)
  651. (setq current-level next-level)
  652. (setq this-children-visible next-children-visible)
  653. (forward-char)
  654. ))
  655. ;;; (unless (if node-at-line-last
  656. ;;; (>= (point) node-at-line-last)
  657. ;;; nil)
  658. ;; Write last node:
  659. (setq this-m2 next-m2)
  660. (setq current-level next-level)
  661. (setq next-node-start (if node-at-line-last
  662. (1+ node-at-line-last)
  663. (point-max)))
  664. (setq num-left-nodes (org-freemind-write-node mm-buffer drawers-regexp num-left-nodes base-level current-level next-level this-m2 this-node-end this-children-visible next-node-start next-has-some-visible-child))
  665. (with-current-buffer mm-buffer (insert "</node>\n"))
  666. ;)
  667. )
  668. (with-current-buffer mm-buffer
  669. (while (> current-level base-level)
  670. (insert "</node>\n")
  671. (setq current-level
  672. (- current-level (if odd-only 2 1)))
  673. ))
  674. (with-current-buffer mm-buffer
  675. (insert "</map>")
  676. (delete-trailing-whitespace)
  677. (goto-char (point-min))
  678. ))))))
  679. (defun org-freemind-get-node-style (node-name)
  680. "NOT READY YET."
  681. ;;<node BACKGROUND_COLOR="#eeee00" CREATED="1234668815593" MODIFIED="1234668815593" STYLE="bubble">
  682. ;;<font BOLD="true" NAME="SansSerif" SIZE="12"/>
  683. (let (node-styles
  684. node-style)
  685. (dolist (style-list org-freemind-node-style)
  686. (let ((node-regexp (car style-list)))
  687. (message "node-regexp=%s node-name=%s" node-regexp node-name)
  688. (when (org-string-match-p node-regexp node-name)
  689. ;;(setq node-style (org-freemind-do-apply-node-style style-list))
  690. (setq node-style (cadr style-list))
  691. (when node-style
  692. (message "node-style=%s" node-style)
  693. (setq node-styles (append node-styles node-style)))
  694. )))))
  695. (defun org-freemind-do-apply-node-style (style-list)
  696. (message "style-list=%S" style-list)
  697. (let ((node-style 'fork)
  698. (color "red")
  699. (background-color "yellow")
  700. (edge-color "green")
  701. (edge-style 'bezier)
  702. (edge-width 'thin)
  703. (italic t)
  704. (bold t)
  705. (font-name "SansSerif")
  706. (font-size 12))
  707. (dolist (style (cadr style-list))
  708. (message " style=%s" style)
  709. (let ((what (car style)))
  710. (cond
  711. ((eq what 'node-style)
  712. (setq node-style (cadr style)))
  713. ((eq what 'color)
  714. (setq color (cadr style)))
  715. ((eq what 'background-color)
  716. (setq background-color (cadr style)))
  717. ((eq what 'edge-color)
  718. (setq edge-color (cadr style)))
  719. ((eq what 'edge-style)
  720. (setq edge-style (cadr style)))
  721. ((eq what 'edge-width)
  722. (setq edge-width (cadr style)))
  723. ((eq what 'italic)
  724. (setq italic (cadr style)))
  725. ((eq what 'bold)
  726. (setq bold (cadr style)))
  727. ((eq what 'font-name)
  728. (setq font-name (cadr style)))
  729. ((eq what 'font-size)
  730. (setq font-size (cadr style)))
  731. )
  732. (insert (format " style=\"%s\"" node-style))
  733. (insert (format " color=\"%s\"" color))
  734. (insert (format " background_color=\"%s\"" background-color))
  735. (insert ">\n")
  736. (insert "<edge")
  737. (insert (format " color=\"%s\"" edge-color))
  738. (insert (format " style=\"%s\"" edge-style))
  739. (insert (format " width=\"%s\"" edge-width))
  740. (insert "/>\n")
  741. (insert "<font")
  742. (insert (format " italic=\"%s\"" italic))
  743. (insert (format " bold=\"%s\"" bold))
  744. (insert (format " name=\"%s\"" font-name))
  745. (insert (format " size=\"%s\"" font-size))
  746. ))))
  747. ;;;###autoload
  748. (defun org-freemind-from-org-mode-node (node-line mm-file)
  749. "Convert node at line NODE-LINE to the FreeMind file MM-FILE."
  750. (interactive
  751. (progn
  752. (unless (org-back-to-heading nil)
  753. (error "Can't find org-mode node start"))
  754. (let* ((line (line-number-at-pos))
  755. (default-mm-file (concat (if buffer-file-name
  756. (file-name-nondirectory buffer-file-name)
  757. "nofile")
  758. "-line-" (number-to-string line)
  759. ".mm"))
  760. (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
  761. (list line mm-file))))
  762. (when (org-freemind-check-overwrite mm-file (interactive-p))
  763. (let ((org-buffer (current-buffer))
  764. (mm-buffer (find-file-noselect mm-file)))
  765. (org-freemind-write-mm-buffer org-buffer mm-buffer node-line)
  766. (with-current-buffer mm-buffer
  767. (basic-save-buffer)
  768. (when (interactive-p)
  769. (switch-to-buffer-other-window mm-buffer)
  770. (when (y-or-n-p "Show in FreeMind? ")
  771. (org-freemind-show buffer-file-name)))))))
  772. ;;;###autoload
  773. (defun org-freemind-from-org-mode (org-file mm-file)
  774. "Convert the `org-mode' file ORG-FILE to the FreeMind file MM-FILE."
  775. ;; Fix-me: better doc, include recommendations etc.
  776. (interactive
  777. (let* ((org-file buffer-file-name)
  778. (default-mm-file (concat
  779. (if org-file
  780. (file-name-nondirectory org-file)
  781. "nofile")
  782. ".mm"))
  783. (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
  784. (list org-file mm-file)))
  785. (when (org-freemind-check-overwrite mm-file (interactive-p))
  786. (let ((org-buffer (if org-file (find-file-noselect org-file) (current-buffer)))
  787. (mm-buffer (find-file-noselect mm-file)))
  788. (org-freemind-write-mm-buffer org-buffer mm-buffer nil)
  789. (with-current-buffer mm-buffer
  790. (basic-save-buffer)
  791. (when (interactive-p)
  792. (switch-to-buffer-other-window mm-buffer)
  793. (when (y-or-n-p "Show in FreeMind? ")
  794. (org-freemind-show buffer-file-name)))))))
  795. ;;;###autoload
  796. (defun org-freemind-from-org-sparse-tree (org-buffer mm-file)
  797. "Convert visible part of buffer ORG-BUFFER to FreeMind file MM-FILE."
  798. (interactive
  799. (let* ((org-file buffer-file-name)
  800. (default-mm-file (concat
  801. (if org-file
  802. (file-name-nondirectory org-file)
  803. "nofile")
  804. "-sparse.mm"))
  805. (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
  806. (list (current-buffer) mm-file)))
  807. (when (org-freemind-check-overwrite mm-file (interactive-p))
  808. (let (org-buffer
  809. (mm-buffer (find-file-noselect mm-file)))
  810. (save-window-excursion
  811. (org-export-visible ?\ nil)
  812. (setq org-buffer (current-buffer)))
  813. (org-freemind-write-mm-buffer org-buffer mm-buffer nil)
  814. (with-current-buffer mm-buffer
  815. (basic-save-buffer)
  816. (when (interactive-p)
  817. (switch-to-buffer-other-window mm-buffer)
  818. (when (y-or-n-p "Show in FreeMind? ")
  819. (org-freemind-show buffer-file-name)))))))
  820. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  821. ;;; FreeMind => Org
  822. ;; (sort '(b a c) 'org-freemind-lt-symbols)
  823. (defun org-freemind-lt-symbols (sym-a sym-b)
  824. (string< (symbol-name sym-a) (symbol-name sym-b)))
  825. ;; (sort '((b . 1) (a . 2) (c . 3)) 'org-freemind-lt-xml-attrs)
  826. (defun org-freemind-lt-xml-attrs (attr-a attr-b)
  827. (string< (symbol-name (car attr-a)) (symbol-name (car attr-b))))
  828. ;; xml-parse-region gives things like
  829. ;; ((p nil "\n"
  830. ;; (a
  831. ;; ((href . "link"))
  832. ;; "text")
  833. ;; "\n"
  834. ;; (b nil "hej")
  835. ;; "\n"))
  836. ;; '(a . nil)
  837. ;; (org-freemind-symbols= 'a (car '(A B)))
  838. (defsubst org-freemind-symbols= (sym-a sym-b)
  839. "Return t if downcased names of SYM-A and SYM-B are equal.
  840. SYM-A and SYM-B should be symbols."
  841. (or (eq sym-a sym-b)
  842. (string= (downcase (symbol-name sym-a))
  843. (downcase (symbol-name sym-b)))))
  844. (defun org-freemind-get-children (parent path)
  845. "Find children node to PARENT from PATH.
  846. PATH should be a list of steps, where each step has the form
  847. '(NODE-NAME (ATTR-NAME . ATTR-VALUE))"
  848. ;; Fix-me: maybe implement op? step: Name, number, attr, attr op val
  849. ;; Fix-me: case insensitive version for children?
  850. (let* ((children (if (not (listp (car parent)))
  851. (cddr parent)
  852. (let (cs)
  853. (dolist (p parent)
  854. (dolist (c (cddr p))
  855. (add-to-list 'cs c)))
  856. cs)
  857. ))
  858. (step (car path))
  859. (step-node (if (listp step) (car step) step))
  860. (step-attr-list (when (listp step) (sort (cdr step) 'org-freemind-lt-xml-attrs)))
  861. (path-tail (cdr path))
  862. path-children)
  863. (dolist (child children)
  864. ;; skip xml.el formatting nodes
  865. (unless (stringp child)
  866. ;; compare node name
  867. (when (if (not step-node)
  868. t ;; any node name
  869. (org-freemind-symbols= step-node (car child)))
  870. (if (not step-attr-list)
  871. ;;(throw 'path-child child) ;; no attr to care about
  872. (add-to-list 'path-children child)
  873. (let* ((child-attr-list (cadr child))
  874. (step-attr-copy (copy-sequence step-attr-list)))
  875. (dolist (child-attr child-attr-list)
  876. ;; Compare attr names:
  877. (when (org-freemind-symbols= (caar step-attr-copy) (car child-attr))
  878. ;; Compare values:
  879. (let ((step-val (cdar step-attr-copy))
  880. (child-val (cdr child-attr)))
  881. (when (if (not step-val)
  882. t ;; any value
  883. (string= step-val child-val))
  884. (setq step-attr-copy (cdr step-attr-copy))))))
  885. ;; Did we find all?
  886. (unless step-attr-copy
  887. ;;(throw 'path-child child)
  888. (add-to-list 'path-children child)
  889. ))))))
  890. (if path-tail
  891. (org-freemind-get-children path-children path-tail)
  892. path-children)))
  893. (defun org-freemind-get-richcontent-node (node)
  894. (let ((rc-nodes
  895. (org-freemind-get-children node '((richcontent (type . "NODE")) html body))))
  896. (when (> (length rc-nodes) 1)
  897. (lwarn t :warning "Unexpected structure: several <richcontent type=\"NODE\" ...>"))
  898. (car rc-nodes)))
  899. (defun org-freemind-get-richcontent-note (node)
  900. (let ((rc-notes
  901. (org-freemind-get-children node '((richcontent (type . "NOTE")) html body))))
  902. (when (> (length rc-notes) 1)
  903. (lwarn t :warning "Unexpected structure: several <richcontent type=\"NOTE\" ...>"))
  904. (car rc-notes)))
  905. (defun org-freemind-test-get-tree-text ()
  906. (let ((node '(p nil "\n"
  907. (a
  908. ((href . "link"))
  909. "text")
  910. "\n"
  911. (b nil "hej")
  912. "\n")))
  913. (org-freemind-get-tree-text node)))
  914. ;; (org-freemind-test-get-tree-text)
  915. (defun org-freemind-get-tree-text (node)
  916. (when node
  917. (let ((ntxt "")
  918. (link nil)
  919. (lf-after nil))
  920. (dolist (n node)
  921. (case n
  922. ;;(a (setq is-link t) )
  923. ((h1 h2 h3 h4 h5 h6 p)
  924. ;;(setq ntxt (concat "\n" ntxt))
  925. (setq lf-after 2)
  926. )
  927. (br
  928. (setq lf-after 1)
  929. )
  930. (t
  931. (cond
  932. ((stringp n)
  933. (when (string= n "\n") (setq n ""))
  934. (if link
  935. (setq ntxt (concat ntxt
  936. "[[" link "][" n "]]"))
  937. (setq ntxt (concat ntxt n))))
  938. ((and n (listp n))
  939. (if (symbolp (car n))
  940. (setq ntxt (concat ntxt (org-freemind-get-tree-text n)))
  941. ;; This should be the attributes:
  942. (dolist (att-val n)
  943. (let ((att (car att-val))
  944. (val (cdr att-val)))
  945. (when (eq att 'href)
  946. (setq link val)))))
  947. )))))
  948. (if lf-after
  949. (setq ntxt (concat ntxt (make-string lf-after ?\n)))
  950. (setq ntxt (concat ntxt " ")))
  951. ;;(setq ntxt (concat ntxt (format "{%s}" n)))
  952. ntxt)))
  953. (defun org-freemind-get-richcontent-node-text (node)
  954. "Get the node text as from the richcontent node NODE."
  955. (save-match-data
  956. (let* ((rc (org-freemind-get-richcontent-node node))
  957. (txt (org-freemind-get-tree-text rc)))
  958. ;;(when txt (setq txt (replace-regexp-in-string (rx (1+ whitespace)) " " txt)))
  959. txt
  960. )))
  961. (defun org-freemind-get-richcontent-note-text (node)
  962. "Get the node text as from the richcontent note NODE."
  963. (save-match-data
  964. (let* ((rc (org-freemind-get-richcontent-note node))
  965. (txt (when rc (org-freemind-get-tree-text rc))))
  966. ;;(when txt (setq txt (replace-regexp-in-string (rx (1+ whitespace)) " " txt)))
  967. txt
  968. )))
  969. (defun org-freemind-get-icon-names (node)
  970. (let* ((icon-nodes (org-freemind-get-children node '((icon ))))
  971. names)
  972. (dolist (icn icon-nodes)
  973. (setq names (cons (cdr (assq 'builtin (cadr icn))) names)))
  974. ;; (icon (builtin . "full-1"))
  975. names))
  976. (defun org-freemind-node-to-org (node level skip-levels)
  977. (let ((qname (car node))
  978. (attributes (cadr node))
  979. text
  980. (note (org-freemind-get-richcontent-note-text node))
  981. (mark "-- This is more about ")
  982. (icons (org-freemind-get-icon-names node))
  983. (children (cddr node)))
  984. (when (< 0 (- level skip-levels))
  985. (dolist (attrib attributes)
  986. (case (car attrib)
  987. ('TEXT (setq text (cdr attrib)))
  988. ('text (setq text (cdr attrib)))))
  989. (unless text
  990. ;; There should be a richcontent node holding the text:
  991. (setq text (org-freemind-get-richcontent-node-text node)))
  992. (when icons
  993. (when (member "full-1" icons) (setq text (concat "[#A] " text)))
  994. (when (member "full-2" icons) (setq text (concat "[#B] " text)))
  995. (when (member "full-3" icons) (setq text (concat "[#C] " text)))
  996. (when (member "full-4" icons) (setq text (concat "[#D] " text)))
  997. (when (member "full-5" icons) (setq text (concat "[#E] " text)))
  998. (when (member "full-6" icons) (setq text (concat "[#F] " text)))
  999. (when (member "full-7" icons) (setq text (concat "[#G] " text)))
  1000. (when (member "button_cancel" icons) (setq text (concat "TODO " text)))
  1001. )
  1002. (if (and note
  1003. (string= mark (substring note 0 (length mark))))
  1004. (progn
  1005. (setq text (replace-regexp-in-string "\n $" "" text))
  1006. (insert text))
  1007. (case qname
  1008. ('node
  1009. (insert (make-string (- level skip-levels) ?*) " " text "\n")
  1010. ))))
  1011. (dolist (child children)
  1012. (unless (or (null child)
  1013. (stringp child))
  1014. (org-freemind-node-to-org child (1+ level) skip-levels)))))
  1015. ;; Fix-me: put back special things, like drawers that are stored in
  1016. ;; the notes. Should maybe all notes contents be put in drawers?
  1017. ;;;###autoload
  1018. (defun org-freemind-to-org-mode (mm-file org-file)
  1019. "Convert FreeMind file MM-FILE to `org-mode' file ORG-FILE."
  1020. (interactive
  1021. (save-match-data
  1022. (let* ((mm-file (buffer-file-name))
  1023. (default-org-file (concat (file-name-nondirectory mm-file) ".org"))
  1024. (org-file (read-file-name "Output org-mode file: " nil nil nil default-org-file)))
  1025. (list mm-file org-file))))
  1026. (when (org-freemind-check-overwrite org-file (interactive-p))
  1027. (let ((mm-buffer (find-file-noselect mm-file))
  1028. (org-buffer (find-file-noselect org-file)))
  1029. (with-current-buffer mm-buffer
  1030. (let* ((xml-list (xml-parse-file mm-file))
  1031. (top-node (cadr (cddar xml-list)))
  1032. (note (org-freemind-get-richcontent-note-text top-node))
  1033. (skip-levels
  1034. (if (and note
  1035. (string-match (rx bol "--org-mode: WHOLE FILE" eol) note))
  1036. 1
  1037. 0)))
  1038. (with-current-buffer org-buffer
  1039. (erase-buffer)
  1040. (org-freemind-node-to-org top-node 1 skip-levels)
  1041. (goto-char (point-min))
  1042. (org-set-tags t t) ;; Align all tags
  1043. )
  1044. (switch-to-buffer-other-window org-buffer)
  1045. )))))
  1046. (provide 'org-freemind)
  1047. ;; arch-tag: e7b0d776-94fd-404a-b35e-0f855fae3627
  1048. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1049. ;;; org-freemind.el ends here