Logo Search packages:      
Sourcecode: xchat-indicator version File versions  Download package

indicator.c

/*
 * indicator.c - xchat plugin to support the Messaging Indicator
 *
 * Copyright (C) 2009 Scott Parkerson <scott.parkerson@gmail.com>
 * Copyright (C) 2009-2011 Canonical Ltd.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

#include <config.h>
#include <libindicate/server.h>
#include <libindicate/indicator-messages.h>
#include <libindicate/indicator.h>
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "xchat-plugin.h"
#include <unity.h>

#define MESSAGING_INDICATOR_PLUGIN_NAME     _("Messaging Indicator")
#define MESSAGING_INDICATOR_PLUGIN_VERSION  VERSION
#define MESSAGING_INDICATOR_PLUGIN_DESC     _("Notify the user on Xchat events via the Messaging Indicator")

void xchat_plugin_get_info   (char **plugin_name, char **plugin_desc, char **plugin_version, void **reserved);
int  xchat_plugin_init       (xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg);
int  xchat_plugin_deinit     (void);

static xchat_plugin     *ph;
static GHashTable *indicators = NULL;
static IndicateServer   *indicate_server = NULL;
#ifdef HAVE_UNITY
static UnityLauncherEntry     *launcher = NULL;
#endif
static xchat_hook *msg_hilight;
static xchat_hook *action_hilight;
static xchat_hook *pm;
static xchat_hook *pm_dialog;
static xchat_hook *ch_nick;
static xchat_hook *tab_focus;
static GtkWindow  *win;
static gchar            *focused;
static gboolean         *run = FALSE;
static void hide_indicator (const gchar *channel, gpointer indicator, gpointer data);
static void remove_indicator (const gchar *channel, gpointer indicator);
static void add_indicator (const gchar *channel);
static void really_activate_window (GtkWindow *win);
gboolean focus_win_cb (GtkWindow *win,  GParamSpec *pspec, gpointer data);


static void
indicator_display (gpointer indicator, guint timestamp, const gchar *channel)
{
      GtkWindow *win;

        if (channel) {
            xchat_context *current_ctx;
            current_ctx = xchat_get_context(ph);
            xchat_context *ctx;
            ctx = xchat_find_context (ph, NULL, channel);
                g_debug ("INDICATOR: Changing to channel %s", channel);
            win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
            if (xchat_set_context (ph, ctx)) {
                  xchat_command (ph, "GUI FOCUS"); 
                  gtk_widget_show (GTK_WIDGET (win));
                  really_activate_window (GTK_WINDOW (win));
                  /* gtk_window_present_with_time(GTK_WINDOW (win), timestamp); */
            } else {
                  g_debug ("INDICATOR: context change fail");
            }
            if (indicator != NULL) {
                  remove_indicator (channel, indicator);
            }
            xchat_set_context (ph, current_ctx);
      }
      g_debug ("INDICATOR: Indicator displayed");
}

static void
server_display (IndicateServer * indicate_server, guint timestamp, gpointer data)
{
      GtkWindow *win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
        really_activate_window (GTK_WINDOW (win));
      /* gtk_window_present_with_time(GTK_WINDOW (win), timestamp); */
      g_debug ("INDICATOR: Showing the window");
        /* XXX: Should clear all indicators */
}

static void 
hide_indicator (const gchar *channel, gpointer indicator, gpointer data)
{
      g_debug ("INDICATOR: hiding indicator");
      indicate_indicator_hide (INDICATE_INDICATOR (indicator));
}

static void
remove_indicator (const gchar *channel, gpointer indicator)
{
      if (indicator == g_hash_table_lookup (indicators, channel))
      {
            g_debug ("INDICATOR: removing indicator for channel %s", channel);
            indicate_indicator_hide (INDICATE_INDICATOR (indicator));
            g_hash_table_remove (indicators, channel);
            g_object_unref (indicator);
      }
#ifdef HAVE_UNITY
        if (launcher == NULL)
        {
                return;
        }

      gint count = g_hash_table_size (indicators);
      g_debug ("LAUNCHER: count is %d", count);
      if (count > 0)
      {
            g_debug ("LAUNCHER: setting count to %d", count);
            unity_launcher_entry_set_count (launcher, count);
            unity_launcher_entry_set_count_visible (launcher, TRUE);
      } else {
            unity_launcher_entry_set_count (launcher, count);
            g_debug ("LAUNCHER: hiding count");
            unity_launcher_entry_set_count_visible (launcher, FALSE);
      }
#endif
}

static void
update_indicator (gpointer indicator)
{
      GTimeVal time;
      if (indicator != NULL)
      {
            g_debug ("INDICATOR: found existing indicator");
            g_get_current_time (&time);
            indicate_indicator_set_property_time (INDICATE_INDICATOR (indicator),
                  "time", &time);
      }

}

