view plugins/gevolution/gevolution.c @ 8986:8cf32769ba1b

[gaim-migrate @ 9761] " This patch adds a Plugin Actions menu item after the Account Actions menu. The Plugin Actions menu is populated from the added 'actions' slot in GaimPluginInfo. As a demonstration, the Idle Maker plugin has been converted to no longer require GTK code and the Preferences interface just to perform its actions. Instead, it uses a Plugin Action to spawn a Fields Request. There's also a minor fix for consistency in the menu building for buddy actions. The pre-existing method for instructing a menu list to display a separator was to insert a NULL rather than a proto_buddy_menu into the GList of actions. The code for the buddy menus was instead checking for a proto_buddy_menu with a '-' label. This has been fixed, and it now correctly uses NULL to indicate a separator." "Date: 2004-05-16 02:25 Sender: taliesein Logged In: YES user_id=77326 I need to add a callback to this patch to watch for loading/unloading of plugins (to determine when to rebuild the menu). Since the appropriate way to handle Plugin Actions is still mildly up for debate, I'm holding of on correcting the patch until I know for sure whether I should fix this patch, or scrap it and write a new one using a different method." "Date: 2004-05-18 12:26 Sender: taliesein Logged In: YES user_id=77326 I've completed changes to this patch to also add plugin load and unload signals (it looks like plugin.c actually had pre-signal callbacks in place, but they were never used or converted to signals) This patch now will correctly update the Plugin Action menu as plugins load and unload." I'm not entirely sure i like the ui of a plugins actions menu, but i think that having some way for plugins to add actions on an account is a good thing, and i'm not sure that every viable action fits under the accounts actions menu. we may want to merge the two (the existing accounts actions and this plugins actions), but both times it came up in #gaim no one seemed to want to comment, and on one commented to the gaim-devel post either. committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Thu, 20 May 2004 05:11:44 +0000
parents d7b8eb1f0a18
children 294ae6548d4e
line wrap: on
line source

