<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>sacha chua :: living an awesome life &#187; emacs</title>
	<atom:link href="http://sachachua.com/blog/category/emacs/feed/" rel="self" type="application/rss+xml" />
	<link>http://sachachua.com/blog</link>
	<description>I help organizations and people learn how to connect and collaborate more effectively using Web 2.0 tools.</description>
	<lastBuildDate>Thu, 02 Sep 2010 12:00:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Using org2blog to publish Org-mode subtrees</title>
		<link>http://sachachua.com/blog/2010/07/using-org2blog-to-publish-org-mode-subtrees/</link>
		<comments>http://sachachua.com/blog/2010/07/using-org2blog-to-publish-org-mode-subtrees/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 12:00:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://sachachua.com/blog/?p=7265</guid>
		<description><![CDATA[This patch modifies punchagan&#8217;s org2blog to allow you to publish an Org subtree with M-x org2blog-post-subtree. It posts a draft by default, and publishes the post if you call it with C-u M-x org2blog-post-subtree. It gets the posting date from SCHEDULED, DEADLINE, active or inactive timestamps, or the Post Date property, and lets you use [...]]]></description>
			<content:encoded><![CDATA[<p> This patch modifies punchagan&#8217;s <a href="http://github.com/punchagan/org2blog">org2blog</a> to allow you to publish an Org subtree with <code>M-x org2blog-post-subtree</code>. It posts a draft by default, and publishes the post if you call it with <code>C-u M-x org2blog-post-subtree</code>. It gets the posting date from SCHEDULED, DEADLINE, active or inactive timestamps, or the Post Date property, and lets you use tags as categories or use a separate Categories property. It inherits tags from parent headings, too. It picks up the title from the subtree heading or uses the Title property. </p>
<div id="outline-container-1" class="outline-4">
<h4 id="sec-1">Patch </h4>
<div class="outline-text-4" id="text-1">
<pre class="example">diff --git a/org2blog.el b/org2blog.el
index dc88291..b95caba 100644
--- a/org2blog.el
+++ b/org2blog.el
@@ -77,6 +77,11 @@
   :group 'org2blog
   :type 'string)

+(defcustom org2blog-use-tags-as-categories nil
+  "Non-nil means assign :tags: to WordPress categories instead."
+  :group 'org2blog
+  :type 'boolean)
+
 (defvar org2blog-categories-list nil
   "List of weblog categories")

@@ -433,4 +438,143 @@
        (goto-char current-pos)
        (command-execute (lookup-key org-mode-map (kbd "C-c t")))))))

+(defun org2blog-create-categories (categories)
+  "Create unknown CATEGORIES."
+  (mapcar
+   (lambda (cat)
+     (if (and (not (member cat org2blog-categories-list))
+              (y-or-n-p (format "Create %s category? " cat)))
+         (wp-new-category org2blog-server-xmlrpc-url
+                          org2blog-server-userid
+                          (org2blog-password)
+                          org2blog-server-blogid
+                          cat)))
+   categories))
+
+(defun org2blog-password ()
+  "Get password or prompt if needed."
+  (or org2blog-server-pass
+      (setq org2blog-server-pass (read-passwd "Weblog password? "))))
+
+(defun org2blog-upload-images-insert-links (&amp;optional beg end)
+  "Upload images and replace with links in the region specified by BEG to END."
+  (interactive "r")
+  (let ((re
+        (concat "\\[\\[\\(.*\\)"
+                (substring (org-image-file-name-regexp) 0 -2)
+                "\\]\\]"))
+       file-all-urls file-name file-web-url blog-pass)
+    (save-excursion
+      (save-restriction
+        (narrow-to-region (or beg (point-min))
+                          (or end (point-max)))
+        (goto-char (point-min))
+        (while (re-search-forward re nil t 1)
+          (setq file-name (concat
+                           (match-string-no-properties 1)
+                           "."
+                           (match-string-no-properties 2)))
+          (unless (save-match-data (string-match org-link-types-re file-name))
+            (save-match-data
+              (if (assoc file-name file-all-urls)
+                  (setq file-web-url (cdr (assoc file-name file-all-urls)))
+                (setq file-web-url
+                      (cdr (assoc "url"
+                                  (metaweblog-upload-image org2blog-server-xmlrpc-url
+                                                           org2blog-server-userid
+                                                           (org2blog-password)
+                                                           org2blog-server-weblog-id
+                                                           (get-image-properties file-name))))
+                      file-all-urls (append file-all-urls (list (cons
+                                                                 file-name file-web-url))))))
+            (replace-match (concat "[[" file-web-url "]]") t t nil 0)))))
+    file-all-urls))
+
+(defun org2blog-post-subtree (&amp;optional publish)
+  "Post the current entry as a draft. Publish if PUBLISH is non-nil."
+  (interactive "P")
+  (let ((post (org2blog-parse-subtree))
+        post-id)
+    (org2blog-create-categories (cdr (assoc "categories" post)))
+    (setq post-id (cdr (assoc "post-id" post)))
+    (save-excursion
+      (org2blog-upload-images-insert-links (org-back-to-heading) (org-end-of-subtree)))
+    (if post-id
+        (metaweblog-edit-post org2blog-server-xmlrpc-url
+                             org2blog-server-userid
+                              (org2blog-password)
+                             post-id
+                              post
+                             publish)
+      (setq post-id
+            (metaweblog-new-post
+             org2blog-server-xmlrpc-url
+             org2blog-server-userid
+             (org2blog-password)
+             org2blog-server-blogid
+             post
+             publish))
+      (org-entry-put (point) "Post ID" post-id)
+      (message (if publish
+                   "Published (%s): %s"
+                 "Draft (%s): %s")
+               post-id
+               (cdr (assoc "title" post))))))
+
+(defun org2blog-parse-subtree ()
+  "Parse the current subtree as a blog entry."
+  (let (html-text
+        (post-title (or (org-entry-get (point) "Title")
+                        (org-get-heading t)))
+        (post-id (org-entry-get (point) "Post ID"))
+        ;; Set post-date to the Post Date property or look for timestamp
+        (post-date (or (org-entry-get (point) "Post Date")
+                       (org-entry-get (point) "SCHEDULED")
+                       (org-entry-get (point) "DEADLINE")
+                       (org-entry-get (point) "TIMESTAMP_IA")
+                       (org-entry-get (point) "TIMESTAMP")))
+        (tags (org-get-tags-at (point) nil))
+        (categories (org-split-string (or (org-entry-get (point) "CATEGORIES") "") ":")))
+    ;; Convert post date to ISO timestamp
+    (setq post-date
+          (format-time-string "%Y%m%dT%T"
+                              (if post-date
+                                  (apply 'encode-time (org-parse-time-string post-date))
+                                (current-time))
+                              t))
+    (if org2blog-use-tags-as-categories
+        (setq categories tags
+              tags nil))
+    (save-excursion
+      (setq html-text
+            (org-export-region-as-html
+             (and (org-back-to-heading) (line-end-position))
+             (org-end-of-subtree)
+             t 'string))
+      (setq html-text
+            (with-temp-buffer
+              (insert html-text)
+              (goto-char (point-min))
+              ;; Fix newlines
+             (let (start-pos end-pos)
+                (setq start-pos (point-min))
+               (goto-char start-pos)
+                (while (re-search-forward "&lt;\\(pre\\|blockquote\\).*?&gt;" nil t 1)
+                  (setq end-pos (match-beginning 0))
+                  (replace-string "\n" " " nil start-pos end-pos)
+                  (re-search-forward (concat "&lt;/" (match-string-no-properties 1) "&gt;") nil t 1)
+                  (setq start-pos (match-end 0))
+                  (goto-char start-pos))
+               (setq end-pos (point-max))
+               (replace-string "\n" " " nil start-pos end-pos))
+              ;; Copy the text
+              (buffer-substring-no-properties (point-min) (point-max)))))
+    (list
+     (cons "date" post-date)
+     (cons "title" post-title)
+     (cons "tags" tags)
+     (cons "categories" categories)
+     (cons "post-id" post-id)
+     (cons "description" html-text))))
+
 (provide 'org2blog)
</pre>
<p> I like using one big Org file for all of my notes so that I can search and categorize things easily.  </p>
<p> Here is the sample code from my ~/.emacs: </p>
<pre class="src src-emacs-lisp">(add-to-list 'load-path <span style="color: #ffa07a;">"~/elisp/org2blog"</span>)
(<span style="color: #00ffff;">require</span> '<span style="color: #7fffd4;">org2blog</span>)
(setq org2blog-server-url <span style="color: #ffa07a;">"http://sachachua.com/blog/xmlrpc.php"</span>
      org2blog-server-user <span style="color: #ffa07a;">"admin"</span>
      org2blog-server-weblog-id <span style="color: #ffa07a;">""</span>
      org2blog-use-tags-as-categories t)
(org2blog-login)
</pre>
<p> Then I can go to the entry and call <code>M-x org2blog-post-subtree</code> to post a draft or <code>C-u M-x org2blog-post-subtree</code> to publish it. </p>
<p> Note that the code uses whatever heading level you&#8217;re on, so if you&#8217;re under a sub-heading of the post you want to publish, use <code>C-c C-u outline-up-heading</code> to go up headings until you&#8217;re at the right level. </p>
<p> You can get the modified source code from <a href="http://github.com/sachac/org2blog">http://github.com/sachac/org2blog</a> . I&#8217;ve also sent a pull request upstream. </p>
</div></div>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2010/07/using-org2blog-to-publish-org-mode-subtrees/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>This is a test post from org2blog</title>
		<link>http://sachachua.com/blog/2010/07/this-is-a-test-post-from-org2blog/</link>
		<comments>http://sachachua.com/blog/2010/07/this-is-a-test-post-from-org2blog/#comments</comments>
		<pubDate>Mon, 19 Jul 2010 12:47:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/blog/2010/07/this-is-a-test-post-from-org2blog/</guid>
		<description><![CDATA[After I moved from Emacs Planner to Org-mode, I missed the ability to quickly publish my notes from my personal information manager. A backlink on my blog led me to punchagan&#8217;s org2blog, which I&#8217;m using to create this test post. I&#8217;d like to eventually be able to publish subtrees instead of starting new posts or [...]]]></description>
			<content:encoded><![CDATA[<p>After I moved from Emacs Planner to Org-mode, I missed the ability to quickly publish my notes from my personal information manager. A <a href="http://punchagan.wordpress.com/2010/07/17/im-back/">backlink on my blog</a> led me to punchagan&#8217;s <a href="http://github.com/punchagan/org2blog">org2blog</a>, which I&#8217;m using to create this test post. </p>
<p> I&#8217;d like to eventually be able to publish subtrees instead of starting new posts or publishing the entire page. This is a pretty good start, though. </p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2010/07/this-is-a-test-post-from-org2blog/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Org-toodledo</title>
		<link>http://sachachua.com/blog/2010/05/org-toodledo/</link>
		<comments>http://sachachua.com/blog/2010/05/org-toodledo/#comments</comments>
		<pubDate>Sun, 16 May 2010 12:00:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[emacslisp]]></category>
		<category><![CDATA[geek]]></category>
		<category><![CDATA[org]]></category>
		<category><![CDATA[productivity]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2010/05/org-toodledo/</guid>
		<description><![CDATA[I finally got around to asking my manager for permission to contribute org-toodledo as open source. Here it is. Enjoy! ;;; org-toodledo.el - Toodledo integration for Emacs Org mode ;; (c) 2010 Sacha Chua (sacha@sachachua.com) ;; ;; This file is not part of GNU Emacs. ;; This is free software; you can redistribute it and/or [...]]]></description>
			<content:encoded><![CDATA[<p>I finally got around to asking my manager for permission to contribute org-toodledo as open source. Here it is. Enjoy!</p>
<pre class="code"><span class="comment-delimiter">;;; </span><span class="comment">org-toodledo.el - Toodledo integration for Emacs Org mode
</span><span class="comment-delimiter">;; </span><span class="comment">(c) 2010 Sacha Chua (sacha@sachachua.com)
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">This file is not part of GNU Emacs.
</span>
<span class="comment-delimiter">;; </span><span class="comment">This is free software; you can redistribute it and/or modify it under
</span><span class="comment-delimiter">;; </span><span class="comment">the terms of the GNU General Public License as published by the Free
</span><span class="comment-delimiter">;; </span><span class="comment">Software Foundation; either version 2, or (at your option) any later
</span><span class="comment-delimiter">;; </span><span class="comment">version.
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">This is distributed in the hope that it will be useful, but WITHOUT
</span><span class="comment-delimiter">;; </span><span class="comment">ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
</span><span class="comment-delimiter">;; </span><span class="comment">FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
</span><span class="comment-delimiter">;; </span><span class="comment">for more details.
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">You should have received a copy of the GNU General Public License
</span><span class="comment-delimiter">;; </span><span class="comment">along with GNU Emacs; see the file COPYING.  If not, write to the
</span><span class="comment-delimiter">;; </span><span class="comment">Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
</span><span class="comment-delimiter">;; </span><span class="comment">MA 02111-1307, USA.
</span>
<span class="comment-delimiter">;; </span><span class="comment">How to use:
</span><span class="comment-delimiter">;; </span><span class="comment">1. Customize org-toodledo-userid and org-toodledo-password
</span><span class="comment-delimiter">;; </span><span class="comment">2. Open a blank org file.
</span><span class="comment-delimiter">;; </span><span class="comment">3. Call org-toodledo-initialize-org
</span><span class="comment-delimiter">;; </span><span class="comment">Call org-toodledo-update to bring in new/updated tasks (skips locally modified tasks newer than updated)
</span><span class="comment-delimiter">;; </span><span class="comment">Call org-toodledo-sync-task to create or update the current task
</span><span class="comment-delimiter">;; </span><span class="comment">Call org-toodledo-delete-current-task to delete the current task
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">Doesn't do lots of error trapping. Might be a good idea to version-control your Org file.
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">TOODLEDO ATTRIBUTES and how they are bi-directionally handled
</span><span class="comment-delimiter">;; </span><span class="comment">Context: Handled by tags (ex:   :@work:  :@errands:)
</span><span class="comment-delimiter">;;   </span><span class="comment">- will create new contexts if necessary
</span><span class="comment-delimiter">;; </span><span class="comment">Task status: Mapped to TODO state.
</span><span class="comment-delimiter">;;   </span><span class="comment">See org-toodledo-status-to-string and org-toodledo-parse-current-task for the mapping
</span><span class="comment-delimiter">;;   </span><span class="comment">You will probably want something like this in your ~/.emacs:
</span><span class="comment-delimiter">;; </span><span class="comment">(setq org-todo-keywords
</span><span class="comment-delimiter">;;      </span><span class="comment">'((sequence
</span><span class="comment-delimiter">;;         </span><span class="comment">"TODO(t)"  ; next action
</span><span class="comment-delimiter">;;         </span><span class="comment">"PLAN(-)"
</span><span class="comment-delimiter">;;         </span><span class="comment">"STARTED(s)"
</span><span class="comment-delimiter">;;         </span><span class="comment">"WAITING(w@/!)"
</span><span class="comment-delimiter">;;         </span><span class="comment">"POSTPONED(p)" "SOMEDAY(s@/!)" "|" "DONE(x!)" "CANCELLED(c@)")
</span><span class="comment-delimiter">;;</span><span class="comment">      (type "DELEGATED(d@!)" "DONE(x)")))
</span><span class="comment-delimiter">;; </span><span class="comment">Length: Mapped to effort
</span><span class="comment-delimiter">;; </span><span class="comment">Priority: Mapped to [#A], [#B], or [#C]. (TODO: Change this to five levels of priority to match Toodledo)
</span><span class="comment-delimiter">;; </span><span class="comment">Start date: Mapped to "SCHEDULED"
</span><span class="comment-delimiter">;; </span><span class="comment">Due date: Mapped to "DEADLINE"
</span><span class="comment-delimiter">;; </span><span class="comment">Tags: Mapped to tags
</span><span class="comment-delimiter">;; </span><span class="comment">Note: Mapped to todo text. May get confused by asterisks, so don't use any starting asterisks in your body text.
</span><span class="comment-delimiter">;;   </span><span class="comment">(or anything that looks like an Org headline).
</span><span class="comment-delimiter">;; </span><span class="comment">Completed: Mapped to DONE todo state.
</span><span class="comment-delimiter">;;</span><span class="comment">
</span><span class="comment-delimiter">;; </span><span class="comment">TODO:
</span><span class="comment-delimiter">;; </span><span class="comment">- [ ] Double-check new/changed/deleted task updating, still seems buggy
</span><span class="comment-delimiter">;; </span><span class="comment">- [ ] Test, test, test - maybe make test harness?
</span><span class="comment-delimiter">;; </span><span class="comment">- [ ] Move status&lt;-&gt;string mapping to a variable - lookups are better than logic
</span><span class="comment-delimiter">;; </span><span class="comment">- [ ] Make sure sync timestamps aren't getting updated more often than needed
</span><span class="comment-delimiter">;; </span><span class="comment">- [ ] Suggest some kind of hook to make it easier to mark a task as locally modified
</span>
(<span class="keyword">require</span> '<span class="constant">org</span>)
(<span class="keyword">require</span> '<span class="constant">w3m</span>)
(<span class="keyword">require</span> '<span class="constant">xml</span>)
(<span class="keyword">defcustom</span> <span class="variable-name">org-toodledo-userid</span> <span class="string">""</span>
  <span class="doc">"UserID from Toodledo: http://www.toodledo.com/info/api_doc.php"</span>
  <span class="builtin">:group</span> 'org-toodledo
  <span class="builtin">:type</span> 'string)

(<span class="keyword">defcustom</span> <span class="variable-name">org-toodledo-password</span> <span class="string">""</span>
  <span class="doc">"Password for Toodledo."</span>
  <span class="builtin">:group</span> 'org-toodledo
  <span class="builtin">:type</span> 'string)

(<span class="keyword">defvar</span> <span class="variable-name">org-toodledo-token-expiry</span> nil <span class="doc">"Expiry time for authentication token."</span>)
(<span class="keyword">defvar</span> <span class="variable-name">org-toodledo-token</span> nil <span class="doc">"Authentication token."</span>)
(<span class="keyword">defvar</span> <span class="variable-name">org-toodledo-key</span> nil <span class="doc">"Authentication key."</span>)

(<span class="keyword">require</span> '<span class="constant">url</span>)
(<span class="keyword">require</span> '<span class="constant">url-http</span>)

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-initialize-org</span> ()
  <span class="doc">"Replace buffer contents with Toodledo tasks."</span>
  (interactive)
  (delete-region (point-min) (point-max))
  (<span class="keyword">let</span> ((account-info (org-toodledo-get-account-info))
        (server-info (org-toodledo-get-server-info))
        (tasks (org-toodledo-get-tasks '((<span class="string">"notcomp"</span> . <span class="string">"1"</span>)))))
    (insert <span class="string">"* Toodledo\n"</span>
            <span class="string">":PROPERTIES:\n"</span>
            <span class="string">":Last-modified: "</span> (cdr (assoc <span class="string">"lastaddedit"</span> account-info)) <span class="string">"\n"</span>
            <span class="string">":Last-deleted: "</span> (cdr (assoc <span class="string">"lastdelete"</span> account-info)) <span class="string">"\n"</span>
            <span class="string">":Last-sync: "</span> (cdr (assoc <span class="string">"unixtime"</span> server-info)) <span class="string">"\n"</span>
            <span class="string">":END:\n"</span>)
    (insert (mapconcat 'org-toodledo-task-to-string tasks <span class="string">"\n"</span>))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-token</span> ()
  <span class="doc">"Retrieve authentication token valid for four hours."</span>
  (<span class="keyword">if</span> (and org-toodledo-token
           org-toodledo-token-expiry
           (time-less-p (current-time) org-toodledo-token-expiry))
      org-toodledo-token
    <span class="comment-delimiter">;; </span><span class="comment">Else retrieve a new token
</span>    (<span class="keyword">let</span> ((response
            (<span class="keyword">with-current-buffer</span>
                (url-retrieve-synchronously
                 (concat <span class="string">"http://api.toodledo.com/api.php?method=getToken;userid="</span>
                         org-toodledo-userid))
              (xml-parse-region (point-min) (point-max)))))
      (<span class="keyword">if</span> (equal (car (car response)) 'error)
          (<span class="keyword">progn</span>
            (setq org-toodledo-token nil
                  org-toodledo-key nil
                  org-toodledo-token-expiry nil)
            (<span class="warning">error</span> <span class="string">"Could not log in to Toodledo: %s"</span> (elt (car response) 2)))
        (setq org-toodledo-token
              (elt (car response) 2))
        (setq org-toodledo-key (org-toodledo-key)
              <span class="comment-delimiter">;; </span><span class="comment">Set the expiry time
</span>              org-toodledo-token-expiry
              (seconds-to-time
               (+ (time-to-seconds (current-time))
                  (* 60 60 4)))))   <span class="comment-delimiter">;; </span><span class="comment">four hours
</span>      org-toodledo-token)))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-key</span> ()
  <span class="doc">"Return authentication key used for each request."</span>
  (<span class="keyword">if</span> (and org-toodledo-token
           org-toodledo-token-expiry
           (time-less-p (current-time) org-toodledo-token-expiry)
           org-toodledo-key)
      org-toodledo-key
    (setq org-toodledo-key
          (md5 (concat (md5 org-toodledo-password)
                       org-toodledo-token
                       org-toodledo-userid)))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-url</span> (method-name <span class="type">&amp;optional</span> params)
  <span class="doc">"Return URL for METHOD-NAME and PARAMS."</span>
  (org-toodledo-get-token)
  (concat <span class="string">"http://api.toodledo.com/api.php?method="</span>
          (w3m-url-encode-string method-name)
          <span class="string">";key="</span> (org-toodledo-key)
          (<span class="keyword">if</span> params
              (concat
               <span class="string">";"</span>
               (mapconcat (<span class="keyword">lambda</span> (x)
                            (concat
                             (w3m-url-encode-string (car x)) <span class="string">"="</span>
                             (w3m-url-encode-string (cdr x))))
                          params
                          <span class="string">";"</span>))
            <span class="string">""</span>)))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-call-method</span> (method-name <span class="type">&amp;optional</span> params)
  <span class="doc">"Call METHOD-NAME with PARAMS and return the parsed XML."</span>
  (setq params (cons (cons <span class="string">"unix"</span> <span class="string">"1"</span>) params))
  (<span class="keyword">with-current-buffer</span>
      (url-retrieve-synchronously
       (org-toodledo-get-url method-name params))
    (xml-parse-region (point-min) (point-max))))

(<span class="keyword">defmacro</span> <span class="function-name">org-toodledo-defun</span> (function-name api-name description)
  `(<span class="keyword">defun</span> ,function-name (params)
     ,description
     (org-toodledo-call-method ,api-name params)))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-server-info</span> ()
  <span class="doc">"Return server information."</span>
  (org-toodledo-convert-xml-result-to-alist
    (car (org-toodledo-call-method <span class="string">"getServerInfo"</span>))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-account-info</span> ()
  <span class="doc">"Return server information."</span>
  (org-toodledo-convert-xml-result-to-alist
   (car (org-toodledo-call-method <span class="string">"getAccountInfo"</span>))))

(org-toodledo-defun org-toodledo-add-task <span class="string">"addTask"</span> <span class="string">"Add task with PARAMS."</span>)
(org-toodledo-defun org-toodledo-edit-task <span class="string">"editTask"</span> <span class="string">"Edit task with PARAMS."</span>)
(org-toodledo-defun org-toodledo-delete-task <span class="string">"deleteTask"</span> <span class="string">"Delete task with PARAMS."</span>)

<span class="comment-delimiter">;; </span><span class="comment">(setq temp (org-toodledo-get-tasks '(("notcomp" . "1"))))
</span><span class="comment-delimiter">;; </span><span class="comment">(setq server-info (org-toodledo-get-server-info))
</span><span class="comment-delimiter">;; </span><span class="comment">(setq account-info (org-toodledo-get-account-info))
</span>(<span class="keyword">defun</span> <span class="function-name">org-toodledo-convert-xml-result-to-alist</span> (info)
  <span class="doc">"Convert INFO to an alist."</span>
  (delq nil
        (mapcar
         (<span class="keyword">lambda</span> (item)
           (<span class="keyword">if</span> (listp item)
               (cons (symbol-name (car item)) (elt item 2))))
         (xml-node-children (delete <span class="string">"\n\t"</span> info)))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-tasks</span> (<span class="type">&amp;optional</span> params)
  <span class="doc">"Retrieve tasks using PARAMS.
Return a list of task alists."</span>
  (mapcar
   'org-toodledo-convert-xml-result-to-alist
   (xml-get-children
    (car (org-toodledo-call-method <span class="string">"getTasks"</span> params))
    'task)))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-deleted</span> (<span class="type">&amp;optional</span> params)
  <span class="doc">"Retrieve deleted tasks using PARAMS.
Return a list of task alists."</span>
  (mapcar
   'org-toodledo-convert-xml-result-to-alist
   (xml-get-children
    (car (org-toodledo-call-method <span class="string">"getDeleted"</span> params))
    'task)))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-entry-note</span> ()
  <span class="doc">"Extract the note for this entry."</span>
  (<span class="keyword">save-excursion</span>
    (org-back-to-heading)
    (<span class="keyword">when</span> (looking-at org-complex-heading-regexp)
      (goto-char (match-end 0))
      (<span class="keyword">let</span> ((text (buffer-substring-no-properties
                   (point)
                   (<span class="keyword">if</span> (re-search-forward org-complex-heading-regexp nil t)
                       (match-beginning 0)
                     (org-end-of-subtree)))))
        (<span class="keyword">with-temp-buffer</span>
          (insert text)
          (goto-char (point-min))
          (<span class="keyword">when</span> (re-search-forward
                 (concat <span class="string">"\\&lt;"</span>
                         (regexp-quote org-deadline-string) <span class="string">" +&lt;[</span><span class="string"><span class="negation-char">^</span></span><span class="string">&gt;\n]+&gt;[ \t]*"</span>) nil t)
            (replace-match <span class="string">""</span>))
          (goto-char (point-min))
          (<span class="keyword">when</span> (re-search-forward
                 (concat <span class="string">"\\&lt;"</span>
                         (regexp-quote org-scheduled-string) <span class="string">" +&lt;[</span><span class="string"><span class="negation-char">^</span></span><span class="string">&gt;\n]+&gt;[ \t]*"</span>) nil t)
            (replace-match <span class="string">""</span>))
          (goto-char (point-min))
          (<span class="keyword">while</span> (re-search-forward <span class="string">"\n\n+"</span> nil t)
            (replace-match <span class="string">"\n"</span>))
          (org-export-remove-or-extract-drawers org-drawers nil nil)
          (buffer-substring-no-properties (point-min)
                                          (point-max)))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-parse-current-task</span> ()
  <span class="doc">"Extract the status and Toodledo ID of the current task."</span>
  (<span class="keyword">save-excursion</span>
    (org-back-to-heading t)
    (<span class="keyword">when</span> (and (looking-at org-complex-heading-regexp)
               (match-string 2)) <span class="comment-delimiter">;; </span><span class="comment">TODO
</span>      (<span class="keyword">let*</span> (info
             (status (match-string-no-properties 2))
             (priority (match-string-no-properties 3))
             (title (match-string-no-properties 4))
             (tags (match-string-no-properties 5))
             (id (org-entry-get (point) <span class="string">"Toodledo-ID"</span>))
             (contexts (org-toodledo-get-contexts))
             context)
        <span class="comment-delimiter">;; </span><span class="comment">(add-to-list 'info (cons "title" (match-string-no-properties 1)))
</span>        (<span class="keyword">if</span> id (add-to-list 'info (cons <span class="string">"id"</span> id)))
        (<span class="keyword">when</span> tags
          (setq tags
              (delq nil
                    (mapcar
                     (<span class="keyword">lambda</span> (tag)
                       (<span class="keyword">if</span> (&gt; (length tag) 0)
                           (<span class="keyword">if</span> (string-match (org-re <span class="string">"@</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[[:alnum:]_]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string">"</span>) tag)
                               (<span class="keyword">progn</span>
                                 <span class="comment-delimiter">;; </span><span class="comment">Not recognized context
</span>                                 (<span class="keyword">if</span> (null (assoc (match-string 1 tag) contexts))
                                     <span class="comment-delimiter">;; </span><span class="comment">Create it if it does not yet exist
</span>                                     (<span class="keyword">let</span> ((result
                                            (org-toodledo-call-method
                                             <span class="string">"addContext"</span>
                                             (list (cons <span class="string">"title"</span> (match-string 1 tag))))))
                                       (<span class="keyword">if</span> (eq (caar result) 'added)
                                           (setq org-toodledo-contexts
                                                 (cons (cons (match-string 1 tag)
                                                             (elt (car result) 2))
                                                       org-toodledo-contexts)
                                                 contexts org-toodledo-contexts))))
                                   <span class="comment-delimiter">;; </span><span class="comment">Get the ID of the context
</span>                                 (setq context
                                       (cdr (assoc (match-string 1 tag) contexts)))
                                 nil)
                             tag)))
                     (split-string tags <span class="string">":"</span>)))))
        (setq info
              (list
               (cons <span class="string">"id"</span> id)
               (cons <span class="string">"title"</span> title)
               (cons <span class="string">"length"</span> (org-entry-get (point) <span class="string">"Effort"</span>))
               (cons <span class="string">"context"</span> context)
               (cons <span class="string">"tag"</span> (mapconcat 'identity tags <span class="string">" "</span>))
               (cons <span class="string">"completed"</span> (<span class="keyword">if</span> (equal status <span class="string">"DONE"</span>) <span class="string">"1"</span> <span class="string">"0"</span>))
               (cons <span class="string">"status"</span>
                     (<span class="keyword">cond</span>
                      ((equal status <span class="string">"STARTED"</span>) <span class="string">"2"</span>)
                      ((equal status <span class="string">"DELEGATED"</span>) <span class="string">"4"</span>)
                      ((equal status <span class="string">"SOMEDAY"</span>) <span class="string">"8"</span>)
                      ((equal status <span class="string">"CANCELLED"</span>) <span class="string">"9"</span>)
                      ((equal status <span class="string">"PLAN"</span>) <span class="string">"3"</span>)
                      ((equal status <span class="string">"WAITING"</span>) <span class="string">"5"</span>)
                      ((equal status <span class="string">"TODO"</span>) <span class="string">"1"</span>)))
               (cons <span class="string">"priority"</span>
                     (<span class="keyword">cond</span>
                      ((equal priority <span class="string">"[#A]"</span>) <span class="string">"2"</span>)
                      ((equal priority <span class="string">"[#B]"</span>) <span class="string">"1"</span>)
                      ((equal priority <span class="string">"[#C]"</span>) <span class="string">"0"</span>)))
               (cons <span class="string">"note"</span>
                     (org-toodledo-entry-note))))
        (<span class="keyword">when</span> (org-entry-get nil <span class="string">"DEADLINE"</span>)
          (setq info (cons (cons <span class="string">"duedate"</span>
                                 (substring (org-entry-get nil <span class="string">"DEADLINE"</span>)
                                            0 10)) info)))
        (<span class="keyword">when</span> (org-entry-get nil <span class="string">"SCHEDULED"</span>)
          (setq info (cons (cons <span class="string">"startdate"</span>
                                 (substring (org-entry-get nil <span class="string">"SCHEDULED"</span>)
                                            0 10)) info)))
        info))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-sync</span> ()
  <span class="doc">"Synchronize all tasks."</span>
  <span class="comment-delimiter">;; </span><span class="comment">Retrieve all tasks
</span>  <span class="comment-delimiter">;; </span><span class="comment">For each task in the current buffer
</span>  <span class="comment-delimiter">;;   </span><span class="comment">Synchronize an existing task that has changed
</span>   (<span class="keyword">let</span> ((regexp (concat <span class="string">"^\\*+[ \t]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">"</span> org-todo-regexp <span class="string">"</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string">"</span>)))
    (goto-char (point-min))
    (<span class="keyword">while</span> (re-search-forward regexp nil t)
      (org-toodledo-sync-task))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-update</span> ()
  <span class="doc">"Insert new tasks and update previous tasks."</span>
  (interactive)
  (<span class="keyword">let*</span> ((server-info (org-toodledo-get-server-info))
         (account-info (org-toodledo-get-account-info))
         (changed (org-toodledo-account-changed account-info))
         (last-deleted (string-to-number (or (org-entry-get-with-inheritance <span class="string">"Last-deleted"</span>) <span class="string">"0"</span>)))
         (last-modified (string-to-number (or (org-entry-get-with-inheritance <span class="string">"Last-modified"</span>) <span class="string">"0"</span>)))
         (last-update (string-to-number (or (org-entry-get-with-inheritance <span class="string">"Last-sync"</span>) <span class="string">"0"</span>)))
         processed)
    <span class="comment-delimiter">;; </span><span class="comment">If tasks have been deleted or modified, then the Toodledo API
</span>    <span class="comment-delimiter">;; </span><span class="comment">will give us the timestamps. We need to find out which tasks
</span>    <span class="comment-delimiter">;; </span><span class="comment">have been deleted or modified since the last time we retrieved
</span>    <span class="comment-delimiter">;; </span><span class="comment">the list of tasks that have been deleted or modified. We store
</span>    <span class="comment-delimiter">;; </span><span class="comment">the last times in the properties of the root element.
</span>
    (<span class="keyword">if</span> (and (assoc <span class="string">"deleted"</span> changed) <span class="comment-delimiter">;; </span><span class="comment">Tasks have been deleted
</span>             (&gt;= (string-to-number (cdr (assoc <span class="string">"deleted"</span> changed))) last-deleted))
        (setq processed
              (append (org-toodledo-process-deleted-tasks
                       last-deleted)
                       processed)))
    (<span class="keyword">if</span> (and (assoc <span class="string">"modified"</span> changed) <span class="comment-delimiter">;; </span><span class="comment">Tasks have been added or edited
</span>             (&gt;= (string-to-number (cdr (assoc <span class="string">"modified"</span> changed)))
                last-modified))
        <span class="comment-delimiter">;; </span><span class="comment">Retrieve added/modified tasks
</span>        (setq processed (append
                         (org-toodledo-process-modified-tasks last-modified) processed)))
    <span class="comment-delimiter">;; </span><span class="comment">TODO Look for tasks that were modified locally since the last synchronization
</span>    (org-toodledo-process-locally-modified-tasks last-update processed)
    <span class="comment-delimiter">;; </span><span class="comment">TODO Update timestamps here
</span>    (goto-char (point-min))
    (<span class="keyword">when</span> (re-search-forward (concat <span class="string">"^</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">"</span> outline-regexp <span class="string">"</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string">"</span>) nil t)
      (org-entry-put (point)
                     <span class="string">"Last-sync"</span>
                     (cdr (assoc <span class="string">"unixtime"</span> server-info)))
      (<span class="keyword">when</span> (assoc <span class="string">"lastaddedit"</span> account-info)
        (org-entry-put (point)
                       <span class="string">"Last-modified"</span>
                       (cdr
                        (assoc <span class="string">"lastaddedit"</span> account-info))))
      (<span class="keyword">when</span> (assoc <span class="string">"lastdelete"</span> account-info)
        (org-entry-put (point)
                         <span class="string">"Last-deleted"</span>
                         (cdr
                          (assoc <span class="string">"lastdelete"</span> account-info)))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-process-locally-modified-tasks</span> (last-update processed)
  <span class="doc">"Synchronize tasks that were locally modified after LAST-UPDATE.
Skip tasks with IDs in PROCESSED."</span>
  (goto-char (point-min))
  (<span class="keyword">let</span> ((start (float-time (current-time))))
    (<span class="keyword">while</span> (re-search-forward org-complex-heading-regexp nil t)
      <span class="comment-delimiter">;; </span><span class="comment">Look for all tasks in this buffer
</span>      (<span class="keyword">if</span> (match-string 2)
          <span class="comment-delimiter">;; </span><span class="comment">Is it a new task, or has it been modified since the last update?
</span>          (<span class="keyword">let</span> ((id (org-entry-get (point) <span class="string">"Toodledo-ID"</span>))
                (modified (string-to-number (or (org-entry-get (point) <span class="string">"Modified"</span>) <span class="string">""</span>)))
                (last-sync (<span class="keyword">if</span> (org-entry-get (point) <span class="string">"Sync"</span>)
                               (string-to-number (org-entry-get (point) <span class="string">"Sync"</span>))
                             0)))
            (<span class="keyword">if</span> (or (null id)
                    (and (&gt; modified last-sync)
                         (&lt; modified start)
                         (not (member id processed))))
                (<span class="keyword">save-excursion</span> (org-toodledo-sync-task))))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-touch</span> ()
  <span class="doc">"Update the current task."</span>
  (interactive)
  (org-entry-put (point) <span class="string">"Modified"</span> (format <span class="string">"%d"</span> (float-time (current-time)))))

(<span class="keyword">defvar</span> <span class="variable-name">org-toodledo-actually-delete</span> t)
(<span class="keyword">defun</span> <span class="function-name">org-toodledo-process-deleted-tasks</span> (timestamp)
  <span class="doc">"Remove tasks deleted after TIMESTAMP."</span>
  (delq nil
        (mapcar
         (<span class="keyword">lambda</span> (task)
           (<span class="keyword">when</span> (org-toodledo-find-task task)
             (<span class="keyword">if</span> org-toodledo-actually-delete
                 (delete-region (org-back-to-heading)
                                (<span class="keyword">if</span> (re-search-forward org-complex-heading-regexp nil t)
                                    (match-beginning 0)
                                  (org-end-of-subtree)))
               (org-entry-delete (point) <span class="string">"Toodledo-ID"</span>)
               (org-entry-put (point) <span class="string">"Toodledo-Deleted"</span> (timestamp)))
             (org-toodledo-task-id task)))
         (org-toodledo-get-deleted
          (list (cons <span class="string">"after"</span> (number-to-string timestamp)))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-process-modified-tasks</span> (modified)
  <span class="doc">"Handle all the tasks that have been modified since MODIFIED."</span>
  (delq nil
        (mapcar
         (<span class="keyword">lambda</span> (task)
           (<span class="keyword">if</span> (org-toodledo-find-task task)
               (<span class="keyword">if</span> (null (org-toodledo-update-task task modified))
                   (org-toodledo-task-id task))
             (org-toodledo-create-task task)))
         (org-toodledo-get-tasks (list (cons <span class="string">"modafter"</span> (number-to-string modified)))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-create-task</span> (task)
  <span class="doc">"Create a task for TASK."</span>
  (goto-char (point-max))
  (<span class="keyword">if</span> (point-at-eol) (insert <span class="string">"\n"</span>))
  (insert (org-toodledo-task-to-string task))
  (org-toodledo-task-id task))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-find-task</span> (task)
  <span class="doc">"Find the task specified by TASK."</span>
  (goto-char (point-min))
  (re-search-forward
   (concat <span class="string">"^[ \t]*:Toodledo-ID:[ \t]+"</span> (org-toodledo-task-id task) <span class="string">"$"</span>)
   nil t))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-account-changed</span> (account-info)
  <span class="doc">"Return non-nil if the account has changed since the last check.
The result will be an alist of (\"modified\" . \"timestamp\") if tasks have
been added/edited and (\"deleted\" . \"timestamp\") if tasks have been deleted."</span>
  (<span class="keyword">let</span> ((last-modified (org-entry-get-with-inheritance <span class="string">"Last-modified"</span>))
        (last-deleted (org-entry-get-with-inheritance <span class="string">"Last-deleted"</span>))
        result)
    (<span class="keyword">if</span> (&gt; (string-to-number (or (cdr (assoc <span class="string">"lastaddedit"</span> account-info)) <span class="string">"0"</span>))
           (string-to-number (or last-modified <span class="string">"0"</span>)))
        (add-to-list 'result (cons <span class="string">"modified"</span> last-modified)))
    (<span class="keyword">if</span> (&gt; (string-to-number (or (cdr (assoc <span class="string">"lastdelete"</span> account-info)) <span class="string">""</span>))
           (string-to-number (or last-deleted <span class="string">"0"</span>)))
        (add-to-list 'result (cons <span class="string">"deleted"</span> last-deleted)))
    result))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-sync-task</span> (<span class="type">&amp;optional</span> force)
  <span class="doc">"Update my Toodledo for the current task."</span>
  (interactive <span class="string">"P"</span>)
  (<span class="keyword">save-excursion</span>
    (<span class="keyword">let</span> ((task (org-toodledo-parse-current-task)))
      (<span class="keyword">if</span> (null (org-toodledo-task-id task))
          <span class="comment-delimiter">;; </span><span class="comment">New task, create it
</span>          (<span class="keyword">let</span> ((result (org-toodledo-add-task task)))
            (<span class="keyword">when</span> (eq (elt (car result) 0) 'added)
              (org-entry-put (point) <span class="string">"Toodledo-ID"</span> (elt (car result) 2))
              (org-entry-put (point) <span class="string">"Sync"</span>
                             (format <span class="string">"%d"</span> (float-time (current-time)) 1000))))
        <span class="comment-delimiter">;; </span><span class="comment">Old task, update
</span>        (<span class="keyword">when</span> (org-toodledo-success-p (org-toodledo-edit-task task))
          (<span class="keyword">if</span> (equal (org-toodledo-task-completed task) <span class="string">"1"</span>)
              (org-entry-put (point) <span class="string">"Completed"</span> <span class="string">"1"</span>)
            (org-entry-put (point) <span class="string">"Status"</span> (org-toodledo-task-status task)))
          (org-entry-put (point) <span class="string">"Sync"</span>
                         (format <span class="string">"%d"</span> (float-time (current-time)) 1000)))))))

<span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-format-date "2003-08-12") "&lt;2003-08-12 Tue&gt;"))
</span>(<span class="keyword">defun</span> <span class="function-name">org-toodledo-format-date</span> (date <span class="type">&amp;optional</span> repeat)
  <span class="doc">"Return yyyy-mm-dd day for DATE."</span>
  (concat
   <span class="string">"&lt;"</span>
   (format-time-string
    <span class="string">"%Y-%m-%d %a"</span>
    (<span class="keyword">cond</span>
     ((listp date) date)
     ((numberp date) (seconds-to-time date))
     ((and (stringp date)
           (string-match <span class="string">"^[0-9]+$"</span> date))
      (seconds-to-time (string-to-number date)))
     (t (apply 'encode-time (org-parse-time-string date)))))
   (<span class="keyword">if</span> repeat (concat <span class="string">" "</span> repeat) <span class="string">""</span>)
   <span class="string">"&gt;"</span>))

<span class="comment-delimiter">;; </span><span class="comment">(mapconcat 'org-toodledo-task-to-string temp "\n")
</span><span class="comment-delimiter">;; </span><span class="comment">(setq task (elt temp 2))
</span><span class="comment-delimiter">;; </span><span class="comment">(org-toodledo-task-to-string task)
</span>(<span class="keyword">defun</span> <span class="function-name">org-toodledo-task-to-string</span> (task <span class="type">&amp;optional</span> level)
  <span class="doc">"Return an Org-formatted version of TASK."</span>
  (<span class="keyword">let*</span> ((repeat (string-to-number (org-toodledo-task-repeat task)))
         (rep-advanced (org-toodledo-task-repeat-advanced task))
         (repeat-string (org-toodledo-repeat-to-string repeat rep-advanced))
         (priority (org-toodledo-task-priority task)))
    (concat
     (make-string (or level 2) ?*) <span class="string">" "</span>
     (org-toodledo-status-to-string task) <span class="string">" "</span>
     (<span class="keyword">cond</span>
      ((equal priority <span class="string">"-1"</span>) <span class="string">""</span>)
      ((equal priority <span class="string">"0"</span>) <span class="string">"[#C] "</span>)
      ((equal priority <span class="string">"1"</span>) <span class="string">"[#B] "</span>)
      ((equal priority <span class="string">"2"</span>) <span class="string">"[#A] "</span>)
      ((equal priority <span class="string">"3"</span>) <span class="string">"[#A] "</span>))
     (org-toodledo-task-title task)
     (<span class="keyword">if</span> (org-toodledo-task-context task)
         (concat <span class="string">" :@"</span> (org-toodledo-task-context task) <span class="string">":"</span>)
       <span class="string">""</span>)
     <span class="string">"\n"</span>
     (<span class="keyword">if</span> (and (org-toodledo-task-duedate task)
              (not (equal (org-toodledo-task-duedate task) <span class="string">""</span>))
              (not (&lt; (string-to-number (org-toodledo-task-duedate task)) 0)))
         (concat org-deadline-string <span class="string">" "</span>
                 (org-toodledo-format-date
                  (org-toodledo-task-duedate task)
                  repeat-string)
                 <span class="string">"\n"</span>)
       <span class="string">""</span>)
     (or (org-toodledo-task-note task) <span class="string">""</span>) <span class="string">"\n"</span>
     <span class="string">":PROPERTIES:\n"</span>
     <span class="string">":Toodledo-ID: "</span> (org-toodledo-task-id task) <span class="string">"\n"</span>
     <span class="string">":Modified: "</span> (org-toodledo-task-modified task) <span class="string">"\n"</span>
     <span class="string">":Sync: "</span> (format <span class="string">"%d"</span> (float-time (current-time))) <span class="string">"\n"</span>
     <span class="string">":Effort: "</span> (org-toodledo-task-length task) <span class="string">"\n"</span>
     <span class="string">":END:\n"</span>
     )))

<span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 0) ""))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 1) "+1w"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 2) "+1m"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 3) "+1y"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 4) "+1d"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 5) "+2w"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 6) "+2m"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 7) "+6m"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 8) "+3m"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 108) ".+3m"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 101) ".+1w"))
</span><span class="comment-delimiter">;; </span><span class="comment">(assert (equal (org-toodledo-repeat-to-string 0) ""))
</span>
(<span class="keyword">defconst</span> <span class="variable-name">org-toodledo-repeat-intervals</span> '(<span class="string">""</span> <span class="string">"+1w"</span> <span class="string">"+1m"</span> <span class="string">"+1y"</span> <span class="string">"+1d"</span> <span class="string">"+2w"</span> <span class="string">"+2m"</span> <span class="string">"+6m"</span> <span class="string">"+3m"</span>))
(<span class="keyword">defun</span> <span class="function-name">org-toodledo-status-to-string</span> (task)
  (<span class="keyword">let</span> ((comp (org-toodledo-task-completed task))
        (status (string-to-number (org-toodledo-task-status task))))
    (<span class="keyword">cond</span>
     ((not (or (null comp) (equal comp <span class="string">""</span>) (equal comp <span class="string">"0"</span>))) <span class="string">"DONE"</span>)
     ((= status 0) <span class="string">"TODO"</span>)
     ((= status 1) <span class="string">"TODO"</span>)
     ((= status 2) <span class="string">"STARTED"</span>)
     ((= status 3) <span class="string">"PLAN"</span>)
     ((= status 4) <span class="string">"DELEGATED"</span>)
     ((= status 5) <span class="string">"WAITING"</span>)
     ((= status 6) <span class="string">"PLAN"</span>)  <span class="comment-delimiter">; </span><span class="comment">hold
</span>     ((= status 7) <span class="string">"SOMEDAY"</span>)  <span class="comment-delimiter">; </span><span class="comment">postponed
</span>     ((= status 8) <span class="string">"SOMEDAY"</span>)
     ((= status 9) <span class="string">"CANCELLED"</span>)
     )))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-repeat-to-string</span> (repeat <span class="type">&amp;optional</span> rep-advanced)
  <span class="doc">"Turn TASK into a repeat sequence."</span>
  (<span class="keyword">cond</span>
   ((= repeat 0) nil)
   ((&gt; repeat 100) (concat <span class="string">"+"</span> (org-toodledo-repeat-to-string (mod repeat 100) rep-advanced)))
   ((and (= repeat 50) rep-advanced)
    (<span class="keyword">cond</span>
     ((string-match <span class="string">"Every </span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[0-9]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string"> week"</span> rep-advanced)
      (concat <span class="string">"+"</span> (match-string 1 rep-advanced) <span class="string">"w"</span>))
     ((string-match <span class="string">"Every </span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[0-9]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string"> month"</span> rep-advanced)
      (concat <span class="string">"+"</span> (match-string 1 rep-advanced) <span class="string">"m"</span>))
     ((string-match <span class="string">"Every </span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[0-9]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string"> year"</span> rep-advanced)
      (concat <span class="string">"+"</span> (match-string 1 rep-advanced) <span class="string">"y"</span>))
     ((string-match <span class="string">"Every </span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[0-9]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string"> day"</span> rep-advanced)
      (concat <span class="string">"+"</span> (match-string 1 rep-advanced) <span class="string">"d"</span>))
     (t rep-advanced)))
   (t (elt org-toodledo-repeat-intervals repeat))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-delete-current-task</span> ()
  <span class="doc">"Delete the current task."</span>
  (interactive)
  (org-back-to-heading t)
  (<span class="keyword">let</span> ((task (org-toodledo-parse-current-task)))
    (and (&gt; (length (org-toodledo-task-id task)) 0)
         (org-toodledo-success-p (org-toodledo-delete-task task)))
    (delete-region
     (point)
     (<span class="keyword">if</span> (and (end-of-line)
              (re-search-forward org-complex-heading-regexp nil t))
         (match-beginning 0)
       (org-end-of-subtree t t)
       (point)))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-task-get-prop</span> (task prop) (cdr (assoc prop task)))
(<span class="keyword">defmacro</span> <span class="function-name">org-toodledo-task-prop-defun</span> (field)
  `(<span class="keyword">defun</span> ,(intern (concat <span class="string">"org-toodledo-task-"</span> field)) (task)
     (cdr (assoc ,field task))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-success-p</span> (result)
  <span class="doc">"Return non-nil if RESULT indicates success."</span>
  (eq (car (car result)) 'success))

(org-toodledo-task-prop-defun <span class="string">"id"</span>)
(org-toodledo-task-prop-defun <span class="string">"title"</span>)
(org-toodledo-task-prop-defun <span class="string">"status"</span>)
(org-toodledo-task-prop-defun <span class="string">"completed"</span>)
(org-toodledo-task-prop-defun <span class="string">"repeat"</span>)
(org-toodledo-task-prop-defun <span class="string">"context"</span>)
(org-toodledo-task-prop-defun <span class="string">"duedate"</span>)
(org-toodledo-task-prop-defun <span class="string">"modified"</span>)
(org-toodledo-task-prop-defun <span class="string">"priority"</span>)
(org-toodledo-task-prop-defun <span class="string">"note"</span>)
(org-toodledo-task-prop-defun <span class="string">"length"</span>)
<span class="comment-delimiter">;; </span><span class="comment">defun'd separately because of the change in name
</span>(<span class="keyword">defun</span> <span class="function-name">org-toodledo-task-repeat-advanced</span> (task)
  (cdr (assoc <span class="string">"rep_advanced"</span> task)))

(<span class="keyword">defvar</span> <span class="variable-name">org-toodledo-contexts</span> nil <span class="doc">"An alist of (context . id)."</span>)
(<span class="keyword">defun</span> <span class="function-name">org-toodledo-get-contexts</span> (<span class="type">&amp;optional</span> force)
  <span class="doc">"Store an alist of (context . id) in `</span><span class="doc"><span class="constant">org-toodledo-contexts</span></span><span class="doc">'.
Reload if FORCE is non-nil."</span>
  (<span class="keyword">if</span> (or force (null org-toodledo-contexts))
      (setq org-toodledo-contexts
            (mapcar
             (<span class="keyword">lambda</span> (node)
               (cons
              (car (xml-node-children node))
              (xml-get-attribute node 'id)))
             (xml-get-children (car
                                (org-toodledo-call-method <span class="string">"getContexts"</span>)) 'context)))
    org-toodledo-contexts))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-agenda-touch</span> ()
  <span class="doc">"Update the Modified timestamp for the current entry in the agenda."</span>
  (org-agenda-check-type t 'agenda 'timeline)
  (org-agenda-check-no-diary)
  (<span class="keyword">let*</span> ((marker (or (org-get-at-bol 'org-marker)
                     (org-agenda-error)))
         (buffer (marker-buffer marker))
         (pos (marker-position marker)))
    (org-with-remote-undo buffer
     (<span class="keyword">with-current-buffer</span> buffer
       (widen)
       (goto-char pos)
       (<span class="keyword">if</span> (org-entry-get (point) <span class="string">"Modified"</span>)
           (org-entry-put (point) <span class="string">"Modified"</span> (format <span class="string">"%d"</span> (float-time (current-time)))))))))

(<span class="keyword">defun</span> <span class="function-name">org-toodledo-update-task</span> (task <span class="type">&amp;optional</span> last-update)
  (<span class="keyword">let*</span> ((modified (string-to-number (or (org-entry-get (point) <span class="string">"Modified"</span>) <span class="string">""</span>)))
         (last-sync (<span class="keyword">if</span> (org-entry-get (point) <span class="string">"Sync"</span>)
                        (string-to-number (org-entry-get (point) <span class="string">"Sync"</span>))
                      0))
         (level (car (org-heading-components)))
         (locally-modified (&gt; modified last-sync)))
    <span class="comment-delimiter">;; </span><span class="comment">Locally modified? keep
</span>    (<span class="keyword">if</span> locally-modified
        nil
      <span class="comment-delimiter">;; </span><span class="comment">Not locally modified? replace
</span>      <span class="comment-delimiter">;; </span><span class="comment">Figure out what our level is
</span>      (delete-region (org-back-to-heading)
                     (<span class="keyword">progn</span> (goto-char (match-end 0))
                            (<span class="keyword">if</span> (re-search-forward org-complex-heading-regexp nil t)
                                (goto-char (match-beginning 0))
                              (org-end-of-subtree))))
      (insert (org-toodledo-task-to-string task level))
      t)))

(<span class="keyword">provide</span> '<span class="constant">org-toodledo</span>)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2010/05/org-toodledo/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Dusting off my Emacs config files</title>
		<link>http://sachachua.com/blog/2010/03/dusting-off-my-emacs-config-files/</link>
		<comments>http://sachachua.com/blog/2010/03/dusting-off-my-emacs-config-files/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 12:00:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2010/03/dusting-off-my-emacs-config-files/</guid>
		<description><![CDATA[In June 2009, I switched hats and started facilitating workshops instead of developing Drupal code. Organizing resources and learning more about consulting kept me in a mostly-Windows environment with little reason to delve into the mysteries of the Emacs text editor, although I occasionally used Emacs to automate repetitive editing tasks. Even though I haven’t [...]]]></description>
			<content:encoded><![CDATA[<p>In June 2009, I <a href="http://sachachua.com/wp/2009/06/changing-hats/">switched hats</a> and started facilitating workshops instead of developing Drupal code. Organizing resources and learning more about consulting kept me in a mostly-Windows environment with little reason to delve into the mysteries of the Emacs text editor, although I occasionally used Emacs to <a href="http://sachachua.com/wp/2009/11/automating-tedious-wiki-editing-tasks-with-emacs-and-w3m">automate repetitive editing tasks</a>.</p>
<p>Even though I haven’t played around with Emacs for a while, the text editor—and the community around it—hasn’t let go of me yet. My Emacs-related posts remain among the most popular on my blog, and people often write to me to ask questions or thank me for inspiration. I still keep <a href="http://planet.emacsen.org">Planet Emacsen</a> in my feed reader, and I occasionally drop by the <a href="http://emacswiki.org">Emacs Wiki</a>.</p>
<p>I miss working on Wicked Cool Emacs. I’ve turned it over to <a href="http://twitter.com/IEURE">Ian Eure</a>, whom I assume is persistently plugging away at it (although he might also have gotten distracted by real life as well&#8211;totally understandable). Now that I’m no longer distracted by the fun of tweaking Drupal (almost as flexible as Emacs, thanks to its hook system), Emacs calls.</p>
<p>I miss geeking out in #emacs on irc.freenode.net, helping people with questions on mailing lists, and playing with ideas that can be translated into code.</p>
<p>So I’m dusting off my configuration files and my notes, writing an org-toodledo module that pulls in my tasks from the web-based Toodledo task management system I’ve been using, and developing a rudimentary synchronization system that will help me make the transition.</p>
<p>(message &#8220;Hello, world!&#8221;)</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2010/03/dusting-off-my-emacs-config-files/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Automation</title>
		<link>http://sachachua.com/blog/2010/03/automation/</link>
		<comments>http://sachachua.com/blog/2010/03/automation/#comments</comments>
		<pubDate>Sat, 27 Mar 2010 13:00:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[autohotkey]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2010/03/automation/</guid>
		<description><![CDATA[A repetitive task is an excuse to learn more about automation tools. My text automation tool of choice is Emacs, a ridiculously programmable text editor. When it comes to numbers, I can do a lot with equations, pivot tables / data pilot, and judicious use of macros. For general automation on Windows, there’s AutoHotkey. My [...]]]></description>
			<content:encoded><![CDATA[<p>A repetitive task is an excuse to learn more about automation tools. My text automation tool of choice is <a href="http://en.wikipedia.org/wiki/Emacs">Emacs</a>, a ridiculously programmable text editor. When it comes to numbers, I can do a lot with equations, pivot tables / data pilot, and judicious use of macros. For general automation on Windows, there’s <a href="http://autohotkey.com">AutoHotkey</a>.</p>
<p>My threshold for automation is lower than most people’s. When faced with a repetitive task that will take me an afternoon to do, I’ll spend maybe half an hour understanding the task, an hour figuring out how to do it using the tools, and another half hour to an hour completing it with automation’s help. I spend the extra time learning more about the automation tools or sharing what I’ve done.</p>
<p>Even if the time savings are probably not going to be significant, if the task is sufficiently repetitive, I’ll go for the intellectual thrill of automating it. Bonus: if I have to do the task again (which occasionally happens), I have my process all ready to go.</p>
<p>What would it take for people to learn how to automate more things? How do I do it? How did I pick up this habit?</p>
<p>Part of it is knowing the capabilities of a tool. I know that I can simulate mouse clicks and keypresses, so when I catch myself repeating certain motions, I think about how I can automate that.</p>
<p>Part of it is being able to abstract the steps in a procedure. I can figure out what can be easily automated and what needs manual intervention. If I can automate 80% of something, that’s usually enough.</p>
<p>Part of it is being able to program and not being afraid of geeky interfaces.</p>
<p>Part of it is asking if the time-intensive parts of the procedure are really necessary. (Sometimes they’re not.)</p>
<p>I like automation. I <a href="http://sachachua.com/wp/2008/12/why-automation-matters-to-me/">wish more people were comfortable doing it</a>.</p>
<p>Want to get started? The best way is probably to pick a tool depending on what you spend most of your time doing, learning lots about your tool, and doing little experiments. For Emacs, it might be learning how to use keyboard macros, then using Lisp. For Excel, learn different functions (I use CONCATENATE and IF a lot). For AutoHotkey, try using it for abbreviations, then expand.</p>
<p>Have fun and save time!</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2010/03/automation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Behind the scenes: Livin&#8217; la Vida Emacs</title>
		<link>http://sachachua.com/blog/2009/12/behind-the-scenes-livin-la-vida-emacs/</link>
		<comments>http://sachachua.com/blog/2009/12/behind-the-scenes-livin-la-vida-emacs/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 13:37:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[democamp]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[emacspeak]]></category>
		<category><![CDATA[geek]]></category>
		<category><![CDATA[presentation]]></category>
		<category><![CDATA[speaking]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2009/12/07/behind-the-scenes-livin-la-vida-emacs/</guid>
		<description><![CDATA[In October 2006, I gave a short presentation on Livin’ la Vida Emacs (or the Emacs editor as a way of life). It was well-received—in fact, so well-received that folks in the audience anticipated my punchlines and I ended up shifting parts of my talk around. ;) People said: Last, and certainly not least was [...]]]></description>
			<content:encoded><![CDATA[<p>In October 2006, I gave a short presentation on Livin’ la Vida Emacs (or the Emacs editor as a way of life). It was well-received—in fact, so well-received that folks in the audience anticipated my punchlines and I ended up shifting parts of my talk around. ;)</p>
<p><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/eoyi2vrsWow&amp;hl=en_US&amp;fs=1&amp;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/eoyi2vrsWow&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></p>
<p>People said:</p>
<blockquote><p>Last, and certainly not least was Sacha Chua. If we could harness it I&#8217;m pretty sure we could power a few small cities of the energy that&#8217;s contained in this one, tiny person &#8211; especially when you get her talking about Emacs. Sacha&#8217;s demo, entitled, &#8220;Livin&#8217; la vida Emacs&#8221; was hands down the most entertaining of the evening. Sacha has basically taken this simple, extendable text editor and pushed it about as far as it can go &#8211; at DemoCamp10 she pulled back the curtain and showed us all her little systems and apps she&#8217;s created in it. I like my GUI/Windows so the whole text-based thing isn&#8217;t for me but it certainly was interesting to see just how strung out she&#8217;s got that machine.</p>
</blockquote>
<p>Ryan Coleman</p>
<blockquote><p>Sacha Chua showed off what can be done in the scriptable environment, in this case emacs, as she went from Text editor, to a.i. doctor, to game engine to task / email organizer and beyond. Sacha was six feet tall on that stage, even though she did not actual levitate at anytime (although she came close, as always). A Tour de force of the Emacs, a text editing tool built in a interpreted lisp language environment, bascially a personalized productivity platform which allows for massive customization. Sacha had the crowd entertained and enthralled. (Sacha blogged her own impressions and mentions that Emacs was speaking to her!)</p>
</blockquote>
<p>Ian Irving</p>
<blockquote><p>Within Toronto’s Web community, Sacha Chua has become one of the leading “personalities”. Armed with infectious enthusiasm, charm and smarts, she would be an excellent person to hire once she graduates from UoT. The key question is how best to use her talents. It would probably be as a customer service rep, who can come into a bad situation and get everyone happy by the time she leaves. If I was an HR person from Microsoft, IBM, etc. I’d be knocking on Sacha’s door ASAP.</p>
</blockquote>
<p>Mark Evans, consultant</p>
<p>Why? Probably because in addition to my natural sugar high, I was also presenting something that was awesomely, arcanely geeky. =)</p>
<p>I hadn’t figured out how to make a screencast on Linux, so the actual display on my screen is lost to time. I do, however, have the source code that I used to step through my speech&#8211;including speaking cues, as I didn’t want to mess up the presentation in front of Toronto’s technorati! So here’s the <a href="http://sachachua.com/notebook/emacs/democamp.el">source code</a>, for your amusement. I set up two of my function keys to go forward and backward, executing the parenthesized expressions. I also set up a hidden window that could handle my speaking cues so that Emacs could remind me what the next &#8220;slide&#8221; was before I transitioned to it, for smoother flow. <code>emacspeak-speak-buffer</code> is a function that reads the current window&#8217;s text using the Emacspeak speech interface with the Festival synthesizer I&#8217;d configured. At some point, I switched to a more interactive demo, but I still had the speaking cues remind me of the sequence. So yes, Emacs really was telling me what to say. ;) </p>
<pre class="code"><span class="comment-delimiter">;; </span><span class="comment">Emacs as a text editor
</span>
(<span class="keyword">progn</span> <span class="comment-delimiter">;; </span><span class="comment">Setup
</span>  (<span class="keyword">defvar</span> <span class="variable-name">democamp/presentation-file</span> <span class="string">"~/democamp.el"</span>)
  (<span class="keyword">defvar</span> <span class="variable-name">democamp/cue-buffer</span> <span class="string">"*DemoCamp*"</span>)
  (<span class="keyword">defun</span> <span class="function-name">democamp/next</span> ()
    (interactive)
    (<span class="keyword">let</span> (start sexp)
      (<span class="keyword">with-current-buffer</span> (find-file-noselect democamp/presentation-file)
        (setq start (point))
        (forward-sexp)
        (setq sexp (buffer-substring-no-properties start (point))))
      (eval (read sexp))))

  (<span class="keyword">defun</span> <span class="function-name">democamp/previous</span> ()
    (interactive)
    (<span class="keyword">let</span> (start sexp)
      (<span class="keyword">with-current-buffer</span> (find-file-noselect democamp/presentation-file)
        (setq start (point))
        (backward-sexp)
        (setq sexp (buffer-substring-no-properties (point) start)))
      (eval (read sexp))))

  (<span class="keyword">defun</span> <span class="function-name">democamp/repeat</span> ()
    (interactive)
    (<span class="keyword">let</span> (start sexp)
      (<span class="keyword">with-current-buffer</span> (find-file-noselect democamp/presentation-file)
        (setq start (point))
        (backward-sexp)
        (setq sexp (buffer-substring (point) start))
        (forward-sexp))
      (eval (read sexp))))

  (<span class="keyword">defun</span> <span class="function-name">democamp/say</span> (text)
    (<span class="keyword">with-current-buffer</span> (get-buffer-create democamp/cue-buffer)
      (erase-buffer)
      (insert text)
      (goto-char (point-min))
      (call-interactively 'emacspeak-speak-buffer)))
  (global-set-key (kbd <span class="string">"&lt;f12&gt;"</span>) 'democamp/next)
  (global-set-key (kbd <span class="string">"S-&lt;f12&gt;"</span>) 'democamp/previous)
  (global-set-key (kbd <span class="string">"C-&lt;f12&gt;"</span>) 'democamp/repeat)
)

<span class="comment-delimiter">;; </span><span class="comment">Window register a
</span>(<span class="keyword">progn</span>
  (menu-bar-mode 1)
  (tool-bar-mode 1)
  (set-face-attribute 'default nil <span class="builtin">:height</span> 200)
  (find-file <span class="string">"~/hello.txt"</span>)
  (democamp/say <span class="string">"I don't know why people are so intimidated by Emacs. It's a text editor. It has menus and toolbars and everything."</span>)
  (frame-configuration-to-register ?a)) 

(<span class="keyword">progn</span>
  (democamp/say <span class="string">"Spell-check, too."</span>)
  (load-library <span class="string">"flyspell"</span>)
  (load-library <span class="string">"emacspeak-flyspell"</span>)
  (setq emacspeak-flyspell-highlight-personality nil)
  (flyspell-mode 1))

<span class="comment-delimiter">;; </span><span class="comment">Emacs learning curve
</span><span class="comment-delimiter">;; </span><span class="comment">Frame configuration b
</span>(<span class="keyword">progn</span>
  (find-file <span class="string">"~/tmp/learningcurves.jpg"</span>)
  (democamp/say <span class="string">"So why are people terrified of Emacs?"</span>))

(<span class="keyword">progn</span>
  (set-face-attribute 'default nil <span class="builtin">:height</span> 700)
  (delete-other-windows)
  (sit-for 1)
  (animate-sequence (list <span class="string">"Livin' la Vida Emacs"</span> <span class="string">"DemoCamp10"</span> <span class="string">"Sacha Chua"</span> <span class="string">"Oct 23, 2006"</span>) 1))

(<span class="keyword">progn</span>
  (set-face-attribute 'default nil <span class="builtin">:height</span> 200)
  (democamp/say <span class="string">"Emacs comes with a psychotherapist."</span>)
  (doctor))
(<span class="keyword">progn</span>
  (democamp/say <span class="string">"an adventure game"</span>)
  (dunnet))
(<span class="keyword">progn</span>
  (democamp/say <span class="string">"random geek stuff"</span>)
  (hanoi 3))
(<span class="keyword">progn</span>
  (democamp/say <span class="string">"even Snake"</span>)
  (delete-other-windows)
  (snake))
(<span class="keyword">progn</span>
  (democamp/say <span class="string">"And of course, my favorite game, Nethack."</span>)
  (load-library <span class="string">"nethack-config"</span>)
  (nethack))
(democamp/say <span class="string">"Some of it is useful"</span>)
<span class="comment-delimiter">;; </span><span class="comment">Flashcard
</span>(<span class="keyword">progn</span>
  (democamp/say <span class="string">"Flashcards"</span>)
  (load-library <span class="string">"flashcard-config"</span>)
  (find-file <span class="string">"~/notebook/japan/japanese.deck"</span>)
  (goto-char (point-max)))
<span class="comment-delimiter">;; </span><span class="comment">With fortunes
</span>(<span class="keyword">progn</span>
  (democamp/say <span class="string">"And you can mash things together"</span>)
  (load-library <span class="string">"fortune"</span>)
  (setq fortune-file <span class="string">"/usr/share/games/fortunes/computers"</span>)
  (fortune)
  (delete-other-windows))
(<span class="keyword">progn</span>
  (find-file-other-window <span class="string">"~/notebook/japan/japanese.deck"</span>)
  (goto-char (point-max)))

<span class="comment-delimiter">;; </span><span class="comment">Planner
</span>(democamp/say <span class="string">"Now the wild stuff starts."</span>)
(<span class="keyword">progn</span>
  (planner-goto-today)
  (delete-other-windows)
  (democamp/say <span class="string">"Emacs: more than just an editor. It's a way of life."</span>))

<span class="comment-delimiter">;; </span><span class="comment">- Show tasks
</span>(democamp/say <span class="string">"I use Emacs to manage my tasks with Planner."</span>)
(democamp/say <span class="string">"I can even keep detailed time logs!"</span>)
(<span class="keyword">progn</span>
  (load-library <span class="string">"/usr/src/planner-el/planner-timeclock-summary.el"</span>)
  (planner-timeclock-summary-show (planner-today)))

<span class="comment-delimiter">;; </span><span class="comment">- Show notes
</span>(democamp/say <span class="string">"... blog..."</span>)
<span class="comment-delimiter">;; </span><span class="comment">- Jump to URL in w3m
</span>(democamp/say <span class="string">"... surf the Web (with tabbed browsing and a custom keymap!)"</span>)
33<span class="comment-delimiter">;; </span><span class="comment">- Jump to URL in Firefox
</span>(democamp/say <span class="string">"Although sometimes you really want Firefox."</span>)
<span class="comment-delimiter">;; </span><span class="comment">- Jump to task from mail message
</span>(democamp/say <span class="string">"I can hyperlink to pretty much anything."</span>)
(democamp/say <span class="string">"People's contact information"</span>)
(democamp/say <span class="string">"E-mail, etc."</span>)
(democamp/say <span class="string">"Which comes in handy when I need to create tasks from mail messages."</span>)
<span class="comment-delimiter">;; </span><span class="comment">Add note to BBDB from mail message
</span>(<span class="keyword">progn</span>
  (democamp/say <span class="string">"Keeping track of the number of recipients"</span>)
  (planner-visit-link <span class="string">"pos://~/.gnus#19820"</span>))
(democamp/say <span class="string">"BBDB indicators"</span>)

<span class="comment-delimiter">;; </span><span class="comment">Reply to message with nickname,
</span>(<span class="keyword">progn</span>
  (democamp/say <span class="string">"CRM in e-mail: Nicknames..."</span>)
  (planner-visit-link <span class="string">"pos://~/.gnus#16025"</span>))

<span class="comment-delimiter">;; </span><span class="comment">and show how it shows up in BBDB record and ping code
</span>(<span class="keyword">progn</span>
  (democamp/say <span class="string">"... notes..."</span>)
  (planner-visit-link <span class="string">"pos://~/.gnus#18804"</span>))

(<span class="keyword">progn</span>
  (democamp/say <span class="string">"... pings..."</span>)
  (planner-visit-link <span class="string">"pos://~/.gnus#16932"</span>))

(democamp/say <span class="string">"... filters..."</span>)
(democamp/say <span class="string">"So those are just a few examples of the crazy stuff you can do if you have an infinitely tweakable environment."</span>)
(democamp/say <span class="string">"Sacha, remember to plug in the speakers."</span>)
(democamp/say <span class="string">"Demo Campers, welcome to Emacs. What else do you want it to do?"</span>)
</pre>
<p>I love pushing the envelope. I love getting to know my tools inside and out (I enjoy reading source code!) and then making them do things other people can’t imagine. I love tweaking processes in real life, too, making them better and better every time. That’s just part of the way I <strike>work</strike> have fun. =)</p>
<p>How can you rock your next technical demo?</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/12/behind-the-scenes-livin-la-vida-emacs/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Automating tedious wiki editing tasks with Emacs and w3m</title>
		<link>http://sachachua.com/blog/2009/11/automating-tedious-wiki-editing-tasks-with-emacs-and-w3m/</link>
		<comments>http://sachachua.com/blog/2009/11/automating-tedious-wiki-editing-tasks-with-emacs-and-w3m/#comments</comments>
		<pubDate>Tue, 17 Nov 2009 19:24:31 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[geek]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2009/11/17/automating-tedious-wiki-editing-tasks-with-emacs-and-w3m/</guid>
		<description><![CDATA[I needed to update many of the links in our wiki because a team member left, so I had to reupload all of her files to a shared service and change all the URLs to point to the new files. Unfortunately, the file service didn’t send me the former URLs of the files, so that [...]]]></description>
			<content:encoded><![CDATA[<p>I needed to update many of the links in our wiki because a team member left, so I had to reupload all of her files to a shared service and change all the URLs to point to the new files. Unfortunately, the file service didn’t send me the former URLs of the files, so that was going to be a manual process. Our wiki had 149 pages in it. Not fun.</p>
<p>After a few pages of editing (and correcting the occasional typo that crept in as I changed URLs), I decided to partially automate the process. Using a smidgen of Emacs Lisp, I created a function that pasted text into a temporary buffer, performed whatever automatic fixes it could make, prompted me for any URLs it didn’t recognize, remembered the old URL – new URL mapping I defined, and copied the text back.</p>
<p>The function looked somewhat like this:</p>
<style type="text/css">
pre.code { background: black; color: white }
.code .comment { background: black; border: 0 }
      .comment {
        /* font-lock-comment-face */
        color: #ff7f24;
      }
      .comment-delimiter {
        /* font-lock-comment-delimiter-face */
        color: #ff7f24;
      }
      .doc {
        /* font-lock-doc-face */
        color: #ffa07a;
      }
      .function-name {
        /* font-lock-function-name-face */
        color: #87cefa;
      }
      .keyword {
        /* font-lock-keyword-face */
        color: #00ffff;
      }
      .negation-char {
      }
      .regexp-grouping-backslash {
        /* font-lock-regexp-grouping-backslash */
        font-weight: bold;
      }
      .regexp-grouping-construct {
        /* font-lock-regexp-grouping-construct */
        font-weight: bold;
      }
      .string {
        /* font-lock-string-face */
        color: #ffa07a;
      }
      .variable-name {
        /* font-lock-variable-name-face */
        color: #eedd82;
      }
</style>
<pre class="code">(<span class="keyword">defvar</span> <span class="variable-name">sacha/wiki-links</span> nil <span class="doc">"Associative list of (old-url . new-url)."</span>)
(<span class="keyword">defun</span> <span class="function-name">sacha/wiki-fix</span> ()
  (interactive)
  (<span class="keyword">with-temp-buffer</span>
    <span class="comment-delimiter">;; </span><span class="comment">Insert text from clipboard
</span>    (yank)
    (goto-char (point-min))
    <span class="comment-delimiter">;; </span><span class="comment">Look for all the links
</span>    (<span class="keyword">while</span> (re-search-forward
            <span class="string">"\\[</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[</span><span class="string"><span class="negation-char">^</span></span><span class="string">|]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string">|</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">(</span></span><span class="string">[</span><span class="string"><span class="negation-char">^</span></span><span class="string">\]]+</span><span class="string"><span class="regexp-grouping-backslash">\\</span></span><span class="string"><span class="regexp-grouping-construct">)</span></span><span class="string">\\]"</span> nil t)
      <span class="comment-delimiter">;; </span><span class="comment">Check if it's one of the links I want to replace
</span>      (<span class="keyword">if</span> (or (string-match-p <span class="string">"viewpage"</span> (match-string 2))
              (string-match-p <span class="string">"lsoohoo"</span> (match-string 2)))
          (replace-match
           (<span class="keyword">save-match-data</span>
             <span class="comment-delimiter">;; </span><span class="comment">Prompt and the entry to the map if it does not yet exist
</span>             (<span class="keyword">unless</span> (assoc (match-string 2) sacha/wiki-links)
               (add-to-list 'sacha/wiki-links
                            (cons (match-string 2)
                                  (read-string (concat (match-string 1)
                                                       <span class="string">"? "</span>)))))
             <span class="comment-delimiter">;; </span><span class="comment">pick up the corresponding URL
</span>             (cdr (assoc (match-string 2) sacha/wiki-links)))
           t t nil 2)))
    <span class="comment-delimiter">;; </span><span class="comment">Copy the text into the clipboard
</span>    (kill-new (buffer-string))))
</pre>
<p>I used <code>M-x global-set-key</code> to bind a convenient function key to it (F12, I think), and then it was just a matter of clicking on each page, clicking on Edit, typing Ctrl-C to copy the text, switching to Emacs, pressing F12, switching back to my browser, typing Ctrl-V, and saving the wiki page. I also added some lines (not shown here) to convert the previous wiki gardener&#8217;s full links to intrawiki links, change server URLs, and do other fun things.</p>
<p>I thought about fully automating it (somehow hooking into w3, perhaps?), but that seemed to be more trouble than needed. Besides, it was good to review all the pages.</p>
<p>As a result of this Emacs wizardry, processing all 149 wiki pages took me a few hours instead of a few days. Yay!</p>
<p>Of course, I finished the last wiki page, I found out that I needed to change the servers in the URL. I decided to go ahead and fully automate the darn thing.</p>
<p>I extracted a list of URLs for the wiki by viewing the tree version of the wiki index. It used Javascript, so I couldn&#8217;t just pull the URLs out of the source code. Fortunately, the Firebug plugin for Firefox lets me copy the rendered HTML, so I used that instead. Some judicious text-editing later (<code>replace-regexp</code> rocks), I had a list of URLs to the different pages. I knew I needed to put in some kind of delay when loading web pages. <code>sleep-for</code> let me spread out my requests so I didn&#8217;t hammer the server too badly. Reading the w3m.el source code turned up <code>w3m-async-exec</code>. Once I set that to nil, requesting web pages and running code on the results turned out to be straightforward. Selecting the right widgets was a bit of a hack (<code>re-search-forward</code> here, <code>w3m-previous-anchor</code> there), but hey, it worked. After confirming it by manually running it on a few pages, I left it merrily running in the background.</p>
<p>Here it is (some tweaking required):
<pre class="code">(<span class="keyword">defun</span> <span class="function-name">sacha/edit-wiki-page</span> ()
  (interactive)
  (<span class="keyword">let</span> ((buffer (current-buffer))
        (w3m-async-exec nil)
        (delay 5)) <span class="comment-delimiter">;; </span><span class="comment">number of seconds
</span>    <span class="comment-delimiter">;; </span><span class="comment">While not at the end of the buffer
</span>    (<span class="keyword">while</span> (not (eobp))
      <span class="comment-delimiter">;; </span><span class="comment">Load the URL on the current line
</span>      (w3m-browse-url
       (buffer-substring
        (line-beginning-position)
        (line-end-position)))
      <span class="comment-delimiter">;; </span><span class="comment">Look for the edit button
</span>      (goto-char (point-min))
      (<span class="keyword">when</span> (search-forward <span class="string">"Edit"</span> nil t)
        <span class="comment-delimiter">;; </span><span class="comment">Click it
</span>        (w3m-view-this-url)
        <span class="comment-delimiter">;; </span><span class="comment">Look for the Minor change checkbox
</span>        (goto-char (point-min))
        (<span class="keyword">when</span> (search-forward <span class="string">"Minor change"</span> nil t)
          <span class="comment-delimiter">;; </span><span class="comment">The text area is the second widget back
</span>          (w3m-previous-anchor 2)
          <span class="comment-delimiter">;; </span><span class="comment">Open the text area in a temporary buffer for editing
</span>          (w3m-view-this-url)
          <span class="comment-delimiter">;; </span><span class="comment">Do the changes
</span>          (<span class="keyword">while</span> (re-search-forward <span class="string">"https?://example.com/path"</span> nil t)
            (replace-match <span class="string">"http://path.example.com"</span> t t nil 0))
          <span class="comment-delimiter">;; </span><span class="comment">Save the value
</span>          (w3m-form-input-textarea-set)
          (<span class="keyword">when</span> (search-backward <span class="string">"Save"</span> nil t)
            (w3m-view-this-url))))
      (switch-to-buffer buffer)
      (forward-line)
      (sleep-for delay))))</pre>
<p>I’m sure this kind of automation might be possible with lots of hacking in Mozilla Firefox, and I’ve seen great scripts for the Mac, too. But I know Emacs, I’m comfortable digging into source code, and I can make things work.</p>
<p>Awesome. =D</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/11/automating-tedious-wiki-editing-tasks-with-emacs-and-w3m/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Thinking about Planner/EmacsWiki versus WordPress</title>
		<link>http://sachachua.com/blog/2009/11/thinking-about-planneremacswiki-versus-wordpress/</link>
		<comments>http://sachachua.com/blog/2009/11/thinking-about-planneremacswiki-versus-wordpress/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 12:08:00 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[blogging]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2009/11/03/thinking-about-planneremacswiki-versus-wordpress/</guid>
		<description><![CDATA[Was it really only less than two years ago that I shifted from my venerable Planner-based wiki/blog to my WordPress-powered one after experimenting with syndicating my entries into WordPress? I miss writing in wiki markup on Emacs and knowing that publishing would Just Work. I miss being able to dynamically expand entries from my address [...]]]></description>
			<content:encoded><![CDATA[<p>Was it really only less than two years ago that I shifted from my venerable <a href="http://sachachua.com/notebook/wiki/2008.01.13.php">Planner-based wiki/blog</a> to my <a href="http://sachachua.com/wp/">WordPress-powered one</a> after experimenting with <a href="http://sachachua.com/wp/2007/11/27/okay-were-in-business-5-years-of-my-life-in-wordpress/">syndicating my entries</a> into WordPress?</p>
<p>I miss writing in wiki markup on Emacs and knowing that publishing would Just Work. I miss being able to dynamically expand entries from my address book in a way that automatically links to people’s blogs. (Or Twitter accounts, if I were going to do this now.) ScribeFire is a pain on my Eee (needs more horizontal screen space), and I have a hard time marking up the occasional bit of HTML in weblogger.el. Windows Live Writer is pretty slick (particularly with the SnagIt Screen Capture plugin and the Amazon Book Linker), but I can live without it. Or maybe I can resurrect that WordPress Emacs client Ashish mentioned.</p>
<p>Let me think about the differences in experiences.</p>
<ul>
<li><strong>I wanted to support comments, but I didn’t want to spend a lot of time hacking on some custom commenting system.</strong> This was a big issue for me. I found some commenting scripts, but dealing with spam was a pain, so I switched to WordPress. If I switched back to Emacs for my blog, I’d probably use something like <a href="http://disqus.com">Disqus</a> to handle the conversation, and just find some way of backing up the comments regularly.</li>
<li><strong>I wanted to make it easy to navigate posts.</strong> I modified Planner to generate a more <a href="http://sachachua.com/notebook/wiki/WikiIndex.php">browsable blog index</a>, but it’s still not as slick as what you’d see with WordPress. On this WordPress blog, I like offering people random blog posts (good for me too – great way to rediscover old posts and make serendipitous connections!), related posts, and posts on the same day. I can do posts on the same day in Planner with a custom hook, but the others would require some hacking. Also, Planner is very much day-based, while WordPress lists N posts per page and has good category lists.</li>
<li><strong>I wanted to make it easy to edit posts.</strong> In my Emacs-based system, I published to RSS when I saved a note in the Remember window. I had a hack that made it possible to propagate changes from an already-published post to my WordPress blog, but it wasn’t completely reliable. </li>
<li><strong>Scheduling posts is handy, too. </strong>I hadn’t gotten around to figuring out how to build a post scheduler for Emacs. I suppose if I wasn’t picky about the time it went out, I could simply write posts on different days and then publish notes conditionally, plus have some kind of hook to notice if any of the current page’s posts should be published in the RSS feed, plus some way to handle previous days, plus maybe a server-friendly way to do this for the times when I’m not going to be online every day. Right.</li>
</ul>
<p>That said, I miss automatically sharing some details of what I’m working on (with details deleted before publishing so that they’re available offline), publicly crossing off tasks, and other cool things.</p>
<p>Planner’s model for task planning isn’t quite compatible with Org’s model, and I’ve been using Org + Toodledo more these days.</p>
<p>What am I really looking for here?</p>
<ul>
<li>A quick, reliable way to post from a text editor, so that I can do it from the Eee. <strong>Hmm, the WP Postie plugin will probably do the trick.</strong></li>
<li>Easy way to share/review tasks: Toodledo export of week’s tasks?</li>
<li>And maybe a custom plugin for weekly displays, org agendas, that sort of thing.</li>
</ul>
<p>Hmm….</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/11/thinking-about-planneremacswiki-versus-wordpress/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Thoughts on Toodledo versus Emacs Org</title>
		<link>http://sachachua.com/blog/2009/06/thoughts-on-toodledo-versus-emacs-org/</link>
		<comments>http://sachachua.com/blog/2009/06/thoughts-on-toodledo-versus-emacs-org/#comments</comments>
		<pubDate>Thu, 11 Jun 2009 01:44:53 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[highlight]]></category>
		<category><![CDATA[productivity]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2009/06/10/thoughts-on-toodledo-versus-emacs-org/</guid>
		<description><![CDATA[It&#8217;s been a couple of months since I switched to using the Toodledo instead of Emacs Org to manage my tasks. I decided to use Toodledo because I wanted a to-do list that I could access on any computer and on my iPod Touch, so that I could capture tasks from anywhere. I liked Toodledo [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been a couple of months since I switched to using the Toodledo instead of Emacs Org to manage my tasks. I decided to use Toodledo because I wanted a to-do list that I could access on any computer and on my iPod Touch, so that I could capture tasks from anywhere. I liked Toodledo more than I liked Remember the Milk because Toodledo&#8217;s tagging and time-tracking features fit the way I work. I also liked how I could ask my virtual assistant to fill in templates&#8211;for example, preparation tasks for upcoming presentations&#8211;based on certain dates.</p>
<p>I still like Toodledo, but I feel an unsatisfied itch. Toodle-do is a great Getting Things Done system for capturing and tracking tasks, but even though it has some support for defining short-, medium-, and long-term goals, I wasn&#8217;t using them because they were split up on multiple screens. I missed the outlines of Org where I could define my higher-level goals, easily add new ones, and create tasks and subtasks. I missed the close integration with my schedule and the ability to easily see my historical task information. I missed the distinction between scheduled dates and deadlines, and the calculation of days until something is due.</p>
<p>So I think it&#8217;s time to switch back, at least temporarily. Maybe I can find a way to seamlessly synchronize my Org files so that changes are reflected on the different computers I&#8217;m on, and maybe I can find a way to easily capture or review tasks while I&#8217;m on the go. Perhaps I&#8217;ll continue working on org-toodledo so that I can export tasks from Org to the web-based interface. That way, I can quickly review today&#8217;s tasks while I&#8217;m out and about.</p>
<p>Hmm&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/06/thoughts-on-toodledo-versus-emacs-org/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Nothing quite like Org for Emacs</title>
		<link>http://sachachua.com/blog/2009/04/nothing-quite-like-org-for-emacs/</link>
		<comments>http://sachachua.com/blog/2009/04/nothing-quite-like-org-for-emacs/#comments</comments>
		<pubDate>Mon, 06 Apr 2009 14:32:05 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[highlight]]></category>
		<category><![CDATA[productivity]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2009/04/06/nothing-quite-like-org-for-emacs/</guid>
		<description><![CDATA[I&#8217;ve been trying lots of different Web-based GTD task managers like Remember the Milk, Toodledo, and GTDAgenda. I&#8217;m slowly coming to the conclusion that there&#8217;s nothing quite like Org for Emacs. Here&#8217;s what I like about the other services: Both Remember the Milk and Toodledo have iPhone/iPod Touch interfaces, so I can review, check off, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been trying lots of different Web-based GTD task managers like <a href="http://www.rememberthemilk.com">Remember the Milk</a>, <a href="http://www.toodledo.com">Toodledo</a>, and <a href="http://www.gtdagenda.com">GTDAgenda</a>. I&#8217;m slowly coming to the conclusion that there&#8217;s nothing quite like <a href="http://orgmode.org/">Org for Emacs</a>. </p>
<p>Here&#8217;s what I like about the other services:
<ul>
<li>Both Remember the Milk and Toodledo have iPhone/iPod Touch interfaces, so I can review, check off, and create tasks anywhere.</li>
<li>All those services allow me to e-mail tasks to an address.</li>
<li>It&#8217;s easy to filter tasks by context.</li>
<li>I can share my tasks with my assistant so that he or she can remind me of the tasks I&#8217;ve scheduled for the day.</li>
</ul>
<p>Here&#8217;s what I like about Org:
<ul>
<li>I can use templates and outlines to organize my tasks however I want.</li>
<li>I can set deadlines, scheduled tasks, and prior notice easily.</li>
<li>I can track time more finely than Toodledo can.</li>
<li>I can use Org as an activity log.</li>
<li>I can schedule tasks onto specific timeslots.</li>
</ul>
<p>Of all the options I&#8217;ve tried, Toodledo is closest to where I am with Org, although it still doesn&#8217;t do everything. </p>
<p>Some options are:
<ul>
<li>Use worg or develop a web-based interface for displaying tasks based on my Org file</li>
<li>Write code for synchronizing Org with Toodledo or RememberTheMilk, making lots of geeks happy in the process =)</li>
</ul>
<div class="zemanta-pixie"><img class="zemanta-pixie-img" src="http://img.zemanta.com/pixy.gif?x-id=91fba46e-ed29-8637-a258-ee7f1942881a" /></div>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/04/nothing-quite-like-org-for-emacs/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>Planning meetings, get-togethers, and interviews with AgreeADate</title>
		<link>http://sachachua.com/blog/2009/02/planning-meetings-get-togethers-and-interviews-with-agreeadate/</link>
		<comments>http://sachachua.com/blog/2009/02/planning-meetings-get-togethers-and-interviews-with-agreeadate/#comments</comments>
		<pubDate>Tue, 10 Feb 2009 03:59:17 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[geek]]></category>
		<category><![CDATA[life]]></category>
		<category><![CDATA[networking]]></category>
		<category><![CDATA[productivity]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5723</guid>
		<description><![CDATA[One of the reasons why I love thinking of ways to help people rock even more is that I often end up learning a lot in the process. For example, last week, I needed to coordinate schedules with Katie Bartlett, Jeff Widman&#8217;s assistant, for a chat with Jeff about networking. I thought about how much [...]]]></description>
			<content:encoded><![CDATA[<p>One of the reasons why I love thinking of ways to help people rock even more is that I often end up learning a lot in the process.</p>
<p>For example, last week, I needed to<strong> coordinate schedules</strong> with Katie Bartlett, Jeff Widman&#8217;s assistant, for a chat with Jeff about networking. I thought about how much e-mail was sent back-and-forth figuring out <strong>times and timezones</strong>, and I multiplied that by the number of appointments Katie probably needed to arrange. <strong>That sounded like a lot of work.</strong> If I could find a tool to make that easier for our appointment, and she used that tool to organize lots of other appointments, then that would be great!</p>
<p>I checked out a couple of tools such as <a href="http://timetomeet.com">TimeToMeet</a>, <a href="http://scheduleonce.com">ScheduleOnce</a>, and <a href="http://www.agreeadate.com">AgreeADate</a>. Of the tools I tried, I liked AgreeADate the most because it made it easy to create different kinds of schedules and it supported timezones. I sent Katie a few potential timeslots for my chat with Jeff. After she replied, I confirmed the time that was mutually convenient. <strong>That wasn&#8217;t hard at all.</strong></p>
<p>Then I explored the other features. I found out that I could create a schedule where people could pick a slot. AgreeADate allowed me to specify <strong>how many people each timeslot was limited to, and how many slots people could choose.</strong> This opened up all sorts of possibilities, and has become my favorite feature!</p>
<p>Things that I&#8217;d formerly had been worried about scheduling became so much easier to schedule because I could create all the slots in parallel and then let people choose the slots that hadn&#8217;t been taken yet. For example, I&#8217;d been meaning to try outsourcing some technical and personal tasks, but <strong>scheduling interview times sounded like a hassle.</strong> With this system, I just had to <strong>set all the slots up and invite people to choose</strong> the timeslot most convenient for them. Yay!</p>
<p>AgreeADate isn&#8217;t perfect, but it makes scheduling things so much easier for me, and it <strong>lowers the barrier</strong> enough for me to go and make things happen. In the past week that I&#8217;ve been trying it out, I&#8217;ve:</p>
<ul>
<li>scheduled a capoeira trial</li>
<li>scheduled a conference call</li>
<li>set up interviews for a system administrator and a virtual assistant, and</li>
<li>set up lunch with a poll on where to eat.</li>
</ul>
<p>You know, stuff that would&#8217;ve taken way too much e-mail if I had to do it by hand. =)</p>
<p>I&#8217;d like to see it integrated with my Google Calendar, and one of the AgreeADate folks told me that that&#8217;s the next thing on their roadmap. I found myself wondering which slots I&#8217;d tentatively offered for other events, and being able to see all the tentative slots overlaid on my calendar would be fantastic.</p>
<p>In the meantime, I read the confirmation e-mails in Emacs Gnus, use Remember and Org to create an appointment hyperlinked to that mail, and then use my own code, the twit.el library, and the <a href="http://www.twittercal.com">Twittercal</a> service to create an appointment on my Google Calendar (on google.com), which is then shared with my Google Calendar on sachachua.com thanks to the joys of Google Apps. It&#8217;s quite Rube Goldberg-esque (as many of my bubblegum-and-string tricks are), but so far, it works. Maybe this&#8217;ll be something I&#8217;ll outsource (except perhaps without the Gnus + Remember + Org part) once I&#8217;ve gotten that virtual assistance thing sorted out. =)</p>
<p>For the geeks, some Emacs Lisp to mash together with Org, TwitterCal, Google Calendar, and twit.el (some wizardry required):</p>
<pre>(defun sacha/org-as-gcal-quick-add ()
  "Convert the current SCHEDULED: timestamp and header into a GCal quick add item."
  (save-excursion
    (org-back-to-heading t)
    (setq end (save-excursion (outline-next-heading) (point)))
    (when (re-search-forward org-scheduled-time-regexp
			     end t)
      (let (ts default-time stamp plain s0 srp s1 s2)
	(setq ts (match-string 1)
	      default-time (apply 'encode-time (org-parse-time-string ts)))
	(when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts))
		  (setq plain (string-match org-plain-time-of-day-regexp ts)))
	  (setq s0 (match-string 0 ts)
		srp (and stamp (match-end 3))
		s1 (match-string (if plain 1 2) ts)
		s2 (match-string (if plain 8 (if srp 4 6)) ts)))
	(cond
	 (s2
	  (concat (org-no-properties (org-get-heading t)) " on "
		  (format-time-string "%x" default-time) " from "
		  (org-get-time-of-day s1 'string t) " to "
		  (org-get-time-of-day s2 'string t)
		  ))
	 (s1

	  (concat (org-no-properties (org-get-heading t)) " on "
		  (format-time-string "%x" default-time) " from "
		  (org-get-time-of-day s1 'string t)
		  ))
	 (t
	  (concat (org-no-properties (org-get-heading t)) " on "
		  (format-time-string "%x" default-time)
		  )
	  ))))))

(defun sacha/org-to-twittercal (string)
  "Post STRING to TwitterCal. See http://www.twittercal.com for details."
  (interactive (list (read-string "Event: " (sacha/org-as-gcal-quick-add))))
  (twit-post-function twit-update-url (concat "d gcal " string)))</pre>
<p>I&#8217;ll keep you posted on my experiments! =)</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/02/planning-meetings-get-togethers-and-interviews-with-agreeadate/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Upcoming talk: Totally Rocking Your Drupal Development Environment</title>
		<link>http://sachachua.com/blog/2009/01/upcoming-talk-totally-rocking-your-drupal-development-environment/</link>
		<comments>http://sachachua.com/blog/2009/01/upcoming-talk-totally-rocking-your-drupal-development-environment/#comments</comments>
		<pubDate>Wed, 21 Jan 2009 20:08:23 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[drupal]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[talk]]></category>
		<category><![CDATA[abstract]]></category>
		<category><![CDATA[bio]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5649</guid>
		<description><![CDATA[On February 12, at 12 EST, I&#8217;ll be giving a teleconference presentation to the IBM Drupal Users Group. =) It&#8217;s internal-only, but I wanted to post it here because I often need to look up my abstracts and bios. The abstract is the same as the talk I submitted to DrupalCon09 (Totally Rocking Your Development [...]]]></description>
			<content:encoded><![CDATA[<p>On February 12, at 12 EST, I&#8217;ll be giving a teleconference presentation to the IBM Drupal Users Group. =) It&#8217;s internal-only, but I wanted to post it here because I often need to look up my abstracts and bios. The abstract is the same as the talk I submitted to <a href="http://dc2009.drupalcon.org/session/totally-rocking-your-development-environment">DrupalCon09 (Totally Rocking Your Development Environment)</a>, but I&#8217;ll add some more IBM-specific tips.</p>
<p><strong>Abstract:</strong></p>
<blockquote><p>Are you a lazy developer? If you aren&#8217;t, you should be! Find out about editor tricks that can save you hours and hours of effort and frustration. Learn about browser tips that make it easy to test your sites with different users, track down elusive bugs, and test. Develop the virtue of laziness by automating as much as you can with makefiles, the Drupal Shell, regression tests, and other goodies. Share your best tips during this interactive session. Use your new free time to rock even more!
</p></blockquote>
<p><strong>Bio</strong></p>
<p>
<blockquote>Sacha Chua has an unshakable belief that life is too short to waste doing repetitive tasks that can be automated, an irrational love for tweaking her development environment, and an irresistible urge to share whatever she&#8217;s learned along the way. To learn more about her and Web 2.0, Drupal, Emacs, and other things she&#8217;s interested, visit [INTERNAL IBM URL] or <a href="http://livinganawesomelife.com">http://LivingAnAwesomeLife.com</a> (personal blog).</p></blockquote>
<p>Inside IBM and want to get a copy of the calendar invite? Contact William Shaouy.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/01/upcoming-talk-totally-rocking-your-drupal-development-environment/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Emacs basics: Changing the background color</title>
		<link>http://sachachua.com/blog/2009/01/emacs-basics-changing-the-background-color/</link>
		<comments>http://sachachua.com/blog/2009/01/emacs-basics-changing-the-background-color/#comments</comments>
		<pubDate>Sun, 18 Jan 2009 21:22:09 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[wickedcoolemacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5635</guid>
		<description><![CDATA[One of the first things you&#8217;ll probably want to change about Emacs is the default face. This controls the foreground color, the background color, the font, and other attributes. For example, many graphical environments give Emacs a background of white. If you prefer a black background (which can be less tiring during long periods of [...]]]></description>
			<content:encoded><![CDATA[<p>One of the first things you&#8217;ll probably want to change about Emacs is the default face. This controls the foreground color, the background color, the font, and other attributes. For example, many graphical environments give Emacs a background of white. If you prefer a black background (which can be less tiring during long periods of use), you can change the background to black my customizing the default face.</p>
<p>To change the background color and other attributes, use the command <code>M-x customize-face</code>, and specify <code>default</code> as the face to customize. Change the attributes and the sample text will reflect your settings. When you are satisfied, select <b>Save for future sessions</b>. Your changes will be saved to ~/.emacs and reloaded the next time you start the editor.</p>
<hr size="1"/>
<p>This is a draft for <a href="http://sachachua.com/wp/category/wickedcoolemacs">Wicked Cool Emacs</a>, a book that will be published by <a href="http://www.nsp.com">No Starch Press</a>. (if we manage to get it all together! =)</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/01/emacs-basics-changing-the-background-color/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Emacs, file-cache, and ido</title>
		<link>http://sachachua.com/blog/2009/01/emacs-file-cache-and-ido/</link>
		<comments>http://sachachua.com/blog/2009/01/emacs-file-cache-and-ido/#comments</comments>
		<pubDate>Wed, 07 Jan 2009 00:56:12 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5572</guid>
		<description><![CDATA[(require 'filecache) (require 'ido) (defun file-cache-ido-find-file (file) "Using ido, interactively open file from file cache'. First select a file, matched using ido-switch-buffer against the contents in `file-cache-alist'. If the file exist in more than one directory, select directory. Lastly the file is opened." (interactive (list (file-cache-ido-read "File: " (mapcar (lambda (x) (car x)) file-cache-alist)))) (let* [...]]]></description>
			<content:encoded><![CDATA[<pre>
(require 'filecache)
(require 'ido)
(defun file-cache-ido-find-file (file)
  "Using ido, interactively open file from file cache'.
First select a file, matched using ido-switch-buffer against the contents
in `file-cache-alist'. If the file exist in more than one
directory, select directory. Lastly the file is opened."
  (interactive (list (file-cache-ido-read "File: "
                                          (mapcar
                                           (lambda (x)
                                             (car x))
                                           file-cache-alist))))
  (let* ((record (assoc file file-cache-alist)))
    (find-file
     (expand-file-name
      file
      (if (= (length record) 2)
          (car (cdr record))
        (file-cache-ido-read
         (format "Find %s in dir: " file) (cdr record)))))))

(defun file-cache-ido-read (prompt choices)
  (let ((ido-make-buffer-list-hook
	 (lambda ()
	   (setq ido-temp-list choices))))
    (ido-read-buffer prompt)))
(add-to-list 'file-cache-filter-regexps "docs/html")
(add-to-list 'file-cache-filter-regexps "\\.svn-base$")
(add-to-list 'file-cache-filter-regexps "\\.dump$")
(defmacro sacha/file-cache-setup-tree (prefix shortcut directories)
  "Set up the file-cache tree for PREFIX using the keyboard SHORTCUT.
DIRECTORIES should be a list of directory names."
  `(let ((file-cache-alist nil)
	 (directories ,directories))
     (file-cache-clear-cache)
     (while directories
       (file-cache-add-directory-using-find (car directories))
       (setq directories (cdr directories)))
     (setq ,(intern (concat "sacha/file-cache-" prefix "-alist")) file-cache-alist)
     (defun ,(intern (concat "sacha/file-cache-ido-find-" prefix)) ()
       (interactive)
       (let ((file-cache-alist ,(intern (concat "sacha/file-cache-" prefix "-alist"))))
	 (call-interactively 'file-cache-ido-find-file)))
     (global-set-key (kbd ,shortcut)
		     (quote ,(intern (concat "sacha/file-cache-ido-find-" prefix))))))
</pre>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2009/01/emacs-file-cache-and-ido/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Emacs: Working with multiple source trees</title>
		<link>http://sachachua.com/blog/2008/12/emacs-working-with-multiple-source-trees/</link>
		<comments>http://sachachua.com/blog/2008/12/emacs-working-with-multiple-source-trees/#comments</comments>
		<pubDate>Fri, 19 Dec 2008 20:00:33 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[elisp]]></category>
		<category><![CDATA[emacs-lisp]]></category>
		<category><![CDATA[file-cache]]></category>
		<category><![CDATA[ido]]></category>
		<category><![CDATA[macro]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5463</guid>
		<description><![CDATA[As a developer, I often find myself working with multiple trees of source code. Sometimes, I&#8217;m comparing the trunk and the branches. Sometimes, I&#8217;m copying ideas from one place to another. Sometimes, I&#8217;m working on one project and an urgent defect comes in for a different project. With good development environments such as Eclipse, I&#8217;d [...]]]></description>
			<content:encoded><![CDATA[<p>As a developer, I often find myself working with multiple trees of source code. Sometimes, I&#8217;m comparing the trunk and the branches. Sometimes, I&#8217;m copying ideas from one place to another. Sometimes, I&#8217;m working on one project and an urgent defect comes in for a different project. With good development environments such as Eclipse, I&#8217;d still need to click on the project or directory in order to change my context, and then use a keyboard shortcut to find the resource in that tree. </p>
<p>With an awesome development environment like my customized Emacs, however, I can easily juggle multiple source trees. Here&#8217;s the macro I wrote to make setting this up much easier:</p>
<pre>
(defmacro sacha/file-cache-setup-tree (prefix shortcut directories)
  "Set up the file-cache tree for PREFIX using the keyboard SHORTCUT.
DIRECTORIES should be a list of directory names."
  `(let ((file-cache-alist nil)
	 (directories ,directories))
     (file-cache-clear-cache)
     (while directories
       (file-cache-add-directory-using-find (car directories))
       (setq directories (cdr directories)))
     (setq ,(intern (concat "sacha/file-cache-" prefix "-alist")) file-cache-alist)
     (defun ,(intern (concat "sacha/file-cache-ido-find-" prefix)) ()
       (interactive)
       (let ((file-cache-alist ,(intern (concat "sacha/file-cache-" prefix "-alist"))))
	 (call-interactively 'file-cache-ido-find-file)))
     (global-set-key (kbd ,shortcut)
		     (quote ,(intern (concat "sacha/file-cache-ido-find-" prefix))))))
</pre>
<p>With that, I can use the following to map C-c p to finding files in my personal directories:</p>
<pre>
(sacha/file-cache-setup-tree
 "personal"
 "C-c p"
 '("/var/www/html/drupal"
   "~/elisp"
   "~/personal"))
</pre>
<p>I&#8217;ve also set up keyboard shortcuts for the other trees I&#8217;m working on.</p>
<p>You&#8217;ll need file-cache, ido, and probably something like this:</p>
<pre>
(require 'filecache)
(require 'ido)
(defun file-cache-ido-find-file (file)
  "Using ido, interactively open file from file cache'.
First select a file, matched using ido-switch-buffer against the contents
in `file-cache-alist'. If the file exist in more than one
directory, select directory. Lastly the file is opened."
  (interactive (list (file-cache-ido-read "File: "
                                          (mapcar
                                           (lambda (x)
                                             (car x))
                                           file-cache-alist))))
  (let* ((record (assoc file file-cache-alist)))
    (find-file
     (expand-file-name
      file
      (if (= (length record) 2)
          (car (cdr record))
        (file-cache-ido-read
         (format "Find %s in dir: " file) (cdr record)))))))

(defun file-cache-ido-read (prompt choices)
  (let ((ido-make-buffer-list-hook
	 (lambda ()
	   (setq ido-temp-list choices))))
    (ido-read-buffer prompt)))
(add-to-list 'file-cache-filter-regexps "docs/html")
(add-to-list 'file-cache-filter-regexps "\\.svn-base$")
(add-to-list 'file-cache-filter-regexps "\\.dump$")
</pre>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/12/emacs-working-with-multiple-source-trees/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Emacs, Org, and BBDB: Hyperlinking names to blogs</title>
		<link>http://sachachua.com/blog/2008/12/emacs-org-and-bbdb-hyperlinking-names-to-blogs/</link>
		<comments>http://sachachua.com/blog/2008/12/emacs-org-and-bbdb-hyperlinking-names-to-blogs/#comments</comments>
		<pubDate>Fri, 19 Dec 2008 02:48:44 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[bbdb]]></category>
		<category><![CDATA[emacs]]></category>
		<category><![CDATA[org]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5461</guid>
		<description><![CDATA[Back when I used Planner for Emacs, I coded some shortcuts to make it easier to write about the people I met and the conversations I had. I used the hippie-expand module to complete names from my Big Brother Database addressbook, and I wrote a function that converted those names into links to people&#8217;s blogs [...]]]></description>
			<content:encoded><![CDATA[<p>Back when I used Planner for Emacs, I coded some shortcuts to make it easier to write about the people I met and the conversations I had. I used the hippie-expand module to complete names from my Big Brother Database addressbook, and I wrote a function that converted those names into links to people&#8217;s blogs or websites whenever I published my blog posts as HTML.</p>
<p>I switched to WordPress for my blog because I got tired of trying to figure out a way to enable comments without getting mired in spam-fighting. That meant I could explore other Emacs personal information managers like Org, which I turned into my main task manager. I often used the WordPress interface to write blog posts. I sometimes used Windows Live Writer to write posts about books (there&#8217;s a good book review plugin that makes this easy). I also sometimes used Emacs and Org to draft blog posts using Org&#8217;s friendlier markup, exporting snippets to HTML that I then pasted into my blog posts.</p>
<p>Reading the posts on <a href="http://planet.emacsen.org">Planet Emacsen</a> reminded me that my customized configuration was pretty darn sweet. That and the conversation notes I&#8217;ve been blogging lately encouraged me to dust off my configuration files and get them to work under Org. So here&#8217;s the code:</p>
<pre>
 (defun sacha/org-bbdb-get (path)
   "Return BBDB record for PATH."
   (car (bbdb-search (bbdb-records) path path path)))

 (defun sacha/org-bbdb-export (path desc format)
   "Create the export version of a BBDB link specified by PATH or DESC.
 If exporting to HTML, it will be linked to the person's blog,
 www, or web address. If exporting to LaTeX FORMAT the link will be
 italicised. In all other cases, it is left unchanged."
     (cond
      ((eq format 'html)
       (let* ((record
             (sacha/org-bbdb-get path))
            url)
       (setq url (and record
                      (or (bbdb-record-getprop record 'blog)
                          (bbdb-record-getprop record 'www)
                          (bbdb-record-getprop record 'web))))
       (if url
           (format "&lt;a href=\"%s\"&gt;%s&lt;/a&gt;"
                   url (or desc path))
         (format "&lt;em&gt;%s&lt;/em&gt;"
                 (or desc path)))))
      ((eq format 'latex) (format "\\textit{%s}" (or desc path)))
      (t (or desc path))))

 (defadvice org-bbdb-export (around sacha activate)
   "Override org-bbdb-export."
   (setq ad-return-value (sacha/org-bbdb-export path desc format)))

 ;;;_+ Hippie expansion for BBDB; map M-/ to hippie-expand for most fun
 (add-to-list 'hippie-expand-try-functions-list 'sacha/try-expand-bbdb-annotation)
 (defun sacha/try-expand-bbdb-annotation (old)
   "Expand from BBDB. If OLD is non-nil, cycle through other possibilities."
   (unless old
     ;; First time, so search through BBDB records for the name
     (he-init-string (he-dabbrev-beg) (point))
     (when (&gt; (length he-search-string) 0)
       (setq he-expand-list nil)
       (mapcar
        (lambda (item)
        (let ((name (bbdb-record-name item)))
          (when name
            (setq he-expand-list
                  (cons (org-make-link-string
                       (org-make-link "bbdb:" name)
                       name)
                        he-expand-list)))))
        (bbdb-search (bbdb-records)
                     he-search-string
                     he-search-string
                     he-search-string
                     nil nil))))
   (while (and he-expand-list
               (or (not (car he-expand-list))
                   (he-string-member (car he-expand-list) he-tried-table t)))
     (setq he-expand-list (cdr he-expand-list)))
   (if (null he-expand-list)
       (progn
         (if old (he-reset-string))
         nil)
     (progn
       (he-substitute-string (car he-expand-list) t)
       (setq he-expand-list (cdr he-expand-list))
       t)))
</pre>
<p>If you&#8217;ve got Org and BBDB, drop this into your ~/.emacs and fiddle with it. =)</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/12/emacs-org-and-bbdb-hyperlinking-names-to-blogs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Emacs w3m: Open pages in external browsers</title>
		<link>http://sachachua.com/blog/2008/09/emacs-w3m-open-pages-in-external-browsers/</link>
		<comments>http://sachachua.com/blog/2008/09/emacs-w3m-open-pages-in-external-browsers/#comments</comments>
		<pubDate>Sat, 13 Sep 2008 13:26:51 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[wickedcoolemacs]]></category>
		<category><![CDATA[w3m]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5138</guid>
		<description><![CDATA[Sometimes w3m is not enough. To make it easier to open the current page in a browser such as Mozilla Firefox, add the following to your ~/.emacs: (defun wicked/w3m-open-current-page-in-firefox () "Open the current URL in Mozilla Firefox." (interactive) (browse-url-firefox w3m-current-url)) ;; (1) (defun wicked/w3m-open-link-or-image-in-firefox () "Open the current link or image in Firefox." (interactive) (browse-url-firefox [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes w3m is not enough. To make it easier to open the current page in a browser such as Mozilla Firefox, add the following to your <em>~/.emacs</em>:</p>
<pre>
(defun wicked/w3m-open-current-page-in-firefox ()
  "Open the current URL in Mozilla Firefox."
  (interactive)
  (browse-url-firefox w3m-current-url)) ;; (1)

(defun wicked/w3m-open-link-or-image-in-firefox ()
  "Open the current link or image in Firefox."
  (interactive)
  (browse-url-firefox (or (w3m-anchor) ;; (2)
                          (w3m-image)))) ;; (3)
</pre>
<p>This defines a function that uses the current URL being browsed(1) and another function that takes the URL of the link at point(2). If no link is found, it takes the URL of the image at point(3). </p>
<p>You can use other browse-url functions instead of <code>browse-url-firefox</code>. For example, replacing <code>browse-url-firefox</code> with <code>browse-url-kde</code> will open the page, link, or image in Konqueror, KDE&#8217;s web browser.</p>
<p>I like binding <kbd>f</kbd> to the function that opens the current URL in Mozilla Firefox and <kbd>F</kbd> to the function that opens the current link or image in Mozilla Firefox. To do the same, add the following to your <em>~/.emacs</em>:</p>
<pre>
(eval-after-load 'w3m
  (progn
    (define-key w3m-mode-map "f" 'wicked/w3m-open-current-page-in-firefox)
    (define-key w3m-mode-map "F" 'wicked/w3m-open-link-or-image-in-firefox)))
</pre>
<p>&#8212;<br />
This is part of the <a href="http://sachachua.com/wp/category/wickedcoolemacs">book that I&#8217;m writing about Emacs</a>, which will be published by No Starch Press if I manage to get it together in time.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/09/emacs-w3m-open-pages-in-external-browsers/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Emacs and w3m: Making tabbed browsing easier</title>
		<link>http://sachachua.com/blog/2008/09/emacs-and-w3m-making-tabbed-browsing-easier/</link>
		<comments>http://sachachua.com/blog/2008/09/emacs-and-w3m-making-tabbed-browsing-easier/#comments</comments>
		<pubDate>Fri, 12 Sep 2008 12:43:41 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[wickedcoolemacs]]></category>
		<category><![CDATA[w3m]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/09/12/emacs-and-w3m-making-tabbed-browsing-easier/</guid>
		<description><![CDATA[If you browse with a lot of open tabs, like I do, w3m will be much easier to use once you remap w3m-next-buffer and w3m-previous-buffer onto single-key shortcuts, allowing you to press a key to quickly flip between tabs. By default, w3m-previous-buffer is mapped to C-c C-p and w3m-next-buffer is mapped to C-c C-n. On [...]]]></description>
			<content:encoded><![CDATA[<p>If you browse with a lot of open tabs, like I do, w3m will be much easier to use once you remap <code>w3m-next-buffer</code> and <code>w3m-previous-buffer</code> onto single-key shortcuts, allowing you to press a key to quickly flip between tabs.</p>
<p>By default, <code>w3m-previous-buffer</code> is mapped to <kbd>C-c C-p</kbd> and <code>w3m-next-buffer</code> is mapped to <kbd>C-c C-n</kbd>. On a QWERTY keyboard, you may want to remap <code>w3m-previous-buffer</code> to <kbd>q</kbd> and <code>w3m-next-buffer</code> to <kbd>w</kbd>. You&#8217;ll probably also want to remap <code>w3m-close-window</code> (which had been bound to <kbd>q</kbd>), and <kbd>x</kbd> is a good keybinding for that. To make all these changes, add the following to your <em>~/.emacs</em>:</p>
<pre>
(eval-after-load 'w3m
  '(progn
     (define-key w3m-mode-map "q" 'w3m-previous-buffer)
     (define-key w3m-mode-map "w" 'w3m-next-buffer)
     (define-key w3m-mode-map "x" 'w3m-close-window)))
</pre>
<p>If you use a Dvorak keyboard layout, you can bind <kbd>.</kbd> to <code>w3m-previous-buffer</code> and <kbd>,</kbd> to <code>w3m-next-buffer</code> instead. Just add the following code to your <em>~/.emacs</em>:</p>
<pre>
(eval-after-load 'w3m
  '(progn
     (define-key w3m-mode-map "." 'w3m-previous-buffer)
     (define-key w3m-mode-map "," 'w3m-next-buffer)))
</pre>
<p>(This is part of the draft for my <a href="http://sachachua.com/wp/category/wickedcoolemacs">book on Emacs</a>, to be published by No Starch Press if I&#8217;m not too late.)</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/09/emacs-and-w3m-making-tabbed-browsing-easier/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Emacs: Jump to anything</title>
		<link>http://sachachua.com/blog/2008/09/emacs-jump-to-anything/</link>
		<comments>http://sachachua.com/blog/2008/09/emacs-jump-to-anything/#comments</comments>
		<pubDate>Fri, 05 Sep 2008 13:51:52 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[emacs]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/2008/09/05/emacs-jump-to-anything/</guid>
		<description><![CDATA[I just came across the Anything extension for Emacs. After you load anything.el, you can type M-x anything RET and a substring of, well, anything. Looks like a handy shortcut. It&#8217;s like Quicksilver, but for Emacs. Grab anything-config.el for a bunch of useful functions. Here&#8217;s my current configuration: (require 'anything) (require 'anything-config) (setq anything-sources (list [...]]]></description>
			<content:encoded><![CDATA[<p>I just came across the <a href="http://www.emacswiki.org/cgi-bin/wiki/Anything">Anything</a> extension for Emacs. After you load anything.el, you can type M-x anything RET and a substring of, well, anything. Looks like a handy shortcut. It&#8217;s like Quicksilver, but for Emacs. Grab <a href="http://www.emacswiki.org/cgi-bin/wiki/download/anything-config.el">anything-config.el</a> for a bunch of useful functions.</p>
<p>Here&#8217;s my current configuration:</p>
<pre>
(require 'anything)
(require 'anything-config)
(setq anything-sources
      (list anything-c-source-buffers
            anything-c-source-file-name-history
            anything-c-source-info-pages
            anything-c-source-man-pages
	    anything-c-source-file-cache
            anything-c-source-emacs-commands))
(global-set-key (kbd "M-X") 'anything)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/09/emacs-jump-to-anything/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Emacs and W3M: Toggling between work and the Web</title>
		<link>http://sachachua.com/blog/2008/08/emacs-and-w3m-toggling-between-work-and-the-web/</link>
		<comments>http://sachachua.com/blog/2008/08/emacs-and-w3m-toggling-between-work-and-the-web/#comments</comments>
		<pubDate>Wed, 27 Aug 2008 12:24:12 +0000</pubDate>
		<dc:creator>Sacha Chua</dc:creator>
				<category><![CDATA[emacs]]></category>
		<category><![CDATA[wickedcoolemacs]]></category>
		<category><![CDATA[w3m]]></category>

		<guid isPermaLink="false">http://sachachua.com/wp/?p=5088</guid>
		<description><![CDATA[Here&#8217;s a handy shortcut that toggles between the W3M web browser and other buffers you&#8217;re working on. I use it to quickly switch between code and documentation (or your favorite timewasting site, as it also makes a handy boss key). Define the function by adding the following code to your ~/.emacs: (defun wicked/toggle-w3m () "Switch [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a handy shortcut that toggles between the W3M web browser and other buffers you&#8217;re working on. I use it to quickly switch between code and documentation (or your favorite timewasting site, as it also makes a handy boss key).</p>
<p>Define the function by adding the following code to your ~/.emacs:</p>
<pre>
(defun wicked/toggle-w3m ()
  "Switch to a w3m buffer or return to the previous buffer."
  (interactive)
  (if (derived-mode-p 'w3m-mode)
      ;; Currently in a w3m buffer
      ;; Bury buffers until you reach a non-w3m one
      (while (derived-mode-p 'w3m-mode)
	(bury-buffer))
    ;; Not in w3m
    ;; Find the first w3m buffer
    (let ((list (buffer-list)))
      (while list
	(if (with-current-buffer (car list)
	      (derived-mode-p 'w3m-mode))
	    (progn
	      (switch-to-buffer (car list))
	      (setq list nil))
	  (setq list (cdr list))))
      (unless (derived-mode-p 'w3m-mode)
	(call-interactively 'w3m)))))
</pre>
<p>Then bind it to a shortcut key sequence (F7 F7 in this example) by adding the following code to your ~/.emacs:</p>
<pre>
(global-set-key (kbd "<f7> </f7><f7>") 'wicked/toggle-w3m)
</f7></pre>
<p>You can then use F7 F7 to switch back and forth between your web browser and whatever else you&#8217;re working on.</p>
]]></content:encoded>
			<wfw:commentRss>http://sachachua.com/blog/2008/08/emacs-and-w3m-toggling-between-work-and-the-web/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->

