[gupnp-tools: 18/18] av-cp: Show DIDL in search dialog



commit 16b0fb1a88f97aaa917e55f6cebd540fb3bcc961
Author: Jens Georg <mail jensge org>
Date:   Mon Sep 5 22:27:52 2016 +0200

    av-cp: Show DIDL in search dialog

 configure.ac                                       |    2 +-
 data/Makefile.am                                   |    4 +-
 ...{didl-lite-dialog.ui => didl-lite-dialog.ui.in} |    2 +-
 data/{gupnp-av-cp.ui.in => gupnp-av-cp.ui}         |   70 -------
 data/search-dialog.ui                              |   20 ++-
 po/POTFILES.in                                     |    4 +-
 src/av-cp/playlist-treeview.c                      |    1 +
 src/av-cp/search-dialog.c                          |  190 +++++++++++++++++++-
 src/av-cp/server-device.c                          |    8 +-
 9 files changed, 219 insertions(+), 82 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index c863aaf..f67bd08 100644
--- a/configure.ac
+++ b/configure.ac
@@ -138,7 +138,7 @@ src/network-light/Makefile
 src/universal-cp/Makefile
 src/upload/Makefile
 data/Makefile
-data/gupnp-av-cp.ui
+data/didl-lite-dialog.ui
 data/pixmaps/Makefile
 data/xml/Makefile
 po/Makefile.in
diff --git a/data/Makefile.am b/data/Makefile.am
index dfe289d..53248a7 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -10,7 +10,7 @@ desktop_in_files = gupnp-universal-cp.desktop.in \
                   gupnp-network-light.desktop.in
 
 if BUILD_AV
-dist_shared_DATA += gupnp-av-cp.ui search-dialog.ui
+dist_shared_DATA += gupnp-av-cp.ui search-dialog.ui didl-lite-dialog.ui
 desktop_in_files += gupnp-av-cp.desktop.in
 endif
 
@@ -25,7 +25,7 @@ desktop_in_in_files = $(desktop_in_files:.desktop.in=.desktop.in.in)
        $(AM_V_GEN) $(SED) -e 's|@VERSION[@]|$(VERSION)|g' \
                                        -e 's|@PKGDATADIR[@]|$(PKGDATADIR)|g' $< > $@
 
-EXTRA_DIST = $(desktop_in_in_files) gupnp-av-cp.ui.in $(desktop_in_files)
+EXTRA_DIST = $(desktop_in_in_files) didl-lite-dialog.ui.in $(desktop_in_files)
 
 CLEANFILES = $(desktop_DATA)
 
diff --git a/data/didl-lite-dialog.ui b/data/didl-lite-dialog.ui.in
similarity index 97%
rename from data/didl-lite-dialog.ui
rename to data/didl-lite-dialog.ui.in
index 3b50007..f17d643 100644
--- a/data/didl-lite-dialog.ui
+++ b/data/didl-lite-dialog.ui.in
@@ -50,7 +50,7 @@
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <child>
-              <object class="GtkSourceView" id="didl_textview">
+              <object class="@TEXT_VIEW@" id="didl_textview">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="editable">False</property>
diff --git a/data/gupnp-av-cp.ui.in b/data/gupnp-av-cp.ui
similarity index 92%
rename from data/gupnp-av-cp.ui.in
rename to data/gupnp-av-cp.ui
index 19a21c4..bfc90fb 100644
--- a/data/gupnp-av-cp.ui.in
+++ b/data/gupnp-av-cp.ui
@@ -722,76 +722,6 @@ Vinicius Depizzol &lt;vdepizzol gmail com&gt;</property>
       <placeholder/>
     </child>
   </object>
