;;; org-export-freemind - exporting utilities from org-mode to freemind

;; Copyright (C) 2007 Marco Vezzoli

;; Author: marco vezzoli <noise.otn@alice.it>
;; Created:
;; Version: 0.1.0
;; Keywords: org-mode export freemind
;; Commentary:

;; 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 2 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 this program; if not, write to the Free
;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301 USA

;;; Code:
(defgroup org-export-freemind ()
  "This group let you customize your own export into freemind format"
  :group 'org-export)

(defcustom org-freemind-icons-alist
  '(("TODO" . "button_cancel")
    ("SPEC" . "pencil")
    ("WIP"  . "pencil")
    ("TEST" . "xmag")
    ("DONE" . "button_ok"))
  "change the icon according to a regular expression"
  :type '(alist :key-type regexp 
		:value-type string)
  :group 'org-export-freemind)

(defcustom org-freemind-cloud-alist
  '((":PROJECT:" . "ccffcc")
    (":MEETING:" . "ccccff"))
  "create a cloud with the defined color if title match a regexp"
  :type '(alist :key-type regexp :value-type string)
  :group 'org-export-freemind)

(defun org-export-as-freemind (&optional buffer outbuffer)
  "Export the org buffer as FreeMind XML.
"
  (interactive (list (current-buffer) ()))
  ;; A quickie abstraction

  ;; Output everything as FreeMind
  (with-current-buffer (get-buffer buffer)
    (goto-char (point-min))  ;; CD:  beginning-of-buffer is not allowed.
    (let* ((opt-plist (org-combine-plists (org-default-export-plist)
					(org-infile-export-plist)))
	   (title       (or (plist-get opt-plist :title)
			    (file-name-sans-extension
			     (file-name-nondirectory buffer-file-name))))
	   (filename (concat (file-name-as-directory
			      (org-export-directory :xoxo opt-plist))
			     title
			     ".mm"))
	   (out (if (bufferp outbuffer)
		    outbuffer
		    (find-file-noselect filename)))
	   (last-level 0)
	   (hanging-li nil))

      ;; Check the output buffer is empty.
      ;; Kick off the output
      (unless (bufferp outbuffer)
	  (progn
	    (with-current-buffer out (erase-buffer))
	    (org-export-as-xoxo-insert-into out "<map version='0.8.0'>\n")))
      (org-export-as-xoxo-insert-into out 
	"<node TEXT='" title "'"
	(if (bufferp outbuffer)
	    " FOLDED='true'" "")
	">\n")
      (if (bufferp outbuffer)
	  (org-export-as-xoxo-insert-into out "<cloud COLOR='#ccffff'/>\n"))
      (while (re-search-forward "^\\(\\*+\\) \\(.+\\)" (point-max) 't)
        (let* ((hd (match-string-no-properties 1))
               (level (length hd))
               (text (match-string-no-properties 2)))
	  (save-excursion
	    (goto-char (match-end 0))
	    (catch 'loop
	      (while 't
		(forward-line)
		(if (looking-at "^[ \t]\\(.*\\)")
		    ()
		  (throw 'loop "")))))

          ;; Handle level rendering
	  (cond
           ((> level last-level)
	    (let ((rept (- level last-level 1))
		  (value ()))
	      (dotimes (i rept value)
		(org-export-as-xoxo-insert-into out "<node FOLDED='false' TEXT='.'>\n")))
            (org-export-as-xoxo-insert-into out "\n<node FOLDED='true' TEXT='"))

           ((< level last-level)
	    (let ((rept (+ (- last-level level) 1))
		  (value ()))
	      (dotimes (i rept value)
		(org-export-as-xoxo-insert-into out "</node>\n")))
	    (org-export-as-xoxo-insert-into out "<node FOLDED='true' TEXT='"))

           ((equal level last-level)
	    (org-export-as-xoxo-insert-into out "</node>\n<node FOLDED='true' TEXT='")))

          (setq last-level level)

          ;; And output the new node
	  (let* ((heading
		  (concat "<html><h3>" 
			  (replace-regexp-in-string 
			   ":.*:" 
			   (lambda (x) 
			     (concat "<font color='red'>" x "</font>")) 
			   text)
			  "</h3></html>"))
		 (html-quoted-heading (org-html-expand heading))
		 (exp-quote-heading (replace-regexp-in-string "'" "&quot;" html-quoted-heading)))
	    (org-export-as-xoxo-insert-into out exp-quote-heading "'>\n"))

	  (dolist (rule org-freemind-icons-alist)
	    (if (string-match (car rule) text)
		(org-export-as-xoxo-insert-into out "<icon BUILTIN='" (cdr rule) "'/>\n")))
	  (dolist (rule org-freemind-cloud-alist)
	    (when (string-match (car rule) text)
	      (progn
		(org-export-as-xoxo-insert-into out 
		  "<cloud COLOR='#" (cdr rule) "'/>\n")
		(message (cdr rule))
		)))
	  ))

      ;; Finally finish off the map
      (let ((value ()))
	(org-export-as-xoxo-insert-into out "\n")
	(dotimes (i last-level value)
	  (org-export-as-xoxo-insert-into out "</node>\n")))
      (org-export-as-xoxo-insert-into out "</node>\n")

      ;; Finish the buffer off and clean it up.
      (unless (bufferp outbuffer)
	(progn
	  (org-export-as-xoxo-insert-into out "</map>\n")
	  (switch-to-buffer-other-window out)
	  (indent-region (point-min) (point-max) nil)
	  (save-buffer)
	  (goto-char (point-min))
	  )))))

(defun org-export-as-freemind-agenda-files ()
  "Export all agenda files into Freemind format
each files is saved with .mm extension
into the XOXO publishing directory"
  (interactive)
  (dolist (file org-agenda-files)
    (org-check-agenda-file file)
    (set-buffer (org-get-agenda-file-buffer file))
    (org-export-as-freemind (current-buffer))
    ))

(defun org-export-as-freemind-agenda-files-one-file (filename)
  "Export all agenda files into FreeMind format.
All results are grouped in a single .mm file"
  (interactive "FFile to save: ")
  (let* ((title (file-name-sans-extension
		 (file-name-nondirectory filename)))
	 (out (find-file-noselect filename)))
    (with-current-buffer out (erase-buffer))
    (org-export-as-xoxo-insert-into out "<map version='0.8.0'><node TEXT='" title "'>\n")
    (dolist (file org-agenda-files)
      (org-check-agenda-file file)
      (set-buffer (org-get-agenda-file-buffer file))
      (org-export-as-freemind (current-buffer) out)
      )
    (org-export-as-xoxo-insert-into out "</node></map>\n")
    (switch-to-buffer-other-window out)
    (indent-region (point-min) (point-max) nil)
    (save-buffer)
    (goto-char (point-min))
    ))

(define-key org-mode-map "\C-c\C-xf" 'org-export-as-freemind)
;;; org-export-freemind ends here