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, 20 Oct 2011 08:19:22 +0200
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
diffstat 14 files changed, 873 insertions(+), 372 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Tue Oct 11 17:36:32 2011 +0200
+++ b/Makefile	Thu Oct 20 08:19:22 2011 +0200
@@ -18,7 +18,7 @@
 PACKAGE =	pk-update-icon
 APP_NAME =	org.opensuse.pk-update-icon
 VERSION =	0.1
-OBJS =		main.o notify.o packagekit.o
+OBJS =		main.o pkui-icon.o pkui-backend.o
 AUTOSTART_FILE =	$(PACKAGE).desktop
 MOFILES :=	$(patsubst %.po,%.mo,$(wildcard po/*.po))
 POTFILE =	po/$(PACKAGE).pot
--- a/design.txt	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-Design
-======
-
-Objects:
-OUIApp(GtkApplication)
-    - Icon *icon
-    - Backend *backend
-    - GSettings *settings
-        * bind preferences to settings above
-        + check_frequency
-        + check_mobile_connection
-        + startup_timeout
-        + notify_package
-        + notify_patch_critical
-        + notify_patch_recommended
-        + notify_patch_optional
-    - PreferencesDialog *preferences_dialog
-
-startup:
-    unique
-    one-shot timer
-    regular timer
-    hide
-one-shot timer: check update
-timer: 24 h check update
-check update:
-    if battery critical:
-        set timer +1h
-    if not online:
-        set timer +1h
-    if on mobile and no allowed:
-        set timer +1h
-    run /usr/sbin/zypp-refresh-wrapper
-    run zypper --xmlout list-updates --type patch
-    run zypper --xmlout list-updates --type package
-    parse XML output
-    merge package and patch updates?
-    if updates:
-        select icon based on type of update
-        display icon and tooltip with number of updates
-    else:
-        hide
-left click on icon:
-    start yast2 online_update
-right click:
-    preferences
-    about
-preferences:
-    interval (hourly/daily/weekly/never)
-    check on mobile internet
-
-
-TODO:
-parse xml
-DBus check
-check low power with upower
-check connection with NetworkManager
-unique application
-see packagekit
-
-
-Defer to later:
-NM online and mobile check
-UPower battery check
--- a/main.c	Tue Oct 11 17:36:32 2011 +0200
+++ b/main.c	Thu Oct 20 08:19:22 2011 +0200
@@ -1,5 +1,4 @@
 /*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
  * Copyright (C) 2011 Guido Berhoerster <gber@opensuse.org>
  *
  * Licensed under the GNU General Public License Version 2
@@ -19,103 +18,47 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include "main.h"
-#include "notify.h"
-#include "packagekit.h"
 #include <locale.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <unique/unique.h>
-
-struct UpdatesInfo info;
-
-GtkStatusIcon *tray_icon;
+#include <libnotify/notify.h>
+#include "pkui-icon.h"
 
-void tray_icon_on_click(GtkStatusIcon *status_icon, gpointer user_data)
-{
-	char *argv[2] = { "gpk-update-viewer", NULL };
-	g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
-}
-
-static void tray_menu_destroy(GtkWidget *menu, gpointer userdata)
-{
-	gtk_widget_destroy(menu);
-	g_object_unref(menu);
-}
-
-void tray_icon_on_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
+int
+main(int argc, char **argv)
 {
-	GtkWidget *item;
-	GtkWidget *menu = gtk_menu_new();
-	item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
-	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-	g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_main_quit), user_data);
-	gtk_widget_show(item);
-	g_object_ref(menu);
-	g_object_ref_sink(menu);
-	g_object_unref(menu);
-	g_signal_connect(G_OBJECT(menu), "selection-done", G_CALLBACK(tray_menu_destroy), NULL);
-	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, user_data, button, activate_time);
-	gtk_widget_show_all(GTK_WIDGET(menu));
-}
-
-static GtkStatusIcon *create_tray_icon()
-{
-	tray_icon = gtk_status_icon_new();
-	g_signal_connect(G_OBJECT(tray_icon), "activate", G_CALLBACK(tray_icon_on_click), NULL);
-	g_signal_connect(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(tray_icon_on_menu), NULL);
-	gtk_status_icon_set_from_icon_name(tray_icon, "system-software-update");
-	gtk_status_icon_set_title(tray_icon, _("Software Update(s)"));
-	gtk_status_icon_set_visible(tray_icon, TRUE);
-	return tray_icon;
-}
-
-gboolean periodic_check(gpointer user_data)
-{
-	query_packagekit(&info);
-	if (info.critical > 0) {
-		gtk_status_icon_set_from_icon_name(tray_icon, "software-update-urgent");
-		gtk_status_icon_set_tooltip_text(tray_icon, notify_text(&info));
-	} else
-	if (info.normal > 0) {
-		gtk_status_icon_set_from_icon_name(tray_icon, "software-update-available");
-		gtk_status_icon_set_tooltip_text(tray_icon, notify_text(&info));
-	}
-	return TRUE;
-}
-
-int main(int argc, char **argv)
-{
-	GtkStatusIcon *tray_icon;
-	UniqueApp *app;
-	int exitval = 0;
+	PkuiIcon	*icon;
+	UniqueApp	*app;
+	int		exitval = 0;
 
 	setlocale(LC_ALL, "");
+
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	bind_textdomain_codeset(PACKAGE, "UTF-8");
 	textdomain(PACKAGE);
 
 	gtk_init(&argc, &argv);
+
 	app = unique_app_new(APP_NAME, NULL);
 	if (unique_app_is_running(app)) {
-		g_printerr("Another instance of pk-update-icon is already running. Exiting.\n");
+		g_printerr("Another instance of pk-update-icon is already "
+		    "running. Exiting.\n");
 		exitval = 1;
 		goto out;
 	}
-	tray_icon = create_tray_icon();
-	init_notify();
 
-	periodic_check(NULL);
-	send_notify(&info);
-	// update tray icon and tooltip every 2 hours
-	g_timeout_add_seconds(2*3600, periodic_check, NULL);
+	notify_init(PACKAGE);
+
+	icon = pkui_icon_new(10, 2 * 3600);
 
 	gtk_main();
 
-	g_object_unref(tray_icon);
-
+	g_object_unref(icon);
 out:
 	g_object_unref(app);
+	if (notify_is_initted())
+		notify_uninit ();
 
-	return exitval;
+	return (exitval);
 }
--- a/main.h	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
- * Copyright (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.
- */
-
-#ifndef MAIN_H
-#define MAIN_H
-
-struct UpdatesInfo {
-	int normal;
-	int critical;
-};
-
-#endif
--- a/notify.c	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
- * Copyright (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 "notify.h"
-
-void init_notify()
-{
-	notify_init(PACKAGE);
-}
-
-void send_notify(struct UpdatesInfo *info)
-{
-	NotifyNotification *ntfy;
-	ntfy = notify_notification_new(
-		_("Software Update(s)"),
-		_(notify_text(info)),
-		info->critical > 0 ? "software-update-urgent" : "software-update-available"
-#if (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
-		, NULL
-#endif
-	);
-	notify_notification_set_timeout(ntfy, 3000);
-	notify_notification_set_urgency(ntfy, info->critical > 0 ? NOTIFY_URGENCY_CRITICAL : NOTIFY_URGENCY_NORMAL);
-	notify_notification_show(ntfy, NULL);
-}
-
-const char *notify_text(struct UpdatesInfo *info)
-{
-	static char buf[128];
-	if (info->critical > 0) {
-		snprintf(buf, sizeof(buf), _("There are %d software updates available, %d of them critical."), info->normal + info->critical, info->critical);
-	} else {
-		snprintf(buf, sizeof(buf), _("There are %d software updates available."), info->normal);
-	}
-	return buf;
-}
--- a/notify.h	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
- * Copyright (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.
- */
-
-#ifndef NOTIFY_H
-#define NOTIFY_H
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <libnotify/notify.h>
-#include "main.h"
-
-void init_notify();
-void send_notify(struct UpdatesInfo *info);
-const char *notify_text(struct UpdatesInfo *info);
-
-#endif
--- a/packagekit.c	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
- *
- * 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 <packagekit-glib2/packagekit.h>
-#include "packagekit.h"
-
-void process(PkPackage *pkg, struct UpdatesInfo *info)
-{
-	PkInfoEnum e = pk_package_get_info(pkg);
-	switch (e) {
-		case PK_INFO_ENUM_LOW:
-		case PK_INFO_ENUM_ENHANCEMENT:
-		case PK_INFO_ENUM_NORMAL:
-			info->normal++;
-			break;
-		case PK_INFO_ENUM_BUGFIX:
-		case PK_INFO_ENUM_IMPORTANT:
-		case PK_INFO_ENUM_SECURITY:
-			info->critical++;
-			break;
-		default:
-			break;
-	}
-}
-
-void query_packagekit(struct UpdatesInfo *info)
-{
-	PkClient *client = NULL;
-	PkResults *res = NULL;
-	GPtrArray *list = NULL;
-	GFunc process_func = (GFunc)process;
-	client = pk_client_new();
-	res = pk_client_get_updates(client, pk_bitfield_value(PK_FILTER_ENUM_NEWEST), NULL, NULL, NULL, NULL);
-	if (!res) {
-		goto out;
-	}
-	list = pk_results_get_package_array(res);
-	if (!list) {
-		goto out;
-	}
-	info->normal = 0;
-	info->critical = 0;
-	g_ptr_array_foreach(list, process_func, info);
-out:
-	if (list != NULL)
-		g_ptr_array_unref(list);
-	if(res != NULL)
-		g_object_unref(res);
-	if(client != NULL)
-		g_object_unref(client);
-}
--- a/packagekit.h	Tue Oct 11 17:36:32 2011 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2011 Pavol Rusnak <stick@gk2.sk>
- *
- * 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 PACKAGEKIT_H
-#define PACKAGEKIT_H 1
-
-#include "main.h"
-
-void query_packagekit(struct UpdatesInfo *info);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkui-backend.c	Thu Oct 20 08:19:22 2011 +0200
@@ -0,0 +1,418 @@
+/*
+ * (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 <packagekit-glib2/packagekit.h>
+#include "pkui-backend.h"
+
+G_DEFINE_TYPE(PkuiBackend, pkui_backend, G_TYPE_OBJECT)
+
+#define	PKUI_BACKEND_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
+	PKUI_TYPE_BACKEND, PkuiBackendPrivate))
+
+struct _PkuiBackendPrivate
+{
+	PkuiBackend *backend;
+	PkClient *pk_client;
+	guint periodic_check_id;
+	guint updates_normal;
+	guint updates_important;
+	guint previous_updates_normal;
+	guint previous_updates_important;
+	guint startup_delay;
+	guint check_interval;
+	gint64 last_check;
+	gboolean inhibit_check;
+};
+
+enum
+{
+	PROP_0,
+
+	PROP_UPDATES_NORMAL,
+	PROP_UPDATES_IMPORTANT,
+	PROP_STARTUP_DELAY,
+	PROP_CHECK_INTERVAL,
+	PROP_INHIBIT_CHECK
+};
+
+enum
+{
+	STATE_CHANGED_SIGNAL,
+
+	LAST_SIGNAL
+};
+
+static guint	pkui_backend_signals[LAST_SIGNAL] = { 0, };
+
+static gboolean periodic_check(gpointer data);
+
+static void
+pkui_backend_set_property(GObject *object, guint property_id,
+    const GValue *value, GParamSpec *pspec)
+{
+	PkuiBackend	*self = PKUI_BACKEND(object);
+	gboolean	inhibit_check;
+	gint64		time_to_check;
+
+	switch (property_id) {
+	case PROP_STARTUP_DELAY:
+		self->priv->startup_delay = g_value_get_uint(value);
+
+		g_source_remove(self->priv->periodic_check_id);
+		self->priv->periodic_check_id =
+		    g_timeout_add_seconds(self->priv->startup_delay,
+		    (GSourceFunc)periodic_check, self);
+		break;
+	case PROP_CHECK_INTERVAL:
+		self->priv->check_interval = g_value_get_uint(value);
+
+		/*
+		 * reschedule only if the first run has been completed and
+		 * checks are currently not inibited, otherwise the new
+		 * interval will be picked up anyway
+		 */
+		if (!self->priv->inhibit_check && self->priv->last_check > 0) {
+			time_to_check = g_get_real_time() -
+			    self->priv->last_check;
+			if (time_to_check <= 0)
+				pkui_backend_check_now(self);
+			else {
+				g_source_remove(self->priv->periodic_check_id);
+				self->priv->periodic_check_id =
+				    g_timeout_add_seconds(time_to_check,
+				    periodic_check, self);
+			}
+		}
+		break;
+	case PROP_INHIBIT_CHECK:
+		inhibit_check = g_value_get_boolean(value);
+
+		/*
+		 * when changing self->priv->inhibit_check from FALSE to TRUE
+		 * and the first run has been completed remove the periodic
+		 * check, when changing from TRUE to FALSE either trigger a run
+		 * immediately when more time than self->priv->check_interval
+		 * has passed or just reschedule the next check
+		 */
+		if (!self->priv->inhibit_check && inhibit_check &&
+		    self->priv->periodic_check_id != 0) {
+			g_source_remove(self->priv->periodic_check_id);
+			self->priv->periodic_check_id = 0;
+		} else if (self->priv->inhibit_check && !inhibit_check) {
+			self->priv->periodic_check_id =
+			    g_timeout_add_seconds(self->priv->check_interval,
+			    (GSourceFunc)periodic_check, self);
+		}
+		self->priv->inhibit_check = inhibit_check;
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+		break;
+	}
+}
+
+static void
+pkui_backend_get_property(GObject *object, guint property_id, GValue *value,
+    GParamSpec *pspec)
+{
+	PkuiBackend	*self = PKUI_BACKEND(object);
+
+	switch (property_id) {
+	case PROP_UPDATES_NORMAL:
+		g_value_set_uint(value, self->priv->updates_normal);
+		break;
+	case PROP_UPDATES_IMPORTANT:
+		g_value_set_uint(value, self->priv->updates_important);
+		break;
+	case PROP_STARTUP_DELAY:
+		g_value_set_uint(value, self->priv->startup_delay);
+		break;
+	case PROP_CHECK_INTERVAL:
+		g_value_set_uint(value, self->priv->check_interval);
+		break;
+	case PROP_INHIBIT_CHECK:
+		g_value_set_boolean(value, self->priv->inhibit_check);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+		break;
+	}
+}
+
+static void
+pkui_backend_finalize(GObject *gobject)
+{
+	PkuiBackend	*self = PKUI_BACKEND(gobject);
+
+	if (self->priv->pk_client != NULL)
+		g_object_unref(self->priv->pk_client);
+
+	G_OBJECT_CLASS(pkui_backend_parent_class)->finalize(gobject);
+}
+
+static void
+pkui_backend_class_init(PkuiBackendClass *klass)
+{
+	GObjectClass	*gobject_class = G_OBJECT_CLASS(klass);
+	GParamSpec	*pspec;
+
+	gobject_class->set_property = pkui_backend_set_property;
+	gobject_class->get_property = pkui_backend_get_property;
+	gobject_class->finalize = pkui_backend_finalize;
+
+	pspec = g_param_spec_uint("updates-normal", "Normal updates",
+	    "Number of normal package updates", 0, G_MAXUINT, 0,
+	    G_PARAM_READABLE);
+	g_object_class_install_property(gobject_class, PROP_UPDATES_NORMAL,
+	    pspec);
+
+	pspec = g_param_spec_uint("updates-important", "Important updates",
+	    "Number of important package updates", 0, G_MAXUINT, 0,
+	    G_PARAM_READABLE);
+	g_object_class_install_property(gobject_class, PROP_UPDATES_IMPORTANT,
+	    pspec);
+
+	pspec = g_param_spec_uint("startup-delay", "Startup delay",
+	    "Initial delay in second before the first check for new package "
+	    "updates", 0, G_MAXUINT, 60,
+	    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+	g_object_class_install_property(gobject_class, PROP_STARTUP_DELAY,
+	    pspec);
+
+	pspec = g_param_spec_uint("check-interval", "Check interval",
+	    "Interval in seconds for checking for new package updates", 1,
+	    G_MAXUINT, 3600, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
+	g_object_class_install_property(gobject_class, PROP_CHECK_INTERVAL,
+	    pspec);
+
+	pspec = g_param_spec_boolean("inhibit-check", "Inhibit check",
+	    "Whether to inhibit checks for new package updates", FALSE,
+	    G_PARAM_READWRITE);
+	g_object_class_install_property(gobject_class, PROP_INHIBIT_CHECK,
+	    pspec);
+
+	pkui_backend_signals[STATE_CHANGED_SIGNAL] =
+	    g_signal_newv("state-changed", G_TYPE_FROM_CLASS(gobject_class),
+	    G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, NULL,
+	    NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, NULL);
+
+	g_type_class_add_private(klass, sizeof (PkuiBackendPrivate));
+}
+
+static void
+pkui_backend_init(PkuiBackend *self)
+{
+	self->priv = PKUI_BACKEND_GET_PRIVATE(self);
+
+	self->priv->pk_client = pk_client_new();
+	self->priv->updates_normal = 0;
+	self->priv->updates_important = 0;
+	self->priv->periodic_check_id =
+	    g_timeout_add_seconds(self->priv->startup_delay,
+	    (GSourceFunc)periodic_check, self);
+	self->priv->last_check = 0;
+}
+
+static gboolean
+periodic_check(gpointer data)
+{
+	PkuiBackend	*self = PKUI_BACKEND(data);
+
+	pkui_backend_check_now(self);
+
+	/* rescheduling happens after results are available */
+	return (FALSE);
+}
+
+static void
+process_pk_package_info(PkPackage *pkg, gpointer *user_data)
+{
+	PkuiBackend	*self = PKUI_BACKEND(user_data);
+	PkInfoEnum	type_info = pk_package_get_info(pkg);
+
+	switch (type_info) {
+	case PK_INFO_ENUM_LOW:
+	case PK_INFO_ENUM_ENHANCEMENT:
+	case PK_INFO_ENUM_NORMAL:
+		self->priv->updates_normal++;
+		break;
+	case PK_INFO_ENUM_BUGFIX:
+	case PK_INFO_ENUM_IMPORTANT:
+	case PK_INFO_ENUM_SECURITY:
+		self->priv->updates_important++;
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+get_updates_finished(GObject *object, GAsyncResult *res, gpointer user_data)
+{
+	PkClient	*client = PK_CLIENT(object);
+	PkuiBackend	*self = PKUI_BACKEND(user_data);
+	PkResults	*results = NULL;
+	GError		*error = NULL;
+	GPtrArray	*list = NULL;
+
+	g_debug("PackageKit finished checking for updates");
+
+	results = pk_client_generic_finish(PK_CLIENT(client), res, &error);
+	if (results == NULL) {
+		g_warning("error: %s\n", error->message);
+		g_error_free(error);
+		goto out;
+	}
+
+	list = pk_results_get_package_array(results);
+	if (list == NULL)
+		goto out;
+	self->priv->previous_updates_normal = self->priv->updates_normal;
+	self->priv->previous_updates_important = self->priv->updates_important;
+	self->priv->updates_normal = 0;
+	self->priv->updates_important = 0;
+	g_ptr_array_foreach(list, (GFunc)process_pk_package_info, self);
+
+	if (self->priv->updates_normal != self->priv->previous_updates_normal ||
+	    self->priv->updates_important !=
+	    self->priv->previous_updates_important) {
+		g_debug("normal updates: %d, important updates: %d",
+		    self->priv->updates_normal, self->priv->updates_important);
+		g_debug("emit state-changed");
+
+		g_signal_emit(self, pkui_backend_signals[STATE_CHANGED_SIGNAL],
+		    0);
+	}
+
+out:
+	if (results != NULL)
+		g_object_unref(results);
+	if (list != NULL)
+		g_ptr_array_unref(list);
+
+	self->priv->last_check = g_get_real_time();
+
+	if (!self->priv->inhibit_check) {
+		if (self->priv->periodic_check_id != 0)
+			g_source_remove(self->priv->periodic_check_id);
+		self->priv->periodic_check_id =
+			g_timeout_add_seconds(self->priv->check_interval,
+			    (GSourceFunc)periodic_check, self);
+	}
+}
+
+PkuiBackend *
+pkui_backend_new(guint startup_delay, guint check_interval)
+{
+	g_return_val_if_fail(check_interval > 0, NULL);
+
+	return (g_object_new(PKUI_TYPE_BACKEND, "startup-delay", startup_delay,
+	    "check-interval", check_interval, NULL));
+}
+
+guint
+pkui_backend_get_updates_normal(PkuiBackend *self)
+{
+	guint	updates_normal;
+
+	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
+
+	g_object_get(G_OBJECT(self), "updates-normal", &updates_normal, NULL);
+
+	return (updates_normal);
+}
+
+guint
+pkui_backend_get_updates_important(PkuiBackend *self)
+{
+	guint	updates_important;
+
+	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
+
+	g_object_get(G_OBJECT(self), "updates-important", &updates_important,
+	    NULL);
+
+	return (updates_important);
+}
+
+void
+pkui_backend_set_inhibit_check(PkuiBackend *self, gboolean inhibit_check)
+{
+	g_return_if_fail(PKUI_IS_BACKEND(self));
+
+	g_object_set(G_OBJECT(self), "inhibit-check", inhibit_check, NULL);
+}
+
+gboolean
+pkui_backend_get_inhibit_check(PkuiBackend *self)
+{
+	gboolean	inhibit_check;
+
+	g_return_val_if_fail(PKUI_IS_BACKEND(self), FALSE);
+
+	g_object_get(G_OBJECT(self), "inhibit-check", &inhibit_check, NULL);
+
+	return (inhibit_check);
+}
+
+guint
+pkui_backend_get_startup_interval(PkuiBackend *self)
+{
+	guint	startup_interval;
+
+	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
+
+	g_object_get(G_OBJECT(self), "startup-interval", &startup_interval,
+	    NULL);
+
+	return (startup_interval);
+}
+
+void
+pkui_backend_set_check_interval(PkuiBackend *self, guint check_interval)
+{
+	g_return_if_fail(PKUI_IS_BACKEND(self));
+
+	g_object_set(G_OBJECT(self), "check-interval", check_interval, NULL);
+}
+
+guint
+pkui_backend_get_check_interval(PkuiBackend *self)
+{
+	guint	check_interval;
+
+	g_return_val_if_fail(PKUI_IS_BACKEND(self), 0);
+
+	g_object_get(G_OBJECT(self), "check-interval", &check_interval, NULL);
+
+	return (check_interval);
+}
+
+void
+pkui_backend_check_now(PkuiBackend *self)
+{
+	g_return_if_fail(PKUI_IS_BACKEND(self));
+
+	g_debug("querying PackageKit for updates");
+
+	pk_client_get_updates_async(self->priv->pk_client,
+	    pk_bitfield_value(PK_FILTER_ENUM_NONE), NULL, NULL, NULL,
+	    get_updates_finished, self);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkui-backend.h	Thu Oct 20 08:19:22 2011 +0200
@@ -0,0 +1,69 @@
+/*
+ * (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.
+ */
+
+#ifndef	__PKUI_BACKEND_H
+#define	__PKUI_BACKEND_H
+
+#include <glib-object.h>
+#include <packagekit-glib2/packagekit.h>
+
+G_BEGIN_DECLS
+
+#define	PKUI_TYPE_BACKEND		(pkui_backend_get_type())
+#define	PKUI_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_CAST((obj), \
+	PKUI_TYPE_BACKEND, PkuiBackend))
+#define	PKUI_IS_BACKEND(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+	PKUI_TYPE_BACKEND))
+#define	PKUI_BACKEND_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST((klass), \
+	PKUI_TYPE_BACKEND, PkuiBackendClass))
+#define	PKUI_IS_BACKEND_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), \
+	PKUI_TYPE_BACKEND))
+#define	PKUI_BACKEND_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), \
+	PKUI_TYPE_BACKEND, PkuiBackendClass))
+
+typedef struct _PkuiBackend		PkuiBackend;
+typedef struct _PkuiBackendClass	PkuiBackendClass;
+typedef struct _PkuiBackendPrivate	PkuiBackendPrivate;
+
+struct _PkuiBackend
+{
+	GObject		parent_instance;
+
+	PkuiBackendPrivate *priv;
+};
+
+struct _PkuiBackendClass
+{
+	GObjectClass	parent_class;
+};
+
+PkuiBackend *pkui_backend_new(guint startup_delay, guint interval);
+guint pkui_backend_get_updates_normal(PkuiBackend *self);
+guint pkui_backend_get_updates_important(PkuiBackend *self);
+void pkui_backend_set_inhibit_check(PkuiBackend *self, gboolean inhibit_check);
+gboolean pkui_backend_get_inhibit_check(PkuiBackend *self);
+void pkui_backend_set_check_interval(PkuiBackend *self, guint check_interval);
+guint pkui_backend_get_check_interval(PkuiBackend *self);
+guint pkui_backend_get_startup_interval(PkuiBackend *self);
+void pkui_backend_check_now(PkuiBackend *self);
+
+G_END_DECLS
+
+#endif /* __PKUI_BACKEND_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkui-icon.c	Thu Oct 20 08:19:22 2011 +0200
@@ -0,0 +1,267 @@
+/*
+ * (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 backend_state_changed(PkuiBackend *backend, 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
+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);
+
+	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
+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
+		    );
+	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);
+	static const gchar *argv[] = { UPDATE_VIEWER_COMMAND, NULL };
+	GPid		pid;
+	gboolean	retval;
+
+	g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
+
+	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);
+}
+
+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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkui-icon.h	Thu Oct 20 08:19:22 2011 +0200
@@ -0,0 +1,60 @@
+/*
+ * (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.
+ */
+
+#ifndef	__PKUI_ICON_H
+#define	__PKUI_ICON_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define	PKUI_TYPE_ICON			(pkui_icon_get_type())
+#define	PKUI_ICON(obj)			(G_TYPE_CHECK_INSTANCE_CAST((obj), \
+	PKUI_TYPE_ICON, PkuiIcon))
+#define	PKUI_IS_ICON(obj)		(G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+	PKUI_TYPE_ICON))
+#define	PKUI_ICON_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST((klass), \
+	PKUI_TYPE_ICON, PkuiIconClass))
+#define	PKUI_IS_ICON_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE((klass), \
+	PKUI_TYPE_ICON))
+#define	PKUI_ICON_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS((obj), \
+	PKUI_TYPE_ICON, PkuiIconClass))
+
+typedef struct _PkuiIcon	PkuiIcon;
+typedef struct _PkuiIconClass	PkuiIconClass;
+typedef struct _PkuiIconPrivate	PkuiIconPrivate;
+
+struct _PkuiIcon
+{
+	GObject parent_instance;
+	PkuiIconPrivate *priv;
+};
+
+struct _PkuiIconClass
+{
+	GObjectClass parent_class;
+};
+
+PkuiIcon *pkui_icon_new(guint startup_delay, guint check_interval);
+
+G_END_DECLS
+
+#endif /* __PKUI_ICON_H */
--- a/po/POTFILES.in	Tue Oct 11 17:36:32 2011 +0200
+++ b/po/POTFILES.in	Thu Oct 20 08:19:22 2011 +0200
@@ -1,4 +1,4 @@
 main.c
