grim/purple-plugin-pack
* Stocker - Adds a stock ticker to the buddy list * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com> * 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 /* If you can't figure out what this line is for, DON'T TOUCH IT. */ #include "../common/pp_internal.h" #include "stocker_prefs.h" #define URL_REQUEST "http://quotewebvip-m01.blue.aol.com/?action=aim&syms=%s&fields=nspg" #define CHANGE_INCREASE "<span color=\"green3\">%+0.04g</span>" #define CHANGE_DECREASE "<span color=\"red\">%+0.04g</span>" #define CHANGE_NONE "%0.04g" /****************************************************************************** *****************************************************************************/ #define STOCKER_QUOTE(obj) ((StockerQuote *)(obj)) /****************************************************************************** *****************************************************************************/ static GtkWidget *ticker = NULL; static GHashTable *quotes = NULL; static guint quotes_id = 0, interval_id = 0, interval_timer = 0; /****************************************************************************** *****************************************************************************/ stocker_quote_new(const gchar *symbol) { StockerQuote *ret = g_new0(StockerQuote, 1); ret->symbol = g_strdup(symbol); label = g_strdup_printf("%s (refreshing)", symbol); ret->label = gtk_label_new(label); gtk_ticker_add(GTK_TICKER(ticker), ret->label); gtk_widget_show(ret->label); stocker_quote_ref(StockerQuote *quote) { stocker_quote_unref(StockerQuote *quote) { gtk_widget_destroy(quote->label); stocker_quote_update(StockerQuote *quote, const gchar *name, gdouble current, GString *str = g_string_sized_new(512); g_string_append_printf(str, "<span weight=\"bold\">%s</span> " "<span size=\"small\">(%s)</span> $%g ", name, quote->symbol, current); g_string_append_printf(str, CHANGE_DECREASE, change); g_string_append_printf(str, CHANGE_INCREASE, change); g_string_append_printf(str, CHANGE_NONE, change); gtk_label_set_markup(GTK_LABEL(quote->label), str->str); g_string_free(str, TRUE); /****************************************************************************** *****************************************************************************/ stocker_refresh_url_cb(PurpleUtilFetchUrlData *url_data, gpointer data, const gchar *text, gsize len, const gchar *errmsg) while((p = g_strstr_len(p, strlen(p), "DATA="))) { const gchar *name = NULL, *symbol = NULL; gdouble current = 0.0, change = 0.0; /* move paste the data text */ /* find the current price */ /* now move p to the EOL */ StockerQuote *quote = g_hash_table_lookup(quotes, symbol); stocker_quote_update(quote, name, current, change); stocker_refresh_helper(gpointer k, gpointer v, gpointer d) { GString *str = (GString *)d; gchar *symbol = (gchar *)k; g_string_append_printf(str, "%s%s", (str->len > 0) ? "," : "", GString *syms = g_string_sized_new(64); g_hash_table_foreach(quotes, stocker_refresh_helper, syms); url = g_strdup_printf(URL_REQUEST, syms->str); g_string_free(syms, TRUE); purple_util_fetch_url(url, TRUE, "purple", TRUE, PidginBuddyList *gtkblist; if(GTK_IS_WIDGET(ticker)) gtk_widget_destroy(ticker); blist = purple_get_blist(); gtkblist = PIDGIN_BLIST(blist); ticker = gtk_ticker_new(); gtk_box_pack_start(GTK_BOX(gtkblist->vbox), ticker, FALSE, FALSE, 0); gtk_ticker_set_spacing(GTK_TICKER(ticker), 16); gtk_ticker_start_scroll(GTK_TICKER(ticker)); gtk_widget_show_all(ticker); stocker_blist_created(PurpleBuddyList *blist, gpointer data) { stocker_quotes_refresh(GList *symbols) { StockerQuote *quote = NULL; GHashTable *new_quotes = NULL, *temp = NULL; /* this is a bit more complicated than I'd like, but we need a way to be * able to remove quotes that we don't know by name. So we create a new * hashtable, copy all the existing quotes over to it, add the new ones, * then delete the old table and use the new one. new_quotes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)stocker_quote_unref); for(l = symbols; l; l = l->next) { /* sanity check to make sure we have a symbol */ quote = g_hash_table_lookup(quotes, symbol); /* ref the quote so it stays alive */ stocker_quote_ref(quote); /* this is a new symbol, create a quote for it */ quote = stocker_quote_new(symbol); /* insert the quote into the new hashtable */ g_hash_table_insert(new_quotes, g_strdup(symbol), quote); /* hold onto the old pointer */ /* update the pointer to the updated list */ g_hash_table_destroy(temp); stocker_quotes_changed_cb(const gchar *name, PurplePrefType type, gconstpointer value, gpointer data) stocker_quotes_refresh((GList *)value); stocker_refresh_cb(gpointer data) { stocker_interval_changed_cb(const gchar *name, PurplePrefType type, gconstpointer value, gpointer data) gint new_time = GPOINTER_TO_INT(value); /* remove the old timer */ purple_timeout_remove(interval_timer); interval_timer = purple_timeout_add_seconds(new_time * 60, stocker_refresh_cb, NULL); /****************************************************************************** *****************************************************************************/ stocker_load(PurplePlugin *plugin) { void *prefs_handle = purple_prefs_get_handle(); quotes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)stocker_quote_unref); purple_signal_connect(pidgin_blist_get_handle(), "gtkblist-created", PURPLE_CALLBACK(stocker_blist_created), NULL); quotes_id = purple_prefs_connect_callback(prefs_handle, PREF_SYMBOLS, stocker_quotes_changed_cb, interval_id = purple_prefs_connect_callback(prefs_handle, PREF_INTERVAL, stocker_interval_changed_cb, interval = 60 * purple_prefs_get_int(PREF_INTERVAL); interval_timer = purple_timeout_add_seconds(interval, stocker_refresh_cb, stocker_quotes_refresh(purple_prefs_get_string_list(PREF_SYMBOLS)); stocker_unload(PurplePlugin *plugin) { stocker_destroy(PurplePlugin *plugin) { purple_timeout_remove(interval_timer); if(GTK_IS_WIDGET(ticker)) gtk_widget_destroy(ticker); static PidginPluginUiInfo stocker_ui_info = { stocker_prefs_get_frame }; static PurplePluginInfo stocker_info = "gtk-plugin_pack-stocker", "Gary Kramlich <grim@reaperworld.com>", stocker_init(PurplePlugin *plugin) { bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); stocker_info.name = _("Stocker"); stocker_info.summary = _("A stock ticker"); stocker_info.description = _("Adds a stock ticker similar to the one in the Windows AIM client to" " the bottom of the buddy list."); PURPLE_INIT_PLUGIN(stocker, stocker_init, stocker_info)