view lisp/org/org-freemind.el @ 109462:a150e8a14679

Install version 7.01 of Org-mode 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * ob-C.el: New file. * ob-R.el: New file. * ob-asymptote.el: New file. * ob-clojure.el: New file. * ob-comint.el: New file. * ob-css.el: New file. * ob-ditaa.el: New file. * ob-dot.el: New file. * ob-emacs-lisp.el: New file. * ob-eval.el: New file. * ob-exp.el: New file. * ob-gnuplot.el: New file. * ob-haskell.el: New file. * ob-keys.el: New file. * ob-latex.el: New file. * ob-lob.el: New file. * ob-matlab.el: New file. * ob-mscgen.el: New file. * ob-ocaml.el: New file. * ob-octave.el: New file. * ob-perl.el: New file. * ob-python.el: New file. * ob-ref.el: New file. * ob-ruby.el: New file. * ob-sass.el: New file. * ob-screen.el: New file. * ob-sh.el: New file. * ob-sql.el: New file. * ob-sqlite.el: New file. * ob-table.el: New file. * ob-tangle.el: New file. * ob.el: New file. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-mks.el: New file. * org-capture.el: New file. 2010-07-19 Christian Egli <christian.egli@sbszh.ch> * org-taskjuggler.el: New file. 2010-07-19 Matt Lundin <mdl@imapmail.org> * org-agenda.el (org-search-view): Fixed inclusion of agenda-archives in org-agenda-text-search-extra-files. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-list.el (org-list-send-list): Locally bind variable `txt'. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org.el (org-reload): now also reloading babel files 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-set-plist): Make sure txt is a string before calling `string-match'. (org-capture-templates): Fix customization type. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-preprocess): Make a special case for \nbsp. (org-latex-entities): Remove the entry for \nbsp. (org-latex-entities-exceptions): Variable removed. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-refile): Do not try to manipulate bookmark list. * org.el (org-refile): Use the correct bookmark here. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-list.el (org-list-send-list): Parse list from its true beginning. * org.el (org-ctrl-c-ctrl-c): Maybe send the list when at a list item. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-insert-link): Correctly determine if we should use a relative path. 2010-07-19 Nicolas Goaziou <n.goaziou@gmail.com> * org-list.el (org-list-radio-list-templates): Fix templates. 2010-07-19 Nicolas Goaziou <n.goaziou@gmail.com> * org-list.el (org-list-send-list): regexp defining the start of a radio list is now on par with the one used for radio tables. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-entities.el (org-entities-help): Add a headline for the user-defined entities. 2010-07-19 Dirk-Jan C. Binnema <djcb.bulk@gmail.com> (tiny change) * org-agenda.el (org-agenda-action): Document capture key and add it to the prompt. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-latex.el (org-export-latex-listings-langs): added (sqlite "SQL") 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-first-lines): Do not mark meta lines for removal. Do not remove BABEL config lines during export 2010-07-19 David Maus <dmaus@ictsoc.de> * org-capture.el (org-capture): Check if `org-capture-link-is-already-stored' is bound before evaluating. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org.el: added autoload for org-babel-do-load-languages 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-src.el (org-src-lang-modes): added sqlite to sql-mode 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el: Change indentation to match coding style guideline. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (org-feed-unescape, org-feed-parse-atom-feed): Load XML library if necessary. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-beamer.el (org-beamer-amend-header): Standardize the header cookie for the beamer extra stuff. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-beamer.el (org-beamer-amend-header): Put extra header last in header. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-exp-blocks.el (org-export-blocks-format-ditaa) (org-export-blocks-format-dot): Remove text properties of body before calculating cache hash. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-tabular-environment): New option. (org-export-latex-tables): Use `org-export-latex-tabular-environment'. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-compat.el (org-version-check): New function. * org-indent.el (org-indent-mode): Check for exact emacs version. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-templates): Allow the template to come from a file or function call. (org-capture-place-entry): Get the template from file or function. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-agenda.el (org-agenda-bulk-action): Don't create marker for position if target is entire file. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-autoload): Autoload a few more org-table functions. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org.el (org-babel-load-languages): adding ob-mscgen 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-latex.el (org-export-latex-tables): format string now matches options 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org.el (org-babel-load-languages): this variable controls which languages will be loaded by org-babel. It is customizable through the customize interface. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-latex.el (org-export-latex-format-image): updated number of arguments to allow for an optional short-name 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-indent.el (org-indent-mode): Refuse to turn on prior to Emacs 23.2 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-set-target-location): Store exact positions for file+regexp and file+function targets. (org-capture-place-entry, org-capture-place-item) (org-capture-place-table-line, org-capture-place-plain-text): Respect exact positions. (org-capture-finalize): Make sure we are at the beginning of a line when fixing the empty lines after the entry. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-entry-get-with-inheritance): New argument LITERAL-NIL. (org-entry-get): Pass `literal-nil' into `org-entry-get-with-inheritance'. (org-todo): React to nil values of the LOGGING property. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-default-notes-file): Update docstring 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-link-frame-setup): Use `org-gnus-no-new-news' as default. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp.el (org-export-attach-captions-and-attributes): adding a shortname attribute to caption strings under the symbol name org-caption-shortn. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-switchb): Renamed from `org-iswitchb'. Improve docstring. (org-iswitchb): New alias. (org-ido-switchb): Make alias point to `org-switchb'. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-fill-template): Respect time-of-day preference in template prompt. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (org-feed-unescape): Remove superfluous lambda. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-wl.el (org-wl-disable-folder-check): New customization variable. (org-wl-open): Disable folder check depending on `org-wl-disable-folder-check'. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-set-target-location): Fix file+function interpretation. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (org-feed-parse-rss-entry): Unescape rss element content. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (xml-entity-alist): Declare variable `xml-entity-alist' for byte compiler. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (org-feed-unescape): New function. Unescape protected entities. (org-feed-parse-atom-entry): Use function for atom:content type text and html. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-feed.el (org-feed-parse-rss-feed): Ignore case of rss element names. 2010-07-19 Bernt Hansen <bernt@norang.ca> * org.el (org-time-string-to-absolute): Ignore cyclic repeater when displaying items on todays agenda date. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-get-progress): Avoid reusing previous value of EXTRA. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-publish.el (org-publish-initialize-cache): Make timestamp directory, the entire path to it. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-exp.el (org-export-handle-comments): Make sure to check for protection in the comment line, and not in the line after it. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-html.el (org-export-html-preprocess): Call org-format-latex, possibly with a protect-only argument. * org.el (org-format-latex): New argument PROTECT-ONLY. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp.el (org-export-handle-table-metalines): this function removes table specific meta-lines, now that we aren't wiping everything that looks remotely like a comment at the end of the export process we have to be sure to catch all of the specific lines in org-exp.el 2010-07-19 Nicolas Goaziou <n.goaziou@gmail.com> * org-exp.el: (org-export-select-backend-specific-text) Properly get rid of #+Backend and #+ATTR_Backend specifics to backends not matching the one we're exporting to. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * Makefile (lisp/org-install.el): replacing babel files in construction of org-install.el 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-table.el (orgtbl-to-generic): added the :remove-newlines option which will strip newline characters from the text of table cells and replace then with "\n" 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-confirm-shell-link-function): (org-confirm-elisp-link-function): Limit the values that can be set by file variables. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-compute-latex-and-specials-regexp): Deal with string elements by discarding them. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-iswitchb): Make sure to use at least iswitchb. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-capture.el (org-capture-position-for-last-stored): org-capture-bookmark-last-stored-position): New functions. (org-capture-place-table-line): Better error catching. (org-capture-place-item): (org-capture-place-entry): (org-capture-place-plain-text): Call `org-capture-position-for-last-stored'. (org-capture-finalize): Just call `org-capture-bookmark-last-stored-position'. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp.el (org-export-mark-blockquote-verse-center): fixed small bug, now grabbing match data before overwritten by looking-at this fixes a problem with remainders of #+end_quote lines appearing in exported output 2010-07-19 David Maus <dmaus@ictsoc.de> * org.el (org-link-frame-setup): Add customization option for Wanderlust. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-latex.el (org-export-latex-fixed-width): now checking org-example rather than org-protected on verbatim export, because by default all ": " prefixed lines are marked protected 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-latex.el (org-export-latex-fixed-width): check for protection before wrapping ": " lines as verbatim 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp.el (org-export-handle-comments): check for protection before removing comments 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-entities.el (org-entities): Restructure the list. (org-entities-help): Turn the help output into a buffer in Org-mode, so that it becomes easier to find a symbol in the structure. (org-entities-create-table): Deal with new structure. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-agenda.el (org-write-agenda): Use backquotes to expand `flet' at compile time. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-entry-properties): Make sure that standard property names are used even if the user has customized time keywords. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-macs.el (org-not-nil): Return the value if not interpreted as nil. * org.el (org-entry-get): (org-entry-get-with-inheritance): Interpret the value "nil" as nil for properties. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-switch-to-buffer-other-window): Return the buffer. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-macs.el (org-not-nil): New function. * org.el (org-block-todo-from-children-or-siblings-or-parent): Use `org-not-nil' to interpret a property value of nil. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-truely-invisible-p): New function. (org-beginning-of-line): Use `org-truely-invisible-p'. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-get-timestamps): No errors while getting TODO state. (org-agenda-highlight-todo): No error when no keyword has been matched. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-timestamp-change): New optional argument UPDOWN. Use this to identify calls from org-timestamp-up/down, so that we can skip by rounding minutes in this case. (org-timestamp-up): (org-timestamp-down): (org-timestamp-up-day): (org-timestamp-down-day): Call org-timestamp-change with the updown argument. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-action): Make `c' key call org-capture. * org-capture.el: New file. * org-compat.el (org-get-x-clipboard): Function moved here from remember.el. * org-mks.el: New file * org.el (org-set-regexps-and-options): Allow statistic cookies as part of complex headlines. (org-find-olp): New argument THIS-BUFFER. When set, assume that the OLP does not contain a file name. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-mode): Set `comment-start' instead of changing the syntax of the `#' character. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-exp.el (org-export-format-source-code-or-example): Mark examples by a property. o * org-html.el (org-export-html-close-lists-maybe): Check if raw HTML stuff was actually made from an example 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * Makefile (LISPF): let's not compile files that won't often be used. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-latex.el: items are no longer skipped when their first line ends on a protected element. * org-list.el: protected environments looking like lists are not exported anymore. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp-blocks.el (org-export-blocks-preprocess): cleanup trailing newline after block 2010-07-19 Bastien Guerry <bzg@altern.org> * org-exp.el: comment regexp now matches documentation. No more protection check when deleting comments before export. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-exp.el (org-export-preprocess-string): now using `org-export-handle-include-files-recurse' to resolve included files 2010-07-19 Bastien Guerry <bzg@altern.org> * org-agenda.el (org-agenda-get-deadlines): (org-agenda-get-scheduled): * org.el (org-time-string-to-seconds): For deadline and scheduled agenda display ignore the cyclic repeater when calculating how many days late the task is. If you have a weekly task and miss the date the agenda view will show more than a week late now instead of resetting on the cyclic repeating date. This makes it much more obvious when you missed a repeating task after the repeater. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-exp.el (org-export-mark-blockquote-verse-center): Consider environments that end at eob. 2010-07-19 Mikael Fornius <mfo@abc.se> * org.el (org-raise-scripts): Do not fontify sub/superscripts of text with face `org-special-keyword'. Makes property keys as :LAST_REPEAT: display correctly. 2010-07-19 Mikael Fornius <mfo@abc.se> * org.el (org-at-property-p): Use save-match-data macro instead of let. 2010-07-19 Mikael Fornius <mfo@abc.se> * org.el (test): Removed unused test function. 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp-blocks.el (org-export-blocks-preprocess): fixed typo 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp-blocks.el (org-export-blocks-postblock-hook): adding documentation to and turning into a defcustom 2010-07-19 Eric Schulte <schulte.eric@gmail.com> * org-exp.el (org-get-file-contents): by un-setting prefix1 to "" instead of to nil we avoid errors when :prefix1 is defined, but prefix is not. 2010-07-19 Nicolas Goaziou <n.goaziou@gmail.com> * org-latex.el (org-export-latex-preprocess): Environments coming from latex backend specific instructions (#+LaTeX) are already protected and won't be treated as normal environments. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-timer.el (org-timer-set-timer): Fix typo in the docstring. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-timer.el (org-timer-set-timer): Use a prefix argument. See the docstring of the function. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-timer.el (org-timer-set-timer): Fix bug about cancelling timers. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-w3m.el (org-w3m-copy-for-org-mode) (org-w3m-get-next-link-start, org-w3m-get-prev-link-start): Get text property directly, not using macro `w3m-anchor'. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-emph-re): Document the match groups. 2010-07-19 Bernt Hansen <bernt@norang.ca> * org-clock.el (org-clock-in): Set `org-clock-clocking-in' to t before calling `org-clock-out', so that that function can know its call context. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-timer.el (org-timer-default-timer): New variable. (org-timer-set-timer): Use the new variable. Also offer the possibility to replace the current timer by a new one. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-kill-note-or-show-branches): Hide subtree before exposing the headings. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-add-planning-info): Remove the empty line also if there is no whitespace at all in there. * org-table.el (org-table-align): Fix alignment of strings with invisible characters. 2010-07-19 David Maus <dmaus@ictsoc.de> * org.el (org-refile-cache-get): Return empty list of targets when cache was cleared. (org-clone-subtree-with-time-shift): Maybe create ID property in cloned subtrees. (org-clone-delete-id): New customization variable. (org-clone-subtree-with-time-shift): Use customization variable `org-clone-delete-id'. (org-clone-subtree-with-time-shift): Remove empty property drawer in cloned subtrees. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-refile-use-cache): New option. (org-refile-cache, org-refile-markers): New variable. (org-refile-marker, org-refile-cache-clear) (org-refile-cache-check-set, org-refile-cache-put) (org-refile-cache-get): New function. (org-get-refile-targets): Use the refile cache. * org-clock.el (org-clock-sum): Don't include running clock if the time block is wrong. 2010-07-19 John Wiegley <jwiegley@gmail.com> * org-clock.el (org-clock-clock-in, org-clock-in): Added parameter `start-time'. (org-clock-resolve-clock): Added parameter `clock-out-time'. If set, and resolve-to is a past time, then the clock out event occurs at `clock-out-time' rather than at `resolve-to'. In this case, `resolve-to' becomes the clock in time. (org-clock-jump-to-current-clock): Created new global command to reveal the current clock. (org-clock-resolve): Added new commands g/G and j/J, and a help window describing all commands and their meaning. (org-clock-resolve-expert): New customization variable. (org-find-open-clocks): Fixed a bug that caused discovered clocks not to match up with the currently active clock. (org-resolve-clocks): Changed the argument `also-non-dangling-p' to `only-dangling-p', since due to a bug this was the default behavior all along. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-id.el (org-id-uuid): New function. Return string with random (version 4) UUID. (org-id-method): Make 'uuid the new default value. (org-id-new): Use `org-id-uuid' if call to uuidgen program does not return a UUID. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-format-image): Add support for multicolumn figures in LaTeX. 2010-07-19 David Maus <dmaus@ictsoc.de> * org.el (org-clone-subtree-with-time-shift): Remove ID property of original subtree in cloned subtrees. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-exp.el (org-export-format-source-code-or-example): XEmacs compatibility. * org-latex.el (org-export-latex-tables): Accept comma in align string. * org-docbook.el (org-export-docbook-xslt-stylesheet): New option. (org-export-docbook-xslt-proc-command): Fix docstring. (org-export-docbook-xsl-fo-proc-command): Fix docstring. (org-export-as-docbook-pdf): Improve formatting of the xslt command. * org-exp.el (org-infile-export-plist): Check for XSLT setting. * org.el (org-file-contents): Improve error message. (org-set-regexps-and-options): Remove spaces at both ends. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-docbook.el (org-export-as-docbook-pdf): Improve formatting of the xslt command. 2010-07-19 Sebastian Rose <sebastian_rose@gmx.de> * org-publish.el (org-publish-cache): Use one big hashmap for each project defined in `org-publish-project-alist'. (initialize-files-alist): Function removed. (org-publish-validate-link): Function removed. (org-publish-get-base-files): Add variable `sitemap-requested' to avoid sorting where possible. (org-publish-get-files): Function removed. (org-publish-get-project-from-filename): Make independent of file list. (org-publish-file): New argument NO-CACHE. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-beginning-of-defun, org-end-of-defun): New functions. (org-mode): Install the `org-beginning-of-defun' and `org-end-of-defun' functions. (org-pretty-entities): New option. (org-toggle-pretty-entities): New command. (org-fontify-entities): New function. (org-startup-options): New keywords for pretty entities. (org-set-font-lock-defaults): Call the pretty entities function. * org-latex.el (org-export-latex-keywords-maybe): Protect the TODO markup. 2010-07-19 Mikael Fornius <mfo@abc.se> * org-habit.el (org-habit-build-graph): Help-echo date when mouse is over stars. 2010-07-19 Jan Bker <jan.boecker@jboecker.de> * org.el (org-file-apps): Improve docstring to reflect grouping matches 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-set-startup-visibility): Fix empty line display. * org-latex.el (org-export-latex-links): Use the formatting function of the link type, if it is available. * org-table.el (org-table-get-remote-range): Return to original buffer when retrieving remote reference. * org.el (org-display-inline-images): Do the entire buffer, not just the narrowed region. Clear the cache. (org-display-inline-images): Match mode file paths. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-wl.el (org-wl-store-link-folder): Don't throw error when called on WL folder group. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-replace-escapes): Make sure the cdr is not nil. (org-read-date): Make `M-v' and `C-v' scroll the popup calendar. (org-mode): Revert comment syntax changes. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-sparse-tree): Make `C-c / t' search for all TODO keywords, and `C-c / T' for a specific one. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-mode): Fix comment syntax settings. * org-src.el (org-edit-src-allow-write-back-p): Define variable. * org.el (org-inline-image-overlays): New variable. (org-toggle-inline-images, org-display-inline-images) (org-remove-inline-images): New commands. (org-mode-map): Define a key for `org-toggle-inline-images'. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-wl.el (org-wl-message-field): New function. Return content of header field in message entity. (org-wl-store-link): Call `org-wl-store-link-folder' or `org-wl-store-link-message' depending on major-mode. (org-wl-store-link-folder): New function. Store link to Wanderlust folder. (org-wl-store-link-message): New function. Store link to Wanderlust message. (org-wl-store-link-message): Store link to message while visiting message. (org-wl-open): Don't try to jump to message when opening a folder link. 2010-07-19 David Maus <dmaus@ictsoc.de> * org.el (org-replace-escapes): Avoid infinite loop when replace string contains escape sequence it replaces. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-crypt.el (org-crypt-key-for-heading): Use symmetric encryption when now key is set. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-table.el (org-table-recalculate-buffer-tables) (org-table-iterate-buffer-tables): New commands. * org.el (org-check-for-hidden): When there is a region, skip the check. 2010-07-19 Dan Davison <davison@stats.ox.ac.uk> * org-src.el (org-edit-src-code): allow-write-back-p had erroneously been omitted from let binding 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-sorting-choice): New sorting type alpha. (org-cmp-alpha): New defsubst. (org-em): New defsubst. (org-entries-lessp): Only compute needed comparisons. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-html.el (org-format-org-table-html): Test all columns for number content. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-latex.el (org-export-latex-treat-sub-super-char): Make sure parenthesis matching is consistent. * org-table.el (org-table-colgroup-line-p) (org-table-cookie-line-p): New functions. * org-exp.el (org-table-clean-before-export): Better tests for colgroup and cookie lines. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-agenda.el (org-agenda-goto): Push a mark before changing the position. * org-footnote.el (org-footnote): New group. (org-footnote-section) (org-footnote-tag-for-non-org-mode-files): Fix typos. * org-list.el (org-end-of-item-text-before-children): Also do the right thing at the end of a file. * org.el (org-set-packages-alist, org-get-packages-alist): New function. (org-export-latex-default-packages-alist) (org-export-latex-packages-alist): Add extra flag to each package, indicating if it should be used for snippets. (org-create-formula-image): Add the snippet argument. (org-splice-latex-header): New argument SNIPPET-P, pass it through to `org-latex-packages-to-string'. (org-latex-packages-to-string): New argument SNIPPET-P. * org-latex.el (org-export-latex-make-header): Add the snippet argument. * org-docbook.el (org-export-as-docbook): Implement ordered lists starting at some offset. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-link-types, org-open-at-point): Add doi links. * org-ascii.el (org-export-ascii-preprocess): Remove list startcounter cookies. * org-list.el (org-renumber-ordered-list): Respect counter start values. * org-latex.el (org-export-latex-lists): Accept ordered list item offset cookie. * org-html.el (org-export-as-html): Accept ordered list item offset cookie. * org-indent.el (org-indent-mode): Turn off `indent-tabs-mode' which messes up alignment of tags. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-clock.el (org-clock-cancel, org-clock-out): Make sure the modeline display is removed. * org-exp.el (org-export-format-drawer-function): Fix docstring. * org-agenda.el (org-agenda-refile): New optional argument NO-UPDATE. (org-agenda-refile): Call `org-agenda-redo' unless NO-UPDATE is set. (org-agenda-bulk-action): Call the refile command with updates suppressed - but arrange for `org-agenda-redo' to be called at the end. * org.el (org-mode): Make table mapping quiet. (org-table-map-tables): New optional argument QUIETLY. * org-ascii.el (org-export-ascii-preprocess): Make table mapping quiet. * org-html.el (org-export-as-html, org-html-level-start): Change XHTML IDs to not use dots. * org-exp.el (org-export-define-heading-targets): Change XHTML IDs to not use dots. * org-docbook.el (org-export-docbook-level-start): Change XHTML IDs to not use dots. * org-latex.el (org-export-as-latex): Make sure that the result buffer is in latex-mode. * org.el (org-shiftup-final-hook, org-shiftdown-final-hook) (org-shiftleft-final-hook, org-shiftright-final-hook): New hooks. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-table.el (org-table-justify-field-maybe): Make sure that inserting a value does not turn a line into a hline. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-clock.el (org-clock-sum): New argument HEADLINE-FILTER. (org-clock-sum): Add property to selected headlines. (org-dblock-write:clocktable): Make tags matcher. * org.el (org-set-autofill-regexps): XEmacs compatibility. * org-latex.el (org-export-latex-set-initial-vars): Allow "-" in latex class definitions * org.el (org-shiftup-hook, org-shiftdown-hook) (org-shiftleft-hook, org-shiftright-hook): New hooks. * org-entities.el (org-entities): Use \land and \lor for logical operators. * org.el (org-shiftmetaleft, org-shiftmetaright): Call the subtree indentation commands. (org-hidden-tree-error): New defsubst. (org-metaleft, org-metaright): Check for hidden stuff and throw an error. (org-check-for-hidden): New function. * org-list.el (org-item-re): New function. (org-at-item-p): Use `org-item-re'. (org-end-of-item-text-before-children): New function. (org-outdent-item, org-indent-item): Arrange for leaving the subtree alone. (org-outdent-item-tree, org-indent-item-tree): New argument NO-SUBTREE. (org-indent-item-tree): Use `org-end-of-item-text-before-children' to find the end for processing while ignoring the subtree. * org-publish.el (org-publish-sitemap-sort-alphabetically) (org-publish-sitemap-sort-folders) (org-publish-sitemap-sort-ignore-case): New options. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-publish.el (org-publish-compare-directory-files): Fix sorting. * org-compat.el (org-get-x-clipboard-compat): Use (featurep 'xemacs). * org-publish.el (org-publish-project-alist): Update docstring. (org-publish-file-title-cache): New variable. (org-publish-initialize-files-alist): Initialize `org-publish-initialize-files-alist' to nil. (org-publish-sort-directory-files): New function. (org-publish-projects): Access the new properties. (org-publish-find-title): Use the file title cache. (org-publish-find-title): Build the file title cache. (org-publish-get-base-files-1): Sort files. (org-publish-aux-preprocess): Do not throw an error when before the first headline. Allow an empty target, meaning to link just to the file. (org-publish-index-generate-theindex.inc): Check if there is actually a target and only then add it to the link. (org-publish-projects): Fix a remaining issue with the last commit * org-html.el (org-export-as-html): Treat verse as open/close paragraph. (org-export-html-close-lists-maybe): Allow to splice raw HTML into and out of lists. 2010-07-19 Dan Davison <davison@stats.ox.ac.uk> * org-src.el (org-edit-src-code): Allow the org-src edit buffer to be used in a read-only mode. (org-edit-src-code): Different message in read-only mode 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-src.el (org-edit-src-find-region-and-lang): Test for table.el as late as possible. * org-colview-xemacs.el: Make sure this file is never loaded into Emacs. Remove all tests for XEmacs. * org-colview.el: Make sure this file is never loaded into XEmacs. * org-agenda.el (org-highlight, org-unhighlight): Use direct overlay calls. * org.el (org-key): Apply the translations defined in `org-xemacs-key-equivalents'. * org-mouse.el (org-mode-hook): Use `org-defkey'. * org-compat.el (org-xemacs-key-equivalents): New constant. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-inlinetask.el (org-inlinetask-defaut-state): New option. (org-inlinetask-insert-task): Use `org-inlinetask-defaut-state'. Obey `org-odd-levels-only'. * org-compat.el (org-find-overlays): Use overlays-in/at. * org.el (org-remove-empty-overlays-at) (org-outline-overlay-data, org-hide-block-toggle) (org-format-latex, org-context): Use overlays-in/at. * org-src.el (org-edit-src-exit): Use overlays-in/at. * org-agenda.el (org-agenda-mark-clocking-task) (org-agenda-fontify-priorities, org-agenda-dim-blocked-tasks) (org-agenda-entry-text-hide) (org-agenda-fix-tags-filter-overlays-at) (org-agenda-bulk-remove-overlays): Use overlays-in/at. * org-compat.el (org-overlays-at): Function removed. (org-overlays-in): Function removed. 2010-07-19 Bastien Guerry <bzg@altern.org> * org-clock.el (org-clock-set-current): Just return the headline itself, strip the TODO keyword, the priority cookie and the tags. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-compat.el (org-xemacs-without-invisibility): New macro. (org-xemacs-without-invisibility): New macro. (org-indent-to-column, org-indent-line-to, org-move-to-column): Redefine using the macro `org-xemacs-without-invisibility'. * org.el (org-mode, org-org-menu): Use `add-to-invisibility-spec'. * org-table.el (orgtbl-mode): Use `add-to-invisibility-spec'. * org-compat.el (org-make-overlay, org-delete-overlay) (org-overlay-start, org-overlay-end, org-overlay-put) (org-overlay-get, org-overlay-move, org-overlay-buffer): Functions removed. (org-add-to-invisibility-spec): Function removed. * org-html.el (org-export-as-html-and-open): Add argument to kill-buffer. * org-habit.el (require): `calendar' is now required already by org.el on top level. * org-clock.el (require): `calendar' is now required already by org.el on top level. * org-agenda.el (require, org-timeline, org-agenda-list) (org-todo-list, org-agenda-to-appt): `calendar' is now required already by org.el on top level. * org.el (org-export-latex-fix-inputenc): Declare function. * org-agenda.el (org-agenda-goto-calendar): Do not bind obsolete variables. * org.el (calendar): Require calendar now on top level in org.el and define aliases to new variables when needed. (org-read-date, org-goto-calendar): Do not bind obsolete variables. * org-clock.el (org-clock-out, org-clock-cancel): Get rid of compilation warning, add comment that this cannot be done with `with-current-buffer'. * org-wl.el (org-wl-open): Use `with-current-buffer'. * org.el (overlay, org-remove-empty-overlays-at) (org-outline-overlay-data, org-set-outline-overlay-data) (org-show-block-all, org-hide-block-toggle) (org-highlight-new-match, org-remove-occur-highlights) (org-tags-overlay, org-fast-tag-selection, org-date-ovl) (org-read-date, org-read-date-display, org-eval-in-calendar) (org-format-latex, org-context) (org-speedbar-restriction-lock-overlay) (org-speedbar-set-agenda-restriction): Use the normal overlay API. * org-table.el (org-table-add-rectangle-overlay) (org-table-remove-rectangle-highlight) (org-table-overlay-coordinates) (org-table-toggle-coordinate-overlays): Use the normal overlay API. * org-src.el (org-edit-src-code, org-edit-fixed-width-region) (org-edit-src-exit, org-src-mode-configure-edit-buffer): Use the normal overlay API. * org-colview.el (org-columns-new-overlay) (org-columns-display-here, org-columns-remove-overlays) (org-columns-edit-value, org-columns-next-allowed-value) (org-columns-update): Use the normal overlay API. * org-clock.el (org-clock-out, org-clock-cancel) (org-clock-put-overlay, org-clock-remove-overlays): Use the normal overlay API. * org-agenda.el (org-agenda-mark-filtered-text) (org-agenda-mark-clocking-task, org-agenda-fontify-priorities) (org-agenda-dim-blocked-tasks, org-agenda-entry-text-show-here) (org-agenda-entry-text-hide) (org-agenda-restriction-lock-overlay) (org-agenda-set-restriction-lock) (org-agenda-filter-by-tag-hide-line) (org-agenda-fix-tags-filter-overlays-at) (org-agenda-filter-by-tag-show-all, org-hl) (org-agenda-goto-calendar, org-agenda-bulk-mark) (org-agenda-bulk-remove-overlays): Use the normal overlay API. * org-freemind.el (org-freemind-from-org-mode-node) (org-freemind-from-org-mode, ) (org-freemind-from-org-sparse-tree, org-freemind-to-org-mode): Use interactive-p instead of called-interactively, because this is backward compatible with older Emacsen I still support.. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-exp.el (org-export-define-heading-targets): Fix bug in regexp finding ID and CUSTOM_ID properties. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-footnote.el (org-footnote-goto-previous-reference): Renamed from `org-footnote-goto-next-reference'. * org.el (org-auto-repeat-maybe): Only record LAST_REPEAT if org-log-repeat is non-nil, or if there is clocking data in the entry. * org-crypt.el (org-encrypt-entry): Improve mapping behavior. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-align-all-tags): New command. 2010-07-19 David Maus <dmaus@ictsoc.de> * org-wl.el (org-wl-link-remove-filter): New customizable variable. If non-nil, filter conditions are stripped when storing link to message in filter folder. (org-wl-shimbun-prefer-web-links): New customizable variable. If non-nil, links to shimbun messages are created as web links to message source. (org-wl-nntp-prefer-web-links): New customizable variable. If non-nil, links to nntp message are created as web links to gmane or googlegroups. (org-wl-namazu-default-index): New customizable variable. Directory of namazu search index that should be used as default when opening a link in a search folder. (org-wl-folder-types): New constant. Wanderlust folder type indicators. (org-wl-folder-type): New function. Return type of Wanderlust folder. (org-wl-store-link): Create web links for shimbun or nntp messages and strip filter conditions depending on customizable variables. (org-wl-open): Open namazu search folder for message when called with prefix. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-remove-if, org-remove-if-not): New functions. (org-open-file): Use internal remove-if functions. 2010-07-19 Jan Bcker <jan.boecker@jboecker.de> * org.el (org-file-apps-entry-match-against-dlink-p): new function. (org-file-apps-ex): remove variable. (org-open-file): Integrate org-file-apps-ex functionality back into org-file-apps, and decide whether to match a regexp against the link or the filename using org-file-apps-entry-uses-grouping-p. 2010-07-19 Jan Bcker <jan.boecker@jboecker.de> * org.el (org-file-apps-ex): new variable. (org-open-file): Before considering org-file-apps, first match the regexps from org-file-apps-ex against the whole link. See docstring of org-file-apps-ex. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.el (org-export-latex-default-packages-alist): Remove microtype package. (org-todo-repeat-to-state): New variable. (org-auto-repeat-maybe): Allow user-selected target states. (org-default-properties): Add the new property REPEAT_TO_STATE. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org-mobile.el (org-mobile-check-setup): Make sure that there is a binary to compute checksums. 2010-07-19 Carsten Dominik <carsten.dominik@gmail.com> * org.texi: Add macros to get plain quotes in PDF output. List additional contributors. (Capture): New section, replaces the section about remember. (Working With Source Code): New chapter, focused on documenting Org Babel. (Code evaluation security): New section. (MobileOrg): Document DropBox support. (TaskJuggler export): Document taskjuggler and Gantt chart support. (Special symbols): Show how to display UTF8 characters for entities. (Global TODO list): Clarify the use of the "M" key and the differences to the "m" key. (RSS Feeds): Mention Atom feeds as well. (Setting tags): Remove paragraph about `org-complete-tags-always-offer-all-agenda-tags'.
author Carsten Dominik <carsten.dominik@gmail.com>
date Mon, 19 Jul 2010 11:47:27 +0200
parents 594e81986a75
children 5cb272c831e8
line wrap: on
line source

