Mercurial > projects > package-update-indicator
comparison pui-application.c @ 0:6884bb8130ca
Initial revision
author | Guido Berhoerster <guido+pui@berhoerster.name> |
---|---|
date | Sun, 20 May 2018 11:32:57 +0200 |
parents | |
children | 2f04ec9e0506 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:6884bb8130ca |
---|---|
1 /* | |
2 * Copyright (C) 2018 Guido Berhoerster <guido+pui@berhoerster.name> | |
3 * | |
4 * Permission is hereby granted, free of charge, to any person obtaining | |
5 * a copy of this software and associated documentation files (the | |
6 * "Software"), to deal in the Software without restriction, including | |
7 * without limitation the rights to use, copy, modify, merge, publish, | |
8 * distribute, sublicense, and/or sell copies of the Software, and to | |
9 * permit persons to whom the Software is furnished to do so, subject to | |
10 * the following conditions: | |
11 * | |
12 * The above copyright notice and this permission notice shall be included | |
13 * in all copies or substantial portions of the Software. | |
14 * | |
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
22 */ | |
23 | |
24 #define G_SETTINGS_ENABLE_BACKEND | |
25 #include <gio/gsettingsbackend.h> | |
26 #include <glib/gi18n.h> | |
27 #include <libappindicator/app-indicator.h> | |
28 #include <stdlib.h> | |
29 #include <string.h> | |
30 | |
31 #include "pui-common.h" | |
32 #include "pui-application.h" | |
33 #include "pui-types.h" | |
34 #include "pui-backend.h" | |
35 | |
36 #define SETTINGS_ROOT_PATH \ | |
37 "/org/guido-berhoerster/code/package-update-indicator/" | |
38 #define SETTINGS_ROOT_GROUP "General" | |
39 | |
40 struct _PuiApplication { | |
41 GApplication parent_instance; | |
42 GSettings *settings; | |
43 GCancellable *cancellable; | |
44 PuiBackend *backend; | |
45 AppIndicator *indicator; | |
46 GtkWidget *about_dialog; | |
47 GIcon *icons[PUI_STATE_LAST]; | |
48 PuiState state; | |
49 gchar *update_command; | |
50 gchar *error_message; | |
51 }; | |
52 | |
53 G_DEFINE_TYPE(PuiApplication, pui_application, G_TYPE_APPLICATION) | |
54 | |
55 enum { | |
56 PROP_0, | |
57 PROP_UPDATE_COMMAND, | |
58 PROP_LAST | |
59 }; | |
60 | |
61 extern gboolean restart; | |
62 | |
63 static GParamSpec *properties[PROP_LAST] = { NULL }; | |
64 | |
65 static const gchar *icon_names[PUI_STATE_LAST] = { | |
66 [PUI_STATE_INITIAL] = "system-software-update", | |
67 [PUI_STATE_UP_TO_DATE] = "system-software-update", | |
68 [PUI_STATE_NORMAL_UPDATES_AVAILABLE] = "software-update-available", | |
69 [PUI_STATE_IMPORTANT_UPDATES_AVAILABLE] = "software-update-urgent", | |
70 [PUI_STATE_ERROR] = "dialog-warning" | |
71 }; | |
72 | |
73 static const GOptionEntry cmd_options[] = { | |
74 { "debug", '\0', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, | |
75 N_("Enable debugging messages"), NULL }, | |
76 { "quit", 'q', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, | |
77 N_("Quit running instance of Package Update Indicator"), NULL }, | |
78 { "version", 'V', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, | |
79 N_("Print the version number and quit"), NULL }, | |
80 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, NULL, NULL, NULL }, | |
81 { NULL } | |
82 }; | |
83 | |
84 static void pui_application_show_about_dialog(GSimpleAction *, GVariant *, | |
85 gpointer); | |
86 static void pui_application_quit(GSimpleAction *, GVariant *, gpointer); | |
87 static void pui_application_install_updates(GSimpleAction *, GVariant *, | |
88 gpointer); | |
89 | |
90 static const GActionEntry pui_application_actions[] = { | |
91 { "about", pui_application_show_about_dialog }, | |
92 { "quit", pui_application_quit }, | |
93 { "install-updates", pui_application_install_updates } | |
94 }; | |
95 | |
96 static gboolean | |
97 program_exists(const gchar *command_line) | |
98 { | |
99 gboolean is_program_in_path; | |
100 gchar **argv = NULL; | |
101 gchar *program_path; | |
102 | |
103 if (!g_shell_parse_argv(command_line, NULL, &argv, NULL)) { | |
104 return (FALSE); | |
105 } | |
106 program_path = g_find_program_in_path(argv[0]); | |
107 is_program_in_path = (program_path != NULL) ? TRUE : FALSE; | |
108 g_free(program_path); | |
109 g_strfreev(argv); | |
110 | |
111 return (is_program_in_path); | |
112 } | |
113 | |
114 static void | |
115 pui_application_show_about_dialog(GSimpleAction *simple, GVariant *parameter, | |
116 gpointer user_data) | |
117 { | |
118 PuiApplication *self = user_data; | |
119 const gchar *translators; | |
120 | |
121 if (self->about_dialog == NULL) { | |
122 translators = _("Translators"); | |
123 if (strcmp(translators, "Translators") == 0) { | |
124 translators = NULL; | |
125 } | |
126 | |
127 self->about_dialog = gtk_about_dialog_new(); | |
128 g_object_set(G_OBJECT(self->about_dialog), | |
129 "authors", (gchar *[]){ "Guido Berhoerster", NULL }, | |
130 "comments", _("Notify about available software updates"), | |
131 "copyright", "Copyright \xc2\xa9 2018 Guido Berhoerster", | |
132 "license-type", GTK_LICENSE_MIT_X11, | |
133 "logo-icon-name", "system-software-update", | |
134 "translator-credits", translators, | |
135 "version", VERSION, | |
136 "website", "https://code.guido-berhoerster.org/projects/" | |
137 "package-update-indicator/", | |
138 "wrap-license", TRUE, | |
139 NULL); | |
140 } | |
141 | |
142 gtk_dialog_run(GTK_DIALOG(self->about_dialog)); | |
143 gtk_widget_hide(self->about_dialog); | |
144 } | |
145 | |
146 static void | |
147 pui_application_quit(GSimpleAction *simple, GVariant *parameter, | |
148 gpointer user_data) | |
149 { | |
150 PuiApplication *self = user_data; | |
151 | |
152 /* quit the GTK main loop if the about dialog is running */ | |
153 if (self->about_dialog != NULL) { | |
154 gtk_widget_hide(self->about_dialog); | |
155 } | |
156 | |
157 g_application_quit(G_APPLICATION(self)); | |
158 } | |
159 | |
160 static void | |
161 pui_application_install_updates(GSimpleAction *simple, GVariant *parameter, | |
162 gpointer user_data) | |
163 { | |
164 PuiApplication *self = user_data; | |
165 GError *error = NULL; | |
166 | |
167 if (!g_spawn_command_line_async(self->update_command, &error)) { | |
168 g_warning("failed to run update command: %s", error->message); | |
169 g_error_free(error); | |
170 } | |
171 } | |
172 | |
173 static void | |
174 update_ui(PuiApplication *self) | |
175 { | |
176 GSimpleAction *install_updates_action; | |
177 guint important_updates = 0; | |
178 guint normal_updates = 0; | |
179 gchar *title = NULL; | |
180 gchar *body = NULL; | |
181 GApplication *application = G_APPLICATION(self); | |
182 GNotification *notification = NULL; | |
183 | |
184 install_updates_action = | |
185 G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(self), | |
186 "install-updates")); | |
187 | |
188 if ((self->state == PUI_STATE_NORMAL_UPDATES_AVAILABLE) || | |
189 (self->state == PUI_STATE_IMPORTANT_UPDATES_AVAILABLE)) { | |
190 g_object_get(self->backend, | |
191 "important-updates", &important_updates, | |
192 "normal-updates", &normal_updates, NULL); | |
193 } | |
194 | |
195 /* actions */ | |
196 switch (self->state) { | |
197 case PUI_STATE_INITIAL: /* FALLTHGROUGH */ | |
198 case PUI_STATE_UP_TO_DATE: /* FALLTHGROUGH */ | |
199 case PUI_STATE_ERROR: | |
200 g_simple_action_set_enabled(install_updates_action, FALSE); | |
201 break; | |
202 case PUI_STATE_NORMAL_UPDATES_AVAILABLE: /* FALLTHROUGH */ | |
203 case PUI_STATE_IMPORTANT_UPDATES_AVAILABLE: | |
204 g_simple_action_set_enabled(install_updates_action, | |
205 program_exists(self->update_command)); | |
206 break; | |
207 } | |
208 | |
209 /* title and body for indicator and notification */ | |
210 switch (self->state) { | |
211 case PUI_STATE_INITIAL: | |
212 title = g_strdup(""); | |
213 body = g_strdup(""); | |
214 break; | |
215 case PUI_STATE_UP_TO_DATE: | |
216 title = g_strdup(_("Up to Date")); | |
217 body = g_strdup(_("The system is up to date.")); | |
218 break; | |
219 case PUI_STATE_NORMAL_UPDATES_AVAILABLE: | |
220 title = g_strdup(g_dngettext(NULL, "Software Update", | |
221 "Software Updates", (gulong)normal_updates)); | |
222 if (normal_updates == 1) { | |
223 body = g_strdup(_("There is a software update " | |
224 "avaliable.")); | |
225 } else { | |
226 body = g_strdup_printf(_("There are %u " | |
227 "software updates avaliable."), normal_updates); | |
228 } | |
229 break; | |
230 case PUI_STATE_IMPORTANT_UPDATES_AVAILABLE: | |
231 title = g_strdup(g_dngettext(NULL, "Important Software Update", | |
232 "Important Software Updates", (gulong)important_updates)); | |
233 if ((normal_updates == 0) && (important_updates == 1)) { | |
234 body = g_strdup(_("There is an important " | |
235 "software update available.")); | |
236 } else if ((normal_updates == 0) && (important_updates > 1)) { | |
237 body = g_strdup_printf(_("There are %u " | |
238 "important software updates available."), | |
239 important_updates); | |
240 } else if ((normal_updates > 0) && (important_updates == 1)) { | |
241 body = g_strdup_printf(_("There are %u " | |
242 "software updates available, " | |
243 "one of them is important."), | |
244 normal_updates + important_updates); | |
245 } else { | |
246 body = g_strdup_printf(_("There are %u " | |
247 "software updates available, " | |
248 "%u of them are important."), | |
249 normal_updates + important_updates, | |
250 important_updates); | |
251 } | |
252 break; | |
253 case PUI_STATE_ERROR: | |
254 title = g_strdup(self->error_message); | |
255 break; | |
256 } | |
257 | |
258 /* indicator */ | |
259 switch (self->state) { | |
260 case PUI_STATE_INITIAL: | |
261 app_indicator_set_status(self->indicator, | |
262 APP_INDICATOR_STATUS_PASSIVE); | |
263 break; | |
264 case PUI_STATE_UP_TO_DATE: /* FALLTHGROUGH */ | |
265 case PUI_STATE_NORMAL_UPDATES_AVAILABLE: /* FALLTHGROUGH */ | |
266 case PUI_STATE_IMPORTANT_UPDATES_AVAILABLE: /* FALLTHGROUGH */ | |
267 case PUI_STATE_ERROR: | |
268 app_indicator_set_status(self->indicator, | |
269 APP_INDICATOR_STATUS_ACTIVE); | |
270 break; | |
271 } | |
272 app_indicator_set_icon_full(self->indicator, icon_names[self->state], | |
273 title); | |
274 | |
275 /* notification */ | |
276 switch (self->state) { | |
277 case PUI_STATE_INITIAL: /* FALLTHGROUGH */ | |
278 case PUI_STATE_UP_TO_DATE: /* FALLTHGROUGH */ | |
279 case PUI_STATE_ERROR: | |
280 /* withdraw exisiting notification */ | |
281 g_application_withdraw_notification(application, | |
282 "package-updates"); | |
283 break; | |
284 case PUI_STATE_NORMAL_UPDATES_AVAILABLE: /* FALLTHGROUGH */ | |
285 case PUI_STATE_IMPORTANT_UPDATES_AVAILABLE: | |
286 /* create notification */ | |
287 notification = g_notification_new(title); | |
288 g_notification_set_body(notification, body); | |
289 g_notification_set_icon(notification, self->icons[self->state]); | |
290 g_notification_set_priority(notification, | |
291 G_NOTIFICATION_PRIORITY_NORMAL); | |
292 if (g_action_get_enabled(G_ACTION(install_updates_action))) { | |
293 g_notification_add_button(notification, | |
294 _("Install Updates"), | |
295 "app.install-updates"); | |
296 } | |
297 g_application_send_notification(application, "package-updates", | |
298 notification); | |
299 break; | |
300 } | |
301 | |
302 if (notification != NULL) { | |
303 g_object_unref(notification); | |
304 } | |
305 | |
306 g_debug("indicator icon: %s, notification title: \"%s\", " | |
307 "notification body: \"%s\"", icon_names[self->state], title, body); | |
308 | |
309 g_free(body); | |
310 g_free(title); | |
311 } | |
312 | |
313 static void | |
314 transition_state(PuiApplication *self) | |
315 { | |
316 PuiState state = self->state; | |
317 guint important_updates; | |
318 guint normal_updates; | |
319 gchar *old_state; | |
320 gchar *new_state; | |
321 | |
322 switch (self->state) { | |
323 case PUI_STATE_INITIAL: /* FALLTHROUGH */ | |
324 case PUI_STATE_UP_TO_DATE: /* FALLTHROUGH */ | |
325 case PUI_STATE_NORMAL_UPDATES_AVAILABLE: /* FALLTHROUGH */ | |
326 case PUI_STATE_IMPORTANT_UPDATES_AVAILABLE: | |
327 if (self->error_message != NULL) { | |
328 state = PUI_STATE_ERROR; | |
329 break; | |
330 } | |
331 | |
332 g_object_get(self->backend, | |
333 "important-updates", &important_updates, | |
334 "normal-updates", &normal_updates, NULL); | |
335 if (important_updates > 0) { | |
336 state = PUI_STATE_IMPORTANT_UPDATES_AVAILABLE; | |
337 } else if (normal_updates > 0) { | |
338 state = PUI_STATE_NORMAL_UPDATES_AVAILABLE; | |
339 } else { | |
340 state = PUI_STATE_UP_TO_DATE; | |
341 } | |
342 break; | |
343 case PUI_STATE_ERROR: | |
344 break; | |
345 } | |
346 | |
347 if (state != self->state) { | |
348 old_state = pui_types_enum_to_string(PUI_TYPE_STATE, | |
349 self->state); | |
350 new_state = pui_types_enum_to_string(PUI_TYPE_STATE, state); | |
351 g_debug("state %s -> %s", old_state, new_state); | |
352 | |
353 self->state = state; | |
354 update_ui(self); | |
355 | |
356 g_free(new_state); | |
357 g_free(old_state); | |
358 } | |
359 } | |
360 | |
361 static void | |
362 on_backend_restart_required(PuiBackend *backend, gpointer user_data) | |
363 { | |
364 PuiApplication *self = user_data; | |
365 | |
366 restart = TRUE; | |
367 g_action_group_activate_action(G_ACTION_GROUP(G_APPLICATION(self)), | |
368 "quit", NULL); | |
369 } | |
370 | |
371 static void | |
372 on_backend_state_changed(PuiBackend *backend, gpointer user_data) | |
373 { | |
374 PuiApplication *self = user_data; | |
375 | |
376 transition_state(self); | |
377 } | |
378 | |
379 static void | |
380 on_pui_backend_finished(GObject *source_object, GAsyncResult *result, | |
381 gpointer user_data) | |
382 { | |
383 PuiApplication *self = user_data; | |
384 GError *error = NULL; | |
385 | |
386 self->backend = pui_backend_new_finish(result, &error); | |
387 if (self->backend == NULL) { | |
388 g_warning("failed to instantiate backend: %s", error->message); | |
389 g_free(self->error_message); | |
390 g_error_free(error); | |
391 self->error_message = g_strdup(_("Update notifications " | |
392 "are not supported.")); | |
393 transition_state(self); | |
394 return; | |
395 } | |
396 | |
397 g_settings_bind(self->settings, "refresh-interval", self->backend, | |
398 "refresh-interval", G_SETTINGS_BIND_GET); | |
399 | |
400 transition_state(self); | |
401 | |
402 g_signal_connect(self->backend, "restart-required", | |
403 G_CALLBACK(on_backend_restart_required), self); | |
404 g_signal_connect(self->backend, "state-changed", | |
405 G_CALLBACK(on_backend_state_changed), self); | |
406 } | |
407 | |
408 static void | |
409 pui_application_startup(GApplication *application) | |
410 { | |
411 PuiApplication *self = PUI_APPLICATION(application); | |
412 gsize i; | |
413 gchar *settings_filename; | |
414 GSettingsBackend *settings_backend; | |
415 GtkWidget *menu; | |
416 GtkWidget *menu_item; | |
417 | |
418 G_APPLICATION_CLASS(pui_application_parent_class)->startup(application); | |
419 | |
420 /* create actions */ | |
421 g_action_map_add_action_entries(G_ACTION_MAP(self), | |
422 pui_application_actions, G_N_ELEMENTS(pui_application_actions), | |
423 self); | |
424 | |
425 /* load icons */ | |
426 for (i = 0; i < G_N_ELEMENTS(self->icons); i++) { | |
427 self->icons[i] = g_themed_icon_new(icon_names[i]); | |
428 } | |
429 | |
430 /* create settings */ | |
431 settings_filename = g_build_filename(g_get_user_config_dir(), PACKAGE, | |
432 PACKAGE ".conf", NULL); | |
433 settings_backend = g_keyfile_settings_backend_new(settings_filename, | |
434 SETTINGS_ROOT_PATH, SETTINGS_ROOT_GROUP); | |
435 self->settings = g_settings_new_with_backend(APPLICATION_ID, | |
436 settings_backend); | |
437 g_settings_bind(self->settings, "update-command", self, | |
438 "update-command", G_SETTINGS_BIND_GET); | |
439 | |
440 /* start instantiating backend */ | |
441 pui_backend_new_async(self->cancellable, on_pui_backend_finished, self); | |
442 | |
443 /* create indicator */ | |
444 self->indicator = app_indicator_new(APPLICATION_ID, | |
445 "system-software-update", | |
446 APP_INDICATOR_CATEGORY_APPLICATION_STATUS); | |
447 | |
448 /* build menu */ | |
449 menu = gtk_menu_new(); | |
450 gtk_widget_insert_action_group(GTK_WIDGET(menu), "app", | |
451 G_ACTION_GROUP(self)); | |
452 | |
453 menu_item = gtk_menu_item_new_with_label(_("Install " | |
454 "Updates\342\200\246")); | |
455 gtk_actionable_set_action_name(GTK_ACTIONABLE(menu_item), | |
456 "app.install-updates"); | |
457 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); | |
458 | |
459 menu_item = gtk_menu_item_new_with_label(_("About\342\200\246")); | |
460 gtk_actionable_set_action_name(GTK_ACTIONABLE(menu_item), | |
461 "app.about"); | |
462 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); | |
463 | |
464 gtk_widget_show_all(menu); | |
465 app_indicator_set_menu(self->indicator, GTK_MENU(menu)); | |
466 | |
467 update_ui(self); | |
468 | |
469 /* keep GApplication running */ | |
470 g_application_hold(application); | |
471 | |
472 g_object_unref(settings_backend); | |
473 g_free(settings_filename); | |
474 } | |
475 | |
476 static void | |
477 pui_application_shutdown(GApplication *application) | |
478 { | |
479 GApplicationClass *application_class = | |
480 G_APPLICATION_CLASS(pui_application_parent_class); | |
481 | |
482 application_class->shutdown(application); | |
483 } | |
484 | |
485 static gint | |
486 pui_application_handle_local_options(GApplication *application, | |
487 GVariantDict *options) | |
488 { | |
489 gchar *messages_debug; | |
490 gchar **args = NULL; | |
491 GError *error = NULL; | |
492 | |
493 /* filename arguments are not allowed */ | |
494 if (g_variant_dict_lookup(options, G_OPTION_REMAINING, "^a&ay", | |
495 &args)) { | |
496 g_printerr("invalid argument: \"%s\"\n", args[0]); | |
497 g_free(args); | |
498 return (1); | |
499 } | |
500 | |
501 if (g_variant_dict_contains(options, "version")) { | |
502 g_print("%s %s\n", PACKAGE, VERSION); | |
503 | |
504 /* quit */ | |
505 return (0); | |
506 } | |
507 | |
508 if (g_variant_dict_contains(options, "debug")) { | |
509 /* enable debug logging */ | |
510 messages_debug = g_strjoin(":", G_LOG_DOMAIN, | |
511 g_getenv("G_MESSAGES_DEBUG"), NULL); | |
512 g_setenv("G_MESSAGES_DEBUG", messages_debug, TRUE); | |
513 g_free(messages_debug); | |
514 } | |
515 | |
516 /* | |
517 * register with the session bus so that it is possible to discern | |
518 * between remote and primary instance and that remote actions can be | |
519 * invoked, this causes the startup signal to be emitted which, in case | |
520 * of the primary instance, starts to instantiate the | |
521 * backend with the given values | |
522 */ | |
523 if (!g_application_register(application, NULL, &error)) { | |
524 g_critical("g_application_register: %s", error->message); | |
525 g_error_free(error); | |
526 return (1); | |
527 } | |
528 | |
529 if (g_variant_dict_contains(options, "quit")) { | |
530 /* only valid if a remote instance is running */ | |
531 if (!g_application_get_is_remote(application)) { | |
532 g_printerr("%s is not running\n", g_get_prgname()); | |
533 return (1); | |
534 } | |
535 | |
536 /* signal remote instance to quit */ | |
537 g_action_group_activate_action(G_ACTION_GROUP(application), | |
538 "quit", NULL); | |
539 | |
540 /* quit local instance */ | |
541 return (0); | |
542 } | |
543 | |
544 /* proceed with default command line processing */ | |
545 return (-1); | |
546 } | |
547 | |
548 static void | |
549 pui_application_activate(GApplication *application) { | |
550 GApplicationClass *application_class = | |
551 G_APPLICATION_CLASS(pui_application_parent_class); | |
552 | |
553 /* do nothing, implementation required by GApplication */ | |
554 | |
555 application_class->activate(application); | |
556 } | |
557 | |
558 static void | |
559 pui_application_set_property(GObject *object, guint property_id, | |
560 const GValue *value, GParamSpec *pspec) | |
561 { | |
562 PuiApplication *self = PUI_APPLICATION(object); | |
563 | |
564 switch (property_id) { | |
565 case PROP_UPDATE_COMMAND: | |
566 g_free(self->update_command); | |
567 self->update_command = g_value_dup_string(value); | |
568 g_debug("property \"update-command\" set to \"%s\"", | |
569 self->update_command); | |
570 break; | |
571 default: | |
572 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); | |
573 break; | |
574 } | |
575 } | |
576 | |
577 static void | |
578 pui_application_get_property(GObject *object, guint property_id, GValue *value, | |
579 GParamSpec *pspec) | |
580 { | |
581 PuiApplication *self = PUI_APPLICATION(object); | |
582 | |
583 switch (property_id) { | |
584 case PROP_UPDATE_COMMAND: | |
585 g_value_set_string(value, self->update_command); | |
586 break; | |
587 default: | |
588 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); | |
589 break; | |
590 } | |
591 } | |
592 | |
593 static void | |
594 pui_application_dispose(GObject *object) | |
595 { | |
596 PuiApplication *self = PUI_APPLICATION(object); | |
597 gsize i; | |
598 | |
599 if (self->settings != NULL) { | |
600 g_signal_handlers_disconnect_by_data(self->settings, self); | |
601 g_clear_object(&self->settings); | |
602 } | |
603 | |
604 if (self->cancellable != NULL) { | |
605 g_cancellable_cancel(self->cancellable); | |
606 g_clear_object(&self->cancellable); | |
607 } | |
608 | |
609 if (self->backend != NULL) { | |
610 g_clear_object(&self->backend); | |
611 } | |
612 | |
613 if (self->indicator != NULL) { | |
614 g_clear_object(&self->indicator); | |
615 } | |
616 | |
617 if (self->about_dialog != NULL) { | |
618 g_clear_pointer(&self->about_dialog, | |
619 (GDestroyNotify)(gtk_widget_destroy)); | |
620 } | |
621 | |
622 for (i = 0; i < G_N_ELEMENTS(self->icons); i++) { | |
623 if (self->icons[i] != NULL) { | |
624 g_clear_object(&self->icons[i]); | |
625 } | |
626 } | |
627 | |
628 G_OBJECT_CLASS(pui_application_parent_class)->dispose(object); | |
629 } | |
630 | |
631 static void | |
632 pui_application_finalize(GObject *object) | |
633 { | |
634 PuiApplication *self = PUI_APPLICATION(object); | |
635 | |
636 g_free(self->update_command); | |
637 g_free(self->error_message); | |
638 | |
639 G_OBJECT_CLASS(pui_application_parent_class)->finalize(object); | |
640 } | |
641 | |
642 static void | |
643 pui_application_class_init(PuiApplicationClass *klass) | |
644 { | |
645 GObjectClass *object_class = G_OBJECT_CLASS(klass); | |
646 GApplicationClass *application_class = G_APPLICATION_CLASS(klass); | |
647 | |
648 object_class->set_property = pui_application_set_property; | |
649 object_class->get_property = pui_application_get_property; | |
650 object_class->dispose = pui_application_dispose; | |
651 object_class->finalize = pui_application_finalize; | |
652 | |
653 properties[PROP_UPDATE_COMMAND] = g_param_spec_string("update-command", | |
654 "Update command", "Command for installing updates", NULL, | |
655 G_PARAM_READWRITE); | |
656 | |
657 g_object_class_install_properties(object_class, PROP_LAST, properties); | |
658 | |
659 application_class->startup = pui_application_startup; | |
660 application_class->shutdown = pui_application_shutdown; | |
661 application_class->handle_local_options = | |
662 pui_application_handle_local_options; | |
663 application_class->activate = pui_application_activate; | |
664 } | |
665 | |
666 static void | |
667 pui_application_init(PuiApplication *self) | |
668 { | |
669 g_application_add_main_option_entries(G_APPLICATION(self), | |
670 cmd_options); | |
671 | |
672 self->cancellable = g_cancellable_new(); | |
673 } | |
674 | |
675 PuiApplication * | |
676 pui_application_new(void) | |
677 { | |
678 return (g_object_new(PUI_TYPE_APPLICATION, "application-id", | |
679 APPLICATION_ID, NULL)); | |
680 } |