X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fscreen_lyrics.c;h=b937e79bc3792ca16030d766178989d746faec9c;hb=e52df3dd5be37471c4fceceb683479bd81bdec27;hp=5356548c90b5ef691f09196696ceaf28672a9350;hpb=ca2b9d1390a56b89336b963113b5763edd70238e;p=ncmpc.git diff --git a/src/screen_lyrics.c b/src/screen_lyrics.c index 5356548..b937e79 100644 --- a/src/screen_lyrics.c +++ b/src/screen_lyrics.c @@ -1,6 +1,6 @@ -/* - * (c) 2006 by Kalle Wallin - * Copyright (C) 2008 Max Kellermann +/* ncmpc (Ncurses MPD Client) + * (c) 2004-2017 The Music Player Daemon Project + * Project homepage: http://musicpd.org * * 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 @@ -11,54 +11,71 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "config.h" -#ifndef DISABLE_LYRICS_SCREEN -#include -#include "ncmpc.h" +#include "screen_lyrics.h" +#include "screen_interface.h" +#include "screen_status.h" +#include "screen_file.h" +#include "screen_song.h" +#include "i18n.h" #include "options.h" #include "mpdclient.h" -#include "command.h" #include "screen.h" -#include "screen_utils.h" -#include "strfsong.h" #include "lyrics.h" -#include "gcc.h" +#include "screen_text.h" +#include "screen_utils.h" +#include "ncu.h" -#define _GNU_SOURCE +#include +#include +#include +#include #include #include #include -#include #include #include -static list_window_t *lw = NULL; -static int lyrics_text_rows = -1; +static struct screen_text text; + +static struct mpd_song *next_song; +static bool follow = false; +/** Set if the cursor position shall be kept during the next lyrics update. */ +static bool reloading = false; static struct { - const struct mpd_song *song; + struct mpd_song *song; - char *artist, *title; + char *artist, *title, *plugin_name; - struct lyrics_loader *loader; + struct plugin_cycle *loader; - GPtrArray *lines; + guint loader_timeout; } current; static void screen_lyrics_abort(void) { if (current.loader != NULL) { - lyrics_free(current.loader); + plugin_stop(current.loader); current.loader = NULL; } + if (current.loader_timeout != 0) { + g_source_remove(current.loader_timeout); + current.loader_timeout = 0; + } + + if (current.plugin_name != NULL) { + g_free(current.plugin_name); + current.plugin_name = NULL; + } + if (current.artist != NULL) { g_free(current.artist); current.artist = NULL; @@ -66,292 +83,432 @@ screen_lyrics_abort(void) if (current.title != NULL) { g_free(current.title); - current.artist = NULL; + current.title = NULL; } - current.song = NULL; + if (current.song != NULL) { + mpd_song_free(current.song); + current.song = NULL; + } } +/** + * Repaint and update the screen, if it is currently active. + */ static void -screen_lyrics_clear(void) +lyrics_repaint_if_active(void) { - guint i; + if (screen_is_visible(&screen_lyrics)) { + screen_text_repaint(&text); - assert(current.loader == NULL || - lyrics_result(current.loader) == LYRICS_SUCCESS); - - current.song = NULL; - - for (i = 0; i < current.lines->len; ++i) - g_free(g_ptr_array_index(current.lines, i)); - - g_ptr_array_set_size(current.lines, 0); + /* XXX repaint the screen title */ + } } static void -screen_lyrics_set(const GString *str) +path_lyr_file(char *path, size_t size, + const char *artist, const char *title) { - const char *p, *eol, *next; - - screen_lyrics_clear(); + snprintf(path, size, "%s/.lyrics/%s - %s.txt", + getenv("HOME"), artist, title); +} - p = str->str; - while ((eol = strchr(p, '\n')) != NULL) { - char *line; +static bool +exists_lyr_file(const char *artist, const char *title) +{ + char path[1024]; + path_lyr_file(path, 1024, artist, title); - next = eol + 1; + struct stat result; + return (stat(path, &result) == 0); +} - /* strip whitespace at end */ +static FILE * +create_lyr_file(const char *artist, const char *title) +{ + char path[1024]; + snprintf(path, 1024, "%s/.lyrics", + getenv("HOME")); + mkdir(path, S_IRWXU); - while (eol > p && (unsigned char)eol[-1] <= 0x20) - --eol; + path_lyr_file(path, 1024, artist, title); - /* create copy and append it to current.lines*/ + return fopen(path, "w"); +} - line = g_malloc(eol - p + 1); - memcpy(line, p, eol - p); - line[eol - p] = 0; +static int +store_lyr_hd(void) +{ + FILE *lyr_file = create_lyr_file(current.artist, current.title); + if (lyr_file == NULL) + return -1; - g_ptr_array_add(current.lines, line); + for (unsigned i = 0; i < text.lines->len; ++i) + fprintf(lyr_file, "%s\n", + (const char*)g_ptr_array_index(text.lines, i)); - /* reset control characters */ + fclose(lyr_file); + return 0; +} - for (eol = line + (eol - p); line < eol; ++line) - if ((unsigned char)*line < 0x20) - *line = ' '; +static int +delete_lyr_hd(void) +{ + if (!exists_lyr_file(current.artist, current.title)) + return -1; - p = next; - } + char path[1024]; + path_lyr_file(path, 1024, current.artist, current.title); + if (unlink(path) != 0) + return -2; - if (*p != 0) - g_ptr_array_add(current.lines, g_strdup(p)); + return 0; } -static int -screen_lyrics_poll(void) +static void +screen_lyrics_set(const GString *str) { - assert(current.loader != NULL); - - switch (lyrics_result(current.loader)) { - case LYRICS_BUSY: - return 0; + if (reloading) { + unsigned saved_start = text.lw->start; - case LYRICS_SUCCESS: - screen_lyrics_set(lyrics_get(current.loader)); - lyrics_free(current.loader); - current.loader = NULL; - return 1; + screen_text_set(&text, str->str); - case LYRICS_FAILED: - lyrics_free(current.loader); - current.loader = NULL; - screen_status_message (_("No lyrics")); - return -1; + /* restore the cursor and ensure that it's still valid */ + text.lw->start = saved_start; + list_window_fetch_cursor(text.lw); + } else { + screen_text_set(&text, str->str); } - assert(0); - return -1; + reloading = false; + + /* paint new data */ + + lyrics_repaint_if_active(); } static void -screen_lyrics_load(struct mpd_song *song) +screen_lyrics_callback(const GString *result, const bool success, + const char *plugin_name, gcc_unused void *data) { - char buffer[MAX_SONGNAME_LENGTH]; + assert(current.loader != NULL); - assert(song != NULL); + current.plugin_name = g_strdup(plugin_name); - screen_lyrics_abort(); - screen_lyrics_clear(); + /* Display result, which may be lyrics or error messages */ + if (result != NULL) + screen_lyrics_set(result); - strfsong(buffer, sizeof(buffer), "%artist%", song); - current.artist = g_strdup(buffer); + if (success == true) { + if (options.lyrics_autosave && + !exists_lyr_file(current.artist, current.title)) + store_lyr_hd(); + } else { + /* translators: no lyrics were found for the song */ + screen_status_message (_("No lyrics")); + } - strfsong(buffer, sizeof(buffer), "%title%", song); - current.title = g_strdup(buffer); + if (current.loader_timeout != 0) { + g_source_remove(current.loader_timeout); + current.loader_timeout = 0; + } - current.loader = lyrics_load(current.artist, current.title); + plugin_stop(current.loader); + current.loader = NULL; } -static void lyrics_paint(screen_t *screen, mpdclient_t *c); - -static FILE *create_lyr_file(const char *artist, const char *title) +static gboolean +screen_lyrics_timeout_callback(gpointer gcc_unused data) { - char path[1024]; + plugin_stop(current.loader); + current.loader = NULL; - snprintf(path, 1024, "%s/.lyrics", - getenv("HOME")); - mkdir(path, S_IRWXU); - - snprintf(path, 1024, "%s/.lyrics/%s - %s.txt", - getenv("HOME"), artist, title); + screen_status_printf(_("Lyrics timeout occurred after %d seconds"), + options.lyrics_timeout); - return fopen(path, "w"); + current.loader_timeout = 0; + return FALSE; } -static int store_lyr_hd(void) +static void +screen_lyrics_load(const struct mpd_song *song) { - FILE *lyr_file; - unsigned i; + assert(song != NULL); - lyr_file = create_lyr_file(current.artist, current.title); - if (lyr_file == NULL) - return -1; + screen_lyrics_abort(); + screen_text_clear(&text); - for (i = 0; i < current.lines->len; ++i) - fprintf(lyr_file, "%s\n", - (const char*)g_ptr_array_index(current.lines, i)); + const char *artist = mpd_song_get_tag(song, MPD_TAG_ARTIST, 0); + const char *title = mpd_song_get_tag(song, MPD_TAG_TITLE, 0); - fclose(lyr_file); - return 0; -} + current.song = mpd_song_dup(song); + current.artist = g_strdup(artist); + current.title = g_strdup(title); -static const char * -list_callback(unsigned idx, mpd_unused int *highlight, mpd_unused void *data) -{ - if (current.lines == NULL || idx >= current.lines->len) - return ""; + current.loader = lyrics_load(current.artist, current.title, + screen_lyrics_callback, NULL); - return g_ptr_array_index(current.lines, idx); + if (options.lyrics_timeout != 0) { + current.loader_timeout = + g_timeout_add_seconds(options.lyrics_timeout, + screen_lyrics_timeout_callback, + NULL); + } } +static void +screen_lyrics_reload(void) +{ + if (current.loader == NULL && current.artist != NULL && + current.title != NULL) { + reloading = true; + current.loader = lyrics_load(current.artist, current.title, + screen_lyrics_callback, NULL); + screen_text_repaint(&text); + } +} static void lyrics_screen_init(WINDOW *w, int cols, int rows) { - current.lines = g_ptr_array_new(); - lw = list_window_init(w, cols, rows); - lw->flags = LW_HIDE_CURSOR; + screen_text_init(&text, w, cols, rows); } static void lyrics_resize(int cols, int rows) { - lw->cols = cols; - lw->rows = rows; + screen_text_resize(&text, cols, rows); } static void lyrics_exit(void) { - list_window_free(lw); - screen_lyrics_abort(); - screen_lyrics_clear(); - g_ptr_array_free(current.lines, TRUE); - current.lines = NULL; + screen_text_deinit(&text); } static void -lyrics_open(mpd_unused screen_t *screen, mpdclient_t *c) +lyrics_open(struct mpdclient *c) { - if (c->song != NULL && c->song != current.song) - screen_lyrics_load(c->song); - else if (current.loader != NULL) - screen_lyrics_poll(); + const struct mpd_song *next_song_c = + next_song != NULL ? next_song : c->song; + + if (next_song_c != NULL && + (current.song == NULL || + strcmp(mpd_song_get_uri(next_song_c), + mpd_song_get_uri(current.song)) != 0)) + screen_lyrics_load(next_song_c); + + if (next_song != NULL) { + mpd_song_free(next_song); + next_song = NULL; + } } +static void +lyrics_update(struct mpdclient *c) +{ + if (!follow) + return; + + if (c->song != NULL && + (current.song == NULL || + strcmp(mpd_song_get_uri(c->song), + mpd_song_get_uri(current.song)) != 0)) + screen_lyrics_load(c->song); +} static const char * lyrics_title(char *str, size_t size) { - if (current.loader != NULL) - return "Lyrics (loading)"; - else if (current.artist != NULL && current.title != NULL && - current.lines->len > 0) { - snprintf(str, size, "Lyrics: %s - %s", - current.artist, current.title); + if (current.loader != NULL) { + snprintf(str, size, "%s (%s)", + _("Lyrics"), + /* translators: this message is displayed + while data is retrieved */ + _("loading...")); + return str; + } else if (current.artist != NULL && current.title != NULL && + !screen_text_is_empty(&text)) { + int n; + n = snprintf(str, size, "%s: %s - %s", + _("Lyrics"), + current.artist, current.title); + + if (options.lyrics_show_plugin && current.plugin_name != NULL && + (unsigned int) n < size - 1) + snprintf(str + n, size - n, " (%s)", + current.plugin_name); + return str; } else - return "Lyrics"; + return _("Lyrics"); } static void -lyrics_paint(mpd_unused screen_t *screen, mpd_unused mpdclient_t *c) +lyrics_paint(void) { - lw->clear = 1; - list_window_paint(lw, list_callback, NULL); - wrefresh(lw->w); + screen_text_paint(&text); } - +/* save current lyrics to a file and run editor on it */ static void -lyrics_update(mpd_unused screen_t *screen, mpd_unused mpdclient_t *c) +lyrics_edit(void) { - if( lw->repaint ) { - list_window_paint(lw, list_callback, NULL); - wrefresh(lw->w); - lw->repaint = 0; + char *editor = options.text_editor; + if (editor == NULL) { + screen_status_message(_("Editor not configured")); + return; } -} + if (options.text_editor_ask) { + char *buf = g_strdup_printf( + _("Do you really want to start an editor and edit these lyrics [%s/%s]? "), + YES, NO); + bool really = screen_get_yesno(buf, false); + g_free(buf); + if (!really) { + screen_status_message(_("Aborted")); + return; + } + } -static int -lyrics_cmd(screen_t *screen, mpdclient_t *c, command_t cmd) -{ - if (list_window_scroll_cmd(lw, current.lines->len, cmd)) - return 1; + if (store_lyr_hd() < 0) + return; + + ncu_deinit(); + + /* TODO: fork/exec/wait won't work on Windows, but building a command + string for system() is too tricky */ + int status; + pid_t pid = fork(); + if (pid == -1) { + screen_status_printf(("%s (%s)"), _("Can't start editor"), g_strerror(errno)); + ncu_init(); + return; + } else if (pid == 0) { + char path[1024]; + path_lyr_file(path, sizeof(path), current.artist, current.title); + execlp(editor, editor, path, NULL); + /* exec failed, do what system does */ + _exit(127); + } else { + int ret; + do { + ret = waitpid(pid, &status, 0); + } while (ret == -1 && errno == EINTR); + } + + ncu_init(); + + /* TODO: hardly portable */ + if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) + /* update to get the changes */ + screen_lyrics_reload(); + else if (WEXITSTATUS(status) == 127) + screen_status_message(_("Can't start editor")); + else + screen_status_printf(_("Editor exited unexpectedly (%d)"), + WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + screen_status_printf(_("Editor exited unexpectedly (signal %d)"), + WTERMSIG(status)); + } +} - lw->repaint=1; +static bool +lyrics_cmd(struct mpdclient *c, command_t cmd) +{ + if (screen_text_cmd(&text, c, cmd)) + return true; switch(cmd) { - case CMD_SELECT: - /* XXX */ - if (current.loader != NULL) { - int ret = screen_lyrics_poll(); - if (ret != 0) - lyrics_paint(NULL, NULL); - } - return 1; case CMD_INTERRUPT: if (current.loader != NULL) { screen_lyrics_abort(); - screen_lyrics_clear(); + screen_text_clear(&text); } - return 1; - case CMD_ADD: + return true; + case CMD_SAVE_PLAYLIST: if (current.loader == NULL && current.artist != NULL && current.title != NULL && store_lyr_hd() == 0) - screen_status_message (_("Lyrics saved!")); - return 1; + /* lyrics for the song were saved on hard disk */ + screen_status_message (_("Lyrics saved")); + return true; + case CMD_DELETE: + if (current.loader == NULL && current.artist != NULL && + current.title != NULL) { + switch (delete_lyr_hd()) { + case 0: + screen_status_message (_("Lyrics deleted")); + break; + case -1: + screen_status_message (_("No saved lyrics")); + break; + } + } + return true; case CMD_LYRICS_UPDATE: if (c->song != NULL) { screen_lyrics_load(c->song); - lyrics_paint(NULL, NULL); + screen_text_paint(&text); + } + return true; + case CMD_EDIT: + lyrics_edit(); + return true; + case CMD_SELECT: + screen_lyrics_reload(); + return true; + +#ifdef ENABLE_SONG_SCREEN + case CMD_SCREEN_SONG: + if (current.song != NULL) { + screen_song_switch(c, current.song); + return true; } - return 1; - default: - break; - } - lw->selected = lw->start+lw->rows; - if (screen_find(screen, - lw, lyrics_text_rows, - cmd, list_callback, NULL)) { - /* center the row */ - lw->start = lw->selected - (lw->rows / 2); - if (lw->start + lw->rows > (unsigned)lyrics_text_rows) { - if (lw->rows < (unsigned)lyrics_text_rows) - lw->start = lyrics_text_rows - lw->rows; - else - lw->start = 0; + break; +#endif + case CMD_SCREEN_SWAP: + screen_swap(c, current.song); + return true; + + case CMD_LOCATE: + if (current.song != NULL) { + screen_file_goto_song(c, current.song); + return true; } - return 1; + + return false; + + default: + break; } - return 0; + return false; } const struct screen_functions screen_lyrics = { .init = lyrics_screen_init, .exit = lyrics_exit, .open = lyrics_open, + .update = lyrics_update, .close = NULL, .resize = lyrics_resize, .paint = lyrics_paint, - .update = lyrics_update, .cmd = lyrics_cmd, .get_title = lyrics_title, }; -#endif /* ENABLE_LYRICS_SCREEN */ +void +screen_lyrics_switch(struct mpdclient *c, const struct mpd_song *song, bool f) +{ + assert(song != NULL); + + follow = f; + next_song = mpd_song_dup(song); + screen_switch(&screen_lyrics, c); +}