-notify.c
-packagekit.c
+pkui-icon.c
+pkui-backend.c
 pk-update-icon.desktop.in
--- a/po/de.po	Tue Oct 11 17:36:32 2011 +0200
+++ b/po/de.po	Thu Oct 20 08:19:22 2011 +0200
@@ -7,7 +7,7 @@
 msgstr ""
 "Project-Id-Version: pk-update-icon 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-10-10 19:19+0200\n"
+"POT-Creation-Date: 2011-10-19 23:05+0200\n"
 "PO-Revision-Date: 2011-10-10 19:21+0200\n"
 "Last-Translator: Guido Berhoerster <gber@opensuse.org>\n"
 "Language-Team: German\n"
@@ -17,28 +17,50 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: ../main.c:49
-msgid "_Quit"
-msgstr "_Verlassen"
+#: ../pkui-icon.c:86
+msgid "Software Updates"
+msgstr "Softwareaktualisierungen"
+
+#: ../pkui-icon.c:115
+msgid "_Check for Updates"
+msgstr "Nach Aktualisierungen _Suchen"
 
-#: ../main.c:67 ../notify.c:32
-msgid "Software Update(s)"
-msgstr "Software Update(s)"
+#: ../pkui-icon.c:144
+msgid "Important Software Update"
+msgid_plural "Important Software Updates"
+msgstr[0] "Wichtige Aktualisierung"
+msgstr[1] "Wichtige Aktualisierungen"
+
+#: ../pkui-icon.c:146
+msgid "Software Update"
+msgid_plural "Software Updates"
+msgstr[0] "Softwareaktualisierung"
+msgstr[1] "Softwareaktualisierungen"
 
