grim/purple-plugin-pack
--leaks
org.guifications.plugins
2009-04-15, darkrain42
* Buddy Time - Displays a buddy's local time * A libpurple plugin that allows you to configure a timezone on a per-contact * basis so it can display the localtime of your contact when a conversation * starts. Convenient if you deal with contacts from many parts of the * Copyright (C) 2006-2007, Richard Laager <rlaager@pidgin.im> * Copyright (C) 2006, Martijn van Oosterhout <kleptog@svana.org> * 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 /* If you can't figure out what this line is for, DON'T TOUCH IT. */ #include "../common/pp_internal.h" #define PLUGIN_STATIC_NAME CORE_PLUGIN_STATIC_NAME #define PLUGIN_ID CORE_PLUGIN_ID #define SETTING_NAME "timezone" #define CONTROL_NAME PLUGIN_ID "-" SETTING_NAME #include "conversation.h" #define TIMEZONE_FLAG ((void*)1) #define DISABLED_FLAG ((void*)2) BuddyTimeUiOps *ui_ops = NULL; static PurplePlugin *plugin_self; /* Resolve specifies what the return value should mean: * If TRUE, it's for display, we want to know the *effect* thus hiding the * "none" value and going to going level to find the default * If false, we only want what the user enter, thus the string "none" if * data is here so we can use this as a callback for IPC buddy_get_timezone(PurpleBlistNode * node, gboolean resolve, void *data) PurpleBlistNode *datanode = NULL; case PURPLE_BLIST_BUDDY_NODE: datanode = (PurpleBlistNode *) purple_buddy_get_contact((PurpleBuddy *) node); case PURPLE_BLIST_CONTACT_NODE: case PURPLE_BLIST_GROUP_NODE: timezone = purple_blist_node_get_string(datanode, SETTING_NAME); /* The effect of "none" is to stop recursion */ if (timezone && strcmp(timezone, "none") == 0) if (datanode->type == PURPLE_BLIST_CONTACT_NODE) /* There is no purple_blist_contact_get_group(), though there probably should be */ datanode = datanode->parent; timezone = purple_blist_node_get_string(datanode, SETTING_NAME); if (timezone && strcmp(timezone, "none") == 0) /* Calcuates the difference between two struct tm's. */ timezone_calc_difference(struct tm *remote_tm, struct tm *tmp_tm) /* Note this only works because the times are * known to be within 24 hours of each other! */ if (remote_tm->tm_mday != tmp_tm->tm_mday) hours_diff += (remote_tm->tm_hour - tmp_tm->tm_hour); minutes_diff = (remote_tm->tm_min - tmp_tm->tm_min); return (double)minutes_diff / 60.0 + hours_diff; /* data is here so we can use this as a callback for IPC */ timezone_get_time(const char *timezone, struct tm *tm, double *diff, void *data) struct state *tzinfo = timezone_load(timezone); localsub(&now, 0, tm, tzinfo); /* Store the current TZ value. */ g_setenv("TZ", timezone, TRUE); tm_tmp = localtime(&now); *tm = *tm_tmp; /* Must copy, localtime uses local buffer */ /* Reset the old TZ value. */ g_setenv("TZ", old_tz, TRUE); /* Calculate user's localtime, and compare. If same, no output */ tm_tmp = localtime(&now); if (tm_tmp->tm_hour == tm->tm_hour && tm_tmp->tm_min == tm->tm_min) *diff = timezone_calc_difference(tm, tm_tmp); timezone_createconv_cb(PurpleConversation * conv, void *data) if (purple_conversation_get_type(conv) != PURPLE_CONV_TYPE_IM) name = purple_conversation_get_name(conv); buddy = purple_find_buddy(purple_conversation_get_account(conv), name); timezone = buddy_get_timezone((PurpleBlistNode *) buddy, TRUE, NULL); ret = timezone_get_time(timezone, &tm, &diff, NULL); const char *text = purple_time_format(&tm); str = g_strdup_printf(dngettext(GETTEXT_PACKAGE, "Remote Local Time: %s (%.4g hour behind)", "Remote Local Time: %s (%.4g hours behind)", diff), str = g_strdup_printf(dngettext(GETTEXT_PACKAGE, "Remote Local Time: %s (%.4g hour ahead)", "Remote Local Time: %s (%.4g hours ahead)", diff), purple_conversation_write(conv, PLUGIN_STATIC_NAME, str, PURPLE_MESSAGE_SYSTEM, time(NULL)); buddytimezone_submitfields_cb(PurpleRequestFields * fields, PurpleBlistNode * data) PurpleRequestField *list; purple_debug(PURPLE_DEBUG_INFO, PLUGIN_STATIC_NAME, "buddytimezone_submitfields_cb(%p,%p)\n", fields, data); case PURPLE_BLIST_BUDDY_NODE: node = (PurpleBlistNode *) purple_buddy_get_contact((PurpleBuddy *) data); case PURPLE_BLIST_CONTACT_NODE: case PURPLE_BLIST_GROUP_NODE: /* code handles either case */ case PURPLE_BLIST_CHAT_NODE: case PURPLE_BLIST_OTHER_NODE: list = purple_request_fields_get_field(fields, CONTROL_NAME); if (ui_ops != NULL && ui_ops->get_timezone_menu_selection != NULL) const char *seldata = ui_ops->get_timezone_menu_selection(list->ui_data); purple_blist_node_remove_setting(node, SETTING_NAME); purple_blist_node_set_string(node, SETTING_NAME, seldata); sellist = purple_request_field_list_get_selected(list); seldata = purple_request_field_list_get_data(list, sellist->data); /* Otherwise, it's fixed value and this means deletion */ if (seldata == TIMEZONE_FLAG) purple_blist_node_set_string(node, SETTING_NAME, sellist->data); else if (seldata == DISABLED_FLAG) purple_blist_node_set_string(node, SETTING_NAME, "none"); purple_blist_node_remove_setting(node, SETTING_NAME); buddy_add_timezone_cb(char *filename, void *data) PurpleRequestField *field = (PurpleRequestField *) data; if (isupper(filename[0])) purple_request_field_list_add(field, filename, TIMEZONE_FLAG); buddytimezone_createfields_cb(PurpleRequestFields * fields, PurpleBlistNode * data) purple_debug(PURPLE_DEBUG_INFO, PLUGIN_STATIC_NAME, "buddytimezone_createfields_cb(%p,%p)\n", fields, data); PurpleRequestField *field; PurpleRequestFieldGroup *group; case PURPLE_BLIST_BUDDY_NODE: case PURPLE_BLIST_CONTACT_NODE: case PURPLE_BLIST_GROUP_NODE: case PURPLE_BLIST_CHAT_NODE: case PURPLE_BLIST_OTHER_NODE: group = purple_request_field_group_new(NULL); purple_request_fields_add_group(fields, group); timezone = buddy_get_timezone(data, FALSE, NULL); if (ui_ops != NULL && ui_ops->create_menu) purple_request_field_new(CONTROL_NAME, is_default ? "Default timezone for group" : "Timezone of contact", PURPLE_REQUEST_FIELD_LIST); field->ui_data = ui_ops->create_menu(timezone); purple_request_field_list_new(CONTROL_NAME, is_default ? "Default timezone for group" : "Timezone of contact (type to select)"); purple_request_field_list_set_multi_select(field, FALSE); purple_request_field_list_add(field, "<Default>", ""); purple_request_field_list_add(field, "<Disabled>", DISABLED_FLAG); recurse_directory("/usr/share/zoneinfo/", buddy_add_timezone_cb, field); if (strcmp(timezone, "none") == 0) purple_request_field_list_add_selected(field, "<Disabled>"); purple_request_field_list_add_selected(field, timezone); purple_request_field_list_add_selected(field, "<Default>"); purple_request_field_group_add_field(group, field); marshal_POINTER__POINTER_BOOL(PurpleCallback cb, va_list args, void *data, void *arg1 = va_arg(args, void *); gboolean arg2 = va_arg(args, gboolean); ret_val = ((gpointer (*)(void *, gboolean, void *))cb)(arg1, arg2, data); marshal_POINTER__POINTER_POINTER_POINTER(PurpleCallback cb, va_list args, void *data, void *arg1 = va_arg(args, void *); void *arg2 = va_arg(args, void *); void *arg3 = va_arg(args, void *); ret_val = ((gpointer (*)(void *, void *, void *, void *))cb)(arg1, arg2, arg3, data); load_ui_plugin(gpointer data) ui_plugin_id = g_strconcat(purple_core_get_ui(), "-", PLUGIN_STATIC_NAME, NULL); ui_plugin = purple_plugins_find_with_id(ui_plugin_id); if (!purple_plugin_load(ui_plugin)) purple_notify_error(ui_plugin, NULL, _("Failed to load the Buddy Timezone UI."), ui_plugin->error ? ui_plugin->error : ""); plugin_load(PurplePlugin * plugin) purple_signal_connect(purple_conversations_get_handle(), "conversation-created", plugin, PURPLE_CALLBACK(timezone_createconv_cb), NULL); purple_plugin_ipc_register(plugin, BUDDYTIME_BUDDY_GET_TIMEZONE, PURPLE_CALLBACK(buddy_get_timezone), marshal_POINTER__POINTER_BOOL, purple_value_new(PURPLE_TYPE_STRING), purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_BLIST_NODE), purple_value_new(PURPLE_TYPE_BOOLEAN)); purple_plugin_ipc_register(plugin, BUDDYTIME_TIMEZONE_GET_TIME, PURPLE_CALLBACK(timezone_get_time), marshal_POINTER__POINTER_POINTER_POINTER, purple_value_new(PURPLE_TYPE_INT), purple_value_new(PURPLE_TYPE_POINTER), purple_value_new(PURPLE_TYPE_POINTER)); /* This is done as an idle callback to avoid an infinite loop * when we try to load the UI plugin which depends on this plugin * which isn't officially loaded yet. */ purple_timeout_add(0, load_ui_plugin, NULL); static PurplePluginInfo info = PURPLE_PLUGIN_STANDARD, /**< type */ NULL, /**< ui_requirement */ NULL, /**< dependencies */ PURPLE_PRIORITY_DEFAULT, /**< priority */ PP_VERSION, /**< version */ NULL, /**< description */ PLUGIN_AUTHOR, /**< author */ PP_WEBSITE, /**< homepage */ plugin_load, /**< load */ init_plugin(PurplePlugin * plugin) bindtextdomain(GETTEXT_PACKAGE, PP_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); info.name = _("Buddy Time"); info.summary = _("Quickly see the local time of a buddy"); info.description = _("Quickly see the local time of a buddy"); PURPLE_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info);