Skip to content

Commit

Permalink
first commit!
Browse files Browse the repository at this point in the history
  • Loading branch information
Konrad Scorciapino committed Jan 5, 2012
0 parents commit 0ca4eb8
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 0 deletions.
41 changes: 41 additions & 0 deletions README.org
@@ -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 added tack.wav
Binary file not shown.
Binary file added tick.wav
Binary file not shown.
215 changes: 215 additions & 0 deletions tomatinho.el
@@ -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)

0 comments on commit 0ca4eb8

Please sign in to comment.