Browse Source

Merge branch 'MobileOrg'

Carsten Dominik 15 năm trước cách đây
mục cha
commit
8c2a918fd9
8 tập tin đã thay đổi với 880 bổ sung139 xóa
  1. 2 0
      Makefile
  2. 6 0
      doc/ChangeLog
  3. 146 30
      doc/org.texi
  4. 24 0
      lisp/ChangeLog
  5. 254 82
      lisp/org-agenda.el
  6. 11 10
      lisp/org-id.el
  7. 406 0
      lisp/org-mobile.el
  8. 31 17
      lisp/org.el

+ 2 - 0
Makefile

@@ -91,6 +91,7 @@ LISPF      = 	org.el			\
 	     	org-macs.el		\
 		org-mew.el              \
 		org-mhe.el		\
+		org-mobile.el		\
 		org-mouse.el		\
 		org-publish.el		\
 		org-plot.el		\
@@ -362,6 +363,7 @@ lisp/org-mac-message.elc:	lisp/org.el
 lisp/org-macs.elc:
 lisp/org-mew.elc:	lisp/org.el
 lisp/org-mhe.elc:	lisp/org.el
+lisp/org-mobile.elc:	lisp/org.el
 lisp/org-mouse.elc:	lisp/org.el
 lisp/org-plot.elc:	lisp/org.el lisp/org-exp.el lisp/org-table.el
 lisp/org-publish.elc:

+ 6 - 0
doc/ChangeLog

@@ -1,3 +1,9 @@
+2009-09-15  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org.texi (MobileOrg): New section.
+	(Agenda commands, Exporting Agenda Views): Document exporting the
+	agenda view to Org files.
+
 2009-08-29  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* orgcard.tex: Document new effort setting commands.

+ 146 - 30
doc/org.texi

@@ -109,6 +109,7 @@ license to the document, as described in section 6 of the license.
 * Publishing::                  Create a web site of linked Org files
 * Miscellaneous::               All the rest which did not fit elsewhere
 * Hacking::                     How to hack your way around
+* MobileOrg::                   Viewing and capture on a mobile device
 * History and Acknowledgments::  How Org came into being
 * Main Index::                  An index of Org's concepts and features
 * Key Index::                   Key bindings and where they are described
@@ -258,7 +259,7 @@ Capture
 
 Remember
 
-* Setting up Remember for Org:: Some code for .emacs to get things going
+* Setting up Remember for Org::  Some code for .emacs to get things going
 * Remember templates::          Define the outline of different note types
 * Storing notes::               Directly get the note to where it belongs
 * Refiling notes::              Moving a note or task to a project
@@ -271,7 +272,7 @@ Agenda Views
 * Presentation and sorting::    How agenda items are prepared for display
 * Agenda commands::             Remote editing of Org trees
 * Custom agenda views::         Defining special searches and views
-* Exporting Agenda Views::
+* Exporting Agenda Views::      Writing a view to a file
 * Agenda column view::          Using column view for collected entries
 
 The built-in agenda views
@@ -420,6 +421,12 @@ Tables and lists in arbitrary syntax
 * Translator functions::        Copy and modify
 * Radio lists::                 Doing the same for lists
 
+MobileOrg
+
+* Setting up the staging area::  Where to interact with the mobile device
+* Pushing to MobileOrg::        Uploading Org files and agendas
+* Pulling from MobileOrg::      Integrating captured and flagged items
+
 @end detailmenu
 @end menu
 
@@ -5669,7 +5676,7 @@ with specific templates.  It also allows you to select the location where a
 note should be stored interactively, on the fly.
 
 @menu
-* Setting up Remember for Org:: Some code for .emacs to get things going
+* Setting up Remember for Org::  Some code for .emacs to get things going
 * Remember templates::          Define the outline of different note types
 * Storing notes::               Directly get the note to where it belongs
 * Refiling notes::              Moving a note or task to a project
@@ -6160,7 +6167,7 @@ window configuration is restored when the agenda exits:
 * Presentation and sorting::    How agenda items are prepared for display
 * Agenda commands::             Remote editing of Org trees
 * Custom agenda views::         Defining special searches and views
-* Exporting Agenda Views::
+* Exporting Agenda Views::      Writing a view to a file
 * Agenda column view::          Using column view for collected entries
 @end menu
 
@@ -7407,10 +7414,10 @@ This is a globally available command, and also available in the agenda menu.
 Write the agenda view to a file.  Depending on the extension of the selected
 file name, the view will be exported as HTML (extension @file{.html} or
 @file{.htm}), Postscript (extension @file{.ps}), PDF (extension @file{.pdf}),
-or plain text (any other extension).  When called with a @kbd{C-u} prefix
-argument, immediately open the newly created file.  Use the variable
-@code{org-agenda-exporter-settings} to set options for @file{ps-print} and
-for @file{htmlize} to be used during export.
+Org-mode (extension @file{.org}), and plain text (any other extension).  When
+called with a @kbd{C-u} prefix argument, immediately open the newly created
+file.  Use the variable @code{org-agenda-exporter-settings} to set options
+for @file{ps-print} and for @file{htmlize} to be used during export.
 
 @tsubheading{Quit and Exit}
 @kindex q
@@ -7634,13 +7641,13 @@ you want to do this only occasionally, use the command
 @cindex exporting agenda views
 @cindex agenda views, exporting
 @vindex org-agenda-exporter-settings
-Write the agenda view to a file.  Depending on the extension of the
-selected file name, the view will be exported as HTML (extension
-@file{.html} or @file{.htm}), Postscript (extension @file{.ps}),
-iCalendar (extension @file{.ics}), or plain text (any other extension).
-Use the variable @code{org-agenda-exporter-settings} to
-set options for @file{ps-print} and for @file{htmlize} to be used during
-export, for example
+Write the agenda view to a file.  Depending on the extension of the selected
+file name, the view will be exported as HTML (extension @file{.html} or
+@file{.htm}), Postscript (extension @file{.ps}), iCalendar (extension
+@file{.ics}), Org-mode (extension @file{.org}), or plain text (any other
+extension).  Use the variable @code{org-agenda-exporter-settings} to set
+options for @file{ps-print} and for @file{htmlize} to be used during export,
+for example
 
 @vindex org-agenda-add-entry-text-maxlines
 @vindex htmlize-output-type
@@ -10690,7 +10697,8 @@ in the paragraph above about CUA mode also applies here.
 
 @end table
 
-@node Hacking, History and Acknowledgments, Miscellaneous, Top
+
+@node Hacking, MobileOrg, Miscellaneous, Top
 @appendix Hacking
 @cindex hacking
 
@@ -11456,19 +11464,6 @@ foreach $line (split(/\n/,$agenda)) @{
 @}
 @end example
 
-
-
-
-
-
-
-
-
-
-
-
-
-
 @node Using the property API, Using the mapping API, Extracting agenda information, Hacking
 @section Using the property API
 @cindex API, for properties
