1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine 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  * xine 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  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * Playlist Editor
21  *
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <stdio.h>
28 #include <errno.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 #include <X11/keysym.h>
32 
33 #include "common.h"
34 #include "xine-toolkit/button_list.h"
35 
36 
37 typedef struct {
38   Window                window;
39   ImlibImage           *bg_image;
40   xitk_widget_list_t   *widget_list;
41 
42   xitk_widget_t        *playlist;
43   xitk_widget_t        *winput;
44   xitk_button_list_t   *autoplay_buttons;
45 
46   xitk_widget_t        *move_up;
47   xitk_widget_t        *move_down;
48   xitk_widget_t        *play;
49   xitk_widget_t        *delete;
50   xitk_widget_t        *delete_all;
51   xitk_widget_t        *add;
52   xitk_widget_t        *load;
53   xitk_widget_t        *save;
54   xitk_widget_t        *close;
55 
56   int                   running;
57   int                   visible;
58   xitk_register_key_t   widget_key;
59 
60   char                **playlist_mrls;
61   char                **playlist_idents;
62   int                   playlist_len;
63 
64 } _playlist_t;
65 
66 static filebrowser_t   *load_fb = NULL, *save_fb = NULL;
67 static _playlist_t     *playlist;
68 
69 void playlist_handle_event(XEvent *event, void *data);
70 
playlist_deactivate(void)71 static void playlist_deactivate (void) {
72   if (playlist) {
73     xitk_disable_widget (playlist->playlist);
74     xitk_disable_widget (playlist->winput);
75     xitk_button_list_able (playlist->autoplay_buttons, 0);
76     xitk_disable_widget (playlist->move_up);
77     xitk_disable_widget (playlist->move_down);
78     xitk_disable_widget (playlist->play);
79     xitk_disable_widget (playlist->delete);
80     xitk_disable_widget (playlist->delete_all);
81     xitk_disable_widget (playlist->add);
82     xitk_disable_widget (playlist->load);
83     xitk_disable_widget (playlist->save);
84     xitk_disable_widget (playlist->close);
85   }
86 }
87 
playlist_reactivate(void)88 static void playlist_reactivate (void) {
89   if (playlist) {
90     xitk_enable_widget (playlist->playlist);
91     xitk_enable_widget (playlist->winput);
92     xitk_button_list_able (playlist->autoplay_buttons, 1);
93     xitk_enable_widget (playlist->move_up);
94     xitk_enable_widget (playlist->move_down);
95     xitk_enable_widget (playlist->play);
96     xitk_enable_widget (playlist->delete);
97     xitk_enable_widget (playlist->delete_all);
98     xitk_enable_widget (playlist->add);
99     xitk_enable_widget (playlist->load);
100     xitk_enable_widget (playlist->save);
101     xitk_enable_widget (playlist->close);
102   }
103 }
104 /*
105  *
106  */
107 
_playlist_update_browser_list(int start)108 static void _playlist_update_browser_list(int start) {
109   gGui_t *gui = gGui;
110   int sel = xitk_browser_get_current_selected(playlist->playlist);
111   int old_start = xitk_browser_get_current_start(playlist->playlist);
112 
113   if(gui->is_display_mrl)
114     xitk_browser_update_list(playlist->playlist,
115 			     (const char **)playlist->playlist_mrls, NULL,
116 			     playlist->playlist_len, start);
117   else
118     xitk_browser_update_list(playlist->playlist,
119 			     (const char **)playlist->playlist_idents, NULL,
120 			     playlist->playlist_len, start);
121 
122   if((sel >= 0) && ((start == -1) || (old_start == start)))
123     xitk_browser_set_select(playlist->playlist, sel);
124 }
125 
126 
_playlist_free_playlists(void)127 static void _playlist_free_playlists(void) {
128 
129   if(playlist->playlist_len) {
130 
131     while(playlist->playlist_len >= 0) {
132       free(playlist->playlist_mrls[playlist->playlist_len]);
133       free(playlist->playlist_idents[playlist->playlist_len]);
134       playlist->playlist_len--;
135     }
136 
137     SAFE_FREE(playlist->playlist_mrls);
138     SAFE_FREE(playlist->playlist_idents);
139     playlist->playlist_len = 0;
140   }
141 }
142 
_playlist_create_playlists(void)143 static void _playlist_create_playlists(void) {
144   gGui_t *gui = gGui;
145   int i;
146 
147   _playlist_free_playlists();
148 
149   pthread_mutex_lock (&gui->mmk_mutex);
150   if(gui->playlist.mmk && gui->playlist.num) {
151 
152     playlist->playlist_mrls = (char **) calloc((gui->playlist.num + 1), sizeof(char *));
153     playlist->playlist_idents = (char **) calloc((gui->playlist.num + 1), sizeof(char *));
154 
155     for(i = 0; i < gui->playlist.num; i++) {
156       playlist->playlist_mrls[i] = strdup(gui->playlist.mmk[i]->mrl);
157       playlist->playlist_idents[i] = strdup(gui->playlist.mmk[i]->ident);
158     }
159 
160     playlist->playlist_mrls[i] = NULL;
161     playlist->playlist_idents[i] = NULL;
162     playlist->playlist_len = gui->playlist.num;
163   }
164   pthread_mutex_unlock (&gui->mmk_mutex);
165 }
166 
167 /*
168  *
169  */
_playlist_handle_selection(xitk_widget_t * w,void * data,int selected)170 static void _playlist_handle_selection(xitk_widget_t *w, void *data, int selected) {
171   gGui_t *gui = gGui;
172 
173   if(playlist->playlist_mrls[selected] != NULL) {
174     xitk_inputtext_change_text(playlist->winput, playlist->playlist_mrls[selected]);
175     pthread_mutex_lock (&gui->mmk_mutex);
176     mmkeditor_set_mmk(&gui->playlist.mmk[selected]);
177     pthread_mutex_unlock (&gui->mmk_mutex);
178   }
179 
180 }
181 
_playlist_xine_play(void)182 static void _playlist_xine_play(void) {
183   gGui_t *gui = gGui;
184 
185   if (!gui_playlist_play (gui, gui->playlist.cur)) {
186 
187     if(mediamark_all_played() && (gui->actions_on_start[0] == ACTID_QUIT))
188       gui_exit (NULL, gui);
189     else
190       gui_display_logo();
191   }
192 }
193 /*
194  * Start playing an MRL
195  */
playlist_play_current(xitk_widget_t * w,void * data)196 void playlist_play_current(xitk_widget_t *w, void *data) {
197   gGui_t *gui = gGui;
198   int j;
199 
200   j = xitk_browser_get_current_selected(playlist->playlist);
201 
202   pthread_mutex_lock (&gui->mmk_mutex);
203   if((j >= 0) && (gui->playlist.mmk[j]->mrl != NULL)) {
204     gui->playlist.cur = j;
205     _playlist_xine_play();
206     xitk_browser_release_all_buttons(playlist->playlist);
207   }
208   pthread_mutex_unlock (&gui->mmk_mutex);
209 }
210 /*
211  * Start to play the selected stream on double click event in playlist.
212  */
_playlist_play_on_dbl_click(xitk_widget_t * w,void * data,int selected)213 static void _playlist_play_on_dbl_click(xitk_widget_t *w, void *data, int selected) {
214   gGui_t *gui = gGui;
215 
216   pthread_mutex_lock (&gui->mmk_mutex);
217   if(gui->playlist.mmk[selected]->mrl != NULL) {
218     gui->playlist.cur = selected;
219     _playlist_xine_play();
220     xitk_browser_release_all_buttons(playlist->playlist);
221   }
222   pthread_mutex_unlock (&gui->mmk_mutex);
223 }
224 
225 /*
226  * Delete a given entry from playlist
227  */
playlist_delete_entry(int j)228 void playlist_delete_entry(int j) {
229   gGui_t *gui = gGui;
230   int i;
231 
232   if(j  >= 0) {
233 
234     if((gui->playlist.cur == j) && ((xine_get_status(gui->stream) != XINE_STATUS_STOP)))
235       gui_stop (NULL, gui);
236 
237     mediamark_free_entry(j);
238 
239     pthread_mutex_lock (&gui->mmk_mutex);
240     for(i = j; i < gui->playlist.num; i++)
241       gui->playlist.mmk[i] = gui->playlist.mmk[i + 1];
242 
243     gui->playlist.mmk = (mediamark_t **) realloc(gui->playlist.mmk, sizeof(mediamark_t *) * (gui->playlist.num + 2));
244 
245     gui->playlist.mmk[gui->playlist.num] = NULL;
246     pthread_mutex_unlock (&gui->mmk_mutex);
247 
248 
249     playlist_update_playlist();
250 
251     if(gui->playlist.num) {
252       gui_set_current_mmk(mediamark_get_current_mmk());
253       gui->playlist.cur = 0;
254     }
255     else {
256 
257       gui->playlist.cur = -1;
258 
259       if (is_playback_widgets_enabled (gui->panel))
260         enable_playback_controls (gui->panel, 0);
261 
262       if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
263 	gui_stop(NULL, NULL);
264 
265       gui_set_current_mmk(NULL);
266       xitk_inputtext_change_text(playlist->winput, NULL);
267     }
268   }
269 
270 }
271 
272 /*
273  * Delete selected MRL
274  */
playlist_delete_current(xitk_widget_t * w,void * data)275 void playlist_delete_current(xitk_widget_t *w, void *data) {
276   int j;
277 
278   mmk_editor_end();
279 
280   j = xitk_browser_get_current_selected(playlist->playlist);
281 
282   playlist_delete_entry(j);
283 }
284 
285 /*
286  * Delete all MRLs
287  */
playlist_delete_all(xitk_widget_t * w,void * data)288 void playlist_delete_all(xitk_widget_t *w, void *data) {
289   gGui_t *gui = gGui;
290 
291   mmk_editor_end();
292 
293   mediamark_free_mediamarks();
294   playlist_update_playlist();
295 
296   if(xine_get_status(gui->stream) != XINE_STATUS_STOP)
297     gui_stop(NULL, NULL);
298 
299   if(playlist && playlist->winput)
300     xitk_inputtext_change_text(playlist->winput, NULL);
301 
302   if(playlist)
303     xitk_browser_release_all_buttons(playlist->playlist);
304 
305   gui_set_current_mmk(NULL);
306   enable_playback_controls (gui->panel, 0);
307 }
308 
309 /*
310  * Move entry up/down in playlist
311  */
playlist_move_current_updown(xitk_widget_t * w,void * data)312 void playlist_move_current_updown(xitk_widget_t *w, void *data) {
313   gGui_t *gui = gGui;
314   int j;
315 
316   mmk_editor_end();
317 
318   if((j = xitk_browser_get_current_selected(playlist->playlist)) >= 0) {
319     mediamark_t *mmk;
320     int          start = xitk_browser_get_current_start(playlist->playlist);
321     int          max_vis_len = xitk_browser_get_num_entries(playlist->playlist);
322 
323     if(((intptr_t)data) == DIRECTION_UP && (j > 0)) {
324       pthread_mutex_lock (&gui->mmk_mutex);
325       mmk = gui->playlist.mmk[j - 1];
326 
327       if(j == gui->playlist.cur)
328 	gui->playlist.cur--;
329 
330       gui->playlist.mmk[j - 1] = gui->playlist.mmk[j];
331       gui->playlist.mmk[j] = mmk;
332       j--;
333       pthread_mutex_unlock (&gui->mmk_mutex);
334     }
335     else if(((intptr_t)data) == DIRECTION_DOWN && (j < (gui->playlist.num - 1))) {
336       pthread_mutex_lock (&gui->mmk_mutex);
337       mmk = gui->playlist.mmk[j + 1];
338 
339       if(j == gui->playlist.cur)
340 	gui->playlist.cur++;
341 
342       gui->playlist.mmk[j + 1] = gui->playlist.mmk[j];
343       gui->playlist.mmk[j] = mmk;
344       j++;
345       pthread_mutex_unlock (&gui->mmk_mutex);
346     }
347 
348     _playlist_create_playlists();
349 
350     if(j <= start)
351       _playlist_update_browser_list(j);
352     else if(j >= (start + max_vis_len))
353       _playlist_update_browser_list(start + 1);
354     else
355       _playlist_update_browser_list(-1);
356 
357     xitk_browser_set_select(playlist->playlist, j);
358 
359 
360   }
361 }
362 
363 /*
364  * Load playlist from $HOME/.xine/playlist
365  */
_playlist_load_callback(filebrowser_t * fb)366 static void _playlist_load_callback(filebrowser_t *fb) {
367   gGui_t *gui = gGui;
368   char *file;
369 
370   mmk_editor_end();
371 
372   if((file = filebrowser_get_full_filename(fb)) != NULL) {
373     mediamark_load_mediamarks(file);
374     gui_set_current_mmk(mediamark_get_current_mmk());
375     playlist_update_playlist();
376 
377     if((xine_get_status(gui->stream) == XINE_STATUS_PLAY))
378       gui_stop(NULL, NULL);
379 
380     if ((!is_playback_widgets_enabled (gui->panel)) && gui->playlist.num)
381       enable_playback_controls (gui->panel, 1);
382 
383     free(file);
384   }
385   load_fb = NULL;
386   playlist_reactivate();
387 }
_playlist_cancel_callback(filebrowser_t * fb)388 static void _playlist_cancel_callback(filebrowser_t *fb) {
389   if(fb == load_fb)
390     load_fb = NULL;
391   else if(fb == save_fb)
392     save_fb = NULL;
393 
394   playlist_reactivate();
395 }
playlist_load_playlist(xitk_widget_t * w,void * data)396 void playlist_load_playlist(xitk_widget_t *w, void *data) {
397   filebrowser_callback_button_t  cbb[2];
398   char                          *buffer;
399 
400   if(load_fb)
401     filebrowser_raise_window(load_fb);
402   else {
403     buffer = xitk_asprintf("%s%s", xine_get_homedir(), "/.xine/playlist.tox");
404     if (!buffer)
405       return;
406 
407     cbb[0].label = _("Load");
408     cbb[0].callback = _playlist_load_callback;
409     cbb[0].need_a_file = 1;
410     cbb[1].callback = _playlist_cancel_callback;
411 
412     playlist_deactivate();
413     load_fb = create_filebrowser(_("Load a playlist"), buffer, hidden_file_cb, &cbb[0], NULL, &cbb[1]);
414     free(buffer);
415   }
416 }
417 
418 /*
419  * Save playlist to $HOME/.xine/playlist
420  */
_playlist_save_callback(filebrowser_t * fb)421 static void _playlist_save_callback(filebrowser_t *fb) {
422   char *file;
423 
424   if((file = filebrowser_get_full_filename(fb)) != NULL) {
425     mediamark_save_mediamarks(file);
426     free(file);
427   }
428   save_fb = NULL;
429   playlist_reactivate();
430 }
playlist_save_playlist(xitk_widget_t * w,void * data)431 void playlist_save_playlist(xitk_widget_t *w, void *data) {
432   gGui_t *gui = gGui;
433   filebrowser_callback_button_t  cbb[2];
434   char                          *buffer;
435 
436   if(gui->playlist.num) {
437     if(save_fb)
438       filebrowser_raise_window(save_fb);
439     else {
440       buffer = xitk_asprintf("%s%s", xine_get_homedir(), "/.xine/playlist.tox");
441       if (!buffer)
442         return;
443 
444       cbb[0].label = _("Save");
445       cbb[0].callback = _playlist_save_callback;
446       cbb[0].need_a_file = 1;
447       cbb[1].callback = _playlist_cancel_callback;
448 
449       playlist_deactivate();
450       save_fb = create_filebrowser(_("Save a playlist"), buffer, hidden_file_cb, &cbb[0], NULL, &cbb[1]);
451       free(buffer);
452     }
453   }
454 }
455 
456 /*
457  *
458  */
_playlist_add_input(xitk_widget_t * w,void * data,const char * filename)459 static void _playlist_add_input(xitk_widget_t *w, void *data, const char *filename) {
460 
461   if(filename)
462     gui_dndcallback((char *)filename);
463 
464 }
465 
466 /*
467  * Handle X events here.
468  */
_playlist_handle_event(XEvent * event,void * data)469 static void _playlist_handle_event(XEvent *event, void *data) {
470   gGui_t *gui = gGui;
471 
472   switch(event->type) {
473 
474   case ButtonPress: {
475     XButtonEvent *bevent = (XButtonEvent *) event;
476 
477     if(bevent->button == Button3) {
478       xitk_widget_t *w = xitk_get_focused_widget(playlist->widget_list);
479 
480       if(w && ((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)) {
481 	int wx, wy;
482 
483 	xitk_get_window_position(gui->display, playlist->window, &wx, &wy, NULL, NULL);
484 
485         playlist_menu (gui, playlist->widget_list,
486 		      bevent->x + wx, bevent->y + wy,
487 		      (xitk_browser_get_current_selected(playlist->playlist) >= 0));
488       }
489 
490     }
491     else if((bevent->button == Button4) || (bevent->button == Button5))
492       mmk_editor_end();
493 
494   }
495     break;
496 
497   case ButtonRelease:
498     if(playlist && playlist->playlist) {
499       if(xitk_browser_get_current_selected(playlist->playlist) < 0)
500 	mmk_editor_end();
501     }
502 
503     gui_handle_event (event, gui);
504     break;
505 
506   case KeyPress:
507     {
508       KeySym mkey;
509 
510       if(!playlist)
511 	return;
512 
513       mkey = xitk_get_key_pressed(event);
514 
515       switch (mkey) {
516 
517       case XK_Down:
518       case XK_Next: {
519 	xitk_widget_t *w;
520 
521 	mmk_editor_end();
522 	w = xitk_get_focused_widget(playlist->widget_list);
523 	if((!w) || (w && (!((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)))) {
524 	  if(mkey == XK_Down)
525 	    xitk_browser_step_up(playlist->playlist, NULL);
526 	  else
527 	    xitk_browser_page_up(playlist->playlist, NULL);
528 	}
529       }
530 	break;
531 
532       case XK_Up:
533       case XK_Prior: {
534 	xitk_widget_t *w;
535 
536 	mmk_editor_end();
537 	w = xitk_get_focused_widget(playlist->widget_list);
538 	if((!w) || (w && (!((xitk_get_widget_type(w)) & WIDGET_GROUP_BROWSER)))) {
539 	  if(mkey == XK_Up)
540 	    xitk_browser_step_down(playlist->playlist, NULL);
541 	  else
542 	    xitk_browser_page_down(playlist->playlist, NULL);
543 	}
544       }
545 	break;
546 
547       case XK_Escape:
548 	playlist_exit(NULL, NULL);
549 	break;
550 
551       default:
552         gui_handle_event (event, gui);
553 	break;
554       }
555     }
556     break;
557 
558   case MappingNotify:
559     gui->x_lock_display (gui->display);
560     XRefreshKeyboardMapping((XMappingEvent *) event);
561     gui->x_unlock_display (gui->display);
562     break;
563 
564   }
565 }
566 
_playlist_apply_cb(void * data)567 static void _playlist_apply_cb(void *data) {
568   playlist_mrlident_toggle();
569   gui_set_current_mmk(mediamark_get_current_mmk());
570 }
571 
playlist_get_input_focus(void)572 void playlist_get_input_focus(void) {
573   if(playlist)
574     try_to_set_input_focus(playlist->window);
575 }
576 
playlist_mmk_editor(void)577 void playlist_mmk_editor(void) {
578   gGui_t *gui = gGui;
579   if(playlist) {
580     int sel = xitk_browser_get_current_selected(playlist->playlist);
581 
582     if(sel >= 0) {
583       pthread_mutex_lock (&gui->mmk_mutex);
584       mmk_edit_mediamark(&gui->playlist.mmk[sel], _playlist_apply_cb, NULL);
585       pthread_mutex_unlock (&gui->mmk_mutex);
586     }
587   }
588 }
589 
_scan_for_playlist_infos(xine_stream_t * stream,int n)590 static void _scan_for_playlist_infos(xine_stream_t *stream, int n) {
591   gGui_t *gui = gGui;
592 
593   pthread_mutex_lock (&gui->mmk_mutex);
594   if(xine_open(stream, gui->playlist.mmk[n]->mrl)) {
595     char  *ident;
596 
597     if((ident = stream_infos_get_ident_from_stream(stream)) != NULL) {
598 
599       free(gui->playlist.mmk[n]->ident);
600 
601       gui->playlist.mmk[n]->ident = strdup(ident);
602 
603       if(n == gui->playlist.cur) {
604 
605 	free(gui->mmk.ident);
606 
607 	gui->mmk.ident = strdup(ident);
608 
609         panel_update_mrl_display (gui->panel);
610       }
611 
612       free(ident);
613     }
614     xine_close(stream);
615   }
616   pthread_mutex_unlock (&gui->mmk_mutex);
617 }
618 
playlist_scan_for_infos_selected(void)619 void playlist_scan_for_infos_selected(void) {
620   gGui_t *gui = gGui;
621 
622   if(gui->playlist.num) {
623     int                 selected = xitk_browser_get_current_selected(playlist->playlist);
624     xine_stream_t      *stream;
625 
626     if (selected < 0) {
627       return;
628     }
629 
630     stream = xine_stream_new(__xineui_global_xine_instance, gui->ao_none, gui->vo_none);
631     _scan_for_playlist_infos(stream, selected);
632 
633     xine_dispose(stream);
634 
635     playlist_mrlident_toggle();
636   }
637 }
638 
playlist_scan_for_infos(void)639 void playlist_scan_for_infos(void) {
640   gGui_t *gui = gGui;
641 
642   if(gui->playlist.num) {
643     int                 i;
644     xine_stream_t      *stream;
645 
646     stream = xine_stream_new(__xineui_global_xine_instance, gui->ao_none, gui->vo_none);
647 
648     for(i = 0; i < gui->playlist.num; i++)
649       _scan_for_playlist_infos(stream, i);
650 
651     xine_dispose(stream);
652 
653     playlist_mrlident_toggle();
654   }
655 }
656 
playlist_show_tips(int enabled,unsigned long timeout)657 void playlist_show_tips(int enabled, unsigned long timeout) {
658 
659   if(playlist) {
660     if(enabled)
661       xitk_set_widgets_tips_timeout(playlist->widget_list, timeout);
662     else
663       xitk_disable_widgets_tips(playlist->widget_list);
664   }
665 }
666 
playlist_update_tips_timeout(unsigned long timeout)667 void playlist_update_tips_timeout(unsigned long timeout) {
668   if(playlist)
669     xitk_set_widgets_tips_timeout(playlist->widget_list, timeout);
670 }
671 
playlist_mrlident_toggle(void)672 void playlist_mrlident_toggle(void) {
673   gGui_t *gui = gGui;
674 
675   if(playlist && playlist->visible) {
676     int start = xitk_browser_get_current_start(playlist->playlist);
677 
678     _playlist_create_playlists();
679     _playlist_update_browser_list(start);
680 
681     pthread_mutex_lock (&gui->mmk_mutex);
682     mmkeditor_set_mmk(&gui->playlist.mmk[(xitk_browser_get_current_selected(playlist->playlist))]);
683     pthread_mutex_unlock (&gui->mmk_mutex);
684   }
685 }
686 
687 /*
688  *
689  */
playlist_update_playlist(void)690 void playlist_update_playlist(void) {
691 
692   if(playlist) {
693     _playlist_create_playlists();
694     if(playlist_is_visible()) {
695       _playlist_update_browser_list(0);
696       mmk_editor_end();
697     }
698   }
699 }
700 
701 /*
702  * Leaving playlist editor
703  */
playlist_exit(xitk_widget_t * w,void * data)704 void playlist_exit(xitk_widget_t *w, void *data) {
705   gGui_t *gui = gGui;
706 
707   if(load_fb)
708     filebrowser_end(load_fb);
709   if(save_fb)
710     filebrowser_end(save_fb);
711 
712   if(playlist) {
713     window_info_t wi;
714 
715     mmk_editor_end();
716 
717     playlist->running = 0;
718     playlist->visible = 0;
719 
720     if((xitk_get_window_info(playlist->widget_key, &wi))) {
721       config_update_num ("gui.playlist_x", wi.x);
722       config_update_num ("gui.playlist_y", wi.y);
723       WINDOW_INFO_ZERO(&wi);
724     }
725 
726     xitk_unregister_event_handler(&playlist->widget_key);
727 
728     gui->x_lock_display (gui->display);
729     XUnmapWindow(gui->display, playlist->window);
730     gui->x_unlock_display (gui->display);
731 
732     xitk_destroy_widgets(playlist->widget_list);
733     xitk_button_list_delete (playlist->autoplay_buttons);
734 
735     gui->x_lock_display (gui->display);
736     XDestroyWindow(gui->display, playlist->window);
737     Imlib_destroy_image(gui->imlib_data, playlist->bg_image);
738     gui->x_unlock_display (gui->display);
739 
740     playlist->window = None;
741     /* xitk_dlist_init (&playlist->widget_list->list); */
742 
743     gui->x_lock_display (gui->display);
744     XFreeGC(gui->display, (XITK_WIDGET_LIST_GC(playlist->widget_list)));
745     gui->x_unlock_display (gui->display);
746 
747     _playlist_free_playlists();
748 
749     XITK_WIDGET_LIST_FREE(playlist->widget_list);
750 
751     free(playlist);
752     playlist = NULL;
753 
754     try_to_set_input_focus(gui->video_window);
755   }
756 }
757 
758 
759 /*
760  * return 1 if playlist editor is ON
761  */
playlist_is_running(void)762 int playlist_is_running(void) {
763 
764   if(playlist != NULL)
765     return playlist->running;
766 
767   return 0;
768 }
769 
770 /*
771  * Return 1 if playlist editor is visible
772  */
playlist_is_visible(void)773 int playlist_is_visible(void) {
774   gGui_t *gui = gGui;
775 
776   if(playlist != NULL) {
777     if(gui->use_root_window)
778       return xitk_is_window_visible(gui->display, playlist->window);
779     else
780       return playlist->visible && xitk_is_window_visible(gui->display, playlist->window);
781   }
782 
783   return 0;
784 }
785 
786 /*
787  * Handle autoplay buttons hitting (from panel and playlist windows)
788  */
playlist_scan_input(xitk_widget_t * w,void * ip)789 void playlist_scan_input(xitk_widget_t *w, void *ip) {
790   gGui_t *gui = gGui;
791   const char *name = xitk_labelbutton_get_label (w);
792   int num_mrls = 0;
793   const char * const * autoplay_mrls = xine_get_autoplay_mrls (__xineui_global_xine_instance, name, &num_mrls);
794 
795   if (autoplay_mrls) {
796     xine_stream_t *stream;
797     int j, cdda_mode = 0;
798 
799     if (!strcasecmp (name, "cd"))
800       cdda_mode = 1;
801 
802     /* Flush playlist in newbie mode */
803     if (gui->smart_mode) {
804       mediamark_free_mediamarks ();
805       playlist_update_playlist ();
806       if (playlist != NULL)
807         xitk_inputtext_change_text (playlist->winput, NULL);
808     }
809 
810     stream = xine_stream_new (__xineui_global_xine_instance, gui->ao_none, gui->vo_none);
811     for (j = 0; j < num_mrls; j++) {
812       char *ident = NULL;
813 
814       if (cdda_mode && xine_open (stream, autoplay_mrls[j])) {
815         ident = stream_infos_get_ident_from_stream (stream);
816         xine_close (stream);
817       }
818       mediamark_append_entry (autoplay_mrls[j], ident ? ident : autoplay_mrls[j], NULL, 0, -1, 0, 0);
819       free (ident);
820     }
821     xine_dispose (stream);
822 
823     gui->playlist.cur = gui->playlist.num ? 0 : -1;
824     if (gui->playlist.cur == 0)
825       gui_set_current_mmk (mediamark_get_current_mmk ());
826     /*
827      * If we're in newbie mode, start playback immediately
828      * (even ignoring if we're currently playing something
829      */
830     if (gui->smart_mode) {
831       if (xine_get_status (gui->stream) == XINE_STATUS_PLAY)
832         gui_stop (NULL, NULL);
833       gui_play (NULL, gui);
834     }
835 
836     if (playlist) {
837       _playlist_create_playlists ();
838       _playlist_update_browser_list (0);
839     }
840 
841     enable_playback_controls (gui->panel, (gui->playlist.num > 0));
842   }
843 }
844 
845 /*
846  * Raise playlist->window
847  */
playlist_raise_window(void)848 void playlist_raise_window(void) {
849 
850   if(playlist != NULL) {
851     raise_window(playlist->window, playlist->visible, playlist->running);
852     mmk_editor_raise_window();
853   }
854 }
855 
856 /*
857  * Hide/show the pl panel
858  */
playlist_toggle_visibility(xitk_widget_t * w,void * data)859 void playlist_toggle_visibility (xitk_widget_t *w, void *data) {
860 
861   if(playlist != NULL) {
862     toggle_window(playlist->window, playlist->widget_list,
863 		  &playlist->visible, playlist->running);
864     mmk_editor_toggle_visibility();
865   }
866 }
867 
868 /*
869  * Update first displayed entry and input text widget content with
870  * current selected playlist entry.
871  */
playlist_update_focused_entry(void)872 void playlist_update_focused_entry(void) {
873   gGui_t *gui = gGui;
874 
875   if(playlist != NULL) {
876     if(playlist->window) {
877       if(playlist->visible && playlist->running && playlist->playlist_len) {
878 	char        *pa_mrl, *pl_mrl;
879 	mediamark_t *mmk;
880 
881         pthread_mutex_lock (&gui->mmk_mutex);
882         mmk = mediamark_get_current_mmk ();
883 	if(mmk) {
884 
885 	  pa_mrl = (gui->is_display_mrl) ? gui->mmk.mrl : gui->mmk.ident;
886 	  pl_mrl = (gui->is_display_mrl) ? mmk->mrl : mmk->ident;
887 
888 	  if(!strcmp(pa_mrl, pl_mrl)) {
889 	    int max_displayed = xitk_browser_get_num_entries(playlist->playlist);
890 	    int selected = xitk_browser_get_current_selected(playlist->playlist);
891 
892 	    if(gui->playlist.num <= max_displayed)
893 	      _playlist_update_browser_list(-1);
894 	    else if(gui->playlist.cur <= (gui->playlist.num - max_displayed))
895 	      _playlist_update_browser_list(gui->playlist.cur);
896 	    else
897 	      _playlist_update_browser_list(gui->playlist.num - max_displayed);
898 
899 	    if(selected >= 0) {
900 	      xitk_browser_set_select(playlist->playlist, selected);
901  	      xitk_inputtext_change_text(playlist->winput,
902  					 playlist->playlist_mrls[selected]);
903 	    }
904 	    else
905 	      xitk_inputtext_change_text(playlist->winput,
906 					 playlist->playlist_mrls[gui->playlist.cur]);
907 
908 	  }
909 	}
910 	else
911 	  xitk_inputtext_change_text(playlist->winput, NULL);
912         pthread_mutex_unlock (&gui->mmk_mutex);
913       }
914     }
915   }
916 }
917 
918 /*
919  * Change the current skin.
920  */
playlist_change_skins(int synthetic)921 void playlist_change_skins(int synthetic) {
922   gGui_t *gui = gGui;
923   ImlibImage   *new_img, *old_img;
924   XSizeHints    hint;
925 
926   if(playlist_is_running()) {
927 
928     xitk_skin_lock(gui->skin_config);
929     xitk_hide_widgets(playlist->widget_list);
930 
931     gui->x_lock_display (gui->display);
932 
933     if(!(new_img = Imlib_load_image(gui->imlib_data,
934 				    xitk_skin_get_skin_filename(gui->skin_config, "PlBG")))) {
935       xine_error(_("%s(): couldn't find image for background\n"), __XINE_FUNCTION__);
936       exit(-1);
937     }
938 
939     hint.width  = new_img->rgb_width;
940     hint.height = new_img->rgb_height;
941     hint.flags  = PPosition | PSize;
942     XSetWMNormalHints(gui->display, playlist->window, &hint);
943 
944     XResizeWindow (gui->display, playlist->window,
945 		   (unsigned int)new_img->rgb_width,
946 		   (unsigned int)new_img->rgb_height);
947 
948     gui->x_unlock_display (gui->display);
949 
950     while(!xitk_is_window_size(gui->display, playlist->window,
951 			       new_img->rgb_width, new_img->rgb_height)) {
952       xine_usec_sleep(10000);
953     }
954 
955     old_img = playlist->bg_image;
956     playlist->bg_image = new_img;
957 
958     gui->x_lock_display (gui->display);
959 
960     if(!gui->use_root_window && gui->video_display == gui->display)
961       XSetTransientForHint(gui->display, playlist->window, gui->video_window);
962 
963     Imlib_destroy_image(gui->imlib_data, old_img);
964     Imlib_apply_image(gui->imlib_data, new_img, playlist->window);
965     gui->x_unlock_display (gui->display);
966 
967     if(playlist_is_visible())
968       playlist_raise_window();
969 
970     xitk_skin_unlock(gui->skin_config);
971 
972     xitk_change_skins_widget_list(playlist->widget_list, gui->skin_config);
973 
974     xitk_button_list_new_skin (playlist->autoplay_buttons, gui->skin_config);
975 
976     xitk_paint_widget_list(playlist->widget_list);
977   }
978 }
979 
playlist_deinit(void)980 void playlist_deinit(void) {
981   if(playlist) {
982     if(playlist_is_visible())
983       playlist_toggle_visibility(NULL, NULL);
984     playlist_exit(NULL, NULL);
985   }
986   else {
987     if(load_fb)
988       filebrowser_end(load_fb);
989     if(save_fb)
990       filebrowser_end(save_fb);
991   }
992 }
993 
playlist_reparent(void)994 void playlist_reparent(void) {
995   if(playlist)
996     reparent_window(playlist->window);
997 }
998 
999 /*
1000  * Create playlist editor window
1001  */
playlist_editor(void)1002 void playlist_editor(void) {
1003   gGui_t *gui = gGui;
1004   GC                         gc;
1005   XSizeHints                 hint;
1006   XWMHints                  *wm_hint;
1007   XSetWindowAttributes       attr;
1008   char                      *title = _("xine Playlist Editor");
1009   Atom                       prop;
1010   MWMHints                   mwmhints;
1011   XClassHint                *xclasshint;
1012   xitk_browser_widget_t      br;
1013   xitk_labelbutton_widget_t  lb;
1014   xitk_label_widget_t        lbl;
1015   xitk_inputtext_widget_t    inp;
1016   xitk_button_widget_t       b;
1017 
1018   XITK_WIDGET_INIT(&br, gui->imlib_data);
1019   XITK_WIDGET_INIT(&lb, gui->imlib_data);
1020   XITK_WIDGET_INIT(&lbl, gui->imlib_data);
1021   XITK_WIDGET_INIT(&inp, gui->imlib_data);
1022   XITK_WIDGET_INIT(&b, gui->imlib_data);
1023 
1024   playlist = (_playlist_t *) calloc(1, sizeof(_playlist_t));
1025 
1026   playlist->playlist_len = 0;
1027 
1028   _playlist_create_playlists();
1029 
1030   gui->x_lock_display (gui->display);
1031 
1032   if (!(playlist->bg_image = Imlib_load_image(gui->imlib_data,
1033 					      xitk_skin_get_skin_filename(gui->skin_config, "PlBG")))) {
1034     xine_error(_("playlist: couldn't find image for background\n"));
1035     exit(-1);
1036   }
1037 
1038   hint.x = xine_config_register_num (__xineui_global_xine_instance, "gui.playlist_x",
1039 				     200,
1040 				     CONFIG_NO_DESC,
1041 				     CONFIG_NO_HELP,
1042 				     CONFIG_LEVEL_DEB,
1043 				     CONFIG_NO_CB,
1044 				     CONFIG_NO_DATA);
1045   hint.y = xine_config_register_num (__xineui_global_xine_instance, "gui.playlist_y",
1046 				     200,
1047 				     CONFIG_NO_DESC,
1048 				     CONFIG_NO_HELP,
1049 				     CONFIG_LEVEL_DEB,
1050 				     CONFIG_NO_CB,
1051 				     CONFIG_NO_DATA);
1052 
1053   hint.width = playlist->bg_image->rgb_width;
1054   hint.height = playlist->bg_image->rgb_height;
1055   hint.flags = PPosition | PSize;
1056 
1057   attr.override_redirect = False;
1058   attr.background_pixel  = gui->black.pixel;
1059   /*
1060    * XXX:multivis
1061    * To avoid BadMatch errors on XCreateWindow:
1062    * If the parent and the new window have different depths, we must supply either
1063    * a BorderPixmap or a BorderPixel.
1064    * If the parent and the new window use different visuals, we must supply a
1065    * Colormap
1066    */
1067   attr.border_pixel      = 1;
1068   attr.colormap		 = Imlib_get_colormap(gui->imlib_data);
1069 
1070   playlist->window = XCreateWindow (gui->display,
1071 				    gui->imlib_data->x.root,
1072 				    hint.x, hint.y, hint.width,
1073 				    hint.height, 0,
1074 				    gui->imlib_data->x.depth, InputOutput,
1075 				    gui->imlib_data->x.visual,
1076 				    CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect,
1077 				    &attr);
1078 
1079   XmbSetWMProperties(gui->display, playlist->window, title, title, NULL, 0,
1080                      &hint, NULL, NULL);
1081 
1082   XSelectInput(gui->display, playlist->window, INPUT_MOTION | KeymapStateMask);
1083   gui->x_unlock_display (gui->display);
1084 
1085   if (!video_window_is_visible (gui->vwin))
1086     xitk_set_wm_window_type(playlist->window, WINDOW_TYPE_NORMAL);
1087   else
1088     xitk_unset_wm_window_type(playlist->window, WINDOW_TYPE_NORMAL);
1089 
1090   if(is_layer_above())
1091     xitk_set_layer_above(playlist->window);
1092 
1093   /*
1094    * wm, no border please
1095    */
1096 
1097   memset(&mwmhints, 0, sizeof(mwmhints));
1098   gui->x_lock_display (gui->display);
1099   prop = XInternAtom(gui->display, "_MOTIF_WM_HINTS", True);
1100   mwmhints.flags = MWM_HINTS_DECORATIONS;
1101   mwmhints.decorations = 0;
1102 
1103   XChangeProperty(gui->display, playlist->window, prop, prop, 32,
1104                   PropModeReplace, (unsigned char *) &mwmhints,
1105                   PROP_MWM_HINTS_ELEMENTS);
1106 
1107   /* set xclass */
1108 
1109   if((xclasshint = XAllocClassHint()) != NULL) {
1110     xclasshint->res_name = title;
1111     xclasshint->res_class = "xine";
1112     XSetClassHint(gui->display, playlist->window, xclasshint);
1113     XFree(xclasshint);
1114   }
1115 
1116   wm_hint = XAllocWMHints();
1117   if (wm_hint != NULL) {
1118     wm_hint->input = True;
1119     wm_hint->initial_state = NormalState;
1120     wm_hint->icon_pixmap   = gui->icon;
1121     wm_hint->flags = InputHint | StateHint | IconPixmapHint;
1122     XSetWMHints(gui->display, playlist->window, wm_hint);
1123     XFree(wm_hint);
1124   }
1125 
1126   gc = XCreateGC(gui->display, playlist->window, 0, 0);
1127 
1128   Imlib_apply_image(gui->imlib_data, playlist->bg_image, playlist->window);
1129 
1130   gui->x_unlock_display (gui->display);
1131 
1132   /*
1133    * Widget-list
1134    */
1135   playlist->widget_list = xitk_widget_list_new();
1136   xitk_widget_list_set(playlist->widget_list, WIDGET_LIST_WINDOW, (void *) playlist->window);
1137   xitk_widget_list_set(playlist->widget_list, WIDGET_LIST_GC, gc);
1138 
1139   lbl.window          = (XITK_WIDGET_LIST_WINDOW(playlist->widget_list));
1140   lbl.gc              = (XITK_WIDGET_LIST_GC(playlist->widget_list));
1141 
1142   b.skin_element_name = "PlMoveUp";
1143   b.callback          = playlist_move_current_updown;
1144   b.userdata          = (void *)DIRECTION_UP;
1145   playlist->move_up =  xitk_button_create (playlist->widget_list, gui->skin_config, &b);
1146   xitk_add_widget (playlist->widget_list, playlist->move_up);
1147   xitk_set_widget_tips(playlist->move_up, _("Move up selected MRL"));
1148 
1149   b.skin_element_name = "PlMoveDn";
1150   b.callback          = playlist_move_current_updown;
1151   b.userdata          = (void *)DIRECTION_DOWN;
1152   playlist->move_down =  xitk_button_create (playlist->widget_list, gui->skin_config, &b);
1153   xitk_add_widget (playlist->widget_list, playlist->move_down);
1154   xitk_set_widget_tips(playlist->move_down, _("Move down selected MRL"));
1155 
1156   b.skin_element_name = "PlPlay";
1157   b.callback          = playlist_play_current;
1158   b.userdata          = NULL;
1159   playlist->play =  xitk_button_create (playlist->widget_list, gui->skin_config, &b);
1160   xitk_add_widget (playlist->widget_list, playlist->play);
1161   xitk_set_widget_tips(playlist->play, _("Start playback of selected MRL"));
1162 
1163   b.skin_element_name = "PlDelete";
1164   b.callback          = playlist_delete_current;
1165   b.userdata          = NULL;
1166   playlist->delete =  xitk_button_create (playlist->widget_list, gui->skin_config, &b);
1167   xitk_add_widget (playlist->widget_list, playlist->delete);
1168   xitk_set_widget_tips(playlist->delete, _("Delete selected MRL from playlist"));
1169 
1170   b.skin_element_name = "PlDeleteAll";
1171   b.callback          = playlist_delete_all;
1172   b.userdata          = NULL;
1173   playlist->delete_all =  xitk_button_create (playlist->widget_list, gui->skin_config, &b);
1174   xitk_add_widget (playlist->widget_list, playlist->delete_all);
1175   xitk_set_widget_tips(playlist->delete_all, _("Delete all entries in playlist"));
1176 
1177   lb.skin_element_name = "PlAdd";
1178   lb.button_type       = CLICK_BUTTON;
1179   lb.label             = _("Add");
1180   lb.align             = ALIGN_DEFAULT;
1181   lb.callback          = open_mrlbrowser_from_playlist;
1182   lb.state_callback    = NULL;
1183   lb.userdata          = gui;
1184   playlist->add =  xitk_labelbutton_create (playlist->widget_list, gui->skin_config, &lb);
1185   xitk_add_widget (playlist->widget_list, playlist->add);
1186   xitk_set_widget_tips(playlist->add, _("Add one or more entries in playlist"));
1187 
1188   lb.skin_element_name = "PlLoad";
1189   lb.button_type       = CLICK_BUTTON;
1190   lb.label             = _("Load");
1191   lb.callback          = playlist_load_playlist;
1192   lb.state_callback    = NULL;
1193   lb.userdata          = NULL;
1194   playlist->load =  xitk_labelbutton_create (playlist->widget_list, gui->skin_config, &lb);
1195   xitk_add_widget (playlist->widget_list, playlist->load);
1196   xitk_set_widget_tips(playlist->load, _("Load saved playlist"));
1197 
1198   lb.skin_element_name = "PlSave";
1199   lb.button_type       = CLICK_BUTTON;
1200   lb.label             = _("Save");
1201   lb.callback          = playlist_save_playlist;
1202   lb.state_callback    = NULL;
1203   lb.userdata          = NULL;
1204   playlist->save =  xitk_labelbutton_create (playlist->widget_list, gui->skin_config, &lb);
1205   xitk_add_widget (playlist->widget_list, playlist->save);
1206   xitk_set_widget_tips(playlist->save, _("Save playlist"));
1207 
1208   lb.skin_element_name = "PlDismiss";
1209   lb.button_type       = CLICK_BUTTON;
1210   lb.label             = _("Dismiss");
1211   lb.callback          = playlist_exit;
1212   lb.state_callback    = NULL;
1213   lb.userdata          = NULL;
1214   playlist->close =  xitk_labelbutton_create (playlist->widget_list, gui->skin_config, &lb);
1215   xitk_add_widget (playlist->widget_list, playlist->close);
1216   xitk_set_widget_tips(playlist->close, _("Close playlist window"));
1217 
1218   br.arrow_up.skin_element_name    = "PlUp";
1219   br.slider.skin_element_name      = "SliderPl";
1220   br.arrow_dn.skin_element_name    = "PlDn";
1221   br.arrow_left.skin_element_name  = "PlLeft";
1222   br.slider_h.skin_element_name    = "SliderHPl";
1223   br.arrow_right.skin_element_name = "PlRight";
1224   br.browser.skin_element_name     = "PlItemBtn";
1225   br.browser.num_entries           = playlist->playlist_len;
1226   br.browser.entries               = (const char **)playlist->playlist_mrls;
1227   br.callback                      = _playlist_handle_selection;
1228   br.dbl_click_callback            = _playlist_play_on_dbl_click;
1229   br.parent_wlist                  = playlist->widget_list;
1230   playlist->playlist =  xitk_browser_create(playlist->widget_list, gui->skin_config, &br);
1231   xitk_add_widget (playlist->widget_list, playlist->playlist);
1232 
1233   lbl.skin_element_name = "AutoPlayLbl";
1234   /* TRANSLATORS: only ASCII characters (skin) */
1235   lbl.label             = pgettext("skin", "Scan for:");
1236   lbl.callback          = NULL;
1237   xitk_add_widget (playlist->widget_list, xitk_label_create (playlist->widget_list, gui->skin_config, &lbl));
1238 
1239   inp.skin_element_name = "PlInputText";
1240   inp.text              = NULL;
1241   inp.max_length        = 256;
1242   inp.callback          = _playlist_add_input;
1243   inp.userdata          = NULL;
1244   playlist->winput =  xitk_inputtext_create (playlist->widget_list, gui->skin_config, &inp);
1245   xitk_add_widget (playlist->widget_list, playlist->winput);
1246   xitk_set_widget_tips(playlist->winput, _("Direct MRL entry"));
1247 
1248   do {
1249     char *tips[64];
1250     const char * const *autoplay_plugins = xine_get_autoplay_input_plugin_ids (__xineui_global_xine_instance);
1251     unsigned int i;
1252 
1253     if (!autoplay_plugins)
1254       return;
1255 
1256     for (i = 0; autoplay_plugins[i]; i++) {
1257       if (i >= sizeof (tips) / sizeof (tips[0]))
1258         break;
1259       tips[i] = (char *)xine_get_input_plugin_description (__xineui_global_xine_instance, autoplay_plugins[i]);
1260     }
1261 
1262     playlist->autoplay_buttons = xitk_button_list_new (
1263       gui->imlib_data, playlist->widget_list,
1264       gui->skin_config, "AutoPlayBG",
1265       playlist_scan_input, NULL,
1266       (char **)autoplay_plugins,
1267       tips, 5000, 0);
1268   } while (0);
1269 
1270   _playlist_update_browser_list(0);
1271 
1272   playlist_show_tips (panel_get_tips_enable (gui->panel), panel_get_tips_timeout (gui->panel));
1273 
1274   playlist->widget_key =
1275     xitk_register_event_handler("playlist",
1276 				playlist->window,
1277 				_playlist_handle_event,
1278 				NULL,
1279 				gui_dndcallback,
1280 				playlist->widget_list, (void*) playlist);
1281 
1282   playlist->visible = 1;
1283   playlist->running = 1;
1284 
1285   playlist_update_focused_entry();
1286 
1287   playlist_raise_window();
1288 
1289   try_to_set_input_focus(playlist->window);
1290 
1291   xitk_set_focus_to_widget(playlist->winput);
1292 }
1293