[gnome-software] Add a widget that can show a remote screenshot



commit 0f57a0f9dd1fc6403146a4467f789bc3dda166ed
Author: Richard Hughes <richard hughsie com>
Date:   Fri Oct 4 17:11:13 2013 +0100

    Add a widget that can show a remote screenshot
    
    This also shows loading errors and also shows a progress spinner while loading.

 configure.ac                     |    1 +
 contrib/gnome-software.spec.in   |    1 +
 po/POTFILES.in                   |    1 +
 src/Makefile.am                  |    5 +
 src/gnome-software.gresource.xml |    1 +
 src/gs-screenshot-image.c        |  341 ++++++++++++++++++++++++++++++++++++++
 src/gs-screenshot-image.h        |   71 ++++++++
 src/gtk-style.css                |   12 ++
 src/screenshot-image.ui          |   78 +++++++++
 9 files changed, 511 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 74d0a8f..0494fd0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -76,6 +76,7 @@ PKG_CHECK_MODULES(GTK, gtk+-3.0 >= 3.9.12 gio-unix-2.0)
 PKG_CHECK_MODULES(PACKAGEKIT, packagekit-glib2 >= 0.8.10)
 PKG_CHECK_MODULES(SQLITE, sqlite3)
 PKG_CHECK_MODULES(NOTIFY, libnotify)
