| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 | 
							- ;;; ox-freemind.el --- Freemind Mindmap Back-End for Org Export Engine
 
- ;; Copyright (C) 2013, 2014  Free Software Foundation, Inc.
 
- ;; Author: Jambunathan K <kjambunathan at gmail dot com>
 
- ;; Keywords: outlines, hypermedia, calendar, wp
 
- ;; This file is not part of GNU Emacs.
 
- ;; This program is free software: you can redistribute it and/or modify
 
- ;; it under the terms of the GNU General Public License as published by
 
- ;; the Free Software Foundation, either version 3 of the License, or
 
- ;; (at your option) any later version.
 
- ;; This program is distributed in the hope that it will be useful,
 
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
- ;; GNU General Public License for more details.
 
- ;; You should have received a copy of the GNU General Public License
 
- ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
- ;;; Commentary:
 
- ;; This library implements a Freemind Mindmap back-end for Org generic
 
- ;; exporter.
 
- ;; To test it, run:
 
- ;;
 
- ;;   M-x org-freemind-export-to-freemind
 
- ;;
 
- ;; in an Org mode buffer.  See ox.el for more details on how this
 
- ;; exporter works.
 
- ;;; Code:
 
- ;;; Dependencies
 
- (require 'ox-html)
 
- ;;; Define Back-End
 
- (org-export-define-derived-backend 'freemind 'html
 
-   :export-block "FREEMIND"
 
-   :menu-entry
 
-   '(?f "Export to Freemind Mindmap"
 
-        ((?f "As Freemind Mindmap file" org-freemind-export-to-freemind)
 
- 	(?o "As Freemind Mindmap file and open"
 
- 	    (lambda (a s v b)
 
- 	      (if a (org-freemind-export-to-freemind t s v b)
 
- 		(org-open-file (org-freemind-export-to-freemind nil s v b)))))))
 
-   :translate-alist '((headline . org-freemind-headline)
 
- 		     (template . org-freemind-template)
 
- 		     (inner-template . org-freemind-inner-template)
 
- 		     (section . org-freemind-section)
 
- 		     (entity . org-freemind-entity))
 
-   :filters-alist '((:filter-options . org-freemind-options-function)
 
- 		   (:filter-final-output . org-freemind-final-function)))
 
- ;;; User Configuration Variables
 
- (defgroup org-export-freemind nil
 
-   "Options for exporting Org mode files to Freemind Mindmap."
 
-   :tag "Org Export Freemind Mindmap"
 
-   :group 'org-export)
 
- (defcustom org-freemind-styles
 
-   '((default . "<node>\n</node>")
 
-     (0 . "<node COLOR=\"#000000\">\n<font NAME=\"SansSerif\" SIZE=\"20\"/>\n</node>")
 
-     (1 . "<node COLOR=\"#0033ff\">\n<edge STYLE=\"sharp_bezier\" WIDTH=\"8\"/>\n<font NAME=\"SansSerif\" SIZE=\"18\"/>\n</node>")
 
-     (2 . "<node COLOR=\"#00b439\">\n<edge STYLE=\"bezier\" WIDTH=\"thin\"/>\n<font NAME=\"SansSerif\" SIZE=\"16\"/>\n</node>")
 
-     (3 . "<node COLOR=\"#990000\" FOLDED=\"true\">\n<font NAME=\"SansSerif\" SIZE=\"14\"/>\n</node>")
 
-     (4 . "<node COLOR=\"#111111\">\n</node>"))
 
-   "List of Freemind node styles.
 
- Each entry is of the form (STYLE-NAME . STYLE-SPEC).  STYLE-NAME
 
- can be one of an integer (signifying an outline level), a string
 
- or the symbol `default'.  STYLE-SPEC, a string, is a Freemind
 
- node style."
 
-   :type '(alist :options (default 0 1 2 3)
 
-                 :key-type (choice :tag "Style tag"
 
- 				  (integer :tag "Outline level")
 
- 				  (const :tag "Default value" default)
 
- 				  (string :tag "Node style"))
 
-                 :value-type (string :tag "Style spec"))
 
-   :group 'org-export-freemind)
 
- (defcustom org-freemind-style-map-function 'org-freemind-style-map--automatic
 
-   "Function to map an Org element to it's node style.
 
- The mapping function takes two arguments an Org ELEMENT and INFO.
 
- ELEMENT can be one of the following types - `org-data',
 
- `headline' or `section'.  INFO is a plist holding contextual
 
- information during export.  The function must return a STYLE-SPEC
 
- to be applied to ELEMENT.
 
- See `org-freemind-style-map--automatic' for a sample style
 
- function.  See `org-freemind-styles' for a list of named styles."
 
-   :type '(radio
 
- 	  (function-item org-freemind-style-map--automatic)
 
- 	  (function-item org-freemind-style-map--default)
 
- 	  function)
 