/*
 * Evolution integration plugin for Gaim
 *
 * Copyright (C) 2003 Christian Hammond.
 *
 * 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 "internal.h"
#include "gtkinternal.h"
#include "gtkblist.h"

#include "connection.h"
#include "debug.h"
#include "prefs.h"
#include "signals.h"

#include "gtkconv.h"
#include "gtkplugin.h"
#include "gtkutils.h"

#include "gevolution.h"

#include <libedata-book/Evolution-DataServer-Addressbook.h>

#include <libebook/e-book-listener.h>
#include <libebook/e-book-async.h>
#include <libedata-book/e-data-book-factory.h>
#include <bonobo/bonobo-main.h>

#define GEVOLUTION_PLUGIN_ID "gtk-x11-gevolution"

#define E_DATA_BOOK_FACTORY_OAF_ID \
	"OAFIID:GNOME_Evolution_DataServer_BookFactory"

enum
{
	COLUMN_AUTOADD,
	COLUMN_ICON,
	COLUMN_SCREENNAME,
	COLUMN_DATA,
	NUM_COLUMNS
};

static GaimBlistUiOps *backup_blist_ui_ops = NULL;
static GaimBlistUiOps *blist_ui_ops = NULL;
static EBook *book = NULL;
static gulong timer = 0;
static gulong book_view_tag = 0;
static EBookView *book_view = NULL;

static void
update_ims_from_contact(EContact *contact, const char *name,
						const char *prpl_id, EContactField field)
{
	GList *ims = e_contact_get(contact, field);
	GList *l, *l2;

	if (ims == NULL)
		return;

	for (l = gaim_connections_get_all(); l != NULL; l = l->next)
	{
		GaimConnection *gc = (GaimConnection *)l->data;
		GaimAccount *account = gaim_connection_get_account(gc);

		if (strcmp(gaim_account_get_protocol_id(account), prpl_id))
			continue;

		if (!gaim_account_get_bool(account, "gevo-autoadd", FALSE))
			continue;

		for (l2 = ims; l2 != NULL; l2 = l2->next)
		{
			if (gaim_find_buddy(account, l2->data) != NULL)
				continue;

			gevo_add_buddy(account, _("Buddies"), l2->data, name);
		}
	}

	g_list_foreach(ims, (GFunc)g_free, NULL);
	g_list_free(ims);
}

static void
update_buddies_from_contact(EContact *contact)
{
	const char *name;

	name = e_contact_get_const(contact, E_CONTACT_FULL_NAME);

	update_ims_from_contact(contact, name, "prpl-oscar",  E_CONTACT_IM_AIM);
	update_ims_from_contact(contact, name, "prpl-jabber", E_CONTACT_IM_JABBER);
	update_ims_from_contact(contact, name, "prpl-yahoo",  E_CONTACT_IM_YAHOO);
	update_ims_from_contact(contact, name, "prpl-msn",    E_CONTACT_IM_MSN);
	update_ims_from_contact(contact, name, "prpl-oscar",  E_CONTACT_IM_ICQ);
}

static void
contacts_changed_cb(EBookView *book_view, const GList *contacts)
{
	const GList *l;

	if (gaim_connections_get_all() == NULL)
		return;

	for (l = contacts; l != NULL; l = l->next)
	{
		EContact *contact = (EContact *)l->data;

		update_buddies_from_contact(contact);
	}
}

static void
request_add_buddy(GaimAccount *account, const char *username,
				  const char *group, const char *alias)
{
	if (book == NULL)
	{
		backup_blist_ui_ops->request_add_buddy(account, username, group,
											   alias);
	}
	else
	{
		gevo_add_buddy_dialog_show(account, username, group, alias);
	}
}

static void
got_book_view_cb(EBook *book, EBookStatus status, EBookView *view,
				 gpointer user_data)
{
	book_view_tag = 0;

	if (status != E_BOOK_ERROR_OK)
	{
		gaim_debug_error("evolution", "Unable to retrieve book view! :(\n");

		return;
	}

	book_view = view;

	g_object_ref(book_view);

	g_signal_connect(G_OBJECT(book_view), "contacts_changed",
					 G_CALLBACK(contacts_changed_cb), book);

	g_signal_connect(G_OBJECT(book_view), "contacts_added",
					 G_CALLBACK(contacts_changed_cb), book);

	e_book_view_start(view);
}

static void
signed_on_cb(GaimConnection *gc)
{
	EBookQuery *query;
	gboolean status;
	GList *contacts;
	GList *l;

	if (book == NULL)
		return;

	query = e_book_query_any_field_contains("");

	status = e_book_get_contacts(book, query, &contacts, NULL);

	e_book_query_unref(query);

	if (!status)
		return;

	for (l = contacts; l != NULL; l = l->next)
	{
		EContact *contact = E_CONTACT(l->data);

		update_buddies_from_contact(contact);

		g_object_unref(contact);
	}

	g_list_free(contacts);
}

static void
menu_item_activate_cb(GtkWidget *item, GaimBuddy *buddy)
{
	gevo_associate_buddy_dialog_new(buddy);
}

static void
drawing_menu_cb(GtkWidget *menu, GaimBuddy *buddy)
{
	GtkWidget *item;

	if (gevo_prpl_is_supported(buddy->account, buddy))
	{
		item = gtk_menu_item_new_with_label(_("Add to Address Book"));
		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);

		g_signal_connect(G_OBJECT(item), "activate",
						 G_CALLBACK(menu_item_activate_cb), buddy);
	}
}

static gboolean
load_timeout(gpointer data)
{
	GaimPlugin *plugin = (GaimPlugin *)data;
	EBookQuery *query;

	timer = 0;

	if (!gevo_load_addressbook(&book, NULL))
		return FALSE;

	query = e_book_query_any_field_contains("");

	book_view_tag = e_book_async_get_book_view(book, query, NULL, -1,
											   got_book_view_cb, NULL);

	e_book_query_unref(query);

	gaim_signal_connect(GAIM_GTK_BLIST(gaim_get_blist()), "drawing-menu",
						plugin, GAIM_CALLBACK(drawing_menu_cb), NULL);

	return FALSE;
}

static gboolean
plugin_load(GaimPlugin *plugin)
{
	if (!bonobo_init_full(NULL, NULL, bonobo_activation_orb_get(),
						  CORBA_OBJECT_NIL, CORBA_OBJECT_NIL))
	{
		gaim_debug_error("evolution", "Unable to initialize bonobo.\n");
		return FALSE;
	}

	bonobo_activate();

	backup_blist_ui_ops = gaim_blist_get_ui_ops();

	blist_ui_ops = g_memdup(backup_blist_ui_ops, sizeof(GaimBlistUiOps));
	blist_ui_ops->request_add_buddy = request_add_buddy;

	gaim_blist_set_ui_ops(blist_ui_ops);

	gaim_signal_connect(gaim_connections_get_handle(), "signed-on",
						plugin, GAIM_CALLBACK(signed_on_cb), NULL);

	timer = g_timeout_add(1, load_timeout, plugin);

	return TRUE;
}

static gboolean
plugin_unload(GaimPlugin *plugin)
{
	gaim_blist_set_ui_ops(backup_blist_ui_ops);

	g_free(blist_ui_ops);

	backup_blist_ui_ops = NULL;
	blist_ui_ops = NULL;

	if (book_view != NULL)
	{
		e_book_view_stop(book_view);
		g_object_unref(book_view);
		book_view = NULL;
	}

	if (book != NULL)
	{
		g_object_unref(book);
		book = NULL;
	}

	bonobo_debug_shutdown();

	return TRUE;
}

static void
plugin_destroy(GaimPlugin *plugin)
{
}

static void
autoadd_toggled_cb(GtkCellRendererToggle *renderer, gchar *path_str,
				   gpointer data)
{
	GaimAccount *account;
	GtkTreeModel *model = (GtkTreeModel *)data;
	GtkTreeIter iter;
	gboolean autoadd;

	gtk_tree_model_get_iter_from_string(model, &iter, path_str);
	gtk_tree_model_get(model, &iter,
					   COLUMN_DATA, &account,
					   COLUMN_AUTOADD, &autoadd,
					   -1);

	gaim_account_set_bool(account, "gevo-autoadd", !autoadd);

	gtk_list_store_set(GTK_LIST_STORE(model), &iter,
					   COLUMN_AUTOADD, !autoadd,
					   -1);
}

static GtkWidget *
get_config_frame(GaimPlugin *plugin)
{
	GtkWidget *ret;
	GtkWidget *vbox;
	GtkWidget *label;
	GtkWidget *sw;
	GtkWidget *treeview;
	GtkTreeViewColumn *column;
	GtkCellRenderer *renderer;
	GdkPixbuf *pixbuf, *scale = NULL;
	GtkListStore *model;
	GList *l;

	/* Outside container */
	ret = gtk_vbox_new(FALSE, 18);
	gtk_container_set_border_width(GTK_CONTAINER(ret), 12);

	/* Configuration frame */
	vbox = gaim_gtk_make_frame(ret, _("Evolution Integration Configuration"));

	/* Label */
	label = gtk_label_new(_("Select all accounts that buddies should be "
							"auto-added to."));
	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
	gtk_widget_show(label);

	/* Scrolled window */
	sw = gtk_scrolled_window_new(0, 0);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
								   GTK_POLICY_AUTOMATIC,
								   GTK_POLICY_ALWAYS);
	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
										GTK_SHADOW_IN);
	gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	gtk_widget_set_size_request(sw, 300, 300);
	gtk_widget_show(sw);

	/* Create the list model for the treeview. */
	model = gtk_list_store_new(NUM_COLUMNS,
							   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
							   G_TYPE_STRING, G_TYPE_POINTER);

	/* Setup the treeview */
	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
	gtk_container_add(GTK_CONTAINER(sw), treeview);
	gtk_widget_show(treeview);

	/* Setup the column */
	column = gtk_tree_view_column_new();
	gtk_tree_view_column_set_title(column, _("Account"));
	gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);

	/* Checkbox */
	renderer = gtk_cell_renderer_toggle_new();

	g_signal_connect(G_OBJECT(renderer), "toggled",
					 G_CALLBACK(autoadd_toggled_cb), model);

	gtk_tree_view_column_pack_start(column, renderer, FALSE);
	gtk_tree_view_column_add_attribute(column, renderer,
									   "active", COLUMN_AUTOADD);

	/* Icon */
	renderer = gtk_cell_renderer_pixbuf_new();
	gtk_tree_view_column_pack_start(column, renderer, FALSE);
	gtk_tree_view_column_add_attribute(column, renderer,
									   "pixbuf", COLUMN_ICON);

	/* Screenname */
	renderer = gtk_cell_renderer_text_new();
	gtk_tree_view_column_pack_start(column, renderer, TRUE);
	gtk_tree_view_column_add_attribute(column, renderer,
									   "text", COLUMN_SCREENNAME);


	/* Populate */
	for (l = gaim_accounts_get_all(); l != NULL; l = l->next)
	{
		GaimAccount *account = (GaimAccount *)l->data;
		GtkTreeIter iter;

		gaim_debug_info("evolution", "Adding account\n");

		gtk_list_store_append(model, &iter);

		pixbuf = create_prpl_icon(account);

		if (pixbuf != NULL)
		{
			scale = gdk_pixbuf_scale_simple(pixbuf, 16, 16,
											GDK_INTERP_BILINEAR);

			if (!gaim_account_is_connected(account))
				gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE);
		}

		gtk_list_store_set(model, &iter,
						   COLUMN_AUTOADD,
						   gaim_account_get_bool(account, "gevo-autoadd",
												 FALSE),
						   COLUMN_ICON, scale,
						   COLUMN_SCREENNAME,
						   gaim_account_get_username(account),
						   COLUMN_DATA, account,
						   -1);

		if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf));
		if (scale  != NULL) g_object_unref(G_OBJECT(scale));
	}

	gtk_widget_show_all(ret);

	return ret;
}

