fordon_greeman / voidfiles

Branch: 2079ec5
Switch branches/tags
main
theidioticdev add emacs 2079ec5 21h ago
295 lines · 9.9 KB Raw Blame History
1 ;;; emms-mark.el --- mark track like dired -*- lexical-binding: t; -*-
2
3 ;; Copyright (C) 2006-2023 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Ye Wenbin <wenbinye@163.com>
6
7 ;; This file is part of EMMS.
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 3, or (at your option)
12 ;; any later version.
13 ;;
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program; if not, write to the Free Software
21 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 ;;; Commentary:
24
25 ;; Provide mark operation to tracks
26
27 ;; Put this file into your load-path and the following into your ~/.emacs:
28 ;; (require 'emms-mark)
29
30 ;; To activate it for the current buffer only, do:
31 ;; (emms-mark-mode)
32
33 ;; To make this the default EMMS mode, do:
34 ;; (setq emms-playlist-default-major-mode 'emms-mark-mode)
35
36 ;;; Code:
37
38 (provide 'emms-mark)
39 (require 'cl-lib)
40 (require 'emms)
41 (require 'emms-playlist-mode)
42
43 ;;{{{ set new description-function
44 (defun emms-mark-track-description (track)
45 "Return a description of the current track."
46 (cl-assert (not (eq (default-value 'emms-track-description-function)
47 'emms-mark-track-description))
48 nil (concat "Do not set `emms-track-selection-function' to be"
49 " emms-mark-track-description."))
50 (concat " " (funcall (default-value 'emms-track-description-function)
51 track)))
52
53 (defun emms-mark-update-descriptions ()
54 "Update the track descriptions in the current buffer."
55 (emms-with-inhibit-read-only-t
56 (save-excursion
57 (goto-char (point-min))
58 (emms-walk-tracks
59 (emms-playlist-update-track)))))
60 ;;}}}
61
62 ;;{{{ functions to mark tracks
63 (defvar emms-mark-char ?*)
64 (defvar emms-mark-face-alist
65 '((?* . font-lock-warning-face)
66 (?\040 . emms-playlist-track-face)))
67
68 (defun emms-mark-track (&optional arg)
69 "Mark the current track.
70 If ARG is positive, also mark the next ARG-1 tracks as well.
71 If ARG is negative, also mark the previous ARG-1 tracks."
72 (interactive "p")
73 (or arg (setq arg 1))
74 (let ((face (assoc-default emms-mark-char emms-mark-face-alist))
75 buffer-read-only track)
76 (save-excursion
77 (beginning-of-line)
78 (while (and (not (eobp))
79 (/= arg 0))
80 (setq track (get-text-property (point) 'emms-track))
81 (delete-char 1)
82 (insert (emms-propertize (string emms-mark-char)
83 'emms-track track))
84 (backward-char 1)
85 (if (> arg 0)
86 ;; Propertizing forward...
87 (put-text-property (point)
88 (progn (forward-line 1) (point))
89 'face face)
90 ;; ... and backward
91 (let ((start (save-excursion (end-of-line) (point))))
92 (put-text-property (progn (beginning-of-line) (point))
93 start
94 'face face))
95 (forward-line -1))
96 (setq arg (if (> arg 0)
97 (1- arg)
98 (1+ arg)))))))
99
100 (defun emms-mark-unmark-track (&optional arg)
101 "Unmark the current track.
102 If ARG is positive, also unmark the next ARG-1 tracks as well.
103 If ARG is negative, also unmark the previous ARG-1 tracks."
104 (interactive "p")
105 (let ((emms-mark-char ?\040))
106 (emms-mark-track arg)))
107
108 (defun emms-mark-forward (arg)
109 "Mark one or more tracks and move the point past the newly-marked tracks.
110 See `emms-mark-track' for further details."
111 (interactive "p")
112 (emms-mark-track arg)
113 (forward-line arg))
114
115 (defun emms-mark-unmark-forward (arg)
116 "Unmark one or more tracks and move the point past the tracks.
117 See `emms-mark-unmark-track' for further details."
118 (interactive "p")
119 (emms-mark-unmark-track arg)
120 (forward-line arg))
121
122 (defun emms-mark-all ()
123 "Mark all tracks in the current buffer."
124 (interactive)
125 (save-excursion
126 (goto-char (point-min))
127 (emms-mark-track (count-lines (point-min) (point-max)))))
128
129 (defun emms-mark-unmark-all ()
130 "Unmark all tracks in the current buffer."
131 (interactive)
132 (emms-mark-do-with-marked-track 'emms-mark-unmark-track))
133
134 (defun emms-mark-regexp (regexp arg)
135 "Mark all tracks matching REGEXP. A prefix argument means to
136 unmark them instead."
137 (interactive
138 (list
139 (read-from-minibuffer (if current-prefix-arg
140 "Unmark tracks matching: "
141 "Mark tracks matching: "))
142 current-prefix-arg))
143 (let ((emms-mark-char (if arg ?\040 ?*)))
144 (save-excursion
145 (goto-char (point-min))
146 (while (re-search-forward regexp nil t)
147 (emms-mark-track 1)
148 (forward-line 1)))))
149
150 (defun emms-mark-toggle ()
151 "Toggle all marks in the current buffer."
152 (interactive)
153 (save-excursion
154 (goto-char (point-min))
155 (let (buffer-read-only)
156 (while (not (eobp))
157 (if (eq ?\040 (following-char))
158 (emms-mark-track)
159 (emms-mark-unmark-track))
160 (forward-line 1)))))
161
162 (defsubst emms-mark-has-markedp ()
163 "Return non-nil if the playlist has a marked line, nil otherwise."
164 (save-excursion
165 (goto-char (point-min))
166 (re-search-forward (format "^[%c]" emms-mark-char) nil t)))
167
168 ;;}}}
169
170 ;;{{{ functions to operate marked tracks
171 (defun emms-mark-do-with-marked-track (func &optional move)
172 "Call FUNC on every marked line in current playlist.
173 The function specified by FUNC takes no argument, so if the track
174 on the marked line is needed, use `emms-playlist-track-at' to get
175 it.
176
177 The function can also modify the playlist buffer, such as
178 deleting the current line. If the function doesn't move forward,
179 be sure to set the second parameter MOVE to non-nil. Otherwise
180 the function will never exit the loop."
181 (let ((regexp (format "^[%c]" emms-mark-char))
182 (newfunc func))
183 (if move
184 (setq newfunc (lambda () (funcall func) (forward-line 1))))
185 (save-excursion
186 (goto-char (point-min))
187 (while (re-search-forward regexp nil t)
188 (backward-char 1) ; move to beginning of line
189 (funcall newfunc)))))
190
191 (defun emms-mark-mapcar-marked-track (func &optional move)
192 "This function does the same thing as
193 `emms-mark-do-with-marked-track', the only difference being that
194 this function collects the result of FUNC."
195 (let ((regexp (format "^[%c]" emms-mark-char))
196 result (newfunc func))
197 (if move
198 (setq newfunc (lambda () (let ((res (funcall func)))
199 (forward-line 1) res))))
200 (save-excursion
201 (goto-char (point-min))
202 (while (re-search-forward regexp nil t)
203 (backward-char 1) ; move to beginning of line
204 (setq result (cons (funcall newfunc) result)))
205 (nreverse result))))
206
207 (defun emms-mark-delete-marked-tracks ()
208 "Delete all tracks that have been marked in the current buffer."
209 (interactive)
210 (emms-with-inhibit-read-only-t
211 (emms-mark-do-with-marked-track
212 (lambda nil (delete-region (point)
213 (progn (forward-line 1) (point)))))))
214
215 (defun emms-mark-kill-marked-tracks ()
216 "Kill all tracks that have been marked in the current buffer."
217 (interactive)
218 (let (tracks buffer-read-only)
219 (emms-mark-do-with-marked-track
220 (lambda nil
221 (setq tracks
222 (concat tracks
223 (delete-and-extract-region (point)
224 (progn (forward-line 1) (point)))))))
225 (kill-new tracks)))
226
227 (defun emms-mark-copy-marked-tracks ()
228 "Copy all tracks that have been marked in the current buffer."
229 (interactive)
230 (let (tracks)
231 (emms-mark-do-with-marked-track
232 (lambda nil
233 (setq tracks
234 (concat tracks
235 (buffer-substring (point)
236 (progn (forward-line 1) (point)))))))
237 (kill-new tracks)))
238 ;;}}}
239
240 ;;{{{ mode stuff
241 (defvar emms-mark-mode-map
242 (let ((map (make-sparse-keymap)))
243 (define-key map "W" #'emms-mark-copy-marked-tracks)
244 (define-key map "K" #'emms-mark-kill-marked-tracks)
245 (define-key map "D" #'emms-mark-delete-marked-tracks)
246 (define-key map "m" #'emms-mark-forward)
247 (define-key map "u" #'emms-mark-unmark-forward)
248 (define-key map "U" #'emms-mark-unmark-all)
249 (define-key map "t" #'emms-mark-toggle)
250 (define-key map "%m" #'emms-mark-regexp)
251 map)
252 "Keymap for `emms-mark-mode'.")
253
254 (defun emms-mark-mode () ;FIXME: Use `define-derived-mode'.
255 "An EMMS major mode that allows tracks to be marked like dired.
256 \\{emms-mark-mode-map}"
257 (interactive)
258 (if (eq major-mode 'emms-mark-mode)
259 ;; do nothing if we're already in emms-mark-mode
260 nil
261
262 ;; start emms-playlist-mode exactly once
263 (unless (eq major-mode 'emms-playlist-mode)
264 (emms-playlist-mode))
265 (setq emms-playlist-buffer-p t)
266
267 ;; use inherited keymap
268 (set-keymap-parent emms-mark-mode-map (current-local-map))
269 (use-local-map emms-mark-mode-map)
270 (setq major-mode 'emms-mark-mode
271 mode-name "Emms-Mark")
272
273 ;; show a blank space at beginning of each line
274 (set (make-local-variable 'emms-track-description-function)
275 #'emms-mark-track-description)
276 (emms-mark-update-descriptions)))
277
278 (defun emms-mark-mode-disable ()
279 "Disable `emms-mark-mode' and return to `emms-playlist-mode'."
280 (interactive)
281 (if (not (eq major-mode 'emms-mark-mode))
282 ;; do nothing if we're not in emms-mark-mode
283 nil
284
285 ;; call emms-playlist-mode, saving important variables
286 (let ((selected emms-playlist-selected-marker))
287 (emms-playlist-mode)
288 (setq emms-playlist-selected-marker selected)
289 (emms-playlist-mode-overlay-selected))
290
291 ;; update display
292 (emms-mark-update-descriptions)))
293 ;;}}}
294
295 ;;; emms-mark.el ends here