;;; org-freemind.el --- Export Org files to freemind

;; Copyright (C) 2009, 2010 Free Software Foundation, Inc.

;; Author: Lennart Borgman (lennart O borgman A gmail O com)
;; Keywords: outlines, hypermedia, calendar, wp
;; Homepage: http://orgmode.org
;; Version: 6.35i
;;
;; This file is part of GNU Emacs.
;;
;; GNU Emacs 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.

;; GNU Emacs 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/>.

;; --------------------------------------------------------------------
;; Features that might be required by this library:
;;
;; `backquote', `bytecomp', `cl', `easymenu', `font-lock',
;; `noutline', `org', `org-compat', `org-faces', `org-footnote',
;; `org-list', `org-macs', `org-src', `outline', `syntax',
;; `time-date', `xml'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;; This file tries to implement some functions useful for
;; transformation between org-mode and FreeMind files.
;;
;; Here are the commands you can use:
;;
;;    M-x `org-freemind-from-org-mode'
;;    M-x `org-freemind-from-org-mode-node'
;;    M-x `org-freemind-from-org-sparse-tree'
;;
;;    M-x `org-freemind-to-org-mode'
;;
;;    M-x `org-freemind-show'
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Change log:
;;
;; 2009-02-15: Added check for next level=current+1
;; 2009-02-21: Fixed bug in `org-freemind-to-org-mode'.
;; 2009-10-25: Added support for `org-odd-levels-only'.
;;             Added y/n question before showing in FreeMind.
;; 2009-11-04: Added support for #+BEGIN_HTML.
;;
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 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, 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; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Code:

(require 'xml)
(require 'org)
(require 'rx)
(require 'org-exp)
(eval-when-compile (require 'cl))

;; Fix-me: I am not sure these are useful:
;;
;; (defcustom org-freemind-main-fgcolor "black"
;;   "Color of main node's text."
;;   :type 'color
;;   :group 'freemind)

;; (defcustom org-freemind-main-color "black"
;;   "Background color of main node."
;;   :type 'color
;;   :group 'freemind)

;; (defcustom org-freemind-child-fgcolor "black"
;;   "Color of child nodes' text."
;;   :type 'color
;;   :group 'freemind)

;; (defcustom org-freemind-child-color "black"
;;   "Background color of child nodes."
;;   :type 'color
;;   :group 'freemind)

(defvar org-freemind-node-style nil "Internal use.")

(defcustom org-freemind-node-styles nil
  "Styles to apply to node.
NOT READY YET."
  :type '(repeat
          (list :tag "Node styles for file"
                (regexp :tag "File name")
                (repeat
                 (list :tag "Node"
                       (regexp :tag "Node name regexp")
                       (set :tag "Node properties"
                            (list :format "%v" (const :format "" node-style)
                                  (choice :tag "Style"
                                          :value bubble
                                          (const bubble)
                                          (const fork)))
                            (list :format "%v" (const :format "" color)
                                  (color :tag "Color" :value "red"))
                            (list :format "%v" (const :format "" background-color)
                                  (color :tag "Background color" :value "yellow"))
                            (list :format "%v" (const :format "" edge-color)
                                  (color :tag "Edge color" :value "green"))
                            (list :format "%v" (const :format "" edge-style)
                                  (choice :tag "Edge style" :value bezier
                                          (const :tag "Linear" linear)
                                          (const :tag "Bezier" bezier)
                                          (const :tag "Sharp Linear" sharp-linear)
                                          (const :tag "Sharp Bezier" sharp-bezier)))
                            (list :format "%v" (const :format "" edge-width)
                                  (choice :tag "Edge width" :value thin
                                          (const :tag "Parent" parent)
                                          (const :tag "Thin" thin)
                                          (const 1)
                                          (const 2)
                                          (const 4)
                                          (const 8)))
                            (list :format "%v" (const :format "" italic)
                                  (const :tag "Italic font" t))
                            (list :format "%v" (const :format "" bold)
                                  (const :tag "Bold font" t))
                            (list :format "%v" (const :format "" font-name)
                                  (string :tag "Font name" :value "SansSerif"))
                            (list :format "%v" (const :format "" font-size)
                                  (integer :tag "Font size" :value 12)))))))
  :group 'freemind)

;;;###autoload
(defun org-export-as-freemind (arg &optional hidden ext-plist
				   to-buffer body-only pub-dir)
  (interactive "P")
  (let* ((opt-plist (org-combine-plists (org-default-export-plist)
					ext-plist
					(org-infile-export-plist)))
	 (region-p (org-region-active-p))
	 (rbeg (and region-p (region-beginning)))
	 (rend (and region-p (region-end)))
	 (subtree-p
	  (if (plist-get opt-plist :ignore-subtree-p)
	      nil
	    (when region-p
	      (save-excursion
		(goto-char rbeg)
		(and (org-at-heading-p)
		     (>= (org-end-of-subtree t t) rend))))))
	 (opt-plist (setq org-export-opt-plist
			  (if subtree-p
			      (org-export-add-subtree-options opt-plist rbeg)
			    opt-plist)))
	 (bfname (buffer-file-name (or (buffer-base-buffer) (current-buffer))))
	 (filename (concat (file-name-as-directory
			    (or pub-dir
				(org-export-directory :ascii opt-plist)))
			   (file-name-sans-extension
			    (or (and subtree-p
				     (org-entry-get (region-beginning)
						    "EXPORT_FILE_NAME" t))
				(file-name-nondirectory bfname)))
			   ".mm")))
    (when (file-exists-p filename)
      (delete-file filename))
    (cond
     (subtree-p
      (org-freemind-from-org-mode-node (line-number-at-pos rbeg)
				       filename))
     (t (org-freemind-from-org-mode bfname filename)))))

;;;###autoload
(defun org-freemind-show (mm-file)
  "Show file MM-FILE in Freemind."
  (interactive
   (list
    (save-match-data
      (let ((name (read-file-name "FreeMind file: "
                                  nil nil nil
                                  (if (buffer-file-name)
                                      (file-name-nondirectory (buffer-file-name))
                                    "")
                                  ;; Fix-me: Is this an Emacs bug?
                                  ;; This predicate function is never
                                  ;; called.
                                  (lambda (fn)
                                    (string-match "^mm$" (file-name-extension fn))))))
        (setq name (expand-file-name name))
        name))))
  (org-open-file mm-file))

(defconst org-freemind-org-nfix "--org-mode: ")

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Format converters

(defun org-freemind-escape-str-from-org (org-str)
  "Do some html-escaping of ORG-STR and return the result.
The characters \"&<> will be escaped."
  (let ((chars (append org-str nil))
        (fm-str ""))
    (dolist (cc chars)
      (setq fm-str
            (concat fm-str
                    (if (< cc 256)
                        (cond
                         ((= cc ?\") "&quot;")
                         ((= cc ?\&) "&amp;")
                         ((= cc ?\<) "&lt;")
                         ((= cc ?\>) "&gt;")
                         (t (char-to-string cc)))
                      ;; Formatting as &#number; is maybe needed
                      ;; according to a bug report from kazuo
                      ;; fujimoto, but I have now instead added a xml
                      ;; processing instruction saying that the mm
                      ;; file is utf-8:
                      ;;
                      ;; (format "&#x%x;" (- cc ;; ?\x800))
		      (format "&#x%x;" (encode-char cc 'ucs))
                      ))))
    fm-str))

;;(org-freemind-unescape-str-to-org "&#x6d;A&#x224C;B&lt;C&#x3C;&#x3D;")
;;(org-freemind-unescape-str-to-org "&#x3C;&lt;")
(defun org-freemind-unescape-str-to-org (fm-str)
 "Do some html-unescaping of FM-STR and return the result.
This is the opposite of `org-freemind-escape-str-from-org' but it
will also unescape &#nn;."
 (let ((org-str fm-str))
   (setq org-str (replace-regexp-in-string "&quot;" "\"" org-str))
   (setq org-str (replace-regexp-in-string "&amp;" "&" org-str))
   (setq org-str (replace-regexp-in-string "&lt;" "<" org-str))
   (setq org-str (replace-regexp-in-string "&gt;" ">" org-str))
   (setq org-str (replace-regexp-in-string
                  "&#x\\([a-f0-9]\\{2,4\\}\\);"
                  (lambda (m)
                    (char-to-string
                     (+ (string-to-number (match-string 1 m) 16)
                        0 ;?\x800 ;; What is this for? Encoding?
                        )))
                  org-str))))

