c69b7d512d0ad6f327c1250d95cb035e0ce3847b
1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 #include "config.h"
20 #include "i18n.h"
21 #include "options.h"
22 #include "charset.h"
23 #include "mpdclient.h"
24 #include "command.h"
25 #include "screen.h"
26 #include "screen_utils.h"
27 #include "screen_browser.h"
28 #include "screen_play.h"
30 #include <mpd/client.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <glib.h>
37 static struct screen_browser browser;
38 static char *current_path;
40 static void
41 browse_paint(void);
43 static void
44 file_repaint(void)
45 {
46 browse_paint();
47 wrefresh(browser.lw->w);
48 }
50 static void
51 file_repaint_if_active(void)
52 {
53 if (screen_is_visible(&screen_browse))
54 file_repaint();
55 }
57 static void
58 file_reload(struct mpdclient *c)
59 {
60 if (browser.filelist != NULL)
61 filelist_free(browser.filelist);
63 browser.filelist = mpdclient_filelist_get(c, current_path);
64 }
66 /* the db has changed -> update the filelist */
67 static void
68 file_changed_callback(mpdclient_t *c, G_GNUC_UNUSED int event,
69 G_GNUC_UNUSED gpointer data)
70 {
71 file_reload(c);
73 #ifndef NCMPC_MINI
74 sync_highlights(c, browser.filelist);
75 #endif
76 list_window_check_selected(browser.lw, filelist_length(browser.filelist));
78 file_repaint_if_active();
79 }
81 #ifndef NCMPC_MINI
82 /* the playlist has been updated -> fix highlights */
83 static void
84 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
85 {
86 browser_playlist_changed(&browser, c, event, data);
88 file_repaint_if_active();
89 }
90 #endif
92 /**
93 * Change to the specified absolute directory.
94 */
95 static bool
96 file_change_directory(mpdclient_t *c, const char *new_path)
97 {
98 g_free(current_path);
99 current_path = g_strdup(new_path);
101 file_reload(c);
103 #ifndef NCMPC_MINI
104 sync_highlights(c, browser.filelist);
105 #endif
107 list_window_reset(browser.lw);
109 return browser.filelist != NULL;
110 }
112 /**
113 * Change to the parent directory of the current directory.
114 */
115 static bool
116 file_change_to_parent(mpdclient_t *c)
117 {
118 char *parent = g_path_get_dirname(current_path);
119 char *old_path;
120 int idx;
121 bool success;
123 if (strcmp(parent, ".") == 0)
124 parent[0] = '\0';
126 old_path = current_path;
127 current_path = NULL;
129 success = file_change_directory(c, parent);
130 g_free(parent);
132 idx = success
133 ? filelist_find_directory(browser.filelist, old_path)
134 : -1;
135 g_free(old_path);
137 if (success && idx >= 0) {
138 /* set the cursor on the previous working directory */
139 list_window_set_selected(browser.lw, idx);
140 list_window_center(browser.lw,
141 filelist_length(browser.filelist), idx);
142 }
144 return success;
145 }
147 /**
148 * Change to the directory referred by the specified filelist_entry_t
149 * object.
150 */
151 static bool
152 file_change_to_entry(mpdclient_t *c, const filelist_entry_t *entry)
153 {
154 assert(entry != NULL);
156 if (entry->entity == NULL)
157 return file_change_to_parent(c);
158 else if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY)
159 return file_change_directory(c, mpd_directory_get_path(mpd_entity_get_directory(entry->entity)));
160 else
161 return false;
162 }
164 static bool
165 file_handle_enter(struct mpdclient *c)
166 {
167 const struct filelist_entry *entry = browser_get_selected_entry(&browser);
169 if (entry == NULL)
170 return false;
172 return file_change_to_entry(c, entry);
173 }
175 static int
176 handle_save(mpdclient_t *c)
177 {
178 filelist_entry_t *entry;
179 const char *defaultname = NULL;
180 char *defaultname_utf8 = NULL;
181 int ret;
182 unsigned selected;
184 if (browser.lw->selected >= filelist_length(browser.filelist))
185 return -1;
187 for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
188 {
189 entry = filelist_get(browser.filelist, selected);
190 if( entry && entry->entity ) {
191 struct mpd_entity *entity = entry->entity;
192 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST) {
193 const struct mpd_playlist *playlist =
194 mpd_entity_get_playlist(entity);
195 defaultname = mpd_playlist_get_path(playlist);
196 }
197 }
198 }
200 if(defaultname)
201 defaultname_utf8 = utf8_to_locale(defaultname);
202 ret = playlist_save(c, NULL, defaultname_utf8);
203 g_free(defaultname_utf8);
205 return ret;
206 }
208 static int
209 handle_delete(mpdclient_t *c)
210 {
211 filelist_entry_t *entry;
212 struct mpd_entity *entity;
213 const struct mpd_playlist *playlist;
214 char *str, *buf;
215 int key;
216 unsigned selected;
218 for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
219 {
220 if (selected >= filelist_length(browser.filelist))
221 return -1;
223 entry = filelist_get(browser.filelist, selected);
224 if( entry==NULL || entry->entity==NULL )
225 continue;
227 entity = entry->entity;
229 if (mpd_entity_get_type(entity) != MPD_ENTITY_TYPE_PLAYLIST) {
230 /* translators: the "delete" command is only possible
231 for playlists; the user attempted to delete a song
232 or a directory or something else */
233 screen_status_printf(_("Deleting this item is not possible"));
234 screen_bell();
235 continue;
236 }
238 playlist = mpd_entity_get_playlist(entity);
239 str = utf8_to_locale(g_basename(mpd_playlist_get_path(playlist)));
240 buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
241 g_free(str);
242 key = tolower(screen_getch(screen.status_window.w, buf));
243 g_free(buf);
244 if( key != YES[0] ) {
245 /* translators: a dialog was aborted by the user */
246 screen_status_printf(_("Aborted"));
247 return 0;
248 }
250 if (mpdclient_cmd_delete_playlist(c, mpd_playlist_get_path(playlist)))
251 continue;
253 /* translators: MPD deleted the playlist, as requested by the
254 user */
255 screen_status_printf(_("Playlist deleted"));
256 }
257 return 0;
258 }
260 static void
261 browse_init(WINDOW *w, int cols, int rows)
262 {
263 current_path = g_strdup("");
265 browser.lw = list_window_init(w, cols, rows);
266 }
268 static void
269 browse_resize(int cols, int rows)
270 {
271 browser.lw->cols = cols;
272 browser.lw->rows = rows;
273 }
275 static void
276 browse_exit(void)
277 {
278 if (browser.filelist)
279 filelist_free(browser.filelist);
280 list_window_free(browser.lw);
282 g_free(current_path);
283 }
285 static void
286 browse_open(G_GNUC_UNUSED mpdclient_t *c)
287 {
288 if (browser.filelist == NULL) {
289 browser.filelist = mpdclient_filelist_get(c, "");
290 #ifndef NCMPC_MINI
291 mpdclient_install_playlist_callback(c, playlist_changed_callback);
292 #endif
293 mpdclient_install_browse_callback(c, file_changed_callback);
294 }
295 }
297 static const char *
298 browse_title(char *str, size_t size)
299 {
300 const char *path = NULL, *prev = NULL, *slash = current_path;
301 char *path_locale;
303 /* determine the last 2 parts of the path */
304 while ((slash = strchr(slash, '/')) != NULL) {
305 path = prev;
306 prev = ++slash;
307 }
309 if (path == NULL)
310 /* fall back to full path */
311 path = current_path;
313 path_locale = utf8_to_locale(path);
314 g_snprintf(str, size, "%s: %s",
315 /* translators: caption of the browser screen */
316 _("Browse"), path_locale);
317 g_free(path_locale);
318 return str;
319 }
321 static void
322 browse_paint(void)
323 {
324 list_window_paint(browser.lw, browser_lw_callback, browser.filelist);
325 }
327 static bool
328 browse_cmd(mpdclient_t *c, command_t cmd)
329 {
330 switch(cmd) {
331 case CMD_PLAY:
332 if (file_handle_enter(c)) {
333 file_repaint();
334 return true;
335 }
337 break;
339 case CMD_GO_ROOT_DIRECTORY:
340 file_change_directory(c, "");
341 file_repaint();
342 return true;
343 case CMD_GO_PARENT_DIRECTORY:
344 file_change_to_parent(c);
345 file_repaint();
346 return true;
348 case CMD_LOCATE:
349 /* don't let browser_cmd() evaluate the locate command
350 - it's a no-op, and by the way, leads to a
351 segmentation fault in the current implementation */
352 return false;
354 case CMD_DELETE:
355 handle_delete(c);
356 file_repaint();
357 break;
358 case CMD_SAVE_PLAYLIST:
359 handle_save(c);
360 break;
361 case CMD_SCREEN_UPDATE:
362 file_reload(c);
363 #ifndef NCMPC_MINI
364 sync_highlights(c, browser.filelist);
365 #endif
366 list_window_check_selected(browser.lw,
367 filelist_length(browser.filelist));
368 file_repaint();
369 return false;
371 case CMD_DB_UPDATE:
372 if (c->status == NULL)
373 return true;
375 if (mpd_status_get_update_id(c->status) == 0) {
376 if (mpdclient_cmd_db_update(c, current_path) == 0) {
377 if (strcmp(current_path, "")) {
378 char *path_locale =
379 utf8_to_locale(current_path);
380 screen_status_printf(_("Database update of %s started"),
381 path_locale);
382 g_free(path_locale);
383 } else
384 screen_status_printf(_("Database update started"));
385 }
386 } else
387 screen_status_printf(_("Database update running..."));
388 return true;
390 default:
391 break;
392 }
394 if (browser_cmd(&browser, c, cmd)) {
395 if (screen_is_visible(&screen_browse))
396 file_repaint();
397 return true;
398 }
400 return false;
401 }
403 const struct screen_functions screen_browse = {
404 .init = browse_init,
405 .exit = browse_exit,
406 .open = browse_open,
407 .resize = browse_resize,
408 .paint = browse_paint,
409 .cmd = browse_cmd,
410 .get_title = browse_title,
411 };
413 bool
414 screen_file_goto_song(struct mpdclient *c, const struct mpd_song *song)
415 {
416 const char *uri, *slash, *parent;
417 char *allocated = NULL;
418 bool ret;
419 int i;
421 assert(song != NULL);
423 uri = mpd_song_get_uri(song);
425 if (strstr(uri, "//") != NULL)
426 /* an URL? */
427 return false;
429 /* determine the song's parent directory and go there */
431 slash = strrchr(uri, '/');
432 if (slash != NULL)
433 parent = allocated = g_strndup(uri, slash - uri);
434 else
435 parent = "";
437 ret = file_change_directory(c, parent);
438 g_free(allocated);
439 if (!ret)
440 return false;
442 /* select the specified song */
444 i = filelist_find_song(browser.filelist, song);
445 if (i < 0)
446 i = 0;
448 list_window_set_selected(browser.lw, i);
450 /* finally, switch to the file screen */
451 screen_switch(&screen_browse, c);
452 return true;
453 }