Hello everyone,
I created a patch today which adds multiple playlist support to Xine.
It allows you to save and load playlists from a user selectable file
(something.xinepl). Please note that this is my first version of this
patch so any comments about it are welcome :-). And I/you can make it
better, just send some comments...
--- xine-ui-0.9.13.orig/src/xitk/playlist.c
+++ xine-ui-0.9.13/src/xitk/playlist.c
@@ -28,6 +28,10 @@
#include <stdio.h>
#include <errno.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
@@ -47,9 +51,17 @@
extern gGui_t *gGui;
+enum TPlMode {
+ EPlModeNormal,
+ EPlModeSave,
+ EPlModeLoad
+};
+
typedef struct {
Window window;
xitk_widget_t *playlist;
+ char *templist[MAX_PLAYLIST_LENGTH];
+ int templist_num;
ImlibImage *bg_image;
xitk_widget_list_t *widget_list;
@@ -57,10 +69,11 @@
xitk_widget_t *autoplay_plugins[64];
int running;
int visible;
+ int mode;
xitk_register_key_t widget_key;
} _playlist_t;
-static _playlist_t *playlist;
+static _playlist_t *playlist = NULL;
#define MOVEUP 1
#define MOVEDN 2
@@ -69,6 +82,8 @@
void playlist_handle_event(XEvent *event, void *data);
+void pl_post_process(const char *message);
+static void pl_add_input(xitk_widget_t *w, void *data, char *filename);
/*
*
@@ -172,19 +187,22 @@
/*
* Start to play the selected stream on double click event in playlist.
*/
-static void pl_on_dbl_click(xitk_widget_t *w, void *data, int selected) {
-
- if(gGui->playlist[selected] != NULL) {
-
- gui_set_current_mrl(gGui->playlist[selected]);
- if(xine_get_status(gGui->xine) != XINE_STOP)
- gui_stop(NULL, NULL);
-
- gGui->playlist_cur = selected;
-
- gui_play(NULL, NULL);
- xitk_browser_release_all_buttons(playlist->playlist);
- }
+static void pl_on_dbl_click(xitk_widget_t *w, void *data, int selected)
+{
+ if (playlist->mode == EPlModeNormal) {
+ if(gGui->playlist[selected] != NULL) {
+
+ gui_set_current_mrl(gGui->playlist[selected]);
+ if(xine_get_status(gGui->xine) != XINE_STOP)
+ gui_stop(NULL, NULL);
+
+ gGui->playlist_cur = selected;
+
+ gui_play(NULL, NULL);
+ xitk_browser_release_all_buttons(playlist->playlist);
+ }
+ } else if (gGui->playlist[selected] != NULL)
+ pl_add_input(w, data, gGui->playlist[selected]);
}
/*
@@ -349,95 +367,335 @@
}
/*
- * Load $HOME/.xinepl playlist file
+ * Get button with label
*/
-static void pl_load_pl(xitk_widget_t *w, void *data) {
- FILE *plfile;
- char buf[256], *ln, tmpbuf[256];
- char *tmp_playlist[MAX_PLAYLIST_LENGTH];
- int tmp_playlist_num = -1, i;
-
- memset(&buf, 0, sizeof(buf));
- memset(&tmpbuf, 0, sizeof(tmpbuf));
-
- sprintf(tmpbuf, "%s/.xine/%s", xine_get_homedir(), PL_FILENAME);
- if((plfile = fopen(tmpbuf, "r")) == NULL) {
- xine_error(_("Can't read %s: %s\n"), tmpbuf, strerror(errno));
- return;
- }
- else {
-
- ln = fgets(buf, 255, plfile) ;
- if(!strncasecmp(ln, "[PLAYLIST]", 9)) {
- tmp_playlist_num = 0;
- ln = fgets(buf, 255, plfile);
- while(ln != NULL && strncasecmp("[END]", ln, 5)) {
-
- while(*ln == ' ' || *ln == '\t') ++ln ;
-
- tmp_playlist[tmp_playlist_num] = (char *) xine_xmalloc(strlen(ln));
- strncpy(tmp_playlist[tmp_playlist_num], ln, strlen(ln)-1);
- tmp_playlist[tmp_playlist_num][strlen(ln)-1] = '\0';
-
- tmp_playlist_num++;
-
- ln = fgets(buf, 255, plfile);
-
+xitk_widget_t *pl_get_button(const char *label)
+{
+ xitk_widget_t *b;
+
+ if (label == NULL ||
+ !xitk_list_is_empty(playlist->widget_list->l))
+ return NULL;
+
+ for (b = xitk_list_first_content(playlist->widget_list->l);
+ b != NULL;
+ b = xitk_list_next_content(playlist->widget_list->l)) {
+
+ if (b->widget_type != WIDGET_TYPE_LABELBUTTON)
+ continue;
+
+ if (!strcmp(label, xitk_labelbutton_get_label(b)))
+ return b;
+ }
+
+ return NULL;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+int pl_init_filebrowser()
+{
+ DIR *dir;
+ struct dirent *de;
+ struct stat st;
+ int len, i;
+ char prefix[256];
+
+ dir = opendir(".");
+ if (dir == NULL)
+ return -1;
+
+ for (i = 0; i < gGui->playlist_num; i++)
+ free(gGui->playlist[i]);
+ gGui->playlist_num = 0;
+
+ while ((de = readdir(dir)) != NULL) {
+ /* Check file type */
+ if (stat(de->d_name, &st) < 0)
+ return -1;
+
+ memset(&prefix, 0, sizeof(prefix));
+ if (S_ISDIR(st.st_mode))
+ snprintf(prefix, sizeof(prefix), "[DIR]");
+ else if (!S_ISREG(st.st_mode) || !strstr(de->d_name, ".xinepl"))
+ /* Display only regular (.xinepl) files and directories */
+ continue;
+
+ len = strlen(de->d_name) + strlen(prefix) + 1;
+ gGui->playlist[gGui->playlist_num] = (char *)xine_xmalloc(len);
+ snprintf(gGui->playlist[gGui->playlist_num++],
+ len, "%s%s", prefix, de->d_name);
+ }
+ closedir(dir);
+ pl_update_playlist();
+
+ return 0;
+}
+
+/*
+ * Load user selected playlist file
+ * Returns 0 on success or:
+ * -1 read error
+ * -2 if filename is invalid
+ */
+int pl_load_pl(const char *filename) {
+ FILE *plfile;
+ char buf[256], *ln;
+ int i;
+
+ if (strchr(filename, '.') == NULL)
+ snprintf(buf, sizeof(buf), "%s.xinepl", filename);
+ else {
+ if (!strstr(filename, ".xinepl"))
+ return -3;
+ snprintf(buf, sizeof(buf), "%s", filename);
+ }
+
+ if((plfile = fopen(filename, "r")) == NULL) {
+ // xine_error(_("Can't read %s: %s\n"), filename, strerror(errno));
+ return -1;
+ }
+ else {
+ for (i = 0; i < playlist->templist_num; i++)
+ free(playlist->templist[i]);
+ playlist->templist_num = 0;
+
+ ln = fgets(buf, 255, plfile) ;
+ if(!strncasecmp(ln, "[PLAYLIST]", 9)) {
+ while((ln = fgets(buf, 255, plfile)) != NULL && strncasecmp("[END]", ln, 5)) {
+
+ while(*ln == ' ' || *ln == '\t') ++ln ;
+
+ playlist->templist[playlist->templist_num] = (char *)xine_xmalloc(strlen(ln) + 1);
+ strncpy(playlist->templist[playlist->templist_num], ln, strlen(ln) - 1);
+ if (playlist->templist_num++ >= MAX_PLAYLIST_LENGTH)
+ break;
+ }
}
- }
-
- ln = fgets(buf, 255, plfile);
- }
-
- fclose(plfile);
-
- if(tmp_playlist_num >= 0) {
- gGui->playlist_num = tmp_playlist_num;
-
- for(i = 0; i < tmp_playlist_num; i++)
- gGui->playlist[i] = tmp_playlist[i];
-
- xitk_browser_update_list(playlist->playlist,
- gGui->playlist, gGui->playlist_num, 0);
- /* xitk_browser_rebuild_browser(playlist->playlist, 0); */
- }
-
+ }
+
+ fclose(plfile);
+
+ if (playlist->mode == EPlModeNormal)
+ pl_post_process("");
+
+ return 0;
}
/*
- * Save playlist to $HOME/.xinepl file
+ * Load user selected playlist file
+ * Returns 0 on success or:
+ * -1 read error
+ * -2 if filename is invalid
+ */
+int pl_load_pl_simple(const char *filename) {
+ FILE *plfile;
+ char buf[256], *ln;
+ int i;
+
+ if (strchr(filename, '.') == NULL)
+ snprintf(buf, sizeof(buf), "%s.xinepl", filename);
+ else {
+ if (!strstr(filename, ".xinepl"))
+ return -3;
+ snprintf(buf, sizeof(buf), "%s", filename);
+ }
+
+ if((plfile = fopen(filename, "r")) == NULL) {
+ return -1;
+ }
+ else {
+ gGui->playlist_num = 0;
+
+ ln = fgets(buf, 255, plfile) ;
+ if(!strncasecmp(ln, "[PLAYLIST]", 9)) {
+ while((ln = fgets(buf, 255, plfile)) != NULL && strncasecmp("[END]", ln, 5)) {
+
+ while(*ln == ' ' || *ln == '\t') ++ln ;
+
+ gGui->playlist[gGui->playlist_num] = (char *)xine_xmalloc(strlen(ln) + 1);
+ strncpy(gGui->playlist[gGui->playlist_num], ln, strlen(ln) - 1);
+ if (gGui->playlist_num++ >= MAX_PLAYLIST_LENGTH)
+ break;
+ }
+ }
+ }
+
+ fclose(plfile);
+
+ return 0;
+}
+
+/*
+ * Save playlist to a file
+ * Returns 0 on success or:
+ * -1 on write error
+ * -2 if list is empty
+ * -3 invalid filename
*/
-static void pl_save_pl(xitk_widget_t *w, void *data) {
+int pl_save_pl(const char *filename) {
FILE *plfile;
- char buf[1024], tmpbuf[256];
+ char buf[1024];
int i;
+
+ if (filename == NULL)
+ return -1;
+
+ if(!playlist->templist_num)
+ return -2;
- if(!gGui->playlist_num) return;
-
- memset(&buf, 0, sizeof(buf));
- memset(&tmpbuf, 0, sizeof(tmpbuf));
+ if (strchr(filename, '.') == NULL)
+ snprintf(buf, sizeof(buf), "%s.xinepl", filename);
+ else {
+ if (!strstr(filename, ".xinepl"))
+ return -3;
+ snprintf(buf, sizeof(buf), "%s", filename);
+ }
- sprintf(tmpbuf, "%s/.xine/%s", xine_get_homedir(), PL_FILENAME);
- if((plfile = fopen(tmpbuf, "w")) == NULL) {
- xine_error(_("Can't read %s: %s\n"), tmpbuf, strerror(errno));
- return;
+ if ((plfile = fopen(buf, "w+")) == NULL) {
+ // xine_error(_("Can't read %s: %s\n"), buf, strerror(errno));
+ return -1;
}
else {
- sprintf(buf, "%s", "[PLAYLIST]\n");
- fputs(buf, plfile);
+ fprintf(plfile, "%s", "[PLAYLIST]\n");
- for(i = 0; i < gGui->playlist_num; i++) {
- memset(&buf, 0, sizeof(buf));
- sprintf(buf, "%s\n", gGui->playlist[i]);
- fputs(buf, plfile);
- }
+ for(i = 0; i < playlist->templist_num; i++)
+ fprintf(plfile, "%s\n", playlist->templist[i]);
- sprintf(buf, "%s", "[END]\n");
- fputs(buf, plfile);
+ fprintf(plfile, "%s", "[END]\n");
}
fclose(plfile);
-
+
+ return 0;
+}
+
+/*
+ * Initialize lists for file browser
+ */
+void pl_pre_process()
+{
+ int i;
+
+ if (playlist->mode != EPlModeNormal)
+ return ;
+
+ playlist->templist_num = gGui->playlist_num;
+ /* Backup current playlist */
+ for (i = 0; i < gGui->playlist_num && i < MAX_PLAYLIST_LENGTH; i++)
+ playlist->templist[i] = gGui->playlist[i];
+
+ gGui->playlist_num = 0;
+}
+
+/*
+ * Initialize playlist saving
+ */
+static void pl_init_save_pl(xitk_widget_t *w, void *data)
+{
+ xitk_widget_t *sb, *lb;
+
+ pl_pre_process();
+
+ sb = pl_get_button("Save");
+ lb = pl_get_button("Load");
+
+ if (sb == NULL || lb == NULL)
+ return ;
+
+ switch (playlist->mode) {
+ case EPlModeLoad:
+ xitk_labelbutton_set_state(lb, 0, playlist->widget_list->win,
+ playlist->widget_list->gc);
+ case EPlModeNormal:
+ playlist->mode = EPlModeSave;
+ // snprintf(buf, sizeof(buf), "%s/", get_current_dir_name());
+ xitk_inputtext_change_text(playlist->widget_list,
+ playlist->winput, "playlist");
+ xitk_labelbutton_set_state(sb, 1, playlist->widget_list->win,
+ playlist->widget_list->gc);
+ if (pl_init_filebrowser() < 0)
+ pl_post_process("Error: Can't read directory");
+ break;
+
+ case EPlModeSave:
+ pl_post_process("Playlist not saved");
+ break;
+ }
+}
+
+/*
+ * Initialize playlist loading
+ */
+static void pl_init_load_pl(xitk_widget_t *w, void *data)
+{
+ xitk_widget_t *sb, *lb;
+
+ pl_pre_process();
+
+ sb = pl_get_button("Save");
+ lb = pl_get_button("Load");
+
+ if (sb == NULL || lb == NULL)
+ return ;
+
+ switch (playlist->mode) {
+ case EPlModeSave:
+ pl_post_process("");
+ pl_pre_process();
+
+ case EPlModeNormal:
+ playlist->mode = EPlModeLoad;
+ xitk_inputtext_change_text(playlist->widget_list,
+ playlist->winput, "");
+ xitk_labelbutton_set_state(lb, 1, playlist->widget_list->win,
+ playlist->widget_list->gc);
+ if (pl_init_filebrowser() < 0)
+ pl_post_process("Error: Can't read directory");
+ break;
+
+ case EPlModeLoad:
+ pl_post_process("Playlist not loaded");
+ break;
+ }
+}
+
+/*
+ * Finalize saving / loading
+ */
+void pl_post_process(const char *message)
+{
+ xitk_widget_t *sb, *lb;
+ int i;
+
+ sb = pl_get_button("Save");
+ lb = pl_get_button("Load");
+
+ if (sb == NULL || lb == NULL)
+ return ;
+
+ playlist->mode = EPlModeNormal;
+
+ /* Unglue buttons */
+ xitk_labelbutton_set_state(sb, 0, playlist->widget_list->win,
+ playlist->widget_list->gc);
+ xitk_labelbutton_set_state(lb, 0, playlist->widget_list->win,
+ playlist->widget_list->gc);
+
+ /* Output text */
+ xitk_inputtext_change_text(playlist->widget_list,
+ playlist->winput, message);
+
+ /* Restore original or loaded playlist */
+ for (i = 0; i < gGui->playlist_num; i++)
+ free(gGui->playlist[i]);
+
+ gGui->playlist_num = playlist->templist_num;
+ for (i = 0; i < playlist->templist_num && i < MAX_PLAYLIST_LENGTH; i++)
+ gGui->playlist[i] = playlist->templist[i];
+
+ pl_update_playlist();
}
/*
@@ -623,9 +881,49 @@
*
*/
static void pl_add_input(xitk_widget_t *w, void *data, char *filename) {
-
- if(filename)
- gui_dndcallback((char *)filename);
+ int stat;
+ char buf[256];
+
+ if (playlist->mode != EPlModeNormal) {
+ /* Check if filename is directory */
+ if (!strncmp(filename, "[DIR]", 5)) {
+ snprintf(buf, sizeof(buf), "%s", (char *)&filename[5]);
+ if (chdir(buf) < 0)
+ pl_post_process("Error: Can't change directory");
+ else if (pl_init_filebrowser() < 0)
+ pl_post_process("Error: Can't read directory");
+ return ;
+ }
+ }
+
+ switch (playlist->mode) {
+ case EPlModeNormal:
+ if(filename)
+ gui_dndcallback((char *)filename);
+ break;
+
+ case EPlModeSave:
+ stat = pl_save_pl(filename);
+ if (stat == 0)
+ pl_post_process("Saved.");
+ else if (stat == -2)
+ pl_post_process("Nothing to save.");
+ else if (stat == -3)
+ pl_post_process("Filename must be something.xinepl");
+ else
+ pl_post_process("Failed to save.");
+ break;
+
+ case EPlModeLoad:
+ stat = pl_load_pl(filename);
+ if (stat == 0)
+ pl_post_process("Loaded.");
+ else if (stat == -2)
+ pl_post_process("Filename must be something.xinepl");
+ else
+ pl_post_process("Failed to load.");
+ break;
+ }
}
/*
@@ -890,7 +1188,7 @@
lb.skin_element_name = "PlLoad";
lb.button_type = CLICK_BUTTON;
lb.label = _("Load");
- lb.callback = pl_load_pl;
+ lb.callback = pl_init_load_pl;
lb.state_callback = NULL;
lb.userdata = NULL;
xitk_list_append_content (playlist->widget_list->l,
@@ -900,7 +1198,7 @@
lb.skin_element_name = "PlSave";
lb.button_type = CLICK_BUTTON;
lb.label = _("Save");
- lb.callback = pl_save_pl;
+ lb.callback = pl_init_save_pl;
lb.state_callback = NULL;
lb.userdata = NULL;
xitk_list_append_content (playlist->widget_list->l,
@@ -994,5 +1292,6 @@
playlist->visible = 1;
playlist->running = 1;
+ playlist->mode = EPlModeNormal;
pl_update_focused_entry();
}
--- xine-ui-0.9.13.orig/src/xitk/xine-toolkit/inputtext.c
+++ xine-ui-0.9.13/src/xitk/xine-toolkit/inputtext.c
@@ -958,7 +958,7 @@
/*
* Change and redisplay the text of widget.
*/
-void xitk_inputtext_change_text(xitk_widget_list_t *wl, xitk_widget_t *w, char *text) {
+void xitk_inputtext_change_text(xitk_widget_list_t *wl, xitk_widget_t *w, const char *text) {
inputtext_private_data_t *private_data;
if(w && ((w->widget_type & WIDGET_TYPE_MASK) == WIDGET_TYPE_INPUTTEXT)) {
--- xine-ui-0.9.13.orig/src/xitk/xine-toolkit/labelbutton.c
+++ xine-ui-0.9.13/src/xitk/xine-toolkit/labelbutton.c
@@ -323,7 +323,8 @@
if (w && ((w->widget_type & WIDGET_TYPE_MASK) == WIDGET_TYPE_LABELBUTTON)) {
private_data = (lbutton_private_data_t *) w->private_data;
- private_data->bClicked = !bUp;
+ if (!private_data->bGlued)
+ private_data->bClicked = !bUp;
private_data->bOldState = private_data->bState;
if (bUp && (private_data->focus == FOCUS_RECEIVED)) {
@@ -394,6 +395,10 @@
if (w && ((w->widget_type & WIDGET_TYPE_MASK) == WIDGET_TYPE_LABELBUTTON)) {
private_data = (lbutton_private_data_t *) w->private_data;
+
+ if (private_data->bGlued)
+ return 1;
+
private_data->focus = focus;
}
@@ -493,6 +498,7 @@
/*
* Set radio button to state 'state'
+ * Can be used also to glue click buttons
*/
void xitk_labelbutton_set_state(xitk_widget_t *w, int state, Window win, GC gc) {
lbutton_private_data_t *private_data;
@@ -519,6 +525,12 @@
paint_labelbutton(w, win, gc);
}
+ } else if (private_data->bType == CLICK_BUTTON) {
+ private_data->focus = state ? FOCUS_RECEIVED : FOCUS_LOST;
+ private_data->bClicked = 0;
+ private_data->bGlued = state;
+
+ paint_labelbutton(w, win, gc);
}
}
@@ -551,6 +563,7 @@
private_data->bClicked = 0;
private_data->focus = FOCUS_LOST;
private_data->bState = 0;
+ private_data->bGlued = 0;
private_data->bOldState = 0;
private_data->callback = b->callback;
--- xine-ui-0.9.13.orig/src/xitk/xine-toolkit/inputtext.h
+++ xine-ui-0.9.13/src/xitk/xine-toolkit/inputtext.h
@@ -94,7 +94,7 @@
/*
* Set text in inputtext widget.
*/
-void xitk_inputtext_change_text(xitk_widget_list_t *wl, xitk_widget_t *it, char *text);
+void xitk_inputtext_change_text(xitk_widget_list_t *wl, xitk_widget_t *it, const char *text);
/*
* Return key modifier from an xevent struct.
--- xine-ui-0.9.13.orig/src/xitk/xine-toolkit/labelbutton.h
+++ xine-ui-0.9.13/src/xitk/xine-toolkit/labelbutton.h
@@ -44,6 +44,7 @@
int focus;
int bState;
+ int bGlued;
int bOldState;
xitk_image_t *skin;
--
Miika Pekkarinen <miika@...>
http://www.ihme.org/~miipekk/
+358 44 5331441
|