/* * This file part of gsopcast - A gtk front-end of p2p tv sopcast. * http://lianwei3.googlepages.com/home2 * Copyright (C) 2006 Wei Lian * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "header.h" #include "fork.h" #include "channel.h" #include "loadsave.h" #include "sound.h" #include "iostatistics.h" #include "iorecord.h" #include "iochannel.h" #include "unique.h" extern int stdin_pipe[2]; extern GtkWidget *label_record; extern GtkWidget *entry_status; extern GtkWidget *entry_player; extern GtkWidget *entry_url; extern GtkWidget *entry_url_header; extern GtkWidget *entry_record_dir; extern GtkWidget *button_channel; extern GtkWidget *entry_search; extern GtkWidget *entry_inport; extern GtkWidget *entry_outport; extern char *title; extern guint title_len; GList *items_title; GList *items_url; extern GtkTreeView *tree_channel; extern pid_t pid_sop; extern pid_t pid_player; extern pid_t pid_channel; extern char sop_ip[128]; extern int elapsed_seconds; gboolean flag_player = FALSE; extern Channel iostatistics; extern guint ptimer_statistics; extern Channel iorecord; extern guint ptimer_record; extern FILE *record_fp; extern GtkComboBoxEntry *combo_box_entry; extern Sound *sound; extern void change_notebook_tab(gboolean right); /*void on_combo_changed (GtkEntry * entry) { // printf ("%s\n", gtk_entry_get_text (entry)); } */ /* Handle channel selected event. * Channel descriptions will be displayed at the right pan. */ gboolean on_treeview1_row_selected (GtkTreeView * tree_view, gpointer user_data) { GtkTextBuffer *textbuffer; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; char *desc; textbuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(user_data)); model = gtk_tree_view_get_model(tree_view); gtk_tree_view_get_cursor(tree_view, &path, NULL); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, DESCRIPTION_COLUMN, &desc, -1); if (desc) { gtk_text_buffer_set_text(textbuffer, desc, -1); } else { gtk_text_buffer_set_text(textbuffer, "", -1); } free(desc); gtk_tree_path_free(path); return FALSE; } //----------------------------------------------------- void start_statistics_monitor() { ///close record_fp if (record_fp != NULL) { fclose(record_fp); record_fp = NULL; } ///close io channel if (iostatistics.tag) { delete_iochannel(iostatistics); } if (iorecord.tag) { delete_iochannel(iorecord); } ///////stop timer if (ptimer_statistics != 0) g_source_remove(ptimer_statistics); if (ptimer_record != 0) g_source_remove(ptimer_record); ////set record label to default gtk_label_set_text(GTK_LABEL(label_record), "Record"); gtk_entry_set_text(GTK_ENTRY(entry_status), ""); ////set record color to default GdkColor color; gdk_color_parse("black", &color); gtk_widget_modify_fg(label_record, GTK_STATE_NORMAL, &color); ////create timer for statistics show elapsed_seconds = 0; ptimer_statistics = g_timeout_add(1000, statistics_monitor, NULL); ///create timer ////start player flag_player = TRUE; } //----------------------------------------------------- GList *list_operate(GList * list, char *key) { GList *item = list; while (item) { if (strcmp((char *) item->data, key) == 0) { GList *last = g_list_last(list); last->next = list; list->prev = last; item->prev->next = 0; item->prev = 0; return item; } item = item->next; } return NULL; } //----------------------------------------------------- char *list_search_url(GList * slist, GList * ulist, const gchar * key) { GList *sitem = slist; GList *uitem = ulist; while (sitem) { if (strcmp((char *) sitem->data, key) == 0) { return (char *) uitem->data; } sitem = sitem->next; uitem = uitem->next; } return (char *) ulist->data; } //----------------------------------------------------------------- /* * Fill the launch combo box entry with a name and a url */ void fill_launch_combo(gchar *name, gchar *url){ // Fill combo box entry // Will Copy the strings if neccessary. GtkListStore *combo_list; GList *item; GtkTreeIter iter; GList *result; result = list_operate(items_url, url); if (result) { items_url = result; items_title = list_operate(items_title, name); } else { items_title = g_list_prepend(items_title, g_strdup(name)); items_url = g_list_prepend(items_url, g_strdup(url)); } item = items_title; // Global linked list of titles combo_list = GTK_LIST_STORE(gtk_combo_box_get_model( GTK_COMBO_BOX(combo_box_entry))); gtk_list_store_clear(combo_list); while (item) { gtk_list_store_append(combo_list, &iter); gtk_list_store_set(combo_list, &iter, 0, item->data, -1); item = item->next; } gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box_entry), 0); } //----------------------------------------------------- void on_treeview1_row_activated(GtkTreeView * treeview, GtkTreePath * arg1, GtkTreeViewColumn * arg2, gpointer user_data) { GtkTreeSelection *selection; selection = gtk_tree_view_get_selection(treeview); GtkTreeIter iter; GtkTreeModel *model; GtkTreePath *path; gchar *name = NULL, *url = NULL; if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, URL_COLUMN, &url, -1); if (*url == '\0') ///is group { path = gtk_tree_model_get_path(model, &iter); if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(treeview), path)) { gtk_tree_view_expand_row(GTK_TREE_VIEW(treeview), path, FALSE); } else { gtk_tree_view_collapse_row(GTK_TREE_VIEW(treeview), path); } gtk_tree_path_free(path); } else ///is channel { kill_player(); kill_sop(); fork_sop(url); strcpy(sop_ip, url); gtk_tree_model_get(model, &iter, NAME_COLUMN, &name, -1); strcpy(title, name); fill_launch_combo(name, url); g_free(name); start_statistics_monitor(); } g_free(url); } } //----------------------------------------------------- void on_button_sopcast_clicked() { const gchar *str = NULL; str = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(combo_box_entry)->child)); if (strlen(str) != 0) { char *url = NULL; gchar *strtmp, *bare_str; // Hack to get us enough space. // title was allocated in channel update which is too late if // we have a command line uri to connect to. if (!title || title_len < strlen(str)){ char *newtitle = (char *) realloc(title, strlen(str) + 1); if (newtitle != NULL) { title = newtitle; title_len = strlen(str) + 1; } } strcpy(title, str); strtmp = g_strdup(str); //We should not touch _get_text() result bare_str = g_strstrip(strtmp); // If we have a sop protocol, use it directly. if (g_str_has_prefix(bare_str, "sop://")){ url = bare_str; str = bare_str; }else if(items_title) { url = list_search_url(items_title, items_url, str); } if (url && g_str_has_prefix(url, "sop://")){ fill_launch_combo((char*)str, url); kill_player(); kill_sop(); fork_sop(url); strcpy(sop_ip, url); start_statistics_monitor(); } g_free(strtmp); } } //----------------------------------------------------- void trim(char *str) { int size = strlen(str) - 1; while (str[size] == 13 || str[size] == 10 || str[size] == ' ') { str[size] = '\0'; size--; } size = strlen(str); while (str[0] == ' ') { size--; memmove(str, str + 1, size); } str[size] = '\0'; } //----------------------------------------------------- void tree_search() { const gchar *str; str = gtk_entry_get_text(GTK_ENTRY(entry_search)); gchar *str2 = (char *) malloc(strlen(str) + 1); strcpy(str2, str); trim(str2); ///search if (strlen(str2) != 0) tree_search_key(str2); free(str2); } //----------------------------------------------------- ////search void on_entry_search_activated() { tree_search(); } //----------------------------------------------------- void on_button_search_clicked() { tree_search(); } //----------------------------------------------------- ////expand/collapse void on_button_expand_clicked(GtkWidget * widget) { gboolean flag = TRUE; if (strcmp(gtk_button_get_label(GTK_BUTTON(widget)), "Expand") == 0) { gtk_button_set_label(GTK_BUTTON(widget), "Collap"); flag = TRUE; } else if (strcmp(gtk_button_get_label(GTK_BUTTON(widget)), "Collap") == 0) { gtk_button_set_label(GTK_BUTTON(widget), "Expand"); flag = FALSE; } GtkTreeModel *model; model = gtk_tree_view_get_model(tree_channel); if (model != NULL) ///on program initialization, model will equal 0 { gboolean valid; GtkTreeIter iter; GtkTreePath *path; valid = gtk_tree_model_get_iter_first(model, &iter); while (valid) { path = gtk_tree_model_get_path(model, &iter); if (flag == TRUE && !gtk_tree_view_row_expanded(tree_channel, path)) { gtk_tree_view_expand_row(tree_channel, path, TRUE); } else if (flag == FALSE && gtk_tree_view_row_expanded(tree_channel, path)) { gtk_tree_view_collapse_row(tree_channel, path); } gtk_tree_path_free(path); valid = gtk_tree_model_iter_next(model, &iter); } } } //----------------------------------------------------- void on_button_player_clicked() { kill_player(); pid_player = -2; /////// if (pid_sop != -2) { flag_player = TRUE; } } //----------------------------------------------------- void on_button_channel_toggled(GtkWidget * widget, gpointer data) { if ((GTK_TOGGLE_BUTTON(widget)->active) && pid_channel == -2) fork_channel(); if ((!(GTK_TOGGLE_BUTTON(widget)->active)) && (pid_channel != -2)) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); } //----------------------------------------------------- void on_button_stop_clicked() { ///close record_fp if (record_fp != NULL) { fclose(record_fp); record_fp = NULL; } ///close io channel if (iostatistics.tag) { delete_iochannel(iostatistics); } if (iorecord.tag) { delete_iochannel(iorecord); } ///////stop timer if (ptimer_statistics != 0) g_source_remove(ptimer_statistics); if (ptimer_record != 0) g_source_remove(ptimer_record); ////set record label to default gtk_label_set_text(GTK_LABEL(label_record), "Record"); gtk_entry_set_text(GTK_ENTRY(entry_status), ""); ////set record color to default GdkColor color; gdk_color_parse("black", &color); gtk_widget_modify_fg(label_record, GTK_STATE_NORMAL, &color); ///kill process kill_player(); kill_sop(); } //----------------------------------------------------- void destroy() { //////store tree state save_tree_state(); ///// kill_player(); kill_sop(); if (kill((pid_t) pid_channel, SIGINT) == -1) kill((pid_t) pid_channel, SIGKILL); gtk_main_quit(); } //----------------------------------------------------- void on_window1_destroy() { destroy(); } //----------------------------------------------------- void on_button_exit_clicked() { destroy(); } //----------------------------------------------------- void on_adj_sound_change_value(GtkAdjustment * adj) { sound->volume((int) adj->value); } //----------------------------------------------------- void on_button_save_clicked() { save_config(); } //----------------------------------------------------- void on_button_record_clicked(GtkWidget * widget, gpointer data) { if (strcmp(gtk_label_get_text(GTK_LABEL(label_record)), "Record") == 0) { ptimer_record = g_timeout_add(1000, launch_record, NULL); gtk_label_set_text(GTK_LABEL(label_record), "|Quit|"); } else if (strcmp(gtk_label_get_text(GTK_LABEL(label_record)), "|Quit|") == 0) { ///close record_fp if (record_fp != NULL) { fclose(record_fp); record_fp = NULL; } ///close io channel if (iorecord.tag) { delete_iochannel(iorecord); } ///////stop timer if (ptimer_record != 0) g_source_remove(ptimer_record); gtk_label_set_text(GTK_LABEL(label_record), "Record"); ////set record color to default GdkColor color; gdk_color_parse("black", &color); gtk_widget_modify_fg(label_record, GTK_STATE_NORMAL, &color); } } //---------------------------------------------------- /* Timeout handler to reload channel list periodically. */ gboolean on_channel_update(gpointer user_data){ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button_channel)) != TRUE) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_channel), TRUE); } return TRUE; } gboolean remote_call(GtkClipboard *clip, GdkEvent *event, gpointer data) { gchar *text; text = gtk_clipboard_wait_for_text(clip); if (!text || (strcmp(text, "NONE") != 0)){ GtkEntry *entry; unique_set_text("NONE", FALSE); if (text){ entry = GTK_ENTRY(GTK_BIN(combo_box_entry)->child); gtk_entry_set_text(entry, text); on_button_sopcast_clicked(); } } if (text) g_free(text); return TRUE; } //An idle callback to make connection when gtk is on idle. gboolean connect_on_idle(gpointer uri){ GtkEntry *entry; entry = GTK_ENTRY(GTK_BIN(combo_box_entry)->child); gtk_entry_set_text(entry, (char*)uri); on_button_sopcast_clicked(); return FALSE; } //----------------------------------------------------- #include ////key events gboolean vKeyPressReleaseCallback(GtkWidget * window, GdkEventKey * event) { gboolean pass_out = TRUE; // If we pass the key to external player. switch (event->keyval) { case GDK_Return: return FALSE; case GDK_Escape: gtk_entry_set_text(GTK_ENTRY(entry_search), ""); if (!GTK_WIDGET_HAS_FOCUS(entry_search)) gtk_widget_grab_focus(entry_search); pass_out = FALSE; break; case GDK_Page_Up: case GDK_Page_Down: case GDK_KP_Page_Up: case GDK_KP_Page_Down: if ((event->state & GDK_CONTROL_MASK) && (!(event->state & GDK_MOD1_MASK)) && (!(event->state & GDK_SHIFT_MASK))) { change_notebook_tab(event->keyval == GDK_Page_Down); pass_out = FALSE; break; } case GDK_Up: case GDK_Down: case GDK_KP_Up: case GDK_KP_Down: case GDK_Home: case GDK_End: case GDK_KP_Home: case GDK_KP_End: case GDK_KP_Enter: if (GTK_WIDGET_HAS_FOCUS(tree_channel)) { return FALSE; } break; case GDK_q: case GDK_Q: if ((event->state & GDK_CONTROL_MASK) && (!(event->state & GDK_MOD1_MASK)) && (!(event->state & GDK_SHIFT_MASK))) { destroy(); pass_out = FALSE; } break; } /* pass the key to external player. */ if (pass_out) { char str[2]; // let entry widget handle plain text input. if (GTK_WIDGET_HAS_FOCUS(entry_search) || GTK_WIDGET_HAS_FOCUS(entry_player) || GTK_WIDGET_HAS_FOCUS(entry_url) || GTK_WIDGET_HAS_FOCUS(entry_url_header) || GTK_WIDGET_HAS_FOCUS(entry_record_dir) || GTK_WIDGET_HAS_FOCUS(entry_inport) || GTK_WIDGET_HAS_FOCUS(entry_outport) || GTK_WIDGET_HAS_FOCUS(GTK_BIN(combo_box_entry)->child)){ return FALSE; } sprintf(str, "%c", event->keyval); write(stdin_pipe[1], str, 1); } return TRUE; }