libpurple/roomlist.c

Wed, 02 Apr 2014 02:37:34 +0200

author
Tomasz Wasilczyk <twasilczyk@pidgin.im>
date
Wed, 02 Apr 2014 02:37:34 +0200
changeset 35717
45bde03f86a6
parent 35597
7fcfcf147b99
child 37119
fdd7282fdad6
permissions
-rw-r--r--

Custom smileys: simplify storage implementation

/* purple
 *
 * Purple is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * 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  02111-1301  USA
 */

#include "internal.h"
#include "glibcompat.h"

#include "account.h"
#include "connection.h"
#include "debug.h"
#include "roomlist.h"
#include "server.h"

#define PURPLE_ROOMLIST_GET_PRIVATE(obj) \
	(G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ROOMLIST, PurpleRoomlistPrivate))

typedef struct _PurpleRoomlistPrivate  PurpleRoomlistPrivate;

/*
 * Private data for a room list.
 */
struct _PurpleRoomlistPrivate {
	PurpleAccount *account;  /* The account this list belongs to. */
	GList *fields;           /* The fields.                       */
	GList *rooms;            /* The list of rooms.                */
	gboolean in_progress;    /* The listing is in progress.       */

	/* TODO Remove this and use protocol-specific subclasses. */
	gpointer proto_data;     /* Protocol private data.             */
};

/*
 * Represents a room.
 */
struct _PurpleRoomlistRoom {
	PurpleRoomlistRoomType type; /* The type of room. */
	gchar *name; /* The name of the room. */
	GList *fields; /* Other fields. */
	PurpleRoomlistRoom *parent; /* The parent room, or NULL. */
	gboolean expanded_once; /* A flag the UI uses to avoid multiple expand prpl cbs. */
};

/*
 * A field a room might have.
 */
struct _PurpleRoomlistField {
	PurpleRoomlistFieldType type; /* The type of field. */
	gchar *label; /* The i18n user displayed name of the field. */
	gchar *name; /* The internal name of the field. */
	gboolean hidden; /* Hidden? */
};

/* Room list property enums */
enum
{
	PROP_0,
	PROP_ACCOUNT,
	PROP_FIELDS,
	PROP_IN_PROGRESS,
	PROP_LAST
};

static GObjectClass *parent_class;
static GParamSpec *properties[PROP_LAST];
static PurpleRoomlistUiOps *ops = NULL;

static void purple_roomlist_field_free(PurpleRoomlistField *f);
static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r);

/**************************************************************************/
/* Room List API                                                          */
/**************************************************************************/

void purple_roomlist_show_with_account(PurpleAccount *account)
{
	if (ops && ops->show_with_account)
		ops->show_with_account(account);
}

PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_val_if_fail(priv != NULL, NULL);

	return priv->account;
}

void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_if_fail(priv != NULL);

	priv->fields = fields;

	if (ops && ops->set_fields)
		ops->set_fields(list, fields);

	g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
}

void purple_roomlist_set_in_progress(PurpleRoomlist *list, gboolean in_progress)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_if_fail(priv != NULL);

	priv->in_progress = in_progress;

	if (ops && ops->in_progress)
		ops->in_progress(list, in_progress);

	g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_IN_PROGRESS]);
}

gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_val_if_fail(priv != NULL, FALSE);

	return priv->in_progress;
}

void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_if_fail(priv != NULL);
	g_return_if_fail(room != NULL);

	priv->rooms = g_list_append(priv->rooms, room);

	if (ops && ops->add_room)
		ops->add_room(list, room);
}

PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc)
{
	PurplePlugin *prpl = NULL;
	PurplePluginProtocolInfo *prpl_info = NULL;

	g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
	g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL);

	prpl = purple_connection_get_prpl(gc);

	if(prpl != NULL)
		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);

	if(prpl_info && prpl_info->roomlist_get_list)
		return prpl_info->roomlist_get_list(gc);

	return NULL;
}