-   :group 'org-export-freemind)
 
- (defcustom org-freemind-section-format 'note
 
-   "Specify how outline sections are to be formatted.
 
- If `inline', append it to the contents of it's heading node.  If
 
- `note', attach it as a note to it's heading node.  If `node',
 
- attach it as a separate node to it's heading node.
 
- Use `note', if the input Org file contains large sections.  Use
 
- `node', if the Org file contains mid-sized sections that need to
 
- stand apart.  Otherwise, use `inline'."
 
-   :type '(choice
 
- 	  (const :tag "Append to outline title" inline)
 
- 	  (const :tag "Attach as a note" note)
 
- 	  (const :tag "Create a separate node" node))
 
-   :group 'org-export-freemind)
 
- ;;;; Debugging
 
- (defcustom org-freemind-pretty-output nil
 
-   "Enable this to generate pretty Freemind Mindmap."
 
-   :type 'boolean
 
-   :group 'org-export-freemind)
 
- ;;; Internal Functions
 
- ;;;; XML Manipulation
 
- (defun org-freemind--serialize (parsed-xml &optional contents)
 
-   "Convert PARSED-XML in to XML string.
 
- PARSED-XML is a parse tree as returned by
 
- `libxml-parse-xml-region'.  CONTENTS is an optional string.
 
- Ignore CONTENTS, if PARSED-XML is not a sole XML element.
 
- Otherwise, append CONTENTS to the contents of top-level element
 
- in PARSED-XML.
 
- This is an inverse function of `libxml-parse-xml-region'.
 
- For purposes of Freemind export, PARSED-XML is a node style
 
- specification - \"<node ...>...</node>\" - as a parse tree."
 
-   (when contents
 
-     (assert (symbolp (car parsed-xml))))
 
-   (cond
 
-    ((null parsed-xml) "")
 
-    ((stringp parsed-xml) parsed-xml)
 
-    ((symbolp (car parsed-xml))
 
-     (let ((attributes (mapconcat
 
- 		       (lambda (av)
 
- 			 (format "%s=\"%s\"" (car av) (cdr av)))
 
- 		       (cadr parsed-xml) " ")))
 
-       (if (or (cddr parsed-xml) contents)
 
- 	  (format "\n<%s%s>%s\n</%s>"
 
- 		  (car parsed-xml)
 
- 		  (if (string= attributes "") "" (concat " " attributes))
 
- 		  (concat (org-freemind--serialize (cddr parsed-xml))
 
- 			  contents )
 
- 		  (car parsed-xml))
 
- 	(format "\n<%s%s/>"
 
- 		(car parsed-xml)
 
- 		(if (string= attributes "") "" (concat " " attributes))))))
 
-    (t (mapconcat #'org-freemind--serialize parsed-xml ""))))
 
- (defun org-freemind--parse-xml (xml-string)
 
-   "Return parse tree for XML-STRING using `libxml-parse-xml-region'.
 
- For purposes of Freemind export, XML-STRING is a node style
 
- specification - \"<node ...>...</node>\" - as a string."
 
-   (with-temp-buffer
 
-     (insert (or xml-string ""))
 
-     (libxml-parse-xml-region (point-min) (point-max))))
 
- ;;;; Style mappers :: Default and Automatic layout
 
- (defun org-freemind-style-map--automatic (element info)
 
-   "Return a node style corresponding to relative outline level of ELEMENT.
 
- ELEMENT can be any of the following types - `org-data',
 
- `headline' or `section'.  See `org-freemind-styles' for style
 
- mappings of different outline levels."
 
-   (let ((style-name
 
- 	 (case (org-element-type element)
 
- 	   (headline
 
- 	    (org-export-get-relative-level element info))
 
- 	   (section
 
- 	    (let ((parent (org-export-get-parent-headline element)))
 
- 	      (if (not parent) 1
 
- 		(1+ (org-export-get-relative-level parent info)))))
 
- 	   (t 0))))
 
-     (or (assoc-default style-name org-freemind-styles)
 
- 	(assoc-default 'default org-freemind-styles)
 
- 	"<node></node>")))
 
- (defun org-freemind-style-map--default (element info)
 
-   "Return the default style for all ELEMENTs.
 
- ELEMENT can be any of the following types - `org-data',
 
- `headline' or `section'.  See `org-freemind-styles' for current
 
- value of default style."
 
-   (or (assoc-default 'default org-freemind-styles)
 
-       "<node></node>"))
 
- ;;;; Helpers :: Retrieve, apply Freemind styles
 
- (defun org-freemind--get-node-style (element info)
 
-   "Return Freemind node style applicable for HEADLINE.
 
- ELEMENT is an Org element of type `org-data', `headline' or
 
- `section'.  INFO is a plist holding contextual information."
 
-   (unless (fboundp org-freemind-style-map-function)
 
-     (setq org-freemind-style-map-function 'org-freemind-style-map--default))
 
-   (let ((style (funcall org-freemind-style-map-function element info)))
 
-     ;; Sanitize node style.
 
-     ;; Loop through the attributes of node element and purge those
 
-     ;; attributes that look suspicious.  This is an extra bit of work
 
-     ;; that allows one to copy verbatim node styles from an existing
 
-     ;; Freemind Mindmap file without messing with the exported data.
 
-     (let* ((data (org-freemind--parse-xml style))
 
- 	   (attributes (cadr data))
 
- 	   (ignored-attrs '(POSITION FOLDED TEXT CREATED ID
 
- 				     MODIFIED)))
 
-       (let (attr)
 
- 	(while (setq attr (pop ignored-attrs))
 
- 	  (setq attributes (assq-delete-all attr attributes))))
 
-       (when data (setcar (cdr data) attributes))
 
-       (org-freemind--serialize data))))
 
- (defun org-freemind--build-stylized-node (style-1 style-2 &optional contents)
 
-   "Build a Freemind node with style STYLE-1 + STYLE-2 and add CONTENTS to it.
 
- STYLE-1 and STYLE-2 are Freemind node styles as a string.
 
- STYLE-1 is the base node style and STYLE-2 is the overriding
 
- style that takes precedence over STYLE-1.  CONTENTS is a string.
 
- Return value is a Freemind node with following properties:
 
-   1. The attributes of \"<node ...> </node>\" element is the union
 
-      of corresponding attributes of STYLE-1 and STYLE-2.  When
 
-      STYLE-1 and STYLE-2 specify values for the same attribute
 
-      name, choose the attribute value from STYLE-2.
 
-   2. The children of \"<node ...> </node>\" element is the union of
 
-      top-level children of STYLE-1 and STYLE-2 with CONTENTS
 
-      appended to it.  When STYLE-1 and STYLE-2 share a child
 
-      element of same type, the value chosen is that from STYLE-2.
 
- For example, merging with following parameters
 
-   STYLE-1  =>
 
-               <node COLOR=\"#00b439\" STYLE=\"Bubble\">
 
-                 <edge STYLE=\"bezier\" WIDTH=\"thin\"/>
 
-                 <font NAME=\"SansSerif\" SIZE=\"16\"/>
 
-               </node>
 
-   STYLE-2  =>
 
-               <node COLOR=\"#990000\" FOLDED=\"true\">
 
-                 <font NAME=\"SansSerif\" SIZE=\"14\"/>
 
-               </node>
 
-   CONTENTS =>
 
-                <attribute NAME=\"ORGTAG\" VALUE=\"@home\"/>
 
- will result in following node:
 
-   RETURN   =>
 
-                <node STYLE=\"Bubble\" COLOR=\"#990000\" FOLDED=\"true\">
 
-                  <edge STYLE=\"bezier\" WIDTH=\"thin\"/>
 
-                  <font NAME=\"SansSerif\" SIZE=\"14\"/>
 
-                  <attribute NAME=\"ORGTAG\" VALUE=\"@home\"/>
 
-                </node>."
 
-   (let* ((data1 (org-freemind--parse-xml (or style-1 "")))
 
- 	 (data2 (org-freemind--parse-xml (or style-2 "")))
 
- 	 (attr1 (cadr data1))
 
- 	 (attr2 (cadr data2))
 
- 	 (merged-attr attr2)
 
- 	 (children1 (cddr data1))
 
- 	 (children2 (cddr data2))
 
- 	 (merged-children children2))
 
-     (let (attr)
 
-       (while (setq attr (pop attr1))
 
- 	(unless (assq (car attr) merged-attr)
 
- 	  (push attr merged-attr))))
 
-     (let (child)
 
-       (while (setq child (pop children1))
 
- 	(when (or (stringp child) (not (assq (car child) merged-children)))
 
- 	  (push child merged-children))))
 
-     (let ((merged-data (nconc (list 'node merged-attr) merged-children)))
 
-       (org-freemind--serialize merged-data contents))))
 
- ;;;; Helpers :: Node contents
 
- (defun org-freemind--richcontent (type contents &optional css-style)
 
-   (let* ((type (case type
 
- 		 (note "NOTE")
 
- 		 (node "NODE")
 
- 		 (t "NODE")))
 
- 	 (contents (org-trim contents)))
 
-     (if (string= (org-trim contents) "") ""
 
-       (format "\n<richcontent TYPE=\"%s\">%s\n</richcontent>"
 
- 	      type
 
- 	      (format "\n<html>\n<head>%s\n</head>\n%s\n</html>"
 
- 		      (or css-style "")
 
- 		      (format "<body>\n%s\n</body>" contents))))))
 
- (defun org-freemind--build-node-contents (element contents info)
 
-   (let* ((title (case (org-element-type element)
 
- 		  (headline
 
- 		   (org-element-property :title element))
 
- 		  (org-data
 
- 		   (plist-get info :title))
 
- 		  (t (error "Shouldn't come here."))))
 
- 	 (element-contents (org-element-contents element))
 
- 	 (section (assq 'section element-contents))
 
- 	 (section-contents
 
- 	  (let ((backend (org-export-create-backend
 
- 			  :parent (org-export-backend-name
 
- 				   (plist-get info :back-end))
 
- 			  :transcoders '((section . (lambda (e c i) c))))))
 
- 	    (org-export-data-with-backend section backend info)))
 
- 	 (itemized-contents-p (let ((first-child-headline
 
- 				     (org-element-map element-contents
 
- 					 'headline 'identity info t)))
 
- 				(when first-child-headline
 
- 				  (org-export-low-level-p first-child-headline
 
- 							  info))))
 
- 	 (node-contents (concat section-contents
 
- 				(when itemized-contents-p
 
- 				  contents))))
 
-     (concat (let ((title (org-export-data title info)))
 
- 	      (case org-freemind-section-format
 
- 		(inline
 
- 		  (org-freemind--richcontent
 
- 		   'node (concat (format "\n<h2>%s</h2>" title)
 
- 				 node-contents) ))
 
- 		(note
 
- 		 (concat (org-freemind--richcontent
 
- 			  'node (format "\n<p>%s\n</p>" title))
 
- 			 (org-freemind--richcontent
 
- 			  'note node-contents)))
 
- 		(node
 
- 		 (concat
 
- 		  (org-freemind--richcontent
 
- 		   'node (format "\n<p>%s\n</p>" title))
 
- 		  (when section
 
- 		    (org-freemind--build-stylized-node
 
- 		     (org-freemind--get-node-style section info) nil
 
- 		     (org-freemind--richcontent 'node node-contents)))))))
 
- 	    (unless itemized-contents-p
 
- 	      contents))))
 
- ;;; Template
 
- (defun org-freemind-template (contents info)
 
-   "Return complete document string after Freemind Mindmap conversion.
 
- CONTENTS is the transcoded contents string.  RAW-DATA is the
 
- original parsed data.  INFO is a plist holding export options."
 
-   (format
 
-    "<map version=\"0.9.0\">\n%s\n</map>"
 
-    (org-freemind--build-stylized-node
 
-     (org-freemind--get-node-style nil info) nil
 
-     (let ((org-data (plist-get info :parse-tree)))
 
-       (org-freemind--build-node-contents org-data contents info)))))
 
- (defun org-freemind-inner-template (contents info)
 
-   "Return body of document string after Freemind Mindmap conversion.
 
- CONTENTS is the transcoded contents string.  INFO is a plist
 
- holding export options."
 
-   contents)
 
- ;;;; Tags
 
- (defun org-freemind--tags (tags)
 
-   (mapconcat (lambda (tag)
 
- 	       (format "\n<attribute NAME=\"%s\" VALUE=\"%s\"/>" tag ""))
 
- 	     tags "\n"))
 
- ;;; Transcode Functions
 
- ;;;; Entity
 
- (defun org-freemind-entity (entity contents info)
 
-   "Transcode an ENTITY object from Org to Freemind Mindmap.
 
- CONTENTS are the definition itself.  INFO is a plist holding
 
- contextual information."
 
-   (org-element-property :utf-8 entity))
 
- ;;;; Headline
 
- (defun org-freemind-headline (headline contents info)
 
-   "Transcode a HEADLINE element from Org to Freemind Mindmap.
 
- CONTENTS holds the contents of the headline.  INFO is a plist
 
- holding contextual information."
 
-   ;; Empty contents?
 
-   (setq contents (or contents ""))
 
-   (let* ((numberedp (org-export-numbered-headline-p headline info))
 
- 	 (level (org-export-get-relative-level headline info))
 
- 	 (text (org-export-data (org-element-property :title headline) info))
 
- 	 (todo (and (plist-get info :with-todo-keywords)
 
- 		    (let ((todo (org-element-property :todo-keyword headline)))
 
- 		      (and todo (org-export-data todo info)))))
 
- 	 (todo-type (and todo (org-element-property :todo-type headline)))
 
- 	 (tags (and (plist-get info :with-tags)
 
- 		    (org-export-get-tags headline info)))
 
- 	 (priority (and (plist-get info :with-priority)
 
- 			(org-element-property :priority headline)))
 
- 	 (section-number (and (not (org-export-low-level-p headline info))
 
- 			      (org-export-numbered-headline-p headline info)
 
- 			      (mapconcat 'number-to-string
 
- 					 (org-export-get-headline-number
 
- 					  headline info) ".")))
 
- 	 ;; Create the headline text.
 
- 	 (full-text (org-export-data (org-element-property :title headline)
 
- 				     info))
 
- 	 ;; Headline order (i.e, first digit of the section number)
 
- 	 (headline-order (car (org-export-get-headline-number headline info))))
 
-     (cond
 
-      ;; Case 1: This is a footnote section: ignore it.
 
-      ((org-element-property :footnote-section-p headline) nil)
 
-      ;; Case 2. This is a deep sub-tree, export it as a list item.
 
-      ;;         Delegate the actual export to `html' backend.
 
-      ((org-export-low-level-p headline info)
 
-       (org-html-headline headline contents info))
 
-      ;; Case 3. Standard headline.  Export it as a section.
 
-      (t
 
-       (let* ((section-number (mapconcat 'number-to-string
 
- 					(org-export-get-headline-number
 
- 					 headline info) "-"))
 
- 	     (ids (remove 'nil
 
- 			  (list (org-element-property :CUSTOM_ID headline)
 
- 				(concat "sec-" section-number)
 
- 				(org-element-property :ID headline))))
 
- 	     (preferred-id (car ids))
 
- 	     (extra-ids (cdr ids))
 
- 	     (left-p (zerop (% headline-order 2))))
 
- 	(org-freemind--build-stylized-node
 
- 	 (org-freemind--get-node-style headline info)
 
- 	 (format "<node ID=\"%s\" POSITION=\"%s\" FOLDED=\"%s\">\n</node>"
 
- 		 preferred-id
 
- 		 (if left-p "left" "right")
 
- 		 (if (= level 1) "true" "false"))
 
- 	 (concat (org-freemind--build-node-contents headline contents info)
 
- 		 (org-freemind--tags tags))))))))
 
- ;;;; Section
 
- (defun org-freemind-section (section contents info)
 
-   "Transcode a SECTION element from Org to Freemind Mindmap.
 
- CONTENTS holds the contents of the section.  INFO is a plist
 
- holding contextual information."
 
-   (let ((parent (org-export-get-parent-headline section)))
 
-     (when (and parent (org-export-low-level-p parent info))
 
-       contents)))
 
- ;;; Filter Functions
 
- (defun org-freemind-final-function (contents backend info)
 
-   "Return CONTENTS as pretty XML using `indent-region'."
 
-   (if (not org-freemind-pretty-output) contents
 
-     (with-temp-buffer
 
-       (nxml-mode)
 
-       (insert contents)
 
-       (indent-region (point-min) (point-max))
 
-       (buffer-substring-no-properties (point-min) (point-max)))))
 
- (defun org-freemind-options-function (info backend)
 
-   "Install script in export options when appropriate.
 
- EXP-PLIST is a plist containing export options.  BACKEND is the
 
- export back-end currently used."
 
-   ;; Freemind/Freeplane doesn't seem to like named html entities in
 
-   ;; richcontent.  For now, turn off smart quote processing so that
 
-   ;; entities like "’" & friends are avoided in the exported
 
-   ;; output.
 
-   (plist-put info :with-smart-quotes nil))
 
- ;;; End-user functions
 
- ;;;###autoload
 
- (defun org-freemind-export-to-freemind
 
-   (&optional async subtreep visible-only body-only ext-plist)
 
-   "Export current buffer to a Freemind Mindmap file.
 
- If narrowing is active in the current buffer, only export its
 
- narrowed part.
 
- If a region is active, export that region.
 
- A non-nil optional argument ASYNC means the process should happen
 
- asynchronously.  The resulting file should be accessible through
 
- the `org-export-stack' interface.
 
- When optional argument SUBTREEP is non-nil, export the sub-tree
 
- at point, extracting information from the headline properties
 
- first.
 
- When optional argument VISIBLE-ONLY is non-nil, don't export
 
- contents of hidden elements.
 
- When optional argument BODY-ONLY is non-nil, only write code
 
- between \"<body>\" and \"</body>\" tags.
 
- EXT-PLIST, when provided, is a property list with external
 
- parameters overriding Org default settings, but still inferior to
 
- file-local settings.
 
- Return output file's name."
 
-   (interactive)
 
-   (let* ((extension (concat ".mm" ))
 
- 	 (file (org-export-output-file-name extension subtreep))
 
- 	 (org-export-coding-system 'utf-8))
 
-     (org-export-to-file 'freemind file
 
-       async subtreep visible-only body-only ext-plist)))
 
- (provide 'ox-freemind)
 
- ;;; ox-freemind.el ends here
 
 
  |