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