diff options
Diffstat (limited to 'src/universal-cp/devicetreeview.c')
-rw-r--r-- | src/universal-cp/devicetreeview.c | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/src/universal-cp/devicetreeview.c b/src/universal-cp/devicetreeview.c new file mode 100644 index 0000000..fa23acf --- /dev/null +++ b/src/universal-cp/devicetreeview.c @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2007 Zeeshan Ali <zeenix@gstreamer.net> + * + * Authors: Zeeshan Ali <zeenix@gstreamer.net> + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <string.h> +#include <stdlib.h> +#include <config.h> + +#include "gui.h" +#include "icons.h" +#include "actiondialog.h" +#include "main.h" + +static const char *default_details[] = { + "Software", "GUPnP Universal Control Point", + "Version", VERSION, + "Author", "Zeeshan Ali <zeenix@gstreamer.net>", NULL}; + +static GtkWidget *treeview; +static GtkWidget *popup; +static GtkWidget *subscribe_menuitem; +static GtkWidget *action_menuitem; +static GtkWidget *separator; + +static gboolean expanded; + +gboolean +find_device (GtkTreeModel *model, + const char *udn, + GtkTreeIter *root_iter, + GtkTreeIter *iter) +{ + gboolean found = FALSE; + gboolean more = TRUE; + + if (udn == NULL || root_iter == NULL) + return found; + + more = gtk_tree_model_iter_children (model, iter, root_iter); + + while (more) { + GUPnPDeviceInfo *info; + GtkTreeIter tmp; + guint icon_type; + + gtk_tree_model_get (model, iter, + 2, &info, + 5, &icon_type, -1); + + if (info) { + if (icon_type == ICON_DEVICE) { + const char *device_udn; + + device_udn = gupnp_device_info_get_udn (info); + + if (device_udn && + strcmp (device_udn, udn) == 0) + found = TRUE; + } + + g_object_unref (info); + } + + if (found) + break; + + /* recurse into embedded-devices */ + found = find_device (model, udn, iter, &tmp); + if (found) { + *iter = tmp; + break; + } + + more = gtk_tree_model_iter_next (model, iter); + } + + return found; +} + +GUPnPDeviceInfo * +get_service_device (GUPnPServiceInfo *service_info) +{ + GUPnPDeviceInfo *info = NULL; + GtkTreeModel *model; + GtkTreeIter root_iter; + GtkTreeIter iter; + const char *udn; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + + if (!gtk_tree_model_get_iter_first (model, &root_iter)) + return NULL; + + udn = gupnp_service_info_get_udn (service_info); + if (find_device (model, udn, &root_iter, &iter)) + gtk_tree_model_get (model, &iter, 2, &info, -1); + + return info; +} + +static void +setup_device_popup (GtkWidget *popup) +{ + GUPnPServiceProxy *proxy; + + /* See if a service is selected */ + proxy = get_selected_service (); + if (proxy != NULL) { + g_object_set (subscribe_menuitem, + "visible", + TRUE, + "active", + gupnp_service_proxy_get_subscribed (proxy), + NULL); + + g_object_set (action_menuitem, + "visible", + FALSE, + NULL); + } else { + GUPnPServiceActionInfo *action; + + g_object_set (subscribe_menuitem, + "visible", + FALSE, + NULL); + + /* See if an action is selected */ + action = get_selected_action (NULL, NULL); + g_object_set (action_menuitem, + "visible", + action != NULL, + NULL); + } + + /* Separator should be visible only if either service or action row is + * selected + */ + g_object_set (separator, + "visible", + (proxy != NULL), + NULL); + if (proxy) + g_object_unref (proxy); +} + +gboolean +on_device_treeview_button_release (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + if (event->type != GDK_BUTTON_RELEASE || + event->button != 3) + return FALSE; + + setup_device_popup (popup); + + gtk_menu_popup (GTK_MENU (popup), + NULL, + NULL, + NULL, + NULL, + event->button, + event->time); + return TRUE; +} + +void +on_device_treeview_row_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GUPnPServiceProxy *proxy; + GUPnPServiceIntrospection *introspection; + + /* See if a service is selected */ + proxy = get_selected_service (); + if (proxy != NULL) { + gboolean subscribed; + + subscribed = gupnp_service_proxy_get_subscribed (proxy); + gupnp_service_proxy_set_subscribed (proxy, !subscribed); + } else { + GUPnPServiceActionInfo *action_info; + + /* See if an action is selected */ + action_info = get_selected_action (&proxy, &introspection); + if (action_info != NULL) { + run_action_dialog (action_info, + proxy, + introspection); + g_object_unref (introspection); + } + } + + if (proxy != NULL) + g_object_unref (proxy); +} + +static void +on_something_selected (GtkTreeSelection *selection, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + guint icon_type; + + gtk_tree_model_get (model, &iter, 5, &icon_type, -1); + + /* We recognise things by how they look, don't we? */ + if (icon_type == ICON_DEVICE) { + GUPnPDeviceInfo *info; + + gtk_tree_model_get (model, &iter, 2, &info, -1); + show_device_details (info); + g_object_unref (info); + } else if (icon_type == ICON_SERVICE) { + GUPnPServiceInfo *info; + + gtk_tree_model_get (model, &iter, 2, &info, -1); + show_service_details (info); + g_object_unref (info); + } else if (icon_type == ICON_VARIABLE) { + GUPnPServiceStateVariableInfo *info; + + gtk_tree_model_get (model, &iter, 4, &info, -1); + show_state_variable_details (info); + } else if (icon_type == ICON_ACTION) { + GUPnPServiceActionInfo *info; + + gtk_tree_model_get (model, &iter, 4, &info, -1); + show_action_details (info); + } else if (icon_type == ICON_ACTION_ARG_IN || + icon_type == ICON_ACTION_ARG_OUT) { + GUPnPServiceActionArgInfo *info; + + gtk_tree_model_get (model, &iter, 4, &info, -1); + show_action_arg_details (info); + } else + update_details (default_details); + } + + else + update_details (default_details); +} + +void +remove_device (GUPnPDeviceInfo *info) +{ + GtkTreeModel *model; + GtkTreeIter root_iter; + GtkTreeIter iter; + const char *udn; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + + udn = gupnp_device_info_get_udn (info); + + if (!gtk_tree_model_get_iter_first (model, &root_iter)) + return; + + if (find_device (model, udn, &root_iter, &iter)) { + unschedule_icon_update (info); + gtk_tree_store_remove (GTK_TREE_STORE (model), &iter); + } +} + +static void +append_action_arguments (GList *arguments, + GtkTreeStore *store, + GtkTreeIter *action_iter) +{ + const GList *iter; + + for (iter = arguments; iter; iter = iter->next) { + GUPnPServiceActionArgInfo *info; + IconID icon_id; + + info = iter->data; + + if (info->direction == GUPNP_SERVICE_ACTION_ARG_DIRECTION_IN) { + icon_id = ICON_ACTION_ARG_IN; + } else { + icon_id = ICON_ACTION_ARG_OUT; + } + + gtk_tree_store_insert_with_values + (store, + NULL, action_iter, -1, + 0, get_icon_by_id (icon_id), + 1, info->name, + 4, info, + 5, icon_id, + -1); + } +} + +static void +append_actions (GUPnPServiceProxy *proxy, + GUPnPServiceIntrospection *introspection, + const GList *actions, + GtkTreeStore *store, + GtkTreeIter *service_iter) +{ + const GList *iter; + + for (iter = actions; iter; iter = iter->next) { + GUPnPServiceActionInfo *info; + GtkTreeIter action_iter; + + info = iter->data; + + gtk_tree_store_insert_with_values + (store, &action_iter, + service_iter, -1, + 0, get_icon_by_id (ICON_ACTION), + 1, info->name, + 2, proxy, + 3, introspection, + 4, info, + 5, ICON_ACTION, + -1); + append_action_arguments (info->arguments, + store, + &action_iter); + } +} + +static void +append_state_variables (GUPnPServiceProxy *proxy, + const GList *variables, + GtkTreeStore *store, + GtkTreeIter *service_iter) +{ + const GList *iter; + GtkTreeIter variables_iter; + + gtk_tree_store_insert_with_values (store, + &variables_iter, + service_iter, -1, + 0, get_icon_by_id (ICON_VARIABLES), + 1, "State variables", + 5, ICON_VARIABLES, + -1); + + for (iter = variables; iter; iter = iter->next) { + GUPnPServiceStateVariableInfo *info; + + info = iter->data; + + gtk_tree_store_insert_with_values + (store, + NULL, &variables_iter, -1, + 0, get_icon_by_id (ICON_VARIABLE), + 1, info->name, + 2, proxy, + 4, info, + 5, ICON_VARIABLE, + -1); + + /* Set-up event notitications for variable */ + gupnp_service_proxy_add_notify (proxy, + info->name, + info->type, + on_state_variable_changed, + NULL); + } +} + +static void +append_introspection (GUPnPServiceProxy *proxy, + GUPnPServiceIntrospection *introspection, + GtkTreeStore *store, + GtkTreeIter *service_iter) +{ + const GList *list; + + if (introspection == NULL) { + gtk_tree_store_insert_with_values (store, + NULL, service_iter, -1, + 0, get_icon_by_id (ICON_MISSING), + 1, "Information not available", + 5, ICON_MISSING, + -1); + + return; + } + + list = gupnp_service_introspection_list_state_variables ( + introspection); + if (list) + append_state_variables (proxy, + list, + store, + service_iter); + + list = gupnp_service_introspection_list_actions (introspection); + if (list) + append_actions (proxy, + introspection, + list, + store, + service_iter); +} + +static void +got_introspection (GUPnPServiceInfo *info, + GUPnPServiceIntrospection *introspection, + const GError *error, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter *service_iter; + + service_iter = (GtkTreeIter *) user_data; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + + append_introspection (GUPNP_SERVICE_PROXY (info), + introspection, + GTK_TREE_STORE (model), + service_iter); + + g_slice_free (GtkTreeIter, service_iter); + if (introspection != NULL) { + g_object_unref (introspection); + + /* Services are subscribed to by default */ + gupnp_service_proxy_set_subscribed (GUPNP_SERVICE_PROXY (info), TRUE); + } +} + +static void +append_service_tree (GUPnPServiceInfo *info, + GtkTreeStore *store, + GtkTreeIter *device_iter) +{ + char *id; + + id = gupnp_service_info_get_id (info); + if (id) { + GtkTreeIter *service_iter; + + service_iter = g_slice_new (GtkTreeIter); + gtk_tree_store_insert_with_values + (store, + service_iter, + device_iter, -1, + 0, get_icon_by_id (ICON_SERVICE), + 1, id, + 2, info, + 5, ICON_SERVICE, + -1); + + gupnp_service_info_get_introspection_async (info, + got_introspection, + service_iter); + + g_free (id); + } +} + +static void +append_device_tree (GUPnPDeviceInfo *info, + GtkTreeModel *model, + GtkTreeIter *parent_iter) +{ + char *friendly_name; + + friendly_name = gupnp_device_info_get_friendly_name (info); + if (friendly_name) { + GtkTreeIter device_iter; + GList *child; + + gtk_tree_store_insert_with_values + (GTK_TREE_STORE (model), + &device_iter, parent_iter, -1, + 0, get_icon_by_id (ICON_DEVICE), + 1, friendly_name, + 2, info, + 5, ICON_DEVICE, + -1); + g_free (friendly_name); + + schedule_icon_update (info); + + /* Append the embedded devices */ + child = gupnp_device_info_list_devices (info); + while (child) { + append_device_tree (GUPNP_DEVICE_INFO (child->data), + model, + &device_iter); + g_object_unref (child->data); + child = g_list_delete_link (child, child); + } + + /* Append the services */ + child = gupnp_device_info_list_services (info); + while (child) { + append_service_tree (GUPNP_SERVICE_INFO (child->data), + GTK_TREE_STORE (model), + &device_iter); + g_object_unref (child->data); + child = g_list_delete_link (child, child); + } + } +} + +void +append_device (GUPnPDeviceInfo *info) +{ + GtkTreeModel *model; + GtkTreeIter root_iter; + GtkTreeIter iter; + const char *udn; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + + udn = gupnp_device_info_get_udn (info); + + if (!gtk_tree_model_get_iter_first (model, &root_iter)) + return; + + if (!find_device (model, udn, &root_iter, &iter)) { + + append_device_tree (info, model, &root_iter); + + if (!expanded) { + GtkTreePath *first_row; + + first_row = gtk_tree_model_get_path ( + model, + &root_iter); + expanded = gtk_tree_view_expand_row ( + GTK_TREE_VIEW (treeview), + first_row, + FALSE); + } + } +} + +void +update_device_icon (GUPnPDeviceInfo *info, + GdkPixbuf *icon) +{ + GtkTreeModel *model; + GtkTreeIter root_iter; + GtkTreeIter device_iter; + const char *udn; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + + udn = gupnp_device_info_get_udn (info); + + if (!gtk_tree_model_get_iter_first (model, &root_iter)) + return; + + if (find_device (model, udn, &root_iter, &device_iter)) { + gtk_tree_store_set (GTK_TREE_STORE (model), + &device_iter, + 0, icon, + -1); + } else { + g_object_unref (icon); + } +} + +static GtkTreeModel * +create_device_treemodel (void) +{ + GtkTreeStore *store; + + store = gtk_tree_store_new (6, + GDK_TYPE_PIXBUF, /* Icon */ + G_TYPE_STRING, /* Name */ + G_TYPE_OBJECT, /* Device/Service Info */ + G_TYPE_OBJECT, /* Introspection */ + G_TYPE_POINTER, /* non-object */ + G_TYPE_UINT); /* icon type */ + + gtk_tree_store_insert_with_values (store, NULL, NULL, 0, + 0, get_icon_by_id (ICON_NETWORK), + 1, "UPnP Network", + 5, ICON_NETWORK, + -1); + return GTK_TREE_MODEL (store); +} + +void +setup_device_treeview (GladeXML *glade_xml) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + char *headers[] = { "Device", NULL}; + + treeview = glade_xml_get_widget (glade_xml, "device-treeview"); + g_assert (treeview != NULL); + popup = glade_xml_get_widget (glade_xml, "device-popup"); + g_assert (popup != NULL); + + g_object_weak_ref (G_OBJECT (treeview), + (GWeakNotify) gtk_widget_destroy, + popup); + subscribe_menuitem = glade_xml_get_widget (glade_xml, + "subscribe-to-events"); + g_assert (subscribe_menuitem != NULL); + action_menuitem = glade_xml_get_widget (glade_xml, "invoke-action"); + g_assert (action_menuitem != NULL); + separator = glade_xml_get_widget (glade_xml, "device-popup-separator"); + g_assert (separator != NULL); + + model = create_device_treemodel (); + g_assert (model != NULL); + + setup_treeview (treeview, model, headers, 1); + g_object_unref (model); + + column = gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), 0); + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_add_attribute (column, + renderer, + "pixbuf", 0); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + g_assert (selection != NULL); + g_signal_connect (selection, + "changed", + G_CALLBACK (on_something_selected), + NULL); + + expanded = FALSE; +} + +GUPnPServiceProxy * +get_selected_service (void) +{ + GUPnPServiceProxy *proxy = NULL; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + g_assert (selection != NULL); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + guint icon_type; + + gtk_tree_model_get (model, &iter, + 2, &proxy, + 5, &icon_type, -1); + + if (icon_type != ICON_SERVICE) + proxy = NULL; + } + + return proxy; +} + +GUPnPServiceActionInfo * +get_selected_action (GUPnPServiceProxy **ret_proxy, + GUPnPServiceIntrospection **ret_introspection) +{ + GUPnPServiceProxy *proxy; + GUPnPServiceIntrospection *introspection; + GUPnPServiceActionInfo *action_info = NULL; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview)); + g_assert (model != NULL); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)); + g_assert (selection != NULL); + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + guint icon_type; + + gtk_tree_model_get (model, &iter, + 2, &proxy, + 3, &introspection, + 4, &action_info, + 5, &icon_type, + -1); + + if (icon_type != ICON_ACTION) { + if (proxy != NULL) { + g_object_unref (proxy); + proxy = NULL; + } + + if (introspection != NULL) { + g_object_unref (introspection); + introspection = NULL; + } + + action_info = NULL; + } + + if (ret_proxy) + *ret_proxy = proxy; + else if (proxy != NULL) + g_object_unref (proxy); + + if (ret_introspection) + *ret_introspection = introspection; + else if (introspection != NULL) + g_object_unref (introspection); + } + + return action_info; +} + +void +on_expand_devices_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + gtk_tree_view_expand_all (GTK_TREE_VIEW (treeview)); +} + +void +on_collapse_devices_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + gtk_tree_view_collapse_all (GTK_TREE_VIEW (treeview)); +} + |