[gnome-software] Add a widget that can show a remote screenshot
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] Add a widget that can show a remote screenshot
- Date: Sat, 5 Oct 2013 09:05:04 +0000 (UTC)
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]