diff options
author | Mathias Hasselmann <hasselmm@src.gnome.org> | 2007-11-24 14:24:15 +0000 |
---|---|---|
committer | Mathias Hasselmann <hasselmm@src.gnome.org> | 2007-11-24 14:24:15 +0000 |
commit | 575060573155b677da0cb6e5a3986713fe4b3654 (patch) | |
tree | b9bc1527300e2ca976450a7d09ddaa35a895a3f0 | |
parent | 57c1ad5c4308cb2f23b6832d1f0f490a5e2a4d73 (diff) | |
download | totem-575060573155b677da0cb6e5a3986713fe4b3654.tar.gz |
Implement browsing of nearby Totem instances and playlist download.
* data/publish-plugin.glade, data/publish-plugin.ui: Add
"neighbours" sidepane for finding nearby Totem instances.
* src/plugins/publish/totem-publish.c: Implement "neighbours"
sidepane and playlist download.
svn path=/branches/LIBEPC/; revision=4898
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | data/publish-plugin.glade | 46 | ||||
-rw-r--r-- | data/publish-plugin.ui | 46 | ||||
-rw-r--r-- | src/plugins/publish/totem-publish.c | 308 |
4 files changed, 360 insertions, 49 deletions
@@ -1,5 +1,14 @@ 2007-11-24 Mathias Hasselmann <mathias@openismus.com> + Implement browsing of nearby Totem instances and playlist download. + + * data/publish-plugin.glade, data/publish-plugin.ui: Add + "neighbours" sidepane for finding nearby Totem instances. + * src/plugins/publish/totem-publish.c: Implement "neighbours" + sidepane and playlist download. + +2007-11-24 Mathias Hasselmann <mathias@openismus.com> + Add service monitor for finding other Totem instances. * src/plugins/publish/totem-publish.c: Add service monitor. diff --git a/data/publish-plugin.glade b/data/publish-plugin.glade index 5dc457120..cd7d73003 100644 --- a/data/publish-plugin.glade +++ b/data/publish-plugin.glade @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> -<!--Generated with glade3 3.4.0 on Wed Nov 21 17:33:38 2007 --> +<!--Generated with glade3 3.4.0 on Sat Nov 24 11:44:42 2007 --> <glade-interface> <widget class="GtkDialog" id="publish-settings-dialog"> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -19,7 +19,7 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="spacing">2</property> <child> - <widget class="GtkVBox" id="vbox1"> + <widget class="GtkVBox" id="publish-settings-vbox"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">6</property> @@ -119,4 +119,46 @@ and <b>%h</b> will be replaced by your computer's host name.</sma </widget> </child> </widget> + <widget class="GtkWindow" id="neighbours-window"> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkVBox" id="neighbours-page"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="spacing">6</property> + <child> + <widget class="GtkScrolledWindow" id="neighbours-list-scroller"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTreeView" id="neighbours-list"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">True</property> + <signal name="row_activated" handler="totem_publish_plugin_neighbours_list_row_activated_cb"/> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkProgressBar" id="neighbours-scanning"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="activity_mode">True</property> + <property name="text" translatable="yes"></property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> </glade-interface> diff --git a/data/publish-plugin.ui b/data/publish-plugin.ui index 89a3cdea0..a53953eb3 100644 --- a/data/publish-plugin.ui +++ b/data/publish-plugin.ui @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<!--Generated with glade3 3.4.0 on Wed Nov 21 17:33:38 2007 --> +<!--Generated with glade3 3.4.0 on Sat Nov 24 11:44:42 2007 --> <interface> <object class="GtkDialog" id="publish-settings-dialog"> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> @@ -18,7 +18,7 @@ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="spacing">2</property> <child> - <object class="GtkVBox" id="vbox1"> + <object class="GtkVBox" id="publish-settings-vbox"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">6</property> @@ -116,8 +116,48 @@ and <b>%h</b> will be replaced by your computer's host name.</sma </object> </child> <action-widgets> - <action-widget response="0">encryption-button</action-widget> <action-widget response="-1">close-button</action-widget> </action-widgets> </object> + <object class="GtkWindow" id="neighbours-window"> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <object class="GtkVBox" id="neighbours-page"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="spacing">6</property> + <child> + <object class="GtkScrolledWindow" id="neighbours-list-scroller"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <object class="GtkTreeView" id="neighbours-list"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="headers_visible">False</property> + <property name="headers_clickable">True</property> + <signal handler="totem_publish_plugin_neighbours_list_row_activated_cb" name="row_activated"/> + </object> + </child> + </object> + </child> + <child> + <object class="GtkProgressBar" id="neighbours-scanning"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="activity_mode">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + </object> </interface> diff --git a/src/plugins/publish/totem-publish.c b/src/plugins/publish/totem-publish.c index 075777396..4e4153daf 100644 --- a/src/plugins/publish/totem-publish.c +++ b/src/plugins/publish/totem-publish.c @@ -33,9 +33,12 @@ #include <glib-object.h> #include <glib/gi18n-lib.h> +#include <libepc/consumer.h> +#include <libepc/enums.h> #include <libepc/publisher.h> #include <libepc/service-monitor.h> +#include "ev-sidebar.h" #include "totem-plugin.h" #include "totem-interface.h" #include "totem-private.h" @@ -52,21 +55,36 @@ #define TOTEM_PUBLISH_CONFIG_NAME GCONF_PREFIX "/plugins/publish/name" #define TOTEM_PUBLISH_CONFIG_PROTOCOL GCONF_PREFIX "/plugins/publish/protocol" +enum +{ + NAME_COLUMN, + TYPE_COLUMN, + HOST_COLUMN, + PORT_COLUMN, + + COLUMN_COUNT +}; + typedef struct { TotemPlugin parent; TotemObject *totem; + GtkWidget *settings; + GtkWidget *scanning; + GtkBuilder *ui; + EpcPublisher *publisher; EpcServiceMonitor *monitor; - GtkWidget *dialog; + GtkListStore *neighbours; guint name_id; guint protocol_id; - guint item_added_id; - guint item_removed_id; - guint service_found_id; - guint service_removed_id; + + guint scanning_id; + + gulong item_added_id; + gulong item_removed_id; } TotemPublishPlugin; typedef struct @@ -74,16 +92,20 @@ typedef struct TotemPluginClass parent_class; } TotemPublishPluginClass; -G_MODULE_EXPORT GType register_totem_plugin (GTypeModule *module); -static GType totem_publish_plugin_get_type (void); - -void totem_publish_plugin_service_name_entry_changed_cb (GtkEntry *entry, - gpointer data); -void totem_publish_plugin_encryption_button_toggled_cb (GtkToggleButton *button, - gpointer data); -void totem_publish_plugin_dialog_response_cb (GtkDialog *dialog, - gint response, - gpointer data); +G_MODULE_EXPORT GType register_totem_plugin (GTypeModule *module); +static GType totem_publish_plugin_get_type (void); + +void totem_publish_plugin_service_name_entry_changed_cb (GtkEntry *entry, + gpointer data); +void totem_publish_plugin_encryption_button_toggled_cb (GtkToggleButton *button, + gpointer data); +void totem_publish_plugin_dialog_response_cb (GtkDialog *dialog, + gint response, + gpointer data); +void totem_publish_plugin_neighbours_list_row_activated_cb (GtkTreeView *view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data); TOTEM_PLUGIN_REGISTER(TotemPublishPlugin, totem_publish_plugin) @@ -263,8 +285,16 @@ totem_publish_plugin_service_found_cb (EpcServiceMonitor *monitor, guint port, gpointer data) { - TotemPublishPlugin *self G_GNUC_UNUSED = TOTEM_PUBLISH_PLUGIN (data); - g_print ("+ %s (%s, %s:%d)\n", name, type, host, port); + TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data); + EpcProtocol protocol = epc_service_type_get_protocol (type); + GtkTreeIter iter; + + gtk_list_store_append (self->neighbours, &iter); + gtk_list_store_set (self->neighbours, &iter, NAME_COLUMN, name, + TYPE_COLUMN, protocol, + HOST_COLUMN, host, + PORT_COLUMN, port, + -1); } static void @@ -273,8 +303,180 @@ totem_publish_plugin_service_removed_cb (EpcServiceMonitor *monitor, const gchar *name, gpointer data) { - TotemPublishPlugin *self G_GNUC_UNUSED = TOTEM_PUBLISH_PLUGIN (data); - g_print ("- %s (%s)\n", name, type); + TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data); + GtkTreeModel *model = GTK_TREE_MODEL (self->neighbours); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter_first (model, &iter)) { + GSList *path_list = NULL, *path_iter; + GtkTreePath *path; + gchar *stored; + + do { + gtk_tree_model_get (model, &iter, NAME_COLUMN, &stored, -1); + + if (g_str_equal (stored, name)) { + path = gtk_tree_model_get_path (model, &iter); + path_list = g_slist_prepend (path_list, path); + } + } while (gtk_tree_model_iter_next (model, &iter)); + + for (path_iter = path_list; path_iter; path_iter = path_iter->next) { + path = path_iter->data; + + if (gtk_tree_model_get_iter (model, &iter, path)) + gtk_list_store_remove (self->neighbours, &iter); + + gtk_tree_path_free (path); + } + + g_slist_free (path_list); + } +} + +static void +totem_publish_plugin_scanning_done_cb (EpcServiceMonitor *monitor, + const gchar *type, + gpointer data) +{ + TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data); + + if (self->scanning_id) { + g_source_remove (self->scanning_id); + self->scanning_id = 0; + } + + if (self->scanning) + gtk_widget_hide (self->scanning); +} + +static void +totem_publish_plugin_load_playlist (TotemPublishPlugin *self, + EpcProtocol protocol, + const gchar *host, + guint port) +{ + EpcConsumer *consumer = epc_consumer_new (protocol, host, port); + GKeyFile *keyfile = g_key_file_new (); + gchar *contents = NULL; + GError *error = NULL; + gsize length = 0; + + contents = epc_consumer_lookup (consumer, "playlist.pls", &length, &error); + + if (contents && g_key_file_load_from_data (keyfile, contents, length, G_KEY_FILE_NONE, &error)) { + TotemPlaylist *playlist = totem_get_playlist (self->totem); + gint i, n_entries; + + /* returns zero in case of errors */ + n_entries = g_key_file_get_integer (keyfile, "playlist", "NumberOfEntries", &error); + + if (error) + goto out; + + totem_playlist_clear (playlist); + + for (i = 1; i <= n_entries; ++i) { + gchar *key, *mrl, *title; + + key = g_strdup_printf ("File%d", i); + mrl = g_key_file_get_string (keyfile, "playlist", key, NULL); + g_free (key); + + key = g_strdup_printf ("Title%d", i); + title = g_key_file_get_string (keyfile, "playlist", key, NULL); + g_free (key); + + if (mrl) + totem_playlist_add_mrl (playlist, mrl, title); + + g_free (title); + g_free (mrl); + } + + ev_sidebar_set_current_page (EV_SIDEBAR (self->totem->sidebar), "playlist"); + } + +out: + if (error) { + g_warning ("Cannot load playlist: %s", error->message); + g_error_free (error); + } + + g_key_file_free (keyfile); + g_free (contents); + + g_object_unref (consumer); +} + +void +totem_publish_plugin_neighbours_list_row_activated_cb (GtkTreeView *view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (data); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->neighbours), &iter, path)) { + EpcProtocol protocol; + gchar *host; + guint port; + + gtk_tree_model_get (GTK_TREE_MODEL (self->neighbours), &iter, + TYPE_COLUMN, &protocol, HOST_COLUMN, &host, + PORT_COLUMN, &port, -1); + + totem_publish_plugin_load_playlist (self, protocol, host, port); + + g_free (host); + } +} + +static gboolean +totem_publish_plugin_scanning_cb (gpointer data) +{ + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (data)); + return TRUE; +} + +static GtkWidget* +totem_publish_plugin_create_neigbours_page (TotemPublishPlugin *self) +{ + GtkWidget *page, *list; + + page = GTK_WIDGET (gtk_builder_get_object (self->ui, "neighbours-page")); + list = GTK_WIDGET (gtk_builder_get_object (self->ui, "neighbours-list")); + + self->scanning = GTK_WIDGET (gtk_builder_get_object (self->ui, "neighbours-scanning")); + self->scanning_id = g_timeout_add (100, totem_publish_plugin_scanning_cb, self->scanning); + + g_signal_connect (self->monitor, "service-found", + G_CALLBACK (totem_publish_plugin_service_found_cb), + self); + g_signal_connect (self->monitor, "service-removed", + G_CALLBACK (totem_publish_plugin_service_removed_cb), + self); + g_signal_connect (self->monitor, "scanning-done", + G_CALLBACK (totem_publish_plugin_scanning_done_cb), + self); + + self->neighbours = gtk_list_store_new (COLUMN_COUNT, + G_TYPE_STRING, EPC_TYPE_PROTOCOL, + G_TYPE_STRING, G_TYPE_UINT); + + gtk_tree_view_set_model (GTK_TREE_VIEW (list), + GTK_TREE_MODEL (self->neighbours)); + + gtk_tree_view_append_column (GTK_TREE_VIEW (list), + gtk_tree_view_column_new_with_attributes ( + NULL, gtk_cell_renderer_text_new (), + "text", NAME_COLUMN, NULL)); + + g_object_ref (page); + gtk_widget_unparent (page); + + return page; } static gboolean @@ -293,6 +495,7 @@ totem_publish_plugin_activate (TotemPlugin *plugin, g_return_val_if_fail (NULL == self->totem, FALSE); self->totem = g_object_ref (totem); + self->ui = totem_interface_load ("publish-plugin.ui", TRUE, NULL, self); gconf_client_add_dir (self->totem->gc, TOTEM_PUBLISH_CONFIG_ROOT, @@ -316,10 +519,8 @@ totem_publish_plugin_activate (TotemPlugin *plugin, self->monitor = epc_service_monitor_new ("totem", NULL, EPC_PROTOCOL_UNKNOWN); - self->service_found_id = g_signal_connect (self->monitor, "service-found", - G_CALLBACK (totem_publish_plugin_service_found_cb), self); - self->service_removed_id = g_signal_connect (self->monitor, "service-removed", - G_CALLBACK (totem_publish_plugin_service_removed_cb), self); + ev_sidebar_add_page (EV_SIDEBAR (self->totem->sidebar), "neighbours", _("Neighbours"), + totem_publish_plugin_create_neigbours_page (self)); self->publisher = epc_publisher_new (service_name, "totem", NULL); epc_publisher_set_protocol (self->publisher, protocol); @@ -327,7 +528,6 @@ totem_publish_plugin_activate (TotemPlugin *plugin, g_free (protocol_name); g_free (service_name); - epc_publisher_add_handler (self->publisher, "playlist.pls", totem_publish_plugin_playlist_cb, self, NULL); @@ -347,6 +547,25 @@ totem_publish_plugin_deactivate (TotemPlugin *plugin, TotemObject *totem) { TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (plugin); + TotemPlaylist *playlist = NULL; + + if (self->totem) + playlist = totem_get_playlist (self->totem); + + if (self->scanning_id) { + g_source_remove (self->scanning_id); + self->scanning_id = 0; + } + + if (playlist && self->item_added_id) { + g_signal_handler_disconnect (playlist, self->item_added_id); + self->item_added_id = 0; + } + + if (playlist && self->item_removed_id) { + g_signal_handler_disconnect (playlist, self->item_removed_id); + self->item_removed_id = 0; + } if (self->monitor) { g_object_unref (self->monitor); @@ -363,18 +582,24 @@ totem_publish_plugin_deactivate (TotemPlugin *plugin, gconf_client_notify_remove (self->totem->gc, self->name_id); gconf_client_notify_remove (self->totem->gc, self->protocol_id); gconf_client_remove_dir (self->totem->gc, TOTEM_PUBLISH_CONFIG_ROOT, NULL); + + ev_sidebar_remove_page (EV_SIDEBAR (self->totem->sidebar), "neighbours"); + g_object_unref (self->totem); self->totem = NULL; } - if (self->item_added_id) - self->item_added_id = (g_source_remove (self->item_added_id), 0); - if (self->item_removed_id) - self->item_removed_id = (g_source_remove (self->item_removed_id), 0); - if (self->service_found_id) - self->service_found_id = (g_source_remove (self->service_found_id), 0); - if (self->service_removed_id) - self->service_removed_id = (g_source_remove (self->service_removed_id), 0); + if (self->settings) { + gtk_widget_destroy (self->settings); + self->settings = NULL; + } + + if (self->ui) { + g_object_unref (self->ui); + self->ui = NULL; + } + + self->scanning = NULL; } void @@ -386,7 +611,7 @@ totem_publish_plugin_dialog_response_cb (GtkDialog *dialog, if (response) { gtk_widget_destroy (GTK_WIDGET (dialog)); - self->dialog = NULL; + self->settings = NULL; } } @@ -394,30 +619,25 @@ static GtkWidget* totem_publish_plugin_create_configure_dialog (TotemPlugin *plugin) { TotemPublishPlugin *self = TOTEM_PUBLISH_PLUGIN (plugin); - GtkBuilder *builder; - g_return_val_if_fail (NULL == self->dialog, NULL); + g_return_val_if_fail (NULL == self->settings, NULL); - builder = totem_interface_load ("publish-plugin.ui", TRUE, NULL, self); - - if (builder) { + if (self->ui) { const gchar *service_name = epc_publisher_get_service_name (self->publisher); EpcProtocol protocol = epc_publisher_get_protocol (self->publisher); GtkWidget *widget; - self->dialog = g_object_ref (gtk_builder_get_object (builder, "publish-settings-dialog")); + self->settings = g_object_ref (gtk_builder_get_object (self->ui, "publish-settings-dialog")); - widget = GTK_WIDGET (gtk_builder_get_object (builder, "service-name-entry")); + widget = GTK_WIDGET (gtk_builder_get_object (self->ui, "service-name-entry")); gtk_entry_set_text (GTK_ENTRY (widget), service_name); - widget = GTK_WIDGET (gtk_builder_get_object (builder, "encryption-button")); + widget = GTK_WIDGET (gtk_builder_get_object (self->ui, "encryption-button")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), EPC_PROTOCOL_HTTPS == protocol); - - g_object_unref (builder); } - return self->dialog; + return self->settings; } static void |