-#: ../notify.c:42
-#, c-format
-msgid "There are %d software updates available, %d of them critical."
-msgstr "%d Software Updates sind verfügbar, %d davon sind kritisch."
+#: ../pkui-icon.c:153
+msgid "There are %d software updates available, %d of them is important."
+msgid_plural ""
+"There are %d software updates available, %d of them are important."
+msgstr[0] "Es sind %d Softwareaktualisierungen verfügbar, davon ist %d wichtig."
+msgstr[1] "Es sind %d Softwareaktualisierungen verfügbar, davon sind %d wichtig."
 
-#: ../notify.c:44
+#: ../pkui-icon.c:161
+msgid "There is an important software update available."
+msgid_plural "There are %d important software updates available."
+msgstr[0] "Es ist eine wichtige Softwareaktualisierung verfügbar."
+msgstr[1] "Es sind %d wichtige Softwareaktualisierungen verfügbar."
+
+#: ../pkui-icon.c:166
 #, c-format
-msgid "There are %d software updates available."
-msgstr "%d Software Updates sind verfügbar."
+msgid "There is a software update available."
+msgid_plural "There are %d software updates available."
+msgstr[0] "Eine Softwareaktualisierung ist verfügbar."
+msgstr[1] "Es sind %d Softwareaktualisierungen verfügbar."
 
 #: ../pk-update-icon.desktop.in.h:1
 msgid "PackageKit Update Applet"
-msgstr "PackageKit Update Applet"
+msgstr "PackageKit Aktualisierungsapplet"
 
 #: ../pk-update-icon.desktop.in.h:2
 msgid "Update Applet"
-msgstr "Update Applet"
+msgstr "Aktualisierungsapplet"