diff --git a/Cask b/Cask new file mode 100644 index 0000000..9df7f34 --- /dev/null +++ b/Cask @@ -0,0 +1,7 @@ +(source melpa) +(source org) + +(package "org-timeline" "0.1.0" "Add graphical view of agenda to agenda buffer") + +(depends-on "dash" "2.13.0") +(depends-on "org-plus-contrib") diff --git a/README.md b/README.md new file mode 100644 index 0000000..075eece --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# org-timeline + +Add graphical view of agenda to agenda buffer. + +# Installation + +After you install this package from MELPA Stable, add the following line to your org configuration: + +``` emacs-lisp +(add-hook 'org-agenda-finalize-hook 'org-timeline-insert-timeline :append) +``` + +# How it works + +This package adds a graphical view of the agenda after the last agenda line. By default the display starts at 5 AM today and goes up to 4 AM next day (this covers 24 hours). + +Scheduled tasks or tasks with time ranges are rendered in the display with `RoyalBlue` color. Clocked entires are displayed in `Grey`. The background of timeslots which are in the past is highlighted with `#555555` color. + +You can use custom color for a task by adding the property `TIMELINE_FACE` with either a string which is a color name or a list which specifies the face properties or a symbol which is taken to be a face name. + +# TODO + +- [ ] Add faces instead of colors +- [ ] Make "midnight"/change-of-day configurable (currently 5 AM) diff --git a/org-timeline.el b/org-timeline.el new file mode 100644 index 0000000..31c511a --- /dev/null +++ b/org-timeline.el @@ -0,0 +1,112 @@ +;;; org-timeline.el --- Add graphical view of agenda to agenda buffer. -*- lexical-binding: t -*- + +;; Copyright (C) 2017 Matúš Goljer + +;; Author: Matúš Goljer +;; Maintainer: Matúš Goljer +;; Version: 0.1.0 +;; Created: 16th April 2017 +;; Package-requires: ((dash "2.13.0")) +;; Keywords: calendar + +;; This program is free software; you can redistribute it and/or +;; modify it under the terms of the GNU General Public License +;; as published by the Free Software Foundation; either version 3 +;; of the License, or (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;;; Code: + +(require 'dash) + +(require 'org-agenda) + +(defmacro org-timeline-with-each-line (&rest body) + "Execute BODY on each line in buffer." + (declare (indent 0) + (debug (body))) + `(save-excursion + (goto-char (point-min)) + ,@body + (while (= (forward-line) 0) + ,@body))) + +(defun org-timeline--generate-timeline () + (let* ((start-offset 300) + (current-offset (/ (- (+ (* 60 (string-to-number (format-time-string "%H"))) + (string-to-number (format-time-string "%M"))) + start-offset) 10)) + (slotline (copy-sequence "| | | | | | | | | | | | | | | | | | | | | | | | |")) + (slotline (progn + (when (< 0 current-offset) + (put-text-property 0 current-offset 'font-lock-face '(:background "#555555") slotline)) + slotline)) + (timeline (concat "|05:00|06:00|07:00|08:00|09:00|10:00|11:00|12:00|13:00|14:00|15:00|16:00|17:00|18:00|19:00|20:00|21:00|22:00|23:00|00:00|01:00|02:00|03:00|04:00|" + "\n" + slotline)) + (tasks nil)) + (org-timeline-with-each-line + (-when-let* ((time-of-day (org-get-at-bol 'time-of-day)) + (duration (org-get-at-bol 'duration))) + (when (< duration 0) + (cl-incf duration 1440)) + (let* ((hour (/ time-of-day 100)) + (minute (mod time-of-day 100)) + (beg (+ (* hour 60) minute)) + (end (+ beg duration)) + (face (--if-let (org-entry-get (org-get-at-bol 'org-marker) "TIMELINE_FACE") + (let ((read-face (car (read-from-string it)))) + (if (stringp read-face) + (list :background read-face) + read-face)) + (cond + ((save-excursion + (forward-char 26) + (looking-at "Clocked:")) + '(:background "Grey")) + (t '(:background "RoyalBlue")))))) + (when (>= beg start-offset) + (push (list beg end face) tasks))))) + (setq tasks (nreverse tasks)) + (cl-labels ((get-start-pos (current-line beg) (+ 1 (* current-line (1+ (length slotline))) (/ (- beg start-offset) 10))) + (get-end-pos (current-line end) (+ 1 (* current-line (1+ (length slotline))) (/ (- end start-offset) 10)))) + (let ((current-line 1)) + (with-temp-buffer + (insert timeline) + (-each tasks + (-lambda ((beg end face)) + (while (get-text-property (get-start-pos current-line beg) 'occupied) + (cl-incf current-line) + (when (> (get-start-pos current-line beg) (point-max)) + (save-excursion + (goto-char (point-max)) + (insert "\n" slotline)))) + (let ((start-pos (get-start-pos current-line beg)) + (end-pos (get-end-pos current-line end))) + (put-text-property start-pos end-pos 'font-lock-face face) + (put-text-property start-pos end-pos 'occupied t)) + (setq current-line 1))) + (buffer-string)))))) + +(defun org-timeline-insert-timeline () + (goto-char (point-min)) + (while (eq (get-text-property (line-beginning-position) 'org-agenda-type) 'agenda) + (forward-line)) + (forward-line) + (let ((inhibit-read-only t)) + (insert (org-timeline--generate-timeline)) + (insert (propertize (concat "\n" (make-string (/ (window-width) 2) ?─)) 'face 'org-time-grid) "\n")) + ;; enable `font-lock-mode' in agenda view to display the "chart" + (font-lock-mode)) + +(provide 'org-timeline) +;;; org-timeline.el ends here