Logo Search packages:      
Sourcecode: guifications version File versions

gf_event.c

/*
 * Guifications - The end all, be all, toaster popup plugin
 * Copyright (C) 2003-2004 Gary Kramlich
 *
 * 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 <glib.h>
#include <string.h>

#include <debug.h>
#include <account.h>
#include <blist.h>
#include <connection.h>
#include <conversation.h>
#include <plugin.h>
#include <prefs.h>
#include <util.h>

#include "gf_blist.h"
#include "gf_display.h"
#include "gf_event.h"
#include "gf_internal.h"
#include "gf_notification.h"
#include "gf_preferences.h"
#include "gf_release.h"

struct _GfEvent {
      gchar *n_type;
      gchar *name;
      gchar *description;

      GfEventPriority priority;

      gchar *tokens;

      gboolean show;
};

static GList *events = NULL;

/*******************************************************************************
 * API
 ******************************************************************************/
GfEvent *
gf_event_new(const gchar *notification_type, const gchar *tokens,
                   const gchar *name, const gchar *description,
                   GfEventPriority priority)
{
      GfEvent *event;

      g_return_val_if_fail(notification_type, NULL);
      g_return_val_if_fail(name, NULL);
      g_return_val_if_fail(description, NULL);

      event = g_new0(GfEvent, 1);

      event->priority = priority;
      event->n_type = g_strdup(notification_type);

      if(tokens)
            event->tokens = g_strdup(tokens);
      else
            event->tokens = g_strdup(TOKENS_DEFAULT);

      event->name = g_strdup(name);
      event->description = g_strdup(description);

      if(!g_list_find(events, event))
            events = g_list_append(events, event);
      else {
            gaim_debug_info("Guifications", "Event already exists\n");
            gf_event_destroy(event);
      }

      return event;
}

GfEvent *
gf_event_find_for_notification(const gchar *type) {
      GfEvent *event;
      GList *l;

      for(l = events; l; l = l->next) {
            event = GF_EVENT(l->data);
            if(!g_ascii_strcasecmp(event->n_type, type))
                  return event;
      }

      return NULL;
}

void
gf_event_destroy(GfEvent *event) {
      g_return_if_fail(event);

      events = g_list_remove(events, event);

      g_free(event->n_type);
      g_free(event->name);
      g_free(event->description);

      g_free(event);
}

const gchar *
gf_event_get_notification_type(GfEvent *event) {
      g_return_val_if_fail(event, NULL);

      return event->n_type;
}

const gchar *
gf_event_get_tokens(GfEvent *event) {
      g_return_val_if_fail(event, NULL);

      return event->tokens;
}

const gchar *
gf_event_get_name(GfEvent *event) {
      g_return_val_if_fail(event, NULL);

      return event->name;
}

const gchar *
gf_event_get_description(GfEvent *event) {
      g_return_val_if_fail(event, NULL);

      return event->description;
}

GfEventPriority
gf_event_get_priority(GfEvent *event) {
      g_return_val_if_fail(event, GF_EVENT_PRIORITY_NORMAL);

      return event->priority;
}

void
gf_event_set_show(GfEvent *event, gboolean show) {
      g_return_if_fail(event);

      event->show = show;
}

gboolean
gf_event_get_show(GfEvent *event) {
      g_return_val_if_fail(event, FALSE);

      return event->show;
}

gboolean
gf_event_show_notification(const gchar *n_type) {
      GfEvent *event;

      g_return_val_if_fail(n_type, FALSE);

      event = gf_event_find_for_notification(n_type);
      if(event)
            return event->show;

      return FALSE;
}

const GList *gf_events_get() {
      return events;
}

void
gf_events_save() {
      GfEvent *event;
      GList *l = NULL, *e;

      for(e = events; e; e = e->next) {
            event = GF_EVENT(e->data);

            if(event->show)
                  l = g_list_append(l, event->n_type);
      }

      gaim_prefs_set_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS, l);
      g_list_free(l);
}

gint
gf_events_count() {
      return g_list_length(events);
}

const gchar *
gf_events_get_nth_name(gint nth) {
      GfEvent *event = g_list_nth_data(events, nth);

      return event->name;
}

