org-freemind.el 45 KB

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