@@ -11648,7 +11643,124 @@ The following example counts the number of entries with TODO keyword
 (length (org-map-entries t "/+WAITING" 'agenda))
 @end lisp
 
-@node History and Acknowledgments, Main Index, Hacking, Top
+@node MobileOrg, History and Acknowledgments, Hacking, Top
+@appendix MobileOrg
+@cindex iPhone
+@cindex MobileOrg
+
+@i{MobileOrg} is an application for the @i{iPhone/iPod Touch} series of
+devices, developed by Richard Moreland.  Instead of trying to implement the
+full feature set of Org and fighting with synchronization issues, this
+application chooses a different path.  @i{MobileOrg} provides offline viewing
+and capture support for an Org-mode system rooted on a ``real'' computer.
+Synchronization issues are avoided by making @i{MobileOrg} only @i{write} to
+a special capture file, that is only @i{read} by the computer-based system.
+
+This appendix describes the support Org has for creating agenda views in a
+format that can be displayed by @i{MobileOrg}, and for integrating notes
+captured by @i{MobileOrg} into the main system.  It does not cover the
+operation of @i{MobileOrg} itself (see @uref{http://ncogni.to/mobileorg/}).
+
+@menu
+* Setting up the staging area::  Where to interact with the mobile device
+* Pushing to MobileOrg::        Uploading Org files and agendas
+* Pulling from MobileOrg::      Integrating captured and flagged items
+@end menu
+
+@node Setting up the staging area, Pushing to MobileOrg, MobileOrg, MobileOrg
+@section Setting up the staging area
+
+Org-mode has commands to prepare a directory with files for @i{MobileOrg}, and to
+read captured notes from there.  If Emacs can directly write to the WebDAV
+directory accessed by @i{MobileOrg}, all you need to do is to point to this
+directory using the variable @code{org-mobile-directory}.
+
+If Emacs cannot access the WebDAV directory directly, you can use a local
+directory for staging.  Other means must then be used to keep this directory
+in sync with the WebDAV directory.  In the following example, files are
+staged in @file{~/stage}, and Org-mode hooks take care of moving files to and
+from the WebDAV directory using @file{scp}.
+
+@example
+(setq org-mobile-directory "~/MobileOrg/stage/")
+(add-hook 'org-mobile-post-push-hook
+  (lambda ()
+   (shell-command "scp ~/stage/* user@@webdavhost:mobile/")))
+(add-hook 'org-mobile-pre-pull-hook
+  (lambda ()
+   (shell-command "scp user@@webdavhost:mobile/mobile-capture.org ~/stage/ ")))
+(add-hook 'org-mobile-post-pull-hook
+  (lambda ()
+   (shell-command "scp ~/stage/mobile-capture.org user@@webdavhost:mobile/")))
+@end example
+
+@node Pushing to MobileOrg, Pulling from MobileOrg, Setting up the staging area, MobileOrg
+@section Pushing to MobileOrg
+
+
+This operation copies all files currently listed in @code{org-agenda-files}
+to the directory @code{org-mobile-directory}.  It also creates (in the same
+directory) a special Org file @file{agendas.org}.  This file is an Org-mode
+style outline, containing every custom agenda view defined by the user.
+While creating the agendas, Org-mode will force@footnote{See the variable
+@code{org-mobile-force-id-on-agenda-items}.} an ID property on all entries
+referenced by the agendas, so that these entries can be uniquely identified
+if @i{MobileOrg} flags them for further action.  Finally, Org writes the file
+@file{index.org}, containing links to all other files.  If @i{MobileOrg} is
+configured to request this file from the WebDAV server, all agendas and Org
+files will be downloaded to the iPhone.
+
+@node Pulling from MobileOrg,  , Pushing to MobileOrg, MobileOrg
+@section Pulling from MobileOrg
+
+When @i{MobileOrg} synchronizes with the WebDAV server, it not only pulls the
+Org files for viewing.  It also appends captured entries and pointers to
+flagged entries to the file @file{mobile-capture.org} on the server.  Org has
+a @emph{pull} operation that integrates this information into an inbox file
+and operates on the pointers to flagged entries.  Here is how it works:
+
+@enumerate
+@item
+Org moves all entries found in @file{mobile-capture.org} and appends them to
+the file pointed to by the variable @code{org-mobile-inbox-for-pull}.  Each
+captured entry will be a top-level entry in the inbox file.
+@item
+After moving the entries, Org will attempt to act on the flags.  Some flags
+specify simple operations that will be executed directly and without user
+interaction.  Examples are marking an entry as DONE and/or archiving
+it@footnote{as specified by the variable @code{org-archive-default-action}}.
+All other flagged entries will receive a tag @code{:FLAGGED:}, so that they
+can be easily found again.  When there is a problem finding the entry that
+should be flagged, the pointer entry will remain in the inbox and will be
+marked with an error message.
+@item
+Org will then generate an agenda view with all flagged entries.  The user
+should then go through these entries and do whatever actions are necessary.
+If a note has been stored while flagging an entry in @i{MobileOrg}, that note
+will be displayed in the echo area when the cursor is on the corresponding
+agenda line.
+@table @kbd
+@kindex ?
+@item ?
+Pressing @kbd{?} in that special agenda will display the full flagging note in
+another window and also push it onto the kill ring.  So you could use @kbd{?
+z C-y C-c C-c} to store that flagging note as a normal note in the entry.
+Pressing @kbd{?} twice in succession will offer to remove the
+@code{:FLAGGED:} tag along with the recorded flagging note (which is stored
+in a property).
+@end table
+@end enumerate
+
+@kindex C-c a ?
+If you are not able to process all flagged entries directly, you can always
+return to this agenda view using @kbd{C-c a ?}.  Note, however, that there is
+a subtle difference.  The view created automatically by @kbd{M-x
+org-mobile-pull RET} is guaranteed to search all files that have been
+addressed by the last pull.  This might include a file that is not currently
+in your list of agenda files.  If you later use @kbd{C-c a ?} to regenerate
+the view, only the current agenda files will be searched.
+
+@node History and Acknowledgments, Main Index, MobileOrg, Top
 @appendix History and Acknowledgments
 @cindex acknowledgments
 @cindex history
@@ -11775,6 +11887,8 @@ basis.
 @i{Stefan Monnier} provided a patch to keep the Emacs-Lisp compiler
 happy.
 @item
+@i{Richard Moreland} wrote @i{MobileOrg} for the iPhone.
+@item
 @i{Rick Moynihan} proposed allowing multiple TODO sequences in a file
 and being able to quickly restrict the agenda to a subtree.
 @item
@@ -11913,3 +12027,5 @@ org-customize @key{RET}} and then klick yourself through the tree.
 @c fill-column: 77
 @c End:
 
+
+@c  LocalWords:  webdavhost pre

+ 24 - 0
lisp/ChangeLog

@@ -13,6 +13,30 @@
 
 	* org.el: Add an entry for org-crypt.
 
+2009-09-15  Carsten Dominik  <carsten.dominik@gmail.com>
+
+	* org-agenda.el (org-agenda-menu): Reorganize the menu for more
+	consistency.
+	(org-batch-store-agenda-views): New function.
+	(org-mobile-force-id-on-agenda-items): Mention variable.
+	(org-agenda-title-append): Define variable.
+	(org-write-agenda): New export to Org files.
+	(org-agenda-get-some-entry-text): New arguments INDENT and KEEP.
+	(org-agenda): Allow to keep the restricted file list if a special
+	variable is bound to t.
+	(org-agenda): Define a special agenda view for working on flagged
+	entries.
+	(org-agenda-get-restriction-and-command): List the new agenda
+	view.
+	(org-agenda-show-the-flagging-note): New command.
+	(org-agenda-mode-map): New key `?' for looking at the flagging
+	note.
+
+	* org.el (org-autoload): Autoload org-mobile.el.
+	(org-org-menu): Add menu commands for MobileOrg in the Org menu.
+
+	* org-id.el (org-id-get): Fix bug with forcing ID on an item.
+
 2009-09-15  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org-table.el (orgtbl-line-start-regexp): Match also TBLNAME

+ 254 - 82
lisp/org-agenda.el

@@ -55,10 +55,12 @@
 (declare-function calendar-persian-date-string  "cal-persia" (&optional date))
 (declare-function org-columns-quit              "org-colview" ())
 (defvar calendar-mode-map)
+(defvar org-mobile-force-id-on-agenda-items) ; defined in org-mobile.el
 
 ;; Defined somewhere in this file, but used before definition.
 (defvar org-agenda-buffer-name)
 (defvar org-agenda-overriding-header)
+(defvar org-agenda-title-append nil)
 (defvar entry)
 (defvar date)
 (defvar org-agenda-undo-list)
@@ -1449,6 +1451,7 @@ The following commands are available:
 (org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag)
 (org-defkey org-agenda-mode-map "\\" 'org-agenda-filter-by-tag-refine)
 (org-defkey org-agenda-mode-map ";" 'org-timer-set-timer)
+(define-key org-agenda-mode-map "?" 'org-agenda-show-the-flagging-note)
 
 (defvar org-agenda-keymap (copy-keymap org-agenda-mode-map)
   "Local keymap for agenda entries from Org-mode.")
@@ -1463,40 +1466,93 @@ The following commands are available:
   '("Agenda"
     ("Agenda Files")
     "--"
-    ["Show" org-agenda-show t]
+    ("Agenda Dates"
+     ["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda 'timeline)]
+     ["Next Dates" org-agenda-later (org-agenda-check-type nil 'agenda)]
+     ["Previous Dates" org-agenda-earlier (org-agenda-check-type nil 'agenda)]
+     ["Jump to date" org-agenda-goto-date (org-agenda-check-type nil 'agenda)])
+    "--"
+    ("View"
+     ["Day View" org-agenda-day-view
+      :active (org-agenda-check-type nil 'agenda)
+      :style radio :selected (equal org-agenda-ndays 1)
+      :keys "v d  (or just d)"]
+     ["Week View" org-agenda-week-view
+      :active (org-agenda-check-type nil 'agenda)
+      :style radio :selected (equal org-agenda-ndays 7)
+      :keys "v w  (or just w)"]
+     ["Month View" org-agenda-month-view
+      :active (org-agenda-check-type nil 'agenda)
+      :style radio :selected (member org-agenda-ndays '(28 29 30 31))
+      :keys "v m"]
+     ["Year View" org-agenda-year-view
+      :active (org-agenda-check-type nil 'agenda)
+      :style radio :selected (member org-agenda-ndays '(365 366))
+      :keys "v y"]
+     "--"
+     ["Include Diary" org-agenda-toggle-diary
+      :style toggle :selected org-agenda-include-diary
+      :active (org-agenda-check-type nil 'agenda)]
+     ["Use Time Grid" org-agenda-toggle-time-grid
+      :style toggle :selected org-agenda-use-time-grid
+      :active (org-agenda-check-type nil 'agenda)]
+     "--"
+     ["Show clock report" org-agenda-clockreport-mode
+      :style toggle :selected org-agenda-clockreport-mode
+      :active (org-agenda-check-type nil 'agenda)]
+     ["Show some entry text" org-agenda-entry-text-mode
+      :style toggle :selected org-agenda-entry-text-mode
+      :active t]
+    "--"
+     ["Show Logbook entries" org-agenda-log-mode
+      :style toggle :selected org-agenda-show-log
+      :active (org-agenda-check-type nil 'agenda 'timeline)
+      :keys "v l (or just l)"]
+     ["Include archived trees" org-agenda-archives-mode
+      :style toggle :selected org-agenda-archives-mode :active t
+      :keys "v a"]
+     ["Include archive files" (org-agenda-archives-mode t)
+      :style toggle :selected (eq org-agenda-archives-mode t) :active t
+      :keys "v A"]
+     "--"
+     ["Remove Restriction" org-agenda-remove-restriction-lock org-agenda-restrict])
+    ["Write view to file" org-write-agenda t]
+    ["Rebuild buffer" org-agenda-redo t]
+    ["Save all Org-mode Buffers" org-save-all-org-buffers t]
+    "--"
+    ["Show original entry" org-agenda-show t]
     ["Go To (other window)" org-agenda-goto t]
     ["Go To (this window)" org-agenda-switch-to t]
     ["Follow Mode" org-agenda-follow-mode
      :style toggle :selected org-agenda-follow-mode :active t]
-    ["Tree to indirect frame" org-agenda-tree-to-indirect-buffer t]
+;    ["Tree to indirect frame" org-agenda-tree-to-indirect-buffer t]
     "--"
-    ["Cycle TODO" org-agenda-todo t]
-    ("Archive and Refile"
+    ("TODO"
+     ["Cycle TODO" org-agenda-todo t]
+     ["Next TODO set" org-agenda-todo-nextset t]
+     ["Previous TODO set" org-agenda-todo-previousset t]
+     ["Add note" org-agenda-add-note t])
+    ("Archive/Refile/Delete"
      ["Toggle ARCHIVE tag" org-agenda-toggle-archive-tag t]
      ["Move to archive sibling" org-agenda-archive-to-archive-sibling t]
      ["Archive subtree" org-agenda-archive t]
-     ["Refile" org-agenda-refile t])
-    ["Delete subtree" org-agenda-kill t]
+     "--"
+     ["Refile" org-agenda-refile t]
+     "--"
+     ["Delete subtree" org-agenda-kill t])
     ("Bulk action"
      ["Mark entry" org-agenda-bulk-mark t]
      ["Unmark entry" org-agenda-bulk-unmark t]
      ["Act on all marked" org-agenda-bulk-action t]
      ["Unmark all entries" org-agenda-bulk-remove-all-marks :active t :keys "C-u s"])
     "--"
-    ["Add note" org-agenda-add-note t]
-    "--"
-    ["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda 'timeline)]
-    ["Next Dates" org-agenda-later (org-agenda-check-type nil 'agenda)]
-    ["Previous Dates" org-agenda-earlier (org-agenda-check-type nil 'agenda)]
-    ["Jump to date" org-agenda-goto-date (org-agenda-check-type nil 'agenda)]
-    "--"
     ("Tags and Properties"
      ["Show all Tags" org-agenda-show-tags t]
      ["Set Tags current line" org-agenda-set-tags (not (org-region-active-p))]
      ["Change tag in region" org-agenda-set-tags (org-region-active-p)]
      "--"
      ["Column View" org-columns t])
-    ("Date/Schedule"
+    ("Deadline/Schedule"
      ["Schedule" org-agenda-schedule t]
      ["Set Deadline" org-agenda-deadline t]
      "--"
@@ -1534,57 +1590,17 @@ The following commands are available:
      ["Holidays" org-agenda-holidays (org-agenda-check-type nil 'agenda 'timeline)]
      ["Convert" org-agenda-convert-date (org-agenda-check-type nil 'agenda 'timeline)]
      "--"
-     ["Create iCalendar file" org-export-icalendar-combine-agenda-files t])
+     ["Create iCalendar File" org-export-icalendar-combine-agenda-files t])
     "--"
-    ("View"
-     ["Day View" org-agenda-day-view
-      :active (org-agenda-check-type nil 'agenda)
-      :style radio :selected (equal org-agenda-ndays 1)
-      :keys "v d  (or just d)"]
-     ["Week View" org-agenda-week-view
-      :active (org-agenda-check-type nil 'agenda)
-      :style radio :selected (equal org-agenda-ndays 7)
-      :keys "v w  (or just w)"]
-     ["Month View" org-agenda-month-view
-      :active (org-agenda-check-type nil 'agenda)
-      :style radio :selected (member org-agenda-ndays '(28 29 30 31))
-      :keys "v m"]
-     ["Year View" org-agenda-year-view
-      :active (org-agenda-check-type nil 'agenda)
-      :style radio :selected (member org-agenda-ndays '(365 366))
-      :keys "v y"]
-     "--"
-     ["Include Diary" org-agenda-toggle-diary
-      :style toggle :selected org-agenda-include-diary
-      :active (org-agenda-check-type nil 'agenda)]
-     ["Use Time Grid" org-agenda-toggle-time-grid
-      :style toggle :selected org-agenda-use-time-grid
-      :active (org-agenda-check-type nil 'agenda)]
-     "--"
-     ["Show clock report" org-agenda-clockreport-mode
-      :style toggle :selected org-agenda-clockreport-mode
-      :active (org-agenda-check-type nil 'agenda)]
-     ["Show some entry text" org-agenda-entry-text-mode
-      :style toggle :selected org-agenda-entry-text-mode
-      :active t]
+    ["Undo Remote Editing" org-agenda-undo org-agenda-undo-list]
     "--"
-     ["Show Logbook entries" org-agenda-log-mode
-      :style toggle :selected org-agenda-show-log
-      :active (org-agenda-check-type nil 'agenda 'timeline)
-      :keys "v l (or just l)"]
-     ["Include archived trees" org-agenda-archives-mode
-      :style toggle :selected org-agenda-archives-mode :active t
-      :keys "v a"]
-     ["Include archive files" (org-agenda-archives-mode t)
-      :style toggle :selected (eq org-agenda-archives-mode t) :active t
-      :keys "v A"]
+    ("MobileOrg"
+     ["Push Files and Views" org-mobile-push t]
+     ["Pull Captured and Flagged" org-mobile-pull t]
+     ["Find FLAGGED Tasks" (org-agenda nil "?") t]
+     ["Show note / unflag" org-agenda-show-the-flagging-note t]
      "--"
-     ["Remove Restriction" org-agenda-remove-restriction-lock org-agenda-restrict])
-    ["Write view to file" org-write-agenda t]
-    ["Rebuild buffer" org-agenda-redo t]
-    ["Save all Org-mode Buffers" org-save-all-org-buffers t]
-    "--"
-    ["Undo Remote Editing" org-agenda-undo org-agenda-undo-list]
+     ["Setup" (progn (require 'org-mobile) (customize-group 'org-mobile)) t])
     "--"
     ["Quit" org-agenda-quit t]
     ["Exit and Release Buffers" org-agenda-exit t]
@@ -1707,9 +1723,11 @@ Pressing `<' twice means to restrict to the current subtree or region
 	   (buf (current-buffer))
 	   (bfn (buffer-file-name (buffer-base-buffer)))
 	   entry key type match lprops ans)
-      ;; Turn off restriction unless there is an overriding one
+      ;; Turn off restriction unless there is an overriding one,
       (unless org-agenda-overriding-restriction
-	(put 'org-agenda-files 'org-restrict nil)
+	(unless (org-bound-and-true-p org-agenda-keep-restriced-file-list)
+	  ;; There is a request to keep the file list in place
+	  (put 'org-agenda-files 'org-restrict nil))
 	(setq org-agenda-restrict nil)
 	(move-marker org-agenda-restrict-begin nil)
 	(move-marker org-agenda-restrict-end nil))
@@ -1788,6 +1806,22 @@ Pressing `<' twice means to restrict to the current subtree or region
        ((equal keys "m") (call-interactively 'org-tags-view))
        ((equal keys "M") (org-call-with-arg 'org-tags-view (or arg '(4))))
        ((equal keys "e") (call-interactively 'org-store-agenda-views))
+       ((equal keys "?") (org-tags-view nil "+FLAGGED")
+	(org-add-hook
+	 'post-command-hook
+	 (lambda ()
+	   (unless (current-message)
+	     (let* ((m (org-agenda-get-any-marker))
+		    (note (and m (org-entry-get m "THEFLAGGINGNOTE"))))
+	       (when note
+		 (message (concat
+			   "FLAGGING-NOTE ([?] for more info): "
+			   (org-add-props
+			       (replace-regexp-in-string
+				"\\\\n" "//"
+				(copy-sequence note))
+			       nil 'face 'org-warning)))))))
+	 t t))
        ((equal keys "L")
 	(unless (org-mode-p)
 	  (error "This is not an Org-mode file"))
@@ -1833,7 +1867,7 @@ t   List of all TODO entries            T   Entries with special TODO kwd
 m   Match a TAGS/PROP/TODO query        M   Like m, but only TODO entries
 L   Timeline for current buffer         #   List stuck projects (!=configure)
 s   Search for keywords                 C   Configure custom agenda commands
-/   Multi-occur
+/   Multi-occur                         ?   Find :FLAGGED: entries
 ")
 			(start 0))
 		    (while (string-match
@@ -1952,7 +1986,7 @@ s   Search for keywords                 C   Configure custom agenda commands
 	   ((eq c ?>)
 	    (org-agenda-remove-restriction-lock 'noupdate)
 	    (setq restriction nil))
-	   ((and (equal selstring "") (memq c '(?s ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/)))
+	   ((and (equal selstring "") (memq c '(?s ?a ?t ?m ?L ?C ?e ?T ?M ?# ?! ?/ ??)))
 	    (throw 'exit (cons (setq selstring (char-to-string c)) restriction)))
            ((and (> (length selstring) 0) (eq c ?\d))
             (delete-window)
@@ -2166,6 +2200,17 @@ so the export commands can easily use it."
 	  (and (get-buffer org-agenda-buffer-name)
 	       (kill-buffer org-agenda-buffer-name)))))))
 
+(defun org-agenda-mark-header-line (pos)
+  "Mark the line at POS as an agenda structure header."
+  (save-excursion
+    (goto-char pos)
+    (put-text-property (point-at-bol) (point-at-eol)
+		       'org-agenda-structural-header t)
+    (when org-agenda-title-append
+      (put-text-property (point-at-bol) (point-at-eol)
+			 'org-agenda-title-append org-agenda-title-append))))
+
+
 (defun org-write-agenda (file &optional open nosettings)
   "Write the current buffer (an agenda view) as a file.
 Depending on the extension of the file name, plain text (.txt),
@@ -2188,7 +2233,7 @@ higher priority settings."
     '(save-excursion
        (save-window-excursion
 	 (org-agenda-mark-filtered-text)
-	 (let ((bs (copy-sequence (buffer-string))) beg)
+	 (let ((bs (copy-sequence (buffer-string))) beg app)
 	   (org-agenda-unmark-filtered-text)
 	   (with-temp-buffer
 	     (insert bs)
@@ -2228,6 +2273,63 @@ higher priority settings."
 			      (concat (file-name-sans-extension file) ".ps"))
 			     (expand-file-name file))
 	       (message "PDF written to %s" file))
+	      ((string-match "\\.org\\'" file)
+	       (let ((all (buffer-string)) in-date id pl prefix line)
+		 (with-temp-file file
+		   (org-mode)
+		   (insert all)
+		   (goto-char (point-min))
+		   (while (not (eobp))
+		     (cond
+		      ((looking-at "[ \t]*$")) ; keep empty lines
+		      ((looking-at "=+$")
+		       ;; remove underlining
+		       (delete-region (point) (point-at-eol)))
+		      ((get-text-property (point) 'org-agenda-structural-header)
+		       (setq in-date nil)
+		       (setq app (get-text-property (point)
+						     'org-agenda-title-append))
+		       (setq short (get-text-property (point)
+						      'short-heading))
+		       (when (and short (looking-at ".+"))
+			 (replace-match short)
+			 (beginning-of-line 1))
+		       (when app
+			 (end-of-line 1)
+			 (insert app)
+			 (beginning-of-line 1))
+		       (insert "* "))
+		      ((get-text-property (point) 'org-agenda-date-header)
+		       (setq in-date t)
+		       (insert "** "))
+		      ((setq m (or (get-text-property (point) 'org-hd-marker)
+				   (get-text-property (point) 'org-marker)))
+		       (if (setq pl (get-text-property (point) 'prefix-length))
+			   (progn
+			     (setq prefix (org-trim (buffer-substring
+						     (point) (+ (point) pl)))
+				   line (org-trim (buffer-substring
+						   (+ (point) pl)
+						   (point-at-eol))))
+			     (delete-region (point-at-bol) (point-at-eol))
+			     (insert line "<break>" prefix)
+			     (beginning-of-line 1))
+			 (and (looking-at "[ \t]+") (replace-match "")))
+		       (insert (if in-date "***  " "**  "))
+		       (end-of-line 1)
+		       (insert "\n")
+		       (insert (org-agenda-get-some-entry-text
+				m 10 "   " 'planning)
+			       "\n")
+		       (when (setq id
+				   (if (org-bound-and-true-p
+					org-mobile-force-id-on-agenda-items)
+				       (org-id-get m 'create)
+				     (org-entry-get m "ID")))
+			 (insert "   :PROPERTIES:\n   :ORIGINAL_ID: " id
+				 "\n   :END:\n"))))
+		     (beginning-of-line 2)))
+		 (message "Agenda written to Org file %s" file)))
 	      ((string-match "\\.ics\\'" file)
 	       (require 'org-icalendar)
 	       (let ((org-agenda-marker-table
@@ -2289,13 +2391,17 @@ Drawers will be excluded, also the line with scheduling/deadline info."
 	(if (not (setq m (get-text-property (point) 'org-hd-marker)))
 	    (beginning-of-line 2)
 	  (setq txt (org-agenda-get-some-entry-text
-		     m org-agenda-add-entry-text-maxlines))
+		     m org-agenda-add-entry-text-maxlines "    > "))
 	  (end-of-line 1)
 	  (if (string-match "\\S-" txt) (insert "\n" txt)))))))
 
-(defun org-agenda-get-some-entry-text (marker n-lines)
+(defun org-agenda-get-some-entry-text (marker n-lines &optional indent
+					      &rest keep)
   "Extract entry text from MARKER, at most N-LINES lines.
-This will ignore drawers etc, just get the text."
+This will ignore drawers etc, just get the text.
+If INDENT is given, prefix every line with this string.  If KEEP is
+given, it is a list of symbols, defining stuff that hould not be
+removed from the entry content.  Currently only `planning' is allowed here."
   (let (txt drawer-re kwd-time-re ind)
     (save-excursion
       (with-current-buffer (marker-buffer marker)
@@ -2330,9 +2436,10 @@ This will ignore drawers etc, just get the text."
 		   (progn (re-search-forward
 			   "^[ \t]*:END:.*\n?" nil 'move)
 			  (point))))
-		(goto-char (point-min))
-		(while (re-search-forward kwd-time-re nil t)
-		  (replace-match ""))
+		(unless (member 'planning keep)
+		  (goto-char (point-min))
+		  (while (re-search-forward kwd-time-re nil t)
+		    (replace-match "")))
 		(goto-char (point-min))
 		(when org-agenda-entry-text-exclude-regexps
 		  (let ((re-list org-agenda-entry-text-exclude-regexps)	re)
@@ -2362,8 +2469,9 @@ This will ignore drawers etc, just get the text."
 		(run-hooks 'org-agenda-entry-text-cleanup-hook)
 
 		(goto-char (point-min))
-		(while (and (not (eobp)) (re-search-forward "^" nil t))
-		  (replace-match "    > "))
+		(when indent
+		  (while (and (not (eobp)) (re-search-forward "^" nil t))
+		    (replace-match indent t t)))
 		(goto-char (point-min))
 		(while (looking-at "[ \t]*\n") (replace-match ""))
 		(goto-char (point-max))
@@ -2514,7 +2622,7 @@ bind it in the options section.")
       (org-agenda-mark-clocking-task)
       (when org-agenda-entry-text-mode
 	(org-agenda-entry-text-hide)
-	(org-agenda-entry-text-show))	
+	(org-agenda-entry-text-show))
       (run-hooks 'org-finalize-agenda-hook)
       (setq org-agenda-type (get-text-property (point) 'org-agenda-type))
       (when (get 'org-agenda-filter :preset-filter)
@@ -2679,7 +2787,7 @@ no longer in use."
       (error "No marker points to an entry here"))
     (setq txt (concat "\n" (org-no-properties
 			    (org-agenda-get-some-entry-text
-			     m org-agenda-entry-text-maxlines))))
+			     m org-agenda-entry-text-maxlines "    > "))))
     (when (string-match "\\S-" txt)
       (setq o (org-make-overlay (point-at-bol) (point-at-eol)))
       (org-overlay-put o 'evaporate t)
@@ -2754,6 +2862,10 @@ dates."
     (push :scheduled args)
     (push :sexp args)
     (if dotodo (push :todo args))
+    (insert "Timeline of file " entry "\n")
+    (add-text-properties (point-min) (point)
+			 (list 'face 'org-agenda-structure))
+    (org-agenda-mark-header-line (point-min))
     (while (setq d (pop day-numbers))
       (if (and (listp d) (eq (car d) :omitted))
 	  (progn
@@ -2786,6 +2898,7 @@ dates."
 				     'org-agenda-date-weekend
 				   'org-agenda-date))
 	      (put-text-property s (1- (point)) 'org-date-line t)
+	      (put-text-property s (1- (point)) 'org-agenda-date-header t)
 	      (if (equal d today)
 		  (put-text-property s (1- (point)) 'org-today t))
 	      (and rtn (insert (org-finalize-agenda-entries rtn) "\n"))
@@ -2934,9 +3047,10 @@ given in `org-agenda-start-on-weekday'."
 		     file date :todo))
 	  (setq rtnall (append rtnall rtn))))
       (when rtnall
-	(insert "ALL CURRENTLY OPEN TODO ITEMS:\n")
+	(insert "All currently open TODO items:\n")
 	(add-text-properties (point-min) (1- (point))
-			     (list 'face 'org-agenda-structure))
+			     (list 'face 'org-agenda-structure
+				   'short-heading "All TODO items"))
 	(insert (org-finalize-agenda-entries rtnall) "\n")))
     (unless org-agenda-compact-blocks
       (let* ((d1 (car day-numbers))
@@ -2956,7 +3070,8 @@ given in `org-agenda-start-on-weekday'."
 		    "")
 		  ":\n")))
       (add-text-properties s (1- (point)) (list 'face 'org-agenda-structure
-						'org-date-line t)))
+						'org-date-line t))
+      (org-agenda-mark-header-line s))
     (while (setq d (pop day-numbers))
       (setq date (calendar-gregorian-from-absolute d)
 	    wd (calendar-day-of-week date)
@@ -3003,6 +3118,7 @@ given in `org-agenda-start-on-weekday'."
 				   'org-agenda-date-weekend
 				 'org-agenda-date))
 	    (put-text-property s (1- (point)) 'org-date-line t)
+	    (put-text-property s (1- (point)) 'org-agenda-date-header t)
 	    (put-text-property s (1- (point)) 'org-day-cnt day-cnt)
 	    (when todayp
 	      (put-text-property s (1- (point)) 'org-today t)
@@ -3233,6 +3349,7 @@ in `org-agenda-text-search-extra-files'."
 	(insert "Press `[', `]' to add/sub word, `{', `}' to add/sub regexp, `C-u r' to edit\n")
 	(add-text-properties pos (1- (point))
 			     (list 'face 'org-agenda-structure))))
+    (org-agenda-mark-header-line (point-min))
     (when rtnall
       (insert (org-finalize-agenda-entries rtnall) "\n"))
     (goto-char (point-min))
@@ -3287,7 +3404,11 @@ for a keyword.  A numeric prefix directly selects the Nth keyword in
 		    nil 'face 'org-agenda-structure) "\n")
       (insert "Global list of TODO items of type: ")
       (add-text-properties (point-min) (1- (point))
-			   (list 'face 'org-agenda-structure))
+			   (list 'face 'org-agenda-structure
+				 'short-heading
+				 (concat "ToDo: "
+					 (or org-select-this-todo-keyword "ALL"))))
+      (org-agenda-mark-header-line (point-min))
       (setq pos (point))
       (insert (or org-select-this-todo-keyword "ALL") "\n")
       (add-text-properties pos (1- (point)) (list 'face 'org-warning))
@@ -3303,6 +3424,7 @@ for a keyword.  A numeric prefix directly selects the Nth keyword in
 		kwds))
 	(insert "\n"))
       (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure)))
+    (org-agenda-mark-header-line (point-min))
     (when rtnall
       (insert (org-finalize-agenda-entries rtnall) "\n"))
     (goto-char (point-min))
@@ -3362,7 +3484,9 @@ The prefix arg TODO-ONLY limits the search to TODO entries."
 		    nil 'face 'org-agenda-structure) "\n")
       (insert "Headlines with TAGS match: ")
       (add-text-properties (point-min) (1- (point))
-			   (list 'face 'org-agenda-structure))
+			   (list 'face 'org-agenda-structure
+				 'short-heading
+				 (concat "Match: " match)))
       (setq pos (point))
       (insert match "\n")
       (add-text-properties pos (1- (point)) (list 'face 'org-warning))
@@ -3370,6 +3494,7 @@ The prefix arg TODO-ONLY limits the search to TODO entries."
       (unless org-agenda-multi
 	(insert "Press `C-u r' to search again with new search string\n"))
       (add-text-properties pos (1- (point)) (list 'face 'org-agenda-structure)))
+    (org-agenda-mark-header-line (point-min))
     (when rtnall
       (insert (org-finalize-agenda-entries rtnall) "\n"))
     (goto-char (point-min))
@@ -6701,6 +6826,53 @@ This will remove the markers, and the overlays."
 	       (format ", skipped %d (disappeared before their turn)"
 		       cntskip)))))
 