+PKG_CHECK_MODULES(SOUP, libsoup-2.4)
 AC_ARG_ENABLE(man,
               [AS_HELP_STRING([--enable-man],
                               [generate man pages [default=auto]])],,
diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in
index 4070b5d..43a2c76 100644
--- a/contrib/gnome-software.spec.in
+++ b/contrib/gnome-software.spec.in
@@ -22,6 +22,7 @@ BuildRequires: glib2-devel >= 2.25.9-2
 BuildRequires: gtk3-devel >= 3.9.12
 BuildRequires: libnotify-devel
 BuildRequires: PackageKit-glib-devel >= 0.8.10
+BuildRequires: libsoup-devel
 
 %description
 gnome-software is an application that makes it easy to add, remove
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 66d5fe6..baa477e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ src/gs-feature-tile.c
 src/gs-main.c
 src/gs-plugin-loader.c
 src/gs-popular-tile.c
+src/gs-screenshot-image.c
 src/gs-shell.c
 src/gs-shell-details.c
 src/gs-shell-installed.c
diff --git a/src/Makefile.am b/src/Makefile.am
index b6a4df6..1dfafd3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS =                                               \
 AM_CPPFLAGS =                                          \
        $(GLIB_CFLAGS)                                  \
        $(GTK_CFLAGS)                                   \
+       $(SOUP_CFLAGS)                                  \
        $(PACKAGEKIT_CFLAGS)                            \
        $(NOTIFY_CFLAGS)                                \
        -DG_LOG_DOMAIN=\"Gs\"                           \
@@ -29,6 +30,7 @@ UI_FILES =                                            \
        feature-tile.ui                                 \
        gnome-software.ui                               \
        gnome-software.ui                               \
+       screenshot-image.ui                             \
        popular-tile.ui
 
 bin_PROGRAMS =                                         \
@@ -59,6 +61,8 @@ gnome_software_SOURCES =                              \
        gs-plugin.h                                     \
        gs-profile.c                                    \
        gs-profile.h                                    \
+       gs-screenshot-image.c                           \
+       gs-screenshot-image.h                           \
        gs-screenshot.c                                 \
        gs-screenshot.h                                 \
        gs-shell.c                                      \
@@ -86,6 +90,7 @@ gnome_software_SOURCES =                              \
 gnome_software_LDADD =                                 \
        $(GLIB_LIBS)                                    \
        $(GTK_LIBS)                                     \
+       $(SOUP_LIBS)                                    \
        $(PACKAGEKIT_LIBS)                              \
        $(NOTIFY_LIBS)                                  \
        -lm
diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml
index 9fd5ce0..04349c8 100644
--- a/src/gnome-software.gresource.xml
+++ b/src/gnome-software.gresource.xml
@@ -8,6 +8,7 @@
   <file preprocess="xml-stripblanks">category-tile.ui</file>
   <file preprocess="xml-stripblanks">app-tile.ui</file>
   <file preprocess="xml-stripblanks">app-widget.ui</file>
+  <file preprocess="xml-stripblanks">screenshot-image.ui</file>
   <file>gtk-style.css</file>
   <file>gtk-style-hc.css</file>
   <file preprocess="to-pixdata">shadow.png</file>
diff --git a/src/gs-screenshot-image.c b/src/gs-screenshot-image.c
new file mode 100644
index 0000000..ea1e7ab
--- /dev/null
+++ b/src/gs-screenshot-image.c
@@ -0,0 +1,341 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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 "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+
+#include "gs-screenshot-image.h"
+
+struct _GsScreenshotImagePrivate
+{
+       GsScreenshot    *screenshot;
+       GtkWidget       *box;
+       GtkWidget       *box_error;
+       GtkWidget       *button;
+       GtkWidget       *image;
+       GtkWidget       *label_error;
+       GtkWidget       *spinner;
+       SoupSession     *session;
+       gchar           *cachedir;
+       gchar           *filename;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GsScreenshotImage, gs_screenshot_image, GTK_TYPE_BIN)
+
+enum {
+       SIGNAL_CLICKED,
+       SIGNAL_LAST
+};
+
+static guint signals [SIGNAL_LAST] = { 0 };
+
+/**
+ * gs_screenshot_image_get_screenshot:
+ **/
+GsScreenshot *
+gs_screenshot_image_get_screenshot (GsScreenshotImage *ssimg)
+{
+       GsScreenshotImagePrivate *priv;
+       g_return_val_if_fail (GS_IS_SCREENSHOT_IMAGE (ssimg), NULL);
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+       return priv->screenshot;
+}
+
+/**
+ * gs_screenshot_image_get_cachedir:
+ **/
+const gchar *
+gs_screenshot_image_get_cachedir (GsScreenshotImage *ssimg)
+{
+       GsScreenshotImagePrivate *priv;
+       g_return_val_if_fail (GS_IS_SCREENSHOT_IMAGE (ssimg), NULL);
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+       return priv->cachedir;
+}
+
+/**
+ * gs_screenshot_image_set_error:
+ **/
+static void
+gs_screenshot_image_set_error (GsScreenshotImage *ssimg, const gchar *message)
+{
+       GsScreenshotImagePrivate *priv;
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+
+       gtk_widget_set_visible (priv->image, FALSE);
+       gtk_widget_set_visible (priv->spinner, FALSE);
+       gtk_widget_set_visible (priv->box_error, TRUE);
+       gtk_label_set_label (GTK_LABEL (priv->label_error), message);
+}
+
+/**
+ * gs_screenshot_show_image:
+ **/
+static void
+gs_screenshot_show_image (GsScreenshotImage *ssimg)
+{
+       GsScreenshotImagePrivate *priv;
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+
+       /* stop loading */
+       gtk_spinner_stop (GTK_SPINNER (priv->spinner));
+       gtk_widget_set_visible (priv->spinner, FALSE);
+
+       /* show icon */
+       gtk_image_set_from_file (GTK_IMAGE (priv->image), priv->filename);
+       gtk_widget_set_visible (priv->image, TRUE);
+}
+
+/**
+ * gs_screenshot_image_complete_cb:
+ **/
+static void
+gs_screenshot_image_complete_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+       GsScreenshotImagePrivate *priv;
+       GsScreenshotImage *ssimg = GS_SCREENSHOT_IMAGE (user_data);
+       gboolean ret;
+       GError *error = NULL;
+
+       if (msg->status_code != SOUP_STATUS_OK) {
+               /* TRANSLATORS: this is when we try to download a screenshot and
+                * we get back 404 */
+               gs_screenshot_image_set_error (ssimg, _("Screenshot not found"));
+               goto out;
+       }
+
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+
+       /* save to file */
+       ret = g_file_set_contents (priv->filename,
+                                  msg->response_body->data,
+                                  msg->response_body->length,
+                                  &error);
+       if (!ret) {
+               gs_screenshot_image_set_error (ssimg, error->message);
+               g_error_free (error);
+               goto out;
+       }
+
+       /* got image, so show */
+       gs_screenshot_show_image (ssimg);
+out:
+       return;
+}
+
+/**
+ * gs_screenshot_image_set_cachedir:
+ **/
+void
+gs_screenshot_image_set_cachedir (GsScreenshotImage *ssimg, const gchar *cachedir)
+{
+       GsScreenshotImagePrivate *priv;
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+       g_free (priv->cachedir);
+       priv->cachedir = g_strdup (cachedir);
+}
+
+/**
+ * gs_screenshot_image_set_screenshot:
+ **/
+void
+gs_screenshot_image_set_screenshot (GsScreenshotImage *ssimg,
+                                   GsScreenshot *screenshot,
+                                   guint width, guint height)
+{
+       GsScreenshotImagePrivate *priv;
+       SoupMessage *msg = NULL;
+       SoupURI *base_uri = NULL;
+       const gchar *url;
+       gchar *basename = NULL;
+       gchar *cachedir = NULL;
+       gchar *sizedir = NULL;
+       gint rc;
+
+       g_return_if_fail (GS_IS_SCREENSHOT_IMAGE (ssimg));
+       g_return_if_fail (GS_IS_SCREENSHOT (screenshot));
+       g_return_if_fail (width != 0);
+       g_return_if_fail (height != 0);
+
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+       priv->screenshot = g_object_ref (screenshot);
+       gtk_widget_set_size_request (priv->box, width, height);
+
+       /* test if size specific cachdir exists */
+       url = gs_screenshot_get_url (screenshot, width, height);
+       if (url == NULL) {
+               /* TRANSLATORS: this is when we request a screenshot size that
+                * the generator did not create or the parser did not add */
+               gs_screenshot_image_set_error (ssimg, _("Screenshot size not found"));
+               goto out;
+       }
+       basename = g_path_get_basename (url);
+       sizedir = g_strdup_printf ("%ux%u", width, height);
+       cachedir = g_build_filename (priv->cachedir,
+                                    "gnome-software",
+                                    "screenshots",
+                                    sizedir,
+                                    NULL);
+       rc = g_mkdir_with_parents (cachedir, 0700);
+       if (rc != 0) {
+               /* TRANSLATORS: this is when we try create the cache directory
+                * but we were out of space or permission was denied */
+               gs_screenshot_image_set_error (ssimg, _("Could not create cache"));
+               goto out;
+       }
+
+       /* does local file already exist */
+       priv->filename = g_build_filename (cachedir, basename, NULL);
+       if (g_file_test (priv->filename, G_FILE_TEST_EXISTS)) {
+               gs_screenshot_show_image (ssimg);
+               goto out;
+       }
+
+       /* download file */
+       g_debug ("downloading %s to %s\n", url, priv->filename);
+       base_uri = soup_uri_new (url);
+       if (base_uri == NULL) {
+               /* TRANSLATORS: this is when we try to download a screenshot
+                * that was not a valid URL */
+               gs_screenshot_image_set_error (ssimg, _("Screenshot not valid"));
+               goto out;
+       }
+       msg = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
+       if (msg == NULL) {
+               /* TRANSLATORS: this is when networking is not available */
+               gs_screenshot_image_set_error (ssimg, _("Screenshot not available"));
+               goto out;
+       }
+
+       /* send async */
+       soup_session_queue_message (priv->session, msg,
+                                   gs_screenshot_image_complete_cb, ssimg);
+       gtk_spinner_start (GTK_SPINNER (priv->spinner));
+out:
+       g_free (basename);
+       g_free (sizedir);
+       g_free (cachedir);
+       if (base_uri != NULL)
+               soup_uri_free (base_uri);
+}
+
+/**
+ * gs_screenshot_image_destroy:
+ **/
+static void
+gs_screenshot_image_destroy (GtkWidget *widget)
+{
+       GsScreenshotImage *ssimg = GS_SCREENSHOT_IMAGE (widget);
+       GsScreenshotImagePrivate *priv;
+
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+
+       g_clear_object (&priv->screenshot);
+       g_free (priv->cachedir);
+       priv->cachedir = NULL;
+       g_free (priv->filename);
+       priv->filename = NULL;
+       g_clear_object (&priv->session);
+
+       GTK_WIDGET_CLASS (gs_screenshot_image_parent_class)->destroy (widget);
+}
+
+/**
+ * button_clicked:
+ **/
+static void
+button_clicked (GsScreenshotImage *ssimg)
+{
+       g_signal_emit (ssimg, signals[SIGNAL_CLICKED], 0);
+}
+
+/**
+ * gs_screenshot_image_init:
+ **/
+static void
+gs_screenshot_image_init (GsScreenshotImage *ssimg)
+{
+       GsScreenshotImagePrivate *priv;
+
+       gtk_widget_set_has_window (GTK_WIDGET (ssimg), FALSE);
+       gtk_widget_init_template (GTK_WIDGET (ssimg));
+       priv = gs_screenshot_image_get_instance_private (ssimg);
+       g_signal_connect_swapped (priv->button, "clicked",
+                                 G_CALLBACK (button_clicked), ssimg);
+
+       /* setup networking */
+       priv->session = soup_session_sync_new_with_options (SOUP_SESSION_USER_AGENT,
+                                                           "gnome-software",
+                                                           SOUP_SESSION_TIMEOUT, 5000,
+                                                           NULL);
+       if (priv->session == NULL) {
+               g_warning ("Failed to setup networking");
+               return;
+       }
+       soup_session_add_feature_by_type (priv->session,
+                                         SOUP_TYPE_PROXY_RESOLVER_DEFAULT);
+}
+
+/**
+ * gs_screenshot_image_class_init:
+ **/
+static void
+gs_screenshot_image_class_init (GsScreenshotImageClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+       GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+       widget_class->destroy = gs_screenshot_image_destroy;
+
+       signals [SIGNAL_CLICKED] =
+               g_signal_new ("clicked",
+                             G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+                             G_STRUCT_OFFSET (GsScreenshotImageClass, clicked),
+                             NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                             G_TYPE_NONE, 0);
+
+       gtk_widget_class_set_template_from_resource (widget_class,
+                                                    "/org/gnome/software/screenshot-image.ui");
+
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, box);
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, button);
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, spinner);
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, image);
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, box_error);
+       gtk_widget_class_bind_template_child_private (widget_class, GsScreenshotImage, label_error);
+}
+
+/**
+ * gs_screenshot_image_new:
+ **/
+GtkWidget *
+gs_screenshot_image_new (void)
+{
+       GsScreenshotImage *ssimg;
+       ssimg = g_object_new (GS_TYPE_SCREENSHOT_IMAGE, NULL);
+       return GTK_WIDGET (ssimg);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-screenshot-image.h b/src/gs-screenshot-image.h
new file mode 100644
index 0000000..3a4b1bb
--- /dev/null
+++ b/src/gs-screenshot-image.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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.
+ */
+
+#ifndef GS_SCREENSHOT_IMAGE_H
+#define GS_SCREENSHOT_IMAGE_H
+
+#include <gtk/gtk.h>
+
+#include "gs-screenshot.h"
+
+#define GS_TYPE_SCREENSHOT_IMAGE               (gs_screenshot_image_get_type())
+#define GS_SCREENSHOT_IMAGE(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), GS_TYPE_SCREENSHOT_IMAGE, 
GsScreenshotImage))
+#define GS_SCREENSHOT_IMAGE_CLASS(cls)         (G_TYPE_CHECK_CLASS_CAST((cls), GS_TYPE_SCREENSHOT_IMAGE, 
GsScreenshotImageClass))
+#define GS_IS_SCREENSHOT_IMAGE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), GS_TYPE_SCREENSHOT_IMAGE))
+#define GS_IS_SCREENSHOT_IMAGE_CLASS(cls)      (G_TYPE_CHECK_CLASS_TYPE((cls), GS_TYPE_SCREENSHOT_IMAGE))
+#define GS_SCREENSHOT_IMAGE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), GS_TYPE_SCREENSHOT_IMAGE, 
GsScreenshotImageClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GsScreenshotImage              GsScreenshotImage;
+typedef struct _GsScreenshotImageClass         GsScreenshotImageClass;
+typedef struct _GsScreenshotImagePrivate       GsScreenshotImagePrivate;
+
+struct _GsScreenshotImage
+{
+       GtkBin                           parent;
+       GsScreenshotImagePrivate        *priv;
+};
+
+struct _GsScreenshotImageClass
+{
+       GtkBinClass      parent_class;
+       void            (*clicked)                      (GsScreenshotImage      *ssimg);
+};
+
+GType           gs_screenshot_image_get_type           (void);
+GtkWidget      *gs_screenshot_image_new                (void);
+
+GsScreenshot   *gs_screenshot_image_get_screenshot     (GsScreenshotImage      *ssimg);
+void            gs_screenshot_image_set_screenshot     (GsScreenshotImage      *ssimg,
+                                                        GsScreenshot           *screenshot,
+                                                        guint                   width,
+                                                        guint                   height);
+const gchar    *gs_screenshot_image_get_cachedir       (GsScreenshotImage      *ssimg);
+void            gs_screenshot_image_set_cachedir       (GsScreenshotImage      *ssimg,
+                                                        const gchar            *cachedir);
+
+G_END_DECLS
+
+#endif /* GS_SCREENSHOT_IMAGE_H */
+
+/* vim: set noexpandtab: */
diff --git a/src/gtk-style.css b/src/gtk-style.css
index 53c8be3..7de82d4 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -30,6 +30,18 @@ GtkNotebook.main-notebook-software > GtkScrolledWindow {
         animation: throbbing linear 1s infinite alternate;
 }
 