void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	PurplePlugin *prpl = NULL;
	PurplePluginProtocolInfo *prpl_info = NULL;
	PurpleConnection *gc;

	g_return_if_fail(priv != NULL);

	gc = purple_account_get_connection(priv->account);

	g_return_if_fail(PURPLE_IS_CONNECTION(gc));

	if(gc)
		prpl = purple_connection_get_prpl(gc);

	if(prpl)
		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);

	if(prpl_info && prpl_info->roomlist_cancel)
		prpl_info->roomlist_cancel(list);
}

void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	PurplePlugin *prpl = NULL;
	PurplePluginProtocolInfo *prpl_info = NULL;
	PurpleConnection *gc;

	g_return_if_fail(priv != NULL);
	g_return_if_fail(category != NULL);
	g_return_if_fail(category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);

	gc = purple_account_get_connection(priv->account);
	g_return_if_fail(PURPLE_IS_CONNECTION(gc));

	if(gc)
		prpl = purple_connection_get_prpl(gc);

	if(prpl)
		prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);

	if(prpl_info && prpl_info->roomlist_expand_category)
		prpl_info->roomlist_expand_category(list, category);
}

GList * purple_roomlist_get_fields(PurpleRoomlist *list)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_val_if_fail(priv != NULL, NULL);

	return priv->fields;
}

gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_val_if_fail(priv != NULL, NULL);

	return priv->proto_data;
}

void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	g_return_if_fail(priv != NULL);

	priv->proto_data = proto_data;
}

gpointer purple_roomlist_get_ui_data(PurpleRoomlist *list)
{
	g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);

	return list->ui_data;
}

void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data)
{
	g_return_if_fail(PURPLE_IS_ROOMLIST(list));

	list->ui_data = ui_data;
}

/**************************************************************************/
/* Room List GObject code                                                 */
/**************************************************************************/

/* Set method for GObject properties */
static void
purple_roomlist_set_property(GObject *obj, guint param_id, const GValue *value,
		GParamSpec *pspec)
{
	PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);

	switch (param_id) {
		case PROP_ACCOUNT:
			priv->account = g_value_get_object(value);
			break;
		case PROP_FIELDS:
			purple_roomlist_set_fields(list, g_value_get_pointer(value));
			break;
		case PROP_IN_PROGRESS:
			purple_roomlist_set_in_progress(list, g_value_get_boolean(value));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
			break;
	}
}

/* Get method for GObject properties */
static void
purple_roomlist_get_property(GObject *obj, guint param_id, GValue *value,
		GParamSpec *pspec)
{
	PurpleRoomlist *list = PURPLE_ROOMLIST(obj);

	switch (param_id) {
		case PROP_ACCOUNT:
			g_value_set_object(value, purple_roomlist_get_account(list));
			break;
		case PROP_FIELDS:
			g_value_set_pointer(value, purple_roomlist_get_fields(list));
			break;
		case PROP_IN_PROGRESS:
			g_value_set_boolean(value, purple_roomlist_get_in_progress(list));
			break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
			break;
	}
}

/* Called when done constructing */
static void
purple_roomlist_constructed(GObject *object)
{
	PurpleRoomlist *list = PURPLE_ROOMLIST(object);

	parent_class->constructed(object);

	if (ops && ops->create)
		ops->create(list);
}

/* GObject finalize function */
static void
purple_roomlist_finalize(GObject *object)
{
	PurpleRoomlist *list = PURPLE_ROOMLIST(object);
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	GList *l;

	purple_debug_misc("roomlist", "destroying list %p\n", list);

	if (ops && ops->destroy)
		ops->destroy(list);

	for (l = priv->rooms; l; l = l->next) {
		PurpleRoomlistRoom *r = l->data;
		purple_roomlist_room_destroy(list, r);
	}
	g_list_free(priv->rooms);

	g_list_foreach(priv->fields, (GFunc)purple_roomlist_field_free, NULL);
	g_list_free(priv->fields);

	parent_class->finalize(object);
}

