summaryrefslogtreecommitdiff
path: root/src/tracker-applet/tracker-applet.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tracker-applet/tracker-applet.c')
-rw-r--r--src/tracker-applet/tracker-applet.c2162
1 files changed, 2162 insertions, 0 deletions
diff --git a/src/tracker-applet/tracker-applet.c b/src/tracker-applet/tracker-applet.c
new file mode 100644
index 000000000..d5853787b
--- /dev/null
+++ b/src/tracker-applet/tracker-applet.c
@@ -0,0 +1,2162 @@
+/* Tracker Applet - tray icon for the tracker indexing daemon
+ *
+ * Copyright (C) 2007, Saleem Abdulrasool <compnerd@gentoo.org>
+ * Copyright (C) 2007, Jamie McCracken <jamiemcc@blueyonder.co.uk>
+ * Copyright (C) 2008, Nokia
+ *
+ * Portions derived from xscreensaver and gnome-screensaver
+ * Copyright (c) 1991-2004 Jamie Zawinski <jwz@jwz.org>
+ * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <X11/X.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gtk/gtk.h>
+
+#include <libnotify/notify.h>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#include <glade/glade.h>
+
+#include <libtracker/tracker.h>
+
+#include <libtracker-common/tracker-utils.h>
+
+#include "tracker-applet.h"
+#include "tracker-applet-marshallers.h"
+
+#define PROGRAM "tracker-applet"
+#define PROGRAM_NAME N_("Tracker Applet")
+
+#define HOMEPAGE "http://www.tracker-project.org/"
+#define DESCRIPTION "An applet for tracker"
+
+#define DBUS_SERVICE_TRACKER "org.freedesktop.Tracker"
+#define DBUS_PATH_TRACKER "/org/freedesktop/tracker"
+#define DBUS_INTERFACE_TRACKER "org.freedesktop.Tracker"
+
+#define TRAY_ICON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_TRAY_ICON, TrayIconPrivate))
+
+typedef enum {
+ ICON_DEFAULT,
+ ICON_PAUSED,
+ ICON_INDEX1,
+ ICON_INDEX2,
+} IndexIcon;
+
+typedef enum {
+ INDEX_INITIALIZING,
+ INDEX_IDLE,
+ INDEX_BUSY,
+ INDEX_MERGING
+} IndexStateEnum;
+
+typedef enum {
+ PAUSE_NONE,
+ PAUSE_INTERNAL,
+ PAUSE_BATTERY
+} PauseStateEnum;
+
+typedef enum {
+ AUTO_PAUSE_NONE,
+ AUTO_PAUSE_INDEXING,
+ AUTO_PAUSE_MERGING
+} AutoPauseEnum;
+
+typedef struct {
+ gchar *name;
+ gchar *label;
+ GtkWidget *stat_label;
+} Stat_Info;
+
+typedef struct _TrayIconPrivate {
+ GtkStatusIcon *icon;
+ GKeyFile *keyfile;
+ gchar *filename;
+
+ /* Settings */
+ gboolean auto_hide;
+ gboolean disabled;
+ gboolean show_animation;
+ gboolean reindex;
+ AutoPauseEnum auto_pause_setting;
+
+ /* Auto pause vars */
+ gboolean auto_pause_timer_active;
+ time_t auto_pause_last_time_event;
+
+ gboolean user_pause;
+ gboolean auto_pause;
+
+ /* States */
+ IndexStateEnum index_state;
+ PauseStateEnum pause_state;
+ IndexIcon index_icon;
+ gboolean animated;
+ gboolean animated_timer_active;
+ gboolean is_watching_events;
+ gboolean email_indexing;
+ gboolean indexer_stopped;
+
+ /* Status hints */
+ gint items_done;
+ gint items_remaining;
+ gint items_total;
+ gdouble seconds_elapsed;
+
+ /* Main window */
+ GtkMenu *menu;
+
+ gboolean initial_index_msg_shown;
+
+ /* Tracker connection */
+ TrackerClient *tracker;
+
+ /* Stats window table shown */
+ gboolean stat_window_active;
+ gboolean stat_request_pending;
+
+ /* Prefs window */
+ GtkWidget *prefs_window;
+ GtkWidget *chk_animate;
+ GtkWidget *chk_show_icon;
+ GtkWidget *opt_pause_off;
+ GtkWidget *opt_pause_index;
+ GtkWidget *opt_pause_merge;
+ GtkWidget *btn_close;
+} TrayIconPrivate;
+
+static void set_auto_pause (TrayIcon *icon,
+ gboolean pause);
+
+static TrayIcon *main_icon;
+
+static gchar *initial_index_1;
+static gchar *initial_index_2;
+
+static gchar *index_icons[4] = {
+ "tracker-applet-default.png",
+ "tracker-applet-paused.png",
+ "tracker-applet-indexing1.png",
+ "tracker-applet-indexing2.png"
+};
+
+static Stat_Info stat_info[13] = {
+ {"Files", NULL, NULL},
+ {"Folders", NULL, NULL},
+ {"Documents", NULL, NULL},
+ {"Images", NULL, NULL},
+ {"Music", NULL, NULL},
+ {"Videos", NULL, NULL},
+ {"Text", NULL, NULL},
+ {"Development", NULL, NULL},
+ {"Other", NULL, NULL},
+ {"Applications", NULL, NULL},
+ {"Conversations", NULL, NULL},
+ {"Emails", NULL, NULL},
+ {NULL, NULL, NULL},
+};
+
+static gboolean disable_daemon_start;
+
+static GOptionEntry entries[] = {
+ { "disable-daemon-start", 'd', 0, G_OPTION_ARG_NONE, &disable_daemon_start,
+ NULL,
+ NULL
+ },
+ { NULL }
+};
+
+static gboolean
+query_pointer_timeout (Window window)
+{
+ Window root;
+ Window child;
+ int root_x;
+ int root_y;
+ int win_x;
+ int win_y;
+ unsigned int mask;
+
+ gdk_error_trap_push ();
+ XQueryPointer (GDK_DISPLAY (),
+ window,
+ &root, &child, &root_x, &root_y, &win_x, &win_y,
+ &mask);
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+
+ return FALSE;
+}
+
+static void
+set_status_hint (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ const char *status = NULL;
+ GString *hint;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ hint = g_string_new ("Tracker");
+
+ switch (priv->index_state) {
+ case INDEX_INITIALIZING:
+ status = _("Initializing");
+ break;
+ case INDEX_IDLE:
+ status = _("Idle");
+ break;
+ case INDEX_BUSY:
+ status = _("Indexing");
+ break;
+ case INDEX_MERGING:
+ status = _("Merging");
+ break;
+ }
+
+ if (status) {
+ g_string_append (hint, " : ");
+ g_string_append (hint, status);
+ }
+
+ if (priv->user_pause) {
+ status = _("paused by user");
+ } else if (priv->auto_pause) {
+ status = _("paused by system");
+ } else {
+ switch (priv->pause_state) {
+ case PAUSE_INTERNAL:
+ status = _("paused by system");
+ break;
+#if 0
+ case PAUSE_BATTERY:
+ /* FIXME: We need to check if we are on the
+ * battery first, this state purely means we
+ * WILL pause on battery.
+ */
+ status = _("paused by battery");
+ break;
+#endif
+ default:
+ case PAUSE_NONE:
+ status = NULL;
+ break;
+ }
+ }
+
+ if (status) {
+ g_string_append_printf (hint, " (%s)", status);
+ }
+
+ if (priv->index_state == INDEX_BUSY) {
+ gchar *str1;
+ gchar *str2;
+
+ str1 = tracker_seconds_estimate_to_string (priv->seconds_elapsed,
+ FALSE,
+ priv->items_done,
+ priv->items_remaining);
+ str2 = tracker_seconds_to_string (priv->seconds_elapsed, FALSE);
+
+ if (str1) {
+ str1[0] = g_ascii_toupper (str1[0]);
+ }
+
+ if (str2) {
+ str2[0] = g_ascii_toupper (str2[0]);
+ }
+
+ g_string_append_printf (hint,
+ "\n"
+ "\n"
+ "%s : %d of %d\n"
+ "%s : %s\n"
+ "%s : %s",
+ _("Done"),
+ priv->items_done,
+ priv->items_total,
+ _("Estimated"),
+ str1,
+ _("Elapsed"),
+ str2);
+
+ g_free (str2);
+ g_free (str1);
+ }
+
+ if (priv->index_state == INDEX_MERGING) {
+ g_string_append_printf (hint, " %d/%d indexes being merged",
+ priv->items_done,
+ priv->items_total);
+ }
+
+ tray_icon_set_tooltip (icon, hint->str);
+
+ g_string_free (hint, TRUE);
+}
+
+static gboolean
+can_auto_pause (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (priv->user_pause ||
+ priv->pause_state == PAUSE_BATTERY ||
+ priv->disabled ||
+ priv->indexer_stopped) {
+ return FALSE;
+ }
+
+ switch (priv->auto_pause_setting) {
+ case AUTO_PAUSE_NONE:
+ return FALSE;
+ case AUTO_PAUSE_INDEXING:
+ return priv->index_state != INDEX_IDLE;
+ case AUTO_PAUSE_MERGING:
+ return priv->index_state == INDEX_MERGING;
+ }
+
+ return TRUE;
+}
+
+static void
+set_tracker_icon (TrayIconPrivate *priv)
+{
+ const gchar *name;
+ gchar *path;
+
+ name = index_icons[priv->index_icon];
+ path = g_build_filename (SHAREDIR,
+ "tracker",
+ "icons",
+ name,
+ NULL);
+
+ if (g_file_test (path, G_FILE_TEST_EXISTS)) {
+ gtk_status_icon_set_from_file (priv->icon, path);
+ } else {
+ g_critical ("Could not find icon:'%s'", path);
+ }
+
+ g_free (path);
+}
+
+static gboolean
+set_icon (TrayIconPrivate *priv)
+{
+ if (!priv->user_pause) {
+ if (priv->index_state == INDEX_INITIALIZING ||
+ priv->index_state == INDEX_IDLE) {
+ priv->animated = FALSE;
+ priv->animated_timer_active = FALSE;
+
+ if (priv->index_icon != ICON_DEFAULT) {
+ priv->index_icon = ICON_DEFAULT;
+ set_tracker_icon (priv);
+ }
+
+ return FALSE;
+ }
+ }
+
+ if (priv->user_pause ||
+ priv->auto_pause ||
+ priv->pause_state != PAUSE_NONE) {
+ if (priv->index_icon != ICON_PAUSED) {
+ priv->index_icon = ICON_PAUSED;
+ set_tracker_icon (priv);
+ }
+
+ priv->animated = FALSE;
+ priv->animated_timer_active = FALSE;
+
+ return FALSE;
+ }
+
+ if (priv->index_state != INDEX_INITIALIZING &&
+ priv->index_state != INDEX_IDLE) {
+ if (priv->index_icon == ICON_INDEX2 || !priv->show_animation) {
+ priv->index_icon = ICON_DEFAULT;
+ } else if (priv->index_icon != ICON_INDEX1) {
+ priv->index_icon = ICON_INDEX1;
+ } else {
+ priv->index_icon = ICON_INDEX2;
+ }
+
+ set_tracker_icon (priv);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+auto_pause_timeout (gpointer data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+ time_t t;
+
+ icon = data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ t = time (NULL);
+
+ if (priv->indexer_stopped) {
+ return FALSE;
+ }
+
+ if (t >= priv->auto_pause_last_time_event + 2) {
+ set_auto_pause (icon, FALSE);
+ return FALSE;
+ }
+
+ dbus_g_proxy_begin_call (priv->tracker->proxy,
+ "PromptIndexSignals",
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_INVALID);
+
+ return TRUE;
+}
+
+static void
+set_auto_pause (TrayIcon *icon,
+ gboolean pause)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ /* Do not pause/unpause if in user pause */
+ if (priv->user_pause) {
+ priv->auto_pause_timer_active = FALSE;
+ priv->auto_pause = FALSE;
+ return;
+ }
+
+ priv->auto_pause = pause;
+
+ if (pause) {
+ priv->auto_pause_last_time_event = time (NULL);
+
+ if (!priv->auto_pause_timer_active) {
+ g_timeout_add_seconds (2, auto_pause_timeout, icon);
+
+ priv->auto_pause_timer_active = TRUE;
+ tracker_set_bool_option (priv->tracker, "Pause", TRUE, NULL);
+ }
+
+ priv->animated = FALSE;
+ } else {
+ priv->auto_pause_timer_active = FALSE;
+ priv->auto_pause = FALSE;
+
+ tracker_set_bool_option (priv->tracker, "Pause", FALSE, NULL);
+ }
+
+ set_icon (priv);
+}
+
+static void
+start_auto_pause_timer (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (!can_auto_pause (icon)) {
+ return;
+ }
+
+ priv->auto_pause_last_time_event = time (NULL);
+
+ if (!priv->auto_pause_timer_active) {
+ g_timeout_add_seconds (2, auto_pause_timeout, icon);
+ set_auto_pause (icon, TRUE);
+ }
+}
+
+static void
+set_user_pause (TrayIcon *icon,
+ gboolean pause)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ priv->user_pause = pause;
+
+ tracker_set_bool_option (priv->tracker, "Pause", pause, NULL);
+}
+
+static void
+notice_events_inner (Window window,
+ gboolean enable,
+ gboolean top)
+{
+ XWindowAttributes attrs;
+ unsigned long events;
+ Window root;
+ Window parent;
+ Window *kids;
+ unsigned int nkids;
+ int status;
+ GdkWindow *gwindow;
+
+ gwindow = gdk_window_lookup (window);
+ if (gwindow != NULL && window != GDK_ROOT_WINDOW ()) {
+ /* If it's one of ours, don't mess up its event mask. */
+ return;
+ }
+
+ kids = NULL;
+ status = XQueryTree (GDK_DISPLAY (),
+ window,
+ &root,
+ &parent,
+ &kids,
+ &nkids);
+
+ if (status == 0) {
+ if (kids != NULL) {
+ XFree (kids);
+ }
+
+ return;
+ }
+
+ if (window == root) {
+ top = FALSE;
+ }
+
+ memset (&attrs, 0, sizeof (attrs));
+ XGetWindowAttributes (GDK_DISPLAY (), window, &attrs);
+
+ if (enable) {
+ /* Select for KeyPress on all windows that already have it selected */
+ events = ((attrs.all_event_masks | attrs.
+ do_not_propagate_mask) & KeyPressMask);
+
+ /* Keep already selected events. This is important when the
+ * window == GDK_ROOT_WINDOW () since the mask will contain
+ * StructureNotifyMask that is essential for RANDR support */
+ events |= attrs.your_event_mask;
+
+ /* Select for SubstructureNotify on all windows */
+ events |= SubstructureNotifyMask;
+
+ /* Select for PropertyNotify events to get user time changes */
+ events |= PropertyChangeMask;
+
+ /* As with keypress events, only select mouse motion events
+ * for windows which already have them selected. */
+ events |=
+ ((attrs.all_event_masks | attrs.
+ do_not_propagate_mask) & (PointerMotionMask |
+ PointerMotionHintMask));
+ } else {
+ /* We want to disable all events */
+
+ /* Don't mess up the root window */
+ if (window == GDK_ROOT_WINDOW ()) {
+ events = attrs.your_event_mask;
+ } else {
+ events = 0;
+ }
+ }
+
+ /* Select for SubstructureNotify on all windows.
+ * Select for KeyPress on all windows that already have it selected.
+ *
+ * Note that we can't select for ButtonPress, because of X braindamage:
+ * only one client at a time may select for ButtonPress on a given
+ * window, though any number can select for KeyPress. Someone explain
+ * *that* to me.
+ *
+ * So, if the user spends a while clicking the mouse without ever moving
+ * the mouse or touching the keyboard, we won't know that they've been
+ * active, and the screensaver will come on. That sucks, but I don't
+ * know how to get around it.
+ *
+ * Since X presents mouse wheels as clicks, this applies to those, too:
+ * scrolling through a document using only the mouse wheel doesn't
+ * count as activity... Fortunately, /proc/interrupts helps, on
+ * systems that have it. Oh, if it's a PS/2 mouse, not serial or USB.
+ * This sucks!
+ */
+ XSelectInput (GDK_DISPLAY (), window, events);
+
+ if (top && (events & KeyPressMask)) {
+ /* Only mention one window per tree */
+ top = FALSE;
+ }
+
+ if (kids != NULL) {
+ while (nkids > 0) {
+ notice_events_inner (kids[--nkids], enable, top);
+ }
+
+ XFree (kids);
+ }
+}
+
+static void
+notice_events (Window window,
+ gboolean enable)
+{
+ gdk_error_trap_push ();
+
+ notice_events_inner (window, enable, TRUE);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+static inline void
+start_notice_events (Window window)
+{
+ notice_events (window, TRUE);
+}
+
+static GdkFilterReturn
+filter_x_events (GdkXEvent *xevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ XEvent *ev;
+ TrayIcon *icon;
+
+ icon = data;
+ ev = xevent;
+
+ switch (ev->xany.type) {
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ start_auto_pause_timer (icon);
+ break;
+
+ case PropertyNotify:
+ if (ev->xproperty.atom == gdk_x11_get_xatom_by_name ("_NET_WM_USER_TIME")) {
+ start_auto_pause_timer (icon);
+ }
+ break;
+
+ case CreateNotify: {
+ Window window;
+
+ window = ev->xcreatewindow.window;
+ start_notice_events (window);
+
+ break;
+ }
+
+ case MotionNotify:
+ if (ev->xmotion.is_hint) {
+ /* need to respond to hints so we continue to get events */
+ g_timeout_add (1000,
+ (GSourceFunc) query_pointer_timeout,
+ GINT_TO_POINTER (ev->xmotion.window));
+ }
+
+ start_auto_pause_timer (icon);
+ break;
+
+ default:
+ break;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static inline void
+stop_notice_events (Window window)
+{
+ notice_events (window, FALSE);
+}
+
+static void
+start_watching_events (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (priv->is_watching_events) {
+ return;
+ }
+
+ gdk_window_add_filter (NULL, (GdkFilterFunc) filter_x_events, icon);
+ start_notice_events (DefaultRootWindow (GDK_DISPLAY ()));
+ priv->is_watching_events = TRUE;
+}
+
+static void
+stop_watching_events (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (!priv->is_watching_events) {
+ return;
+ }
+
+ stop_notice_events (DefaultRootWindow (GDK_DISPLAY ()));
+ gdk_window_remove_filter (NULL, (GdkFilterFunc) filter_x_events, icon);
+ priv->is_watching_events = FALSE;
+}
+
+static void
+tray_icon_class_init (TrayIconClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (TrayIconPrivate));
+}
+
+static void
+activate_icon (GtkStatusIcon *icon,
+ gpointer data)
+{
+ GError *error = NULL;
+ const gchar *command = "tracker-search-tool";
+
+ g_print ("Spawning command:'%s'\n", command);
+
+ if (!g_spawn_command_line_async (command, &error)) {
+ g_warning ("Unable to execute command:'%s', %s",
+ command,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+search_menu_activated (GtkMenuItem *item,
+ gpointer data)
+{
+ activate_icon (NULL, NULL);
+}
+
+static void
+pause_menu_toggled (GtkCheckMenuItem *item,
+ gpointer data)
+{
+ TrayIcon *icon;
+
+ icon = TRAY_ICON (data);
+
+ set_user_pause (icon,
+ gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item)));
+}
+
+static inline void
+set_auto_pause_setting (TrayIcon *icon,
+ AutoPauseEnum auto_pause)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->auto_pause_setting = auto_pause;
+}
+
+static void
+save_options (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ GError *error = NULL;
+ guint length;
+ gchar *contents;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ g_key_file_set_boolean (priv->keyfile,
+ "Applet", "AnimateWhenIndexing",
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->chk_animate)));
+ g_key_file_set_boolean (priv->keyfile,
+ "Applet", "AutoHideIcon",
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->chk_show_icon)));
+ g_key_file_set_integer (priv->keyfile, "Applet", "SmartPause",
+ priv->auto_pause_setting);
+
+ contents = g_key_file_to_data (priv->keyfile, &length, &error);
+
+ if (error) {
+ g_error ("failed: g_key_file_to_data(): %s\n",
+ error->message);
+ return;
+ }
+
+ g_file_set_contents (priv->filename, contents, -1, NULL);
+ g_free (contents);
+}
+
+static void
+prefs_closed (GtkWidget *widget,
+ gpointer data)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (data);
+
+ save_options (data);
+
+ gtk_widget_destroy (priv->prefs_window);
+}
+
+static void
+opt_pause_off_group_changed_cb (GtkToggleButton *check_button,
+ gpointer user_data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+ const gchar *name;
+
+ if (!gtk_toggle_button_get_active (check_button)) {
+ return;
+ }
+
+ icon = user_data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ name = gtk_widget_get_name (GTK_WIDGET (check_button));
+
+ if (g_str_equal (name, "opt_pause_off")) {
+ priv->auto_pause_setting = AUTO_PAUSE_NONE;
+ priv->auto_pause = FALSE;
+ stop_watching_events (icon);
+ return;
+ }
+
+ if (g_str_equal (name, "opt_pause_index")) {
+ priv->auto_pause_setting = AUTO_PAUSE_INDEXING;
+
+ if (can_auto_pause (icon)) {
+ start_watching_events (icon);
+ } else {
+ stop_watching_events (icon);
+ }
+
+ return;
+ }
+
+ if (g_str_equal (name, "opt_pause_merge")) {
+ priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+
+ if (can_auto_pause (icon)) {
+ start_watching_events (icon);
+ } else {
+ stop_watching_events (icon);
+ }
+
+ return;
+ }
+}
+
+static void
+chk_animate_toggled_cb (GtkToggleButton *check_button,
+ gpointer user_data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+
+ icon = user_data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->show_animation = gtk_toggle_button_get_active (check_button);
+}
+
+static void
+chk_show_icon_toggled_cb (GtkToggleButton *check_button,
+ gpointer user_data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+
+ icon = user_data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (gtk_toggle_button_get_active (check_button)) {
+ priv->auto_hide = TRUE;
+ gtk_status_icon_set_visible (priv->icon, FALSE);
+ } else {
+ priv->auto_hide = FALSE;
+ if (!priv->disabled) {
+ gtk_status_icon_set_visible (priv->icon, TRUE);
+ }
+ }
+}
+
+static void
+create_prefs (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ GladeXML *glade;
+ gchar *filename;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ filename = g_build_filename (SHAREDIR,
+ "tracker",
+ "tracker-applet-prefs.glade",
+ NULL);
+ glade = glade_xml_new (filename, NULL, NULL);
+
+ if (!glade) {
+ g_error ("Unable to find locate '%s'", filename);
+ g_free (filename);
+ priv->prefs_window = NULL;
+ return;
+ }
+
+ g_free (filename);
+
+ priv->prefs_window = glade_xml_get_widget (glade, "wnd_prefs");
+ gtk_widget_hide (priv->prefs_window);
+ gtk_window_set_deletable (GTK_WINDOW (priv->prefs_window), FALSE);
+
+ priv->chk_animate = glade_xml_get_widget (glade, "chk_animate");
+ priv->chk_show_icon = glade_xml_get_widget (glade, "chk_show_icon");
+ priv->opt_pause_off = glade_xml_get_widget (glade, "opt_pause_off");
+ priv->opt_pause_index =
+ glade_xml_get_widget (glade, "opt_pause_index");
+ priv->opt_pause_merge =
+ glade_xml_get_widget (glade, "opt_pause_merge");
+ priv->btn_close = glade_xml_get_widget (glade, "btn_close");
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->chk_animate),
+ priv->show_animation);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->chk_show_icon),
+ priv->auto_hide);
+
+ switch (priv->auto_pause_setting) {
+ case AUTO_PAUSE_NONE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+ (priv->opt_pause_off), TRUE);
+ break;
+
+ case AUTO_PAUSE_INDEXING:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+ (priv->opt_pause_index), TRUE);
+ break;
+
+ case AUTO_PAUSE_MERGING:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
+ (priv->opt_pause_merge), TRUE);
+ break;
+ }
+
+ /* connect signal handlers */
+ g_signal_connect (GTK_TOGGLE_BUTTON (priv->chk_animate), "toggled",
+ G_CALLBACK (chk_animate_toggled_cb), main_icon);
+ g_signal_connect (GTK_TOGGLE_BUTTON (priv->chk_show_icon), "toggled",
+ G_CALLBACK (chk_show_icon_toggled_cb), main_icon);
+ g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_off), "toggled",
+ G_CALLBACK (opt_pause_off_group_changed_cb),
+ main_icon);
+ g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_index),
+ "toggled",
+ G_CALLBACK (opt_pause_off_group_changed_cb),
+ main_icon);
+ g_signal_connect (GTK_TOGGLE_BUTTON (priv->opt_pause_merge),
+ "toggled",
+ G_CALLBACK (opt_pause_off_group_changed_cb),
+ main_icon);
+ g_signal_connect (priv->btn_close, "clicked",
+ G_CALLBACK (prefs_closed), main_icon);
+}
+
+static void
+restart_tracker (GtkDialog *dialog,
+ gint response,
+ TrayIcon *icon)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (response == GTK_RESPONSE_YES) {
+ TrayIconPrivate *priv;
+
+ g_print ("Attempting to restart tracker\n");
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ priv->reindex = TRUE;
+
+ dbus_g_proxy_begin_call (priv->tracker->proxy,
+ "Shutdown",
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_BOOLEAN,
+ TRUE, G_TYPE_INVALID);
+ }
+}
+
+static void
+reindex (GtkMenuItem *item,
+ TrayIcon *icon)
+{
+ GtkWidget *dialog;
+ gchar *primary;
+ gchar *secondary;
+
+ primary = g_strdup (_("Re-index your system?"));
+ secondary =
+ g_strdup (_("Indexing can take a long time. "
+ "Are you sure you want to re-index?"));
+
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_YES_NO, primary);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ secondary);
+
+ g_free (primary);
+ g_free (secondary);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), "");
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 14);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (restart_tracker), icon);
+ gtk_widget_show (dialog);
+
+}
+
+static void
+applet_preferences_menu_activated (GtkMenuItem *item,
+ gpointer data)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (data);
+
+ create_prefs (data);
+
+ gtk_widget_show (priv->prefs_window);
+}
+
+static void
+preferences_menu_activated (GtkMenuItem *item,
+ gpointer data)
+{
+ GError *error = NULL;
+ const gchar *command = "tracker-preferences";
+
+ g_print ("Spawning command:'%s'\n", command);
+
+ if (!g_spawn_command_line_async (command, &error)) {
+ g_warning ("Unable to execute command:'%s', %s",
+ command,
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+stat_window_free (GtkWidget *widget,
+ gint arg,
+ gpointer data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+
+ icon = data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->stat_window_active = FALSE;
+
+ gtk_widget_destroy (widget);
+}
+
+static gchar *
+get_stat_value (gchar ***stat_array,
+ const gchar *stat)
+{
+ gchar **array;
+ gint i = 0;
+
+ while (stat_array[i][0]) {
+ array = stat_array[i];
+
+ if (array[0] && strcasecmp (stat, array[0]) == 0) {
+ return array[1];
+ }
+
+ i++;
+ }
+
+ return NULL;
+}
+
+static void
+update_stats (GPtrArray *array,
+ GError *error,
+ gpointer data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+ gchar ***pdata;
+ guint i;
+
+ icon = data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (error) {
+ g_warning ("Could not update statistics, %s",
+ error->message);
+ g_error_free (error);
+ priv->stat_request_pending = FALSE;
+ return;
+ }
+
+ if (!array) {
+ return;
+ }
+
+ i = array->len;
+
+ if (i < 1 || !priv->stat_window_active) {
+ g_ptr_array_free (array, TRUE);
+ return;
+ }
+
+ pdata = (gchar ***) array->pdata;
+
+ for (i = 0; i < 12; i++) {
+ gtk_label_set_text (GTK_LABEL (stat_info[i].stat_label),
+ get_stat_value (pdata, stat_info[i].name));
+ }
+
+ g_ptr_array_free (array, TRUE);
+
+ priv->stat_request_pending = FALSE;
+}
+
+static void
+refresh_stats (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (!priv->stat_window_active || priv->stat_request_pending) {
+ return;
+ }
+
+ priv->stat_request_pending = TRUE;
+
+ tracker_get_stats_async (priv->tracker,
+ (TrackerGPtrArrayReply) update_stats,
+ icon);
+}
+
+static void
+statistics_menu_activated (GtkMenuItem *item,
+ gpointer data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+ GtkWidget *dialog;
+ GtkWidget *table;
+ GtkWidget *title_label;
+ GtkWidget *label_to_add;
+ GtkWidget *dialog_hbox;
+ GtkWidget *info_icon;
+ gint i;
+
+ icon = data;
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ dialog = gtk_dialog_new_with_buttons (_("Statistics"),
+ NULL,
+ GTK_DIALOG_NO_SEPARATOR
+ |
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "gtk-info");
+ gtk_window_set_type_hint (GTK_WINDOW (dialog),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+
+ table = gtk_table_new (13, 2, TRUE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 65);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 8);
+
+ title_label = gtk_label_new (NULL);
+ gtk_label_set_markup (GTK_LABEL (title_label),
+ _("<span weight=\"bold\" size=\"larger\">Index statistics</span>"));
+ gtk_misc_set_alignment (GTK_MISC (title_label), 0, 0);
+ gtk_table_attach_defaults (GTK_TABLE (table), title_label, 0, 2, 0,
+ 1);
+
+ for (i = 0; i < 12; i++) {
+ label_to_add = gtk_label_new (stat_info[i].label);
+
+ gtk_label_set_selectable (GTK_LABEL (label_to_add), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label_to_add), 0, 0);
+ gtk_table_attach_defaults (GTK_TABLE (table), label_to_add, 0,
+ 1, i + 1, i + 2);
+
+ stat_info[i].stat_label = gtk_label_new ("");
+
+ gtk_label_set_selectable (GTK_LABEL (stat_info[i].stat_label),
+ TRUE);
+ gtk_misc_set_alignment (GTK_MISC (stat_info[i].stat_label), 0,
+ 0);
+ gtk_table_attach_defaults (GTK_TABLE (table),
+ stat_info[i].stat_label, 1, 2,
+ i + 1, i + 2);
+ }
+
+ priv->stat_window_active = TRUE;
+
+ refresh_stats (icon);
+
+ dialog_hbox = gtk_hbox_new (FALSE, 12);
+ info_icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (info_icon), 0, 0);
+ gtk_container_add (GTK_CONTAINER (dialog_hbox), info_icon);
+ gtk_container_add (GTK_CONTAINER (dialog_hbox), table);
+
+ gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
+ dialog_hbox);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (stat_window_free),
+ icon);
+
+ gtk_widget_show_all (dialog);
+}
+
+static void
+open_uri (GtkWindow *parent,
+ const char *uri)
+{
+ GtkWidget *dialog;
+ GdkScreen *screen;
+ GError *error = NULL;
+ gchar *cmdline;
+
+ screen = gtk_window_get_screen (parent);
+
+ cmdline = g_strconcat ("xdg-open ", uri, NULL);
+
+ if (gdk_spawn_command_line_on_screen (screen, cmdline, &error) == FALSE) {
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ g_error_free (error);
+ }
+
+ g_free (cmdline);
+}
+
+static void
+about_url_hook (GtkAboutDialog *dialog,
+ const gchar *url,
+ gpointer data)
+{
+ open_uri (GTK_WINDOW (dialog), url);
+}
+
+static void
+about_email_hook (GtkAboutDialog *dialog,
+ const gchar *email,
+ gpointer data)
+{
+ gchar *uri;
+
+ uri = g_strconcat ("mailto:", email, NULL);
+ open_uri (GTK_WINDOW (dialog), uri);
+ g_free (uri);
+}
+
+static void
+about_menu_activated (GtkMenuItem * item, gpointer data)
+{
+ const gchar *authors[] = {
+ "Jamie McCracken <jamiemcc at gnome.org>",
+ "Saleem Abdulrasool <compnerd at compnerd.org>"
+ "Laurent Aguerreche <laurent.aguerreche at free fr>",
+ "Luca Ferretti <elle.uca@libero.it>",
+ "Eugenio <me at eugesoftware com>",
+ "Michael Biebl <mbiebl at gmail com>",
+ "Edward Duffy <eduffy at gmail com>",
+ "Gergan Penkov <gergan at gmail com>",
+ "Deji Akingunola <dakingun gmail com>",
+ "Julien <julienc psychologie-fr org>",
+ "Tom <tpgww@onepost.net>",
+ "Samuel Cormier-Iijima <sciyoshi at gmail com>",
+ "Eskil Bylund <eskil at letterboxes org>",
+ "Ulrik Mikaelsson <ulrik mikaelsson gmail com>",
+ "tobutaz <tobutaz gmail com>",
+ "Mikkel Kamstrup Erlandsen <mikkel kamstrup gmail com>",
+ "Baptiste Mille-Mathias <baptiste.millemathias gmail com>",
+ "Richard Quirk <quirky@zoom.co.uk>",
+ "Marcus Fritzsch <fritschy at googlemail com>",
+ "Jedy Wang <Jedy Wang at Sun COM>",
+ "Anders Aagaard <aagaande at gmail com>",
+ "Fabien VALLON <fabien at sonappart net>",
+ "Jaime Frutos Morales <acidborg at gmail com>",
+ "Christoph Laimburg <christoph laimburg at rolmail net>",
+ NULL
+ };
+
+ const gchar *documenters[] = {
+ NULL
+ };
+
+ const gchar *license[] = {
+ N_("Tracker 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_("Tracker 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_("You should have received a copy of the GNU General Public License " "along with Tracker; if not, write to the Free Software Foundation, Inc., " "59 Temple Place, Suite 330, Boston, MA 02111-1307 USA")
+ };
+
+ gchar *license_trans;
+
+ license_trans = g_strjoin ("\n\n", _(license[0]), _(license[1]),
+ _(license[2]), NULL);
+
+ /* Make URLs and email clickable in about dialog */
+ gtk_about_dialog_set_url_hook (about_url_hook, NULL, NULL);
+ gtk_about_dialog_set_email_hook (about_email_hook, NULL, NULL);
+
+ gtk_show_about_dialog (NULL,
+ "version", VERSION,
+ "comments",
+ _
+ ("Tracker is a tool designed to extract info and metadata about your personal data so that it can be searched easily and quickly"),
+ "copyright",
+ _("Copyright \xC2\xA9 2005-2008 "
+ "The Tracker authors"), "license",
+ license_trans, "wrap-license", TRUE, "authors",
+ authors, "documenters", documenters,
+ /* Translators should localize the following string
+ * which will be displayed at the bottom of the about
+ * box to give credit to the translator(s).
+ */
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "tracker",
+ "website", "http://www.tracker-project.org/",
+ "website-label", _("Tracker Web Site"), NULL);
+
+ g_free (license_trans);
+}
+
+static void
+quit_menu_activated (GtkMenuItem *item,
+ gpointer data)
+{
+ gtk_main_quit ();
+}
+
+static void
+create_context_menu (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ GtkWidget *item, *image;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ priv->menu = (GtkMenu *) gtk_menu_new ();
+
+ item = gtk_check_menu_item_new_with_mnemonic (_("_Pause All Indexing"));
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), FALSE);
+ g_signal_connect (G_OBJECT (item), "toggled",
+ G_CALLBACK (pause_menu_toggled), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Search"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_FIND,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (search_menu_activated), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Re-index"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_FIND,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (reindex),
+ icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Preferences"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_PREFERENCES,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (applet_preferences_menu_activated),
+ icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Indexer Preferences"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_PREFERENCES,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (preferences_menu_activated), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("S_tatistics"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_INFO,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (statistics_menu_activated), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_About"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_ABOUT,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (about_menu_activated), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Quit"));
+ image = gtk_image_new_from_icon_name (GTK_STOCK_QUIT,
+ GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (quit_menu_activated), icon);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), item);
+
+ gtk_widget_show_all (GTK_WIDGET (priv->menu));
+}
+
+static void
+index_finished (DBusGProxy *proxy,
+ gdouble seconds_elapsed,
+ TrayIcon *icon)
+{
+ gchar *str;
+
+ str = tracker_seconds_to_string (seconds_elapsed, FALSE);
+ tray_icon_show_message (icon,
+ "%s in %s.\n"
+ "\n"
+ "%s",
+ _("Tracker has finished indexing your system"),
+ str,
+ _("You can now perform searches by clicking here"));
+ g_free (str);
+
+ stop_watching_events (icon);
+}
+
+static void
+index_state_changed (DBusGProxy *proxy,
+ const gchar *state,
+ gboolean initial_index,
+ gboolean in_merge,
+ gboolean is_manual_paused,
+ gboolean is_battery_paused,
+ gboolean is_io_paused,
+ gboolean is_indexing_enabled,
+ TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ gboolean paused;
+
+ if (!state) {
+ return;
+ }
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+ priv->indexer_stopped = FALSE;
+
+ paused = FALSE;
+
+ if (!is_indexing_enabled) {
+ priv->disabled = TRUE;
+ gtk_status_icon_set_visible (priv->icon, FALSE);
+ return;
+ } else {
+ priv->disabled = FALSE;
+ if (!priv->auto_hide) {
+ gtk_status_icon_set_visible (priv->icon, TRUE);
+ }
+ }
+
+ if (!priv->initial_index_msg_shown && initial_index) {
+ priv->initial_index_msg_shown = TRUE;
+ g_usleep (G_USEC_PER_SEC / 10);
+ tray_icon_show_message (icon,
+ "%s\n"
+ "\n"
+ "%s\n",
+ initial_index_1,
+ initial_index_2);
+ }
+
+ priv->animated = FALSE;
+ priv->pause_state = PAUSE_NONE;
+
+ /* Set pause states if applicable */
+ if (is_manual_paused) {
+ stop_watching_events (icon);
+
+ if (!priv->auto_pause) {
+ priv->user_pause = TRUE;
+ }
+
+ paused = TRUE;
+ } else if (is_battery_paused) {
+ priv->pause_state = PAUSE_BATTERY;
+ paused = TRUE;
+ } else if (is_io_paused) {
+ paused = TRUE;
+ priv->pause_state = PAUSE_INTERNAL;
+ }
+
+ if (in_merge) {
+ priv->index_state = INDEX_MERGING;
+ priv->animated = TRUE;
+ } else if (g_ascii_strcasecmp (state, "Initializing") == 0) {
+ priv->index_state = INDEX_INITIALIZING;
+ } else if (g_ascii_strcasecmp (state, "Idle") == 0) {
+ priv->index_state = INDEX_IDLE;
+ } else {
+ priv->index_state = INDEX_BUSY;
+ priv->animated = TRUE;
+ }
+
+ set_icon (priv);
+
+ /* Should we animate? */
+ if (!paused && priv->animated && priv->show_animation) {
+ if (!priv->animated_timer_active) {
+ priv->animated_timer_active = TRUE;
+ }
+ }
+
+ set_status_hint (icon);
+
+ if (can_auto_pause (icon)) {
+ start_watching_events (icon);
+ } else {
+ stop_watching_events (icon);
+ }
+}
+
+static void
+index_progress_changed (DBusGProxy *proxy,
+ const gchar *service,
+ const gchar *uri,
+ gint items_done,
+ gint items_remaining,
+ gint items_total,
+ gdouble seconds_elapsed,
+ TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->items_done = items_done;
+ priv->items_remaining = items_remaining;
+ priv->items_total = items_total;
+ priv->seconds_elapsed = seconds_elapsed;
+
+ priv->email_indexing = strcmp (service, "Emails") == 0;
+
+ g_print ("Indexed %d/%d, seconds elapsed:%f\n",
+ items_done,
+ items_total,
+ seconds_elapsed);
+
+ set_status_hint (icon);
+ set_icon (priv);
+
+ /* Update stat window if its active */
+ refresh_stats (icon);
+}
+
+static void
+init_settings (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->index_state = INDEX_INITIALIZING;
+ priv->pause_state = PAUSE_NONE;
+ priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+ priv->index_icon = ICON_DEFAULT;
+ priv->animated = FALSE;
+ priv->animated_timer_active = FALSE;
+ priv->user_pause = FALSE;
+ priv->auto_pause = FALSE;
+ priv->auto_hide = FALSE;
+ priv->disabled = FALSE;
+ priv->show_animation = TRUE;
+ priv->auto_pause_timer_active = FALSE;
+ priv->is_watching_events = FALSE;
+ priv->initial_index_msg_shown = FALSE;
+ priv->stat_window_active = FALSE;
+ priv->stat_request_pending = FALSE;
+ priv->indexer_stopped = FALSE;
+
+ set_tracker_icon (priv);
+}
+
+static void
+name_owner_changed (DBusGProxy *proxy,
+ const gchar *name,
+ const gchar *prev_owner,
+ const gchar *new_owner,
+ gpointer data)
+{
+ if (!g_str_equal (name, DBUS_SERVICE_TRACKER)) {
+ return;
+ }
+
+ if (g_str_equal (new_owner, "")) {
+ TrayIconPrivate *priv;
+
+ priv = TRAY_ICON_GET_PRIVATE (data);
+
+ /* Tracker has exited so reset status and make
+ * invisible until trackerd relaunched.
+ */
+ index_state_changed (proxy,
+ "Idle",
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ FALSE,
+ data);
+
+ init_settings (data);
+ gtk_status_icon_set_visible (priv->icon, FALSE);
+ priv->indexer_stopped = TRUE;
+
+ g_print ("The Tracker daemon has exited (%s)\n",
+ priv->reindex ? "reindexing" : "not reindexing");
+
+ if (priv->reindex) {
+ GError *error = NULL;
+ const gchar *command = "trackerd";
+
+ priv->reindex = FALSE;
+
+ g_print ("Restarting Tracker daemon in 1 second\n");
+ g_usleep (G_USEC_PER_SEC);
+
+ g_print ("Spawning command:'%s'\n", command);
+
+ if (!g_spawn_command_line_async (command, &error)) {
+ g_warning ("Unable to execute command:'%s', %s",
+ command,
+ error->message);
+ g_error_free (error);
+ }
+ }
+ }
+}
+
+static gboolean
+setup_dbus_connection (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ DBusGConnection *connection;
+ DBusGProxy *dbus_proxy;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->tracker = tracker_connect (FALSE);
+
+ if (!priv->tracker) {
+ g_printerr ("Could not connect to the Tracker daemon\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Set signal handlers */
+ dbus_g_object_register_marshaller (tracker_VOID__STRING_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INVALID);
+
+ dbus_g_object_register_marshaller (tracker_VOID__STRING_STRING_INT_INT_INT_DOUBLE,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_DOUBLE,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_add_signal (priv->tracker->proxy,
+ "IndexStateChange",
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_add_signal (priv->tracker->proxy,
+ "IndexProgress",
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_INT,
+ G_TYPE_DOUBLE,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_add_signal (priv->tracker->proxy,
+ "IndexFinished",
+ G_TYPE_DOUBLE,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (priv->tracker->proxy,
+ "IndexStateChange",
+ G_CALLBACK (index_state_changed),
+ icon,
+ NULL);
+
+ dbus_g_proxy_connect_signal (priv->tracker->proxy,
+ "IndexProgress",
+ G_CALLBACK (index_progress_changed),
+ icon,
+ NULL);
+
+ dbus_g_proxy_connect_signal (priv->tracker->proxy,
+ "IndexFinished",
+ G_CALLBACK (index_finished),
+ icon,
+ NULL);
+
+ connection = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
+ dbus_proxy = dbus_g_proxy_new_for_name (connection,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+
+ dbus_g_proxy_add_signal (dbus_proxy,
+ "NameOwnerChanged",
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (dbus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (name_owner_changed),
+ icon, NULL);
+
+ /* Prompt for updated signals */
+ dbus_g_proxy_begin_call (priv->tracker->proxy,
+ "PromptIndexSignals",
+ NULL,
+ NULL,
+ NULL,
+ G_TYPE_INVALID);
+
+ return FALSE;
+}
+
+static void
+tray_icon_clicked (GtkStatusIcon *status_icon,
+ guint button,
+ guint timestamp,
+ gpointer data)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+
+ icon = TRAY_ICON (data);
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ gtk_menu_popup (GTK_MENU (priv->menu),
+ NULL, NULL,
+ gtk_status_icon_position_menu,
+ status_icon,
+ button,
+ timestamp);
+}
+
+static void
+tray_icon_init (GTypeInstance *instance,
+ gpointer klass)
+{
+ TrayIcon *icon;
+ TrayIconPrivate *priv;
+
+ icon = TRAY_ICON (instance);
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ priv->icon = gtk_status_icon_new ();
+
+ init_settings (icon);
+
+ priv->reindex = FALSE;
+
+ g_signal_connect (G_OBJECT (priv->icon), "activate",
+ G_CALLBACK (activate_icon),
+ instance);
+ g_signal_connect (G_OBJECT (priv->icon), "popup-menu",
+ G_CALLBACK (tray_icon_clicked),
+ instance);
+
+ /* Build context menu */
+ create_context_menu (icon);
+
+ gtk_status_icon_set_visible (priv->icon, FALSE);
+
+ setup_dbus_connection (icon);
+}
+
+void
+tray_icon_set_tooltip (TrayIcon *icon,
+ const gchar *format,
+ ...)
+{
+ TrayIconPrivate *priv;
+ gchar *tooltip = NULL;
+ va_list args;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ va_start (args, format);
+ tooltip = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ gtk_status_icon_set_tooltip (priv->icon, tooltip);
+
+ g_free (tooltip);
+}
+
+void
+tray_icon_show_message (TrayIcon *icon,
+ const gchar *message,
+ ...)
+{
+ TrayIconPrivate *priv;
+ NotifyNotification *notification;
+ gchar *msg = NULL;
+ va_list args;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ va_start (args, message);
+ msg = g_strdup_vprintf (message, args);
+ va_end (args);
+
+ if (priv->disabled) {
+ return;
+ }
+
+ if (!priv->auto_hide && !gtk_status_icon_get_visible (priv->icon)) {
+ gtk_status_icon_set_visible (priv->icon, TRUE);
+ }
+
+ notification =
+ notify_notification_new_with_status_icon ("Tracker",
+ msg,
+ NULL,
+ priv->icon);
+
+ notify_notification_set_urgency (notification, NOTIFY_URGENCY_NORMAL);
+ notify_notification_show (notification, NULL);
+
+ g_object_unref (notification);
+
+ g_free (msg);
+}
+
+GType
+tray_icon_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0) {
+ static const GTypeInfo info = {
+ sizeof (TrayIconClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) tray_icon_class_init,
+ NULL,
+ NULL,
+ sizeof (TrayIcon),
+ 0,
+ tray_icon_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "TrayIconType",
+ &info,
+ 0);
+ }
+
+ return type;
+}
+
+static gboolean
+load_options (TrayIcon *icon)
+{
+ TrayIconPrivate *priv;
+ GError *error = NULL;
+
+ priv = TRAY_ICON_GET_PRIVATE (icon);
+
+ if (!priv->keyfile) {
+ priv->keyfile = g_key_file_new ();
+ }
+
+ if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS)) {
+ gchar *tracker_dir;
+ gchar *contents;
+
+ tracker_dir = g_build_path (g_get_user_config_dir (),
+ "tracker",
+ NULL);
+
+ if (!g_file_test (tracker_dir, G_FILE_TEST_EXISTS)) {
+ g_mkdir_with_parents (tracker_dir, 0700);
+ }
+
+ g_free (tracker_dir);
+
+ contents = g_strconcat ("[Applet]\n",
+ "AnimateWhenIndexing=true\n"
+ "\n",
+ "AutoHideIcon=false\n"
+ "\n",
+ "SmartPause=2\n",
+ NULL);
+
+ g_file_set_contents (priv->filename,
+ contents,
+ strlen (contents),
+ NULL);
+ g_free (contents);
+ }
+
+ if (!g_key_file_load_from_file (priv->keyfile,
+ priv->filename,
+ G_KEY_FILE_KEEP_COMMENTS,
+ &error) || error) {
+ if (error) {
+ g_warning ("Call to g_key_file_load_from_file() failed for filename:'%s', %s\n",
+ priv->filename,
+ error->message);
+ }
+
+ priv->show_animation = TRUE;
+ priv->auto_hide = FALSE;
+ priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+
+ return FALSE;
+ }
+
+ if (g_key_file_has_key (priv->keyfile,
+ "Applet",
+ "AnimateWhenIndexing",
+ NULL)) {
+ priv->show_animation = g_key_file_get_boolean (priv->keyfile,
+ "Applet",
+ "AnimateWhenIndexing",
+ NULL);
+ } else {
+ priv->show_animation = TRUE;
+ }
+
+ if (g_key_file_has_key (priv->keyfile,
+ "Applet",
+ "AutoHideIcon",
+ NULL)) {
+ priv->auto_hide = g_key_file_get_boolean (priv->keyfile,
+ "Applet",
+ "AutoHideIcon",
+ NULL);
+ } else {
+ priv->auto_hide = FALSE;
+ }
+
+ if (g_key_file_has_key (priv->keyfile,
+ "Applet",
+ "SmartPause",
+ NULL)) {
+ priv->auto_pause_setting = g_key_file_get_integer (priv->keyfile,
+ "Applet",
+ "SmartPause",
+ NULL);
+ } else {
+ priv->auto_pause_setting = AUTO_PAUSE_MERGING;
+ }
+
+ switch (priv->auto_pause_setting) {
+ case AUTO_PAUSE_NONE:
+ priv->auto_pause_setting = AUTO_PAUSE_NONE;
+ priv->auto_pause = FALSE;
+ stop_watching_events (icon);
+ break;
+ case AUTO_PAUSE_INDEXING:
+ if (can_auto_pause (icon)) {
+ start_watching_events (icon);
+ } else {
+ stop_watching_events (icon);
+ }
+ break;
+ case AUTO_PAUSE_MERGING:
+ if (can_auto_pause (icon)) {
+ start_watching_events (icon);
+ } else {
+ stop_watching_events (icon);
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ TrayIconPrivate *priv;
+ GOptionContext *context;
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ context = g_option_context_new (_("- Tracker applet for quick control of "
+ "your desktop search tools"));
+
+ g_option_context_add_main_entries (context, entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ g_print ("Initializing GTK+\n");
+
+ gtk_init (&argc, &argv);
+
+ g_print ("Initializing libnotify\n");
+
+ if (!notify_is_initted () &&
+ !notify_init (PROGRAM_NAME)) {
+ g_warning ("Call to notify_init() failed\n");
+ return EXIT_FAILURE;
+ }
+
+ g_print ("Initializing strings\n");
+
+ gtk_window_set_default_icon_name ("tracker");
+ g_set_application_name (_("Tracker"));
+
+ initial_index_1 =
+ _("Your computer is about to be indexed so "
+ "you can perform fast searches of your files and emails");
+ initial_index_2 =
+ _("You can pause indexing at any time and "
+ "configure index settings by right clicking here");
+
+ stat_info[0].label = _("Files:");
+ stat_info[1].label = _(" Folders:");
+ stat_info[2].label = _(" Documents:");
+ stat_info[3].label = _(" Images:");
+ stat_info[4].label = _(" Music:");
+ stat_info[5].label = _(" Videos:");
+ stat_info[6].label = _(" Text:");
+ stat_info[7].label = _(" Development:");
+ stat_info[8].label = _(" Other:");
+ stat_info[9].label = _("Applications:");
+ stat_info[10].label = _("Conversations:");
+ stat_info[11].label = _("Emails:");
+
+ g_print ("Initializing tray icon\n");
+
+ main_icon = g_object_new (TYPE_TRAY_ICON, NULL);
+
+ priv = TRAY_ICON_GET_PRIVATE (main_icon);
+
+ g_print ("Initializing config\n");
+
+ priv->keyfile = NULL;
+ priv->filename = g_build_filename (g_get_user_config_dir (),
+ "tracker"
+ "tracker-applet.cfg",
+ NULL);
+
+ if (!load_options (main_icon)) {
+ return EXIT_FAILURE;
+ }
+
+ g_print ("Starting main loop\n");
+
+ gtk_main ();
+
+ g_print ("Shutting down\n");
+
+ notify_uninit ();
+
+ return EXIT_SUCCESS;
+}