static void
add_indicator (const gchar *channel)
{
      IndicateIndicator *indicator = NULL;
      GTimeVal time;
      gpointer chan = channel;
      
      indicator = g_hash_table_lookup (indicators, channel);
      if (indicator != NULL) {
            update_indicator (indicator);
            return;
      }

        if (!run)
      {
            GtkWindow *win = (GtkWindow *)xchat_get_info (ph, "win_ptr");
            if (GTK_IS_WINDOW (win))
            {
                  g_signal_connect(G_OBJECT(win), "notify::has-toplevel-focus", G_CALLBACK (focus_win_cb), NULL);
            run = TRUE;
            }
      }

      indicator = indicate_indicator_new ();
      indicate_indicator_set_property (INDICATE_INDICATOR (indicator),
            "subtype", "im");
      indicate_indicator_set_property (INDICATE_INDICATOR (indicator),
            "sender", channel);
      indicate_indicator_set_property (INDICATE_INDICATOR (indicator),
            "draw-attention", "true");

      g_signal_connect (G_OBJECT (indicator),
            INDICATE_INDICATOR_SIGNAL_DISPLAY,
            G_CALLBACK (indicator_display), chan);

      g_get_current_time (&time);
      indicate_indicator_set_property_time (INDICATE_INDICATOR (indicator),
            "time", &time);
      indicate_indicator_show (INDICATE_INDICATOR (indicator));

      g_hash_table_insert(indicators, chan, indicator);

#ifdef HAVE_UNITY
      if (launcher == NULL)
      {
            return;
      }

      gint count = g_hash_table_size (indicators);
      g_debug ("LAUNCHER: count is %d", count);
      if (count > 0)
      {
            unity_launcher_entry_set_count (launcher, count);
            unity_launcher_entry_set_count_visible (launcher, TRUE);
      }
#endif
}

/*
static int
nick_change_cb (char *word[], void *data)
{
        gpointer indicator;
        xchat_context *ctx;
        ctx = xchat_find_context (ph, NULL, word[2]);
        xchat_set_context (ph, ctx);
        const gchar *channel = xchat_get_info (ph, "channel");

        g_debug ("INDICATOR: Checking for indicators for %s", channel);
        indicator = g_hash_table_lookup (indicators, channel);
        if (indicator != NULL) {
                g_debug ("INDICATOR: Found indicator for %s, changing it to %s", word[1], word[2]);
                indicate_indicator_set_property (INDICATE_INDICATOR (indicator),
                        "sender", channel);
        }
}
*/

static int
indicate_msg_cb (char *word[], gpointer data)
{
        GtkWindow *win;
        xchat_context *ctx;
        ctx = xchat_find_context (ph, NULL, word[2]);
        xchat_set_context (ph, ctx);
        const gchar *channel = xchat_get_info (ph, "channel");
        win = (GtkWindow *)xchat_get_info (ph, "win_ptr");

      if (focused == channel && gtk_window_is_active (GTK_WINDOW (win)))
            return XCHAT_EAT_NONE;

      g_debug ("INDICATOR: channel %s is not focused, adding indicator", channel);
      add_indicator (channel);
      return XCHAT_EAT_NONE;
}

static int
focus_tab_cb (char *word[], gpointer data)
{
      gpointer indicator;
      const gchar *channel;
      channel = xchat_get_info (ph, "channel");
      g_debug ("INDICATOR: tab focused for channel %s", channel);
        indicator = g_hash_table_lookup(indicators, channel);
      if (indicator != NULL) {
            g_debug ("INDICATOR: found indicator for %s", channel);
            remove_indicator (channel, indicator);
        }
      focused = channel;
        return XCHAT_EAT_NONE;
}

gboolean
focus_win_cb (GtkWindow *win,  GParamSpec *pspec, gpointer data)
{
      if (!gtk_window_is_active (GTK_WINDOW (win)))
      {
            return FALSE;
      }
      g_debug ("INDICATOR: window focused");
        gpointer indicator;
        const gchar *channel;
        channel = xchat_get_info (ph, "channel");
        g_debug ("INDICATOR: tab focused for channel %s", channel);
        indicator = g_hash_table_lookup(indicators, channel);
        if (indicator != NULL) {
                g_debug ("INDICATOR: found indicator for %s", channel);
                remove_indicator (channel, indicator);
        }
        focused = channel;
      return FALSE;
}