/* Class initializer function */
static void
purple_roomlist_class_init(PurpleRoomlistClass *klass)
{
	GObjectClass *obj_class = G_OBJECT_CLASS(klass);

	parent_class = g_type_class_peek_parent(klass);

	obj_class->finalize = purple_roomlist_finalize;
	obj_class->constructed = purple_roomlist_constructed;

	/* Setup properties */
	obj_class->get_property = purple_roomlist_get_property;
	obj_class->set_property = purple_roomlist_set_property;

	g_type_class_add_private(klass, sizeof(PurpleRoomlistPrivate));

	properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
				"The account for the room list.",
				PURPLE_TYPE_ACCOUNT,
				G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
				G_PARAM_STATIC_STRINGS);

	properties[PROP_FIELDS] = g_param_spec_pointer("fields", "Fields",
				"The list of fields for a roomlist.",
				G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	properties[PROP_IN_PROGRESS] = g_param_spec_boolean("in-progress",
				"In progress",
				"Whether the room list is being fetched.", FALSE,
				G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);

	g_object_class_install_properties(obj_class, PROP_LAST, properties);
}

GType
purple_roomlist_get_type(void)
{
	static GType type = 0;

	if(type == 0) {
		static const GTypeInfo info = {
			sizeof(PurpleRoomlistClass),
			NULL,
			NULL,
			(GClassInitFunc)purple_roomlist_class_init,
			NULL,
			NULL,
			sizeof(PurpleRoomlist),
			0,
			NULL,
			NULL,
		};

		type = g_type_register_static(G_TYPE_OBJECT, "PurpleRoomlist",
				&info, 0);
	}

	return type;
}

PurpleRoomlist *purple_roomlist_new(PurpleAccount *account)
{
	g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);

	return g_object_new(PURPLE_TYPE_ROOMLIST,
		"account", account,
		NULL
	);
}

/**************************************************************************/
/* Room API                                                               */
/**************************************************************************/

PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const gchar *name,
                                         PurpleRoomlistRoom *parent)
{
	PurpleRoomlistRoom *room;

	g_return_val_if_fail(name != NULL, NULL);

	room = g_new0(PurpleRoomlistRoom, 1);
	room->type = type;
	room->name = g_strdup(name);
	room->parent = parent;

	return room;
}

void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	PurpleRoomlistField *f;

	g_return_if_fail(priv != NULL);
	g_return_if_fail(room != NULL);
	g_return_if_fail(priv->fields != NULL);

	/* If this is the first call for this room, grab the first field in
	 * the Roomlist's fields.  Otherwise, grab the field that is one
	 * more than the number of fields already present for the room.
         * (This works because g_list_nth_data() is zero-indexed and
         * g_list_length() is one-indexed.) */
	if (!room->fields)
		f = priv->fields->data;
	else
		f = g_list_nth_data(priv->fields, g_list_length(room->fields));

	g_return_if_fail(f != NULL);

	switch(f->type) {
		case PURPLE_ROOMLIST_FIELD_STRING:
			room->fields = g_list_append(room->fields, g_strdup(field));
			break;
		case PURPLE_ROOMLIST_FIELD_BOOL:
		case PURPLE_ROOMLIST_FIELD_INT:
			room->fields = g_list_append(room->fields, GINT_TO_POINTER(field));
			break;
	}

	g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
}

void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	GHashTable *components;
	GList *l, *j;
	PurpleConnection *gc;

	g_return_if_fail(priv != NULL);
	g_return_if_fail(room != NULL);

	gc = purple_account_get_connection(priv->account);
	if (!gc)
		return;

	components = g_hash_table_new(g_str_hash, g_str_equal);

	g_hash_table_replace(components, "name", room->name);
	for (l = priv->fields, j = room->fields; l && j; l = l->next, j = j->next) {
		PurpleRoomlistField *f = l->data;

		g_hash_table_replace(components, f->name, j->data);
	}

	purple_serv_join_chat(gc, components);

	g_hash_table_destroy(components);
}

PurpleRoomlistRoomType purple_roomlist_room_get_room_type(PurpleRoomlistRoom *room)
{
	return room->type;
}

const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room)
{
	return room->name;
}

PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room)
{
	return room->parent;
}

gboolean purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom *room)
{
	g_return_val_if_fail(room != NULL, FALSE);

	return room->expanded_once;
}

void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom *room, gboolean expanded_once)
{
	g_return_if_fail(room != NULL);

	room->expanded_once = expanded_once;
}