;; (org-freemind-test-escape)
(defun org-freemind-test-escape ()
  (let* ((str1 "a quote: \", an amp: &, lt: <; over 256: ")
         (str2 (org-freemind-escape-str-from-org str1))
         (str3 (org-freemind-unescape-str-to-org str2))
        )
    (unless (string= str1 str3)
      (error "str3=%s" str3))
    ))

(defun org-freemind-convert-links-from-org (org-str)
  "Convert org links in ORG-STR to freemind links and return the result."
  (let ((fm-str (replace-regexp-in-string
                 (rx (not (any "[\""))
                     (submatch
                      "http"
                      (opt ?\s)
                      "://"
                      (1+
                       (any "-%.?@a-zA-Z0-9()_/:~=&#"))))
                 "[[\\1][\\1]]"
                 org-str)))
    (replace-regexp-in-string (rx "[["
                                  (submatch (*? nonl))
                                  "]["
                                  (submatch (*? nonl))
                                  "]]")
                              "<a href=\"\\1\">\\2</a>"
                              fm-str)))

;;(org-freemind-convert-links-to-org "<a href=\"http://www.somewhere/\">link-text</a>")
(defun org-freemind-convert-links-to-org (fm-str)
  "Convert freemind links in FM-STR to org links and return the result."
  (let ((org-str (replace-regexp-in-string
                  (rx "<a"
                      space
                      (0+
                       (0+ (not (any ">")))
                       space)
                      "href=\""
                      (submatch (0+ (not (any "\""))))
                      "\""
                      (0+ (not (any ">")))
                       ">"
                       (submatch (0+ (not (any "<"))))
                       "</a>")
                  "[[\\1][\\2]]"
                  fm-str)))
    org-str))

