minor change
[Worg/babel-doc.git] / org-hacks.org
blob58d8abbe1fbb9532e362925797318f0dedcf7afd
1 #+OPTIONS:    H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
2 #+STARTUP:    align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO:   TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS:       Write(w) Update(u) Fix(f) Check(c)
5 #+TITLE:      Org ad hoc code, quick hacks and workarounds
6 #+AUTHOR:     Worg people
7 #+EMAIL:      bzg AT altern DOT org
8 #+LANGUAGE:   en
9 #+PRIORITIES: A C B
10 #+CATEGORY:   worg
12 # This file is the default header for new Org files in Worg.  Feel free
13 # to tailor it to your needs.
15 [[file:index.org][{Back to Worg's index}]]
17 This page is for ad hoc bits of code.  Feel free to add quick hacks and
18 workaround.  Go crazy.
20 * Automatically add an appointment when clocking in a task
22 #+BEGIN_SRC emacs-lisp
23 ;; Make sure you have a sensible value for `appt-message-warning-time'
24 (defvar bzg-org-clock-in-appt-delay 100
25   "Number of minutes for setting an appointment by clocking-in")
26 #+END_SRC
28 This function let's you add an appointment for the current entry.
29 This can be useful when you need a reminder.
31 #+BEGIN_SRC emacs-lisp
32 (defun bzg-org-clock-in-add-appt (&optional n)
33   "Add an appointment for the Org entry at point in N minutes."
34   (interactive)
35   (save-excursion
36     (org-back-to-heading t)
37     (looking-at org-complex-heading-regexp)
38     (let* ((msg (match-string-no-properties 4))
39            (ct-time (decode-time))
40            (appt-min (+ (cadr ct-time)
41                         (or n bzg-org-clock-in-appt-delay)))
42            (appt-time ; define the time for the appointment
43             (progn (setf (cadr ct-time) appt-min) ct-time)))
44       (appt-add (format-time-string
45                  "%H:%M" (apply 'encode-time appt-time)) msg)
46       (if (interactive-p) (message "New appointment for %s" msg)))))
47 #+END_SRC
49 You can advise =org-clock-in= so that =C-c C-x C-i= will automatically
50 add an appointment:
52 #+BEGIN_SRC emacs-lisp
53 (defadvice org-clock-in (after org-clock-in-add-appt activate)
54   "Add an appointment when clocking a task in."
55   (bzg-org-clock-in-add-appt))
56 #+END_SRC
58 You may also want to delete the associated appointment when clocking
59 out.  This function does this:
61 #+BEGIN_SRC emacs-lisp
62 (defun bzg-org-clock-out-delete-appt nil
63   "When clocking out, delete any associated appointment."
64   (interactive)
65   (save-excursion
66     (org-back-to-heading t)
67     (looking-at org-complex-heading-regexp)
68     (let* ((msg (match-string-no-properties 4)))
69       (setq appt-time-msg-list
70             (delete nil
71                     (mapcar
72                      (lambda (appt)
73                        (if (not (string-match (regexp-quote msg)
74                                               (cadr appt))) appt))
75                      appt-time-msg-list)))
76       (appt-check))))
77 #+END_SRC
79 And here is the advice for =org-clock-out= (=C-c C-x C-o=)
81 #+BEGIN_SRC emacs-lisp
82 (defadvice org-clock-out (before org-clock-out-delete-appt activate)
83   "Delete an appointment when clocking a task out."
84   (bzg-org-clock-out-delete-appt))
85 #+END_SRC
87 *IMPORTANT*: You can add appointment by clocking in in both an
88 =org-mode= and an =org-agenda-mode= buffer.  But clocking out from
89 agenda buffer with the advice above will bring an error.
91 * Use Org-mode with Screen [Andrew Hyatt]
93 "The general idea is that you start a task in which all the work will
94 take place in a shell.  This usually is not a leaf-task for me, but
95 usually the parent of a leaf task.  From a task in your org-file, M-x
96 ash-org-screen will prompt for the name of a session.  Give it a name,
97 and it will insert a link.  Open the link at any time to go the screen
98 session containing your work!"
100 http://article.gmane.org/gmane.emacs.orgmode/5276
102 #+BEGIN_SRC emacs-lisp
103 (require 'term)
105 (defun ash-org-goto-screen (name)                                              
106   "Open the screen with the specified name in the window"                      
107   (interactive "MScreen name: ")                                              
108   (let ((screen-buffer-name (ash-org-screen-buffer-name name)))                
109     (if (member screen-buffer-name                                            
110                 (mapcar 'buffer-name (buffer-list)))                          
111         (switch-to-buffer screen-buffer-name)                                  
112       (switch-to-buffer (ash-org-screen-helper name "-dr")))))  
114 (defun ash-org-screen-buffer-name (name)
115   "Returns the buffer name corresponding to the screen name given."
116   (concat "*screen " name "*"))
118 (defun ash-org-screen-helper (name arg)
119   ;; Pick the name of the new buffer.
120   (let ((term-ansi-buffer-name
121          (generate-new-buffer-name 
122           (ash-org-screen-buffer-name name))))
123     (setq term-ansi-buffer-name
124           (term-ansi-make-term 
125            term-ansi-buffer-name "/usr/bin/screen" nil arg name))
126     (set-buffer term-ansi-buffer-name)
127     (term-mode)
128     (term-char-mode)
129     (term-set-escape-char ?\C-x)
130     term-ansi-buffer-name))
132 (defun ash-org-screen (name)
133   "Start a screen session with name"
134   (interactive "MScreen name: ")
135   (save-excursion
136     (ash-org-screen-helper name "-S"))
137   (insert-string (concat "[[screen:" name "]]")))
139 ;; And don't forget to add ("screen" . "elisp:(ash-org-goto-screen
140 ;; \"%s\")") to org-link-abbrev-alist.
141 #+END_SRC
143 * Org Agenda + Appt + Zenity
145 Russell Adams posted this setup [[http://article.gmane.org/gmane.emacs.orgmode/5806][on the list]].  It make sure your agenda
146 appointments are known by Emacs, and it displays warnings in a [[http://live.gnome.org/Zenity][zenity]]
147 popup window.
149 #+BEGIN_SRC emacs-lisp
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 ; For org appointment reminders
153 ;; Get appointments for today
154 (defun my-org-agenda-to-appt ()
155   (interactive)
156   (setq appt-time-msg-list nil)
157   (let ((org-deadline-warning-days 0))    ;; will be automatic in org 5.23
158         (org-agenda-to-appt)))
160 ;; Run once, activate and schedule refresh
161 (my-org-agenda-to-appt)
162 (appt-activate t)
163 (run-at-time "24:01" nil 'my-org-agenda-to-appt)
165 ; 5 minute warnings
166 (setq appt-message-warning-time 15)
167 (setq appt-display-interval 5)
169 ; Update appt each time agenda opened.
170 (add-hook 'org-finalize-agenda-hook 'my-org-agenda-to-appt)
172 ; Setup zenify, we tell appt to use window, and replace default function
173 (setq appt-display-format 'window)
174 (setq appt-disp-window-function (function my-appt-disp-window))
176 (defun my-appt-disp-window (min-to-app new-time msg)                      
177   (save-window-excursion (shell-command (concat 
178     "/usr/bin/zenity --info --title='Appointment' --text='" 
179     msg "' &") nil nil)))
180 #+END_SRC
182 * Org-Mode + gnome-osd
184 Richard Riley uses gnome-osd in interaction with Org-Mode to display
185 appointments.  You can look at the code on the [[http://www.emacswiki.org/emacs-en/OrgMode-OSD][emacswiki]].
187 * remind2org 
189   From Detlef Steuer
191 http://article.gmane.org/gmane.emacs.orgmode/5073
193 #+BEGIN_QUOTE
194 Remind (http://www.roaringpenguin.com/products/remind) is a very powerful
195 command line calendaring program. Its features superseed the possibilities
196 of orgmode in the area of date specifying, so that I want to use it
197 combined with orgmode.
199 Using the script below I'm able use remind and incorporate its output in my
200 agenda views.  The default of using 13 months look ahead is easily
201 changed. It just happens I sometimes like to look a year into the
202 future. :-)
203 #+END_QUOTE
205 * org-remember-anything
207 [[http://www.emacswiki.org/cgi-bin/wiki/Anything][Anything]] users may find the snippet below interesting: 
209 #+BEGIN_SRC emacs-lisp
210 (defvar org-remember-anything
211   '((name . "Org Remember")
212     (candidates . (lambda () (mapcar 'car org-remember-templates)))
213     (action . (lambda (name)
214                 (let* ((orig-template org-remember-templates)
215                        (org-remember-templates
216                         (list (assoc name orig-template))))
217                   (call-interactively 'org-remember))))))
218 #+END_SRC
220 You can add it to your 'anything-sources' variable and open remember directly
221 from anything. I imagine this would be more interesting for people with many
222 remember templatesm, so that you are out of keys to assign those to. You should
223 get something like this:
225 [[file:images/thumbs/org-remember-anything.png]]
227 * Reload Org
229 As of Org version 6.23b (released Sunday Feb 22, 2009) there is a new
230 function to reload org files.
232 Normally you want to use the compiled files since they are faster.
233 If you update your org files you can easily reload them with
235 : M-x org-reload
237 If you run into a bug and want to generate a useful backtrace you can
238 reload the source files instead of the compiled files with
240 : C-u M-x org-reload
242 and turn on the "Enter Debugger On Error" option.  Redo the action
243 that generates the error and cut and paste the resulting backtrace.
244 To switch back to the compiled version just reload again with
246 : M-x org-reload
248 * Split horizontally for agenda
250 If you would like to split the frame into two side-by-side windows when
251 displaying the agenda, try this hack from Jan Rehders, which uses the
252 `toggle-window-split' from
254 http://www.emacswiki.org/cgi-bin/wiki/ToggleWindowSplit
256 #+BEGIN_SRC emacs-lisp
257 ;; Patch org-mode to use vertical splitting
258 (defadvice org-prepare-agenda (after org-fix-split)
259   (toggle-window-split))
260 (ad-activate 'org-prepare-agenda)
261 #+END_SRC
262 * Highlight the agenda line under cursor
264 This is useful to make sure what task you are operating on.
266 #+BEGIN_SRC emacs-lisp
267 (add-hook 'org-agenda-mode-hook '(lambda () (hl-line-mode 1)))
268 #+END_SRC emacs-lisp
270 Under XEmacs:
272 #+BEGIN_SRC emacs-lisp
273 ;; hl-line seems to be only for emacs
274 (require 'highline)
275 (add-hook 'org-agenda-mode-hook '(lambda () (highline-mode 1)))
277 ;; highline-mode does not work straightaway in tty mode.
278 ;; I use a black background
279 (custom-set-faces
280   '(highline-face ((((type tty) (class color)) 
281                     (:background "white" :foreground "black")))))
282 #+END_SRC emacs-lisp
284 * Remove time grid lines that are in an appointment
286 The agenda shows lines for the time grid.  Some people think that
287 these lines are a distraction when there are appointments at those
288 times.  You can get rid of the lines which coincide exactly with the
289 beginning of an appointment.  Michael Ekstrand has written a piece of
290 advice that also removes lines that are somewhere inside an
291 appointment:
293 #+begin_src emacs-lisp
294 (defun org-time-to-minutes (time)
295   "Convert an HHMM time to minutes"
296   (+ (* (/ time 100) 60) (% time 100)))
298 (defun org-time-from-minutes (minutes)
299   "Convert a number of minutes to an HHMM time"
300   (+ (* (/ minutes 60) 100) (% minutes 60)))
302 (defadvice org-agenda-add-time-grid-maybe (around mde-org-agenda-grid-tweakify
303                                                   (list ndays todayp))
304   (if (member 'remove-match (car org-agenda-time-grid))
305       (flet ((extract-window
306               (line)
307               (let ((start (get-text-property 1 'time-of-day line))
308                     (dur (get-text-property 1 'duration line)))
309                 (cond
310                  ((and start dur)
311                   (cons start
312                         (org-time-from-minutes
313                          (+ dur (org-time-to-minutes start)))))
314                  (start start)
315                  (t nil)))))
316         (let* ((windows (delq nil (mapcar 'extract-window list)))
317                (org-agenda-time-grid
318                 (list (car org-agenda-time-grid)
319                       (cadr org-agenda-time-grid)
320                       (remove-if
321                        (lambda (time)
322                          (find-if (lambda (w)
323                                     (if (numberp w)
324                                         (equal w time)
325                                       (and (>= time (car w))
326                                            (< time (cdr w)))))
327                                   windows))
328                        (caddr org-agenda-time-grid)))))
329           ad-do-it))
330     ad-do-it))
331 (ad-activate 'org-agenda-add-time-grid-maybe)
332 #+end_src
334 * Group task list by a property
336 This advice allows you to group a task list in Org-Mode.  To use it,
337 set the variable =org-agenda-group-by-property= to the name of a
338 property in the option list for a TODO or TAGS search.  The resulting
339 agenda view will group tasks by that property prior to searching.
341 #+begin_src emacs-lisp
342 (defvar org-agenda-group-by-property nil
343   "Set this in org-mode agenda views to group tasks by property")
345 (defun org-group-bucket-items (prop items)
346   (let ((buckets ()))
347     (dolist (item items)
348       (let* ((marker (get-text-property 0 'org-marker item))
349              (pvalue (org-entry-get marker prop t))
350              (cell (assoc pvalue buckets)))
351         (if cell
352             (setcdr cell (cons item (cdr cell)))
353           (setq buckets (cons (cons pvalue (list item))
354                               buckets)))))
355     (setq buckets (mapcar (lambda (bucket)
356                             (cons (car bucket)
357                                   (reverse (cdr bucket))))
358                           buckets))
359     (sort buckets (lambda (i1 i2)
360                     (string< (car i1) (car i2))))))
362 (defadvice org-finalize-agenda-entries (around org-group-agenda-finalize
363                                                (list &optional nosort))
364   "Prepare bucketed agenda entry lists"
365   (if org-agenda-group-by-property
366       ;; bucketed, handle appropriately
367       (let ((text ""))
368         (dolist (bucket (org-group-bucket-items
369                          org-agenda-group-by-property
370                          list))
371           (let ((header (concat "Property "
372                                 org-agenda-group-by-property
373                                 " is "
374                                 (or (car bucket) "<nil>") ":\n")))
375             (add-text-properties 0 (1- (length header))
376                                  (list 'face 'org-agenda-structure)
377                                  header)
378             (setq text
379                   (concat text header
380                           ;; recursively process
381                           (let ((org-agenda-group-by-property nil))
382                             (org-finalize-agenda-entries
383                              (cdr bucket) nosort))
384                           "\n\n"))))
385         (setq ad-return-value text))
386     ad-do-it))
387 (ad-activate 'org-finalize-agenda-entries)
388 #+end_src
389 * Link to Gnus messages by Message-Id
391 In a [[http://thread.gmane.org/gmane.emacs.orgmode/8860][recent thread]] on the Org-Mode mailing list, there was some
392 discussion about linking to Gnus messages without encoding the folder
393 name in the link.  The following code hooks in to the store-link
394 function in Gnus to capture links by Message-Id when in nnml folders,
395 and then provides a link type "mid" which can open this link.  The
396 =mde-org-gnus-open-message-link= function uses the
397 =mde-mid-resolve-methods= variable to determine what Gnus backends to
398 scan.  It will go through them, in order, asking each to locate the
399 message and opening it from the first one that reports success.
401 It has only been tested with a single nnml backend, so there may be
402 bugs lurking here and there.
404 The logic for finding the message was adapted from [[http://www.emacswiki.org/cgi-bin/wiki/FindMailByMessageId][an Emacs Wiki
405 article]].
407 #+begin_src emacs-lisp
408 ;; Support for saving Gnus messages by Message-ID
409 (defun mde-org-gnus-save-by-mid ()
410   (when (memq major-mode '(gnus-summary-mode gnus-article-mode))
411     (when (eq major-mode 'gnus-article-mode)
412       (gnus-article-show-summary))
413     (let* ((group gnus-newsgroup-name)
414            (method (gnus-find-method-for-group group)))
415       (when (eq 'nnml (car method))
416         (let* ((article (gnus-summary-article-number))
417                (header (gnus-summary-article-header article))
418                (from (mail-header-from header))
419                (message-id
420                 (save-match-data
421                   (let ((mid (mail-header-id header)))
422                     (if (string-match "<\\(.*\\)>" mid)
423                         (match-string 1 mid)
424                       (error "Malformed message ID header %s" mid)))))
425                (date (mail-header-date header))
426                (subject (gnus-summary-subject-string)))
427           (org-store-link-props :type "mid" :from from :subject subject
428                                 :message-id message-id :group group
429                                 :link (org-make-link "mid:" message-id))
430           (apply 'org-store-link-props
431                  :description (org-email-link-description)
432                  org-store-link-plist)
433           t)))))
435 (defvar mde-mid-resolve-methods '()
436   "List of methods to try when resolving message ID's.  For Gnus,
437 it is a cons of 'gnus and the select (type and name).")
438 (setq mde-mid-resolve-methods
439       '((gnus nnml "")))
441 (defvar mde-org-gnus-open-level 1
442   "Level at which Gnus is started when opening a link")
443 (defun mde-org-gnus-open-message-link (msgid)
444   "Open a message link with Gnus"
445   (require 'gnus)
446   (require 'org-table)
447   (catch 'method-found
448     (message "[MID linker] Resolving %s" msgid)
449     (dolist (method mde-mid-resolve-methods)
450       (cond
451        ((and (eq (car method) 'gnus)
452              (eq (cadr method) 'nnml))
453         (funcall (cdr (assq 'gnus org-link-frame-setup))
454                  mde-org-gnus-open-level)
455         (when gnus-other-frame-object
456           (select-frame gnus-other-frame-object))
457         (let* ((msg-info (nnml-find-group-number
458                           (concat "<" msgid ">")
459                           (cdr method)))
460                (group (and msg-info (car msg-info)))
461                (message (and msg-info (cdr msg-info)))
462                (qname (and group
463                            (if (gnus-methods-equal-p
464                                 (cdr method)
465                                 gnus-select-method)
466                                group
467                              (gnus-group-full-name group (cdr method))))))
468           (when msg-info
469             (gnus-summary-read-group qname nil t)
470             (gnus-summary-goto-article message nil t))
471           (throw 'method-found t)))
472        (t (error "Unknown link type"))))))
474 (eval-after-load 'org-gnus
475   '(progn
476      (add-to-list 'org-store-link-functions 'mde-org-gnus-save-by-mid)
477      (org-add-link-type "mid" 'mde-org-gnus-open-message-link)))
478 #+end_src