GList *purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
{
	return room->fields;
}

static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r)
{
	PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
	GList *l, *j;

	for (l = priv->fields, j = r->fields; l && j; l = l->next, j = j->next) {
		PurpleRoomlistField *f = l->data;
		if (f->type == PURPLE_ROOMLIST_FIELD_STRING)
			g_free(j->data);
	}

	g_list_free(r->fields);
	g_free(r->name);
	g_free(r);
}

/**************************************************************************/
/* Room GBoxed code                                                       */
/**************************************************************************/

static PurpleRoomlistRoom *purple_roomlist_room_copy(PurpleRoomlistRoom *r)
{
	g_return_val_if_fail(r != NULL, NULL);

	return purple_roomlist_room_new(r->type, r->name, r->parent);
}

static void purple_roomlist_room_free(PurpleRoomlistRoom *r)
{
	g_return_if_fail(r != NULL);

	g_list_free(r->fields);
	g_free(r->name);
	g_free(r);
}

GType purple_roomlist_room_get_type(void)
{
	static GType type = 0;

	if (type == 0) {
		type = g_boxed_type_register_static("PurpleRoomlistRoom",
				(GBoxedCopyFunc)purple_roomlist_room_copy,
				(GBoxedFreeFunc)purple_roomlist_room_free);
	}

	return type;
}

/**************************************************************************/
/* Room Field API                                                         */
/**************************************************************************/

PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
                                           const gchar *label, const gchar *name,
                                           gboolean hidden)
{
	PurpleRoomlistField *f;

	g_return_val_if_fail(label != NULL, NULL);
	g_return_val_if_fail(name != NULL, NULL);

	f = g_new0(PurpleRoomlistField, 1);

	f->type = type;
	f->label = g_strdup(label);
	f->name = g_strdup(name);
	f->hidden = hidden;

	return f;
}

PurpleRoomlistFieldType purple_roomlist_field_get_field_type(PurpleRoomlistField *field)
{
	return field->type;
}

const char * purple_roomlist_field_get_label(PurpleRoomlistField *field)
{
	return field->label;
}

gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field)
{
	return field->hidden;
}

/**************************************************************************/
/* Room Field GBoxed code                                                 */
/**************************************************************************/

static PurpleRoomlistField *purple_roomlist_field_copy(PurpleRoomlistField *f)
{
	g_return_val_if_fail(f != NULL, NULL);

	return purple_roomlist_field_new(f->type, f->label, f->name, f->hidden);
}

static void purple_roomlist_field_free(PurpleRoomlistField *f)
{
	g_return_if_fail(f != NULL);

	g_free(f->label);
	g_free(f->name);
	g_free(f);
}

GType purple_roomlist_field_get_type(void)
{
	static GType type = 0;

	if (type == 0) {
		type = g_boxed_type_register_static("PurpleRoomlistField",
				(GBoxedCopyFunc)purple_roomlist_field_copy,
				(GBoxedFreeFunc)purple_roomlist_field_free);
	}

	return type;
}

/**************************************************************************/
/* UI Registration Functions                                              */
/**************************************************************************/

void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps *ui_ops)
{
	ops = ui_ops;
}

PurpleRoomlistUiOps *purple_roomlist_get_ui_ops(void)
{
	return ops;
}

/**************************************************************************
 * UI Ops GBoxed code
 **************************************************************************/

static PurpleRoomlistUiOps *
purple_roomlist_ui_ops_copy(PurpleRoomlistUiOps *ops)
{
	PurpleRoomlistUiOps *ops_new;

	g_return_val_if_fail(ops != NULL, NULL);

	ops_new = g_new(PurpleRoomlistUiOps, 1);
	*ops_new = *ops;

	return ops_new;
}

GType
purple_roomlist_ui_ops_get_type(void)
{
	static GType type = 0;

	if (type == 0) {
		type = g_boxed_type_register_static("PurpleRoomlistUiOps",
				(GBoxedCopyFunc)purple_roomlist_ui_ops_copy,
				(GBoxedFreeFunc)g_free);
	}

	return type;
}

mercurial