diff options
-rw-r--r-- | emms-tageditor.el | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/emms-tageditor.el b/emms-tageditor.el deleted file mode 100644 index 54832d5..0000000 --- a/emms-tageditor.el +++ /dev/null @@ -1,449 +0,0 @@ -;;; emms-tageditor.el --- Info-editor for EMMS - -;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. - -;; Author: Ulrik Jensen <terryp@daimi.au.dk> -;; Keywords: - -;; This file 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 file 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; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -;; Boston, MA 02110-1301 USA - -;;; Commentary: - -;; This file provides an info-editor for EMMS. It also provides, as an -;; option, functions for integrating this with the -;; playlist-buffer-interface as well as the mark-module for the pbi. - -;; To activate the basic editor, use this in your EMMS-configuration: - -;; (require 'emms-tageditor) - -;; And call M-x emms-tageditor-edit-current RET, when you find a song -;; with a tag that needs to be changed. - -;; To use the pbi-functionality, add the following to your -;; configuration: - -;; (emms-tageditor-pbi-mode 1) - -;; Which will add 'e' as a key, to edit the track under the point in -;; the pbi. - -;; To use the extended pbi-functionality, with `emms-pbi-mark', use: - -;; (emms-tageditor-pbi-mark-mode 1) - -;; Which likewise will bind 'E' to edit all the tracks marked as a -;; whole, in a way that allows you to set the "album"-tag of each -;; track, or f.ex. to match the filename to a regexp, and set a group -;; from that regexp as the title-tag, or any other. - -;;; Code: - -(require 'emms) -(require 'emms-info) -(require 'emms-pbi) -(require 'emms-pbi-mark) -(require 'widget) -(require 'wid-edit) - -;; Custom -(defgroup emms-tageditor nil - "*A wizard-style tageditor for EMMS." - :group 'emms - :prefix "emms-tageditor-") - -(defcustom emms-tageditor-buffer-name "*emms-tageditor*" - "The name of the buffer used for tag-editing." - :type 'string - :group 'emms-tageditor) - -;; Variables -(defvar emms-tageditor-current-tracks [] - "A vector of the tracks currently being edited.") - -(defvar emms-tageditor-current-infos [] - "A vector of the info-ojects currently being edited.") - -(defvar emms-tageditor-message nil - "A message to show with the form, if non-nil.") - -(defvar emms-tageditor-widgets nil - "A hash map of the widgets on the screen.") - -;; Hashtable interface -(defun emms-tageditor-get-widget (trackidx fieldname) - "Return the widget of FIELDNAME, for the track with TRACKIDX" - (let ((symbol (emms-tageditor-get-widget-id trackidx fieldname))) - (gethash symbol emms-tageditor-widgets))) - -(defun emms-tageditor-set-widget (trackidx fieldname widget) - "Save a reference to WIDGET, as FIELDNAME of the track with TRACKIDX." - (let ((symbol (emms-tageditor-get-widget-id trackidx fieldname))) - (puthash symbol widget emms-tageditor-widgets))) - -;; Helper function for the hashtable -(defun emms-tageditor-get-widget-id (trackidx fieldname) - "Get a symbol ID of the FIELDNAME widget of TRACKIDX." - (intern (concat "-trackidx-" (number-to-string trackidx) - "-" (symbol-name fieldname)))) - -(defun emms-tageditor-read-tag (trackidx) - "Read the form for a single track, and parse it into an info-object." - (let ((info (aref emms-tageditor-current-infos trackidx)) - (track (aref emms-tageditor-current-tracks trackidx)) - (tags '(title artist album note))) - (while tags - (let* ((tag (car tags)) - (infotag (intern (concat "emms-info-" (symbol-name tag))))) - (eval `(setf (,infotag ,info) - ,(widget-value (emms-tageditor-get-widget - trackidx - tag))))) - (setq tags (cdr tags))) - info)) - -;; Parsing the form -(defun emms-tageditor-read-tags (tracks) - "Create a new list of info-object from the form, and return it." - (mapcar 'emms-tageditor-read-tag tracks)) - -;; Event-handling -(defun emms-tageditor-save (widget &rest ignore) - "Save the info of a single tag." - ;; This function only saves a single form. Figure out which track - ;; this is bound to, by extracting the trackidx from the - (let* ((trackidx (widget-get widget :trackidx)) - (track (aref emms-tageditor-current-tracks trackidx))) - ;; Let's make sure we have an info-source capable of writing tags. - (if (funcall (emms-info-method-for track) 'set) - (progn - ;; we have an info-method for it, let's set it. - (emms-info-set track - (emms-tageditor-read-tag trackidx)) - (when (and (featurep 'emms-pbi) - (get-buffer emms-pbi-playlist-buffer-name)) - ;; If a playlist is available, it's info might need to be updated - ;; for this track. - (emms-pbi-entry-update-track track) - (when (= (length emms-tageditor-current-tracks) 1) - ;; pressing save should kill the buffer when only one track is - ;; being edited. - (emms-tageditor-cleanup)))) - ;; if the above returned nil, no function to save info for this - ;; track has been made! signal an error and escape! - (message (format (concat "Track %s doesn't have an associated info-method " - " capable of saving data") - (emms-track-name track)))))) - -(defun emms-tageditor-save-all () - "Save all entries currently being edited." - ;; Loop through all forms, and save them - (let ((idx 0)) - (while (< idx (length emms-tageditor-current-tracks)) - (emms-tageditor-save (emms-tageditor-get-widget idx 'save)) - (setq idx (1+ idx)))) - ;; Always cleanup when saving everything - (emms-tageditor-cleanup)) - -(defun emms-tageditor-cancel (&rest ignore) - (emms-tageditor-cleanup)) - -(defun emms-tageditor-create-string (rep times) - "Concat TIMES occurances of REP into a string and return it." - (if (> times 1) - (concat (emms-tageditor-create-string rep (1- times)) rep) - rep)) - -;; Setting up the form, and destroying it -(defun emms-tageditor-create-widgets (trackidx info) - "Create widgets for a single track-form" - (let ((inhibit-read-only t) - (track (aref emms-tageditor-current-tracks trackidx)) - (info (aref emms-tageditor-current-infos trackidx))) - (goto-char (point-min)) - (widget-insert (format "Editing tag for track: %s (%s)\n" - (emms-track-name track) - (symbol-name - (emms-track-type track)))) - (widget-insert (concat "----------------------------------------" - "----------------------------------------" - "\n")) - ;; Insert the tags - (let ((tags '(title artist album note))) - (while tags - (let* ((tag (car tags)) - (info-tag (intern (concat "emms-info-" (symbol-name tag)))) - (tag-name - (concat (upcase-initials (symbol-name tag)) ":" - (emms-tageditor-create-string " " - (- 10 (1+ (length (symbol-name tag)))))))) - (widget-insert tag-name) - (eval `(emms-tageditor-set-widget - trackidx (quote ,tag) - (widget-create 'editable-field - :size 69 - :trackidx trackidx - :value (,info-tag ,info)))) - (when (not (= (length tags) 1)) - (widget-insert "\n"))) - (setq tags (cdr tags)))) - ;; Insert the rest - (widget-insert (concat "\n" - "----------------------------------------" - "----------------------------------------" - "\n")) - (emms-tageditor-set-widget - trackidx 'save - (widget-create 'push-button - :notify 'emms-tageditor-save - :trackidx trackidx - :help-echo "Save changes to this tag" - "Save")) - (widget-insert " ") - (widget-create 'push-button - :notify 'emms-tageditor-cancel - :help-echo "Cancel changes" - "Cancel"))) - -(defun emms-tageditor-cleanup () - "Clean up and exit the tageditor." - ;; delete all widgets - (let ((idx 0)) - (while (< idx (length emms-tageditor-current-tracks)) - (when emms-tageditor-widgets - (let ((tags '(title artist album note save))) - (while tags - (let ((tag (car tags))) - (eval `(widget-delete (emms-tageditor-get-widget idx tag)))) - (setq tags (cdr tags))))) - ;; continue idx loop - (setq idx (1+ idx)))) - ;; kill the buffer & delete the hashmap - (setq emms-tageditor-widgets nil) - (kill-buffer (get-buffer-create emms-tageditor-buffer-name))) - -(defun emms-tageditor-replace-create-replacement (replace-with trackidx) - (let ((info (aref emms-tageditor-current-infos trackidx)) - (track (aref emms-tageditor-current-tracks trackidx))) - (setq replace-with (emms-replace-regexp-in-string "$TITLE" (emms-info-title info) replace-with)) - (setq replace-with (emms-replace-regexp-in-string "$ALBUM" (emms-info-album info) replace-with)) - (setq replace-with (emms-replace-regexp-in-string "$ARTIST" (emms-info-artist info) replace-with)) - (setq replace-with (emms-replace-regexp-in-string "$NOTE" (emms-info-note info) replace-with)) - (setq replace-with (emms-replace-regexp-in-string "$TRACKNAME" (emms-track-name track) replace-with))) - replace-with) - -(defun emms-tageditor-replace-tag (field regexp replace-with) - "Replace REGEXP with REPLACE-WITH in all fields of type FIELD." - (let ((idx 0)) - (while (< idx (length emms-tageditor-current-tracks)) - ;; Find the widget for the current track - (let ((widget (emms-tageditor-get-widget idx field))) - (let* ((str (widget-value widget)) - (str (emms-replace-regexp-in-string regexp replace-with str))) - (if (string= "$SET" regexp) - (widget-value-set - widget - (emms-tageditor-replace-create-replacement replace-with idx)) - (widget-value-set - widget - (emms-tageditor-replace-create-replacement str idx))))) - (setq idx (1+ idx))))) - -(defun emms-tageditor-replace-tags (&optional field regexp replace-with) - "Replace REGEXP with REPLACE-WITH in the widgets matching FIELD." - (interactive) - (setq field (or field (intern (completing-read - "Select which tags to replace in: " - '(("all" . all) ("title" . title) - ("artist" . artist) ("album" . album) - ("note" . note)) - nil t "title")))) - (setq regexp (or regexp (read-from-minibuffer "Regexp to replace: "))) - (setq replace-with (or replace-with (read-from-minibuffer (concat "Replace regexp " regexp " with: ")))) - ;; Having all input, let's continue to act on it. - (when (and field regexp replace-with) - ;; two cases, 'all or something else - (if (equal field 'all) - (progn - ;; We need a sweep-search of all tag-fields - (let ((tags '(title artist album note))) - (while tags - (emms-tageditor-replace-tag (car tags) regexp replace-with) - (setq tags (cdr tags))))) - ;; only search the field called field - (emms-tageditor-replace-tag field regexp replace-with)) - ;; we've probably changed some widget values, so we need to make - ;; them count. - (widget-setup))) - -;; Setting up the buffer -(defun emms-tageditor-edit (tracks &optional infos) - "Open an editor for the vector TRACKS. - -Optionally, use the vector INFOS as the default info for each track, -and use the function SAVEFUNCTION as the event-handler for each -save-button." - ;; Save variables - (setq emms-tageditor-current-tracks tracks) - (if infos - (setq emms-tageditor-current-infos infos) - ;; Otherwise, create the vector of infos by loading them. - (setq emms-tageditor-current-infos - (make-vector (length emms-tageditor-current-tracks) - nil)) - (let ((idx 0)) - (while (< idx (length emms-tageditor-current-tracks)) - (setf (aref emms-tageditor-current-infos idx) - ;; should we allow cache here? - (emms-info-get (aref emms-tageditor-current-tracks idx))) - (setq idx (1+ idx))))) - ;; Kill the buffer, then recreate it. Otherwise, everything will be - ;; in one big widget. - (kill-buffer (get-buffer-create emms-tageditor-buffer-name)) - (switch-to-buffer (get-buffer-create emms-tageditor-buffer-name)) - ;; Initialise buffer - (kill-all-local-variables) - (widget-minor-mode 1) - ;; Setup widget hashmap, - (setq emms-tageditor-widgets (make-hash-table :test 'equal)) - ;; and create the widgets - (let ((idx 0)) - (while (< idx (length emms-tageditor-current-tracks)) - (emms-tageditor-create-widgets idx - (aref emms-tageditor-current-infos idx)) - (widget-insert "\n\n") - (setq idx (1+ idx))) - ;; Create the save _all_ widget? - ;; setup the help-message - (when emms-tageditor-message - (goto-char (point-max)) - (widget-insert (concat "\n" - "........................................" - "........................................" - "\n")) - (widget-insert emms-tageditor-message) - (setq emms-tageditor-message nil)) - (use-local-map widget-keymap) - ;; Bind some additional keys - (widget-setup) - (local-set-key (kbd "C-x C-s") (lambda () (interactive) (emms-tageditor-save-all))) - (local-set-key (kbd "C-c C-r") 'emms-tageditor-replace-tags) - (local-set-key (kbd "ESC") (lambda () (interactive) (emms-tageditor-cancel))))) - -;; Entry function -(defun emms-tageditor-edit-current () - "Edit the info of the currently playing track" - (interactive) - (emms-tageditor-edit (vconcat (list (emms-playlist-current-track))))) - -;; Integrating with emms-pbi -(defvar emms-tageditor-pbi-mark-message - "When editing multiple files, some things works a bit -differently. First of all, to save *all* changes made to tracks, use -C-x C-s. - -The changes to each individual track, can be saved by using the -corresponding Save-buttons. - -To utilize the full power of this mode of editing, you should use -M-x emms-tageditor-replace-tags RET, bound to C-c C-r. - -When using `emms-tageditor-replace-tags', you have the following -special keyword available: - -For matching: - - $SET -- Attempting to replace this value with anything, will tell - the function to simply override the previous value. - -For what to replace with: - - $TRACKNAME -- The trackname (for file-type tracks, the full filename) - $TITLE -- The (saved) title of this track. - $ARTIST -- Likewise, with the artist - $ALBUM -- Likewise, with the album - $ALBUM -- Likewise, with the note. - -NOT IMPLEMENTED YET: -If the power of that function doesn't fit your needs, you can use M-x -emms-tageditor-toggle-read-only RET, bound to C-c C-t. This function -will make the buffer read-only, which means you can use the regular -editing functions on the entire buffer. This means that doing an M-x -replace-regexp RET, won't halt if it matches any of the text outsie -widgets, as it would otherwise.") - -(defun emms-tageditor-pbi-mode (&optional arg) - "Register the intergration with the playlist-buffer interface for EMMS. - -Turn the registration on, if and only if ARG is a positive integer, -off otherwise." - (interactive "p") - (if (not (featurep 'emms-pbi)) - (message "You need `emms-pbi' loaded to use this!") - (if (and (numberp arg) (< 0 arg)) - (add-hook 'emms-pbi-mode-hook 'emms-tageditor-pbi-register) - (remove-hook 'emms-pbi-mode-hook 'emms-tageditor-pbi-register)))) - -(defun emms-tageditor-pbi-register () - "Register keybindings for the playlist-buffer interface. - -Should be run in `emms-pbi-mode-hook'." - (local-set-key (kbd "e") - 'emms-tageditor-pbi-edit-current-line)) - -(defun emms-tageditor-pbi-edit-current-line () - "Edit the track under point." - (interactive) - (if (not (featurep 'emms-pbi)) - (message "You need `emms-pbi' loaded to use this!") - ;; Fetch track under current line - (let ((curidx (emms-pbi-return-current-line-index))) - (when (emms-pbi-valid-index-p curidx) - (other-window 1) - (emms-tageditor-edit (vconcat (list (emms-playlist-get-track curidx)))))))) - -;; Integrating with pbi-mark -(defun emms-tageditor-pbi-mark-mode (&optional arg) - "Register the intergration with the playlist-buffer-marks for EMMS. - -Turn the integration on, if and only if ARG is a positive integer, off -otherwise." - (interactive "p") - (if (not (featurep 'emms-pbi-mark)) - (message "You need `emms-pbi-mark' loaded to use this!") - (if (and (numberp arg) (< 0 arg)) - (add-hook 'emms-pbi-mode-hook 'emms-tageditor-pbi-mark-register) - (remove-hook 'emms-pbi-mode-hook 'emms-tageditor-pbi-mark-register)))) - -(defun emms-tageditor-pbi-mark-register () - "Register keybindings for the playlist-buffer interface marking functions. - -Should be run in `emms-pbi-mode-hook'." - (local-set-key (kbd "E") - 'emms-tageditor-pbi-mark-edit-marked-entries)) - -(defun emms-tageditor-pbi-mark-edit-marked-entries () - "Edit all marked entries as one, using a special editor." - (interactive) - (if (not (featurep 'emms-pbi-mark)) - (message "You need `emms-pbi-mark' loaded to use this!") - (other-window 1) - (setq emms-tageditor-message emms-tageditor-pbi-mark-message) - (emms-tageditor-edit (vconcat (emms-pbi-mark-get-marked))) - (goto-char (point-min)))) - -(provide 'emms-tageditor) -;;; emms-tageditor.el ends here |