;; Fix-me:
;;(defun org-freemind-convert-drawers-from-org (text)
;;  )

;; (org-freemind-test-links)
;; (defun org-freemind-test-links ()
;;   (let* ((str1 "[[http://www.somewhere/][link-text]")
;;          (str2 (org-freemind-convert-links-from-org str1))
;;          (str3 (org-freemind-convert-links-to-org str2))
;;         )
;;     (unless (string= str1 str3)
;;       (error "str3=%s" str3))
;;     ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Org => FreeMind

(defun org-freemind-convert-text-p (text)
  "Convert TEXT to html with <p> paragraphs."
  (setq text (org-freemind-escape-str-from-org text))
  (setq text (replace-regexp-in-string (rx "\n" (0+ blank) "\n") "</p><p>\n" text))
  ;;(setq text (replace-regexp-in-string (rx bol (1+ blank) eol) "" text))
  ;;(setq text (replace-regexp-in-string (rx bol (1+ blank)) "<br />" text))
  (setq text (replace-regexp-in-string "\n" "<br />" text))
  (concat "<p>"
          (org-freemind-convert-links-from-org text)
          "</p>\n"))

(defun org-freemind-org-text-to-freemind-subnode/note (node-name start end drawers-regexp)
  "Convert text part of org node to freemind subnode or note.
Convert the text part of the org node named NODE-NAME. The text
is in the current buffer between START and END. Drawers matching
DRAWERS-REGEXP are converted to freemind notes."
  ;; fix-me: doc
  (let ((text (buffer-substring-no-properties start end))
        (node-res "")
        (note-res ""))
    (save-match-data
      ;;(setq text (org-freemind-escape-str-from-org text))
      ;; First see if there is something that should be moved to the
      ;; note part:
      (let (drawers)
        (while (string-match drawers-regexp text)
          (setq drawers (cons (match-string 0 text) drawers))
          (setq text
                (concat (substring text 0 (match-beginning 0))
                        (substring text (match-end 0))))
          )
        (when drawers
          (dolist (drawer drawers)
            (let ((lines (split-string drawer "\n")))
              (dolist (line lines)
                (setq note-res (concat
                                note-res
                                org-freemind-org-nfix line "<br />\n")))
              ))))

      (when (> (length note-res) 0)
        (setq note-res (concat
                        "<richcontent TYPE=\"NOTE\"><html>\n"
                        "<head>\n"
                        "</head>\n"
                        "<body>\n"
                        note-res
                        "</body>\n"
                        "</html>\n"
                        "</richcontent>\n"))
        )

      ;; There is always an LF char:
      (when (> (length text) 1)
        (setq node-res (concat
                        "<node style=\"bubble\" background_color=\"#eeee00\">\n"
                        "<richcontent TYPE=\"NODE\"><html>\n"
                        "<head>\n"
                        "<style type=\"text/css\">\n"
                        "<!--\n"
                        "p { margin-top: 0 }\n"
                        "-->\n"
                        "</style>\n"
                        "</head>\n"
                        "<body>\n"))
        (let ((begin-html-mark (regexp-quote "#+BEGIN_HTML"))
              (end-html-mark   (regexp-quote "#+END_HTML"))
              head
              end-pos
              end-pos-match
              )
          ;; Take care of #+BEGIN_HTML - #+END_HTML
          (while (string-match begin-html-mark text)
            (setq head (substring text 0 (match-beginning 0)))
            (setq end-pos-match (match-end 0))
            (setq node-res (concat node-res
                                   (org-freemind-convert-text-p head)))
            (setq text (substring text end-pos-match))
            (setq end-pos (string-match end-html-mark text))
            (if end-pos
                (setq end-pos-match (match-end 0))
              (message "org-freemind: Missing #+END_HTML")
              (setq end-pos (length text))
              (setq end-pos-match end-pos))
            (setq node-res (concat node-res
                                   (substring text 0 end-pos)))
            (setq text (substring text end-pos-match)))
          (setq node-res (concat node-res
                                 (org-freemind-convert-text-p text))))
        (setq node-res (concat
                        node-res
                        "</body>\n"
                        "</html>\n"
                        "</richcontent>\n"
                        ;; Put a note that this is for the parent node
                        "<richcontent TYPE=\"NOTE\"><html>"
                        "<head>"
                        "</head>"
                        "<body>"
                        "<p>"
                        "-- This is more about \"" node-name "\" --"
                        "</p>"
                        "</body>"
                        "</html>"
                        "</richcontent>\n"
                        "</node>\n" ;; ok
                        )))
      (list node-res note-res))))

(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)
  (let* (this-icons
         this-bg-color
         this-m2-escaped
         this-rich-node
         this-rich-note
         )
    (when (string-match "TODO" this-m2)
      (setq this-m2 (replace-match "" nil nil this-m2))
      (add-to-list 'this-icons "button_cancel")
      (setq this-bg-color "#ffff88")
      (when (string-match "\\[#\\(.\\)\\]" this-m2)
        (let ((prior (string-to-char (match-string 1 this-m2))))
          (setq this-m2 (replace-match "" nil nil this-m2))
          (cond
           ((= prior ?A)
            (add-to-list 'this-icons "full-1")
            (setq this-bg-color "#ff0000"))
           ((= prior ?B)
            (add-to-list 'this-icons "full-2")
            (setq this-bg-color "#ffaa00"))
           ((= prior ?C)
            (add-to-list 'this-icons "full-3")
            (setq this-bg-color "#ffdd00"))
           ((= prior ?D)
            (add-to-list 'this-icons "full-4")
            (setq this-bg-color "#ffff00"))
           ((= prior ?E)
            (add-to-list 'this-icons "full-5"))
           ((= prior ?F)
            (add-to-list 'this-icons "full-6"))
           ((= prior ?G)
            (add-to-list 'this-icons "full-7"))
           ))))
    (setq this-m2 (org-trim this-m2))
    (setq this-m2-escaped (org-freemind-escape-str-from-org this-m2))
    (let ((node-notes (org-freemind-org-text-to-freemind-subnode/note
                       this-m2-escaped
                       this-node-end
                       (1- next-node-start)
                       drawers-regexp)))
      (setq this-rich-node (nth 0 node-notes))
      (setq this-rich-note (nth 1 node-notes)))
    (with-current-buffer mm-buffer
      (insert "<node text=\"" this-m2-escaped "\"")
      (org-freemind-get-node-style this-m2)
      (when (> next-level current-level)
        (unless (or this-children-visible
                    next-has-some-visible-child)
          (insert " folded=\"true\"")))
      (when (and (= current-level (1+ base-level))
                 (> num-left-nodes 0))
        (setq num-left-nodes (1- num-left-nodes))
        (insert " position=\"left\""))
      (when this-bg-color
        (insert " background_color=\"" this-bg-color "\""))
      (insert ">\n")
      (when this-icons
        (dolist (icon this-icons)
          (insert "<icon builtin=\"" icon "\"/>\n")))
      )
    (with-current-buffer mm-buffer
      (when this-rich-note (insert this-rich-note))
      (when this-rich-node (insert this-rich-node))))
  num-left-nodes)

(defun org-freemind-check-overwrite (file interactively)
  "Check if file FILE already exists.
If FILE does not exists return t.

If INTERACTIVELY is non-nil ask if the file should be replaced
and return t/nil if it should/should not be replaced.

Otherwise give an error say the file exists."
  (if (file-exists-p file)
      (if interactively
          (y-or-n-p (format "File %s exists, replace it? " file))
        (error "File %s already exists" file))
    t))

(defvar org-freemind-node-pattern (rx bol
                         (submatch (1+ "*"))
                         (1+ space)
                         (submatch (*? nonl))
                         eol))

(defun org-freemind-look-for-visible-child (node-level)
  (save-excursion
    (save-match-data
      (let ((found-visible-child nil))
        (while (and (not found-visible-child)
                    (re-search-forward org-freemind-node-pattern nil t))
          (let* ((m1 (match-string-no-properties 1))
                 (level (length m1)))
            (if (>= node-level level)
                (setq found-visible-child 'none)
              (unless (get-char-property (line-beginning-position) 'invisible)
                (setq found-visible-child 'found)))))
        (eq found-visible-child 'found)
        ))))

(defun org-freemind-goto-line (line)
  "Go to line number LINE."
  (save-restriction
    (widen)
    (goto-char (point-min))
    (forward-line (1- line))))

(defun org-freemind-write-mm-buffer (org-buffer mm-buffer node-at-line)
  (with-current-buffer org-buffer
    (dolist (node-style org-freemind-node-styles)
      (when (string-match-p (car node-style) buffer-file-name)
        (setq org-freemind-node-style (cadr node-style))))
    ;;(message "org-freemind-node-style =%s" org-freemind-node-style)
    (save-match-data
      (let* ((drawers (copy-sequence org-drawers))
             drawers-regexp
             (num-top1-nodes 0)
             (num-top2-nodes 0)
             num-left-nodes
             (unclosed-nodes 0)
	     (odd-only org-odd-levels-only)
             (first-time t)
             (current-level 1)
             base-level
             prev-node-end
             rich-text
             unfinished-tag
             node-at-line-level
             node-at-line-last)
        (with-current-buffer mm-buffer
          (erase-buffer)
          (insert "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
          (insert "<map version=\"0.9.0\">\n")
          (insert "<!-- To view this file, download free mind mapping software FreeMind from http://freemind.sourceforge.net -->\n"))
        (save-excursion
          ;; Get special buffer vars:
          (goto-char (point-min))
          (while (re-search-forward (rx bol "#+DRAWERS:") nil t)
            (let ((dr-txt (buffer-substring-no-properties (match-end 0) (line-end-position))))
              (setq drawers (append drawers (split-string dr-txt) nil))))
          (setq drawers-regexp
                (concat (rx bol (0+ blank) ":")
                        (regexp-opt drawers)
                        (rx ":" (0+ blank)
                            "\n"
                            (*? anything)
                            "\n"
                            (0+ blank)
                            ":END:"
                            (0+ blank)
                            eol)
                        ))

          (if node-at-line
              ;; Get number of top nodes and last line for this node
              (progn
                (org-freemind-goto-line node-at-line)
                (unless (looking-at org-freemind-node-pattern)
                  (error "No node at line %s" node-at-line))
                (setq node-at-line-level (length (match-string-no-properties 1)))
                (forward-line)
                (setq node-at-line-last
                      (catch 'last-line
                        (while (re-search-forward org-freemind-node-pattern nil t)
                          (let* ((m1 (match-string-no-properties 1))
                                 (level (length m1)))
                            (if (<= level node-at-line-level)
                                (progn
                                  (beginning-of-line)
                                  (throw 'last-line (1- (point))))
                              (if (= level (1+ node-at-line-level))
                                  (setq num-top2-nodes (1+ num-top2-nodes))))))))
                (setq current-level node-at-line-level)
                (setq num-top1-nodes 1)
                (org-freemind-goto-line node-at-line))

            ;; First get number of top nodes
            (goto-char (point-min))
            (while (re-search-forward org-freemind-node-pattern nil t)
              (let* ((m1 (match-string-no-properties 1))
                     (level (length m1)))
                (if (= level 1)
                    (setq num-top1-nodes (1+ num-top1-nodes))
                  (if (= level 2)
                      (setq num-top2-nodes (1+ num-top2-nodes))))))
            ;; If there is more than one top node we need to insert a node
            ;; to keep them together.
            (goto-char (point-min))
            (when (> num-top1-nodes 1)
              (setq num-top2-nodes num-top1-nodes)
              (setq current-level 0)
              (let ((orig-name (if buffer-file-name
                                   (file-name-nondirectory (buffer-file-name))
                                 (buffer-name))))
                (with-current-buffer mm-buffer
                  (insert "<node text=\"" orig-name "\" background_color=\"#00bfff\">\n"
                          ;; Put a note that this is for the parent node
                          "<richcontent TYPE=\"NOTE\"><html>"
                          "<head>"
                          "</head>"
                          "<body>"
                          "<p>"
                          org-freemind-org-nfix "WHOLE FILE"
                          "</p>"
                          "</body>"
                          "</html>"
                          "</richcontent>\n")))))

          (setq num-left-nodes (floor num-top2-nodes 2))
          (setq base-level current-level)
          (let (this-m2
                this-node-end
                this-children-visible
                next-m2
                next-node-start
                next-level
                next-has-some-visible-child
                next-children-visible
                )
            (while (and
                    (re-search-forward org-freemind-node-pattern nil t)
                    (if node-at-line-last (<= (point) node-at-line-last) t)
                    )
              (let* ((next-m1 (match-string-no-properties 1))
                     (next-node-end (match-end 0))
                     )
                (setq next-node-start (match-beginning 0))
                (setq next-m2 (match-string-no-properties 2))
                (setq next-level (length next-m1))
                (setq next-children-visible
                      (not (eq 'outline
                               (get-char-property (line-end-position) 'invisible))))
                (setq next-has-some-visible-child
                      (if next-children-visible t
                        (org-freemind-look-for-visible-child next-level)))
                (when this-m2
                  (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)))
                (when (if (= num-top1-nodes 1) (> current-level base-level) t)
                  (while (>= current-level next-level)
                    (with-current-buffer mm-buffer
                      (insert "</node>\n")
                      (setq current-level
			    (- current-level (if odd-only 2 1))))))
                (setq this-node-end (1+ next-node-end))
                (setq this-m2 next-m2)
                (setq current-level next-level)
                (setq this-children-visible next-children-visible)
                (forward-char)
                ))
;;;             (unless (if node-at-line-last
;;;                         (>= (point) node-at-line-last)
;;;                       nil)
              ;; Write last node:
              (setq this-m2 next-m2)
              (setq current-level next-level)
              (setq next-node-start (if node-at-line-last
                                        (1+ node-at-line-last)
                                      (point-max)))
              (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))
              (with-current-buffer mm-buffer (insert "</node>\n"))
              ;)
            )
          (with-current-buffer mm-buffer
            (while (> current-level base-level)
              (insert "</node>\n")
	      (setq current-level
		    (- current-level (if odd-only 2 1)))
              ))
          (with-current-buffer mm-buffer
            (insert "</map>")
            (delete-trailing-whitespace)
            (goto-char (point-min))
            ))))))

(defun org-freemind-get-node-style (node-name)
  "NOT READY YET."
  ;;<node BACKGROUND_COLOR="#eeee00" CREATED="1234668815593" MODIFIED="1234668815593" STYLE="bubble">
  ;;<font BOLD="true" NAME="SansSerif" SIZE="12"/>
  (let (node-styles
        node-style)
    (dolist (style-list org-freemind-node-style)
      (let ((node-regexp (car style-list)))
        (message "node-regexp=%s node-name=%s" node-regexp node-name)
        (when (string-match-p node-regexp node-name)
          ;;(setq node-style (org-freemind-do-apply-node-style style-list))
          (setq node-style (cadr style-list))
          (when node-style
            (message "node-style=%s" node-style)
            (setq node-styles (append node-styles node-style)))
          )))))

(defun org-freemind-do-apply-node-style (style-list)
  (message "style-list=%S" style-list)
  (let ((node-style 'fork)
        (color "red")
        (background-color "yellow")
        (edge-color "green")
        (edge-style 'bezier)
        (edge-width 'thin)
        (italic t)
        (bold t)
        (font-name "SansSerif")
        (font-size 12))
    (dolist (style (cadr style-list))
      (message "    style=%s" style)
      (let ((what (car style)))
        (cond
         ((eq what 'node-style)
          (setq node-style (cadr style)))
         ((eq what 'color)
          (setq color (cadr style)))
         ((eq what 'background-color)
          (setq background-color (cadr style)))

         ((eq what 'edge-color)
          (setq edge-color (cadr style)))

         ((eq what 'edge-style)
          (setq edge-style (cadr style)))

         ((eq what 'edge-width)
          (setq edge-width (cadr style)))

         ((eq what 'italic)
          (setq italic (cadr style)))

         ((eq what 'bold)
          (setq bold (cadr style)))

         ((eq what 'font-name)
          (setq font-name (cadr style)))

         ((eq what 'font-size)
          (setq font-size (cadr style)))
         )
        (insert (format " style=\"%s\"" node-style))
        (insert (format " color=\"%s\"" color))
        (insert (format " background_color=\"%s\"" background-color))
        (insert ">\n")
        (insert "<edge")
        (insert (format " color=\"%s\"" edge-color))
        (insert (format " style=\"%s\"" edge-style))
        (insert (format " width=\"%s\"" edge-width))
        (insert "/>\n")
        (insert "<font")
        (insert (format " italic=\"%s\"" italic))
        (insert (format " bold=\"%s\"" bold))
        (insert (format " name=\"%s\"" font-name))
        (insert (format " size=\"%s\"" font-size))
        ))))

;;;###autoload
(defun org-freemind-from-org-mode-node (node-line mm-file)
  "Convert node at line NODE-LINE to the FreeMind file MM-FILE."
  (interactive
   (progn
     (unless (org-back-to-heading nil)
       (error "Can't find org-mode node start"))
     (let* ((line (line-number-at-pos))
            (default-mm-file (concat (if buffer-file-name
                                         (file-name-nondirectory buffer-file-name)
                                       "nofile")
                                     "-line-" (number-to-string line)
                                     ".mm"))
            (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
       (list line mm-file))))
  (when (org-freemind-check-overwrite mm-file (called-interactively-p 'any))
    (let ((org-buffer (current-buffer))
          (mm-buffer (find-file-noselect mm-file)))
      (org-freemind-write-mm-buffer org-buffer mm-buffer node-line)
      (with-current-buffer mm-buffer
        (basic-save-buffer)
        (when (called-interactively-p 'any)
          (switch-to-buffer-other-window mm-buffer)
          (when (y-or-n-p "Show in FreeMind? ")
            (org-freemind-show buffer-file-name)))))))

;;;###autoload
(defun org-freemind-from-org-mode (org-file mm-file)
  "Convert the `org-mode' file ORG-FILE to the FreeMind file MM-FILE."
  ;; Fix-me: better doc, include recommendations etc.
  (interactive
   (let* ((org-file buffer-file-name)
          (default-mm-file (concat
                            (if org-file
                                (file-name-nondirectory org-file)
                              "nofile")
                            ".mm"))
          (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
     (list org-file mm-file)))
  (when (org-freemind-check-overwrite mm-file (called-interactively-p 'any))
    (let ((org-buffer (if org-file (find-file-noselect org-file) (current-buffer)))
          (mm-buffer (find-file-noselect mm-file)))
      (org-freemind-write-mm-buffer org-buffer mm-buffer nil)
      (with-current-buffer mm-buffer
        (basic-save-buffer)
        (when (called-interactively-p 'any)
          (switch-to-buffer-other-window mm-buffer)
          (when (y-or-n-p "Show in FreeMind? ")
            (org-freemind-show buffer-file-name)))))))

;;;###autoload
(defun org-freemind-from-org-sparse-tree (org-buffer mm-file)
  "Convert visible part of buffer ORG-BUFFER to FreeMind file MM-FILE."
  (interactive
   (let* ((org-file buffer-file-name)
          (default-mm-file (concat
                            (if org-file
                                (file-name-nondirectory org-file)
                              "nofile")
                            "-sparse.mm"))
          (mm-file (read-file-name "Output FreeMind file: " nil nil nil default-mm-file)))
     (list (current-buffer) mm-file)))
  (when (org-freemind-check-overwrite mm-file (called-interactively-p 'any))
    (let (org-buffer
          (mm-buffer (find-file-noselect mm-file)))
      (save-window-excursion
        (org-export-visible ?\  nil)
        (setq org-buffer (current-buffer)))
      (org-freemind-write-mm-buffer org-buffer mm-buffer nil)
      (with-current-buffer mm-buffer
        (basic-save-buffer)
        (when (called-interactively-p 'any)
          (switch-to-buffer-other-window mm-buffer)
          (when (y-or-n-p "Show in FreeMind? ")
            (org-freemind-show buffer-file-name)))))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; FreeMind => Org

;; (sort '(b a c) 'org-freemind-lt-symbols)
(defun org-freemind-lt-symbols (sym-a sym-b)
  (string< (symbol-name sym-a) (symbol-name sym-b)))
;; (sort '((b . 1) (a . 2) (c . 3)) 'org-freemind-lt-xml-attrs)
(defun org-freemind-lt-xml-attrs (attr-a attr-b)
  (string< (symbol-name (car attr-a)) (symbol-name (car attr-b))))

;; xml-parse-region gives things like
;; ((p nil "\n"
;;     (a
;;      ((href . "link"))
;;      "text")
;;     "\n"
;;     (b nil "hej")
;;     "\n"))

;; '(a . nil)

;; (org-freemind-symbols= 'a (car '(A B)))
(defsubst org-freemind-symbols= (sym-a sym-b)
  "Return t if downcased names of SYM-A and SYM-B are equal.
SYM-A and SYM-B should be symbols."
  (or (eq sym-a sym-b)
      (string= (downcase (symbol-name sym-a))
               (downcase (symbol-name sym-b)))))

(defun org-freemind-get-children (parent path)
  "Find children node to PARENT from PATH.
PATH should be a list of steps, where each step has the form

  '(NODE-NAME (ATTR-NAME . ATTR-VALUE))"
  ;; Fix-me: maybe implement op? step: Name, number, attr, attr op val
  ;; Fix-me: case insensitive version for children?
  (let* ((children (if (not (listp (car parent)))
                       (cddr parent)
                     (let (cs)
                       (dolist (p parent)
                         (dolist (c (cddr p))
                           (add-to-list 'cs c)))
                       cs)
                     ))
         (step (car path))
         (step-node (if (listp step) (car step) step))
         (step-attr-list (when (listp step) (sort (cdr step) 'org-freemind-lt-xml-attrs)))
         (path-tail (cdr path))
         path-children)
    (dolist (child children)
      ;; skip xml.el formatting nodes
      (unless (stringp child)
        ;; compare node name
        (when (if (not step-node)
                  t ;; any node name
                (org-freemind-symbols= step-node (car child)))
          (if (not step-attr-list)
              ;;(throw 'path-child child) ;; no attr to care about
              (add-to-list 'path-children child)
            (let* ((child-attr-list (cadr child))
                   (step-attr-copy (copy-sequence step-attr-list)))
              (dolist (child-attr child-attr-list)
                                   ;; Compare attr names:
                (when (org-freemind-symbols= (caar step-attr-copy) (car child-attr))
                  ;; Compare values:
                  (let ((step-val (cdar step-attr-copy))
                        (child-val (cdr child-attr)))
                    (when (if (not step-val)
                              t ;; any value
                            (string= step-val child-val))
                      (setq step-attr-copy (cdr step-attr-copy))))))
              ;; Did we find all?
              (unless step-attr-copy
                ;;(throw 'path-child child)
                (add-to-list 'path-children child)
                ))))))
    (if path-tail
        (org-freemind-get-children path-children path-tail)
      path-children)))

(defun org-freemind-get-richcontent-node (node)
  (let ((rc-nodes
         (org-freemind-get-children node '((richcontent (type . "NODE")) html body))))
    (when (> (length rc-nodes) 1)
      (lwarn t :warning "Unexpected structure: several <richcontent type=\"NODE\" ...>"))
    (car rc-nodes)))

(defun org-freemind-get-richcontent-note (node)
  (let ((rc-notes
         (org-freemind-get-children node '((richcontent (type . "NOTE")) html body))))
    (when (> (length rc-notes) 1)
      (lwarn t :warning "Unexpected structure: several <richcontent type=\"NOTE\" ...>"))
    (car rc-notes)))

(defun org-freemind-test-get-tree-text ()
  (let ((node '(p nil "\n"
                 (a
                  ((href . "link"))
                  "text")
                 "\n"
                 (b nil "hej")
                 "\n")))
    (org-freemind-get-tree-text node)))
;; (org-freemind-test-get-tree-text)

(defun org-freemind-get-tree-text (node)
  (when node
    (let ((ntxt "")
          (link nil)
          (lf-after nil))
      (dolist (n node)
        (case n
          ;;(a (setq is-link t) )
          ((h1 h2 h3 h4 h5 h6 p)
           ;;(setq ntxt (concat "\n" ntxt))
           (setq lf-after 2)
           )
          (br
           (setq lf-after 1)
           )
          (t
           (cond
            ((stringp n)
             (when (string= n "\n") (setq n ""))
             (if link
                 (setq ntxt (concat ntxt
                                    "[[" link "][" n "]]"))
               (setq ntxt (concat ntxt n))))
            ((and n (listp n))
             (if (symbolp (car n))
                 (setq ntxt (concat ntxt (org-freemind-get-tree-text n)))
               ;; This should be the attributes:
               (dolist (att-val n)
                 (let ((att (car att-val))
                       (val (cdr att-val)))
                   (when (eq att 'href)
                     (setq link val)))))
             )))))
      (if lf-after
          (setq ntxt (concat ntxt (make-string lf-after ?\n)))
        (setq ntxt (concat ntxt " ")))
      ;;(setq ntxt (concat ntxt (format "{%s}" n)))
      ntxt)))

(defun org-freemind-get-richcontent-node-text (node)
  "Get the node text as from the richcontent node NODE."
  (save-match-data
    (let* ((rc (org-freemind-get-richcontent-node node))
           (txt (org-freemind-get-tree-text rc)))
      ;;(when txt (setq txt (replace-regexp-in-string (rx (1+ whitespace)) " " txt)))
      txt
      )))

(defun org-freemind-get-richcontent-note-text (node)
  "Get the node text as from the richcontent note NODE."
  (save-match-data
    (let* ((rc (org-freemind-get-richcontent-note node))
           (txt (when rc (org-freemind-get-tree-text rc))))
      ;;(when txt (setq txt (replace-regexp-in-string (rx (1+ whitespace)) " " txt)))
      txt
      )))

(defun org-freemind-get-icon-names (node)
  (let* ((icon-nodes (org-freemind-get-children node '((icon ))))
         names)
    (dolist (icn icon-nodes)
      (setq names (cons (cdr (assq 'builtin (cadr icn))) names)))
    ;; (icon (builtin . "full-1"))
    names))

(defun org-freemind-node-to-org (node level skip-levels)
  (let ((qname (car node))
        (attributes (cadr node))
        text
        (note (org-freemind-get-richcontent-note-text node))
        (mark "-- This is more about ")
        (icons (org-freemind-get-icon-names node))
        (children (cddr node)))
    (when (< 0 (- level skip-levels))
      (dolist (attrib attributes)
        (case (car attrib)
          ('TEXT (setq text (cdr attrib)))
          ('text (setq text (cdr attrib)))))
      (unless text
        ;; There should be a richcontent node holding the text:
        (setq text (org-freemind-get-richcontent-node-text node)))
      (when icons
        (when (member "full-1" icons) (setq text (concat "[#A] " text)))
        (when (member "full-2" icons) (setq text (concat "[#B] " text)))
        (when (member "full-3" icons) (setq text (concat "[#C] " text)))
        (when (member "full-4" icons) (setq text (concat "[#D] " text)))
        (when (member "full-5" icons) (setq text (concat "[#E] " text)))
        (when (member "full-6" icons) (setq text (concat "[#F] " text)))
        (when (member "full-7" icons) (setq text (concat "[#G] " text)))
        (when (member "button_cancel" icons) (setq text (concat "TODO " text)))
        )
      (if (and note
               (string= mark (substring note 0 (length mark))))
          (progn
            (setq text (replace-regexp-in-string "\n $" "" text))
            (insert text))
        (case qname
          ('node
           (insert (make-string (- level skip-levels) ?*) " " text "\n")
           ))))
    (dolist (child children)
      (unless (or (null child)
                  (stringp child))
        (org-freemind-node-to-org child (1+ level) skip-levels)))))

;; Fix-me: put back special things, like drawers that are stored in
;; the notes. Should maybe all notes contents be put in drawers?
;;;###autoload
(defun org-freemind-to-org-mode (mm-file org-file)
  "Convert FreeMind file MM-FILE to `org-mode' file ORG-FILE."
  (interactive
   (save-match-data
     (let* ((mm-file (buffer-file-name))
            (default-org-file (concat (file-name-nondirectory mm-file) ".org"))
            (org-file (read-file-name "Output org-mode file: " nil nil nil default-org-file)))
       (list mm-file org-file))))
  (when (org-freemind-check-overwrite org-file (called-interactively-p 'any))
    (let ((mm-buffer (find-file-noselect mm-file))
          (org-buffer (find-file-noselect org-file)))
      (with-current-buffer mm-buffer
        (let* ((xml-list (xml-parse-file mm-file))
               (top-node (cadr (cddar xml-list)))
               (note (org-freemind-get-richcontent-note-text top-node))
               (skip-levels
                (if (and note
                         (string-match (rx bol "--org-mode: WHOLE FILE" eol) note))
                    1
                  0)))
          (with-current-buffer org-buffer
            (erase-buffer)
            (org-freemind-node-to-org top-node 1 skip-levels)
            (goto-char (point-min))
            (org-set-tags t t) ;; Align all tags
            )
          (switch-to-buffer-other-window org-buffer)
          )))))

(provide 'org-freemind)

;; arch-tag: e7b0d776-94fd-404a-b35e-0f855fae3627

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; org-freemind.el ends here