const gchar *
gf_events_get_nth_notification(gint nth) {
      GfEvent *event = g_list_nth_data(events, nth);

      return event->n_type;
}

/*******************************************************************************
 * Signon flood be gone!
 ******************************************************************************/
static GList *accounts = NULL;

static gboolean
gf_event_connection_throttle_cb(gpointer data) {
      GaimAccount *account = (GaimAccount *)data;

      gf_release_check();

      if(!account)
            return FALSE;

      if(!gaim_account_get_connection(account)) {
            accounts = g_list_remove(accounts, account);
            return FALSE;
      }

      if(!gaim_account_is_connected(account))
            return TRUE;

      accounts = g_list_remove(accounts, account);
      return FALSE;
}

static void
gf_event_connection_throttle(GaimConnection *gc, gpointer data) {
      GaimAccount *account;

      if(!gc)
            return;

      account = gaim_connection_get_account(gc);
      if(!account)
            return;

      accounts = g_list_append(accounts, account);
      g_timeout_add(10000, gf_event_connection_throttle_cb, (gpointer)account);
}

/*******************************************************************************
 * Chat Join Flooding
 ******************************************************************************/
static GList *chats = NULL;

static gboolean
gf_event_conversation_throttle_cb(gpointer data) {
      GaimConversation *conv = (GaimConversation *)data;

      if(conv)
            chats = g_list_remove(chats, conv);

      return FALSE;
}

static void
gf_event_conversation_throttle(GaimConversation *conv, gpointer data) {
      if(!conv)
            return;

      if(gaim_conversation_get_type(conv) != GAIM_CONV_CHAT)
            return;

      chats = g_list_append(chats, conv);
      g_timeout_add(5000, gf_event_conversation_throttle_cb, (gpointer)conv);
}

/*******************************************************************************
 * Helpers
 ******************************************************************************/
/* first pass to see if a notification should be shown.. */
static gboolean
gf_event_should_show(const gchar *notification, GaimAccount *account) {
      if(gf_display_screen_saver_is_running())
            return FALSE;

      if(g_list_find(accounts, account))
            return FALSE;

      if(!gf_event_show_notification(notification))
            return FALSE;

      if(!gaim_prefs_get_bool(GF_PREF_BEHAVIOR_SHOW_WHILE_AWAY) &&
         account->gc->away_state)
      {
            return FALSE;
      }

      return TRUE;
}

/* second pass to see if a conversation notification should be shown */
static gboolean
gf_event_conversation_show(GaimConversation *conv, const gchar *sender) {
      if(!conv)
            return FALSE;

      if(gaim_conversation_has_focus(conv))
            return FALSE;

      if(gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
            if(g_list_find(chats, conv))
                  return FALSE;

            if(sender) {
                  const gchar *nick;

                  nick = gaim_conv_chat_get_nick(GAIM_CONV_CHAT(conv));
                  if(!strcmp(nick, sender))
                        return FALSE;
        }
      }

      return TRUE;
}

static void
gf_event_common(gchar *n_type, GaimAccount *account, GaimBuddy *buddy,
                        GaimConversation *conv, const gchar *target,
                        const gchar *message, GaimConvChatBuddyFlags flags,
                        const GHashTable *components, const gchar *extra)
{
      GfNotification *notification = NULL;
      GfEventInfo *info = NULL;

      g_return_if_fail(n_type);
      g_return_if_fail(account);

      if(!gf_event_should_show(n_type, account))
            return;

      if(conv && target) {
            if(!gf_event_conversation_show(conv, target))
                  return;
      }

      if(buddy) {
            notification = gf_blist_get_notification_for_buddy(buddy, n_type);
      } else {
            notification = gf_notification_find_for_event(n_type);
      }

      if(!notification)
            return;

      info = gf_event_info_new(n_type);

      gf_event_info_set_account(info, account);

      if(buddy)
            gf_event_info_set_buddy(info, buddy);

      if(conv)
            gf_event_info_set_conversation(info, conv);

      if(target)
            gf_event_info_set_target(info, target);

      if(message)
            gf_event_info_set_message(info, message);

      gf_event_info_set_conv_chat_buddy_flags(info, flags);

      if(components)
            gf_event_info_set_components(info, components);

      if(extra)
            gf_event_info_set_extra(info, extra);

      gf_display_show_event(info, notification);
}