-  <object class="GtkDialog" id="didl-dialog">
-    <property name="width_request">640</property>
-    <property name="height_request">480</property>
-    <property name="can_focus">False</property>
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">GUPnP AV CP - Metadata View</property>
-    <property name="type_hint">normal</property>
-    <property name="transient_for">main-window</property>
-    <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
-    <child internal-child="vbox">
-      <object class="GtkBox" id="dialog-vbox3">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">6</property>
-        <child internal-child="action_area">
-          <object class="GtkButtonBox" id="didl-action-area">
-            <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="layout_style">end</property>
-            <child>
-              <object class="GtkButton" id="didl-close-button">
-                <property name="label">Close</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <signal name="clicked" handler="gtk_widget_hide" object="didl-dialog" swapped="yes"/>
-              </object>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <object class="GtkScrolledWindow" id="didl-scrolledwindow">
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <child>
-              <object class="@TEXT_VIEW@" id="didl-textview">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="editable">False</property>
-                <property name="wrap_mode">char</property>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-      </object>
-    </child>
-    <action-widgets>
-      <action-widget response="0">didl-close-button</action-widget>
-    </action-widgets>
-    <child>
-      <placeholder/>
-    </child>
-  </object>
   <object class="GtkSizeGroup" id="playback-button-sizegroup">
     <widgets>
       <widget name="play-button"/>
diff --git a/data/search-dialog.ui b/data/search-dialog.ui
index ae0bb87..5350726 100644
--- a/data/search-dialog.ui
+++ b/data/search-dialog.ui
@@ -2,6 +2,19 @@
 <!-- Generated with glade 3.20.0 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
+  <object class="GtkMenu" id="popup-menu">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkMenuItem">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label" translatable="yes">Show _DIDL…</property>
+        <property name="use_underline">True</property>
+        <signal name="activate" handler="search_dialog_on_didl_popup_activate" object="SearchDialog" 
swapped="yes"/>
+      </object>
+    </child>
+  </object>
   <object class="GtkListStore" id="search_dialog_liststore">
     <columns>
       <!-- column-name icon -->
@@ -10,6 +23,8 @@
       <column type="gchararray"/>
       <!-- column-name didl-lite-object -->
       <column type="GObject"/>
+      <!-- column-name id -->
+      <column type="gchararray"/>
     </columns>
   </object>
   <template class="SearchDialog" parent="GtkDialog">
@@ -24,7 +39,7 @@
       <object class="GtkBox">
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
+        <property name="spacing">6</property>
         <child internal-child="action_area">
           <object class="GtkButtonBox">
             <property name="can_focus">False</property>
@@ -78,11 +93,12 @@
                 <property name="can_focus">True</property>
                 <property name="shadow_type">in</property>
                 <child>
-                  <object class="GtkTreeView" id="search-dialog-treeview">
+                  <object class="GtkTreeView" id="search_dialog_treeview">
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="model">search_dialog_liststore</property>
                     <property name="headers_visible">False</property>
+                    <signal name="button-release-event" handler="search_dialog_on_listview_button_release" 
object="SearchDialog" swapped="no"/>
                     <child internal-child="selection">
                       <object class="GtkTreeSelection"/>
                     </child>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 120e1dd..b9c2d72 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,7 +1,9 @@
 # List of source files containing translatable strings.
 # Please keep this file sorted alphabetically.
 data/gupnp-av-cp.desktop.in.in
-data/gupnp-av-cp.ui.in
+data/didl-lite-dialog.ui
+data/search-dialog.ui
+data/gupnp-av-cp.ui
 data/gupnp-network-light.desktop.in.in
 data/gupnp-network-light.ui
 data/gupnp-universal-cp.desktop.in.in
diff --git a/src/av-cp/playlist-treeview.c b/src/av-cp/playlist-treeview.c
index e6fc956..22d8549 100644
--- a/src/av-cp/playlist-treeview.c
+++ b/src/av-cp/playlist-treeview.c
@@ -373,6 +373,7 @@ setup_playlist_treeview (GtkBuilder *builder)
 
         popup = GTK_WIDGET (gtk_builder_get_object (builder, "playlist-popup"));
         g_assert (popup != NULL);
