summaryrefslogtreecommitdiffstats
path: root/src/applet/applet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/applet/applet.c')
-rw-r--r--src/applet/applet.c469
1 files changed, 452 insertions, 17 deletions
diff --git a/src/applet/applet.c b/src/applet/applet.c
index 7609c5d4..70482e6d 100644
--- a/src/applet/applet.c
+++ b/src/applet/applet.c
@@ -19,22 +19,456 @@
#if HAVE_LOCALE_H
# include <locale.h>
#endif
+#include <gtk/gtk.h>
+#include <libnotify/notify.h>
#include <dbus/dbus-shared.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
+
#include "abrtlib.h"
#include "abrt_dbus.h"
-#include "applet_gtk.h"
-//This variable is not used anywhere, remove or change to "abrt" and use it
-const char * app_name = "abrt-gui";
-static struct applet* applet = NULL;
+#define ENABLE_ANIMATION 0
+
+static gboolean persistent_notification;
+static GtkStatusIcon *ap_status_icon;
+static GtkWidget *ap_menu;
+static const char *ap_last_problem_dir;
+//static bool ap_daemon_running;
+#define ap_daemon_running 1
+
+#if ENABLE_ANIMATION
+enum ICON_STAGES
+{
+ ICON_DEFAULT,
+ ICON_STAGE1,
+ ICON_STAGE2,
+ ICON_STAGE3,
+ ICON_STAGE4,
+ ICON_STAGE5,
+ /* this must be always the last */
+ ICON_STAGE_LAST
+};
+static int ap_animation_stage;
+static guint ap_animator;
+static unsigned ap_anim_countdown;
+static bool ap_icons_loaded;
+static GdkPixbuf *ap_icon_stages_buff[ICON_STAGE_LAST];
+#endif
+
+#if ENABLE_ANIMATION
+
+static bool load_icons(void)
+{
+ int stage;
+ for (stage = ICON_DEFAULT; stage < ICON_STAGE_LAST; stage++)
+ {
+ char name[sizeof(ICON_DIR"/abrt%02d.png")];
+ GError *error = NULL;
+ if (snprintf(name, sizeof(ICON_DIR"/abrt%02d.png"), ICON_DIR"/abrt%02d.png", stage) > 0)
+ {
+ ap_icon_stages_buff[stage] = gdk_pixbuf_new_from_file(name, &error);
+ if (error != NULL)
+ {
+ error_msg("Can't load pixbuf from %s, animation is disabled", name);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static gboolean update_icon(void *user_data)
+{
+ if (ap_status_icon && ap_animation_stage < ICON_STAGE_LAST)
+ {
+ gtk_status_icon_set_from_pixbuf(ap_status_icon,
+ ap_icon_stages_buff[ap_animation_stage++]);
+ }
+ if (ap_animation_stage == ICON_STAGE_LAST)
+ {
+ ap_animation_stage = 0;
+ }
+ if (--ap_anim_countdown == 0)
+ {
+ stop_animate_icon();
+ }
+ return true;
+}
+
+static void animate_icon(void)
+{
+ if (ap_animator == 0)
+ {
+ ap_animator = g_timeout_add(100, update_icon, NULL);
+ ap_anim_countdown = 10 * 3; /* 3 sec */
+ }
+}
+
+static void stop_animate_icon(void)
+{
+ /* ap_animator should be 0 if icons are not loaded, so this should be safe */
+ if (ap_animator != 0)
+ {
+ g_source_remove(ap_animator);
+ gtk_status_icon_set_from_pixbuf(ap_status_icon,
+ ap_icon_stages_buff[ICON_DEFAULT]
+ );
+ ap_animator = 0;
+ }
+}
+
+#else
+
+# define animate_icon() ((void)0)
+# define stop_animate_icon() ((void)0)
+
+#endif
+
+static void hide_icon(void)
+{
+ if (ap_status_icon == NULL)
+ return;
+
+ gtk_status_icon_set_visible(ap_status_icon, false);
+ stop_animate_icon();
+}
+
+//this action should open the reporter dialog directly, without showing the main window
+static void action_report(NotifyNotification *notification, gchar *action, gpointer user_data)
+{
+ if (ap_daemon_running)
+ {
+ report_problem_in_dir(ap_last_problem_dir, LIBREPORT_ANALYZE | LIBREPORT_NOWAIT);
+
+ GError *err = NULL;
+ notify_notification_close(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+
+ hide_icon();
+ stop_animate_icon();
+ }
+}
+
+//this action should open the main window
+static void action_open_gui(NotifyNotification *notification, gchar *action, gpointer user_data)
+{
+ if (ap_daemon_running)
+ {
+ pid_t pid = vfork();
+ if (pid < 0)
+ perror_msg("vfork");
+ if (pid == 0)
+ { /* child */
+ signal(SIGCHLD, SIG_DFL); /* undo SIG_IGN in abrt-applet */
+ execl(BIN_DIR"/abrt-gui", "abrt-gui", (char*) NULL);
+ /* Did not find abrt-gui in installation directory. Oh well */
+ /* Trying to find it in PATH */
+ execlp("abrt-gui", "abrt-gui", (char*) NULL);
+ perror_msg_and_die("Can't execute abrt-gui");
+ }
+ GError *err = NULL;
+ notify_notification_close(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+ hide_icon();
+ stop_animate_icon();
+ }
+}
+
+static void on_menu_popup_cb(GtkStatusIcon *status_icon,
+ guint button,
+ guint activate_time,
+ gpointer user_data)
+{
+ stop_animate_icon();
+
+ if (ap_menu != NULL)
+ {
+ gtk_menu_popup(GTK_MENU(ap_menu),
+ NULL, NULL,
+ gtk_status_icon_position_menu,
+ status_icon, button, activate_time);
+ }
+}
-/* Initialize GUI stuff. */
-static void init_applet()
+static void on_notify_close(NotifyNotification *notification, gpointer user_data)
{
- if (applet == NULL)
- applet = applet_new(app_name);
+ g_object_unref(notification);
+}
+
+static NotifyNotification *new_warn_notification(void)
+{
+ NotifyNotification *notification;
+
+/* the fourth argument was removed in libnotify 0.7.0 */
+#if !defined(NOTIFY_VERSION_MINOR) || (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR < 7)
+ notification = notify_notification_new(_("Warning"), NULL, NULL, NULL);
+#else
+ notification = notify_notification_new(_("Warning"), NULL, NULL);
+#endif
+
+ g_signal_connect(notification, "closed", G_CALLBACK(on_notify_close), NULL);
+
+ GdkPixbuf *pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
+ GTK_STOCK_DIALOG_WARNING, 48, GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
+
+ if (pixbuf)
+ notify_notification_set_icon_from_pixbuf(notification, pixbuf);
+ notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);
+ notify_notification_set_timeout(notification,
+ persistent_notification ? NOTIFY_EXPIRES_NEVER
+ : NOTIFY_EXPIRES_DEFAULT);
+
+ return notification;
+}
+
+static void on_about_cb(GtkMenuItem *menuitem, gpointer dialog)
+{
+ if (dialog)
+ {
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_hide(GTK_WIDGET(dialog));
+ }
+}
+
+static GtkWidget *create_about_dialog(void)
+{
+ const char *copyright_str = "Copyright © 2009 Red Hat, Inc\nCopyright © 2010 Red Hat, Inc";
+ const char *license_str = "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."
+ "\n\nThis 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."
+ "\n\nYou should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.";
+
+ const char *website_url = "https://fedorahosted.org/abrt/";
+ const char *authors[] = {"Anton Arapov <aarapov@redhat.com>",
+ "Karel Klic <kklic@redhat.com>",
+ "Jiri Moskovcak <jmoskovc@redhat.com>",
+ "Nikola Pajkovsky <npajkovs@redhat.com>",
+ "Zdenek Prikryl <zprikryl@redhat.com>",
+ "Denys Vlasenko <dvlasenk@redhat.com>",
+ NULL};
+
+ const char *artists[] = {"Patrick Connelly <pcon@fedoraproject.org>",
+ "Lapo Calamandrei",
+ "Jakub Steinar <jsteiner@redhat.com>",
+ NULL};
+
+ const char *comments = _("Notification area applet that notifies users about "
+ "issues detected by ABRT");
+ GtkWidget *about_d = gtk_about_dialog_new();
+ if (about_d)
+ {
+ gtk_window_set_default_icon_name("abrt");
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about_d), VERSION);
+ gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(about_d), "abrt");
+ gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about_d), comments);
+ gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about_d), "ABRT");
+ gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about_d), copyright_str);
+ gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about_d), license_str);
+ gtk_about_dialog_set_wrap_license(GTK_ABOUT_DIALOG(about_d),true);
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about_d), website_url);
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about_d), authors);
+ gtk_about_dialog_set_artists(GTK_ABOUT_DIALOG(about_d), artists);
+ gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about_d), _("translator-credits"));
+ }
+ return about_d;
+}
+
+static GtkWidget *create_menu(void)
+{
+ GtkWidget *menu = gtk_menu_new();
+ GtkWidget *b_quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(b_quit, "activate", gtk_main_quit, NULL);
+ GtkWidget *b_hide = gtk_menu_item_new_with_label(_("Hide"));
+ g_signal_connect(b_hide, "activate", G_CALLBACK(hide_icon), NULL);
+ GtkWidget *b_about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
+ GtkWidget *about_dialog = create_about_dialog();
+ g_signal_connect(b_about, "activate", G_CALLBACK(on_about_cb), about_dialog);
+ GtkWidget *separator = gtk_separator_menu_item_new();
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), b_hide);
+ gtk_widget_show(b_hide);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), b_about);
+ gtk_widget_show(b_about);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
+ gtk_widget_show(separator);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), b_quit);
+ gtk_widget_show(b_quit);
+
+ return menu;
+}
+
+static void on_applet_activate_cb(GtkStatusIcon *status_icon, gpointer user_data)
+{
+ if (ap_daemon_running)
+ {
+ pid_t pid = vfork();
+ if (pid < 0)
+ perror_msg("vfork");
+ if (pid == 0)
+ {
+ /* child */
+ signal(SIGCHLD, SIG_DFL); /* undo SIG_IGN in abrt-applet */
+ execl(BIN_DIR"/abrt-gui", "abrt-gui", (char*) NULL);
+ /* Did not find abrt-gui in installation directory. Oh well */
+ /* Trying to find it in PATH */
+ execlp("abrt-gui", "abrt-gui", (char*) NULL);
+ perror_msg_and_die("Can't execute abrt-gui");
+ }
+ hide_icon();
+ stop_animate_icon();
+ }
+}
+
+static void set_icon_tooltip(const char *format, ...)
+{
+ if (ap_status_icon == NULL)
+ return;
+
+ va_list args;
+ char *buf;
+
+ va_start(args, format);
+ buf = xvasprintf(format, args);
+ va_end(args);
+
+ gtk_status_icon_set_tooltip_text(ap_status_icon, buf);
+ free(buf);
+}
+
+static void show_crash_notification(const char* crash_dir, const char *format, ...)
+{
+ ap_last_problem_dir = crash_dir;
+ va_list args;
+ va_start(args, format);
+ char *buf = xvasprintf(format, args);
+ va_end(args);
+
+ NotifyNotification *notification = new_warn_notification();
+ notify_notification_add_action(notification, "REPORT", _("Report"),
+ NOTIFY_ACTION_CALLBACK(action_report),
+ NULL, NULL);
+ notify_notification_add_action(notification, "default", _("Show"),
+ NOTIFY_ACTION_CALLBACK(action_open_gui),
+ NULL, NULL);
+
+ notify_notification_update(notification, _("A Problem has Occurred"), buf, NULL);
+ free(buf);
+
+ GError *err = NULL;
+ notify_notification_show(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+}
+
+static void show_msg_notification(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ char *buf = xvasprintf(format, args);
+ va_end(args);
+
+ /* we don't want to show any buttons now,
+ maybe later we can add action binded to message
+ like >>Clear old dumps<< for quota exceeded
+ */
+ NotifyNotification *notification = new_warn_notification();
+ notify_notification_add_action(notification, "OPEN_MAIN_WINDOW", _("Open ABRT"),
+ NOTIFY_ACTION_CALLBACK(action_open_gui),
+ NULL, NULL);
+ notify_notification_update(notification, _("A Problem has Occurred"), buf, NULL);
+ free(buf);
+ GError *err = NULL;
+ notify_notification_show(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+}
+
+static void show_icon(void)
+{
+ if (ap_status_icon == NULL)
+ return;
+
+ gtk_status_icon_set_visible(ap_status_icon, true);
+#if ENABLE_ANIMATION
+ /* only animate if all icons are loaded, use the "gtk-warning" instead */
+ if (ap_icons_loaded)
+ animate_icon();
+#endif
+}
+
+#if !defined(NOTIFY_VERSION_MINOR) || (NOTIFY_VERSION_MAJOR == 0 && NOTIFY_VERSION_MINOR >= 6)
+static gboolean server_has_persistence(void)
+{
+ GList *caps;
+ GList *l;
+
+ caps = notify_get_server_caps();
+ if (caps == NULL)
+ {
+ error_msg("Failed to receive server caps");
+ return FALSE;
+ }
+
+ l = g_list_find_custom(caps, "persistence", (GCompareFunc)strcmp);
+
+ list_free_with_free(caps);
+ VERB1 log("notify server %s support pesistence", l ? "DOES" : "DOESN'T");
+ return (l != NULL);
+}
+#else
+# define server_has_persistence() false
+#endif
+
+static void init_applet(void)
+{
+ static bool inited = 0;
+ if (inited)
+ return;
+ inited = 1;
+
+ // ap_daemon_running = true;
+ persistent_notification = server_has_persistence();
+
+ if (!persistent_notification)
+ {
+#if ENABLE_ANIMATION
+ /* set-up icon buffers */
+ if (ICON_DEFAULT != 0)
+ ap_animation_stage = ICON_DEFAULT;
+ ap_icons_loaded = load_icons();
+ /* - animation - */
+ if (ap_icons_loaded == true)
+ {
+ //FIXME: animation is disabled for now
+ ap_status_icon = gtk_status_icon_new_from_pixbuf(ap_icon_stages_buff[ICON_DEFAULT]);
+ }
+ else
+#endif
+ {
+ ap_status_icon = gtk_status_icon_new_from_icon_name("abrt");
+ }
+ hide_icon();
+ g_signal_connect(G_OBJECT(ap_status_icon), "activate", GTK_SIGNAL_FUNC(on_applet_activate_cb), NULL);
+ g_signal_connect(G_OBJECT(ap_status_icon), "popup_menu", GTK_SIGNAL_FUNC(on_menu_popup_cb), NULL);
+ ap_menu = create_menu();
+ }
+
+ notify_init("ABRT");
}
static void Crash(DBusMessage* signal)
@@ -84,9 +518,8 @@ static void Crash(DBusMessage* signal)
if (package_name[0] == '\0')
message = _("A crash has been detected");
init_applet();
- //applet->AddEvent(uid, package_name);
- set_icon_tooltip(applet, message, package_name);
- show_icon(applet);
+ set_icon_tooltip(message, package_name);
+ show_icon();
/* If this crash seems to be repeating, do not annoy user with popup dialog.
* (The icon in the tray is not suppressed)
@@ -108,7 +541,7 @@ static void Crash(DBusMessage* signal)
free(last_dir);
last_dir = xstrdup(dir);
- show_crash_notification(applet, dir, message, package_name);
+ show_crash_notification(dir, message, package_name);
}
static void QuotaExceeded(DBusMessage* signal)
@@ -127,8 +560,8 @@ static void QuotaExceeded(DBusMessage* signal)
//if (m_pSessionDBus->has_name("com.redhat.abrt.gui"))
// return;
init_applet();
- show_icon(applet);
- show_msg_notification(applet, "%s", str);
+ show_icon();
+ show_msg_notification("%s", str);
}
static void NameOwnerChanged(DBusMessage* signal)
@@ -164,8 +597,8 @@ static void NameOwnerChanged(DBusMessage* signal)
}
// hide icon if it's visible - as NM and don't show it, if it's not
- if (applet && !new_owner[0])
- hide_icon(applet);
+ if (!new_owner[0])
+ hide_icon();
}
static DBusHandlerResult handle_message(DBusConnection* conn, DBusMessage* msg, void* user_data)
@@ -295,6 +728,8 @@ int main(int argc, char** argv)
gtk_main();
gdk_threads_leave();
- applet_destroy(applet);
+ if (notify_is_initted())
+ notify_uninit();
+
return 0;
}