Code

screen_file: moved update code to screen_client.c
[ncmpc.git] / src / screen_file.c
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 */
20 #include "config.h"
21 #include "i18n.h"
22 #include "options.h"
23 #include "charset.h"
24 #include "mpdclient.h"
25 #include "filelist.h"
26 #include "command.h"
27 #include "screen.h"
28 #include "screen_utils.h"
29 #include "screen_browser.h"
30 #include "screen_play.h"
31 #include "screen_client.h"
33 #include <mpd/client.h>
35 #include <ctype.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <glib.h>
40 static struct screen_browser browser;
41 static char *current_path;
43 static void
44 browse_paint(void);
46 static void
47 file_repaint(void)
48 {
49         browse_paint();
50         wrefresh(browser.lw->w);
51 }
53 static void
54 file_repaint_if_active(void)
55 {
56         if (screen_is_visible(&screen_browse))
57                 file_repaint();
58 }
60 static void
61 file_reload(struct mpdclient *c)
62 {
63         if (browser.filelist != NULL)
64                 filelist_free(browser.filelist);
66         browser.filelist = mpdclient_filelist_get(c, current_path);
67 }
69 /* the db has changed -> update the filelist */
70 static void
71 file_changed_callback(struct mpdclient *c, G_GNUC_UNUSED int event,
72                       G_GNUC_UNUSED gpointer data)
73 {
74         file_reload(c);
76 #ifndef NCMPC_MINI
77         sync_highlights(c, browser.filelist);
78 #endif
79         list_window_check_selected(browser.lw, filelist_length(browser.filelist));
81         file_repaint_if_active();
82 }
84 #ifndef NCMPC_MINI
85 /* the playlist has been updated -> fix highlights */
86 static void
87 playlist_changed_callback(struct mpdclient *c, int event, gpointer data)
88 {
89         browser_playlist_changed(&browser, c, event, data);
91         file_repaint_if_active();
92 }
93 #endif
95 /**
96  * Change to the specified absolute directory.
97  */
98 static bool
99 file_change_directory(struct mpdclient *c, const char *new_path)
101         g_free(current_path);
102         current_path = g_strdup(new_path);
104         file_reload(c);
106 #ifndef NCMPC_MINI
107         sync_highlights(c, browser.filelist);
108 #endif
110         list_window_reset(browser.lw);
112         return browser.filelist != NULL;
115 /**
116  * Change to the parent directory of the current directory.
117  */
118 static bool
119 file_change_to_parent(struct mpdclient *c)
121         char *parent = g_path_get_dirname(current_path);
122         char *old_path;
123         int idx;
124         bool success;
126         if (strcmp(parent, ".") == 0)
127                 parent[0] = '\0';
129         old_path = current_path;
130         current_path = NULL;
132         success = file_change_directory(c, parent);
133         g_free(parent);
135         idx = success
136                 ? filelist_find_directory(browser.filelist, old_path)
137                 : -1;
138         g_free(old_path);
140         if (success && idx >= 0) {
141                 /* set the cursor on the previous working directory */
142                 list_window_set_selected(browser.lw, idx);
143                 list_window_center(browser.lw,
144                                    filelist_length(browser.filelist), idx);
145         }
147         return success;
150 /**
151  * Change to the directory referred by the specified #filelist_entry
152  * object.
153  */
154 static bool
155 file_change_to_entry(struct mpdclient *c, const struct filelist_entry *entry)
157         assert(entry != NULL);
159         if (entry->entity == NULL)
160                 return file_change_to_parent(c);
161         else if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY)
162                 return file_change_directory(c, mpd_directory_get_path(mpd_entity_get_directory(entry->entity)));
163         else
164                 return false;
167 static bool
168 file_handle_enter(struct mpdclient *c)
170         const struct filelist_entry *entry = browser_get_selected_entry(&browser);
172         if (entry == NULL)
173                 return false;
175         return file_change_to_entry(c, entry);
178 static int
179 handle_save(struct mpdclient *c)
181         struct filelist_entry *entry;
182         const char *defaultname = NULL;
183         char *defaultname_utf8 = NULL;
184         int ret;
185         unsigned selected;
187         if (browser.lw->selected >= filelist_length(browser.filelist))
188                 return -1;
190         for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
191         {
192                 entry = filelist_get(browser.filelist, selected);
193                 if( entry && entry->entity ) {
194                         struct mpd_entity *entity = entry->entity;
195                         if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST) {
196                                 const struct mpd_playlist *playlist =
197                                         mpd_entity_get_playlist(entity);
198                                 defaultname = mpd_playlist_get_path(playlist);
199                         }
200                 }
201         }
203         if(defaultname)
204                 defaultname_utf8 = utf8_to_locale(defaultname);
205         ret = playlist_save(c, NULL, defaultname_utf8);
206         g_free(defaultname_utf8);
208         return ret;
211 static int
212 handle_delete(struct mpdclient *c)
214         struct filelist_entry *entry;
215         struct mpd_entity *entity;
216         const struct mpd_playlist *playlist;
217         char *str, *buf;
218         int key;
219         unsigned selected;
221         for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
222         {
223                 if (selected >= filelist_length(browser.filelist))
224                         return -1;
226                 entry = filelist_get(browser.filelist, selected);
227                 if( entry==NULL || entry->entity==NULL )
228                         continue;
230                 entity = entry->entity;
232                 if (mpd_entity_get_type(entity) != MPD_ENTITY_TYPE_PLAYLIST) {
233                         /* translators: the "delete" command is only possible
234                            for playlists; the user attempted to delete a song
235                            or a directory or something else */
236                         screen_status_printf(_("Deleting this item is not possible"));
237                         screen_bell();
238                         continue;
239                 }
241                 playlist = mpd_entity_get_playlist(entity);
242                 str = utf8_to_locale(g_basename(mpd_playlist_get_path(playlist)));
243                 buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
244                 g_free(str);
245                 key = tolower(screen_getch(screen.status_window.w, buf));
246                 g_free(buf);
247                 if( key != YES[0] ) {
248                         /* translators: a dialog was aborted by the user */
249                         screen_status_printf(_("Aborted"));
250                         return 0;
251                 }
253                 if (mpdclient_cmd_delete_playlist(c, mpd_playlist_get_path(playlist)))
254                         continue;
256                 /* translators: MPD deleted the playlist, as requested by the
257                    user */
258                 screen_status_printf(_("Playlist deleted"));
259         }
260         return 0;
263 static void
264 browse_init(WINDOW *w, int cols, int rows)
266         current_path = g_strdup("");
268         browser.lw = list_window_init(w, cols, rows);
271 static void
272 browse_resize(int cols, int rows)
274         browser.lw->cols = cols;
275         browser.lw->rows = rows;
278 static void
279 browse_exit(void)
281         if (browser.filelist)
282                 filelist_free(browser.filelist);
283         list_window_free(browser.lw);
285         g_free(current_path);
288 static void
289 browse_open(G_GNUC_UNUSED struct mpdclient *c)
291         if (browser.filelist == NULL) {
292                 browser.filelist = mpdclient_filelist_get(c, "");
293 #ifndef NCMPC_MINI
294                 mpdclient_install_playlist_callback(c, playlist_changed_callback);
295 #endif
296                 mpdclient_install_browse_callback(c, file_changed_callback);
297         }
300 static const char *
301 browse_title(char *str, size_t size)
303         const char *path = NULL, *prev = NULL, *slash = current_path;
304         char *path_locale;
306         /* determine the last 2 parts of the path */
307         while ((slash = strchr(slash, '/')) != NULL) {
308                 path = prev;
309                 prev = ++slash;
310         }
312         if (path == NULL)
313                 /* fall back to full path */
314                 path = current_path;
316         path_locale = utf8_to_locale(path);
317         g_snprintf(str, size, "%s: %s",
318                    /* translators: caption of the browser screen */
319                    _("Browse"), path_locale);
320         g_free(path_locale);
321         return str;
324 static void
325 browse_paint(void)
327         list_window_paint(browser.lw, browser_lw_callback, browser.filelist);
330 static bool
331 browse_cmd(struct mpdclient *c, command_t cmd)
333         switch(cmd) {
334         case CMD_PLAY:
335                 if (file_handle_enter(c)) {
336                         file_repaint();
337                         return true;
338                 }
340                 break;
342         case CMD_GO_ROOT_DIRECTORY:
343                 file_change_directory(c, "");
344                 file_repaint();
345                 return true;
346         case CMD_GO_PARENT_DIRECTORY:
347                 file_change_to_parent(c);
348                 file_repaint();
349                 return true;
351         case CMD_LOCATE:
352                 /* don't let browser_cmd() evaluate the locate command
353                    - it's a no-op, and by the way, leads to a
354                    segmentation fault in the current implementation */
355                 return false;
357         case CMD_DELETE:
358                 handle_delete(c);
359                 file_repaint();
360                 break;
361         case CMD_SAVE_PLAYLIST:
362                 handle_save(c);
363                 break;
364         case CMD_SCREEN_UPDATE:
365                 file_reload(c);
366 #ifndef NCMPC_MINI
367                 sync_highlights(c, browser.filelist);
368 #endif
369                 list_window_check_selected(browser.lw,
370                                            filelist_length(browser.filelist));
371                 file_repaint();
372                 return false;
374         case CMD_DB_UPDATE:
375                 if (c->status == NULL)
376                         return true;
378                 screen_database_update(c, current_path);
379                 return true;
381         default:
382                 break;
383         }
385         if (browser_cmd(&browser, c, cmd)) {
386                 if (screen_is_visible(&screen_browse))
387                         file_repaint();
388                 return true;
389         }
391         return false;
394 const struct screen_functions screen_browse = {
395         .init = browse_init,
396         .exit = browse_exit,
397         .open = browse_open,
398         .resize = browse_resize,
399         .paint = browse_paint,
400         .cmd = browse_cmd,
401         .get_title = browse_title,
402 };
404 bool
405 screen_file_goto_song(struct mpdclient *c, const struct mpd_song *song)
407         const char *uri, *slash, *parent;
408         char *allocated = NULL;
409         bool ret;
410         int i;
412         assert(song != NULL);
414         uri = mpd_song_get_uri(song);
416         if (strstr(uri, "//") != NULL)
417                 /* an URL? */
418                 return false;
420         /* determine the song's parent directory and go there */
422         slash = strrchr(uri, '/');
423         if (slash != NULL)
424                 parent = allocated = g_strndup(uri, slash - uri);
425         else
426                 parent = "";
428         ret = file_change_directory(c, parent);
429         g_free(allocated);
430         if (!ret)
431                 return false;
433         /* select the specified song */
435         i = filelist_find_song(browser.filelist, song);
436         if (i < 0)
437                 i = 0;
439         list_window_set_selected(browser.lw, i);
441         /* finally, switch to the file screen */
442         screen_switch(&screen_browse, c);
443         return true;