projects/pk-update-icon

changeset 14:64f05992d8ec

GObject-based rewrite
use asynchronous packagekit-glib API
use persistent menu widget and notification object
update existing notification when new updates become available, close it when no updates are available
show status icon when updates are available, hide it when no updates are available
hide icon when gpk-update-viewer is executed, check for updates when gpk-update-viewer exits
introduce a startup delay before the first check for updates is made
add context menu item to manually trigger a check for updates
remove context menu item for quitting pk-update-icon
author Guido Berhoerster <guido@berhoerster.name>
date Thu Oct 20 08:19:22 2011 +0200 (2011-10-20)
parents dca97330d81e
children c7eac2574c76
files Makefile design.txt main.c main.h notify.c notify.h packagekit.c packagekit.h pkui-backend.c pkui-backend.h pkui-icon.c pkui-icon.h po/POTFILES.in po/de.po
line diff
     1.1 --- a/Makefile	Tue Oct 11 17:36:32 2011 +0200
     1.2 +++ b/Makefile	Thu Oct 20 08:19:22 2011 +0200
     1.3 @@ -18,7 +18,7 @@
     1.4  PACKAGE =	pk-update-icon
     1.5  APP_NAME =	org.opensuse.pk-update-icon
     1.6  VERSION =	0.1
     1.7 -OBJS =		main.o notify.o packagekit.o
     1.8 +OBJS =		main.o pkui-icon.o pkui-backend.o
     1.9  AUTOSTART_FILE =	$(PACKAGE).desktop
    1.10  MOFILES :=	$(patsubst %.po,%.mo,$(wildcard po/*.po))
    1.11  POTFILE =	po/$(PACKAGE).pot
     2.1 --- a/design.txt	Tue Oct 11 17:36:32 2011 +0200
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,64 +0,0 @@
     2.4 -Design
     2.5 -======
     2.6 -
     2.7 -Objects:
     2.8 -OUIApp(GtkApplication)
     2.9 -    - Icon *icon
    2.10 -    - Backend *backend
    2.11 -    - GSettings *settings
    2.12 -        * bind preferences to settings above
    2.13 -        + check_frequency
    2.14 -        + check_mobile_connection
    2.15 -        + startup_timeout
    2.16 -        + notify_package
    2.17 -        + notify_patch_critical
    2.18 -        + notify_patch_recommended
    2.19 -        + notify_patch_optional
    2.20 -    - PreferencesDialog *preferences_dialog
    2.21 -
    2.22 -startup:
    2.23 -    unique
    2.24 -    one-shot timer
    2.25 -    regular timer
    2.26 -    hide
    2.27 -one-shot timer: check update
    2.28 -timer: 24 h check update
    2.29 -check update:
    2.30 -    if battery critical:
    2.31 -        set timer +1h
    2.32 -    if not online:
    2.33 -        set timer +1h
    2.34 -    if on mobile and no allowed:
    2.35 -        set timer +1h
    2.36 -    run /usr/sbin/zypp-refresh-wrapper
    2.37 -    run zypper --xmlout list-updates --type patch
    2.38 -    run zypper --xmlout list-updates --type package
    2.39 -    parse XML output
    2.40 -    merge package and patch updates?
    2.41 -    if updates:
    2.42 -        select icon based on type of update
    2.43 -        display icon and tooltip with number of updates
    2.44 -    else:
    2.45 -        hide
    2.46 -left click on icon:
    2.47 -    start yast2 online_update
    2.48 -right click:
    2.49 -    preferences
    2.50 -    about
    2.51 -preferences:
    2.52 -    interval (hourly/daily/weekly/never)
    2.53 -    check on mobile internet
    2.54 -
    2.55 -
    2.56 -TODO:
    2.57 -parse xml
    2.58 -DBus check
    2.59 -check low power with upower
    2.60 -check connection with NetworkManager
    2.61 -unique application
    2.62 -see packagekit
    2.63 -
    2.64 -
    2.65 -Defer to later:
    2.66 -NM online and mobile check
    2.67 -UPower battery check
     3.1 --- a/main.c	Tue Oct 11 17:36:32 2011 +0200
     3.2 +++ b/main.c	Thu Oct 20 08:19:22 2011 +0200
     3.3 @@ -1,5 +1,4 @@
     3.4  /*
     3.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     3.6   * Copyright (C) 2011 Guido Berhoerster <gber@opensuse.org>
     3.7   *
     3.8   * Licensed under the GNU General Public License Version 2
     3.9 @@ -19,103 +18,47 @@
    3.10   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    3.11   */
    3.12  
    3.13 -#include "main.h"
    3.14 -#include "notify.h"
    3.15 -#include "packagekit.h"
    3.16  #include <locale.h>
    3.17  #include <glib/gi18n.h>
    3.18  #include <gtk/gtk.h>
    3.19  #include <unique/unique.h>
    3.20 +#include <libnotify/notify.h>
    3.21 +#include "pkui-icon.h"
    3.22  
    3.23 -struct UpdatesInfo info;
    3.24 -
    3.25 -GtkStatusIcon *tray_icon;
    3.26 -
    3.27 -void tray_icon_on_click(GtkStatusIcon *status_icon, gpointer user_data)
    3.28 +int
    3.29 +main(int argc, char **argv)
    3.30  {
    3.31 -	char *argv[2] = { "gpk-update-viewer", NULL };
    3.32 -	g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
    3.33 -}
    3.34 -
    3.35 -static void tray_menu_destroy(GtkWidget *menu, gpointer userdata)
    3.36 -{
    3.37 -	gtk_widget_destroy(menu);
    3.38 -	g_object_unref(menu);
    3.39 -}
    3.40 -
    3.41 -void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
    3.42 -{
    3.43 -	GtkWidget *item;
    3.44 -	GtkWidget *menu = gtk_menu_new();
    3.45 -	item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
    3.46 -	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    3.47 -	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_main_quit), user_data);
    3.48 -	gtk_widget_show(item);
    3.49 -	g_object_ref(menu);
    3.50 -	g_object_ref_sink(menu);
    3.51 -	g_object_unref(menu);
    3.52 -	g_signal_connect(G_OBJECT(menu), "selection-done", G_CALLBACK(tray_menu_destroy), NULL);
    3.53 -	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, user_data, button, activate_time);
    3.54 -	gtk_widget_show_all(GTK_WIDGET(menu));
    3.55 -}
    3.56 -
    3.57 -static GtkStatusIcon *create_tray_icon()
    3.58 -{
    3.59 -	tray_icon = gtk_status_icon_new();
    3.60 -	g_signal_connect(G_OBJECT(tray_icon), "activate", G_CALLBACK(tray_icon_on_click), NULL);
    3.61 -	g_signal_connect(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(tray_icon_on_menu), NULL);
    3.62 -	gtk_status_icon_set_from_icon_name(tray_icon, "system-software-update");
    3.63 -	gtk_status_icon_set_title(tray_icon, _("Software Update(s)"));
    3.64 -	gtk_status_icon_set_visible(tray_icon, TRUE);
    3.65 -	return tray_icon;
    3.66 -}
    3.67 -
    3.68 -gboolean periodic_check(gpointer user_data)
    3.69 -{
    3.70 -	query_packagekit(&info);
    3.71 -	if (info.critical > 0) {
    3.72 -		gtk_status_icon_set_from_icon_name(tray_icon, "software-update-urgent");
    3.73 -		gtk_status_icon_set_tooltip_text(tray_icon, notify_text(&info));
    3.74 -	} else
    3.75 -	if (info.normal > 0) {
    3.76 -		gtk_status_icon_set_from_icon_name(tray_icon, "software-update-available");
    3.77 -		gtk_status_icon_set_tooltip_text(tray_icon, notify_text(&info));
    3.78 -	}
    3.79 -	return TRUE;
    3.80 -}
    3.81 -
    3.82 -int main(int argc, char **argv)
    3.83 -{
    3.84 -	GtkStatusIcon *tray_icon;
    3.85 -	UniqueApp *app;
    3.86 -	int exitval = 0;
    3.87 +	PkuiIcon	*icon;
    3.88 +	UniqueApp	*app;
    3.89 +	int		exitval = 0;
    3.90  
    3.91  	setlocale(LC_ALL, "");
    3.92 +
    3.93  	bindtextdomain(PACKAGE, LOCALEDIR);
    3.94  	bind_textdomain_codeset(PACKAGE, "UTF-8");
    3.95  	textdomain(PACKAGE);
    3.96  
    3.97  	gtk_init(&argc, &argv);
    3.98 +
    3.99  	app = unique_app_new(APP_NAME, NULL);
   3.100  	if (unique_app_is_running(app)) {
   3.101 -		g_printerr("Another instance of pk-update-icon is already running. Exiting.\n");
   3.102 +		g_printerr("Another instance of pk-update-icon is already "
   3.103 +		    "running. Exiting.\n");
   3.104  		exitval = 1;
   3.105  		goto out;
   3.106  	}
   3.107 -	tray_icon = create_tray_icon();
   3.108 -	init_notify();
   3.109  
   3.110 -	periodic_check(NULL);
   3.111 -	send_notify(&info);
   3.112 -	// update tray icon and tooltip every 2 hours
   3.113 -	g_timeout_add_seconds(2*3600, periodic_check, NULL);
   3.114 +	notify_init(PACKAGE);
   3.115 +
   3.116 +	icon = pkui_icon_new(10, 2 * 3600);
   3.117  
   3.118  	gtk_main();
   3.119  
   3.120 -	g_object_unref(tray_icon);
   3.121 -
   3.122 +	g_object_unref(icon);
   3.123  out:
   3.124  	g_object_unref(app);
   3.125 +	if (notify_is_initted())
   3.126 +		notify_uninit ();
   3.127  
   3.128 -	return exitval;
   3.129 +	return (exitval);
   3.130  }
     4.1 --- a/main.h	Tue Oct 11 17:36:32 2011 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,30 +0,0 @@
     4.4 -/*
     4.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     4.6 - * Copyright (C) 2011 Guido Berhoerster <gber@opensuse.org>
     4.7 - *
     4.8 - * Licensed under the GNU General Public License Version 2
     4.9 - *
    4.10 - * This program is free software; you can redistribute it and/or modify
    4.11 - * it under the terms of the GNU General Public License as published by
    4.12 - * the Free Software Foundation; either version 2 of the License, or
    4.13 - * (at your option) any later version.
    4.14 - *
    4.15 - * This program is distributed in the hope that it will be useful,
    4.16 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.17 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.18 - * GNU General Public License for more details.
    4.19 - *
    4.20 - * You should have received a copy of the GNU General Public License
    4.21 - * along with this program; if not, write to the Free Software
    4.22 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    4.23 - */
    4.24 -
    4.25 -#ifndef MAIN_H
    4.26 -#define MAIN_H
    4.27 -
    4.28 -struct UpdatesInfo {
    4.29 -	int normal;
    4.30 -	int critical;
    4.31 -};
    4.32 -
    4.33 -#endif
     5.1 --- a/notify.c	Tue Oct 11 17:36:32 2011 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,54 +0,0 @@
     5.4 -/*
     5.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     5.6 - * Copyright (C) 2011 Guido Berhoerster <gber@opensuse.org>
     5.7 - *
     5.8 - * Licensed under the GNU General Public License Version 2
     5.9 - *
    5.10 - * This program is free software; you can redistribute it and/or modify
    5.11 - * it under the terms of the GNU General Public License as published by
    5.12 - * the Free Software Foundation; either version 2 of the License, or
    5.13 - * (at your option) any later version.
    5.14 - *
    5.15 - * This program is distributed in the hope that it will be useful,
    5.16 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.17 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.18 - * GNU General Public License for more details.
    5.19 - *
    5.20 - * You should have received a copy of the GNU General Public License
    5.21 - * along with this program; if not, write to the Free Software
    5.22 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    5.23 - */
    5.24 -
    5.25 -#include "notify.h"
    5.26 -
    5.27 -void init_notify()
    5.28 -{
    5.29 -	notify_init(PACKAGE);
    5.30 -}
    5.31 -
    5.32 -void send_notify(struct UpdatesInfo *info)
    5.33 -{
    5.34 -	NotifyNotification *ntfy;
    5.35 -	ntfy = notify_notification_new(
    5.36 -		_("Software Update(s)"),
    5.37 -		_(notify_text(info)),
    5.38 -		info->critical > 0 ? "software-update-urgent" : "software-update-available"
    5.39 -#if (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
    5.40 -		, NULL
    5.41 -#endif
    5.42 -	);
    5.43 -	notify_notification_set_timeout(ntfy, 3000);
    5.44 -	notify_notification_set_urgency(ntfy, info->critical > 0 ? NOTIFY_URGENCY_CRITICAL : NOTIFY_URGENCY_NORMAL);
    5.45 -	notify_notification_show(ntfy, NULL);
    5.46 -}
    5.47 -
    5.48 -const char *notify_text(struct UpdatesInfo *info)
    5.49 -{
    5.50 -	static char buf[128];
    5.51 -	if (info->critical > 0) {
    5.52 -		snprintf(buf, sizeof(buf), _("There are %d software updates available, %d of them critical."), info->normal + info->critical, info->critical);
    5.53 -	} else {
    5.54 -		snprintf(buf, sizeof(buf), _("There are %d software updates available."), info->normal);
    5.55 -	}
    5.56 -	return buf;
    5.57 -}
     6.1 --- a/notify.h	Tue Oct 11 17:36:32 2011 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,34 +0,0 @@
     6.4 -/*
     6.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     6.6 - * Copyright (C) 2011 Guido Berhoerster <gber@opensuse.org>
     6.7 - *
     6.8 - * Licensed under the GNU General Public License Version 2
     6.9 - *
    6.10 - * This program is free software; you can redistribute it and/or modify
    6.11 - * it under the terms of the GNU General Public License as published by
    6.12 - * the Free Software Foundation; either version 2 of the License, or
    6.13 - * (at your option) any later version.
    6.14 - *
    6.15 - * This program is distributed in the hope that it will be useful,
    6.16 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.17 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.18 - * GNU General Public License for more details.
    6.19 - *
    6.20 - * You should have received a copy of the GNU General Public License
    6.21 - * along with this program; if not, write to the Free Software
    6.22 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    6.23 - */
    6.24 -
    6.25 -#ifndef NOTIFY_H
    6.26 -#define NOTIFY_H
    6.27 -
    6.28 -#include <glib.h>
    6.29 -#include <glib/gi18n.h>
    6.30 -#include <libnotify/notify.h>
    6.31 -#include "main.h"
    6.32 -
    6.33 -void init_notify();
    6.34 -void send_notify(struct UpdatesInfo *info);
    6.35 -const char *notify_text(struct UpdatesInfo *info);
    6.36 -
    6.37 -#endif
     7.1 --- a/packagekit.c	Tue Oct 11 17:36:32 2011 +0200
     7.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.3 @@ -1,68 +0,0 @@
     7.4 -/*
     7.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     7.6 - *
     7.7 - * Licensed under the GNU General Public License Version 2
     7.8 - *
     7.9 - * This program is free software; you can redistribute it and/or modify
    7.10 - * it under the terms of the GNU General Public License as published by
    7.11 - * the Free Software Foundation; either version 2 of the License, or
    7.12 - * (at your option) any later version.
    7.13 - *
    7.14 - * This program is distributed in the hope that it will be useful,
    7.15 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.16 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.17 - * GNU General Public License for more details.
    7.18 - *
    7.19 - * You should have received a copy of the GNU General Public License
    7.20 - * along with this program; if not, write to the Free Software
    7.21 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    7.22 - */
    7.23 -
    7.24 -#include <packagekit-glib2/packagekit.h>
    7.25 -#include "packagekit.h"
    7.26 -
    7.27 -void process(PkPackage *pkg, struct UpdatesInfo *info)
    7.28 -{
    7.29 -	PkInfoEnum e = pk_package_get_info(pkg);
    7.30 -	switch (e) {
    7.31 -		case PK_INFO_ENUM_LOW:
    7.32 -		case PK_INFO_ENUM_ENHANCEMENT:
    7.33 -		case PK_INFO_ENUM_NORMAL:
    7.34 -			info->normal++;
    7.35 -			break;
    7.36 -		case PK_INFO_ENUM_BUGFIX:
    7.37 -		case PK_INFO_ENUM_IMPORTANT:
    7.38 -		case PK_INFO_ENUM_SECURITY:
    7.39 -			info->critical++;
    7.40 -			break;
    7.41 -		default:
    7.42 -			break;
    7.43 -	}
    7.44 -}
    7.45 -
    7.46 -void query_packagekit(struct UpdatesInfo *info)
    7.47 -{
    7.48 -	PkClient *client = NULL;
    7.49 -	PkResults *res = NULL;
    7.50 -	GPtrArray *list = NULL;
    7.51 -	GFunc process_func = (GFunc)process;
    7.52 -	client = pk_client_new();
    7.53 -	res = pk_client_get_updates(client, pk_bitfield_value(PK_FILTER_ENUM_NEWEST), NULL, NULL, NULL, NULL);
    7.54 -	if (!res) {
    7.55 -		goto out;
    7.56 -	}
    7.57 -	list = pk_results_get_package_array(res);
    7.58 -	if (!list) {
    7.59 -		goto out;
    7.60 -	}
    7.61 -	info->normal = 0;
    7.62 -	info->critical = 0;
    7.63 -	g_ptr_array_foreach(list, process_func, info);
    7.64 -out:
    7.65 -	if (list != NULL)
    7.66 -		g_ptr_array_unref(list);
    7.67 -	if(res != NULL)
    7.68 -		g_object_unref(res);
    7.69 -	if(client != NULL)
    7.70 -		g_object_unref(client);
    7.71 -}
     8.1 --- a/packagekit.h	Tue Oct 11 17:36:32 2011 +0200
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,28 +0,0 @@
     8.4 -/*
     8.5 - * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
     8.6 - *
     8.7 - * Licensed under the GNU General Public License Version 2
     8.8 - *
     8.9 - * This program is free software; you can redistribute it and/or modify
    8.10 - * it under the terms of the GNU General Public License as published by
    8.11 - * the Free Software Foundation; either version 2 of the License, or
    8.12 - * (at your option) any later version.
    8.13 - *
    8.14 - * This program is distributed in the hope that it will be useful,
    8.15 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.16 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.17 - * GNU General Public License for more details.
    8.18 - *
    8.19 - * You should have received a copy of the GNU General Public License
    8.20 - * along with this program; if not, write to the Free Software
    8.21 - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    8.22 - */
    8.23 -
    8.24 -#ifndef PACKAGEKIT_H
    8.25 -#define PACKAGEKIT_H 1
    8.26 -
    8.27 -#include "main.h"
    8.28 -
    8.29 -void query_packagekit(struct UpdatesInfo *info);
    8.30 -
    8.31 -#endif
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/pkui-backend.c	Thu Oct 20 08:19:22 2011 +0200
     9.3 @@ -0,0 +1,418 @@
     9.4 +/*
     9.5 + * (C) 2011 Guido Berhoerster <gber@opensuse.org>
     9.6 + *
     9.7 + * Licensed under the GNU General Public License Version 2
     9.8 + *
     9.9 + * This program is free software; you can redistribute it and/or modify
    9.10 + * it under the terms of the GNU General Public License as published by
    9.11 + * the Free Software Foundation; either version 2 of the License, or
    9.12 + * (at your option) any later version.
    9.13 + *
    9.14 + * This program is distributed in the hope that it will be useful,
    9.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9.17 + * GNU General Public License for more details.
    9.18 + *
    9.19 + * You should have received a copy of the GNU General Public License
    9.20 + * along with this program; if not, write to the Free Software
    9.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    9.22 + */
    9.23 +
    9.24 +#include <packagekit-glib2/packagekit.h>
    9.25 +#include "pkui-backend.h"
    9.26 +
    9.27 +G_DEFINE_TYPE(PkuiBackend, pkui_backend, G_TYPE_OBJECT)
    9.28 +
    9.29 +#define	PKUI_BACKEND_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
    9.30 +	PKUI_TYPE_BACKEND, PkuiBackendPrivate))
    9.31 +
    9.32 +struct _PkuiBackendPrivate
    9.33 +{
    9.34 +	PkuiBackend *backend;
    9.35 +	PkClient *pk_client;
    9.36 +	guint periodic_check_id;
    9.37 +	guint updates_normal;
    9.38 +	guint updates_important;
    9.39 +	guint previous_updates_normal;
    9.40 +	guint previous_updates_important;
    9.41 +	guint startup_delay;
    9.42 +	guint check_interval;
    9.43 +	gint64 last_check;
    9.44 +	gboolean inhibit_check;
    9.45 +};
    9.46 +
    9.47 +enum
    9.48 +{
    9.49 +	PROP_0,
    9.50 +
    9.51 +	PROP_UPDATES_NORMAL,
    9.52 +	PROP_UPDATES_IMPORTANT,
    9.53 +	PROP_STARTUP_DELAY,
    9.54 +	PROP_CHECK_INTERVAL,
    9.55 +	PROP_INHIBIT_CHECK
    9.56 +};
    9.57 +
    9.58 +enum
    9.59 +{
    9.60 +	STATE_CHANGED_SIGNAL,
    9.61 +
    9.62 +	LAST_SIGNAL
    9.63 +};
    9.64 +
    9.65 +static guint	pkui_backend_signals[LAST_SIGNAL] = { 0, };
    9.66 +
    9.67 +static gboolean periodic_check(gpointer data);
    9.68 +
    9.69 +static void
    9.70 +pkui_backend_set_property(GObject *object, guint property_id,
    9.71 +    const GValue *value, GParamSpec *pspec)
    9.72 +{
    9.73 +	PkuiBackend	*self = PKUI_BACKEND(object);
    9.74 +	gboolean	inhibit_check;
    9.75 +	gint64		time_to_check;
    9.76 +
    9.77 +	switch (property_id) {
    9.78 +	case PROP_STARTUP_DELAY:
    9.79 +		self->priv->startup_delay = g_value_get_uint(value);
    9.80 +
    9.81 +		g_source_remove(self->priv->periodic_check_id);
    9.82 +		self->priv->periodic_check_id =
    9.83 +		    g_timeout_add_seconds(self->priv->startup_delay,
    9.84 +		    (GSourceFunc)periodic_check, self);
    9.85 +		break;
    9.86 +	case PROP_CHECK_INTERVAL:
    9.87 +		self->priv->check_interval = g_value_get_uint(value);
    9.88 +
    9.89 +		/*
    9.90 +		 * reschedule only if the first run has been completed and
    9.91 +		 * checks are currently not inibited, otherwise the new
    9.92 +		 * interval will be picked up anyway
    9.93 +		 */
    9.94 +		if (!self->priv->inhibit_check && self->priv->last_check > 0) {
    9.95 +			time_to_check = g_get_real_time() -
    9.96 +			    self->priv->last_check;
    9.97 +			if (time_to_check <= 0)
    9.98 +				pkui_backend_check_now(self);
    9.99 +			else {
   9.100 +				g_source_remove(self->priv->periodic_check_id);
   9.101 +				self->priv->periodic_check_id =
   9.102 +				    g_timeout_add_seconds(time_to_check,
   9.103 +				    periodic_check, self);
   9.104 +			}
   9.105 +		}
   9.106 +		break;
   9.107 +	case PROP_INHIBIT_CHECK:
   9.108 +		inhibit_check = g_value_get_boolean(value);
   9.109 +
   9.110 +		/*
   9.111 +		 * when changing self->priv->inhibit_check from FALSE to TRUE
   9.112 +		 * and the first run has been completed remove the periodic
   9.113 +		 * check, when changing from TRUE to FALSE either trigger a run
   9.114 +		 * immediately when more time than self->priv->check_interval
   9.115 +		 * has passed or just reschedule the next check
   9.116 +		 */
   9.117 +		if (!self->priv->inhibit_check && inhibit_check &&
   9.118 +		    self->priv->periodic_check_id != 0) {
   9.119 +			g_source_remove(self->priv->periodic_check_id);
   9.120 +			self->priv->periodic_check_id = 0;
   9.121 +		} else if (self->priv->inhibit_check && !inhibit_check) {
   9.122 +			self->priv->periodic_check_id =
   9.123 +			    g_timeout_add_seconds(self->priv->check_interval,
   9.124 +			    (GSourceFunc)periodic_check, self);
   9.125 +		}
   9.126 +		self->priv->inhibit_check = inhibit_check;
   9.127 +		break;
   9.128 +	default:
   9.129 +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
   9.130 +		break;
   9.131 +	}
   9.132 +}
   9.133 +
   9.134 +static void
   9.135 +pkui_backend_get_property(GObject *object, guint property_id, GValue *value,
   9.136 +    GParamSpec *pspec)
   9.137 +{
   9.138 +	PkuiBackend	*self = PKUI_BACKEND(object);
   9.139 +
   9.140 +	switch (property_id) {
   9.141 +	case PROP_UPDATES_NORMAL:
   9.142 +		g_value_set_uint(value, self->priv->updates_normal);
   9.143 +		break;
   9.144 +	case PROP_UPDATES_IMPORTANT:
   9.145 +		g_value_set_uint(value, self->priv->updates_important);
   9.146 +		break;
   9.147 +	case PROP_STARTUP_DELAY:
   9.148 +		g_value_set_uint(value, self->priv->startup_delay);
   9.149 +		break;
   9.150 +	case PROP_CHECK_INTERVAL:
   9.151 +		g_value_set_uint(value, self->priv->check_interval);
   9.152 +		break;
   9.153 +	case PROP_INHIBIT_CHECK:
   9.154 +		g_value_set_boolean(value, self->priv->inhibit_check);
   9.155 +		break;
   9.156 +	default:
   9.157 +		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
   9.158 +		break;
   9.159 +	}
   9.160 +}
   9.161 +
   9.162 +static void
   9.163 +pkui_backend_finalize(GObject *gobject)
   9.164 +{
   9.165 +	PkuiBackend	*self = PKUI_BACKEND(gobject);
   9.166 +
   9.167 +	if (self->priv->pk_client != NULL)
   9.168 +		g_object_unref(self->priv->pk_client);
   9.169 +
   9.170 +	G_OBJECT_CLASS(pkui_backend_parent_class)->finalize(gobject);
   9.171 +}
   9.172 +
   9.173 +static void
   9.174 +pkui_backend_class_init(PkuiBackendClass *klass)
   9.175 +{
   9.176 +	GObjectClass	*gobject_class = G_OBJECT_CLASS(klass);
   9.177 +	GParamSpec	*pspec;
   9.178 +
   9.179 +	gobject_class->set_property = pkui_backend_set_property;
   9.180 +	gobject_class->get_property = pkui_backend_get_property;
   9.181 +	gobject_class->finalize = pkui_backend_finalize;
   9.182 +
   9.183 +	pspec = g_param_spec_uint("updates-normal", "Normal updates",
   9.184 +	    "Number of normal package updates", 0, G_MAXUINT, 0,
   9.185 +	    G_PARAM_READABLE);
   9.186 +	g_object_class_install_property(gobject_class, PROP_UPDATES_NORMAL,
   9.187 +	    pspec);
   9.188 +
   9.189 +	pspec = g_param_spec_uint("updates-important", "Important updates",
   9.190 +	    "Number of important package updates", 0, G_MAXUINT, 0,
   9.191 +	    G_PARAM_READABLE);
   9.192 +	g_object_class_install_property(gobject_class, PROP_UPDATES_IMPORTANT,
   9.193 +	    pspec);
   9.194 +
   9.195 +	pspec = g_param_spec_uint("startup-delay", "Startup delay",
   9.196 +	    "Initial delay in second before the first check for new package "
   9.197 +	    "updates", 0, G_MAXUINT, 60,
   9.198 +	    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
   9.199 +	g_object_class_install_property(gobject_class, PROP_STARTUP_DELAY,
   9.200 +	    pspec);
   9.201 +
   9.202 +	pspec = g_param_spec_uint("check-interval", "Check interval",
   9.203 +	    "Interval in seconds for checking for new package updates", 1,
   9.204 +	    G_MAXUINT, 3600, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
   9.205 +	g_object_class_install_property(gobject_class, PROP_CHECK_INTERVAL,
   9.206 +	    pspec);
   9.207 +
   9.208 +	pspec = g_param_spec_boolean("inhibit-check", "Inhibit check",
   9.209 +	    "Whether to inhibit checks for new package updates", FALSE,
   9.210 +	    G_PARAM_READWRITE);
   9.211 +	g_object_class_install_property(gobject_class, PROP_INHIBIT_CHECK,
   9.212 +	    pspec);
   9.213 +
   9.214 +	pkui_backend_signals[STATE_CHANGED_SIGNAL] =
   9.215 +	    g_signal_newv("state-changed", G_TYPE_FROM_CLASS(gobject_class),
   9.216 +	    G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, NULL,
   9.217 +	    NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
   9.218 +
   9.219 +	g_type_class_add_private(klass, sizeof (PkuiBackendPrivate));
   9.220 +}
   9.221 +
   9.222 +static void
   9.223 +pkui_backend_init(PkuiBackend *self)
   9.224 +{
   9.225 +	self->priv = PKUI_BACKEND_GET_PRIVATE(self);
   9.226 +
   9.227 +	self->priv->pk_client = pk_client_new();
   9.228 +	self->priv->updates_normal = 0;
   9.229 +	self->priv->updates_important = 0;
   9.230 +	self->priv->periodic_check_id =
   9.231 +	    g_timeout_add_seconds(self->priv->startup_delay,
   9.232 +	    (GSourceFunc)periodic_check, self);
   9.233 +	self->priv->last_check = 0;
   9.234 +}
   9.235 +
   9.236 +static gboolean
   9.237 +periodic_check(gpointer data)
   9.238 +{
   9.239 +	PkuiBackend	*self = PKUI_BACKEND(data);
   9.240 +
   9.241 +	pkui_backend_check_now(self);
   9.242 +
   9.243 +	/* rescheduling happens after results are available */
   9.244 +	return (FALSE);
   9.245 +}
   9.246 +
   9.247 +static void
   9.248 +process_pk_package_info(PkPackage *pkg, gpointer *user_data)
   9.249 +{
   9.250 +	PkuiBackend	*self = PKUI_BACKEND(user_data);
   9.251 +	PkInfoEnum	type_info = pk_package_get_info(pkg);
   9.252 +
   9.253 +	switch (type_info) {
   9.254 +	case PK_INFO_ENUM_LOW:
   9.255 +	case PK_INFO_ENUM_ENHANCEMENT:
   9.256 +	case PK_INFO_ENUM_NORMAL:
   9.257 +		self->priv->updates_normal++;
   9.258 +		break;
   9.259 +	case PK_INFO_ENUM_BUGFIX:
   9.260 +	case PK_INFO_ENUM_IMPORTANT:
   9.261 +	case PK_INFO_ENUM_SECURITY:
   9.262 +		self->priv->updates_important++;
   9.263 +		break;
   9.264 +	default:
   9.265 +		break;
   9.266 +	}
   9.267 +}
   9.268 +
   9.269 +static void
   9.270 +get_updates_finished(GObject *object, GAsyncResult *res, gpointer user_data)
   9.271 +{
   9.272 +	PkClient	*client = PK_CLIENT(object);
   9.273 +	PkuiBackend	*self = PKUI_BACKEND(user_data);
   9.274 +	PkResults	*results = NULL;
   9.275 +	GError		*error = NULL;
   9.276 +	GPtrArray	*list = NULL;
   9.277 +
   9.278 +	g_debug("PackageKit finished checking for updates");
   9.279 +
   9.280 +	results = pk_client_generic_finish(PK_CLIENT(client), res, &error);
   9.281 +	if (results == NULL) {
   9.282 +		g_warning("error: %s\n", error->message);
   9.283 +		g_error_free(error);
   9.284 +		goto out;
   9.285 +	}
   9.286 +
   9.287 +	list = pk_results_get_package_array(results);
   9.288 +	if (list == NULL)
   9.289 +		goto out;
   9.290 +	self->priv->previous_updates_normal = self->priv->updates_normal;
   9.291 +	self->priv->previous_updates_important = self->priv->updates_important;
   9.292 +	self->priv->updates_normal = 0;
   9.293 +	self->priv->updates_important = 0;
   9.294 +	g_ptr_array_foreach(list, (GFunc)process_pk_package_info, self);
   9.295 +
   9.296 +	if (self->priv->updates_normal != self->priv->previous_updates_normal ||
   9.297 +	    self->priv->updates_important !=
   9.298 +	    self->priv->previous_updates_important) {
   9.299 +		g_debug("normal updates: %d, important updates: %d",
   9.300 +		    self->priv->updates_normal, self->priv->updates_important);
   9.301 +		g_debug("emit state-changed");
   9.302 +
   9.303 +		g_signal_emit(self, pkui_backend_signals[STATE_CHANGED_SIGNAL],
   9.304 +		    0);
   9.305 +	}
   9.306 +
   9.307 +out:
   9.308 +	if (results != NULL)
   9.309 +		g_object_unref(results);
   9.310 +	if (list != NULL)
   9.311 +		g_ptr_array_unref(list);
   9.312 +
   9.313 +	self->priv->last_check = g_get_real_time();
   9.314 +
   9.315 +	if (!self->priv->inhibit_check) {
   9.316 +		if (self->priv->periodic_check_id != 0)
   9.317 +			g_source_remove(self->priv->periodic_check_id);
   9.318 +		self->priv->periodic_check_id =
   9.319 +			g_timeout_add_seconds(self->priv->check_interval,
   9.320 +			    (GSourceFunc)periodic_check, self);
   9.321 +	}
   9.322 +}
   9.323 +
   9.324 +PkuiBackend *
   9.325 +pkui_backend_new(guint startup_delay, guint check_interval)
   9.326 +{
   9.327 +	g_return_val_if_fail(check_interval > 0, NULL);
   9.328 +
   9.329 +	return (g_object_new(PKUI_TYPE_BACKEND, "startup-delay", startup_delay,
   9.330 +	    "check-interval", check_interval, NULL));
   9.331 +}
   9.332 +
   9.333 +guint
   9.334 +pkui_backend_get_updates_normal(PkuiBackend *self)
   9.335 +{
   9.336 +	guint	updates_normal;
   9.337 +
   9.338 +	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
   9.339 +
   9.340 +	g_object_get(G_OBJECT(self), "updates-normal", &updates_normal, NULL);
   9.341 +
   9.342 +	return (updates_normal);
   9.343 +}
   9.344 +
   9.345 +guint
   9.346 +pkui_backend_get_updates_important(PkuiBackend *self)
   9.347 +{
   9.348 +	guint	updates_important;
   9.349 +
   9.350 +	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
   9.351 +
   9.352 +	g_object_get(G_OBJECT(self), "updates-important", &updates_important,
   9.353 +	    NULL);
   9.354 +
   9.355 +	return (updates_important);
   9.356 +}
   9.357 +
   9.358 +void
   9.359 +pkui_backend_set_inhibit_check(PkuiBackend *self, gboolean inhibit_check)
   9.360 +{
   9.361 +	g_return_if_fail(PKUI_IS_BACKEND(self));
   9.362 +
   9.363 +	g_object_set(G_OBJECT(self), "inhibit-check", inhibit_check, NULL);
   9.364 +}
   9.365 +
   9.366 +gboolean
   9.367 +pkui_backend_get_inhibit_check(PkuiBackend *self)
   9.368 +{
   9.369 +	gboolean	inhibit_check;
   9.370 +
   9.371 +	g_return_val_if_fail(PKUI_IS_BACKEND(self), FALSE);
   9.372 +
   9.373 +	g_object_get(G_OBJECT(self), "inhibit-check", &inhibit_check, NULL);
   9.374 +
   9.375 +	return (inhibit_check);
   9.376 +}
   9.377 +
   9.378 +guint
   9.379 +pkui_backend_get_startup_interval(PkuiBackend *self)
   9.380 +{
   9.381 +	guint	startup_interval;
   9.382 +
   9.383 +	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
   9.384 +
   9.385 +	g_object_get(G_OBJECT(self), "startup-interval", &startup_interval,
   9.386 +	    NULL);
   9.387 +
   9.388 +	return (startup_interval);
   9.389 +}
   9.390 +
   9.391 +void
   9.392 +pkui_backend_set_check_interval(PkuiBackend *self, guint check_interval)
   9.393 +{
   9.394 +	g_return_if_fail(PKUI_IS_BACKEND(self));
   9.395 +
   9.396 +	g_object_set(G_OBJECT(self), "check-interval", check_interval, NULL);
   9.397 +}
   9.398 +
   9.399 +guint
   9.400 +pkui_backend_get_check_interval(PkuiBackend *self)
   9.401 +{
   9.402 +	guint	check_interval;
   9.403 +
   9.404 +	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
   9.405 +
   9.406 +	g_object_get(G_OBJECT(self), "check-interval", &check_interval, NULL);
   9.407 +
   9.408 +	return (check_interval);
   9.409 +}
   9.410 +
   9.411 +void
   9.412 +pkui_backend_check_now(PkuiBackend *self)
   9.413 +{
   9.414 +	g_return_if_fail(PKUI_IS_BACKEND(self));
   9.415 +
   9.416 +	g_debug("querying PackageKit for updates");
   9.417 +
   9.418 +	pk_client_get_updates_async(self->priv->pk_client,
   9.419 +	    pk_bitfield_value(PK_FILTER_ENUM_NONE), NULL, NULL, NULL,
   9.420 +	    get_updates_finished, self);
   9.421 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/pkui-backend.h	Thu Oct 20 08:19:22 2011 +0200
    10.3 @@ -0,0 +1,69 @@
    10.4 +/*
    10.5 + * (C) 2011 Guido Berhoerster <gber@opensuse.org>
    10.6 + *
    10.7 + * Licensed under the GNU General Public License Version 2
    10.8 + *
    10.9 + * This program is free software; you can redistribute it and/or modify
   10.10 + * it under the terms of the GNU General Public License as published by
   10.11 + * the Free Software Foundation; either version 2 of the License, or
   10.12 + * (at your option) any later version.
   10.13 + *
   10.14 + * This program is distributed in the hope that it will be useful,
   10.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.17 + * GNU General Public License for more details.
   10.18 + *
   10.19 + * You should have received a copy of the GNU General Public License
   10.20 + * along with this program; if not, write to the Free Software
   10.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   10.22 + */
   10.23 +
   10.24 +#ifndef	__PKUI_BACKEND_H
   10.25 +#define	__PKUI_BACKEND_H
   10.26 +
   10.27 +#include <glib-object.h>
   10.28 +#include <packagekit-glib2/packagekit.h>
   10.29 +
   10.30 +G_BEGIN_DECLS
   10.31 +
   10.32 +#define	PKUI_TYPE_BACKEND		(pkui_backend_get_type())
   10.33 +#define	PKUI_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), \
   10.34 +	PKUI_TYPE_BACKEND, PkuiBackend))
   10.35 +#define	PKUI_IS_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
   10.36 +	PKUI_TYPE_BACKEND))
   10.37 +#define	PKUI_BACKEND_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), \
   10.38 +	PKUI_TYPE_BACKEND, PkuiBackendClass))
   10.39 +#define	PKUI_IS_BACKEND_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), \
   10.40 +	PKUI_TYPE_BACKEND))
   10.41 +#define	PKUI_BACKEND_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), \
   10.42 +	PKUI_TYPE_BACKEND, PkuiBackendClass))
   10.43 +
   10.44 +typedef struct _PkuiBackend		PkuiBackend;
   10.45 +typedef struct _PkuiBackendClass	PkuiBackendClass;
   10.46 +typedef struct _PkuiBackendPrivate	PkuiBackendPrivate;
   10.47 +
   10.48 +struct _PkuiBackend
   10.49 +{
   10.50 +	GObject		parent_instance;
   10.51 +
   10.52 +	PkuiBackendPrivate *priv;
   10.53 +};
   10.54 +
   10.55 +struct _PkuiBackendClass
   10.56 +{
   10.57 +	GObjectClass	parent_class;
   10.58 +};
   10.59 +
   10.60 +PkuiBackend *pkui_backend_new(guint startup_delay, guint interval);
   10.61 +guint pkui_backend_get_updates_normal(PkuiBackend *self);
   10.62 +guint pkui_backend_get_updates_important(PkuiBackend *self);
   10.63 +void pkui_backend_set_inhibit_check(PkuiBackend *self, gboolean inhibit_check);
   10.64 +gboolean pkui_backend_get_inhibit_check(PkuiBackend *self);
   10.65 +void pkui_backend_set_check_interval(PkuiBackend *self, guint check_interval);
   10.66 +guint pkui_backend_get_check_interval(PkuiBackend *self);
   10.67 +guint pkui_backend_get_startup_interval(PkuiBackend *self);
   10.68 +void pkui_backend_check_now(PkuiBackend *self);
   10.69 +
   10.70 +G_END_DECLS
   10.71 +
   10.72 +#endif /* __PKUI_BACKEND_H */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/pkui-icon.c	Thu Oct 20 08:19:22 2011 +0200
    11.3 @@ -0,0 +1,267 @@
    11.4 +/*
    11.5 + * (C) 2011 Guido Berhoerster <gber@opensuse.org>
    11.6 + *
    11.7 + * Licensed under the GNU General Public License Version 2
    11.8 + *
    11.9 + * This program is free software; you can redistribute it and/or modify
   11.10 + * it under the terms of the GNU General Public License as published by
   11.11 + * the Free Software Foundation; either version 2 of the License, or
   11.12 + * (at your option) any later version.
   11.13 + *
   11.14 + * This program is distributed in the hope that it will be useful,
   11.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.17 + * GNU General Public License for more details.
   11.18 + *
   11.19 + * You should have received a copy of the GNU General Public License
   11.20 + * along with this program; if not, write to the Free Software
   11.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   11.22 + */
   11.23 +
   11.24 +#include <glib/gi18n.h>
   11.25 +#include <libnotify/notify.h>
   11.26 +#include "pkui-backend.h"
   11.27 +#include "pkui-icon.h"
   11.28 +
   11.29 +G_DEFINE_TYPE(PkuiIcon, pkui_icon, G_TYPE_OBJECT)
   11.30 +
   11.31 +#define	UPDATE_VIEWER_COMMAND		"/usr/bin/gpk-update-viewer"
   11.32 +
   11.33 +#define	PKUI_ICON_GET_PRIVATE(obj)	(G_TYPE_INSTANCE_GET_PRIVATE((obj), \
   11.34 +					    PKUI_TYPE_ICON, PkuiIconPrivate))
   11.35 +
   11.36 +struct _PkuiIconPrivate
   11.37 +{
   11.38 +	PkuiBackend	*backend;
   11.39 +
   11.40 +	GtkStatusIcon	*status_icon;
   11.41 +	GtkWidget	*status_icon_popup_menu;
   11.42 +	NotifyNotification *notification;
   11.43 +};
   11.44 +
   11.45 +static GtkWidget* icon_popup_menu_create(PkuiIcon *self);
   11.46 +static void icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
   11.47 +    guint activate_time, gpointer user_data);
   11.48 +static void icon_activated(GtkStatusIcon *status_icon, gpointer user_data);
   11.49 +static void backend_state_changed(PkuiBackend *backend, gpointer user_data);
   11.50 +
   11.51 +static void
   11.52 +pkui_icon_finalize(GObject *gobject)
   11.53 +{
   11.54 +	PkuiIcon	*self = PKUI_ICON(gobject);
   11.55 +
   11.56 +	gtk_widget_destroy(self->priv->status_icon_popup_menu);
   11.57 +	g_object_unref(self->priv->status_icon_popup_menu);
   11.58 +	g_object_unref(self->priv->status_icon);
   11.59 +	if (self->priv->notification != NULL) {
   11.60 +		notify_notification_close(self->priv->notification, NULL);
   11.61 +		g_object_unref(self->priv->notification);
   11.62 +	}
   11.63 +	g_object_unref(self->priv->backend);
   11.64 +
   11.65 +	G_OBJECT_CLASS(pkui_icon_parent_class)->finalize(gobject);
   11.66 +}
   11.67 +
   11.68 +static void
   11.69 +pkui_icon_class_init(PkuiIconClass *klass)
   11.70 +{
   11.71 +	GObjectClass	*gobject_class = G_OBJECT_CLASS(klass);
   11.72 +
   11.73 +	gobject_class->finalize = pkui_icon_finalize;
   11.74 +
   11.75 +	g_type_class_add_private(klass, sizeof (PkuiIconPrivate));
   11.76 +}
   11.77 +
   11.78 +static void
   11.79 +pkui_icon_init(PkuiIcon *self)
   11.80 +{
   11.81 +	self->priv = PKUI_ICON_GET_PRIVATE(self);
   11.82 +
   11.83 +	gtk_window_set_default_icon_name("system-software-update");
   11.84 +
   11.85 +	self->priv->status_icon_popup_menu = icon_popup_menu_create(self);
   11.86 +	g_object_ref(self->priv->status_icon_popup_menu);
   11.87 +	g_object_ref_sink(GTK_OBJECT(self->priv->status_icon_popup_menu));
   11.88 +
   11.89 +	self->priv->status_icon = gtk_status_icon_new();
   11.90 +	gtk_status_icon_set_title(self->priv->status_icon,
   11.91 +	    _("Software Updates"));
   11.92 +	gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
   11.93 +	g_signal_connect(G_OBJECT(self->priv->status_icon), "activate",
   11.94 +	    G_CALLBACK(icon_activated), self);
   11.95 +	g_signal_connect(G_OBJECT(self->priv->status_icon), "popup-menu",
   11.96 +	    G_CALLBACK(icon_popup_menu_popup), self);
   11.97 +
   11.98 +	self->priv->notification = NULL;
   11.99 +
  11.100 +	self->priv->backend = NULL;
  11.101 +}
  11.102 +
  11.103 +static void
  11.104 +backend_check_now(GtkMenuItem *menu_item, gpointer user_data)
  11.105 +{
  11.106 +	PkuiIcon	*self = PKUI_ICON(user_data);
  11.107 +
  11.108 +	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
  11.109 +
  11.110 +	pkui_backend_check_now(self->priv->backend);
  11.111 +}
  11.112 +
  11.113 +static GtkWidget*
  11.114 +icon_popup_menu_create(PkuiIcon *self)
  11.115 +{
  11.116 +	GtkWidget	*popup_menu = gtk_menu_new();
  11.117 +	GtkWidget	*item;
  11.118 +	GtkWidget	*image;
  11.119 +
  11.120 +	item = gtk_image_menu_item_new_with_mnemonic(_("_Check for Updates"));
  11.121 +	image = gtk_image_new_from_icon_name(GTK_STOCK_REFRESH,
  11.122 +	    GTK_ICON_SIZE_MENU);
  11.123 +	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
  11.124 +	gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), item);
  11.125 +	g_signal_connect(G_OBJECT(item), "activate",
  11.126 +	    G_CALLBACK(backend_check_now), self);
  11.127 +
  11.128 +	gtk_widget_show_all(GTK_WIDGET(popup_menu));
  11.129 +
  11.130 +	return (popup_menu);
  11.131 +}
  11.132 +
  11.133 +static void
  11.134 +icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
  11.135 +    guint activate_time, gpointer user_data)
  11.136 +{
  11.137 +	PkuiIcon *self = PKUI_ICON(user_data);
  11.138 +
  11.139 +	gtk_menu_popup(GTK_MENU(self->priv->status_icon_popup_menu), NULL, NULL,
  11.140 +	    NULL, NULL, button, activate_time);
  11.141 +}
  11.142 +
  11.143 +static void
  11.144 +update_notification(PkuiIcon *self, guint updates_normal,
  11.145 +    guint updates_important)
  11.146 +{
  11.147 +	gchar *message;
  11.148 +	gchar *title = updates_important ?
  11.149 +	    ngettext("Important Software Update", "Important Software Updates",
  11.150 +	    updates_important + updates_normal) :
  11.151 +	    ngettext("Software Update", "Software Updates", updates_important +
  11.152 +	    updates_normal);
  11.153 +	gchar *icon = updates_important ? "software-update-urgent" :
  11.154 +	    "software-update-available";
  11.155 +
  11.156 +	if (updates_important > 0)
  11.157 +		if (updates_normal > 0)
  11.158 +			message = g_strdup_printf(ngettext("There are %d "
  11.159 +			    "software updates available, %d of them is "
  11.160 +			    "important.", "There are %d software updates "
  11.161 +			    "available, %d of them are important.",
  11.162 +			    updates_important),
  11.163 +			    updates_normal + updates_important,
  11.164 +			    updates_important);
  11.165 +		else
  11.166 +			message = g_strdup_printf(ngettext("There is an "
  11.167 +			    "important software update available.", "There are "
  11.168 +			    "%d important software updates available.",
  11.169 +			    updates_important), updates_important);
  11.170 +	else
  11.171 +		message = g_strdup_printf(ngettext("There is a software update "
  11.172 +		    "available.", "There are %d software updates available.",
  11.173 +		    updates_normal), updates_normal);
  11.174 +
  11.175 +	gtk_status_icon_set_tooltip(self->priv->status_icon, message);
  11.176 +	gtk_status_icon_set_from_icon_name(self->priv->status_icon, icon);
  11.177 +	gtk_status_icon_set_visible(self->priv->status_icon, TRUE);
  11.178 +
  11.179 +	if (self->priv->notification == NULL)
  11.180 +		self->priv->notification = notify_notification_new(title,
  11.181 +		    message, icon
  11.182 +#if (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
  11.183 +		    , NULL
  11.184 +#endif
  11.185 +		    );
  11.186 +	else
  11.187 +		notify_notification_update(self->priv->notification, title,
  11.188 +		    message, icon);
  11.189 +
  11.190 +	notify_notification_set_timeout(self->priv->notification,
  11.191 +	    NOTIFY_EXPIRES_NEVER);
  11.192 +	notify_notification_set_urgency(self->priv->notification,
  11.193 +	    updates_important ? NOTIFY_URGENCY_CRITICAL :
  11.194 +	    NOTIFY_URGENCY_NORMAL);
  11.195 +	notify_notification_show(self->priv->notification, NULL);
  11.196 +
  11.197 +	g_free(message);
  11.198 +}
  11.199 +
  11.200 +static void
  11.201 +hide_notification(PkuiIcon *self)
  11.202 +{
  11.203 +	gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
  11.204 +	notify_notification_close(self->priv->notification, NULL);
  11.205 +}
  11.206 +
  11.207 +
  11.208 +static void
  11.209 +backend_state_changed(PkuiBackend *backend, gpointer user_data)
  11.210 +{
  11.211 +	PkuiIcon	*self = PKUI_ICON(user_data);
  11.212 +	guint		updates_normal;
  11.213 +	guint		updates_important;
  11.214 +
  11.215 +	g_return_if_fail(PKUI_IS_BACKEND(backend));
  11.216 +
  11.217 +	updates_normal = pkui_backend_get_updates_normal(backend);
  11.218 +	updates_important = pkui_backend_get_updates_important(backend);
  11.219 +	if (updates_normal > 0 || updates_important > 0)
  11.220 +		update_notification(self, updates_normal, updates_important);
  11.221 +	else if (updates_normal + updates_important == 0)
  11.222 +		hide_notification(self);
  11.223 +}
  11.224 +
  11.225 +static void
  11.226 +update_viewer_exited(GPid pid, gint status, gpointer user_data)
  11.227 +{
  11.228 +	PkuiIcon	*self = PKUI_ICON(user_data);
  11.229 +
  11.230 +	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
  11.231 +
  11.232 +	g_spawn_close_pid(pid);
  11.233 +
  11.234 +	pkui_backend_check_now(self->priv->backend);
  11.235 +	pkui_backend_set_inhibit_check(self->priv->backend, FALSE);
  11.236 +}
  11.237 +
  11.238 +static void
  11.239 +icon_activated(GtkStatusIcon *status_icon, gpointer user_data)
  11.240 +{
  11.241 +	PkuiIcon	*self = PKUI_ICON(user_data);
  11.242 +	static const gchar *argv[] = { UPDATE_VIEWER_COMMAND, NULL };
  11.243 +	GPid		pid;
  11.244 +	gboolean	retval;
  11.245 +
  11.246 +	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
  11.247 +
  11.248 +	retval = g_spawn_async(NULL, (gchar **)argv, NULL,
  11.249 +	    G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
  11.250 +	if (!retval) {
  11.251 +		g_warning("Could not execute" UPDATE_VIEWER_COMMAND);
  11.252 +		return;
  11.253 +	}
  11.254 +	g_child_watch_add(pid, (GChildWatchFunc)update_viewer_exited, self);
  11.255 +
  11.256 +	pkui_backend_set_inhibit_check(self->priv->backend, TRUE);
  11.257 +	hide_notification(self);
  11.258 +}
  11.259 +
  11.260 +PkuiIcon *
  11.261 +pkui_icon_new(guint startup_delay, guint check_interval)
  11.262 +{
  11.263 +	PkuiIcon	*icon = g_object_new(PKUI_TYPE_ICON, NULL);
  11.264 +
  11.265 +	icon->priv->backend = pkui_backend_new(startup_delay, check_interval);
  11.266 +	g_signal_connect(icon->priv->backend, "state-changed",
  11.267 +	    G_CALLBACK(backend_state_changed), icon);
  11.268 +
  11.269 +	return (icon);
  11.270 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/pkui-icon.h	Thu Oct 20 08:19:22 2011 +0200
    12.3 @@ -0,0 +1,60 @@
    12.4 +/*
    12.5 + * (C) 2011 Guido Berhoerster <gber@opensuse.org>
    12.6 + *
    12.7 + * Licensed under the GNU General Public License Version 2
    12.8 + *
    12.9 + * This program is free software; you can redistribute it and/or modify
   12.10 + * it under the terms of the GNU General Public License as published by
   12.11 + * the Free Software Foundation; either version 2 of the License, or
   12.12 + * (at your option) any later version.
   12.13 + *
   12.14 + * This program is distributed in the hope that it will be useful,
   12.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.17 + * GNU General Public License for more details.
   12.18 + *
   12.19 + * You should have received a copy of the GNU General Public License
   12.20 + * along with this program; if not, write to the Free Software
   12.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   12.22 + */
   12.23 +
   12.24 +#ifndef	__PKUI_ICON_H
   12.25 +#define	__PKUI_ICON_H
   12.26 +
   12.27 +#include <glib-object.h>
   12.28 +#include <gtk/gtk.h>
   12.29 +
   12.30 +G_BEGIN_DECLS
   12.31 +
   12.32 +#define	PKUI_TYPE_ICON			(pkui_icon_get_type())
   12.33 +#define	PKUI_ICON(obj)			(G_TYPE_CHECK_INSTANCE_CAST((obj), \
   12.34 +	PKUI_TYPE_ICON, PkuiIcon))
   12.35 +#define	PKUI_IS_ICON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
   12.36 +	PKUI_TYPE_ICON))
   12.37 +#define	PKUI_ICON_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), \
   12.38 +	PKUI_TYPE_ICON, PkuiIconClass))
   12.39 +#define	PKUI_IS_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), \
   12.40 +	PKUI_TYPE_ICON))
   12.41 +#define	PKUI_ICON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), \
   12.42 +	PKUI_TYPE_ICON, PkuiIconClass))
   12.43 +
   12.44 +typedef struct _PkuiIcon	PkuiIcon;
   12.45 +typedef struct _PkuiIconClass	PkuiIconClass;
   12.46 +typedef struct _PkuiIconPrivate	PkuiIconPrivate;
   12.47 +
   12.48 +struct _PkuiIcon
   12.49 +{
   12.50 +	GObject parent_instance;
   12.51 +	PkuiIconPrivate *priv;
   12.52 +};
   12.53 +
   12.54 +struct _PkuiIconClass
   12.55 +{
   12.56 +	GObjectClass parent_class;
   12.57 +};
   12.58 +
   12.59 +PkuiIcon *pkui_icon_new(guint startup_delay, guint check_interval);
   12.60 +
   12.61 +G_END_DECLS
   12.62 +
   12.63 +#endif /* __PKUI_ICON_H */
    13.1 --- a/po/POTFILES.in	Tue Oct 11 17:36:32 2011 +0200
    13.2 +++ b/po/POTFILES.in	Thu Oct 20 08:19:22 2011 +0200
    13.3 @@ -1,4 +1,4 @@
    13.4  main.c
    13.5 -notify.c
    13.6 -packagekit.c
    13.7 +pkui-icon.c
    13.8 +pkui-backend.c
    13.9  pk-update-icon.desktop.in
    14.1 --- a/po/de.po	Tue Oct 11 17:36:32 2011 +0200
    14.2 +++ b/po/de.po	Thu Oct 20 08:19:22 2011 +0200
    14.3 @@ -7,7 +7,7 @@
    14.4  msgstr ""
    14.5  "Project-Id-Version: pk-update-icon 0.1\n"
    14.6  "Report-Msgid-Bugs-To: \n"
    14.7 -"POT-Creation-Date: 2011-10-10 19:19+0200\n"
    14.8 +"POT-Creation-Date: 2011-10-19 23:05+0200\n"
    14.9  "PO-Revision-Date: 2011-10-10 19:21+0200\n"
   14.10  "Last-Translator: Guido Berhoerster <gber@opensuse.org>\n"
   14.11  "Language-Team: German\n"
   14.12 @@ -17,28 +17,50 @@
   14.13  "Content-Transfer-Encoding: 8bit\n"
   14.14  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
   14.15  
   14.16 -#: ../main.c:49
   14.17 -msgid "_Quit"
   14.18 -msgstr "_Verlassen"
   14.19 +#: ../pkui-icon.c:86
   14.20 +msgid "Software Updates"
   14.21 +msgstr "Softwareaktualisierungen"
   14.22  
   14.23 -#: ../main.c:67 ../notify.c:32
   14.24 -msgid "Software Update(s)"
   14.25 -msgstr "Software Update(s)"
   14.26 +#: ../pkui-icon.c:115
   14.27 +msgid "_Check for Updates"
   14.28 +msgstr "Nach Aktualisierungen _Suchen"
   14.29  
   14.30 -#: ../notify.c:42
   14.31 +#: ../pkui-icon.c:144
   14.32 +msgid "Important Software Update"
   14.33 +msgid_plural "Important Software Updates"
   14.34 +msgstr[0] "Wichtige Aktualisierung"
   14.35 +msgstr[1] "Wichtige Aktualisierungen"
   14.36 +
   14.37 +#: ../pkui-icon.c:146
   14.38 +msgid "Software Update"
   14.39 +msgid_plural "Software Updates"
   14.40 +msgstr[0] "Softwareaktualisierung"
   14.41 +msgstr[1] "Softwareaktualisierungen"
   14.42 +
   14.43 +#: ../pkui-icon.c:153
   14.44 +msgid "There are %d software updates available, %d of them is important."
   14.45 +msgid_plural ""
   14.46 +"There are %d software updates available, %d of them are important."
   14.47 +msgstr[0] "Es sind %d Softwareaktualisierungen verfügbar, davon ist %d wichtig."
   14.48 +msgstr[1] "Es sind %d Softwareaktualisierungen verfügbar, davon sind %d wichtig."
   14.49 +
   14.50 +#: ../pkui-icon.c:161
   14.51 +msgid "There is an important software update available."
   14.52 +msgid_plural "There are %d important software updates available."
   14.53 +msgstr[0] "Es ist eine wichtige Softwareaktualisierung verfügbar."
   14.54 +msgstr[1] "Es sind %d wichtige Softwareaktualisierungen verfügbar."
   14.55 +
   14.56 +#: ../pkui-icon.c:166
   14.57  #, c-format
   14.58 -msgid "There are %d software updates available, %d of them critical."
   14.59 -msgstr "%d Software Updates sind verfügbar, %d davon sind kritisch."
   14.60 -
   14.61 -#: ../notify.c:44
   14.62 -#, c-format
   14.63 -msgid "There are %d software updates available."
   14.64 -msgstr "%d Software Updates sind verfügbar."
   14.65 +msgid "There is a software update available."
   14.66 +msgid_plural "There are %d software updates available."
   14.67 +msgstr[0] "Eine Softwareaktualisierung ist verfügbar."
   14.68 +msgstr[1] "Es sind %d Softwareaktualisierungen verfügbar."
   14.69  
   14.70  #: ../pk-update-icon.desktop.in.h:1
   14.71  msgid "PackageKit Update Applet"
   14.72 -msgstr "PackageKit Update Applet"
   14.73 +msgstr "PackageKit Aktualisierungsapplet"
   14.74  
   14.75  #: ../pk-update-icon.desktop.in.h:2
   14.76  msgid "Update Applet"
   14.77 -msgstr "Update Applet"
   14.78 +msgstr "Aktualisierungsapplet"