summaryrefslogtreecommitdiffstats
path: root/src/applet
diff options
context:
space:
mode:
authorNikola Pajkovsky <npajkovs@redhat.com>2010-08-10 10:21:25 +0200
committerNikola Pajkovsky <npajkovs@redhat.com>2010-08-10 10:21:56 +0200
commit83a6ce9ad4b1828e163dc7172ef603201b748473 (patch)
tree9d0580eba6c01cb5964655df42bafab9de91329b /src/applet
parente84ab7783d05eb7b5f1b55ab44e7c23c85e50516 (diff)
downloadabrt-83a6ce9ad4b1828e163dc7172ef603201b748473.tar.gz
abrt-83a6ce9ad4b1828e163dc7172ef603201b748473.tar.xz
abrt-83a6ce9ad4b1828e163dc7172ef603201b748473.zip
lower case direcotry(no code changed)
Signed-off-by: Nikola Pajkovsky <npajkovs@redhat.com>
Diffstat (limited to 'src/applet')
-rw-r--r--src/applet/Applet.cpp302
-rw-r--r--src/applet/CCApplet.cpp456
-rw-r--r--src/applet/CCApplet.h88
-rw-r--r--src/applet/Makefile.am43
-rw-r--r--src/applet/abrt-applet.desktop.in9
5 files changed, 898 insertions, 0 deletions
diff --git a/src/applet/Applet.cpp b/src/applet/Applet.cpp
new file mode 100644
index 00000000..85ee0db8
--- /dev/null
+++ b/src/applet/Applet.cpp
@@ -0,0 +1,302 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ 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 <dbus/dbus-shared.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(S) gettext(S)
+#else
+# define _(S) (S)
+#endif
+#include "abrtlib.h"
+#include "abrt_dbus.h"
+#include "dbus_common.h"
+#include "CCApplet.h"
+
+
+static CApplet* applet;
+
+
+static void Crash(DBusMessage* signal)
+{
+ int r;
+ DBusMessageIter in_iter;
+ dbus_message_iter_init(signal, &in_iter);
+
+ /* 1st param: package */
+ const char* package_name;
+ r = load_val(&in_iter, package_name);
+
+ /* 2nd param: crash_id */
+ const char* crash_id = NULL;
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+ r = load_val(&in_iter, crash_id);
+
+ /* Optional 3rd param: uid */
+ const char* uid_str = NULL;
+ if (r == ABRT_DBUS_MORE_FIELDS)
+ {
+ r = load_val(&in_iter, uid_str);
+ }
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+
+ if (uid_str != NULL)
+ {
+ char *end;
+ errno = 0;
+ unsigned long uid_num = strtoul(uid_str, &end, 10);
+ if (errno || *end != '\0' || uid_num != getuid())
+ {
+ return;
+ }
+ }
+
+ const char* message = _("A crash in the %s package has been detected");
+ if (package_name[0] == '\0')
+ message = _("A crash has been detected");
+ //applet->AddEvent(uid, package_name);
+ applet->SetIconTooltip(message, package_name);
+ applet->ShowIcon();
+
+ /* If this crash seems to be repeating, do not annoy user with popup dialog.
+ * (The icon in the tray is not suppressed)
+ */
+ static time_t last_time = 0;
+ static char* last_package_name = NULL;
+ static char* last_crash_id = NULL;
+ time_t cur_time = time(NULL);
+ if (last_package_name && strcmp(last_package_name, package_name) == 0
+ && last_crash_id && strcmp(last_crash_id, crash_id) == 0
+ && (unsigned)(cur_time - last_time) < 2 * 60 * 60
+ ) {
+ log_msg("repeated crash in %s, not showing the notification", package_name);
+ return;
+ }
+ last_time = cur_time;
+ free(last_package_name);
+ last_package_name = xstrdup(package_name);
+ free(last_crash_id);
+ last_crash_id = xstrdup(crash_id);
+
+ applet->CrashNotify(crash_id, message, package_name);
+}
+
+static void QuotaExceed(DBusMessage* signal)
+{
+ int r;
+ DBusMessageIter in_iter;
+ dbus_message_iter_init(signal, &in_iter);
+ const char* str;
+ r = load_val(&in_iter, str);
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+
+ //if (m_pSessionDBus->has_name("com.redhat.abrt.gui"))
+ // return;
+ applet->ShowIcon();
+ applet->MessageNotify("%s", str);
+}
+
+static void NameOwnerChanged(DBusMessage* signal)
+{
+ int r;
+ DBusMessageIter in_iter;
+ dbus_message_iter_init(signal, &in_iter);
+ const char* name;
+ r = load_val(&in_iter, name);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+
+ /* We are only interested in (dis)appearances of our daemon */
+ if (strcmp(name, "com.redhat.abrt") != 0)
+ return;
+
+ const char* old_owner;
+ r = load_val(&in_iter, old_owner);
+ if (r != ABRT_DBUS_MORE_FIELDS)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+ const char* new_owner;
+ r = load_val(&in_iter, new_owner);
+ if (r != ABRT_DBUS_LAST_FIELD)
+ {
+ error_msg("dbus signal %s: parameter type mismatch", __func__);
+ return;
+ }
+
+// hide icon if it's visible - as NM and don't show it, if it's not
+ if (!new_owner[0])
+ applet->HideIcon();
+}
+
+static DBusHandlerResult handle_message(DBusConnection* conn, DBusMessage* msg, void* user_data)
+{
+ const char* member = dbus_message_get_member(msg);
+
+ VERB1 log("%s(member:'%s')", __func__, member);
+
+ int type = dbus_message_get_type(msg);
+ if (type != DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ log("The message is not a signal. ignoring");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ if (strcmp(member, "NameOwnerChanged") == 0)
+ NameOwnerChanged(msg);
+ else if (strcmp(member, "Crash") == 0)
+ Crash(msg);
+ else if (strcmp(member, "QuotaExceed") == 0)
+ QuotaExceed(msg);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+//TODO: move to abrt_dbus.cpp
+static void die_if_dbus_error(bool error_flag, DBusError* err, const char* msg)
+{
+ if (dbus_error_is_set(err))
+ {
+ error_msg("dbus error: %s", err->message);
+ /*dbus_error_free(&err); - why bother, we will exit in a microsecond */
+ error_flag = true;
+ }
+ if (!error_flag)
+ return;
+ error_msg_and_die("%s", msg);
+}
+
+int main(int argc, char** argv)
+{
+ const char * app_name = "abrt-gui";
+ /* I18n */
+ setlocale(LC_ALL, "");
+#if ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ /* Need to be thread safe */
+ g_thread_init(NULL);
+ gdk_threads_init();
+ gdk_threads_enter();
+
+ /* Parse options */
+ int opt;
+ while ((opt = getopt(argc, argv, "dv")) != -1)
+ {
+ switch (opt)
+ {
+ case 'v':
+ g_verbose++;
+ break;
+ default:
+ error_msg_and_die(
+ "Usage: abrt-applet [-v]\n"
+ "\nOptions:"
+ "\n\t-v\tVerbose"
+ );
+ }
+ }
+ gtk_init(&argc, &argv);
+
+ /* Prevent zombies when we spawn abrt-gui */
+ signal(SIGCHLD, SIG_IGN);
+
+ /* Initialize our (dbus_abrt) machinery: hook _system_ dbus to glib main loop.
+ * (session bus is left to be handled by libnotify, see below) */
+ DBusError err;
+ dbus_error_init(&err);
+ DBusConnection* system_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ die_if_dbus_error(system_conn == NULL, &err, "Can't connect to system dbus");
+ attach_dbus_conn_to_glib_main_loop(system_conn);
+ if (!dbus_connection_add_filter(system_conn, handle_message, NULL, NULL))
+ error_msg_and_die("Can't add dbus filter");
+ /* which messages do we want to be fed to handle_message()? */
+ //signal sender=org.freedesktop.DBus -> path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
+ // string "com.redhat.abrt"
+ // string ""
+ // string ":1.70"
+ dbus_bus_add_match(system_conn, "type='signal',member='NameOwnerChanged'", &err);
+ die_if_dbus_error(false, &err, "Can't add dbus match");
+ //signal sender=:1.73 -> path=/com/redhat/abrt; interface=com.redhat.abrt; member=Crash
+ // string "coreutils-7.2-3.fc11"
+ // string "0"
+ dbus_bus_add_match(system_conn, "type='signal',path='/com/redhat/abrt'", &err);
+ die_if_dbus_error(false, &err, "Can't add dbus match");
+
+ /* Initialize GUI stuff.
+ * Note: inside CApplet ctor, libnotify hooks session dbus
+ * to glib main loop */
+ applet = new CApplet(app_name);
+ /* dbus_abrt cannot handle more than one bus, and we don't really need to.
+ * The only thing we want to do is to announce ourself on session dbus */
+ DBusConnection* session_conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
+ die_if_dbus_error(session_conn == NULL, &err, "Can't connect to session dbus");
+ int r = dbus_bus_request_name(session_conn,
+ "com.redhat.abrt.applet",
+ /* flags */ DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
+ die_if_dbus_error(r != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER, &err,
+ "Problem connecting to dbus, or applet is already running");
+
+ /* show the warning in terminal, as nm-applet does */
+ if (!dbus_bus_name_has_owner(system_conn, ABRTD_DBUS_NAME, &err))
+ {
+ const char* msg = _("ABRT service is not running");
+ puts(msg);
+ }
+
+ /* dbus_bus_request_name can already read some data. Thus while dbus fd hasn't
+ * any data anymore, dbus library can buffer a message or two.
+ * If we don't do this, the data won't be processed until next dbus data arrives.
+ */
+ int cnt = 10;
+ while (dbus_connection_dispatch(system_conn) != DBUS_DISPATCH_COMPLETE && --cnt)
+ continue;
+
+ /* Enter main loop */
+ gtk_main();
+
+ gdk_threads_leave();
+ delete applet;
+ return 0;
+}
diff --git a/src/applet/CCApplet.cpp b/src/applet/CCApplet.cpp
new file mode 100644
index 00000000..5349c7a5
--- /dev/null
+++ b/src/applet/CCApplet.cpp
@@ -0,0 +1,456 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ 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.
+*/
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(S) gettext(S)
+#else
+# define _(S) (S)
+#endif
+#include "abrtlib.h"
+#include "CCApplet.h"
+
+static void on_notify_close(NotifyNotification *notification, gpointer user_data)
+{
+ g_object_unref(notification);
+}
+
+static NotifyNotification *new_warn_notification()
+{
+ NotifyNotification *notification;
+ notification = notify_notification_new(_("Warning"), NULL, NULL, NULL);
+ 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, NOTIFY_EXPIRES_DEFAULT);
+
+ return notification;
+}
+
+
+static void on_hide_cb(GtkMenuItem *menuitem, gpointer applet)
+{
+ if (applet)
+ ((CApplet*)applet)->HideIcon();
+}
+
+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()
+{
+ 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",
+ 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(CApplet *applet)
+{
+ 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(on_hide_cb), applet);
+ 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();
+ if (menu)
+ {
+ 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;
+}
+
+CApplet::CApplet(const char* app_name)
+{
+ m_bDaemonRunning = true;
+ /* set-up icon buffers */
+ m_iAnimator = 0;
+ m_iAnimationStage = ICON_DEFAULT;
+ m_bIconsLoaded = load_icons();
+ /* - animation - */
+ if (m_bIconsLoaded == true)
+ {
+ //FIXME: animation is disabled for now
+ m_pStatusIcon = gtk_status_icon_new_from_pixbuf(icon_stages_buff[ICON_DEFAULT]);
+ }
+ else
+ {
+ m_pStatusIcon = gtk_status_icon_new_from_icon_name("abrt");
+ }
+ notify_init(app_name);
+
+ gtk_status_icon_set_visible(m_pStatusIcon, FALSE);
+
+ g_signal_connect(G_OBJECT(m_pStatusIcon), "activate", GTK_SIGNAL_FUNC(CApplet::OnAppletActivate_CB), this);
+ g_signal_connect(G_OBJECT(m_pStatusIcon), "popup_menu", GTK_SIGNAL_FUNC(CApplet::OnMenuPopup_cb), this);
+
+// SetIconTooltip(_("Pending events: %i"), m_mapEvents.size());
+
+ m_pMenu = create_menu(this);
+}
+
+CApplet::~CApplet()
+{
+ if (notify_is_initted())
+ notify_uninit();
+}
+
+void CApplet::SetIconTooltip(const char *format, ...)
+{
+ va_list args;
+ int n;
+ char *buf;
+
+ va_start(args, format);
+ buf = NULL;
+ n = vasprintf(&buf, format, args);
+ va_end(args);
+
+ gtk_status_icon_set_tooltip_text(m_pStatusIcon, (n >= 0 && buf) ? buf : "");
+ free(buf);
+}
+
+void CApplet::action_report(NotifyNotification *notification, gchar *action, gpointer user_data)
+{
+ CApplet *applet = (CApplet *)user_data;
+ if (applet->m_bDaemonRunning)
+ {
+ pid_t pid = vfork();
+ if (pid < 0)
+ perror_msg("vfork");
+ if (pid == 0)
+ { /* child */
+ char *buf = xasprintf("--report=%s", applet->m_pLastCrashID);
+ signal(SIGCHLD, SIG_DFL); /* undo SIG_IGN in abrt-applet */
+ execl(BIN_DIR"/abrt-gui", "abrt-gui", buf, (char*) NULL);
+ /* Did not find abrt-gui in installation directory. Oh well */
+ /* Trying to find it in PATH */
+ execlp("abrt-gui", "abrt-gui", buf, (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);
+ }
+ gtk_status_icon_set_visible(applet->m_pStatusIcon, false);
+ applet->stop_animate_icon();
+ }
+}
+
+void CApplet::action_open_gui(NotifyNotification *notification, gchar *action, gpointer user_data)
+{
+ CApplet *applet = (CApplet *)user_data;
+ if (applet->m_bDaemonRunning)
+ {
+ 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);
+ }
+ gtk_status_icon_set_visible(applet->m_pStatusIcon, false);
+ applet->stop_animate_icon();
+ }
+}
+
+void CApplet::CrashNotify(const char* crash_id, const char *format, ...)
+{
+ m_pLastCrashID = crash_id;
+ 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(CApplet::action_report),
+ this, NULL);
+ notify_notification_add_action(notification, "OPEN_MAIN_WINDOW", _("Open ABRT"),
+ NOTIFY_ACTION_CALLBACK(CApplet::action_open_gui),
+ this, NULL);
+
+ notify_notification_update(notification, _("Warning"), buf, NULL);
+ free(buf);
+ GError *err = NULL;
+ notify_notification_show(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+}
+
+void CApplet::MessageNotify(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(CApplet::action_open_gui),
+ this, NULL);
+ notify_notification_update(notification, _("Warning"), buf, NULL);
+ free(buf);
+ GError *err = NULL;
+ notify_notification_show(notification, &err);
+ if (err != NULL)
+ {
+ error_msg("%s", err->message);
+ g_error_free(err);
+ }
+}
+
+void CApplet::OnAppletActivate_CB(GtkStatusIcon *status_icon, gpointer user_data)
+{
+ CApplet *applet = (CApplet *)user_data;
+ if (applet->m_bDaemonRunning)
+ {
+ 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");
+ }
+ gtk_status_icon_set_visible(applet->m_pStatusIcon, false);
+ applet->stop_animate_icon();
+ }
+}
+
+void CApplet::OnMenuPopup_cb(GtkStatusIcon *status_icon,
+ guint button,
+ guint activate_time,
+ gpointer user_data)
+{
+ CApplet *applet = (CApplet *)user_data;
+ /* stop the animation */
+ applet->stop_animate_icon();
+
+ if (applet->m_pMenu != NULL)
+ {
+ gtk_menu_popup(GTK_MENU(((CApplet *)user_data)->m_pMenu),
+ NULL, NULL,
+ gtk_status_icon_position_menu,
+ status_icon, button, activate_time);
+ }
+}
+
+void CApplet::ShowIcon()
+{
+ gtk_status_icon_set_visible(m_pStatusIcon, true);
+ /* only animate if all icons are loaded, use the "gtk-warning" instead */
+ if (m_bIconsLoaded)
+ animate_icon();
+}
+
+void CApplet::HideIcon()
+{
+ gtk_status_icon_set_visible(m_pStatusIcon, false);
+ stop_animate_icon();
+}
+
+void CApplet::Disable(const char *reason)
+{
+ /*
+ FIXME: once we have our icon
+ */
+ m_bDaemonRunning = false;
+ GdkPixbuf *gray_scaled;
+ GdkPixbuf *pixbuf = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(),
+ GTK_STOCK_DIALOG_WARNING, 24, GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
+ if (pixbuf)
+ {
+ gray_scaled = gdk_pixbuf_copy(pixbuf);
+ gdk_pixbuf_saturate_and_pixelate(pixbuf, gray_scaled, 0.0, false);
+ gtk_status_icon_set_from_pixbuf(m_pStatusIcon, gray_scaled);
+//do we need to free pixbufs nere?
+ }
+ else
+ error_msg("Can't load icon");
+ SetIconTooltip(reason);
+ ShowIcon();
+}
+
+void CApplet::Enable(const char *reason)
+{
+ /* restore the original icon */
+ m_bDaemonRunning = true;
+ SetIconTooltip(reason);
+ gtk_status_icon_set_from_stock(m_pStatusIcon, GTK_STOCK_DIALOG_WARNING);
+ ShowIcon();
+}
+
+gboolean CApplet::update_icon(void *user_data)
+{
+ CApplet* applet = (CApplet*)user_data;
+ if (applet->m_pStatusIcon && applet->m_iAnimationStage < ICON_STAGE_LAST)
+ {
+ gtk_status_icon_set_from_pixbuf(applet->m_pStatusIcon,
+ applet->icon_stages_buff[applet->m_iAnimationStage++]);
+ }
+ if (applet->m_iAnimationStage == ICON_STAGE_LAST)
+ {
+ applet->m_iAnimationStage = 0;
+ }
+ if (--applet->m_iAnimCountdown == 0)
+ {
+ applet->stop_animate_icon();
+ }
+ return true;
+}
+
+void CApplet::animate_icon()
+{
+ if (m_iAnimator == 0)
+ {
+ m_iAnimator = g_timeout_add(100, update_icon, this);
+ m_iAnimCountdown = 10 * 3; /* 3 sec */
+ }
+}
+
+void CApplet::stop_animate_icon()
+{
+ /* animator should be 0 if icons are not loaded, so this should be safe */
+ if (m_iAnimator != 0)
+ {
+ g_source_remove(m_iAnimator);
+ gtk_status_icon_set_from_pixbuf(m_pStatusIcon, icon_stages_buff[ICON_DEFAULT]);
+ m_iAnimator = 0;
+ }
+}
+
+bool CApplet::load_icons()
+{
+ //FIXME: just a tmp workaround
+ return false;
+ 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)
+ {
+ 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;
+}
+
+
+//int CApplet::AddEvent(int pUUID, const char *pProgname)
+//{
+// m_mapEvents[pUUID] = "pProgname";
+// SetIconTooltip(_("Pending events: %i"), m_mapEvents.size());
+// return 0;
+//}
+//
+//int CApplet::RemoveEvent(int pUUID)
+//{
+// m_mapEvents.erase(pUUID);
+// return 0;
+//}
diff --git a/src/applet/CCApplet.h b/src/applet/CCApplet.h
new file mode 100644
index 00000000..a58ec687
--- /dev/null
+++ b/src/applet/CCApplet.h
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ 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 CC_APPLET_H_
+#define CC_APPLET_H_
+
+#include <gtk/gtk.h>
+#include <map>
+#include <string>
+#include <libnotify/notify.h>
+
+class CApplet
+{
+ private:
+ GtkStatusIcon* m_pStatusIcon;
+ GtkWidget *m_pMenu;
+
+// std::map<int, std::string> m_mapEvents;
+ bool m_bDaemonRunning;
+ int m_iAnimationStage;
+ guint m_iAnimator;
+ unsigned m_iAnimCountdown;
+ bool m_bIconsLoaded;
+ const char *m_pLastCrashID;
+
+ enum ICON_STAGES
+ {
+ ICON_DEFAULT,
+ ICON_STAGE1,
+ ICON_STAGE2,
+ ICON_STAGE3,
+ ICON_STAGE4,
+ ICON_STAGE5,
+ /* this must be always the last */
+ ICON_STAGE_LAST
+ } icon_stages;
+ GdkPixbuf *icon_stages_buff[ICON_STAGE_LAST];
+
+ public:
+ CApplet(const char* app_name);
+ ~CApplet();
+ void ShowIcon();
+ void HideIcon();
+ void SetIconTooltip(const char *format, ...);
+ void CrashNotify(const char* crash_id, const char *format, ...);
+ void MessageNotify(const char *format, ...);
+ void Disable(const char *reason);
+ void Enable(const char *reason);
+ // create some event storage, to let user choose
+ // or ask the daemon every time?
+ // maybe just events which occured during current session
+ // map::
+// int AddEvent(int pUUID, const char *pProgname);
+// int RemoveEvent(int pUUID);
+
+ protected:
+ //@@TODO applet menus
+ static void OnAppletActivate_CB(GtkStatusIcon *status_icon, gpointer user_data);
+ //this action should open the reporter dialog directly, without showing the main window
+ static void action_report(NotifyNotification *notification, gchar *action, gpointer user_data);
+ //this action should open the main window
+ static void action_open_gui(NotifyNotification *notification, gchar *action, gpointer user_data);
+ static void OnMenuPopup_cb(GtkStatusIcon *status_icon,
+ guint button,
+ guint activate_time,
+ gpointer user_data);
+ static gboolean update_icon(void *data);
+ void animate_icon();
+ void stop_animate_icon();
+ bool load_icons();
+};
+
+#endif
diff --git a/src/applet/Makefile.am b/src/applet/Makefile.am
new file mode 100644
index 00000000..743c18df
--- /dev/null
+++ b/src/applet/Makefile.am
@@ -0,0 +1,43 @@
+bin_PROGRAMS = abrt-applet
+
+abrt_applet_SOURCES = \
+ Applet.cpp \
+ CCApplet.h CCApplet.cpp
+abrt_applet_CPPFLAGS = \
+ -Wall -Werror \
+ -I$(srcdir)/../../inc \
+ -I$(srcdir)/../../lib/utils \
+ -I/usr/include/glib-2.0 \
+ -I/usr/lib/glib-2.0/include \
+ -DBIN_DIR=\"$(bindir)\" \
+ -DVAR_RUN=\"$(VAR_RUN)\" \
+ -DCONF_DIR=\"$(CONF_DIR)\" \
+ -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \
+ -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \
+ -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \
+ -DICON_DIR=\"${datadir}/abrt/icons/hicolor/48x48/status\" \
+ $(GTK_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ -D_GNU_SOURCE
+# $(LIBNOTIFY_CFLAGS)
+# $(DBUS_GLIB_CFLAGS)
+abrt_applet_LDADD = \
+ ../../lib/utils/libABRTUtils.la \
+ -lglib-2.0 \
+ -lgthread-2.0 \
+ $(DBUS_LIBS) \
+ $(LIBNOTIFY_LIBS) \
+ $(GTK_LIBS)
+# ../../lib/Utils/libABRTdUtils.la
+# $(DL_LIBS)
+
+DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
+
+@INTLTOOL_DESKTOP_RULE@
+
+autostartdir = $(sysconfdir)/xdg/autostart
+autostart_in_files = abrt-applet.desktop.in
+
+autostart_DATA = $(autostart_in_files:.desktop.in=.desktop)
+
+EXTRA_DIST = $(autostart_in_files)
diff --git a/src/applet/abrt-applet.desktop.in b/src/applet/abrt-applet.desktop.in
new file mode 100644
index 00000000..57a6278e
--- /dev/null
+++ b/src/applet/abrt-applet.desktop.in
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Encoding=UTF-8
+_Name=Automatic Bug Reporting Tool
+_Comment=ABRT notification applet
+Icon=abrt
+Exec=abrt-applet
+Terminal=false
+Type=Application
+X-GNOME-Autostart-enabled=true