51c722df54478f9d2cdb25453a5dd1dee6a1cb48
1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include <ctype.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25 #include <glib.h>
26 #include <ncurses.h>
27 #include <panel.h>
29 #include "config.h"
30 #include "ncmpc.h"
31 #include "options.h"
32 #include "support.h"
33 #include "mpdclient.h"
34 #include "utils.h"
35 #include "strfsong.h"
36 #include "wreadln.h"
37 #include "command.h"
38 #include "colors.h"
39 #include "screen.h"
40 #include "screen_utils.h"
42 #define MAX_SONG_LENGTH 512
44 static list_window_t *lw = NULL;
46 static void
47 playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
48 {
49 D("screen_play.c> playlist_callback() [%d]\n", event);
50 switch(event)
51 {
52 case PLAYLIST_EVENT_DELETE:
53 break;
54 case PLAYLIST_EVENT_MOVE:
55 lw->selected = *((int *) data);
56 if( lw->selected<lw->start )
57 lw->start--;
58 break;
59 default:
60 break;
61 }
62 /* make shure the playlist is repainted */
63 lw->clear = 1;
64 lw->repaint = 1;
65 list_window_check_selected(lw, c->playlist.length);
66 }
68 static char *
69 list_callback(int index, int *highlight, void *data)
70 {
71 static char songname[MAX_SONG_LENGTH];
72 mpdclient_t *c = (mpdclient_t *) data;
73 mpd_Song *song;
75 *highlight = 0;
76 if( (song=playlist_get_song(c, index)) == NULL )
77 {
78 return NULL;
79 }
81 if( c->song && song->id==c->song->id && !IS_STOPPED(c->status->state) )
82 {
83 *highlight = 1;
84 }
85 strfsong(songname, MAX_SONG_LENGTH, LIST_FORMAT, song);
86 return songname;
87 }
89 static int
90 center_playing_item(screen_t *screen, mpdclient_t *c)
91 {
92 int length = c->playlist.length;
93 int offset = lw->selected-lw->start;
94 int index;
96 if( !lw || !c->song || length<lw->rows || IS_STOPPED(c->status->state) )
97 return 0;
99 /* try to center the song that are playing */
100 index = playlist_get_index(c, c->song);
101 D("Autocenter song id:%d pos:%d index:%d\n", c->song->id,c->song->pos,index);
102 lw->start = index-(lw->rows/2);
103 if( lw->start+lw->rows > length )
104 lw->start = length-lw->rows;
105 if( lw->start<0 )
106 lw->start=0;
108 /* make sure the cursor is in the window */
109 lw->selected = lw->start+offset;
110 list_window_check_selected(lw, length);
112 lw->clear = 1;
113 lw->repaint = 1;
115 return 0;
116 }
119 int
120 playlist_save(screen_t *screen, mpdclient_t *c, char *name, char *defaultname)
121 {
122 gchar *filename;
123 gint error;
124 GCompletion *gcmp;
125 GList *list = NULL;
127 void pre_completion_cb(GCompletion *gcmp, gchar *line)
128 {
129 if( list == NULL )
130 {
131 /* create completion list */
132 list = gcmp_list_from_path(c, "", NULL, GCMP_TYPE_PLAYLIST);
133 g_completion_add_items(gcmp, list);
134 }
135 }
137 void post_completion_cb(GCompletion *gcmp, gchar *line, GList *items)
138 {
139 if( g_list_length(items)>=1 )
140 {
141 screen_display_completion_list(screen, items);
142 lw->clear = 1;
143 lw->repaint = 1;
144 }
145 }
147 if( name==NULL )
148 {
149 /* initialize completion support */
150 gcmp = g_completion_new(NULL);
151 g_completion_set_compare(gcmp, strncmp);
152 wrln_pre_completion_callback = pre_completion_cb;
153 wrln_post_completion_callback = post_completion_cb;
155 /* query the user for a filename */
156 filename = screen_readln(screen->status_window.w,
157 _("Save playlist as: "),
158 defaultname,
159 NULL,
160 gcmp);
162 /* destroy completion support */
163 wrln_pre_completion_callback = NULL;
164 wrln_post_completion_callback = NULL;
165 g_completion_free(gcmp);
166 list = string_list_free(list);
167 if( filename )
168 filename=g_strstrip(filename);
169 }
170 else
171 {
172 filename=g_strdup(name);
173 }
174 if( filename==NULL || filename[0]=='\0' )
175 return -1;
176 /* send save command to mpd */
177 D("Saving playlist as \'%s \'...\n", filename);
178 if( (error=mpdclient_cmd_save_playlist(c, filename)) )
179 {
180 gint code = GET_ACK_ERROR_CODE(error);
182 if( code == MPD_ACK_ERROR_EXIST )
183 {
184 char *buf;
185 int key;
187 buf=g_strdup_printf(_("Replace %s [%s/%s] ? "), filename, YES, NO);
188 key = tolower(screen_getch(screen->status_window.w, buf));
189 g_free(buf);
190 if( key == YES[0] )
191 {
192 if( mpdclient_cmd_delete_playlist(c, filename) )
193 {
194 g_free(filename);
195 return -1;
196 }
197 error = playlist_save(screen, c, filename, NULL);
198 g_free(filename);
199 return error;
200 }
201 screen_status_printf(_("Aborted!"));
202 }
203 g_free(filename);
204 return -1;
205 }
206 /* success */
207 screen_status_printf(_("Saved %s"), filename);
208 g_free(filename);
209 return 0;
210 }
212 static int
213 handle_add_to_playlist(screen_t *screen, mpdclient_t *c)
214 {
215 gchar *path;
216 GCompletion *gcmp;
217 GList *list = NULL;
218 GList *dir_list = NULL;
220 void add_dir(gchar *dir)
221 {
222 g_completion_remove_items(gcmp, list);
223 list = string_list_remove(list, dir);
224 list = gcmp_list_from_path(c, dir, list, GCMP_TYPE_RFILE);
225 g_completion_add_items(gcmp, list);
226 dir_list = g_list_append(dir_list, g_strdup(dir));
227 }
229 void pre_completion_cb(GCompletion *gcmp, gchar *line)
230 {
231 D("pre_completion()...\n");
232 if( list == NULL )
233 {
234 /* create initial list */
235 list = gcmp_list_from_path(c, "", NULL, GCMP_TYPE_RFILE);
236 g_completion_add_items(gcmp, list);
237 }
238 else if( line && line[0] && line[strlen(line)-1]=='/' &&
239 string_list_find(dir_list, line) == NULL )
240 {
241 /* add directory content to list */
242 add_dir(line);
243 }
244 }
246 void post_completion_cb(GCompletion *gcmp, gchar *line, GList *items)
247 {
248 D("post_completion()...\n");
249 if( g_list_length(items)>=1 )
250 {
251 screen_display_completion_list(screen, items);
252 lw->clear = 1;
253 lw->repaint = 1;
254 }
256 if( line && line[0] && line[strlen(line)-1]=='/' &&
257 string_list_find(dir_list, line) == NULL )
258 {
259 /* add directory content to list */
260 add_dir(line);
261 }
262 }
264 /* initialize completion support */
265 gcmp = g_completion_new(NULL);
266 g_completion_set_compare(gcmp, strncmp);
267 wrln_pre_completion_callback = pre_completion_cb;
268 wrln_post_completion_callback = post_completion_cb;
269 /* get path */
270 path = screen_readln(screen->status_window.w,
271 _("Add: "),
272 NULL,
273 NULL,
274 gcmp);
276 /* destroy completion data */
277 wrln_pre_completion_callback = NULL;
278 wrln_post_completion_callback = NULL;
279 g_completion_free(gcmp);
280 string_list_free(list);
281 string_list_free(dir_list);
283 /* add the path to the playlist */
284 if( path && path[0] )
285 mpdclient_cmd_add_path(c, path);
287 return 0;
288 }
290 static void
291 play_init(WINDOW *w, int cols, int rows)
292 {
293 lw = list_window_init(w, cols, rows);
294 }
296 static void
297 play_open(screen_t *screen, mpdclient_t *c)
298 {
299 static gboolean install_cb = TRUE;
301 if( install_cb )
302 {
303 mpdclient_install_playlist_callback(c, playlist_changed_callback);
304 install_cb = FALSE;
305 }
306 }
308 static void
309 play_resize(int cols, int rows)
310 {
311 lw->cols = cols;
312 lw->rows = rows;
313 }
316 static void
317 play_exit(void)
318 {
319 list_window_free(lw);
320 }
322 static char *
323 play_title(char *str, size_t size)
324 {
325 if( strcmp(options.host, "localhost") == 0 )
326 return _("Playlist");
328 g_snprintf(str, size, _("Playlist on %s"), options.host);
330 return str;
331 }
333 static void
334 play_paint(screen_t *screen, mpdclient_t *c)
335 {
336 lw->clear = 1;
338 list_window_paint(lw, list_callback, (void *) c);
339 wnoutrefresh(lw->w);
340 }
342 static void
343 play_update(screen_t *screen, mpdclient_t *c)
344 {
345 /* hide the cursor when mpd are playing and the user are inactive */
346 if( options.hide_cursor>0 && c->status->state == MPD_STATUS_STATE_PLAY &&
347 time(NULL)-screen->input_timestamp >= options.hide_cursor )
348 {
349 lw->flags |= LW_HIDE_CURSOR;
350 }
351 else
352 {
353 lw->flags &= ~LW_HIDE_CURSOR;
354 }
356 /* center the cursor */
357 if( options.auto_center )
358 {
359 static int prev_song_id = 0;
361 if( c->song && prev_song_id != c->song->id )
362 {
363 center_playing_item(screen, c);
364 prev_song_id = c->song->id;
365 }
366 }
368 if( c->playlist.updated )
369 {
370 if( lw->selected >= c->playlist.length )
371 lw->selected = c->playlist.length-1;
372 if( lw->start >= c->playlist.length )
373 list_window_reset(lw);
375 play_paint(screen, c);
376 c->playlist.updated = FALSE;
377 }
378 else if( lw->repaint || 1)
379 {
380 list_window_paint(lw, list_callback, (void *) c);
381 wnoutrefresh(lw->w);
382 lw->repaint = 0;
383 }
384 }
386 #ifdef HAVE_GETMOUSE
387 static int
388 handle_mouse_event(screen_t *screen, mpdclient_t *c)
389 {
390 int row;
391 int selected;
392 unsigned long bstate;
394 if( screen_get_mouse_event(c, lw, c->playlist.length, &bstate, &row) )
395 return 1;
397 if( bstate & BUTTON1_DOUBLE_CLICKED )
398 {
399 /* stop */
400 screen_cmd(c, CMD_STOP);
401 return 1;
402 }
404 selected = lw->start+row;
406 if( bstate & BUTTON1_CLICKED )
407 {
408 /* play */
409 if( lw->start+row < c->playlist.length )
410 mpdclient_cmd_play(c, lw->start+row);
411 }
412 else if( bstate & BUTTON3_CLICKED )
413 {
414 /* delete */
415 if( selected == lw->selected )
416 mpdclient_cmd_delete(c, lw->selected);
417 }
418 lw->selected = selected;
419 list_window_check_selected(lw, c->playlist.length);
421 return 1;
422 }
423 #else
424 #define handle_mouse_event(s,c) (0)
425 #endif
427 static int
428 play_cmd(screen_t *screen, mpdclient_t *c, command_t cmd)
429 {
430 switch(cmd)
431 {
432 case CMD_PLAY:
433 mpdclient_cmd_play(c, lw->selected);
434 return 1;
435 case CMD_DELETE:
436 mpdclient_cmd_delete(c, lw->selected);
437 return 1;
438 case CMD_SAVE_PLAYLIST:
439 playlist_save(screen, c, NULL, NULL);
440 return 1;
441 case CMD_ADD:
442 handle_add_to_playlist(screen, c);
443 return 1;
444 case CMD_SCREEN_UPDATE:
445 screen->painted = 0;
446 lw->clear = 1;
447 lw->repaint = 1;
448 center_playing_item(screen, c);
449 return 1;
450 case CMD_LIST_MOVE_UP:
451 mpdclient_cmd_move(c, lw->selected, lw->selected-1);
452 return 1;
453 case CMD_LIST_MOVE_DOWN:
454 mpdclient_cmd_move(c, lw->selected, lw->selected+1);
455 return 1;
456 case CMD_LIST_FIND:
457 case CMD_LIST_RFIND:
458 case CMD_LIST_FIND_NEXT:
459 case CMD_LIST_RFIND_NEXT:
460 return screen_find(screen, c,
461 lw, c->playlist.length,
462 cmd, list_callback, (void *) c);
463 case CMD_MOUSE_EVENT:
464 return handle_mouse_event(screen,c);
465 default:
466 break;
467 }
468 return list_window_cmd(lw, c->playlist.length, cmd) ;
469 }
473 static list_window_t *
474 play_lw(void)
475 {
476 return lw;
477 }
480 screen_functions_t *
481 get_screen_playlist(void)
482 {
483 static screen_functions_t functions;
485 memset(&functions, 0, sizeof(screen_functions_t));
486 functions.init = play_init;
487 functions.exit = play_exit;
488 functions.open = play_open;
489 functions.close = NULL;
490 functions.resize = play_resize;
491 functions.paint = play_paint;
492 functions.update = play_update;
493 functions.cmd = play_cmd;
494 functions.get_lw = play_lw;
495 functions.get_title = play_title;
497 return &functions;
498 }