Mercurial > projects > pk-update-icon
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 } |