1 /*
2 * (c) 2006 by Kalle Wallin <kaw@linux.se>
3 * Copyright (C) 2008 Max Kellermann <max@duempel.org>
4 *
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.
9 *
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 */
20 #include <sys/stat.h>
21 #include "i18n.h"
22 #include "options.h"
23 #include "mpdclient.h"
24 #include "command.h"
25 #include "screen.h"
26 #include "screen_utils.h"
27 #include "strfsong.h"
28 #include "lyrics.h"
29 #include "charset.h"
30 #include "gcc.h"
32 #define _GNU_SOURCE
33 #include <stdlib.h>
34 #include <string.h>
35 #include <glib.h>
36 #include <unistd.h>
37 #include <stdio.h>
39 static list_window_t *lw = NULL;
41 static const struct mpd_song *next_song;
43 static struct {
44 struct mpd_song *song;
46 char *artist, *title;
48 struct lyrics_loader *loader;
50 GPtrArray *lines;
51 } current;
53 static void
54 screen_lyrics_abort(void)
55 {
56 if (current.loader != NULL) {
57 lyrics_free(current.loader);
58 current.loader = NULL;
59 }
61 if (current.artist != NULL) {
62 g_free(current.artist);
63 current.artist = NULL;
64 }
66 if (current.title != NULL) {
67 g_free(current.title);
68 current.artist = NULL;
69 }
71 if (current.song != NULL) {
72 mpd_freeSong(current.song);
73 current.song = NULL;
74 }
75 }
77 static void
78 screen_lyrics_clear(void)
79 {
80 guint i;
82 for (i = 0; i < current.lines->len; ++i)
83 g_free(g_ptr_array_index(current.lines, i));
85 g_ptr_array_set_size(current.lines, 0);
86 }
88 static void
89 lyrics_paint(void);
91 /**
92 * Repaint and update the screen.
93 */
94 static void
95 lyrics_repaint(void)
96 {
97 lyrics_paint();
98 wrefresh(lw->w);
99 }
101 /**
102 * Repaint and update the screen, if it is currently active.
103 */
104 static void
105 lyrics_repaint_if_active(void)
106 {
107 if (screen_is_visible(&screen_lyrics)) {
108 lyrics_repaint();
110 /* XXX repaint the screen title */
111 }
112 }
114 static void
115 screen_lyrics_set(const GString *str)
116 {
117 const char *p, *eol, *next;
119 screen_lyrics_clear();
121 p = str->str;
122 while ((eol = strchr(p, '\n')) != NULL) {
123 char *line;
125 next = eol + 1;
127 /* strip whitespace at end */
129 while (eol > p && (unsigned char)eol[-1] <= 0x20)
130 --eol;
132 /* create copy and append it to current.lines*/
134 line = g_malloc(eol - p + 1);
135 memcpy(line, p, eol - p);
136 line[eol - p] = 0;
138 g_ptr_array_add(current.lines, line);
140 /* reset control characters */
142 for (eol = line + (eol - p); line < eol; ++line)
143 if ((unsigned char)*line < 0x20)
144 *line = ' ';
146 p = next;
147 }
149 if (*p != 0)
150 g_ptr_array_add(current.lines, g_strdup(p));
152 /* paint new data */
154 lyrics_repaint_if_active();
155 }
157 static void
158 screen_lyrics_callback(const GString *result, mpd_unused void *data)
159 {
160 assert(current.loader != NULL);
162 if (result != NULL)
163 screen_lyrics_set(result);
164 else
165 screen_status_message (_("No lyrics"));
167 lyrics_free(current.loader);
168 current.loader = NULL;
169 }
171 static void
172 screen_lyrics_load(const struct mpd_song *song)
173 {
174 char buffer[MAX_SONGNAME_LENGTH];
176 assert(song != NULL);
178 screen_lyrics_abort();
179 screen_lyrics_clear();
181 current.song = mpd_songDup(song);
183 strfsong(buffer, sizeof(buffer), "%artist%", song);
184 current.artist = g_strdup(buffer);
186 strfsong(buffer, sizeof(buffer), "%title%", song);
187 current.title = g_strdup(buffer);
189 current.loader = lyrics_load(current.artist, current.title,
190 screen_lyrics_callback, NULL);
191 }
193 static FILE *create_lyr_file(const char *artist, const char *title)
194 {
195 char path[1024];
197 snprintf(path, 1024, "%s/.lyrics",
198 getenv("HOME"));
199 mkdir(path, S_IRWXU);
201 snprintf(path, 1024, "%s/.lyrics/%s - %s.txt",
202 getenv("HOME"), artist, title);
204 return fopen(path, "w");
205 }
207 static int store_lyr_hd(void)
208 {
209 FILE *lyr_file;
210 unsigned i;
212 lyr_file = create_lyr_file(current.artist, current.title);
213 if (lyr_file == NULL)
214 return -1;
216 for (i = 0; i < current.lines->len; ++i)
217 fprintf(lyr_file, "%s\n",
218 (const char*)g_ptr_array_index(current.lines, i));
220 fclose(lyr_file);
221 return 0;
222 }
224 static const char *
225 list_callback(unsigned idx, mpd_unused int *highlight, mpd_unused void *data)
226 {
227 static char buffer[256];
228 char *value;
230 if (idx >= current.lines->len)
231 return NULL;
233 value = utf8_to_locale(g_ptr_array_index(current.lines, idx));
234 g_strlcpy(buffer, value, sizeof(buffer));
235 free(value);
237 return buffer;
238 }
241 static void
242 lyrics_screen_init(WINDOW *w, int cols, int rows)
243 {
244 current.lines = g_ptr_array_new();
245 lw = list_window_init(w, cols, rows);
246 lw->flags = LW_HIDE_CURSOR;
247 }
249 static void
250 lyrics_resize(int cols, int rows)
251 {
252 lw->cols = cols;
253 lw->rows = rows;
254 }
256 static void
257 lyrics_exit(void)
258 {
259 list_window_free(lw);
261 screen_lyrics_abort();
262 screen_lyrics_clear();
264 g_ptr_array_free(current.lines, TRUE);
265 current.lines = NULL;
266 }
268 static void
269 lyrics_open(mpdclient_t *c)
270 {
271 if (next_song == NULL)
272 next_song = c->song;
274 if (next_song != NULL &&
275 (current.song == NULL ||
276 strcmp(next_song->file, current.song->file) != 0))
277 screen_lyrics_load(next_song);
279 next_song = NULL;
280 }
283 static const char *
284 lyrics_title(char *str, size_t size)
285 {
286 if (current.loader != NULL) {
287 snprintf(str, size, "%s (%s)",
288 _("Lyrics"), _("loading..."));
289 return str;
290 } else if (current.artist != NULL && current.title != NULL &&
291 current.lines->len > 0) {
292 snprintf(str, size, "%s: %s - %s",
293 _("Lyrics"),
294 current.artist, current.title);
295 return str;
296 } else
297 return _("Lyrics");
298 }
300 static void
301 lyrics_paint(void)
302 {
303 list_window_paint(lw, list_callback, NULL);
304 }
306 static bool
307 lyrics_cmd(mpdclient_t *c, command_t cmd)
308 {
309 if (list_window_scroll_cmd(lw, current.lines->len, cmd)) {
310 lyrics_repaint();
311 return true;
312 }
314 switch(cmd) {
315 case CMD_INTERRUPT:
316 if (current.loader != NULL) {
317 screen_lyrics_abort();
318 screen_lyrics_clear();
319 }
320 return true;
321 case CMD_ADD:
322 if (current.loader == NULL && current.artist != NULL &&
323 current.title != NULL && store_lyr_hd() == 0)
324 screen_status_message (_("Lyrics saved!"));
325 return true;
326 case CMD_LYRICS_UPDATE:
327 if (c->song != NULL) {
328 screen_lyrics_load(c->song);
329 lyrics_repaint();
330 }
331 return true;
333 #ifdef ENABLE_SONG_SCREEN
334 case CMD_VIEW:
335 if (current.song != NULL) {
336 screen_song_switch(c, current.song);
337 return true;
338 }
340 break;
341 #endif
343 case CMD_LOCATE:
344 if (current.song != NULL) {
345 screen_file_goto_song(c, current.song);
346 return true;
347 }
349 return false;
351 default:
352 break;
353 }
355 lw->selected = lw->start+lw->rows;
356 if (screen_find(lw, current.lines->len,
357 cmd, list_callback, NULL)) {
358 /* center the row */
359 list_window_center(lw, current.lines->len, lw->selected);
360 lyrics_repaint();
361 return true;
362 }
364 return false;
365 }
367 const struct screen_functions screen_lyrics = {
368 .init = lyrics_screen_init,
369 .exit = lyrics_exit,
370 .open = lyrics_open,
371 .close = NULL,
372 .resize = lyrics_resize,
373 .paint = lyrics_paint,
374 .cmd = lyrics_cmd,
375 .get_title = lyrics_title,
376 };
378 void
379 screen_lyrics_switch(struct mpdclient *c, const struct mpd_song *song)
380 {
381 assert(song != NULL);
383 next_song = song;
384 screen_switch(&screen_lyrics, c);
385 }