]> code.delx.au - gnu-emacs-elpa/commitdiff
* ampc.el: Sync to version 0.1.3.
authorStefan Monnier <monnier@iro.umontreal.ca>
Wed, 6 Jun 2012 00:56:29 +0000 (20:56 -0400)
committerStefan Monnier <monnier@iro.umontreal.ca>
Wed, 6 Jun 2012 00:56:29 +0000 (20:56 -0400)
git-subtree-dir: packages/ampc
git-subtree-mainline: dec17958e019c17af0947e77533e539a831d00f7
git-subtree-split: c3903564c2b6f23264bd6950a1d836c091ec8f0e

1  2 
packages/ampc/ampc.el

diff --combined packages/ampc/ampc.el
index d2080c0dae922284dc43b465a644e4a34abf53fe,71ef276f9f38be01a3a7af84eb357398ba40e6a5..71ef276f9f38be01a3a7af84eb357398ba40e6a5
@@@ -4,12 -4,12 +4,12 @@@
  
  ;; Author: Christopher Schmidt <christopher@ch.ristopher.com>
  ;; Maintainer: Christopher Schmidt <christopher@ch.ristopher.com>
- ;; Version: 0.1.2
+ ;; Version: 0.1.3
  ;; Created: 2011-12-06
  ;; Keywords: ampc, mpc, mpd
  ;; Compatibility: GNU Emacs: 24.x
  
