comparison pkui-icon.c @ 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
children 9537882d759f
comparison
equal deleted inserted replaced
13:dca97330d81e 14:64f05992d8ec
1 /*
2 * (C) 2011 Guido Berhoerster <gber@opensuse.org>
3 *
4 * Licensed under the GNU General Public License Version 2
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <glib/gi18n.h>
22 #include <libnotify/notify.h>
23 #include "pkui-backend.h"
24 #include "pkui-icon.h"
25
26 G_DEFINE_TYPE(PkuiIcon, pkui_icon, G_TYPE_OBJECT)
27
28 #define UPDATE_VIEWER_COMMAND "/usr/bin/gpk-update-viewer"
29
30 #define PKUI_ICON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
31 PKUI_TYPE_ICON, PkuiIconPrivate))
32
33 struct _PkuiIconPrivate
34 {
35 PkuiBackend *backend;
36
37 GtkStatusIcon *status_icon;
38 GtkWidget *status_icon_popup_menu;
39 NotifyNotification *notification;
40 };
41
42 static GtkWidget* icon_popup_menu_create(PkuiIcon *self);
43 static void icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
44 guint activate_time, gpointer user_data);
45 static void icon_activated(GtkStatusIcon *status_icon, gpointer user_data);
46 static void backend_state_changed(PkuiBackend *backend, gpointer user_data);
47
48 static void
49 pkui_icon_finalize(GObject *gobject)
50 {
51 PkuiIcon *self = PKUI_ICON(gobject);
52
53 gtk_widget_destroy(self->priv->status_icon_popup_menu);
54 g_object_unref(self->priv->status_icon_popup_menu);
55 g_object_unref(self->priv->status_icon);
56 if (self->priv->notification != NULL) {
57 notify_notification_close(self->priv->notification, NULL);
58 g_object_unref(self->priv->notification);
59 }
60 g_object_unref(self->priv->backend);
61
62 G_OBJECT_CLASS(pkui_icon_parent_class)->finalize(gobject);
63 }
64
65 static void
66 pkui_icon_class_init(PkuiIconClass *klass)
67 {
68 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
69
70 gobject_class->finalize = pkui_icon_finalize;
71
72 g_type_class_add_private(klass, sizeof (PkuiIconPrivate));
73 }
74
75 static void
76 pkui_icon_init(PkuiIcon *self)
77 {
78 self->priv = PKUI_ICON_GET_PRIVATE(self);
79
80 gtk_window_set_default_icon_name("system-software-update");
81
82 self->priv->status_icon_popup_menu = icon_popup_menu_create(self);
83 g_object_ref(self->priv->status_icon_popup_menu);
84 g_object_ref_sink(GTK_OBJECT(self->priv->status_icon_popup_menu));
85
86 self->priv->status_icon = gtk_status_icon_new();
87 gtk_status_icon_set_title(self->priv->status_icon,
88 _("Software Updates"));
89 gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
90 g_signal_connect(G_OBJECT(self->priv->status_icon), "activate",
91 G_CALLBACK(icon_activated), self);
92 g_signal_connect(G_OBJECT(self->priv->status_icon), "popup-menu",
93 G_CALLBACK(icon_popup_menu_popup), self);
94
95 self->priv->notification = NULL;
96
97 self->priv->backend = NULL;
98 }
99
100 static void
101 backend_check_now(GtkMenuItem *menu_item, gpointer user_data)
102 {
103 PkuiIcon *self = PKUI_ICON(user_data);
104
105 g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
106
107 pkui_backend_check_now(self->priv->backend);
108 }
109
110 static GtkWidget*
111 icon_popup_menu_create(PkuiIcon *self)
112 {
113 GtkWidget *popup_menu = gtk_menu_new();
114 GtkWidget *item;
115 GtkWidget *image;
116
117 item = gtk_image_menu_item_new_with_mnemonic(_("_Check for Updates"));
118 image = gtk_image_new_from_icon_name(GTK_STOCK_REFRESH,
119 GTK_ICON_SIZE_MENU);
120 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
121 gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), item);
122 g_signal_connect(G_OBJECT(item), "activate",
123 G_CALLBACK(backend_check_now), self);
124
125 gtk_widget_show_all(GTK_WIDGET(popup_menu));
126
127 return (popup_menu);
128 }
129
130 static void
131 icon_popup_menu_popup(GtkStatusIcon *status_icon, guint button,
132 guint activate_time, gpointer user_data)
133 {
134 PkuiIcon *self = PKUI_ICON(user_data);
135
136 gtk_menu_popup(GTK_MENU(self->priv->status_icon_popup_menu), NULL, NULL,
137 NULL, NULL, button, activate_time);
138 }
139
140 static void
141 update_notification(PkuiIcon *self, guint updates_normal,
142 guint updates_important)
143 {
144 gchar *message;
145 gchar *title = updates_important ?
146 ngettext("Important Software Update", "Important Software Updates",
147 updates_important + updates_normal) :
148 ngettext("Software Update", "Software Updates", updates_important +
149 updates_normal);
150 gchar *icon = updates_important ? "software-update-urgent" :
151 "software-update-available";
152
153 if (updates_important > 0)
154 if (updates_normal > 0)
155 message = g_strdup_printf(ngettext("There are %d "
156 "software updates available, %d of them is "
157 "important.", "There are %d software updates "
158 "available, %d of them are important.",
159 updates_important),
160 updates_normal + updates_important,
161 updates_important);
162 else
163 message = g_strdup_printf(ngettext("There is an "
164 "important software update available.", "There are "
165 "%d important software updates available.",
166 updates_important), updates_important);
167 else
168 message = g_strdup_printf(ngettext("There is a software update "
169 "available.", "There are %d software updates available.",
170 updates_normal), updates_normal);
171
172 gtk_status_icon_set_tooltip(self->priv->status_icon, message);
173 gtk_status_icon_set_from_icon_name(self->priv->status_icon, icon);
174 gtk_status_icon_set_visible(self->priv->status_icon, TRUE);
175
176 if (self->priv->notification == NULL)
177 self->priv->notification = notify_notification_new(title,
178 message, icon
179 #if (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
180 , NULL
181 #endif
182 );
183 else
184 notify_notification_update(self->priv->notification, title,
185 message, icon);
186
187 notify_notification_set_timeout(self->priv->notification,
188 NOTIFY_EXPIRES_NEVER);
189 notify_notification_set_urgency(self->priv->notification,
190 updates_important ? NOTIFY_URGENCY_CRITICAL :
191 NOTIFY_URGENCY_NORMAL);
192 notify_notification_show(self->priv->notification, NULL);
193
194 g_free(message);
195 }
196
197 static void
198 hide_notification(PkuiIcon *self)
199 {
200 gtk_status_icon_set_visible(self->priv->status_icon, FALSE);
201 notify_notification_close(self->priv->notification, NULL);
202 }
203
204
205 static void
206 backend_state_changed(PkuiBackend *backend, gpointer user_data)
207 {
208 PkuiIcon *self = PKUI_ICON(user_data);
209 guint updates_normal;
210 guint updates_important;
211
212 g_return_if_fail(PKUI_IS_BACKEND(backend));
213
214 updates_normal = pkui_backend_get_updates_normal(backend);
215 updates_important = pkui_backend_get_updates_important(backend);
216 if (updates_normal > 0 || updates_important > 0)
217 update_notification(self, updates_normal, updates_important);
218 else if (updates_normal + updates_important == 0)
219 hide_notification(self);
220 }
221
222 static void
223 update_viewer_exited(GPid pid, gint status, gpointer user_data)
224 {
225 PkuiIcon *self = PKUI_ICON(user_data);
226
227 g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
228
229 g_spawn_close_pid(pid);
230
231 pkui_backend_check_now(self->priv->backend);
232 pkui_backend_set_inhibit_check(self->priv->backend, FALSE);
233 }
234
235 static void
236 icon_activated(GtkStatusIcon *status_icon, gpointer user_data)
237 {
238 PkuiIcon *self = PKUI_ICON(user_data);
239 static const gchar *argv[] = { UPDATE_VIEWER_COMMAND, NULL };
240 GPid pid;
241 gboolean retval;
242
243 g_return_if_fail(PKUI_IS_BACKEND(self->priv->backend));
244
245 retval = g_spawn_async(NULL, (gchar **)argv, NULL,
246 G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL);
247 if (!retval) {
248 g_warning("Could not execute" UPDATE_VIEWER_COMMAND);
249 return;
250 }
251 g_child_watch_add(pid, (GChildWatchFunc)update_viewer_exited, self);
252
253 pkui_backend_set_inhibit_check(self->priv->backend, TRUE);
254 hide_notification(self);
255 }
256
257 PkuiIcon *
258 pkui_icon_new(guint startup_delay, guint check_interval)
259 {
260 PkuiIcon *icon = g_object_new(PKUI_TYPE_ICON, NULL);
261
262 icon->priv->backend = pkui_backend_new(startup_delay, check_interval);
263 g_signal_connect(icon->priv->backend, "state-changed",
264 G_CALLBACK(backend_state_changed), icon);
265
266 return (icon);
267 }