forked from mpdel/mpdel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mpdel-browser.el
281 lines (206 loc) · 10.4 KB
/
mpdel-browser.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
;;; mpdel-browser.el --- Browsing MPD entities -*- lexical-binding: t; -*-
;; Copyright (c) 2019, 2020 Jose A Ortega Ruiz
;; Author: Jose A Ortega Ruiz <[email protected]>
;; Keywords: multimedia
;; Url: https://github.com/mpdel/mpdel
;; Package-requires: ((emacs "25.1"))
;; Version: 2.1.0
;; 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 3 of the License, 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Executing `mpdel-browser-open' opens the entities browser, letting
;; users to navigate local directories or virtual collections such as
;; playlists offered by the MPD server, as well as search and jump to
;; the "all artists" and "all albums" colections.
;;; Code:
(require 'libmpdel-directory)
(require 'mpdel-core)
(require 'mpdel-tablist)
(require 'mpdel-playlist)
;;; Customization
(defgroup mpdel-browser nil
"Display and navigate listings of MPD entities."
:group 'libmpdel)
(defface mpdel-browser-directory-face
'((t . (:inherit italic)))
"Face for directories in browser.")
(defcustom mpdel-browser-top-level-entries
'(directories empty-line
albums artists empty-line
stored-playlists current-playlist empty-line
search-album search-title search-artist search-filter)
"A list of the entries to show in the browser's top level buffer.
Each entry is shown as a selectable line with the entry's
description; selecting it with the keyboard or mouse will list
its contents in a new buffer."
:type '(repeat (choice (const :tag "Directories" directories)
(string :tag "Directory")
(const :tag "All albums" albums)
(const :tag "All artists" artists)
(const :tag "Stored playlists" stored-playlists)
(const :tag "Current playlist" current-playlist)
(const :tag "Search by artist" search-artist)
(const :tag "Search by album" search-album)
(const :tag "Search by title" search-title)
(const :tag "Search using filter" search-filter)
(const :tag "Separator" empty-line))))
(defcustom mpdel-browser-list-clean-up-function #'identity
"Function called with the list of entries to be displayed, for clean-up.
The function is called with the list of retrieved entries, and
should return a new list of entries, possibly modified and
re-ordered. Use cases include elimination of duplicates (some
backends accumulate renamed songs in their listings) or custom
orderings."
:type 'function)
;;; Formatting of directories in a tablist
(defvar mpdel-browser--song-format (vector (list "Directory / Title" 30 t)
(list "#" 6 nil)
(list "Album" 30 t)
(list "Disk" 4 t)
(list "Date" 5 t)
(list "Artist" 0 t)))
(defun mpdel-browser--directory-format (parent)
"Return the navigel column format for a directory that is a child of PARENT."
(vector (list (or (libmpdel--directory-path parent) "") 60 t)))
(defvar mpdel-browser--retrieving-format (vector (list "Retrieving ..." 60 t))
"Format of the tabulated list before the children of an entity are known.")
(defun mpdel-browser--includes-songs-p (children)
"Check whether there is any song among CHILDREN."
(cl-some #'libmpdel-song-p children))
(defun mpdel-browser--format (parent-directory children)
"Return format for a directory, given its PARENT-DIRECTORY and its CHILDREN."
(if (mpdel-browser--includes-songs-p children)
mpdel-browser--song-format
(mpdel-browser--directory-format parent-directory)))
(defun mpdel-browser--directory-columns (directory)
"Return a column for DIRECTORY containing its name."
(vector (propertize (or directory "") 'face 'mpdel-browser-directory-face)
"" "" "" "" ""))
(navigel-method mpdel navigel-tablist-format-children ((directory libmpdel-directory) children)
(mpdel-browser--format directory children))
(navigel-method mpdel navigel-tablist-format-children ((_e (eql directories)) _c)
(vector (list "Directories" 60 t)))
(navigel-method mpdel navigel-tablist-format ((_e libmpdel-directory))
mpdel-browser--retrieving-format)
(navigel-method mpdel navigel-tablist-format ((_e (eql directories)))
mpdel-browser--retrieving-format)
(navigel-method mpdel navigel-entity-to-columns ((directory libmpdel-directory))
(mpdel-browser--directory-columns (libmpdel-entity-name directory)))
(navigel-method mpdel navigel-entity-to-columns ((_e (eql directories)))
(vector "Music directory"))
;;; Browser buffers
(defun mpdel-browser--buffer-name (entity)
"Return the name of a browser buffer displaying ENTITY."
(format "* %s *"
(cond ((stringp entity) entity)
((libmpdel-directory-p entity)
(file-name-nondirectory (or (libmpdel--directory-path entity) "")))
(t (libmpdel-entity-name entity)))))
(navigel-method mpdel navigel-buffer-name ((_e (eql directories)))
(mpdel-browser--buffer-name 'directories))
(navigel-method mpdel navigel-buffer-name ((entity libmpdel-directory))
(mpdel-browser--buffer-name entity))
(navigel-method mpdel navigel-entity-buffer ((_e (eql directories)))
(mpdel-browser--buffer-name 'directories))
(navigel-method mpdel navigel-entity-buffer ((entity libmpdel-directory))
(mpdel-browser--buffer-name entity))
(navigel-method mpdel navigel-children
(entity callback &context (major-mode mpdel-browser-mode))
(libmpdel-list entity
(lambda (c)
(funcall callback
(funcall mpdel-browser-list-clean-up-function c)))))
;;; Browser top level
(cl-defmethod libmpdel-entity-name ((_e (eql empty-line)))
"The empty line has an empty name."
"")
(navigel-method mpdel navigel-open ((_e (eql empty-line)) _t)
nil)
(defmacro mpdel-browser--defsearch (thing)
"An utility macro for defining methods associated with a search for THING."
(let* ((entity (intern (format "search-%s" thing)))
(name (format "Search by %s" thing))
(prompt (format "%s: " name))
(type (symbol-name thing)))
`(progn
(cl-defmethod libmpdel-entity-name ((_e (eql ,entity))) ,name)
(navigel-method mpdel navigel-open ((_e (eql ,entity)) _t)
(let ((what (read-from-minibuffer ,prompt)))
(navigel-open (libmpdel-search-criteria-create :type ,type
:what what)
nil))))))
(mpdel-browser--defsearch album)
(mpdel-browser--defsearch artist)
(mpdel-browser--defsearch title)
(mpdel-browser--defsearch filter)
(cl-defmethod libmpdel-entity-name ((path string))
"Return the argument PATH, a string."
path)
(navigel-method mpdel navigel-open ((path string) target)
(let ((navigel-app 'mpdel))
(navigel-open (libmpdel--directory-create :path path) target)))
(cl-defmethod libmpdel-entity-name ((_e (eql browser)))
"The name of the top level browser entity."
"Browser")
(cl-defmethod libmpdel-list ((_e (eql browser)) callback)
"Listing of the top level browser, passed to CALLBACK.
This listing is constructed using `mpdel-browser-top-level-entries'."
(funcall callback mpdel-browser-top-level-entries))
(cl-defmethod libmpdel-entity-parent ((_e (eql directories)))
"The new parent of directories is the browser."
'browser)
(navigel-method mpdel navigel-buffer-name ((_e (eql browser)))
(format "* MPDel - %s:%d *" libmpdel-hostname libmpdel-port))
(navigel-method mpdel navigel-entity-buffer ((_e (eql browser)))
(navigel-buffer-name 'browser))
(navigel-method mpdel navigel-tablist-format ((_e (eql browser)))
(vector (list "MPDel Browser" 60 t)))
(navigel-method mpdel navigel-entity-to-columns ((_e (eql browser)))
(vector "Top level"))
(navigel-method mpdel navigel-parent ((_e (eql artists))) 'browser)
(navigel-method mpdel navigel-parent ((_e (eql albums))) 'browser)
(navigel-method mpdel navigel-parent ((_e (eql directories))) 'browser)
(navigel-method mpdel navigel-parent ((_e (eql stored-playlists))) 'browser)
(navigel-method mpdel navigel-parent ((_e (eql current-playlist))) 'browser)
(navigel-method mpdel navigel-children ((_e (eql directories)) callback)
(libmpdel-list 'directories
(lambda (children)
(funcall callback (cons 'browser children)))))
;;;###autoload
(defun mpdel-browser-open ()
"Open the top level MPDel browser buffer."
(interactive)
(mpdel-core-open 'browser))
;;; Major mode
(define-derived-mode mpdel-browser-mode mpdel-tablist-mode "MPDel Browser"
"Mode for browsing directories and their contents.")
(navigel-method mpdel navigel-entity-tablist-mode ((_e (eql browser)))
(mpdel-browser-mode))
(navigel-method mpdel navigel-entity-tablist-mode ((_e (eql directories)))
(mpdel-browser-mode))
(navigel-method mpdel navigel-entity-tablist-mode ((_e libmpdel-directory))
(mpdel-browser-mode))
(navigel-method mpdel navigel-entity-tablist-mode ((_e (eql stored-playlists)))
(mpdel-browser-mode))
(navigel-method mpdel navigel-delete ((stored-playlists list) &context (major-mode mpdel-browser-mode) &optional _callback)
(libmpdel-stored-playlists-delete stored-playlists))
(cl-defmethod navigel-parent-to-open (_e &context (major-mode mpdel-browser-mode))
"Find parent of ENTITY when in a buffer with MAJOR-MODE `mpdel-browser-mode'."
(list (or (navigel-parent navigel-entity) 'browser)))
(cl-defmethod navigel-parent-to-open
(_e &context (major-mode mpdel-playlist-current-playlist-mode))
"Indicate that the parent in MAJOR-MODE is the browser."
'(browser . current-playlist))
(define-key mpdel-core-map (kbd ":") #'mpdel-browser-open)
(provide 'mpdel-browser)
;;; mpdel-browser.el ends here
;; LocalWords: selectable backends navigel
;; LocalWords: MPDel