/* Really raise the window, even if the window manager doesn't agree */
static void
really_activate_window (GtkWindow *window)
{
        Screen *screen;
        Time    timestamp;
        XEvent  xev;

        g_return_if_fail (GTK_IS_WINDOW (window));

        screen = GDK_SCREEN_XSCREEN (gtk_widget_get_screen (GTK_WIDGET (window)));
        timestamp = GDK_CURRENT_TIME;

        xev.xclient.type = ClientMessage;
        xev.xclient.serial = 0;
        xev.xclient.send_event = True;
        xev.xclient.display = GDK_DISPLAY ();
        xev.xclient.window = GDK_WINDOW_XWINDOW (GTK_WIDGET (window)->window);
        xev.xclient.message_type = gdk_x11_get_xatom_by_name ("_NET_ACTIVE_WINDOW");
        xev.xclient.format = 32;
        xev.xclient.data.l[0] = 2; /* Pager client type */
        xev.xclient.data.l[1] = timestamp;
        xev.xclient.data.l[2] = 0;
        xev.xclient.data.l[3] = 0;
        xev.xclient.data.l[4] = 0;

        gdk_error_trap_push ();
        XSendEvent (GDK_DISPLAY (),
                RootWindowOfScreen (screen),
                False,
                SubstructureRedirectMask | SubstructureNotifyMask,
                &xev);
        gdk_error_trap_pop ();
}

void
xchat_plugin_get_info (char **plugin_name, char **plugin_desc, char **plugin_version, void **reserved)
{
      *plugin_name    = MESSAGING_INDICATOR_PLUGIN_NAME;
      *plugin_desc    = MESSAGING_INDICATOR_PLUGIN_DESC;
      *plugin_version = MESSAGING_INDICATOR_PLUGIN_VERSION;
}

int
xchat_plugin_init (xchat_plugin *plugin_handle, char **plugin_name, char **plugin_desc, char **plugin_version, char *arg)
{
      xchat_plugin_get_info (plugin_name, plugin_desc, plugin_version, NULL);

      ph = plugin_handle;
      const gchar *desktop_id = g_strconcat(g_get_prgname(), ".desktop", NULL);
      const gchar *desktop_file = g_strconcat("/usr/share/applications/", desktop_id, NULL);

      indicate_server = indicate_server_ref_default ();
      indicate_server_set_type (indicate_server, "message.im");

      indicate_server_set_desktop_file (indicate_server,
                desktop_file);
      indicate_server_show (indicate_server);
      indicators = g_hash_table_new(g_str_hash, g_str_equal);

      g_signal_connect (G_OBJECT (indicate_server),
            INDICATE_SERVER_SIGNAL_SERVER_DISPLAY,
            G_CALLBACK (server_display), NULL);

#ifdef HAVE_UNITY
      launcher = unity_launcher_entry_get_for_desktop_id (desktop_id);
#endif

      msg_hilight = xchat_hook_print (ph, "Channel Msg Hilight",
            XCHAT_PRI_NORM, indicate_msg_cb, NULL);
      action_hilight = xchat_hook_print (ph, "Channel Action Hilight",
            XCHAT_PRI_NORM, indicate_msg_cb, NULL);
      pm = xchat_hook_print (ph, "Private Message",
            XCHAT_PRI_NORM, indicate_msg_cb, NULL);
      pm_dialog = xchat_hook_print (ph, "Private Message to Dialog",
            XCHAT_PRI_NORM, indicate_msg_cb, NULL);
      tab_focus = xchat_hook_print (ph, "Focus Tab",
            XCHAT_PRI_NORM, focus_tab_cb, NULL);
      /*
        ch_nick = xchat_hook_print (ph, "Change Nick",
                XCHAT_PRI_NORM, nick_change_cb, NULL);
      */

      xchat_print (ph,  (g_strjoin (" ", MESSAGING_INDICATOR_PLUGIN_NAME, MESSAGING_INDICATOR_PLUGIN_VERSION, _("plugin loaded."), NULL)));

      return TRUE;
}

int
xchat_plugin_deinit (void)
{
      xchat_unhook (ph, msg_hilight);
      xchat_unhook (ph, action_hilight);
      xchat_unhook (ph, pm);
      xchat_unhook (ph, pm_dialog);
      xchat_unhook (ph, tab_focus);


      /* XXX: Need to clear all indicators */
      GHashTableIter iter;
      gpointer channel, indicator;

      g_hash_table_iter_init (&iter, indicators);
      while (g_hash_table_iter_next (&iter, &channel, &indicator)) {
            g_debug ("INDICATOR: removing indicator for channel %p", channel);
            indicate_indicator_hide (INDICATE_INDICATOR (indicator));
      }


      /* Kill the hash table */
      g_hash_table_destroy(indicators);

      g_debug ("INDICATOR: Hiding the indicator server");

      indicate_server_hide (indicate_server);

      xchat_print (ph,  (g_strjoin (" ", MESSAGING_INDICATOR_PLUGIN_NAME, MESSAGING_INDICATOR_PLUGIN_VERSION, _("plugin unloaded."), NULL)));

      return TRUE;
}

Generated by  Doxygen 1.6.0   Back to index