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
7 #+EMAIL: bzg AT altern DOT org
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
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")
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."
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)))))
49 You can advise =org-clock-in= so that =C-c C-x C-i= will automatically
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))
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."
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
73 (if (not (string-match (regexp-quote msg)
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))
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
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
125 term-ansi-buffer-name "/usr/bin/screen" nil arg name))
126 (set-buffer term-ansi-buffer-name)
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: ")
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.
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]]
149 #+BEGIN_SRC emacs-lisp
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
151 ; For org appointment reminders
153 ;; Get appointments for today
154 (defun my-org-agenda-to-appt ()
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)
163 (run-at-time "24:01" nil 'my-org-agenda-to-appt)
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)))
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]].
191 http://article.gmane.org/gmane.emacs.orgmode/5073
193 :remind (http://www.roaringpenguin.com/products/remind) is a very powerful
194 :command line calendaring program. Its features superseed the possibilities
195 :of orgmode in the area of date specifying, so that I want to use it
196 :combined with orgmode.
198 :Using the script below I'm able use remind and incorporate its output in my
199 :agenda views. The default of using 13 months look ahead is easily
200 :changed. It just happens I sometimes like to look a year into the
203 * org-remember-anything
205 [[http://www.emacswiki.org/cgi-bin/wiki/Anything][Anything]] users may find the snippet below interesting:
207 #+BEGIN_SRC emacs-lisp
208 (defvar org-remember-anything
209 '((name . "Org Remember")
210 (candidates . (lambda () (mapcar 'car org-remember-templates)))
211 (action . (lambda (name)
212 (let* ((orig-template org-remember-templates)
213 (org-remember-templates
214 (list (assoc name orig-template))))
215 (call-interactively 'org-remember))))))
218 You can add it to your 'anything-sources' variable and open remember directly
219 from anything. I imagine this would be more interesting for people with many
220 remember templatesm, so that you are out of keys to assign those to. You should
221 get something like this:
223 [[file:images/thumbs/org-remember-anything.png]]
227 This function by Bernt Hansen reloads Org's compiled files. This is
228 useful when you update and compile Org often.
230 By default it reloads compiled org files. If you call it with a prefix
231 argument it reloads source files.
233 #+BEGIN_SRC emacs-lisp
234 ;; Your org-mode directory
236 (setq my-org-mode-git-directory "~/git/org-mode")
238 (defun org-reload-org (&optional source)
239 "Reload Compiled Org lisp files."
241 (message "source is %s" source)
243 (setq my-org-files "\\.el\\'")
244 (setq my-org-files "\\.elc\\'"))
245 (mapc (lambda(f) (load (car f)))
246 (directory-files-and-attributes (concat my-org-mode-git-directory "/lisp") t my-org-files)))
248 (defun org-reload-org nil
249 "Reload Org lisp files."
251 (mapc (lambda(f) (load (car f)))
252 (directory-files-and-attributes
253 (concat my-org-mode-git-directory "/lisp") t "\\.elc\\'")))
256 Normally you want to use the compiled files since they are faster. If
257 you run into a bug and want to generate a useful backtrace for the maintainer
258 you can reload the source files and enter debugger on error with
260 :C-u M-x org-reload-org
262 and turn on the "Enter Debugger On Error" option. Redo the action
263 that generates the error and cut and paste the resulting backtrace
264 into an email. To switch back to the compiled version just reload again with
268 * Split horizontally for agenda
270 If you would like to split the frame into two side-by-side windows when
271 displaying the agenda, try this hack from Jan Rehders, which uses the
272 `toggle-window-split' from
274 http://www.emacswiki.org/cgi-bin/wiki/ToggleWindowSplit
276 #+BEGIN_SRC emacs-lisp
277 ;; Patch org-mode to use vertical splitting
278 (defadvice org-prepare-agenda (after org-fix-split)
279 (toggle-window-split))
280 (ad-activate 'org-prepare-agenda)
282 * Highlight the agenda line under cursor
284 This is useful to make sure what task you are operating on.
286 #+BEGIN_SRC emacs-lisp
287 (add-hook 'org-agenda-mode-hook '(lambda () (hl-line-mode 1)))
292 #+BEGIN_SRC emacs-lisp
293 ;; hl-line seems to be only for emacs
295 (add-hook 'org-agenda-mode-hook '(lambda () (highline-mode 1)))
297 ;; highline-mode does not work straightaway in tty mode.
298 ;; I use a black background
300 '(highline-face ((((type tty) (class color))
301 (:background "white" :foreground "black")))))
304 * Remove time grid lines that are in an appointment
306 The agenda shows lines for the time grid. Some people think that
307 these lines are a distraction when there are appointments at those
308 times. You can get rid of the lines which coincide exactly with the
309 beginning of an appointment. Michael Ekstrand has written a piece of
310 advice that also removes lines that are somewhere inside an
313 #+begin_src emacs-lisp
314 (defun org-time-to-minutes (time)
315 "Convert an HHMM time to minutes"
316 (+ (* (/ time 100) 60) (% time 100)))
318 (defun org-time-from-minutes (minutes)
319 "Convert a number of minutes to an HHMM time"
320 (+ (* (/ minutes 60) 100) (% minutes 60)))
322 (defadvice org-agenda-add-time-grid-maybe (around mde-org-agenda-grid-tweakify
324 (if (member 'remove-match (car org-agenda-time-grid))
325 (flet ((extract-window
327 (let ((start (get-text-property 1 'time-of-day line))
328 (dur (get-text-property 1 'duration line)))
332 (org-time-from-minutes
333 (+ dur (org-time-to-minutes start)))))
336 (let* ((windows (delq nil (mapcar 'extract-window list)))
337 (org-agenda-time-grid
338 (list (car org-agenda-time-grid)
339 (cadr org-agenda-time-grid)
345 (and (>= time (car w))
348 (caddr org-agenda-time-grid)))))
351 (ad-activate 'org-agenda-add-time-grid-maybe)
354 * Group task list by a property
356 This advice allows you to group a task list in Org-Mode. To use it,
357 set the variable =org-agenda-group-by-property= to the name of a
358 property in the option list for a TODO or TAGS search. The resulting
359 agenda view will group tasks by that property prior to searching.
361 #+begin_src emacs-lisp
362 (defvar org-agenda-group-by-property nil
363 "Set this in org-mode agenda views to group tasks by property")
365 (defun org-group-bucket-items (prop items)
368 (let* ((marker (get-text-property 0 'org-marker item))
369 (pvalue (org-entry-get marker prop t))
370 (cell (assoc pvalue buckets)))
372 (setcdr cell (cons item (cdr cell)))
373 (setq buckets (cons (cons pvalue (list item))
375 (setq buckets (mapcar (lambda (bucket)
377 (reverse (cdr bucket))))
379 (sort buckets (lambda (i1 i2)
380 (string< (car i1) (car i2))))))
382 (defadvice org-finalize-agenda-entries (around org-group-agenda-finalize
383 (list &optional nosort))
384 "Prepare bucketed agenda entry lists"
385 (if org-agenda-group-by-property
386 ;; bucketed, handle appropriately
388 (dolist (bucket (org-group-bucket-items
389 org-agenda-group-by-property
391 (let ((header (concat "Property "
392 org-agenda-group-by-property
394 (or (car bucket) "<nil>") ":\n")))
395 (add-text-properties 0 (1- (length header))
396 (list 'face 'org-agenda-structure)
400 ;; recursively process
401 (let ((org-agenda-group-by-property nil))
402 (org-finalize-agenda-entries
403 (cdr bucket) nosort))
405 (setq ad-return-value text))
407 (ad-activate 'org-finalize-agenda-entries)
409 * Link to Gnus messages by Message-Id
411 In a [[http://thread.gmane.org/gmane.emacs.orgmode/8860][recent thread]] on the Org-Mode mailing list, there was some
412 discussion about linking to Gnus messages without encoding the folder
413 name in the link. The following code hooks in to the store-link
414 function in Gnus to capture links by Message-Id when in nnml folders,
415 and then provides a link type "mid" which can open this link. The
416 =mde-org-gnus-open-message-link= function uses the
417 =mde-mid-resolve-methods= variable to determine what Gnus backends to
418 scan. It will go through them, in order, asking each to locate the
419 message and opening it from the first one that reports success.
421 It has only been tested with a single nnml backend, so there may be
422 bugs lurking here and there.
424 The logic for finding the message was adapted from [[http://www.emacswiki.org/cgi-bin/wiki/FindMailByMessageId][an Emacs Wiki
427 #+begin_src emacs-lisp
428 ;; Support for saving Gnus messages by Message-ID
429 (defun mde-org-gnus-save-by-mid ()
430 (when (memq major-mode '(gnus-summary-mode gnus-article-mode))
431 (when (eq major-mode 'gnus-article-mode)
432 (gnus-article-show-summary))
433 (let* ((group gnus-newsgroup-name)
434 (method (gnus-find-method-for-group group)))
435 (when (eq 'nnml (car method))
436 (let* ((article (gnus-summary-article-number))
437 (header (gnus-summary-article-header article))
438 (from (mail-header-from header))
441 (let ((mid (mail-header-id header)))
442 (if (string-match "<\\(.*\\)>" mid)
444 (error "Malformed message ID header %s" mid)))))
445 (date (mail-header-date header))
446 (subject (gnus-summary-subject-string)))
447 (org-store-link-props :type "mid" :from from :subject subject
448 :message-id message-id :group group
449 :link (org-make-link "mid:" message-id))
450 (apply 'org-store-link-props
451 :description (org-email-link-description)
452 org-store-link-plist)
455 (defvar mde-mid-resolve-methods '()
456 "List of methods to try when resolving message ID's. For Gnus,
457 it is a cons of 'gnus and the select (type and name).")
458 (setq mde-mid-resolve-methods
461 (defvar mde-org-gnus-open-level 1
462 "Level at which Gnus is started when opening a link")
463 (defun mde-org-gnus-open-message-link (msgid)
464 "Open a message link with Gnus"
468 (message "[MID linker] Resolving %s" msgid)
469 (dolist (method mde-mid-resolve-methods)
471 ((and (eq (car method) 'gnus)
472 (eq (cadr method) 'nnml))
473 (funcall (cdr (assq 'gnus org-link-frame-setup))
474 mde-org-gnus-open-level)
475 (when gnus-other-frame-object
476 (select-frame gnus-other-frame-object))
477 (let* ((msg-info (nnml-find-group-number
478 (concat "<" msgid ">")
480 (group (and msg-info (car msg-info)))
481 (message (and msg-info (cdr msg-info)))
483 (if (gnus-methods-equal-p
487 (gnus-group-full-name group (cdr method))))))
489 (gnus-summary-read-group qname nil t)
490 (gnus-summary-goto-article message nil t))
491 (throw 'method-found t)))
492 (t (error "Unknown link type"))))))
494 (eval-after-load 'org-gnus
496 (add-to-list 'org-store-link-functions 'mde-org-gnus-save-by-mid)
497 (org-add-link-type "mid" 'mde-org-gnus-open-message-link)))