static GaimGtkPluginUiInfo ui_info =
{
	get_config_frame
};

static GaimPluginInfo info =
{
	GAIM_PLUGIN_API_VERSION,                          /**< api_version    */
	GAIM_PLUGIN_STANDARD,                             /**< type           */
	GAIM_GTK_PLUGIN_TYPE,                             /**< ui_requirement */
	0,                                                /**< flags          */
	NULL,                                             /**< dependencies   */
	GAIM_PRIORITY_DEFAULT,                            /**< priority       */

	GEVOLUTION_PLUGIN_ID,                             /**< id             */
	N_("Evolution Integration"),                      /**< name           */
	VERSION,                                          /**< version        */
	                                                  /**  summary        */
	N_("Provides integration with Ximian Evolution."),
	                                                  /**  description    */
	N_("Provides integration with Ximian Evolution."),
	"Christian Hammond <chipx86@gnupdate.org>",       /**< author         */
	GAIM_WEBSITE,                                     /**< homepage       */

	plugin_load,                                      /**< load           */
	plugin_unload,                                    /**< unload         */
	plugin_destroy,                                   /**< destroy        */

	&ui_info,                                         /**< ui_info        */
	NULL                                              /**< extra_info     */
};

static void
init_plugin(GaimPlugin *plugin)
{
	/* TODO: Change to core-remote when possible. */
	/* info.dependencies = g_list_append(info.dependencies, "gtk-remote"); */

	/*
	 * I'm going to rant a little bit here...
	 *
	 * For some reason, when we init bonobo from inside a plugin, it will
	 * segfault when destroyed. The backtraces are within gmodule somewhere.
	 * There's not much I can do, and I'm not sure where the bug lies.
	 * However, plugins are only destroyed when Gaim is shutting down. After
	 * destroying the plugins, gaim ends, and anything else is of course
	 * freed. That includes this, if we make the module resident, which
	 * prevents us from being able to actually unload it.
	 *
	 * So, in conclusion, this is an evil hack, but it doesn't harm anything
	 * and it works.
	 */
	g_module_make_resident(plugin->handle);
}

GAIM_INIT_PLUGIN(gevolution, init_plugin, info)