view pkui-icon.c @ 19:38b37e4a4d63

added a button for installing updates to the notification
author Guido Berhoerster <guido@berhoerster.name>
date Thu, 20 Oct 2011 13:37:50 +0200
parents 9537882d759f
children bae8a7e20382
line wrap: on
line source

/*
 * (C) 2011 Guido Berhoerster <gber@opensuse.org>
 *
 * 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 <glib/gi18n.h>
#include <libnotify/notify.h>
#include "pkui-backend.h"
#include "pkui-icon.h"

G_DEFINE_TYPE(PkuiIcon, pkui_icon, G_TYPE_OBJECT)

#define	UPDATE_VIEWER_COMMAND		"/usr/bin/gpk-update-viewer"

#define	PKUI_ICON_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
					    PKUI_TYPE_ICON, PkuiIconPrivate))

struct _PkuiIconPrivate
{
	PkuiBackend	*backend;

	GtkStatusIcon	*status_icon;
	GtkWidget	*status_icon_popup_menu;
	NotifyNotification *notification;
};

static GtkWidget* icon_popup_menu_create(PkuiIcon *self);
static void icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
    guint activate_time, gpointer user_data);
static void icon_activated(GtkStatusIcon *status_icon, gpointer user_data);
static void hide_notification(PkuiIcon *self);
static void backend_state_changed(PkuiBackend *backend, gpointer user_data);
static void update_viewer_exited(GPid pid, gint status, gpointer user_data);

static void
pkui_icon_finalize(GObject *gobject)
{
	PkuiIcon	*self = PKUI_ICON(gobject);

	gtk_widget_destroy(self->priv->status_icon_popup_menu);
	g_object_unref(self->priv->status_icon_popup_menu);
	g_object_unref(self->priv->status_icon);
	if (self->priv->notification != NULL) {
		notify_notification_close(self->priv->notification, NULL);
		g_object_unref(self->priv->notification);
	}
	g_object_unref(self->priv->backend);

	G_OBJECT_CLASS(pkui_icon_parent_class)->finalize(gobject);
}

static void
pkui_icon_class_init(PkuiIconClass *klass)
{
	GObjectClass	*gobject_class = G_OBJECT_CLASS(klass);

	gobject_class->finalize = pkui_icon_finalize;

	g_type_class_add_private(klass, sizeof (PkuiIconPrivate));
}

static void
pkui_icon_init(PkuiIcon *self)
{
	self->priv = PKUI_ICON_GET_PRIVATE(self);

	gtk_window_set_default_icon_name("system-software-update");

	self->priv->status_icon_popup_menu = icon_popup_menu_create(self);
	g_object_ref(self->priv->status_icon_popup_menu);
	g_object_ref_sink(GTK_OBJECT(self->priv->status_icon_popup_menu));

	self->priv->status_icon = gtk_status_icon_new();
	gtk_status_icon_set_title(self->priv->status_icon,
	    _("Software Updates"));
	gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
	g_signal_connect(G_OBJECT(self->priv->status_icon), "activate",
	    G_CALLBACK(icon_activated), self);
	g_signal_connect(G_OBJECT(self->priv->status_icon), "popup-menu",
	    G_CALLBACK(icon_popup_menu_popup), self);

	self->priv->notification = NULL;

	self->priv->backend = NULL;
}

static void
exec_update_viewer(PkuiIcon *self)
{
	static const gchar *argv[] = { UPDATE_VIEWER_COMMAND, NULL };
	GPid		pid;
	gboolean	retval;

	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));

	g_debug("executing " UPDATE_VIEWER_COMMAND);

	retval = g_spawn_async(NULL, (gchar **)argv, NULL,
	    G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
	if (!retval) {
		g_warning("Could not execute" UPDATE_VIEWER_COMMAND);
		return;
	}
	g_child_watch_add(pid, (GChildWatchFunc)update_viewer_exited, self);

	pkui_backend_set_inhibit_check(self->priv->backend, TRUE);
	hide_notification(self);
}

static void
about_dialog_show(GtkMenuItem *item, gpointer user_data)
{
	static const gchar *copyright = "Copyright \xc2\xa9 2011 Guido "
	    "Berhoerster\nCopyright \xc2\xa9 2011 Pavol Rusnak\n";
	static const gchar *authors[3] = {
	    "Guido Berhoerster <gber@opensuse.org>",
	    "Pavol Rusnak <stick@gk2.sk>",
	    NULL
	};
	static const gchar *documenters[2] = {
	    "Guido Berhoerster <gber@opensuse.org>",
	    NULL
	};
	static const gchar *license =
	    "Licensed under the GNU General Public License Version 2\n\nThis "
	    "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.\n\nThis 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.\n\nYou 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.";
	const gchar *translators = _("translators");

	if (strcmp(translators, "translators") == 0)
	    translators = NULL;

	gtk_show_about_dialog (NULL, "version", "0.1", "copyright", copyright,
	    "authors", authors, "documenters", documenters,
	    "translator-credits", translators, "license", license,
	    "wrap-license", TRUE, NULL);
}

static void
backend_check_now(GtkMenuItem *menu_item, gpointer user_data)
{
	PkuiIcon	*self = PKUI_ICON(user_data);

	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));

	pkui_backend_check_now(self->priv->backend);
}

static GtkWidget*
icon_popup_menu_create(PkuiIcon *self)
{
	GtkWidget	*popup_menu = gtk_menu_new();
	GtkWidget	*item;
	GtkWidget	*image;

	item = gtk_image_menu_item_new_with_mnemonic(_("_Check for Updates"));
	image = gtk_image_new_from_icon_name(GTK_STOCK_REFRESH,
	    GTK_ICON_SIZE_MENU);
	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
	gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), item);
	g_signal_connect(G_OBJECT(item), "activate",
	    G_CALLBACK(backend_check_now), self);

	item = gtk_image_menu_item_new_with_mnemonic(_("_About"));
	image = gtk_image_new_from_icon_name(GTK_STOCK_ABOUT,
	    GTK_ICON_SIZE_MENU);
	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
	gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), item);
	g_signal_connect(G_OBJECT(item), "activate",
	    G_CALLBACK(about_dialog_show), self);

	gtk_widget_show_all(GTK_WIDGET(popup_menu));

	return (popup_menu);
}

static void
icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
    guint activate_time, gpointer user_data)
{
	PkuiIcon *self = PKUI_ICON(user_data);

	gtk_menu_popup(GTK_MENU(self->priv->status_icon_popup_menu), NULL, NULL,
	    NULL, NULL, button, activate_time);
}

static void
notification_handle_action(NotifyNotification *notification, gchar *action,
    gpointer user_data)
{
	PkuiIcon *self = PKUI_ICON(user_data);

	if (strcmp(action, "install-updates") == 0)
		exec_update_viewer(self);
}

static void
update_notification(PkuiIcon *self, guint updates_normal,
    guint updates_important)
{
	gchar *message;
	gchar *title = updates_important ?
	    ngettext("Important Software Update", "Important Software Updates",
	    updates_important + updates_normal) :
	    ngettext("Software Update", "Software Updates", updates_important +
	    updates_normal);
	gchar *icon = updates_important ? "software-update-urgent" :
	    "software-update-available";

	if (updates_important > 0)
		if (updates_normal > 0)
			message = g_strdup_printf(ngettext("There are %d "
			    "software updates available, %d of them is "
			    "important.", "There are %d software updates "
			    "available, %d of them are important.",
			    updates_important),
			    updates_normal + updates_important,
			    updates_important);
		else
			message = g_strdup_printf(ngettext("There is an "
			    "important software update available.", "There are "
			    "%d important software updates available.",
			    updates_important), updates_important);
	else
		message = g_strdup_printf(ngettext("There is a software update "
		    "available.", "There are %d software updates available.",
		    updates_normal), updates_normal);

	gtk_status_icon_set_tooltip(self->priv->status_icon, message);
	gtk_status_icon_set_from_icon_name(self->priv->status_icon, icon);
	gtk_status_icon_set_visible(self->priv->status_icon, TRUE);

	if (self->priv->notification == NULL) {
		self->priv->notification = notify_notification_new(title,
		    message, icon
#if (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
		    , NULL
#endif
		    );
		notify_notification_add_action(self->priv->notification,
		     "install-updates", ngettext("Install Update",
		     "Install Updates", updates_normal + updates_important),
		     notification_handle_action, self, NULL);
	} else
		notify_notification_update(self->priv->notification, title,
		    message, icon);

	notify_notification_set_timeout(self->priv->notification,
	    NOTIFY_EXPIRES_NEVER);
	notify_notification_set_urgency(self->priv->notification,
	    updates_important ? NOTIFY_URGENCY_CRITICAL :
	    NOTIFY_URGENCY_NORMAL);
	notify_notification_show(self->priv->notification, NULL);

	g_free(message);
}

static void
hide_notification(PkuiIcon *self)
{
	gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
	notify_notification_close(self->priv->notification, NULL);
}


static void
backend_state_changed(PkuiBackend *backend, gpointer user_data)
{
	PkuiIcon	*self = PKUI_ICON(user_data);
	guint		updates_normal;
	guint		updates_important;

	g_return_if_fail(PKUI_IS_BACKEND(backend));

	updates_normal = pkui_backend_get_updates_normal(backend);
	updates_important = pkui_backend_get_updates_important(backend);
	if (updates_normal > 0 || updates_important > 0)
		update_notification(self, updates_normal, updates_important);
	else if (updates_normal + updates_important == 0)
		hide_notification(self);
}

static void
update_viewer_exited(GPid pid, gint status, gpointer user_data)
{
	PkuiIcon	*self = PKUI_ICON(user_data);

	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));

	g_spawn_close_pid(pid);

	pkui_backend_check_now(self->priv->backend);
	pkui_backend_set_inhibit_check(self->priv->backend, FALSE);
}

static void
icon_activated(GtkStatusIcon *status_icon, gpointer user_data)
{
	PkuiIcon	*self = PKUI_ICON(user_data);

	exec_update_viewer(self);
}

PkuiIcon *
pkui_icon_new(guint startup_delay, guint check_interval)
{
	PkuiIcon	*icon = g_object_new(PKUI_TYPE_ICON, NULL);

	icon->priv->backend = pkui_backend_new(startup_delay, check_interval);
	g_signal_connect(icon->priv->backend, "state-changed",
	    G_CALLBACK(backend_state_changed), icon);

	return (icon);
}