/*******************************************************************************
 * Buddy callbacks
 ******************************************************************************/
static void
gf_event_buddy(GaimBuddy *buddy, gpointer data) {
      gchar *notification = (gchar *)data;

      if(!gf_event_should_show(notification, buddy->account))
            return;

      gf_event_common(notification, buddy->account, buddy, NULL, buddy->name,
                              NULL, GAIM_CBFLAGS_NONE, NULL, NULL);
}

/*******************************************************************************
 * Conversation callbacks
 ******************************************************************************/
static void
gf_event_im_message(GaimAccount *account, const gchar *sender,
                              const gchar *message, int flags, gpointer data)
{
      GaimConversation *conv = NULL;
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;

      conv = gaim_find_conversation_with_account(sender, account);
      buddy = gaim_find_buddy(account, sender);
      plain_message = gaim_markup_strip_html(message);

      gf_event_common(notification, account, buddy, conv, sender, plain_message,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
      g_free(plain_message);
}

static void
gf_event_chat_message(GaimAccount *account, const gchar *sender,
                                const gchar *message, GaimConversation *conv,
                                gpointer data)
{
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;

      plain_message = gaim_markup_strip_html(message);
      buddy = gaim_find_buddy(account, sender);

      gf_event_common(notification, account, buddy, conv, sender, plain_message,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
      g_free(plain_message);
}

static void
gf_event_chat_nick(GaimAccount *account, const gchar *sender,
                           const gchar *message, GaimConversation *conv,
                           gpointer data)
{
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;
      const gchar *nick = NULL;

      nick = gaim_conv_chat_get_nick(GAIM_CONV_CHAT(conv));
      if (nick && !strcmp(sender, nick))
            return;

      if(!g_strstr_len(message, strlen(message), nick))
            return;

      plain_message = gaim_markup_strip_html(message);
      buddy = gaim_find_buddy(account, sender);

      gf_event_common(notification, account, buddy, conv, sender, plain_message,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
      g_free(plain_message);
}

static void
gf_event_typing(GaimConversation *conv, gpointer data) {
      GaimAccount *account = NULL;
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;

      account = gaim_conversation_get_account(conv);
      buddy = gaim_find_buddy(account, conv->name);

      gf_event_common(notification, account, buddy, conv, conv->name, NULL,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
}

static void
gf_event_chat_join(GaimConversation *conv, const gchar *name,
                           GaimConvChatBuddyFlags flags, gpointer data) {
      GaimAccount *account = NULL;
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;

      account = gaim_conversation_get_account(conv);
      buddy = gaim_find_buddy(account, name);

      gf_event_common(notification, account, buddy, conv, name, NULL, flags,
                              NULL, NULL);
}

static void
gf_event_chat_part(GaimConversation *conv, const gchar *name,
                           const gchar *reason, gpointer data)
{
      GaimAccount *account = NULL;
    GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;

      account = gaim_conversation_get_account(conv);
      plain_message = (reason) ? gaim_markup_strip_html(reason) : g_strdup("");
      buddy = gaim_find_buddy(account, name);

      gf_event_common(notification, account, buddy, conv, name, plain_message,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
      g_free(plain_message);
}

static void
gf_event_chat_invite(GaimAccount *account, const gchar *inviter,
                               const gchar *chat, const gchar *invite_message,
                               const GHashTable *components, gpointer data)
{
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;

      plain_message = (invite_message) ? gaim_markup_strip_html(invite_message) :
                              g_strdup("");
      buddy = gaim_find_buddy(account, inviter);

      gf_event_common(notification, account, buddy, NULL, inviter, plain_message,
                              GAIM_CBFLAGS_NONE, components, chat);
      g_free(plain_message);
}

static void
gf_event_topic_changed(GaimConversation *conv, const gchar *who,
                                 const gchar *topic, gpointer data)
{
      GaimAccount *account = NULL;
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;
      gchar *plain_message = NULL;

      account = gaim_conversation_get_account(conv);
      plain_message = (topic) ? gaim_markup_strip_html(topic) : g_strdup("");
      if(who)
            buddy = gaim_find_buddy(account, who);

      gf_event_common(notification, account, buddy, conv, who, plain_message,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
      g_free(plain_message);
}

/*******************************************************************************
 * Account callbacks
 ******************************************************************************/
static void
gf_event_account(GaimAccount *account, const gchar *warner,
                         gint level, gpointer data)
{
      GaimBuddy *buddy = NULL;
      gchar *notification = (gchar *)data;

      if(warner)
            buddy = gaim_find_buddy(account, warner);

      gf_event_common(notification, account, buddy, NULL,
                              (warner) ? warner : _("Anonymous"), NULL,
                              GAIM_CBFLAGS_NONE, NULL, NULL);
}

/*******************************************************************************
 * Subsystem
 ******************************************************************************/
void
gf_events_init(GaimPlugin *plugin) {
      GList *l, *ll = NULL;
      void *blist_handle, *accounts_handle, *conv_handle;

      g_return_if_fail(plugin);

      /* create all of our default events */
      gf_event_new("sign-on", TOKENS_DEFAULT "n", _("Sign on"),
                         _("Displayed when a buddy comes online."),
                         GF_EVENT_PRIORITY_HIGHER);
      gf_event_new("sign-off", TOKENS_DEFAULT "n", _("Sign off"),
                         _("Displayed when a buddy goes offline."),
                         GF_EVENT_PRIORITY_HIGHER);

      gf_event_new("away", TOKENS_DEFAULT "n", _("Away"),
                         _("Displayed when a buddy goes away."),
                         GF_EVENT_PRIORITY_HIGH);
      gf_event_new("back", TOKENS_DEFAULT "n", _("Back"),
                         _("Displayed when a buddy returns from away."),
                         GF_EVENT_PRIORITY_HIGH);

      gf_event_new("idle", TOKENS_DEFAULT "n", _("Idle"),
                         _("Displayed when a buddy goes idle."),
                         GF_EVENT_PRIORITY_NORMAL);
      gf_event_new("unidle", TOKENS_DEFAULT "n", _("Unidle"),
                         _("Displayed when a buddy returns from idle."),
                         GF_EVENT_PRIORITY_NORMAL);

      gf_event_new("im-message", TOKENS_DEFAULT "Ccnr", _("IM message"),
                         _("Displayed when someone sends you a message."),
                         GF_EVENT_PRIORITY_HIGHEST);
      gf_event_new("typing", TOKENS_DEFAULT "Ccnr", _("Typing"),
                         _("Displayed when someone is typing a message to you."),
                         GF_EVENT_PRIORITY_HIGHER);
      gf_event_new("typing-stopped", TOKENS_DEFAULT "Ccnr", _("Stopped typing"),
                         _("Displayed when someone has stopped typing a message to you."),
                         GF_EVENT_PRIORITY_HIGHER);

      gf_event_new("chat-message", TOKENS_DEFAULT "Ccnr", _("Chat message"),
                         _("Displayed when someone talks in a chat."),
                         GF_EVENT_PRIORITY_HIGHER);
      gf_event_new("nick-highlight", TOKENS_DEFAULT "Ccnr", _("Name spoken"),
                         _("Displayed when someone says your nick in a chat"),
                         GF_EVENT_PRIORITY_HIGHEST);

      gf_event_new("chat-join", TOKENS_DEFAULT "Ccnr", _("Join"),
                         _("Displayed when someone joins a chat."),
                         GF_EVENT_PRIORITY_LOW);
      gf_event_new("chat-part", TOKENS_DEFAULT "Ccnr", _("Leave"),
                         _("Displayed when someone leaves a chat."),
                         GF_EVENT_PRIORITY_LOW);

      gf_event_new("chat-invite", TOKENS_DEFAULT "Ccnr", _("Invited"),
                         _("Displayed when someone invites you to a chat."),
                         GF_EVENT_PRIORITY_HIGHEST);

      gf_event_new("topic-changed", TOKENS_DEFAULT "Ccnr", _("Topic changed"),
                         _("Displayed when a topic is changed in a chat."),
                         GF_EVENT_PRIORITY_LOW);

      gf_event_new("warned", TOKENS_DEFAULT, _("Warned"),
                         _("Displayed when you are warned."),
                         GF_EVENT_PRIORITY_NORMAL);

      gf_event_new(GF_NOTIFICATION_MASTER, TOKENS_DEFAULT "Ccnr",
                         _("Master"), _("Master notification for the theme editor."),
                         GF_EVENT_PRIORITY_NORMAL);

      /* add our string list pref */
      for(l = events; l; l = l->next) {
            GfEvent *event = GF_EVENT(l->data);

            ll = g_list_append(ll, event->n_type);
      }
      gaim_prefs_add_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS, ll);
      g_list_free(ll);

      /* now that the pref is created, set the events that are supposed to be displayed
       * to allow displaying
       */
      l = gaim_prefs_get_string_list(GF_PREF_BEHAVIOR_NOTIFICATIONS);
      for(ll = l; ll; ll = ll->next) {
            gchar *event_name = (gchar *)ll->data;

            if(event_name) {
                  GfEvent *event = gf_event_find_for_notification(event_name);
                  g_free(ll->data);

                  if(event)
                        event->show = TRUE;
            }
      }
      g_list_free(l);

      /* connect all of our signals */
      blist_handle = gaim_blist_get_handle();
      accounts_handle = gaim_accounts_get_handle();
      conv_handle = gaim_conversations_get_handle();

      /* blist signals */
      gaim_signal_connect(blist_handle, "buddy-signed-on", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "sign-on");
      gaim_signal_connect(blist_handle, "buddy-signed-off", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "sign-off");
      gaim_signal_connect(blist_handle, "buddy-away", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "away");
      gaim_signal_connect(blist_handle, "buddy-back", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "back");
      gaim_signal_connect(blist_handle, "buddy-idle", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "idle");
      gaim_signal_connect(blist_handle, "buddy-unidle", plugin,
                                    GAIM_CALLBACK(gf_event_buddy), "unidle");

      /* conversation signals */
      gaim_signal_connect(conv_handle, "received-im-msg", plugin,
                                    GAIM_CALLBACK(gf_event_im_message), "im-message");
      gaim_signal_connect(conv_handle, "received-chat-msg", plugin,
                                    GAIM_CALLBACK(gf_event_chat_message), "chat-message");
      gaim_signal_connect(conv_handle, "received-chat-msg", plugin,
                                    GAIM_CALLBACK(gf_event_chat_nick), "nick-highlight");
      gaim_signal_connect(conv_handle, "chat-buddy-joined", plugin,
                                    GAIM_CALLBACK(gf_event_chat_join), "chat-join");
      gaim_signal_connect(conv_handle, "chat-buddy-left", plugin,
                                    GAIM_CALLBACK(gf_event_chat_part), "chat-part");
      gaim_signal_connect(conv_handle, "chat-invited", plugin,
                                    GAIM_CALLBACK(gf_event_chat_invite), "chat-invite");
      gaim_signal_connect(conv_handle, "buddy-typing", plugin,
                                    GAIM_CALLBACK(gf_event_typing), "typing");
      gaim_signal_connect(conv_handle, "buddy-typing-stopped", plugin,
                                    GAIM_CALLBACK(gf_event_typing), "typed");
      gaim_signal_connect(conv_handle, "chat-topic-changed", plugin,
                                    GAIM_CALLBACK(gf_event_topic_changed), "topic-changed");

      /* account signals */
      gaim_signal_connect(accounts_handle, "account-warned", plugin,
                                    GAIM_CALLBACK(gf_event_account), "warned");

      /* both of these are used just to not display the flood of guifications we'd get if
       * we weren't throttling them.  Makes it look much prettier im my opinion :)
       */
      gaim_signal_connect(gaim_connections_get_handle(), "signed-on", plugin,
                                    GAIM_CALLBACK(gf_event_connection_throttle), NULL);

      gaim_signal_connect(conv_handle, "chat-joined", plugin,
                                    GAIM_CALLBACK(gf_event_conversation_throttle), NULL);

}

void
gf_events_uninit() {
      GList *l, *ll;

      for(l = events; l; l = ll) {
            ll = l->next;
            gf_event_destroy(GF_EVENT(l->data));
      }
}

Generated by  Doxygen 1.6.0   Back to index