+        gtk_menu_attach_to_widget (GTK_MENU (popup), treeview, NULL);
 
         didl_dialog = GTK_WIDGET (av_cp_didl_dialog_new ());
         expanded = FALSE;
diff --git a/src/av-cp/search-dialog.c b/src/av-cp/search-dialog.c
index fa8dab4..47ca78a 100644
--- a/src/av-cp/search-dialog.c
+++ b/src/av-cp/search-dialog.c
@@ -25,6 +25,7 @@
 
 #include "search-dialog.h"
 #include "server-device.h"
+#include "didl-dialog.h"
 #include "icons.h"
 
 /* DLNA recommends something between 10 and 30, let's just use 30
@@ -51,6 +52,7 @@ struct _SearchDialogClass {
 struct _SearchDialogPrivate {
         GtkListStore *search_dialog_liststore;
         GtkEntry *search_dialog_entry;
+        GtkTreeView *search_dialog_treeview;
         char *id;
         char *title;
         AVCPMediaServer *server;
@@ -58,6 +60,7 @@ struct _SearchDialogPrivate {
         SearchTask *task;
         GUPnPSearchCriteriaParser *parser;
         GRegex *position_re;
+        GtkWidget *popup_menu;
 };
 
 typedef struct _SearchDialogPrivate SearchDialogPrivate;
@@ -66,6 +69,14 @@ G_DEFINE_TYPE_WITH_PRIVATE (SearchDialog, search_dialog, GTK_TYPE_DIALOG)
 void
 search_dialog_on_search_activate (SearchDialog *self, GtkEntry *entry);
 
+gboolean
+search_dialog_on_listview_button_release (GtkWidget      *widget,
+                                          GdkEventButton *event,
+                                          gpointer        user_data);
+
+void
+search_dialog_on_didl_popup_activate (SearchDialog *self, gpointer user_data);
+
 static void
 search_dialog_finalize (GObject *object);
 
@@ -320,7 +331,7 @@ search_task_on_didl_object_available (GUPnPDIDLLiteParser *parser,
                                            -1,
                                            0, get_item_icon (object),
                                            1, gupnp_didl_lite_object_get_title (object),
-                                           2, g_object_ref (object),
+                                           3, gupnp_didl_lite_object_get_id (object),
                                            -1);
 }
 
@@ -338,6 +349,9 @@ search_dialog_class_init (SearchDialogClass *klass)
         gtk_widget_class_bind_template_child_private (widget_class,
                                                       SearchDialog,
                                                       search_dialog_entry);
+        gtk_widget_class_bind_template_child_private (widget_class,
+                                                      SearchDialog,
+                                                      search_dialog_treeview);
 
         object_class->finalize = search_dialog_finalize;
         object_class->dispose = search_dialog_dispose;
@@ -347,11 +361,16 @@ static void
 search_dialog_init (SearchDialog *self)
 {
         SearchDialogPrivate *priv = NULL;
+        GtkBuilder *builder = gtk_builder_new ();
 
         gtk_widget_init_template (GTK_WIDGET (self));
         priv = search_dialog_get_instance_private (self);
 
         priv->parser = gupnp_search_criteria_parser_new ();
+
+        gtk_builder_add_from_resource (builder, DIALOG_RESOURCE_PATH, NULL);
+        gtk_builder_connect_signals (builder, self);
+        priv->popup_menu = GTK_WIDGET (gtk_builder_get_object (builder, "popup-menu"));
 }
 
 static void
@@ -372,6 +391,7 @@ search_dialog_dispose (GObject *object)
         }
 
         g_clear_object (&priv->parser);
+        g_clear_object (&priv->popup_menu);
 
         if (parent_class->dispose != NULL) {
                 parent_class->dispose (object);
@@ -576,3 +596,171 @@ search_dialog_on_search_activate (SearchDialog *self, GtkEntry *entry)
         }
 
 }
+
+static void
+do_popup_menu (GtkMenu *menu, GtkWidget *widget, GdkEventButton *event)
+{
+        int button = 0;
+        int event_time;
+        if (event) {
+                button = event->button;
+                event_time = event->time;
+        } else {
+                event_time = gtk_get_current_event_time ();
+        }
+
+        gtk_menu_popup (menu, NULL, NULL, NULL, NULL, button, event_time);
+}
+
+G_MODULE_EXPORT
+gboolean
+search_dialog_on_listview_button_release (GtkWidget      *widget,
+                                          GdkEventButton *event,
+                                          gpointer        user_data)
+{
+        SearchDialog *self = SEARCH_DIALOG (user_data);
+        SearchDialogPrivate *priv = search_dialog_get_instance_private (self);
+        GtkTreeSelection *selection = NULL;
+        GtkTreeModel *model = NULL;
+        GtkTreeIter iter;
+        GtkTreeView  *treeview = priv->search_dialog_treeview;
+
+        if (event->type != GDK_BUTTON_RELEASE || event->button != 3) {
+                return FALSE;
+        }
+
+        selection = gtk_tree_view_get_selection (treeview);
+        g_assert (selection != NULL);
+
+        /* Only show the popup menu when a row is selected */
+        if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                return FALSE;
+        }
+
+        do_popup_menu (GTK_MENU (priv->popup_menu),
+                       GTK_WIDGET (treeview),
+                       event);
+
+        return TRUE;
+}
+
+static void
+on_object (GUPnPDIDLLiteParser *parser,
+           GUPnPDIDLLiteObject *object,
+           gpointer user_data)
+{
+        GUPnPDIDLLiteObject **result = (GUPnPDIDLLiteObject **)user_data;
+        if (*result == NULL) {
+                *result = g_object_ref (object);
+        }
+}
+
+static void
+search_dialog_on_metadata_ready (GObject *source,
+                                 GAsyncResult *res,
+                                 gpointer user_data)
+{
+        SearchDialog *self = SEARCH_DIALOG (user_data);
+        SearchDialogPrivate *priv  = search_dialog_get_instance_private (self);
+        AVCPMediaServer *server = AV_CP_MEDIA_SERVER (source);
+        GtkTreeView  *treeview = priv->search_dialog_treeview;
+        GtkTreeModel *model = NULL;
+        GtkTreeSelection *selection = NULL;
+        char *xml;
+        GError *error = NULL;
+        GtkTreeIter iter;
+
+        if (!av_cp_media_server_browse_metadata_finish (server,
+                                                        res,
+                                                        &xml,
+                                                        &error)) {
+                GtkWidget *message = NULL;
+
+                message = gtk_message_dialog_new (GTK_WINDOW (self),
+                                                  GTK_DIALOG_MODAL,
+                                                  GTK_MESSAGE_WARNING,
+                                                  GTK_BUTTONS_CLOSE,
+                                                  _("Error fetching detailed information: %s"),
+                                                  error->message);
+                gtk_dialog_run (GTK_DIALOG (message));
+                gtk_widget_destroy (message);
+        } else {
+                selection = gtk_tree_view_get_selection (treeview);
+                if (gtk_tree_selection_get_selected (selection,
+                                                     &model,
+                                                     &iter)) {
+                        GUPnPDIDLLiteParser *parser = NULL;
+                        GUPnPDIDLLiteObject *didl_object = NULL;
+
+                        parser = gupnp_didl_lite_parser_new ();
+                        g_signal_connect (G_OBJECT (parser),
+                                          "object-available",
+                                          G_CALLBACK (on_object),
+                                          &didl_object);
+                        gupnp_didl_lite_parser_parse_didl (parser, xml, NULL);
+                        gtk_list_store_set (GTK_LIST_STORE (model),
+                                            &iter,
+                                            2, didl_object,
+                                            -1);
+                        g_object_unref (parser);
+                        g_object_unref (didl_object);
+                        {
+                            AVCPDidlDialog *dialog = av_cp_didl_dialog_new ();
+
+                            av_cp_didl_dialog_set_xml (dialog, xml);
+                            gtk_window_set_transient_for (GTK_WINDOW (dialog),
+                                                          GTK_WINDOW (self));
+                            gtk_dialog_run (GTK_DIALOG (dialog));
+                            gtk_widget_destroy (GTK_WIDGET (dialog));
+                        }
+                }
+                g_free (xml);
+        }
+}
+
+G_MODULE_EXPORT
+void
+search_dialog_on_didl_popup_activate (SearchDialog *self, gpointer user_data)
+{
+        SearchDialogPrivate *priv  = search_dialog_get_instance_private (self);
+        GtkTreeView  *treeview = priv->search_dialog_treeview;
+        GtkTreeModel *model = NULL;
+        GUPnPDIDLLiteObject *didl_object = NULL;
+        GtkTreeSelection *selection = NULL;
+        GtkTreeIter iter;
+        char *id = NULL;
+
+        selection = gtk_tree_view_get_selection (treeview);
+
+        /* Only show the popup menu when a row is selected */
+        if (!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+                return;
+        }
+
+        gtk_tree_model_get (model,
+                            &iter,
+                            2, &didl_object,
+                            3, &id,
+                            -1);
+
+        if (didl_object != NULL) {
+                AVCPDidlDialog *dialog = av_cp_didl_dialog_new ();
+                char *xml = NULL;
+
+                g_free (id);
+                xml = gupnp_didl_lite_object_get_xml_string (didl_object);
+                av_cp_didl_dialog_set_xml (dialog, xml);
+                gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (self));
+                gtk_dialog_run (GTK_DIALOG (dialog));
+                gtk_widget_destroy (GTK_WIDGET (dialog));
+                g_free (xml);
+                g_object_unref (didl_object);
+        } else {
+                av_cp_media_server_browse_metadata_async (priv->server,
+                                                          NULL,
+                                                          search_dialog_on_metadata_ready,
+                                                          id,
+                                                          self);
+                g_free (id);
+        }
+}
diff --git a/src/av-cp/server-device.c b/src/av-cp/server-device.c
index 3f6736d..09a0e4a 100644
--- a/src/av-cp/server-device.c
+++ b/src/av-cp/server-device.c
@@ -315,7 +315,7 @@ av_cp_media_server_introspect_finish (AVCPMediaServer *self)
         GList *l;
 
         for (l = self->priv->tasks; l != NULL; l = l->next) {
-                GTask *task = l->data;
+                GTask *task = (GTask *) l->data;
 
                 if (self->priv->state == INITIALIZED) {
                         g_task_return_boolean (task, TRUE);
@@ -349,7 +349,7 @@ av_cp_media_server_get_content_directory (AVCPMediaServer *self)
                 self->priv->content_directory =  GUPNP_SERVICE_PROXY (info);
         }
 
-        return g_object_ref (self->priv->content_directory);
+        return GUPNP_SERVICE_PROXY (g_object_ref (self->priv->content_directory));
 }
 
 typedef struct _BrowseReturn {
@@ -448,7 +448,7 @@ av_cp_media_server_browse_finish (AVCPMediaServer  *self,
 
         g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
 
-        res = g_task_propagate_pointer (G_TASK (result), error);
+        res = (BrowseReturn *)g_task_propagate_pointer (G_TASK (result), error);
         if (res != NULL) {
                 if (didl_xml != NULL) {
                         *didl_xml = res->didl_xml;
@@ -543,7 +543,7 @@ av_cp_media_server_browse_metadata_finish (AVCPMediaServer  *self,
 
         g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
 
-        res = g_task_propagate_pointer (G_TASK (result), error);
+        res = (char *)g_task_propagate_pointer (G_TASK (result), error);
         if (res != NULL) {
                 if (didl_xml != NULL) {
                         *didl_xml = res;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]