Code

screen: moved screen_interface externs to each screen header
[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 "screen_file.h"
21 #include "screen_browser.h"
22 #include "screen_interface.h"
23 #include "config.h"
24 #include "i18n.h"
25 #include "charset.h"
26 #include "mpdclient.h"
27 #include "filelist.h"
28 #include "screen_utils.h"
29 #include "screen_play.h"
30 #include "screen_client.h"
32 #include <mpd/client.h>
34 #include <ctype.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <glib.h>
39 static struct screen_browser browser;
40 static char *current_path;
42 static void
43 screen_file_paint(void);
45 static void
46 screen_file_repaint(void)
47 {
48         screen_file_paint();
49         wrefresh(browser.lw->w);
50 }
52 static void
53 screen_file_reload(struct mpdclient *c)
54 {
55         if (browser.filelist != NULL)
56                 filelist_free(browser.filelist);
58         browser.filelist = mpdclient_filelist_get(c, current_path);
59         if (browser.filelist == NULL)
60                 browser.filelist = filelist_new();
62         if (*current_path != 0)
63                 /* add a dummy entry for ./.. */
64                 filelist_prepend(browser.filelist, NULL);
65 }
67 /**
68  * Change to the specified absolute directory.
69  */
70 static bool
71 change_directory(struct mpdclient *c, const char *new_path)
72 {
73         g_free(current_path);
74         current_path = g_strdup(new_path);
76         screen_file_reload(c);
78 #ifndef NCMPC_MINI
79         screen_browser_sync_highlights(browser.filelist, &c->playlist);
80 #endif
82         list_window_reset(browser.lw);
84         return browser.filelist != NULL;
85 }
87 /**
88  * Change to the parent directory of the current directory.
89  */
90 static bool
91 change_to_parent(struct mpdclient *c)
92 {
93         char *parent = g_path_get_dirname(current_path);
94         char *old_path;
95         int idx;
96         bool success;
98         if (strcmp(parent, ".") == 0)
99                 parent[0] = '\0';
101         old_path = current_path;
102         current_path = NULL;
104         success = change_directory(c, parent);
105         g_free(parent);
107         idx = success
108                 ? filelist_find_directory(browser.filelist, old_path)
109                 : -1;
110         g_free(old_path);
112         if (success && idx >= 0) {
113                 /* set the cursor on the previous working directory */
114                 list_window_set_selected(browser.lw, idx);
115                 list_window_center(browser.lw,
116                                    filelist_length(browser.filelist), idx);
117         }
119         return success;
122 /**
123  * Change to the directory referred by the specified #filelist_entry
124  * object.
125  */
126 static bool
127 change_to_entry(struct mpdclient *c, const struct filelist_entry *entry)
129         assert(entry != NULL);
131         if (entry->entity == NULL)
132                 return change_to_parent(c);
133         else if (mpd_entity_get_type(entry->entity) == MPD_ENTITY_TYPE_DIRECTORY)
134                 return change_directory(c, mpd_directory_get_path(mpd_entity_get_directory(entry->entity)));
135         else
136                 return false;
139 static bool
140 screen_file_handle_enter(struct mpdclient *c)
142         const struct filelist_entry *entry = browser_get_selected_entry(&browser);
144         if (entry == NULL)
145                 return false;
147         return change_to_entry(c, entry);
150 static int
151 handle_save(struct mpdclient *c)
153         struct filelist_entry *entry;
154         const char *defaultname = NULL;
155         char *defaultname_utf8 = NULL;
156         int ret;
157         unsigned selected;
159         if (browser.lw->selected >= filelist_length(browser.filelist))
160                 return -1;
162         for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
163         {
164                 entry = filelist_get(browser.filelist, selected);
165                 if( entry && entry->entity ) {
166                         struct mpd_entity *entity = entry->entity;
167                         if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_PLAYLIST) {
168                                 const struct mpd_playlist *playlist =
169                                         mpd_entity_get_playlist(entity);
170                                 defaultname = mpd_playlist_get_path(playlist);
171                         }
172                 }
173         }
175         if(defaultname)
176                 defaultname_utf8 = utf8_to_locale(defaultname);
177         ret = playlist_save(c, NULL, defaultname_utf8);
178         g_free(defaultname_utf8);
180         return ret;
183 static int
184 handle_delete(struct mpdclient *c)
186         struct filelist_entry *entry;
187         struct mpd_entity *entity;
188         const struct mpd_playlist *playlist;
189         char *str, *buf;
190         int key;
191         unsigned selected;
193         for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
194         {
195                 if (selected >= filelist_length(browser.filelist))
196                         return -1;
198                 entry = filelist_get(browser.filelist, selected);
199                 if( entry==NULL || entry->entity==NULL )
200                         continue;
202                 entity = entry->entity;
204                 if (mpd_entity_get_type(entity) != MPD_ENTITY_TYPE_PLAYLIST) {
205                         /* translators: the "delete" command is only possible
206                            for playlists; the user attempted to delete a song
207                            or a directory or something else */
208                         screen_status_printf(_("Deleting this item is not possible"));
209                         screen_bell();
210                         continue;
211                 }
213                 playlist = mpd_entity_get_playlist(entity);
214                 str = utf8_to_locale(g_basename(mpd_playlist_get_path(playlist)));
215                 buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
216                 g_free(str);
217                 key = tolower(screen_getch(buf));
218                 g_free(buf);
219                 if( key != YES[0] ) {
220                         /* translators: a dialog was aborted by the user */
221                         screen_status_printf(_("Aborted"));
222                         return 0;
223                 }
225                 if (mpdclient_cmd_delete_playlist(c, mpd_playlist_get_path(playlist)))
226                         continue;
228                 /* translators: MPD deleted the playlist, as requested by the
229                    user */
230                 screen_status_printf(_("Playlist deleted"));
231         }
232         return 0;
235 static void
236 screen_file_init(WINDOW *w, int cols, int rows)
238         current_path = g_strdup("");
240         browser.lw = list_window_init(w, cols, rows);
243 static void
244 screen_file_resize(int cols, int rows)
246         browser.lw->cols = cols;
247         browser.lw->rows = rows;
250 static void
251 screen_file_exit(void)
253         if (browser.filelist)
254                 filelist_free(browser.filelist);
255         list_window_free(browser.lw);
257         g_free(current_path);
260 static void
261 screen_file_open(struct mpdclient *c)
263         screen_file_reload(c);
266 static const char *
267 screen_file_get_title(char *str, size_t size)
269         const char *path = NULL, *prev = NULL, *slash = current_path;
270         char *path_locale;
272         /* determine the last 2 parts of the path */
273         while ((slash = strchr(slash, '/')) != NULL) {
274                 path = prev;
275                 prev = ++slash;
276         }
278         if (path == NULL)
279                 /* fall back to full path */
280                 path = current_path;
282         path_locale = utf8_to_locale(path);
283         g_snprintf(str, size, "%s: %s",
284                    /* translators: caption of the browser screen */
285                    _("Browse"), path_locale);
286         g_free(path_locale);
287         return str;
290 static void
291 screen_file_paint(void)
293         list_window_paint(browser.lw, browser_lw_callback, browser.filelist);
296 static void
297 screen_file_update(struct mpdclient *c)
299         if (c->events & (MPD_IDLE_DATABASE | MPD_IDLE_STORED_PLAYLIST)) {
300                 /* the db has changed -> update the filelist */
301                 screen_file_reload(c);
302                 list_window_check_selected(browser.lw,
303                                            filelist_length(browser.filelist));
304         }
306 #ifndef NCMPC_MINI
307         if (c->events & (MPD_IDLE_DATABASE | MPD_IDLE_STORED_PLAYLIST |
308                          MPD_IDLE_PLAYLIST))
309                 screen_browser_sync_highlights(browser.filelist, &c->playlist);
310 #endif
312         if (c->events & (MPD_IDLE_DATABASE | MPD_IDLE_STORED_PLAYLIST
313 #ifndef NCMPC_MINI
314                          | MPD_IDLE_PLAYLIST
315 #endif
316                          ))
317                 screen_file_repaint();
320 static bool
321 screen_file_cmd(struct mpdclient *c, command_t cmd)
323         switch(cmd) {
324         case CMD_PLAY:
325                 if (screen_file_handle_enter(c)) {
326                         screen_file_repaint();
327                         return true;
328                 }
330                 break;
332         case CMD_GO_ROOT_DIRECTORY:
333                 change_directory(c, "");
334                 screen_file_repaint();
335                 return true;
336         case CMD_GO_PARENT_DIRECTORY:
337                 change_to_parent(c);
338                 screen_file_repaint();
339                 return true;
341         case CMD_LOCATE:
342                 /* don't let browser_cmd() evaluate the locate command
343                    - it's a no-op, and by the way, leads to a
344                    segmentation fault in the current implementation */
345                 return false;
347         case CMD_DELETE:
348                 handle_delete(c);
349                 screen_file_repaint();
350                 break;
351         case CMD_SAVE_PLAYLIST:
352                 handle_save(c);
353                 break;
354         case CMD_SCREEN_UPDATE:
355                 screen_file_reload(c);
356 #ifndef NCMPC_MINI
357                 screen_browser_sync_highlights(browser.filelist, &c->playlist);
358 #endif
359                 list_window_check_selected(browser.lw,
360                                            filelist_length(browser.filelist));
361                 screen_file_repaint();
362                 return false;
364         case CMD_DB_UPDATE:
365                 if (c->status == NULL)
366                         return true;
368                 screen_database_update(c, current_path);
369                 return true;
371         default:
372                 break;
373         }
375         if (browser_cmd(&browser, c, cmd)) {
376                 if (screen_is_visible(&screen_browse))
377                         screen_file_repaint();
378                 return true;
379         }
381         return false;
384 const struct screen_functions screen_browse = {
385         .init = screen_file_init,
386         .exit = screen_file_exit,
387         .open = screen_file_open,
388         .resize = screen_file_resize,
389         .paint = screen_file_paint,
390         .update = screen_file_update,
391         .cmd = screen_file_cmd,
392         .get_title = screen_file_get_title,
393 };
395 bool
396 screen_file_goto_song(struct mpdclient *c, const struct mpd_song *song)
398         const char *uri, *slash, *parent;
399         char *allocated = NULL;
400         bool ret;
401         int i;
403         assert(song != NULL);
405         uri = mpd_song_get_uri(song);
407         if (strstr(uri, "//") != NULL)
408                 /* an URL? */
409                 return false;
411         /* determine the song's parent directory and go there */
413         slash = strrchr(uri, '/');
414         if (slash != NULL)
415                 parent = allocated = g_strndup(uri, slash - uri);
416         else
417                 parent = "";
419         ret = change_directory(c, parent);
420         g_free(allocated);
421         if (!ret)
422                 return false;
424         /* select the specified song */
426         i = filelist_find_song(browser.filelist, song);
427         if (i < 0)
428                 i = 0;
430         list_window_set_selected(browser.lw, i);
432         /* finally, switch to the file screen */
433         screen_switch(&screen_browse, c);
434         return true;