Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Konrad Scorciapino
committed
Jan 5, 2012
0 parents
commit 0ca4eb8
Showing
4 changed files
with
256 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
* About the program | ||
|
||
/Tomatinho/ is a simple and beautiful [[http://www.pomodorotechnique.com/][pomodoro technique]] timer that | ||
runs on Emacs and is not bloated with distractive graphics or inorganic | ||
commands. Just press Enter, see time flow and do you best. | ||
|
||
* Using it | ||
|
||
I bind it here to /F12/ with the following command: | ||
|
||
#+BEGIN_SRC lisp | ||
(global-set-key (kbd "<f12>") 'tomatinho) | ||
#+END_SRC | ||
|
||
When you start /Tomatinho/, you automatically begin your first | ||
pomodoro. There is nothing to do at this point, except to work. You | ||
can, of course, restart the pomodoro if you get distracted, or even | ||
the whole series, but the program takes care of itself until the | ||
25-minute mark is reached. At this point, the pause period will | ||
start, and you'll have to press <Enter> when you done. | ||
|
||
This cycle goes on forever. | ||
|
||
* Keybindings | ||
|
||
| Key | Description | | ||
|---------+-------------------------------------------------| | ||
| <Enter> | Forgoes the current pomodoro or leaves a break. | | ||
| R | Resets the timer. | | ||
| <Tab> | Toggles between display modes. | | ||
| q | Kills the buffer. | | ||
| Q | Turns off Tomatinho. | | ||
|
||
* Seeing is believing | ||
|
||
Check out these screenshots! | ||
|
||
- [[http://i.imgur.com/0saTG.png]] | ||
- [[http://i.imgur.com/sqB0M.png]] | ||
|
||
Also try [[http://tomatinho.com]]. |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
;;; tomatinho.el --- Tomatinho | ||
|
||
;; Author: Konrad Scorciapino <konr@konr.mobi> | ||
;; Keywords: time, productivity, pomodoro technique | ||
|
||
;; 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 2, 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
;;; Commentary | ||
|
||
;; Tomatinho is a simple and beautiful [[http://www.pomodorotechnique.com/][pomodoro technique]] timer that | ||
;; runs on Emacs and is not bloated with distractive graphics or inorganic | ||
;; commands. Just press Enter, see time flow and do you best. | ||
|
||
;;; Code | ||
|
||
(defvar tomatinho-buffer "Tomatinho!") | ||
(defvar tomatinho-format "%H:%M:%S") | ||
(defvar tomatinho-timer nil) | ||
(defvar tomatinho-bar-length 25) | ||
(defvar tomatinho-pomodoro-length 25) | ||
(defvar tomatinho-time-face | ||
'(:family "DejaVu Sans" :height 888 :width semi-condensed)) | ||
(defvar tomatinho-map | ||
(let ((map (make-sparse-keymap))) | ||
(define-key map (kbd "q") 'tomatinho-interactive-kill-buffer) | ||
(define-key map (kbd "Q") 'tomatinho-interactive-quit) | ||
(define-key map (kbd "R") 'tomatinho-interactive-reset) | ||
(define-key map (kbd "<return>") 'tomatinho-interactive-new-pomodoro) | ||
(define-key map (kbd "<tab>") 'tomatinho-interactive-toggle-display) | ||
map)) | ||
(defvar tomatinho-ok-face '(:foreground "#ff0000")) | ||
(defvar tomatinho-pause-face '(:foreground "#00ff00")) | ||
(defvar tomatinho-reset-face '(:foreground "#333333")) | ||
(defvar tomatinho-events nil) | ||
(defvar tomatinho-current '(ok . 0)) | ||
(defvar tomatinho-last 0) | ||
(defvar tomatinho-debug nil) | ||
(defvar tomatinho-display-tubes t) | ||
(defvar tomatinho-dir (file-name-directory (or load-file-name buffer-file-name))) | ||
(defvar tomatinho-sound-tick (expand-file-name (concat tomatinho-dir "tick.wav"))) | ||
(defvar tomatinho-sound-tack (expand-file-name (concat tomatinho-dir "tack.wav"))) | ||
|
||
|
||
;;;;;;;;;;;;;;;;; | ||
;; Interactive ;; | ||
;;;;;;;;;;;;;;;;; | ||
|
||
(defun tomatinho-interactive-kill-buffer () | ||
"Kills the buffer." | ||
(interactive) | ||
(kill-current-buffer)) | ||
|
||
(defun tomatinho-interactive-new-pomodoro () | ||
"Forgoes the current pomodoro or leaves a break." | ||
(interactive) | ||
(case (car tomatinho-current) | ||
(ok (setq tomatinho-events | ||
(append tomatinho-events `((reset . ,(cdr tomatinho-current)))) | ||
tomatinho-current '(ok . 0))) | ||
(pause (setq tomatinho-events (append tomatinho-events (list tomatinho-current))))) | ||
(setq tomatinho-current '(ok . 0)) | ||
(play-sound-file-async tomatinho-sound-tick)) | ||
|
||
(defun tomatinho-interactive-reset () | ||
"Resets the timer." | ||
(interactive) | ||
(if (y-or-n-p "Are you sure you want to reset the timer? ") | ||
(progn (setq tomatinho-current '(ok . 0) tomatinho-events nil | ||
tomatinho-last (timestamp)) | ||
(play-sound-file-async tomatinho-sound-tick)) | ||
(message "Pfew! That was close!"))) | ||
|
||
(defun tomatinho-interactive-toggle-display () | ||
"Toggles between display modes." | ||
(interactive) | ||
(setq tomatinho-display-tubes (not tomatinho-display-tubes)) | ||
(tomatinho-update)) | ||
|
||
(defun tomatinho-interactive-quit () | ||
"Turns off Tomatinho." | ||
(interactive) | ||
(if (y-or-n-p "Are you sure you want to turn off Tomatinho? ") | ||
(progn (cancel-timer tomatinho-timer) | ||
(kill-current-buffer) | ||
(play-sound-file-async tomatinho-sound-tick)) | ||
(message "Pfew! That was close!"))) | ||
|
||
|
||
;;;;;;;;;;; | ||
;; Utils ;; | ||
;;;;;;;;;;; | ||
|
||
(defun timestamp () | ||
"Returns the timestamp as an integer." | ||
(string-to-int (format-time-string "%s"))) | ||
|
||
(defun play-sound-file-async (file) | ||
"Plys with some overhead, but at least doesn't freeze Emacs." | ||
(let ((command (car command-line-args))) | ||
(start-process "play-sound-file-async" nil command "-Q" "--batch" "--eval" | ||
(format "(play-sound-file \"%s\")" file)))) | ||
|
||
(defun kill-current-buffer () | ||
"Kills the current-buffer." | ||
(interactive) | ||
(kill-buffer (current-buffer))) | ||
|
||
(defmacro unlocking-buffer (&rest body) | ||
"Macro that allows safer manipulation of a read-only buffer." | ||
`(progn (toggle-read-only -1) | ||
,@body | ||
(toggle-read-only 1))) | ||
|
||
;;;;;;;;;;;;;;;;;;;;;;;;; | ||
;; Display and updates ;; | ||
;;;;;;;;;;;;;;;;;;;;;;;;; | ||
|
||
(defun tomatinho-tubes-string (cons i) | ||
"Auxiliary function to display the tubes correctly." | ||
(let* ((type (car cons)) (amount (cdr cons)) | ||
(length (ceiling (/ (* 1.0 amount tomatinho-bar-length) tomatinho-pomodoro-length))) | ||
(text (make-string length ?░)) | ||
(text (if (not (equal type 'reset)) text | ||
(concat text (make-string (- tomatinho-bar-length length) ?▁)))) | ||
(text (if (equal type 'pause) text (format "\n%d. %s" i text)))) | ||
(propertize text 'font-lock-face | ||
(case type | ||
(ok tomatinho-ok-face) (reset tomatinho-reset-face) | ||
(pause tomatinho-pause-face) (t nil))))) | ||
|
||
(defun tomatinho-display-tubes () | ||
"Displays the pomodoros done so far as a series of tubes." | ||
(let ((i 1)) | ||
(dolist (item (append tomatinho-events (list tomatinho-current))) | ||
(insert (tomatinho-tubes-string item i)) | ||
(unless (equal (car item) 'pause) | ||
(when (equal (car item) 'ok) (setq i (1+ i)))))) | ||
(insert (propertize "→\n\n" 'font-lock-face '(:weight bold))) | ||
(loop for item in tomatinho-events | ||
and extra = (if (equal (car tomatinho-current) 'ok) (cdr tomatinho-current) 0) | ||
when (equal (car item) 'ok) sum (cdr item) into ok | ||
when (equal (car item) 'reset) sum (cdr item) into reset | ||
when (equal (car item) 'pause) sum (cdr item) into pause | ||
finally (insert (format "Currently using %.2f%% of your time in full pomodoros." | ||
(/ (+ ok (or extra (cdr tomatinho-current))) 0.01 | ||
(+ 1e-20 ok reset pause (cdr tomatinho-current))))))) | ||
|
||
(defun tomatinho-display-history () | ||
"Displays the pomodoros done so far as a history log." | ||
(let ((i 0)) | ||
(dolist (item tomatinho-events) | ||
(when (equal (car item) 'ok) (setq i (1+ i))) | ||
(let* ((type (car item)) (val (cdr item)) | ||
(number (format "%d. " i)) | ||
(number (if (equal type 'ok) number (make-string (length number) ? ))) | ||
(m-ok (format "Completed a pomodoro with %d minutes\n" val)) | ||
(m-reset (format "Gave up after %d minutes\n" val)) | ||
(m-pause (format "Had a break of %d minutes\n" val)) | ||
(message (case type | ||
(ok (propertize m-ok 'font-lock-face tomatinho-ok-face)) | ||
(reset (propertize m-reset 'font-lock-face tomatinho-reset-face)) | ||
(pause (propertize m-pause 'font-lock-face tomatinho-pause-face))))) | ||
(insert (concat number message))))) | ||
(let ((type (car tomatinho-current)) (val (cdr tomatinho-current)) | ||
(diff (- (timestamp) tomatinho-last))) | ||
(insert (format "\nCurrently on %s for %d minutes and %d seconds." | ||
(if (equal type 'ok) "a pomodoro" "break") val diff)))) | ||
|
||
|
||
(defun tomatinho-update () | ||
"First updates the variables and then the buffer, if it exists." | ||
(let ((time (timestamp)) (type (car tomatinho-current)) (val (cdr tomatinho-current)) | ||
(l tomatinho-pomodoro-length) (tick tomatinho-sound-tick) (tack tomatinho-sound-tack)) | ||
(when (>= (- time tomatinho-last) (if tomatinho-debug 0 60)) | ||
(setq tomatinho-current (cons type (1+ val)) tomatinho-last time) | ||
(when (and (equal type 'ok) (>= (1+ val) l)) | ||
(setq tomatinho-events (append tomatinho-events `((ok . ,l))) | ||
tomatinho-current '(pause . 0))) | ||
(play-sound-file-async (if (equal (car tomatinho-current) 'ok) tick tack)))) | ||
(when (get-buffer tomatinho-buffer) | ||
(with-current-buffer (get-buffer tomatinho-buffer) | ||
(unlocking-buffer | ||
(delete-region (point-min) (point-max)) | ||
(insert (propertize (format-time-string tomatinho-format) | ||
'font-lock-face tomatinho-time-face)) | ||
(insert "\n") | ||
(if tomatinho-display-tubes (tomatinho-display-tubes) (tomatinho-display-history)))))) | ||
|
||
;;;;;;;;;;;;;;;;;;; | ||
;; Main function ;; | ||
;;;;;;;;;;;;;;;;;;; | ||
|
||
(defun tomatinho () | ||
"A simple and beautiful pomodoro technique timer." | ||
(interactive) | ||
(with-current-buffer (get-buffer-create tomatinho-buffer) | ||
(use-local-map tomatinho-map) (font-lock-mode t)) | ||
(tomatinho-update) | ||
(when tomatinho-timer (cancel-timer tomatinho-timer)) | ||
(setq tomatinho-timer (run-at-time nil 1 'tomatinho-update)) | ||
(switch-to-buffer tomatinho-buffer)) | ||
|
||
|
||
(provide 'tomatinho) |