- ;; This file is part of GNU Emacs.
+ ;; This file is part of ampc.
  
  ;; 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
  ;; Optionally bind a key to this function, e.g.:
  ;;
  ;; (global-set-key (kbd "<f9>") 'ampc)
+ ;;
+ ;; or
+ ;;
+ ;; (global-set-key (kbd "<f9>") (lambda () (interactive) (ampc "host" "port")))
+ ;;
  ;; Byte-compile ampc (M-x byte-compile-file RET /path/to/ampc.el RET) to improve
  ;; its performance!
  
  ;;; ** usage
- ;; To invoke ampc, call the command `ampc', e.g. via M-x ampc RET.  Once ampc is
- ;; connected to the daemon, it creates its window configuration in the selected
- ;; window.  To make ampc use the full frame rather than the selected window,
- ;; customise `ampc-use-full-frame'.
+ ;; To invoke ampc, call the command `ampc', e.g. via M-x ampc RET.  When called
+ ;; interactively, `ampc' reads host address and port from the minibuffer.  If
+ ;; called non-interactively, the first argument to `ampc' is the host, the
+ ;; second is the port.  Both values default to nil, which will make ampc connect
+ ;; to localhost:6600.  Once ampc is connected to the daemon, it creates its
+ ;; window configuration in the selected window.  To make ampc use the full frame
+ ;; rather than the selected window, customise `ampc-use-full-frame'.
  ;;
  ;; ampc offers three independent views which expose different parts of the user
  ;; interface.  The current playlist view, the default view at startup, may be
  ;;
  ;; To mark an entry, move the point to the entry and press `m' (ampc-mark).  To
  ;; unmark an entry, press `u' (ampc-unmark).  To unmark all entries, press `U'
- ;; (ampc-unmark-all).  To toggle marks, press `t' (ampc-toggle-marks).  To
- ;; navigate to the next entry, press `n' (ampc-next-line).  Analogous, pressing
- ;; `p' (ampc-previous-line) moves the point to the previous entry.
+ ;; (ampc-unmark-all).  To toggle marks, press `t' (ampc-toggle-marks).  Pressing
+ ;; `<down-mouse-1>' with the mouse mouse cursor on a list entry will move point
+ ;; to the entry and toggle the mark.  To navigate to the next entry, press `n'
+ ;; (ampc-next-line).  Analogous, pressing `p' (ampc-previous-line) moves the
+ ;; point to the previous entry.
  ;;
  ;; Window two shows the current playlist.  The song that is currently played by
  ;; the daemon, if any, is highlighted.  To delete the selected songs from the
- ;; playlist, press `d' (ampc-delete).  To move the selected songs up, press
- ;; `<up>' (ampc-up).  Analogous, press `<down>' (ampc-down) to move the selected
- ;; songs down.
+ ;; playlist, press `d' (ampc-delete).  Pressing `<down-mouse-3>' will move the
+ ;; point to the entry under cursor and delete it from the playlist.  To move the
+ ;; selected songs up, press `<up>' (ampc-up).  Analogous, press `<down>'
+ ;; (ampc-down) to move the selected songs down.  Pressing `<return>'
+ ;; (ampc-play-this) or `<down-mouse-2>' will play the song at point/cursor.
  ;;
  ;; Windows three to five are tag browsers.  You use them to narrow the song
  ;; database to certain songs.  Think of tag browsers as filters, analogous to
  ;; songs that is filtered is displayed in the header line of the window.
  ;;
  ;; Window six shows the songs that match the filters defined by windows three to
- ;; five.  To add the selected song to the playlist, press `a' (ampc-add).  This
- ;; key binding works in tag browsers as well.  Calling ampc-add in a tag browser
- ;; adds all songs filtered up to the selected browser to the playlist.
+ ;; five.  To add the selected song to the playlist, press `a' (ampc-add).
+ ;; Pressing `<down-mouse-3>' will move the point to the entry under the cursor
+ ;; and execute `ampc-add'.  These key bindings works in tag browsers as well.
+ ;; Calling `ampc-add' in a tag browser adds all songs filtered up to the
+ ;; selected browser to the playlist.
  ;;
  ;; The tag browsers of the (default) current playlist view (accessed via `J')
  ;; are `Genre' (window 3), `Artist' (window 4) and `Album' (window 5).  The key
  ;;; *** outputs view
  ;; The outputs view contains a single list which shows the configured outputs of
  ;; mpd.  To toggle the enabled property of the selected outputs, press `a'
- ;; (ampc-toggle-output-enabled).
+ ;; (ampc-toggle-output-enabled) or `<mouse-3>'.
  
  ;;; *** global keys
  ;; Aside from `J', `M', `K', `<' and `L', which may be used to select different
  ;;
  ;; The keymap of ampc is designed to fit the QWERTY United States keyboard
  ;; layout.  If you use another keyboard layout, feel free to modify
- ;; ampc-mode-map.  For example, I use a regular QWERTZ German keyboard (layout),
- ;; so I modify `ampc-mode-map' in my init.el like this:
+ ;; `ampc-mode-map'.  For example, I use a regular QWERTZ German keyboard
+ ;; (layout), so I modify `ampc-mode-map' in my init.el like this:
  ;;
  ;; (eval-after-load 'ampc
  ;;   '(flet ((substitute-ampc-key
@@@ -291,19 -304,22 +304,22 @@@ all the time!
                      ("Artist" :offset 20)
                      ("Album" :offset 40)
                      ("Time" :offset 60))))
-     `((,(kbd "J")
+     `(("Current playlist view (Genre|Artist|Album)"
+        ,(kbd "J")
         horizontal
         (0.4 vertical
              (6 status)
              (1.0 current-playlist :properties ,pl-prop))
         ,rs_a)
-       (,(kbd "M")
+       ("Current playlist view (Genre|Album|Artist)"
+        ,(kbd "M")
         horizontal
         (0.4 vertical
              (6 status)
              (1.0 current-playlist :properties ,pl-prop))
         ,rs_b)
-       (,(kbd "K")
+       ("Playlist view (Genre|Artist|Album)"
+        ,(kbd "K")
         horizontal
         (0.4 vertical
              (6 status)
                   (0.8 playlist :properties ,pl-prop)
                   (1.0 playlists)))
         ,rs_a)
-       (,(kbd "<")
+       ("Playlist view (Genre|Album|Artist)"
+        ,(kbd "<")
         horizontal
         (0.4 vertical
              (6 status)
                   (0.8 playlist :properties ,pl-prop)
                   (1.0 playlists)))
         ,rs_b)
-       (,(kbd "L")
+       ("Outputs view"
+        ,(kbd "L")
         outputs :properties (("outputname" :title "Name")
                              ("outputenabled" :title "Enabled" :offset 10))))))
  
      (define-key map (kbd "z") 'ampc-suspend)
      (define-key map (kbd "T") 'ampc-trigger-update)
      (loop for view in ampc-views
-           do (define-key map (car view)
+           do (define-key map (cadr view)
                 `(lambda ()
                    (interactive)
-                   (ampc-configure-frame ',(cdr view)))))
+                   (ampc-change-view ',view))))
      map))
  
  (defvar ampc-item-mode-map
      (define-key map (kbd "U") 'ampc-unmark-all)
      (define-key map (kbd "n") 'ampc-next-line)
      (define-key map (kbd "p") 'ampc-previous-line)
+     (define-key map [remap next-line] 'ampc-next-line)
+     (define-key map [remap previous-line] 'ampc-previous-line)
+     (define-key map (kbd "<down-mouse-1>") 'ampc-mouse-toggle-mark)
+     (define-key map (kbd "<mouse-1>") 'ampc-mouse-align-point)
      map))
  
  (defvar ampc-current-playlist-mode-map
    (let ((map (make-sparse-keymap)))
      (suppress-keymap map)
      (define-key map (kbd "<return>") 'ampc-play-this)
+     (define-key map (kbd "<down-mouse-2>") 'ampc-mouse-play-this)
+     (define-key map (kbd "<mouse-2>") 'ampc-mouse-align-point)
+     (define-key map (kbd "<down-mouse-3>") 'ampc-mouse-delete)
      map))
  
  (defvar ampc-playlist-mode-map
      (define-key map (kbd "d") 'ampc-delete)
      (define-key map (kbd "<up>") 'ampc-up)
      (define-key map (kbd "<down>") 'ampc-down)
+     (define-key map (kbd "<down-mouse-3>") 'ampc-mouse-delete)
      map))
  
  (defvar ampc-playlists-mode-map
      (suppress-keymap map)
      (define-key map (kbd "t") 'ampc-toggle-marks)
      (define-key map (kbd "a") 'ampc-add)
+     (define-key map (kbd "<down-mouse-3>") 'ampc-mouse-add)
+     (define-key map (kbd "<mouse-3>") 'ampc-mouse-align-point)
      map))
  
  (defvar ampc-outputs-mode-map
      (suppress-keymap map)
      (define-key map (kbd "t") 'ampc-toggle-marks)
      (define-key map (kbd "a") 'ampc-toggle-output-enabled)
+     (define-key map (kbd "<down-mouse-3>") 'ampc-mouse-toggle-output-enabled)
+     (define-key map (kbd "<mouse-3>") 'ampc-mouse-align-point)
      map))
  
  ;;; **** menu
- (easy-menu-define ampc-menu ampc-mode-map
-   "Main Menu for ampc"
-   '("ampc"
+ (easy-menu-define nil ampc-mode-map nil
+   `("ampc"
+     ("Change view" ,@(loop for view in ampc-views
+                            collect (vector (car view)
+                                            `(lambda ()
+                                               (interactive)
+                                               (ampc-change-view ',view)))))
+     "--"
      ["Play" ampc-toggle-play
       :visible (and ampc-status
                     (not (equal (cdr (assq 'state ampc-status)) "play")))]
      ["Pause" ampc-toggle-play
       :visible (and ampc-status
                     (equal (cdr (assq 'state ampc-status)) "play"))]
+     ["Stop" (lambda () (interactive) (ampc-toggle-play 4))
+      :visible (and ampc-status
+                    (equal (cdr (assq 'state ampc-status)) "play"))]
+     ["Next" ampc-next]
+     ["Previous" ampc-previous]
      "--"
      ["Clear playlist" ampc-clear]
      ["Shuffle playlist" ampc-shuffle]
      ["Decrease volume" ampc-decrease-volume]
      ["Increase crossfade" ampc-increase-crossfade]
      ["Decrease crossfade" ampc-decrease-crossfade]
-     ["Toggle repeat" ampc-toggle-repeat]
-     ["Toggle random" ampc-toggle-random]
-     ["Toggle consume" ampc-toggle-consume]
+     ["Toggle repeat" ampc-toggle-repeat
+      :style toggle
+      :selected (equal (cdr-safe (assq 'repeat ampc-status)) "1")]
+     ["Toggle random" ampc-toggle-random
+      :style toggle
+      :selected (equal (cdr-safe (assq 'random ampc-status)) "1")]
+     ["Toggle consume" ampc-toggle-consume
+      :style toggle
+      :selected (equal (cdr-safe (assq 'consume ampc-status)) "1")]
      "--"
      ["Trigger update" ampc-trigger-update]
+     ["Suspend" ampc-suspend]
      ["Quit" ampc-quit]))
  
  (easy-menu-define ampc-selection-menu ampc-item-mode-map
      ["Toggle marks" ampc-toggle-marks
       :visible (not (eq (car ampc-type) 'playlists))]))
  
+ (defvar ampc-tool-bar-map
+   (let ((map (make-sparse-keymap)))
+     (tool-bar-local-item
+      "mpc/prev" 'ampc-previous 'previous map
+      :help "Previous")
+     (tool-bar-local-item
+      "mpc/play" 'ampc-toggle-play 'play map
+      :help "Play"
+      :visible '(and ampc-status
+                     (not (equal (cdr (assq 'state ampc-status)) "play"))))
+     (tool-bar-local-item
+      "mpc/pause" 'ampc-toggle-play 'pause map
+      :help "Pause"
+      :visible '(and ampc-status
+                     (equal (cdr (assq 'state ampc-status)) "play")))
+     (tool-bar-local-item
+      "mpc/stop" (lambda () (interactive) (ampc-toggle-play 4)) 'stop map
+      :help "Stop"
+      :visible '(and ampc-status
+                     (equal (cdr (assq 'state ampc-status)) "play")))
+     (tool-bar-local-item
+      "mpc/next" 'ampc-next 'next map
+      :help "Next")
+     map))
  ;;; ** code
  ;;; *** macros
  (defmacro ampc-with-buffer (type &rest body)
                 when (get-text-property (point) 'updated)
                 do (delete-region (point) (1+ (line-end-position)))
                 else
-                do (forward-line nil)
+                do (add-text-properties
+                    (+ (point) 2)
+                    (progn (forward-line nil)
+                           (1- (point)))
+                    '(mouse-face highlight))
                 end)
           (goto-char point)
           (ampc-align-point))
                 do (save-excursion
                      ,@body))
         (loop until (eobp)
-              for index from 0 to (1- (prefix-numeric-value arg-))
+              for index from 0 to (1- (if (numberp arg-)
+                                          arg-
+                                        (prefix-numeric-value arg-)))
               do (save-excursion
                    (goto-char (line-end-position))
                    ,@body)
  (define-derived-mode ampc-item-mode ampc-mode ""
    nil)
  
- (define-derived-mode ampc-mode fundamental-mode "ampc"
+ (define-derived-mode ampc-mode special-mode "ampc"
    nil
    (buffer-disable-undo)
-   (setf buffer-read-only t
-         truncate-lines ampc-truncate-lines
+   (set (make-local-variable 'tool-bar-map) ampc-tool-bar-map)
+   (setf truncate-lines ampc-truncate-lines
          font-lock-defaults '((("^\\(\\*\\)\\(.*\\)$"
                                 (1 'ampc-mark-face)
                                 (2 'ampc-marked-face))
                (2 'ampc-current-song-marked-face)))))
  
  ;;; *** internal functions
+ (defun ampc-change-view (view)
+   (if (equal ampc-outstanding-commands '((idle)))
+       (ampc-configure-frame (cddr view))
+     (message "ampc is busy, cannot change window layout")))
  (defun ampc-quote (string)
    (concat "\"" (replace-regexp-in-string "\"" "\\\"" string) "\""))
  
                                  data)
             (ampc-send-command 'add t (ampc-quote data))))
          (t
-          (loop for d in data
+          (loop for d in (reverse data)
                 do (ampc-add-impl (cdr (assoc "file" d)))))))
  
  (defun* ampc-skip (N &aux (song (cdr-safe (assq 'song ampc-status))))
                       ((tag song)
                        (if (assoc (ampc-tags) ampc-internal-db)
                            (ampc-fill-tag-song)
-                         (push `(,(ampc-tags) . ,(ampc-create-tree))
-                               ampc-internal-db)
+                         (push `(,(ampc-tags) . nil) ampc-internal-db)
                          (ampc-send-command 'listallinfo)))
                       (status
                        (ampc-send-command 'status)
                                                       (t a))))))
  
  (defun ampc-tree< (a b)
-   (not (string< (if (listp a) (car a) a) (if (listp b) (car b) b))))
+   (string< (car a) (car b)))
  
  (defun ampc-create-tree ()
    (avl-tree-create 'ampc-tree<))
    (loop with new-trees
          finally return new-trees
          for tree in trees
+         when tree
          do (avl-tree-mapc (lambda (e)
                              (when (ampc-insert (car e) (cdr e) t)
                                (push (cdr e) new-trees)))
-                           tree)))
+                           tree)
+         end))
  
  (defun ampc-fill-song (trees)
    (loop
                            (line-beginning-position))
          then next
          while origin
-         for next  = (progn
-                       (forward-char)
-                       (and (search-forward-regexp "^file: " nil t)
-                            (move-beginning-of-line nil)))
-         while next
+         do (goto-char (1+ origin))
+         for next = (and (search-forward-regexp "^file: " nil t)
+                         (line-beginning-position))
+         while (or (not running) next)
          do (save-restriction
-              (narrow-to-region origin next)
+              (narrow-to-region origin (or next (point-max)))
               (ampc-fill-internal-db-entry))
-         (goto-char origin)
-         (when running
-           (delete-region origin next)
-           (setf next origin))))
+         do (when running
+              (delete-region origin next)
+              (setf next origin))))
  
  (defun ampc-tags ()
    (loop for w in (ampc-windows)
  (defun ampc-fill-internal-db-entry ()
    (loop
     with data-buffer = (current-buffer)
-    with tree = `(nil . ,(cdr (assoc (ampc-tags) ampc-internal-db)))
+    with tree = (assoc (ampc-tags) ampc-internal-db)
     for w in (ampc-windows)
     do
     (with-current-buffer (window-buffer w)
       (ampc-set-dirty t)
       (ecase (car ampc-type)
         (tag
-         (let* ((data (or (ampc-extract (cdr ampc-type) data-buffer)
-                          "[Not Specified]"))
-                (member (and (cdr tree) (avl-tree-member (cdr tree) data))))
-           (cond (member (setf tree member))
-                 ((cdr tree)
-                  (setf member `(,data . nil))
-                  (avl-tree-enter (cdr tree) member)
-                  (setf tree member))
-                 (t
-                  (setf (cdr tree) (ampc-create-tree) member`(,data . nil))
-                  (avl-tree-enter (cdr tree) member)
-                  (setf tree member)))))
+         (let ((data (or (ampc-extract (cdr ampc-type) data-buffer)
+                         "[Not Specified]")))
+           (unless (cdr tree)
+             (setf (cdr tree) (ampc-create-tree)))
+           (setf tree (avl-tree-enter (cdr tree)
+                                      `(,data . nil)
+                                      (lambda (data match)
+                                        match)))))
         (song
          (push (loop for p in `(("file")
                                 ,@(plist-get (cdr ampc-type) :properties))
                                          (lambda (a b) (< (car a) (car b))))))
    (ampc-update))
  
+ (defun ampc-mouse-play-this (event)
+   (interactive "e")
+   (select-window (posn-window (event-end event)))
+   (goto-char (posn-point (event-end event)))
+   (ampc-play-this))
+ (defun ampc-mouse-delete (event)
+   (interactive "e")
+   (select-window (posn-window (event-end event)))
+   (goto-char (posn-point (event-end event)))
+   (ampc-delete 1))
+ (defun ampc-mouse-add (event)
+   (interactive "e")
+   (select-window (posn-window (event-end event)))
+   (goto-char (posn-point (event-end event)))
+   (ampc-add-impl))
+ (defun ampc-mouse-toggle-output-enabled (event)
+   (interactive "e")
+   (select-window (posn-window (event-end event)))
+   (goto-char (posn-point (event-end event)))
+   (ampc-toggle-output-enabled 1))
+ (defun* ampc-mouse-toggle-mark (event &aux buffer-read-only)
+   (interactive "e")
+   (let ((window (posn-window (event-end event))))
+     (when (with-selected-window window
+             (goto-char (posn-point (event-end event)))
+             (unless (eobp)
+               (move-beginning-of-line nil)
+               (ampc-mark-impl (not (eq (char-after) ?*)) 1)
+               t))
+       (select-window window))))
+ (defun ampc-mouse-align-point (event)
+   (interactive "e")
+   (select-window (posn-window (event-end event)))
+   (goto-char (posn-point (event-end event)))
+   (ampc-align-point))
  ;;; *** interactives
  (defun* ampc-unmark-all (&aux buffer-read-only)
    "Remove all marks."
@@@ -1622,7 -1741,8 +1741,8 @@@ If ARG is omitted, use the selected ent
  
  (defun ampc-delete (&optional arg)
    "Delete the next ARG songs from the playlist.
- If ARG is omitted, use the selected entries."
+ If ARG is omitted, use the selected entries.  If ARG is non-nil,
+ all marks after point are removed nontheless."
    (interactive "P")
    (assert (ampc-in-ampc-p))
    (let ((point (point)))
      (ampc-send-command 'clear)))
  
  (defun ampc-add (&optional arg)
-   "Add the next ARG songs associated with the entries after point
+   "Add the songs associated with the next ARG entries after point
  to the playlist.
  If ARG is omitted, use the selected entries in the current buffer."
    (interactive "P")
@@@ -1832,7 -1952,7 +1952,7 @@@ connect to.  The values default to loca
    (unless ampc-connection
      (let ((connection (open-network-stream "ampc"
                                             (with-current-buffer
-                                                (get-buffer-create " *mpc*")
+                                                (get-buffer-create " *ampc*")
                                               (delete-region (point-min)
                                                              (point-max))
                                               (current-buffer))
      (set-process-filter ampc-connection 'ampc-filter)
      (set-process-query-on-exit-flag ampc-connection nil)
      (setf ampc-outstanding-commands '((setup))))
-   (ampc-configure-frame (cdar ampc-views))
+   (ampc-configure-frame (cddar ampc-views))
    (run-hooks 'ampc-connected-hook)
    (ampc-filter (process-buffer ampc-connection) nil))