Better faces, track selection without playing.
[emus.git] / emus.el
diff --git a/emus.el b/emus.el
index b03c09d..315ca6e 100644 (file)
--- a/emus.el
+++ b/emus.el
   :type '(string))
 
 (defface emus-artist
-  '((t :inherit font-lock-keyword-face))
+  '((t :inherit font-lock-keyword-face :background "#333"))
   "Face used for artist names in browser.")
 
 (defface emus-album
-  '((t :inherit font-lock-function-name-face))
+  '((t :inherit font-lock-function-name-face :background "#222"))
   "Face used for album names in browser.")
 
-(defface emus-title
+(defface emus-track
   '((t :inherit font-lock-string-face))
   "Face used for track titles in browser.")
 
+(defface emus-track-current
+  '((t :inherit font-lock-string-face :inverse-video t))
+  "Face used for track titles in browser.")
+
+(defface emus-cursor
+  '((t :inherit bold))
+  "Face used for current track cursor")
+
 ;;; Library
 ;;
 
@@ -83,7 +91,7 @@
 
 (defun emus-update-records ()
   (interactive)
-  (emus--suspend-cp)
+  (emus-suspend-cp)
   (setq emus-state 'stopped)
   (let ((proc (emus-get-process))
         (tagstr "")
                                    (setq emus-records (reverse emus-records))
                                    (emus-sort-records)
                                    (emus-render-records)
-                                   (emus--resume-cp)))))
+                                   (emus-resume-cp)))))
     (emus-send-cmd "lp" (car filenames))))
 
 (defun emus-sort-records ()
 (defvar emus-state 'stopped)
 (defvar emus-continuous-playback t)
 
-(defun emus--suspend-cp ()
+(defun emus-suspend-cp ()
   (setq emus-continuous-playback nil))
 
-(defun emus--resume-cp ()
+(defun emus-resume-cp ()
   (setq emus-continuous-playback t)
   (set-process-filter (emus-get-process)
                       (lambda (proc string)
                              (emus-play-next)))))
 
 (defun emus-play-record (record)
+  "Set RECORD as current and start playing."
   (let ((old-record emus-current-record))
     (emus-send-cmd "l" (emus-record-file record))
     (setq emus-state 'playing)
     (setq emus-current-record record)
     (emus-update-record old-record)
     (emus-update-record record)
-    (emus--resume-cp)))
+    (emus-resume-cp)))
+
+(defun emus-select-record (record)
+  "Set RECORD as current, but do not start playing."
+  (let ((old-record emus-current-record))
+    (setq emus-state 'stopped)
+    (setq emus-current-record record)
+    (emus-update-record old-record)
+    (emus-update-record record)
+    (emus-send-cmd "o")
+    (emus-resume-cp)))
 
 (defun emus-stop ()
+  "Stop playback of the current record."
   (interactive)
   (setq emus-state 'stopped)
   (emus-update-record emus-current-record)
   (interactive)
   (emus-volume-delta -10))
 
-(defun emus--play-nearby (offset)
+(defun emus-play-nearby (offset)
   (let ((idx (seq-position emus-records emus-current-record)))
     (if idx
         (let ((next-record (elt emus-records (+ idx offset))))
           (if next-record
-              (emus-play-record next-record)
+              (if (eq emus-state 'playing)
+                  (emus-play-record next-record)
+                (emus-select-record next-record))
             (error "Track does not exist")))
       (error "No track is currently selected."))))
 
 (defun emus-play-next ()
   (interactive)
-  (emus--play-nearby 1))
+  (emus-play-nearby 1))
 
 (defun emus-play-prev ()
   (interactive)
-  (emus--play-nearby -1))
+  (emus-play-nearby -1))
 
 (defun emus-display-status ()
   (interactive)
 ;;; Browser
 ;;
 
-(defun emus-insert-record (record &optional pref-record)
-  (emus-set-record-browser-pos record (point))
-  (let ((current (equal record emus-current-record)))
-   (insert-text-button
-    (concat
-     (if current
-         (pcase emus-state
-           ('playing (propertize ">" 'face 'bold))
-           ('paused (propertize ")" 'face 'bold))
-           ('stopped (propertize "]" 'face 'bold)))
-       (propertize " " 'face 'default))
-     (propertize (format "%-20.20s" (emus-record-artist record))
-                 'face 'emus-artist)
-     (propertize (format "%  -20.20s" (emus-record-album record))
-                 'face 'emus-album)
-     (propertize (format "  %s" (emus-record-title record))
-                 'face 'emus-title))
-    'action #'emus-click-record
-    'follow-link t
-    'emus-record record))
-  (insert "\n"))
+(defun emus-insert-record (record &optional prev-record first)
+  (let* ((artist (emus-record-artist record))
+         (album (emus-record-album record))
+         (title (emus-record-title record))
+         (help-str (format "mouse-1, RET: Play '%.30s' (%.20s)" title artist)))
+    (when (or prev-record first)
+      (unless (equal (emus-record-artist prev-record) artist)
+        (insert-text-button
+         (propertize artist 'face 'emus-artist)
+         'action #'emus-click-record
+         'follow-link t
+         'help-echo help-str
+         'emus-record record)
+        (insert (propertize "\n" 'face 'emus-artist)))
+      (unless (equal (emus-record-album prev-record) album)
+        (insert-text-button
+         (propertize (concat "  " album) 'face 'emus-album)
+         'action #'emus-click-record
+         'follow-link t
+         'help-echo help-str
+         'emus-record record)
+        (insert (propertize "\n" 'face 'emus-album))))
+    (emus-set-record-browser-pos record (point))
+    (let ((is-current (equal record emus-current-record)))
+      (insert-text-button
+       (concat
+        (if is-current
+            (propertize
+             (pcase emus-state
+               ('playing "->")
+               ('paused "-)")
+               ('stopped "-]"))
+             'face 'emus-cursor)
+          (propertize "  " 'face 'default))
+        (propertize (format "   %s" title)
+                    'face (if is-current
+                              'emus-track-current
+                            'emus-track)))
+       'action #'emus-click-record
+       'follow-link t
+       'help-echo help-str
+       'emus-record record)
+      (insert (propertize "\n"
+                          'face (if is-current
+                                    'emus-track-current
+                                  'emus-track))))))
 
 (defun emus-update-record (record)
   (let ((record-pos (emus-record-browser-pos record)))
             (goto-char old-point))))))
 
 (defun emus-render-records ()
-  (save-mark-and-excursion
-    (with-current-buffer "*emus*"
-      (let ((inhibit-read-only t))
-        (erase-buffer)
-        (goto-char (point-min))
+  (with-current-buffer "*emus*"
+    (let ((inhibit-read-only t)
+          (old-pos (point)))
+      (erase-buffer)
+      (goto-char (point-min))
+      (let ((prev-record nil))
         (dolist (record emus-records)
-          (emus-insert-record record))))))
+          (emus-insert-record record prev-record (not prev-record))
+          (setq prev-record record)))
+      (goto-char old-pos))))
 
 (defun emus-click-record (button)
   (emus-play-record (button-get button 'emus-record)))
 
+(defun emus-centre-current ()
+  (interactive)
+  (when (get-buffer "*emus*")
+    (switch-to-buffer "*emus*")
+    (when emus-current-record
+      (goto-char (emus-record-browser-pos emus-current-record))
+      (recenter))))
+
 (defun emus-browse ()
   "Switch to *emus* audio library browser."
   (interactive)
     (define-key map (kbd "R") 'emus-update-records)
     (define-key map (kbd "n") 'emus-play-next)
     (define-key map (kbd "p") 'emus-play-prev)
+    (define-key map (kbd "c") 'emus-centre-current)
     (when (fboundp 'evil-define-key*)
       (evil-define-key* 'motion map
         (kbd "SPC") 'emus-playpause
         (kbd "-") 'emus-volume-down
         (kbd "R") 'emus-update-records
         (kbd "n") 'emus-play-next
-        (kbd "p") 'emus-play-prev))
+        (kbd "p") 'emus-play-prev
+        (kbd "c") 'emus-centre-current))
     map)
   "Keymap for emus.")