+;;; Flagging notes
+
+(defun org-agenda-show-the-flagging-note ()
+  "Display the flagging note in the other window.
+When called a second time in direct sequence, offer to remove the FLAGGING
+tag and (if present) the flagging note."
+  (interactive)
+  (let ((hdmarker (get-text-property (point) 'org-hd-marker))
+	(win (selected-window))
+	note heading newhead)
+    (unless hdmarker
+      (error "No linked entry at point"))
+    (if (and (eq this-command last-command)
+	     (y-or-n-p "Unflag and remove any flagging note? "))
+	(progn
+	  (org-agenda-remove-flag hdmarker)
+	  (let ((win (get-buffer-window "*Flagging Note*")))
+	    (and win (delete-window win)))
+	  (message "Entry unflaged"))
+      (setq note (org-entry-get hdmarker "THEFLAGGINGNOTE"))
+      (unless note
+	(error "No flagging note"))
+      (org-kill-new note)
+      (org-switch-to-buffer-other-window "*Flagging Note*")
+      (erase-buffer)
+      (insert note)
+      (goto-char (point-min))
+      (while (re-search-forward "\\\\n" nil t)
+	(replace-match "\n" t t))
+      (goto-char (point-min))
+      (select-window win)
+      (message "Flagging note pushed to kill ring.  Press [?] again to remove tag and note"))))
+
+(defun org-agenda-remove-flag (marker)
+  "Remove the FLAGGED tag and any flaging note in the entry."
+  (let (newhead)
+    (org-with-point-at marker
+      (org-toggle-tag "FLAGGED" 'off)
+      (org-entry-delete nil "THEFLAGGINGNOTE")
+      (setq newhead (org-get-heading)))
+    (org-agenda-change-all-lines newhead marker)
+    (message "Entry unflaged")))
+
+(defun org-agenda-get-any-marker (&optional pos)
+  (or (get-text-property (or pos (point)) 'org-hd-marker)
+      (get-text-property (or pos (point)) 'org-marker)))
+
 ;;; Appointment reminders
 
 (defvar appt-time-msg-list)

+ 11 - 10
lisp/org-id.el

@@ -212,16 +212,17 @@ If the entry does not have an ID, the function returns nil.
 However, when CREATE is non nil, create an ID if none is present already.
 PREFIX will be passed through to `org-id-new'.
 In any case, the ID of the entry is returned."
-  (let ((id (org-entry-get pom "ID")))
-    (cond
-     ((and id (stringp id) (string-match "\\S-" id))
-      id)
-     (create
-      (setq id (org-id-new prefix))
-      (org-entry-put pom "ID" id)
-      (org-id-add-location id (buffer-file-name (buffer-base-buffer)))
-      id)
-     (t nil))))
+  (org-with-point-at pom
+    (let ((id (org-entry-get nil "ID")))
+      (cond
+       ((and id (stringp id) (string-match "\\S-" id))
+	id)
+       (create
+	(setq id (org-id-new prefix))
+	(org-entry-put pom "ID" id)
+	(org-id-add-location id (buffer-file-name (buffer-base-buffer)))
+	id)
+       (t nil)))))
 
 ;;;###autoload
 (defun org-id-get-with-outline-path-completion (&optional targets)

+ 406 - 0
lisp/org-mobile.el

@@ -0,0 +1,406 @@
+;;; org-mobile.el --- Code for asymmetric sync with a mobile device
+;; Copyright (C) 2009 Free Software Foundation, Inc.
+;;
+;; Author: Carsten Dominik <carsten at orgmode dot org>
+;; Keywords: outlines, hypermedia, calendar, wp
+;; Homepage: http://orgmode.org
+;; Version: 6.30trans
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+;;
+;; This file contains the code to interact with Richard Moreland's iPhone
+;; application MobileOrg.  This code is documented in Appendix B of the
+;; Org-mode manual.
+
+(require 'org)
+(require 'org-agenda)
+
+(defgroup org-mobile nil
+  "Options concerning support for a viewer on a mobile device."
+  :tag "Org Mobile"
+  :group 'org)
+
+(defcustom org-mobile-directory ""
+  "The WebDAV directory where the interaction with the mobile takes place."
+  :group 'org-mobile
+  :type 'directory)
+
+(defcustom org-mobile-inbox-for-pull "~/org/from-mobile.org"
+  "The file where captured notes and flags will be appended to.
+During the execution of `org-mobile-pull', the file
+`org-mobile-capture-file' will be emptied it's contents have
+been appended to the file given here."
+  :group 'org-mobile
+  :type 'file)
+
+(defcustom org-mobile-capture-file "mobile-capture.org"
+  "The capture file where the mobile stores captured notes and flags.
+Relative to `org-mobile-directory'."
+  :group 'org-mobile
+  :type 'file)
+
+(defcustom org-mobile-index-file "index.org"
+  "The index file with inks to all Org files that should be loaded by MobileOrg.
+Relative to `org-mobile-directory'."
+  :group 'org-mobile
+  :type 'file)
+
+(defcustom org-mobile-force-id-on-agenda-items t
+  "Non-nil means make all agenda items carry and ID."
+  :group 'org-mobile
+  :type 'boolean)
+
+(defcustom org-mobile-action-alist
+  '(("d" . (org-todo 'done))
+    ("a" . (org-archive-subtree-default))
+    ("d-a" . (progn (org-todo 'done) (org-archive-subtree-default))))
+  "Alist with flags and actions for mobile sync.
+When flagging an entry, MobileOrg will create entries that look like
+
+  * F(action)  [[id:entry-id][entry title]]
+
+This alist defines that the action in the parentheses of F() should mean,
+i.e. what action should be taken.  The car of each elements of the alist
+is an actions string.  The cdr is an Emacs Lisp form that will be evaluated
+with the cursor on the headline of that entry."
+  :group 'org-mobile
+  :type '(repeat
+	  (cons (string :tag "Action flag")
+		(sexp   :tag "Action form"))))
+
+(defvar org-mobile-pre-push-hook nil
+  "Hook run before running `org-mobile-push'.
+This could be used to clean up `org-mobile-directory', for example to
+remove files that used to be included in the agenda but no longer are.
+The presence of such files would not really be a problem, but after time
+they may accumulate.")
+
+(defvar org-mobile-post-push-hook nil
+  "Hook run after running `org-mobile-push'.
+If Emacs does not have direct write access to the WebDAV directory used
+by the mobile device, this hook could be used to copy all files from the
+local `org-mobile-directory' to the WebDAV directory, for example using
+`rsync' or `scp'.")
+
+(defvar org-mobile-pre-pull-hook nil
+  "Hook run before executing `org-mobile-pull'.
+If Emacs does not have direct write access to the WebDAV directory used
+by the mobile device, this hook could be used to copy all files from the
+WebDAV location to the local staging directory `org-mobile-directory'.")
+
+(defvar org-mobile-post-pull-hook nil
+  "Hook run after running `org-mobile-pull'.
+If Emacs does not have direct write access to the WebDAV directory used
+by the mobile device, this hook could be used to copy all files from the
+local `org-mobile-directory' to the WebDAV directory, for example using
+`rsync' or `scp'.  The only file that will be changed after a pull is
+`org-mobile-capture-file'.")
+
+(defvar org-mobile-last-flagged-files nil
+  "List of files containing entreis flagged in the latest pull.")
+
+;;;###autoload
+(defun org-mobile-push ()
+  "Push the current state of Org affairs to the WebDAV directory.
+This will create the index file, copy all agenda files there, and also
+create all custom agenda views, for upload to the mobile phone."
+  (interactive)
+  (org-mobile-check-setup)
+  (run-hooks 'org-mobile-pre-push-hook)
+  (org-mobile-create-sumo-agenda)
+  (org-save-all-org-buffers) ; to save any IDs created by this process
+  (org-mobile-copy-agenda-files)
+  (org-mobile-create-index-file)
+  (org-mobile-write-checksums)
+  (run-hooks 'org-mobile-post-push-hook)
+  (message "Files for mobile viewer staged"))
+
+;;;###autoload
+(defun org-mobile-pull ()
+  "Pull the contents of `org-mobile-capture-file' and integrate them.
+Apply all flagged actions, flag entries to be flagged and then call an
+agenda view showing the flagged items."
+  (interactive)
+  (org-mobile-check-setup)
+  (run-hooks 'org-mobile-pre-pull-hook)
+  (let ((insertion-marker (org-mobile-move-capture)))
+    (if (not (markerp insertion-marker))
+	(message "No new items")
+      (org-with-point-at insertion-marker
+	(org-mobile-apply-flags (point) (point-max)))
+      (move-marker insertion-marker nil)
+      (run-hooks 'org-mobile-post-pull-hook)
+      (when org-mobile-last-flagged-files
+	;; Make an agenda view of flagged entries, but only in the files
+	;; where stuff has been added.
+	(put 'org-agenda-files 'org-restrict org-mobile-last-flagged-files)
+	(let ((org-agenda-keep-restriced-file-list t))
+	  (org-agenda nil "?"))))))
+
+(defun org-mobile-check-setup ()
+  "Check if org-mobile-directory has been set up."
+  (when (or (not org-mobile-directory)
+	    (not (stringp org-mobile-directory))
+	    (not (string-match "\\S-" org-mobile-directory))
+	    (not (file-exists-p org-mobile-directory))
+	    (not (file-directory-p org-mobile-directory)))
+    (error
+     "Variable `org-mobile-directory' must point to an existing directory"))
+  (when (or (not org-mobile-inbox-for-pull)
+	    (not (stringp org-mobile-inbox-for-pull))
+	    (not (string-match "\\S-" org-mobile-inbox-for-pull))
+	    (not (file-exists-p
+		  (file-name-directory org-mobile-inbox-for-pull))))
+    (error
+     "Variable `org-mobile-inbox-for-pull' must point to a file in an existing directory")))
+
+(defun org-mobile-create-index-file ()
+  "Write the index file in the WebDAV directory."
+  (interactive)
+  (with-temp-file (expand-file-name org-mobile-index-file org-mobile-directory)
+    (insert "* [[file:agendas.org][Agenda Views]]\n")
+    (let ((files (org-agenda-files t)) file)
+      (while (setq file (pop files))
+	(insert (format "* [[file:%s][%s]]\n"
+			(file-name-nondirectory file)
+			(capitalize
+			 (file-name-sans-extension
+			  (file-name-nondirectory file))))))
+      (insert (format "* [[file:%s][Captured before last sync]]\n"
+		      (file-name-sans-extension org-mobile-capture-file))))))
+
+(defun org-mobile-copy-agenda-files ()
+  "Copy all agenda files to the stage or WebDAV directory."
+  (let ((files (org-agenda-files t)) file)
+    (while (setq file (pop files))
+      (if (file-exists-p file)
+	  (copy-file file (expand-file-name (file-name-nondirectory file)
+					    org-mobile-directory)
+		     'ok-if-exists)))))
+
+(defun org-mobile-write-checksums ()
+  "Create checksums for all files in `org-mobile-directory'.
+The table of checksums is written to the file mobile-checksums."
+  (let ((cmd (cond ((executable-find "shasum"))
+		   ((executable-find "sha1sum"))
+		   ((executable-find "md5sum"))
+		   ((executable-find "md5")))))
+    (if (not cmd)
+	(message "Checksums could not be generated: no executable")
+      (with-temp-buffer
+	(cd org-mobile-directory)
+	(if (equal 0 (shell-command (concat cmd " *.org > checksums.dat")))
+	    (message "Checksums written")
+	  (message "Checksums could not be generated"))))))
+
+(defun org-mobile-sumo-agenda-command ()
+  "Return an agenda custom command that comprises all custom commands."
+  (let ((custom-list
+	 ;; normalize different versions
+	 (delq nil
+	       (mapcar
+		(lambda (x)
+		  (cond ((stringp (cdr x)) nil)
+			((stringp (nth 1 x)) x)
+			((not (nth 1 x)) (cons (car x) (cons "" (cddr x))))
+			(t (cons (car x) (cons "" (cdr x))))))
+		org-agenda-custom-commands)))
+	new e key desc type match settings cmds gkey gdesc gsettings cnt)
+    (while (setq e (pop custom-list))
+      (cond
+       ((stringp (cdr e))
+	;; this is a description entry - skip it
+	)
+       ((eq (nth 2 e) 'search)
+	;; Search view is interactive, skip
+	)
+       ((memq (nth 2 e) '(todo-tree tags-tree occur-tree))
+	;; These are trees, not really agenda commands
+	)
+       ((memq (nth 2 e) '(agenda todo tags))
+	;; a normal command
+	(setq key (car e) desc (nth 1 e) type (nth 2 e) match (nth 3 e)
+	      settings (nth 4 e))
+	(setq settings
+	      (cons (list 'org-agenda-title-append
+			  (concat "<break>KEYS=" key " TITLE: "
+				  (if (and (stringp desc) (> (length desc) 0))
+				      desc (symbol-name type))
+				  " " match))
+		    settings))
+	(push (list type match settings) new))
+       ((symbolp (nth 2 e))
+	;; A user-defined function, not sure how to handle that yet
+	)
+       (t
+	;; a block agenda
+	(setq gkey (car e) gdesc (nth 1 e) gsettings (nth 3 e) cmds (nth 2 e))
+	(setq cnt 0)
+	(while (setq e (pop cmds))
+	  (setq type (car e) match (nth 1 e) settings (nth 2 e))
+	  (setq settings (append gsettings settings))
+	  (setq settings
+		(cons (list 'org-agenda-title-append
+			    (concat "<break>KEYS=" gkey "#" (number-to-string
+						      (setq cnt (1+ cnt)))
+				    " TITLE: " gdesc " " match))
+		      settings))
+	  (push (list type match settings) new)))))
+    (list "X" "SUMO" (reverse new) nil)))
+
+;;;###autoload
+(defun org-mobile-create-sumo-agenda ()
+  "Create a file that contains all custom agenda views."
+  (interactive)
+  (let* ((file (expand-file-name "agendas.org"
+				 org-mobile-directory))
+	 (org-agenda-custom-commands
+	  (list (append (org-mobile-sumo-agenda-command)
+			(list (list file))))))
+    (unless (file-writable-p file)
+      (error "Cannot write to file %s" file))
+    (org-batch-store-agenda-views)))
+
+(defun org-mobile-move-capture ()
+  "Move the contents of the capture file to the inbox file.
+Return a marker to the location where the new content has been added.
+If nothing new has beed added, return nil."
+  (interactive)
+  (let ((inbox-buffer (find-file-noselect org-mobile-inbox-for-pull))
+	(capture-buffer (find-file-noselect
+			 (expand-file-name org-mobile-capture-file
+					   org-mobile-directory)))
+	(insertion-point (make-marker))
+	not-empty content)
+    (save-excursion
+      (set-buffer capture-buffer)
+      (setq content (buffer-string))
+      (setq not-empty (string-match "\\S-" content))
+      (when not-empty
+	(set-buffer inbox-buffer)
+	(widen)
+	(goto-char (point-max))
+	(or (bolp) (newline))
+	(move-marker insertion-point
+		     (prog1 (point) (insert content)))
+	(save-buffer)
+	(set-buffer capture-buffer)
+	(erase-buffer)
+	(save-buffer)))
+    (kill-buffer capture-buffer)
+    (if not-empty insertion-point)))
+
+(defun org-mobile-apply-flags (&optional beg end)
+  "Apply all flags in the current buffer.
+If BEG and END are given, only do this in that region."
+  (interactive)
+  (setq org-mobile-last-flagged-files nil)
+  (setq beg (or beg (point-min)) end (or end (point-max)))
+  (goto-char beg)
+  (let ((marker (make-marker))
+	(end (move-marker (make-marker) end))
+	action id id-pos cmd text)
+    (while (re-search-forward
+	    "^\\*+[ \t]+F(\\([^()\n]*\\))[ \t]+\\[\\[id:\\([^]\n ]+\\)" end t)
+      (goto-char (- (match-beginning 1) 2))
+      (catch 'next
+	(setq action (match-string 1)
+	      id (match-string 2)
+	      cmd (if (equal action "")
+		      '(progn
+			 (org-toggle-tag "FLAGGED" 'on)
+			 (and text (org-entry-put nil "THEFLAGGINGNOTE" text)))
+		    (cdr (assoc action org-mobile-action-alist)))
+	      text (org-trim (buffer-substring (1+ (point-at-eol))
+					       (save-excursion
+						 (org-end-of-subtree t))))
+	      id-pos (org-id-find id 'marker))
+	(if (> (length text) 0)
+	    ;; Make TEXT into a single line, to fit into a property
+	    (setq text (mapconcat 'identity
+				  (org-split-string text "\n")
+				  "\\n"))
+	  (setq text nil))
+	(unless id-pos
+	  (insert "BAD ID REFERENCE ")
+	  (throw 'next t))
+	(unless cmd
+	  (insert "BAD FLAG ")
+	  (throw 'next t))
+	(move-marker marker (point))
+	(save-excursion
+	  (condition-case nil
+	      (org-with-point-at id-pos
+		(progn
+		  (eval cmd)
+		  (if (member "FLAGGED" (org-get-tags))
+		      (add-to-list 'org-mobile-last-flagged-files
+				   (buffer-file-name (current-buffer))))))
+	    (error
+	     (progn
+	       (switch-to-buffer (marker-buffer marker))
+	       (goto-char marker)
+	       (insert "EXECUTION FAILED ")
+	       (throw 'next t)))))
+	;; If we get here, the action has been applied successfully
+	;; So remove the entry
+	(org-back-to-heading t)
+	(delete-region (point) (org-end-of-subtree t t))))
+    (move-marker marker nil)
+    (move-marker end nil)))
+
+(defun org-mobile-smart-read ()
+  "Parse the entry at point for shortcuts and expand them.
+These shortcuts are meant for fast and easy typing on the limited
+keyboards of a mobile device.  Below we show a list of the shortcuts
+currently implemented.
+
+The entry is expected to contain an inactive time stamp indicating when
+the entry was created.  When setting dates and
+times (for example for deadlines), the time strings are interpreted
+relative to that creation date.
+Abbreviations are expected to take up entire lines, jst because it is so
+easy to type RET on a mobile device.  Abbreviations start with one or two
+letters, followed immediately by a dot and then additional information.
+Generally the entire shortcut line is removed after action have been taken.
+Time stamps will be constructed using `org-read-date'.  So for example a
+line \"dd. 2tue\" will set a deadline on the second Tuesday after the
+creation date.
+
+Here are the shortcuts currently implemented:
+
+dd. string             set deadline
+ss. string             set scheduling
+tt. string             set time tamp, here.
+ti. string             set inactive time
+
+tg. tag1 tag2 tag3     set all these tags, change case where necessary
+td. kwd                set this todo keyword, change case where necessary
+
+FIXME: Hmmm, not sure if we can make his work against the
+auto-correction feature.  Needs a bit more thinking.  So this function
+is currently a noop.")
+
+(provide 'org-mobile)
+
+;; arch-tag: ace0e26c-58f2-4309-8a61-05ec1535f658
+
+;;; org-mobile.el ends here
+

+ 31 - 17
lisp/org.el

@@ -3223,6 +3223,13 @@ If yes, offer to stop it and to save the buffer with the changes."
    "org-indent"
    '(org-indent-mode)))
 
+;; Autoload org-mobile.el
+
+(eval-and-compile
+  (org-autoload
+   "org-mobile"
+   '(org-mobile-push org-mobile-pull org-mobile-create-sumo-agenda)))
+
 ;; Autoload archiving code
 ;; The stuff that is needed for cycling and tags has to be defined here.
 
@@ -7914,7 +7921,7 @@ application the system uses for this file type."
   (cond
    ((and (org-on-heading-p)
 	 (not (org-in-regexp
-	       (concat org-plain-link-re "\\|" 
+	       (concat org-plain-link-re "\\|"
 		       org-bracket-link-regexp "\\|"
 		       org-angle-link-re "\\|"
 		       "[ \t]:[^ \t\n]+:[ \t]*$"))))
@@ -7983,12 +7990,12 @@ application the system uses for this file type."
 	    ;; Check if we need to translate the link
 	    (let ((tmp (funcall org-link-translation-function type path)))
 	      (setq type (car tmp) path (cdr tmp))))
-	
+
 	(cond
-	 
+
 	 ((assoc type org-link-protocols)
 	  (funcall (nth 1 (assoc type org-link-protocols)) path))
-	 
+
 	 ((equal type "mailto")
 	  (let ((cmd (car org-link-mailto-program))
 		(args (cdr org-link-mailto-program)) args1
@@ -8006,14 +8013,14 @@ application the system uses for this file type."
 		      (setq a (replace-match subject t t a)))
 		  (push a args1))))
 	    (apply cmd (nreverse args1))))
-	 
+
 	 ((member type '("http" "https" "ftp" "news"))
 	  (browse-url (concat type ":" (org-link-escape
 					path org-link-escape-chars-browser))))
-	 
+
 	 ((member type '("message"))
 	  (browse-url (concat type ":" path)))
-	 
+
 	 ((string= type "tags")
 	  (org-tags-view in-emacs path))
 	 ((string= type "thisfile")
@@ -8029,10 +8036,10 @@ application the system uses for this file type."
 		       ,pos)))
 	    (condition-case nil (eval cmd)
 	      (error (progn (widen) (eval cmd))))))
-	 
+
 	 ((string= type "tree-match")
 	  (org-occur (concat "\\[" (regexp-quote path) "\\]")))
-	 
+
 	 ((string= type "file")
 	  (if (string-match "::\\([0-9]+\\)\\'" path)
 	      (setq line (string-to-number (match-string 1 path))
@@ -8043,11 +8050,11 @@ application the system uses for this file type."
 	  (if (string-match "[*?{]" (file-name-nondirectory path))
 	      (dired path)
 	    (org-open-file path in-emacs line search)))
-	 
+
 	 ((string= type "news")
 	  (require 'org-gnus)
 	  (org-gnus-follow-link path))
-	 
+
 	 ((string= type "shell")
 	  (let ((cmd path))
 	    (if (or (not org-confirm-shell-link-function)
@@ -8059,7 +8066,7 @@ application the system uses for this file type."
 		  (message "Executing %s" cmd)
 		  (shell-command cmd))
 	      (error "Abort"))))
-	 
+
 	 ((string= type "elisp")
 	  (let ((cmd path))
 	    (if (or (not org-confirm-elisp-link-function)
@@ -8072,7 +8079,7 @@ application the system uses for this file type."
 			     (eval (read cmd))
 			   (call-interactively (read cmd))))
 	      (error "Abort"))))
-	 
+
 	 (t
 	  (browse-url-at-point))))))
    (move-marker org-open-link-marker nil)
@@ -8099,7 +8106,7 @@ there is one, offer it as link number zero."
       (while (re-search-forward re end t)
 	(push (match-string 0) links))
       (setq links (org-uniquify (reverse links))))
-    
+
     (cond
      ((null links) (error "No links"))
      ((equal (length links) 1)
@@ -8866,7 +8873,7 @@ See also `org-refile-use-outline-path' and `org-completion-use-ido'"
     (setq answ (funcall cfunc prompt tbl nil (not new-nodes)
 			nil 'org-refile-history))
     (setq pa (or (assoc answ tbl) (assoc (concat answ "/") tbl)))
-    (if pa 
+    (if pa
 	(progn
 	  (when (or (not org-refile-history)
 		    (not (eq old-hist org-refile-history))
@@ -8918,7 +8925,7 @@ See also `org-refile-use-outline-path' and `org-completion-use-ido'"
   "Read an outline path like a file name."
   (let ((thetable collection)
 	(org-completion-use-ido nil)	   ; does not work with ido.
-	(org-completion-use-iswitchb nil)) ; or iswitchb 
+	(org-completion-use-iswitchb nil)) ; or iswitchb
     (apply
      'org-icompleting-read prompt
      (lambda (string predicate &optional flag)
@@ -9716,7 +9723,7 @@ This should be called with the cursor in a line with a statistics cookie."
 	      (error "No data for statistics cookie"))))
 	(goto-char pos)
 	(move-marker pos nil)))))
-  
+
 (defvar org-entry-property-inherited-from) ;; defined below
 (defun org-update-parent-todo-statistics ()
   "Update any statistics cookie in the parent of the current headline.
@@ -15492,6 +15499,13 @@ See the individual commands for more information."
       :style toggle :selected (and (boundp 'org-export-with-LaTeX-fragments)
 				   org-export-with-LaTeX-fragments)])
     "--"
+    ("MobileOrg"
+     ["Push Files and Views" org-mobile-push t]
+     ["Pull Captured and Flagged" org-mobile-pull t]
+     ["Find FLAGGED Tasks" (org-agenda nil "?") t]
+     "--"
+     ["Setup" (progn (require 'org-mobile) (customize-group 'org-mobile)) t])
+    "--"
     ("Documentation"
      ["Show Version" org-version t]
      ["Info Documentation" org-info t])