+.button.screenshot-image {
+        padding: 0;
+        border-radius: 0;
+        border-width: 0px;
+        border-image: none;
+        -GtkWidget-focus-padding: 0;
+        outline-style: dashed;
+        outline-offset: 2px;
+        background-image: none;
+        background-color: rgba(0,0,0,0.5);
+}
+
 .view.tile {
         border-radius: 0;
         border-width: 3px;
diff --git a/src/screenshot-image.ui b/src/screenshot-image.ui
new file mode 100644
index 0000000..ffe57c6
--- /dev/null
+++ b/src/screenshot-image.ui
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.10 -->
+  <template class="GsScreenshotImage" parent="GtkBin">
+    <property name="visible">True</property>
+    <child>
+      <object class="GtkButton" id="button">
+        <property name="visible">True</property>
+        <style>
+          <class name="screenshot-image"/>
+        </style>
+        <child>
+          <object class="GtkBox" id="box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkSpinner" id="spinner">
+                <property name="width_request">16</property>
+                <property name="height_request">16</property>
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkImage" id="image">
+                <property name="visible">False</property>
+                <property name="stock">gtk-missing-image</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box_error">
+                <property name="visible">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkImage" id="image_error">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-dialog-error</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label_error">
+                    <property name="visible">True</property>
+                    <property name="label">label</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


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