[gnome-software/wip/hughsie/GsPluginEvent: 3/3] Allow plugins to report non-critical events up to the UI
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/hughsie/GsPluginEvent: 3/3] Allow plugins to report non-critical events up to the UI
- Date: Wed, 7 Sep 2016 10:39:02 +0000 (UTC)
commit 3eb4d3e509a33e928251f57f1574d3ca8de9a157
Author: Richard Hughes <richard hughsie com>
Date: Tue Sep 6 18:49:12 2016 +0100
Allow plugins to report non-critical events up to the UI
This also removes the hacky last_error GsApp private API and thus converts the
ugly modal failure dialogs to in-app notifications.
Fixes: https://bugzilla.gnome.org/show_bug.cgi?id=770918
doc/api/Makefile.am | 1 +
doc/api/gnome-software-docs.xml | 1 +
doc/api/gnome-software.types | 1 +
src/Makefile.am | 4 +
src/gnome-software.ui | 83 ++++++++++++-
src/gs-app-private.h | 3 -
src/gs-app.c | 40 ------
src/gs-page.c | 28 ----
src/gs-plugin-event.c | 270 +++++++++++++++++++++++++++++++++++++++
src/gs-plugin-event.h | 79 ++++++++++++
src/gs-plugin-loader.c | 177 +++++++++++++++++++++++++-
src/gs-plugin-loader.h | 6 +
src/gs-plugin-vfuncs.h | 9 +-
src/gs-plugin.c | 41 ++++++
src/gs-plugin.h | 7 +-
src/gs-self-test.c | 31 ++++-
src/gs-shell-updates.c | 13 --
src/gs-shell.c | 130 +++++++++++++++++++
src/plugins/gs-plugin-dummy.c | 31 +++++
19 files changed, 854 insertions(+), 101 deletions(-)
---
diff --git a/doc/api/Makefile.am b/doc/api/Makefile.am
index 293e844..e2ac7e4 100644
--- a/doc/api/Makefile.am
+++ b/doc/api/Makefile.am
@@ -85,6 +85,7 @@ GTKDOC_LIBS= \
$(top_builddir)/src/gnome_software-gs-category.o \
$(top_builddir)/src/gnome_software-gs-os-release.o \
$(top_builddir)/src/gnome_software-gs-plugin.o \
+ $(top_builddir)/src/gnome_software-gs-plugin-event.o \
$(top_builddir)/src/gnome_software-gs-utils.o
# This includes the standard gtk-doc make rules, copied by gtkdocize.
diff --git a/doc/api/gnome-software-docs.xml b/doc/api/gnome-software-docs.xml
index ed3a8ae..d3a171b 100644
--- a/doc/api/gnome-software-docs.xml
+++ b/doc/api/gnome-software-docs.xml
@@ -627,6 +627,7 @@ gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app)
<xi:include href="xml/gs-auth.xml"/>
<xi:include href="xml/gs-os-release.xml"/>
<xi:include href="xml/gs-plugin.xml"/>
+ <xi:include href="xml/gs-plugin-event.xml"/>
<xi:include href="xml/gs-plugin-vfuncs.xml"/>
<xi:include href="xml/gs-utils.xml"/>
</reference>
diff --git a/doc/api/gnome-software.types b/doc/api/gnome-software.types
index c7c2514..5e1431f 100644
--- a/doc/api/gnome-software.types
+++ b/doc/api/gnome-software.types
@@ -3,3 +3,4 @@ gs_app_list_get_type
gs_category_get_type
gs_os_release_get_type
gs_plugin_get_type
+gs_plugin_event_get_type
diff --git a/src/Makefile.am b/src/Makefile.am
index b8677dd..a7047c8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -115,6 +115,7 @@ gnome_software_cmd_SOURCES = \
gs-debug.c \
gs-utils.c \
gs-os-release.c \
+ gs-plugin-event.c \
gs-plugin-loader.c \
gs-plugin-loader-sync.c \
gs-category.c \
@@ -192,6 +193,8 @@ gnome_software_SOURCES = \
gs-page.h \
gs-plugin.c \
gs-plugin.h \
+ gs-plugin-event.c \
+ gs-plugin-event.h \
gs-plugin-private.h \
gs-plugin-vfuncs.h \
gs-progress-button.c \
@@ -340,6 +343,7 @@ gs_self_test_SOURCES = \
gs-category.c \
gs-common.c \
gs-os-release.c \
+ gs-plugin-event.c \
gs-plugin-loader-sync.c \
gs-plugin-loader.c \
gs-plugin.c \
diff --git a/src/gnome-software.ui b/src/gnome-software.ui
index 52755ee..e06882f 100644
--- a/src/gnome-software.ui
+++ b/src/gnome-software.ui
@@ -276,6 +276,87 @@
</child>
<child>
+ <object class="GtkInfoBar" id="infobar_events">
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button_events_sources">
+ <property name="label" translatable="yes" comments="button in the info bar">Software
Sources</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="content_area">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <property name="border_width">12</property>
+ <child>
+ <object class="GtkLabel" id="label_events_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label">Some Title</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </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_events">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label">Longer text that explains things some more.</property>
+ <property name="wrap">True</property>
+ <property name="wrap_mode">word-char</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </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="position">2</property>
+ </packing>
+ </child>
+
+ <child>
<object class="GtkStack" id="stack_main">
<property name="visible">True</property>
<child>
@@ -358,7 +439,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
diff --git a/src/gs-app-private.h b/src/gs-app-private.h
index 9fb8391..0a83c81 100644
--- a/src/gs-app-private.h
+++ b/src/gs-app-private.h
@@ -26,9 +26,6 @@
G_BEGIN_DECLS
-GError *gs_app_get_last_error (GsApp *app);
-void gs_app_set_last_error (GsApp *app,
- GError *error);
void gs_app_set_priority (GsApp *app,
guint priority);
guint gs_app_get_priority (GsApp *app);
diff --git a/src/gs-app.c b/src/gs-app.c
index 5d57ec0..654e2cc 100644
--- a/src/gs-app.c
+++ b/src/gs-app.c
@@ -75,7 +75,6 @@ struct _GsApp
gchar *summary_missing;
gchar *description;
GsAppQuality description_quality;
- GError *last_error;
GPtrArray *screenshots;
GPtrArray *categories;
GPtrArray *key_colors;
@@ -253,8 +252,6 @@ gs_app_to_string (GsApp *app)
str = g_string_new ("GsApp:");
g_string_append_printf (str, " [%p]\n", app);
gs_app_kv_lpad (str, "kind", as_app_kind_to_string (app->kind));
- if (app->last_error != NULL)
- gs_app_kv_lpad (str, "last-error", app->last_error->message);
gs_app_kv_lpad (str, "state", as_app_state_to_string (app->state));
if (app->quirk > 0) {
g_autofree gchar *qstr = _as_app_quirk_to_string (app->quirk);
@@ -793,9 +790,6 @@ gs_app_set_state_internal (GsApp *app, AsAppState state)
app->id, as_app_state_to_string (state));
app->state_recover = state;
}
-
- /* clear the error as the application has changed state */
- g_clear_error (&app->last_error);
break;
}
@@ -3185,38 +3179,6 @@ gs_app_get_priority (GsApp *app)
return app->priority;
}
-/**
- * gs_app_get_last_error:
- * @app: a #GsApp
- *
- * Get the last error.
- *
- * Returns: a #GError, or %NULL
- *
- * Since: 3.22
- **/
-GError *
-gs_app_get_last_error (GsApp *app)
-{
- return app->last_error;
-}
-
-/**
- * gs_app_set_last_error:
- * @app: a #GsApp
- * @error: a #GError
- *
- * Sets the last error.
- *
- * Since: 3.22
- **/
-void
-gs_app_set_last_error (GsApp *app, GError *error)
-{
- g_clear_error (&app->last_error);
- app->last_error = g_error_copy (error);
-}
-
static void
gs_app_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
@@ -3365,8 +3327,6 @@ gs_app_finalize (GObject *object)
g_ptr_array_unref (app->key_colors);
if (app->keywords != NULL)
g_ptr_array_unref (app->keywords);
- if (app->last_error != NULL)
- g_error_free (app->last_error);
if (app->local_file != NULL)
g_object_unref (app->local_file);
if (app->pixbuf != NULL)
diff --git a/src/gs-page.c b/src/gs-page.c
index 19e5f42..ef2a4b9 100644
--- a/src/gs-page.c
+++ b/src/gs-page.c
@@ -128,7 +128,6 @@ gs_page_app_installed_cb (GObject *source,
gpointer user_data)
{
g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
- GError *last_error;
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
GsPage *page = helper->page;
GsPagePrivate *priv = gs_page_get_instance_private (page);
@@ -176,19 +175,6 @@ gs_page_app_installed_cb (GObject *source,
return;
}
- /* non-fatal error */
- last_error = gs_app_get_last_error (helper->app);
- if (last_error != NULL) {
- g_warning ("failed to install %s: %s",
- gs_app_get_id (helper->app),
- last_error->message);
- gs_app_notify_failed_modal (helper->app,
- gs_shell_get_window (priv->shell),
- GS_PLUGIN_LOADER_ACTION_INSTALL,
- last_error);
- return;
- }
-
/* only show this if the window is not active */
if (gs_app_get_state (helper->app) != AS_APP_STATE_QUEUED_FOR_INSTALL &&
!gs_shell_is_active (priv->shell))
@@ -204,7 +190,6 @@ gs_page_app_removed_cb (GObject *source,
gpointer user_data)
{
g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
- GError *last_error;
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
GsPage *page = helper->page;
GsPagePrivate *priv = gs_page_get_instance_private (page);
@@ -250,19 +235,6 @@ gs_page_app_removed_cb (GObject *source,
return;
}
- /* non-fatal error */
- last_error = gs_app_get_last_error (helper->app);
- if (last_error != NULL) {
- g_warning ("failed to remove %s: %s",
- gs_app_get_id (helper->app),
- last_error->message);
- gs_app_notify_failed_modal (helper->app,
- gs_shell_get_window (priv->shell),
- GS_PLUGIN_LOADER_ACTION_REMOVE,
- last_error);
- return;
- }
-
if (GS_PAGE_GET_CLASS (page)->app_removed != NULL)
GS_PAGE_GET_CLASS (page)->app_removed (page, helper->app);
}
diff --git a/src/gs-plugin-event.c b/src/gs-plugin-event.c
new file mode 100644
index 0000000..b1547b0
--- /dev/null
+++ b/src/gs-plugin-event.c
@@ -0,0 +1,270 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Richard Hughes <richard hughsie 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.
+ */
+
+/**
+ * SECTION:gs-event
+ * @title: GsPluginEvent
+ * @include: gnome-software.h
+ * @stability: Unstable
+ * @short_description: Infomation about a plugin event
+ *
+ * These functions provide a way for plugins to tell the UI layer about events
+ * that may require displaying to the user. Plugins should not assume that a
+ * specific event is actually shown to the user as it may be ignored
+ * automatically.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "gs-plugin-private.h"
+#include "gs-plugin-event.h"
+
+struct _GsPluginEvent
+{
+ GObject parent_instance;
+ GsApp *app;
+ GError *gerror;
+ GsPluginEventFlag flags;
+ gchar *unique_id;
+};
+
+G_DEFINE_TYPE (GsPluginEvent, gs_plugin_event, G_TYPE_OBJECT)
+
+/**
+ * gs_plugin_event_set_app:
+ * @event: A #GsPluginEvent
+ * @app: A #GsApp
+ *
+ * Set the application (or source, or whatever component) that caused the event
+ * to be created.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_event_set_app (GsPluginEvent *event, GsApp *app)
+{
+ g_return_if_fail (GS_IS_PLUGIN_EVENT (event));
+ g_return_if_fail (GS_IS_APP (app));
+ g_set_object (&event->app, app);
+}
+
+/**
+ * gs_plugin_event_get_app:
+ * @event: A #GsPluginEvent
+ *
+ * Gets an application that created the event.
+ *
+ * Returns: (transfer none): a #GsApp, or %NULL if unset
+ *
+ * Since: 3.22
+ **/
+GsApp *
+gs_plugin_event_get_app (GsPluginEvent *event)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_EVENT (event), NULL);
+ return event->app;
+}
+
+/**
+ * gs_plugin_event_get_unique_id:
+ * @event: A #GsPluginEvent
+ *
+ * Gets the unique ID for the event. In most cases (if an app has been set)
+ * this will just be the actual #GsApp unique-id. In the cases where only error
+ * has been set a virtual (but plausible) ID will be generated.
+ *
+ * Returns: a string, or %NULL for invalid
+ *
+ * Since: 3.22
+ **/
+const gchar *
+gs_plugin_event_get_unique_id (GsPluginEvent *event)
+{
+ /* just proxy */
+ if (event->app != NULL)
+ return gs_app_get_unique_id (event->app);
+
+ /* generate from error */
+ if (event->gerror != NULL) {
+ if (event->unique_id == NULL) {
+ g_autofree gchar *id = NULL;
+ id = g_strdup_printf ("%s.error",
+ gs_plugin_error_to_string (event->gerror->code));
+ event->unique_id = as_utils_unique_id_build (AS_APP_SCOPE_UNKNOWN,
+ AS_BUNDLE_KIND_UNKNOWN,
+ NULL,
+ AS_APP_KIND_UNKNOWN,
+ id,
+ NULL);
+ }
+ return event->unique_id;
+ }
+
+ /* failed */
+ return NULL;
+}
+
+/**
+ * gs_plugin_event_get_kind:
+ * @event: A #GsPluginEvent
+ * @flag: A #GsPluginEventFlag, e.g. %GS_PLUGIN_EVENT_FLAG_INVALID
+ *
+ * Adds a flag to the event.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_event_add_flag (GsPluginEvent *event, GsPluginEventFlag flag)
+{
+ g_return_if_fail (GS_IS_PLUGIN_EVENT (event));
+ event->flags |= flag;
+}
+
+/**
+ * gs_plugin_event_set_kind:
+ * @event: A #GsPluginEvent
+ * @flag: A #GsPluginEventFlag, e.g. %GS_PLUGIN_EVENT_FLAG_INVALID
+ *
+ * Removes a flag from the event.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_event_remove_flag (GsPluginEvent *event, GsPluginEventFlag flag)
+{
+ g_return_if_fail (GS_IS_PLUGIN_EVENT (event));
+ event->flags &= ~flag;
+}
+
+/**
+ * gs_plugin_event_has_flag:
+ * @event: A #GsPluginEvent
+ * @flag: A #GsPluginEventFlag, e.g. %GS_PLUGIN_EVENT_FLAG_INVALID
+ *
+ * Finds out if the event has a specific flag.
+ *
+ * Returns: %TRUE if the flag is set
+ *
+ * Since: 3.22
+ **/
+gboolean
+gs_plugin_event_has_flag (GsPluginEvent *event, GsPluginEventFlag flag)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_EVENT (event), FALSE);
+ return (event->flags & flag > 0);
+}
+
+/**
+ * gs_plugin_event_set_error:
+ * @event: A #GsPluginEvent
+ * @code: A #GsPluginError, e.g. %GS_PLUGIN_ERROR_NO_NETWORK
+ * @message: A error message
+ *
+ * Sets the event error.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_event_set_error (GsPluginEvent *event, gint code, const gchar *message)
+{
+ g_clear_error (&event->gerror);
+ g_set_error_literal (&event->gerror,
+ GS_PLUGIN_ERROR,
+ code,
+ message);
+}
+
+/**
+ * gs_plugin_event_set_gerror:
+ * @event: A #GsPluginEvent
+ * @error: A #GError
+ *
+ * Sets the event error.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_event_set_gerror (GsPluginEvent *event, const GError *error)
+{
+ g_clear_error (&event->gerror);
+ event->gerror = g_error_copy (error);
+}
+
+/**
+ * gs_plugin_event_get_gerror:
+ * @event: A #GsPluginEvent
+ *
+ * Gets the event error.
+ *
+ * Returns: a #GError, or %NULL for unset
+ *
+ * Since: 3.22
+ **/
+const GError *
+gs_plugin_event_get_gerror (GsPluginEvent *event)
+{
+ return event->gerror;
+}
+
+static void
+gs_plugin_event_finalize (GObject *object)
+{
+ GsPluginEvent *event = GS_PLUGIN_EVENT (object);
+ if (event->gerror != NULL)
+ g_error_free (event->gerror);
+ if (event->app != NULL)
+ g_object_unref (event->app);
+ g_free (event->unique_id);
+ G_OBJECT_CLASS (gs_plugin_event_parent_class)->finalize (object);
+}
+
+static void
+gs_plugin_event_class_init (GsPluginEventClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gs_plugin_event_finalize;
+}
+
+static void
+gs_plugin_event_init (GsPluginEvent *event)
+{
+}
+
+/**
+ * gs_plugin_event_new:
+ *
+ * Creates a new event.
+ *
+ * Returns: A newly allocated #GsPluginEvent
+ *
+ * Since: 3.22
+ **/
+GsPluginEvent *
+gs_plugin_event_new (void)
+{
+ GsPluginEvent *event;
+ event = g_object_new (GS_TYPE_PLUGIN_EVENT, NULL);
+ return GS_PLUGIN_EVENT (event);
+}
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-plugin-event.h b/src/gs-plugin-event.h
new file mode 100644
index 0000000..62065f1
--- /dev/null
+++ b/src/gs-plugin-event.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2016 Richard Hughes <richard hughsie 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_PLUGIN_EVENT
+#define __GS_PLUGIN_EVENT
+
+#include <glib-object.h>
+
+#include "gs-app.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PLUGIN_EVENT (gs_plugin_event_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsPluginEvent, gs_plugin_event, GS, PLUGIN_EVENT, GObject)
+
+/**
+ * GsPluginEventFlag:
+ * @GS_PLUGIN_EVENT_FLAG_NONE: No special flags set
+ * @GS_PLUGIN_EVENT_FLAG_INVALID: Event is no longer valid, e.g. was dismissed
+ * @GS_PLUGIN_EVENT_FLAG_VISIBLE: Event is is visible on the screen
+ * @GS_PLUGIN_EVENT_FLAG_WARNING: Event should be shown with more urgency
+ *
+ * Any flags an event can have.
+ **/
+typedef enum {
+ GS_PLUGIN_EVENT_FLAG_NONE = 0, /* Since: 3.22 */
+ GS_PLUGIN_EVENT_FLAG_INVALID = 1 << 0, /* Since: 3.22 */
+ GS_PLUGIN_EVENT_FLAG_VISIBLE = 1 << 1, /* Since: 3.22 */
+ GS_PLUGIN_EVENT_FLAG_WARNING = 1 << 2, /* Since: 3.22 */
+ /*< private >*/
+ GS_PLUGIN_EVENT_FLAG_LAST
+} GsPluginEventFlag;
+
+GsPluginEvent *gs_plugin_event_new (void);
+
+const gchar *gs_plugin_event_get_unique_id (GsPluginEvent *event);
+
+void gs_plugin_event_set_app (GsPluginEvent *event,
+ GsApp *app);
+GsApp *gs_plugin_event_get_app (GsPluginEvent *event);
+
+void gs_plugin_event_set_error (GsPluginEvent *event,
+ gint code,
+ const gchar *message);
+void gs_plugin_event_set_gerror (GsPluginEvent *event,
+ const GError *error);
+const GError *gs_plugin_event_get_gerror (GsPluginEvent *event);
+
+void gs_plugin_event_add_flag (GsPluginEvent *event,
+ GsPluginEventFlag flag);
+void gs_plugin_event_remove_flag (GsPluginEvent *event,
+ GsPluginEventFlag flag);
+gboolean gs_plugin_event_has_flag (GsPluginEvent *event,
+ GsPluginEventFlag flag);
+
+G_END_DECLS
+
+#endif /* __GS_PLUGIN_EVENT */
+
+/* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 83a9fb3..7511f31 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -52,6 +52,7 @@ typedef struct
GPtrArray *pending_apps;
GSettings *settings;
+ GHashTable *events_by_id; /* unique-id : GsPluginEvent */
gchar **compatible_projects;
guint scale;
@@ -71,6 +72,12 @@ enum {
SIGNAL_LAST
};
+enum {
+ PROP_0,
+ PROP_EVENTS,
+ PROP_LAST
+};
+
static guint signals [SIGNAL_LAST] = { 0 };
typedef void (*GsPluginFunc) (GsPlugin *plugin);
@@ -897,17 +904,50 @@ gs_plugin_loader_get_app_is_compatible (GsApp *app, gpointer user_data)
}
static void
-gs_plugin_loader_set_app_error (GsApp *app, GError *error)
+gs_plugin_loader_add_event (GsPluginLoader *plugin_loader, GsPluginEvent *event)
+{
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ g_hash_table_insert (priv->events_by_id,
+ (gpointer) gs_plugin_event_get_unique_id (event),
+ g_object_ref (event));
+ g_object_notify (G_OBJECT (plugin_loader), "events");
+}
+
+/**
+ * gs_plugin_loader_get_event_by_id:
+ * @list: A #GsAppList
+ * @unique_id: A unique_id
+ *
+ * Finds the first matching event in the list using the usual wildcard
+ * rules allowed in unique_ids.
+ *
+ * Returns: (transfer none): a #GsPluginEvent, or %NULL if not found
+ **/
+GsPluginEvent *
+gs_plugin_loader_get_event_by_id (GsPluginLoader *plugin_loader, const gchar *unique_id)
+{
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ return g_hash_table_lookup (priv->events_by_id, unique_id);
+}
+
+static void
+gs_plugin_loader_set_app_error (GsPluginLoader *plugin_loader,
+ GsApp *app, GError *error)
{
if (error == NULL)
return;
/* random, non-plugin error domains are never shown to the user */
if (error->domain == GS_PLUGIN_ERROR) {
+ g_autoptr(GsPluginEvent) event = gs_plugin_event_new ();
g_debug ("saving error for %s: %s",
gs_app_get_unique_id (app),
error->message);
- gs_app_set_last_error (app, error);
+
+ /* create and add event */
+ gs_plugin_event_set_app (event, app);
+ gs_plugin_event_set_gerror (event, error);
+ gs_plugin_loader_add_event (plugin_loader, event);
} else {
g_warning ("not saving error for %s: %s",
gs_app_get_unique_id (app),
@@ -983,7 +1023,8 @@ gs_plugin_loader_run_action (GsPluginLoader *plugin_loader,
function_name,
gs_plugin_get_name (plugin),
error_local->message);
- gs_plugin_loader_set_app_error (app, error_local);
+ gs_plugin_loader_set_app_error (plugin_loader,
+ app, error_local);
continue;
}
anything_ran = TRUE;
@@ -3356,6 +3397,81 @@ gs_plugin_loader_get_enabled (GsPluginLoader *plugin_loader,
return gs_plugin_get_enabled (plugin);
}
+/**
+ * gs_plugin_loader_get_events:
+ * @plugin_loader: A #GsPluginLoader
+ *
+ * Gets all plugin events, even ones that are not active or visible anymore.
+ *
+ * Returns: (transfer container) (element-type GsPluginEvent): events
+ **/
+GPtrArray *
+gs_plugin_loader_get_events (GsPluginLoader *plugin_loader)
+{
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ GPtrArray *events = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ GList *l;
+ g_autoptr(GList) keys = NULL;
+
+ /* just add everything */
+ keys = g_hash_table_get_keys (priv->events_by_id);
+ for (l = keys; l != NULL; l = l->next) {
+ const gchar *key = l->data;
+ GsPluginEvent *event = g_hash_table_lookup (priv->events_by_id, key);
+ g_ptr_array_add (events, g_object_ref (event));
+ }
+ return events;
+}
+
+/**
+ * gs_plugin_loader_get_event_default:
+ * @plugin_loader: A #GsPluginLoader
+ *
+ * Gets an active plugin event where active means that it was not been
+ * already dismissed by the user.
+ *
+ * Returns: a #GsPluginEvent, or %NULL for none
+ **/
+GsPluginEvent *
+gs_plugin_loader_get_event_default (GsPluginLoader *plugin_loader)
+{
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ GList *l;
+ g_autoptr(GList) keys = NULL;
+
+ /* just add everything */
+ keys = g_hash_table_get_keys (priv->events_by_id);
+ for (l = keys; l != NULL; l = l->next) {
+ const gchar *key = l->data;
+ GsPluginEvent *event = g_hash_table_lookup (priv->events_by_id, key);
+ if (!gs_plugin_event_has_flag (event, GS_PLUGIN_EVENT_FLAG_INVALID))
+ return event;
+ }
+ return NULL;
+}
+
+/**
+ * gs_plugin_loader_remove_events:
+ * @plugin_loader: A #GsPluginLoader
+ *
+ * Removes all plugin events from the loader. This function should only be
+ * called from the self tests.
+ **/
+void
+gs_plugin_loader_remove_events (GsPluginLoader *plugin_loader)
+{
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ g_hash_table_remove_all (priv->events_by_id);
+}
+
+static void
+gs_plugin_loader_add_event_cb (GsPlugin *plugin,
+ GsPluginEvent *event,
+ GsPluginLoader *plugin_loader)
+{
+ gs_plugin_loader_add_event (plugin_loader, event);
+}
+
static void
gs_plugin_loader_status_changed_cb (GsPlugin *plugin,
GsApp *app,
@@ -3465,6 +3581,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader,
g_signal_connect (plugin, "status-changed",
G_CALLBACK (gs_plugin_loader_status_changed_cb),
plugin_loader);
+ g_signal_connect (plugin, "add-event",
+ G_CALLBACK (gs_plugin_loader_add_event_cb),
+ plugin_loader);
gs_plugin_set_soup_session (plugin, priv->soup_session);
gs_plugin_set_auth_array (plugin, priv->auth_array);
gs_plugin_set_profile (plugin, priv->profile);
@@ -3835,6 +3954,34 @@ gs_plugin_loader_dump_state (GsPluginLoader *plugin_loader)
}
static void
+gs_plugin_loader_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+
+ switch (prop_id) {
+ case PROP_EVENTS:
+ g_value_set_pointer (value, priv->events_by_id);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_plugin_loader_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ switch (prop_id) {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
gs_plugin_loader_dispose (GObject *object)
{
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
@@ -3868,6 +4015,7 @@ gs_plugin_loader_finalize (GObject *object)
g_free (priv->locale);
g_free (priv->language);
g_object_unref (priv->global_cache);
+ g_hash_table_unref (priv->events_by_id);
g_mutex_clear (&priv->pending_apps_mutex);
@@ -3877,11 +4025,19 @@ gs_plugin_loader_finalize (GObject *object)
static void
gs_plugin_loader_class_init (GsPluginLoaderClass *klass)
{
+ GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->get_property = gs_plugin_loader_get_property;
+ object_class->set_property = gs_plugin_loader_set_property;
object_class->dispose = gs_plugin_loader_dispose;
object_class->finalize = gs_plugin_loader_finalize;
+ pspec = g_param_spec_string ("events", NULL, NULL,
+ NULL,
+ G_PARAM_READABLE);
+ g_object_class_install_property (object_class, PROP_EVENTS, pspec);
+
signals [SIGNAL_STATUS_CHANGED] =
g_signal_new ("status-changed",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
@@ -3926,6 +4082,18 @@ gs_plugin_loader_init (GsPluginLoader *plugin_loader)
priv->profile = as_profile_new ();
priv->settings = g_settings_new ("org.gnome.software");
+#if AS_CHECK_VERSION(0,6,2)
+ priv->events_by_id = g_hash_table_new_full ((GHashFunc) as_utils_unique_id_hash,
+ (GEqualFunc) as_utils_unique_id_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+#else
+ priv->events_by_id = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+#endif
+
/* share a soup session (also disable the double-compression) */
priv->soup_session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, gs_user_agent (),
SOUP_SESSION_TIMEOUT, 10,
@@ -4490,7 +4658,8 @@ gs_plugin_loader_update_thread_cb (GTask *task,
function_name,
gs_plugin_get_name (plugin),
error_local->message);
- gs_plugin_loader_set_app_error (app, error_local);
+ gs_plugin_loader_set_app_error (plugin_loader,
+ app, error_local);
continue;
}
}
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index daac548..f8e93cb 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -270,6 +270,12 @@ void gs_plugin_loader_set_network_status (GsPluginLoader
*plugin_loader,
gboolean gs_plugin_loader_get_plugin_supported (GsPluginLoader *plugin_loader,
const gchar *plugin_func);
+GPtrArray *gs_plugin_loader_get_events (GsPluginLoader *plugin_loader);
+GsPluginEvent *gs_plugin_loader_get_event_by_id (GsPluginLoader *plugin_loader,
+ const gchar *unique_id);
+GsPluginEvent *gs_plugin_loader_get_event_default (GsPluginLoader *plugin_loader);
+void gs_plugin_loader_remove_events (GsPluginLoader *plugin_loader);
+
G_END_DECLS
#endif /* __GS_PLUGIN_LOADER_H */
diff --git a/src/gs-plugin-vfuncs.h b/src/gs-plugin-vfuncs.h
index 525a2ae..2205320 100644
--- a/src/gs-plugin-vfuncs.h
+++ b/src/gs-plugin-vfuncs.h
@@ -513,8 +513,7 @@ gboolean gs_plugin_update_cancel (GsPlugin *plugin,
* to complete.
*
* On failure the error message returned will usually only be shown on the
- * console, but it may also be retained on the #GsApp object.
- * The UI code can retrieve the error using gs_app_get_last_error().
+ * console, but they can also be retrieved using gs_plugin_loader_get_events().
*
* NOTE: Once the action is complete, the plugin must set the new state of @app
* to %AS_APP_STATE_INSTALLED.
@@ -543,8 +542,7 @@ gboolean gs_plugin_app_install (GsPlugin *plugin,
* to complete.
*
* On failure the error message returned will usually only be shown on the
- * console, but it may also be retained on the #GsApp object.
- * The UI code can retrieve the error using gs_app_get_last_error().
+ * console, but they can also be retrieved using gs_plugin_loader_get_events().
*
* NOTE: Once the action is complete, the plugin must set the new state of @app
* to %AS_APP_STATE_AVAILABLE or %AS_APP_STATE_UNKNOWN if not known.
@@ -591,8 +589,7 @@ gboolean gs_plugin_app_set_rating (GsPlugin *plugin,
* to complete.
*
* On failure the error message returned will usually only be shown on the
- * console, but it may also be retained on the #GsApp object.
- * The UI code can retrieve the error using gs_app_get_last_error().
+ * console, but they can also be retrieved using gs_plugin_loader_get_events().
*
* NOTE: Once the action is complete, the plugin must set the new state of @app
* to %AS_APP_STATE_INSTALLED or %AS_APP_STATE_UNKNOWN if not known.
diff --git a/src/gs-plugin.c b/src/gs-plugin.c
index 04e75db..3dee8e5 100644
--- a/src/gs-plugin.c
+++ b/src/gs-plugin.c
@@ -90,6 +90,7 @@ enum {
SIGNAL_UPDATES_CHANGED,
SIGNAL_STATUS_CHANGED,
SIGNAL_RELOAD,
+ SIGNAL_ADD_EVENT,
SIGNAL_LAST
};
@@ -850,6 +851,7 @@ typedef struct {
GsPlugin *plugin;
GsApp *app;
GsPluginStatus status;
+ GsPluginEvent *event;
guint percentage;
} GsPluginStatusHelper;
@@ -891,6 +893,38 @@ gs_plugin_status_update (GsPlugin *plugin, GsApp *app, GsPluginStatus status)
}
static gboolean
+gs_plugin_add_event_cb (gpointer user_data)
+{
+ GsPluginStatusHelper *helper = (GsPluginStatusHelper *) user_data;
+ g_signal_emit (helper->plugin,
+ signals[SIGNAL_ADD_EVENT], 0,
+ helper->event);
+ g_object_unref (helper->event);
+ g_slice_free (GsPluginStatusHelper, helper);
+ return FALSE;
+}
+
+/**
+ * gs_plugin_add_event:
+ * @plugin: a #GsPlugin
+ * @event: a #GsPluginEvent
+ *
+ * Provide a way for plugins to tell the UI layer about events that may require
+ * displaying to the user. Plugins should not assume that a specific event is
+ * actually shown to the user as it may be ignored automatically.
+ *
+ * Since: 3.22
+ **/
+void
+gs_plugin_add_event (GsPlugin *plugin, GsPluginEvent *event)
+{
+ GsPluginStatusHelper *helper = g_slice_new0 (GsPluginStatusHelper);
+ helper->plugin = plugin;
+ helper->event = g_object_ref (event);
+ g_idle_add (gs_plugin_add_event_cb, helper);
+}
+
+static gboolean
gs_plugin_app_launch_cb (gpointer user_data)
{
GAppInfo *appinfo = (GAppInfo *) user_data;
@@ -1362,6 +1396,13 @@ gs_plugin_class_init (GsPluginClass *klass)
NULL, NULL, g_cclosure_marshal_generic,
G_TYPE_NONE, 2, GS_TYPE_APP, G_TYPE_UINT);
+ signals [SIGNAL_ADD_EVENT] =
+ g_signal_new ("add-event",
+ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GsPluginClass, add_event),
+ NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GS_TYPE_PLUGIN_EVENT);
+
signals [SIGNAL_RELOAD] =
g_signal_new ("reload",
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 7c34db9..0c098b1 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -32,6 +32,7 @@
#include "gs-app-list.h"
#include "gs-auth.h"
#include "gs-category.h"
+#include "gs-plugin-event.h"
G_BEGIN_DECLS
@@ -47,7 +48,9 @@ struct _GsPluginClass
GsApp *app,
guint status);
void (*reload) (GsPlugin *plugin);
- gpointer padding[28];
+ void (*add_event) (GsPlugin *plugin,
+ GsPluginEvent *event);
+ gpointer padding[27];
};
typedef struct GsPluginData GsPluginData;
@@ -293,6 +296,8 @@ gboolean gs_plugin_app_launch (GsPlugin *plugin,
void gs_plugin_updates_changed (GsPlugin *plugin);
void gs_plugin_reload (GsPlugin *plugin);
const gchar *gs_plugin_status_to_string (GsPluginStatus status);
+void gs_plugin_add_event (GsPlugin *plugin,
+ GsPluginEvent *event);
G_END_DECLS
diff --git a/src/gs-self-test.c b/src/gs-self-test.c
index 4223739..e6e26c4 100644
--- a/src/gs-self-test.c
+++ b/src/gs-self-test.c
@@ -352,10 +352,15 @@ gs_plugin_loader_install_func (GsPluginLoader *plugin_loader)
static void
gs_plugin_loader_error_func (GsPluginLoader *plugin_loader)
{
+ GsPluginEvent *event;
+ const GError *app_error;
gboolean ret;
- g_autoptr(GsApp) app = NULL;
g_autoptr(GError) error = NULL;
- GError *last_error;
+ g_autoptr(GPtrArray) events = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ /* remove previous errors */
+ gs_plugin_loader_remove_events (plugin_loader);
/* suppress this */
g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
@@ -375,9 +380,25 @@ gs_plugin_loader_error_func (GsPluginLoader *plugin_loader)
/* ensure we failed the plugin action */
g_test_assert_expected_messages ();
- /* retrieve the error from the application */
- last_error = gs_app_get_last_error (app);
- g_assert_error (last_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NO_NETWORK);
+ /* get event by app-id */
+ event = gs_plugin_loader_get_event_by_id (plugin_loader,
+ "*/*/*/*/chiron.desktop/*");
+ g_assert (event != NULL);
+ g_assert (gs_plugin_event_get_app (event) == app);
+
+ /* get last active event */
+ event = gs_plugin_loader_get_event_default (plugin_loader);
+ g_assert (event != NULL);
+ g_assert (gs_plugin_event_get_app (event) == app);
+
+ /* check all the events */
+ events = gs_plugin_loader_get_events (plugin_loader);
+ g_assert_cmpint (events->len, ==, 1);
+ event = g_ptr_array_index (events, 0);
+ g_assert (gs_plugin_event_get_app (event) == app);
+ app_error = gs_plugin_event_get_gerror (event);
+ g_assert (app_error != NULL);
+ g_assert_error (app_error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_NO_NETWORK);
}
static void
diff --git a/src/gs-shell-updates.c b/src/gs-shell-updates.c
index 0dd03f7..8b2d28b 100644
--- a/src/gs-shell-updates.c
+++ b/src/gs-shell-updates.c
@@ -1001,7 +1001,6 @@ upgrade_download_finished_cb (GObject *source,
gpointer user_data)
{
GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source);
- GError *last_error;
g_autoptr(GError) error = NULL;
g_autoptr(GsPageHelper) helper = (GsPageHelper *) user_data;
@@ -1010,18 +1009,6 @@ upgrade_download_finished_cb (GObject *source,
return;
g_warning ("failed to upgrade-download: %s", error->message);
}
-
- last_error = gs_app_get_last_error (helper->app);
- if (last_error != NULL) {
- g_warning ("failed to upgrade-download %s: %s",
- gs_app_get_id (helper->app),
- last_error->message);
- gs_app_notify_failed_modal (helper->app,
- gs_shell_get_window (helper->self->shell),
- GS_PLUGIN_LOADER_ACTION_UPGRADE_DOWNLOAD,
- last_error);
- return;
- }
}
static void
diff --git a/src/gs-shell.c b/src/gs-shell.c
index cfd23b7..426e0b4 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -413,6 +413,12 @@ save_back_entry (GsShell *shell)
}
static void
+gs_shell_plugin_events_sources_cb (GtkWidget *widget, GsShell *shell)
+{
+ gs_shell_show_sources (shell);
+}
+
+static void
gs_shell_back_button_cb (GtkWidget *widget, GsShell *shell)
{
GsShellPrivate *priv = gs_shell_get_instance_private (shell);
@@ -614,6 +620,119 @@ gs_shell_monitor_permission (GsShell *shell)
G_CALLBACK (on_permission_changed), shell);
}
+static void
+gs_shell_events_show (GsShell *shell, GsPluginEvent *event)
+{
+ GsApp *app = gs_plugin_event_get_app (event);
+ GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+ GtkWidget *widget;
+ const GError *error;
+ const gchar *tmp;
+ g_autoptr(GString) header = g_string_new ("");
+
+ /* set type */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "infobar_events"));
+ if (gs_plugin_event_has_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING)) {
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (widget),
+ GTK_MESSAGE_WARNING);
+ } else {
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (widget),
+ GTK_MESSAGE_INFO);
+ }
+ gtk_widget_set_visible (widget, TRUE);
+
+ /* custom error messages */
+ if (app != NULL &&
+ gs_app_get_kind (app) == AS_APP_KIND_SOURCE &&
+ gs_app_get_state (app) == AS_APP_STATE_UNAVAILABLE) {
+ tmp = gs_app_get_name (app);
+ if (tmp == NULL)
+ tmp = gs_app_get_origin_ui (app);
+ if (tmp == NULL)
+ tmp = gs_app_get_origin_hostname (app);
+ if (tmp == NULL)
+ tmp = gs_app_get_origin (app);
+ if (tmp == NULL)
+ tmp = "invalid";
+ /* TRANSLATORS: source refers to a remote or repo */
+ g_string_append_printf (header, _("Failed to load source ā%sā"), tmp);
+ } else {
+ /* TRANSLATORS: fallback case for a plugin event */
+ g_string_append (header, "Important notice");
+ }
+
+ /* sources button */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_sources"));
+ gtk_widget_set_visible (widget,
+ app != NULL &&
+ gs_app_get_kind (app) == AS_APP_KIND_SOURCE);
+
+ /* set header */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_events_title"));
+ gtk_label_set_label (GTK_LABEL (widget), header->str);
+ gtk_widget_set_visible (widget, header->len > 0);
+
+ /* fill in detail */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_events"));
+ error = gs_plugin_event_get_gerror (event);
+ if (error != NULL)
+ gtk_label_set_label (GTK_LABEL (widget), error->message);
+ gtk_widget_set_visible (widget, error != NULL);
+}
+
+static void
+gs_shell_events_rescan (GsShell *shell)
+{
+ GsPluginEvent *event;
+ GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+ GtkWidget *widget;
+ guint i;
+
+ /* find the first active event and show it */
+ event = gs_plugin_loader_get_event_default (priv->plugin_loader);
+ if (event != NULL) {
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_VISIBLE);
+ gs_shell_events_show (shell, event);
+ return;
+ }
+
+ /* nothing to show */
+ g_debug ("no events to show");
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "infobar_events"));
+ gtk_widget_set_visible (widget, FALSE);
+}
+
+static void
+gs_shell_events_notify_cb (GsPluginLoader *plugin_loader,
+ GParamSpec *pspec,
+ GsShell *shell)
+{
+ gs_shell_events_rescan (shell);
+}
+
+static void
+gs_shell_plugin_event_response_cb (GtkInfoBar *info_bar,
+ gint response_id,
+ GsShell *shell)
+{
+ GPtrArray *events;
+ GsShellPrivate *priv = gs_shell_get_instance_private (shell);
+ guint i;
+
+ /* mark any events currently showing as invalid */
+ events = gs_plugin_loader_get_events (priv->plugin_loader);
+ for (i = 0; i < events->len; i++) {
+ GsPluginEvent *event = g_ptr_array_index (events, i);
+ if (gs_plugin_event_has_flag (event, GS_PLUGIN_EVENT_FLAG_VISIBLE)) {
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_INVALID);
+ gs_plugin_event_remove_flag (event, GS_PLUGIN_EVENT_FLAG_VISIBLE);
+ }
+ }
+
+ /* show the next event */
+ gs_shell_events_rescan (shell);
+}
+
void
gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *cancellable)
{
@@ -625,6 +744,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
priv->plugin_loader = g_object_ref (plugin_loader);
g_signal_connect (priv->plugin_loader, "reload",
G_CALLBACK (gs_shell_reload_cb), shell);
+ g_signal_connect_object (priv->plugin_loader, "notify::events",
+ G_CALLBACK (gs_shell_events_notify_cb),
+ shell, 0);
priv->cancellable = g_object_ref (cancellable);
gs_shell_monitor_permission (shell);
@@ -683,6 +805,14 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can
g_signal_connect (widget, "clicked",
G_CALLBACK (gs_shell_overview_button_cb), shell);
+ /* set up infobar */
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "infobar_events"));
+ g_signal_connect (widget, "response",
+ G_CALLBACK (gs_shell_plugin_event_response_cb), shell);
+ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_events_sources"));
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (gs_shell_plugin_events_sources_cb), shell);
+
priv->shell_overview = GS_SHELL_OVERVIEW (gtk_builder_get_object (priv->builder, "shell_overview"));
gs_shell_overview_setup (priv->shell_overview,
shell,
diff --git a/src/plugins/gs-plugin-dummy.c b/src/plugins/gs-plugin-dummy.c
index d2ded6f..44eafac 100644
--- a/src/plugins/gs-plugin-dummy.c
+++ b/src/plugins/gs-plugin-dummy.c
@@ -60,6 +60,33 @@ gs_plugin_initialize (GsPlugin *plugin)
gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_CONFLICTS, "odrs");
}
+static void
+gs_plugin_dummy_add_event (GsPlugin *plugin)
+{
+ g_autoptr(GsPluginEvent) event = gs_plugin_event_new ();
+ g_autoptr(GsApp) source = gs_app_new ("dummy");
+
+ /* create source */
+ gs_app_set_kind (source, AS_APP_KIND_SOURCE);
+ gs_app_set_origin_hostname (source, "packages.dummy.org");
+ gs_app_set_name (source, GS_APP_QUALITY_NORMAL, "Dummy");
+ gs_app_set_state (source, AS_APP_STATE_UNAVAILABLE);
+
+ /* create and add event */
+ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING);
+ gs_plugin_event_set_app (event, source);
+ gs_plugin_event_set_error (event, GS_PLUGIN_ERROR_NO_NETWORK,
+ "Metadata download failed: Could not reach destination.");
+ gs_plugin_add_event (plugin, event);
+}
+
+gboolean
+gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
+{
+ gs_plugin_dummy_add_event (plugin);
+ return TRUE;
+}
+
void
gs_plugin_destroy (GsPlugin *plugin)
{
@@ -579,6 +606,10 @@ gs_plugin_refresh (GsPlugin *plugin,
if (flags & GS_PLUGIN_REFRESH_FLAGS_PAYLOAD)
delay_ms += 5000;
+ /* show warning */
+ if (flags & GS_PLUGIN_REFRESH_FLAGS_METADATA)
+ gs_plugin_dummy_add_event (plugin);
+
/* do delay */
return gs_plugin_dummy_delay (plugin, app, delay_ms, cancellable, error);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]