Migrate certificates, icons, logs to XDG dirs
[pidgin-git.git] / libpurple / roomlist.c
blobe1318f55f12f27790740fbc95f661b3fe49693e6
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
25 #include "account.h"
26 #include "connection.h"
27 #include "debug.h"
28 #include "roomlist.h"
29 #include "server.h"
31 #define PURPLE_ROOMLIST_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_ROOMLIST, PurpleRoomlistPrivate))
34 typedef struct _PurpleRoomlistPrivate PurpleRoomlistPrivate;
37 * Private data for a room list.
39 struct _PurpleRoomlistPrivate {
40 PurpleAccount *account; /* The account this list belongs to. */
41 GList *fields; /* The fields. */
42 GList *rooms; /* The list of rooms. */
43 gboolean in_progress; /* The listing is in progress. */
45 /* TODO Remove this and use protocol-specific subclasses. */
46 gpointer proto_data; /* Protocol private data. */
50 * Represents a room.
52 struct _PurpleRoomlistRoom {
53 PurpleRoomlistRoomType type; /* The type of room. */
54 gchar *name; /* The name of the room. */
55 GList *fields; /* Other fields. */
56 PurpleRoomlistRoom *parent; /* The parent room, or NULL. */
57 gboolean expanded_once; /* A flag the UI uses to avoid multiple expand protocol cbs. */
61 * A field a room might have.
63 struct _PurpleRoomlistField {
64 PurpleRoomlistFieldType type; /* The type of field. */
65 gchar *label; /* The i18n user displayed name of the field. */
66 gchar *name; /* The internal name of the field. */
67 gboolean hidden; /* Hidden? */
70 /* Room list property enums */
71 enum
73 PROP_0,
74 PROP_ACCOUNT,
75 PROP_FIELDS,
76 PROP_IN_PROGRESS,
77 PROP_LAST
80 static GObjectClass *parent_class;
81 static GParamSpec *properties[PROP_LAST];
82 static PurpleRoomlistUiOps *ops = NULL;
84 static void purple_roomlist_field_free(PurpleRoomlistField *f);
85 static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r);
87 /**************************************************************************/
88 /* Room List API */
89 /**************************************************************************/
91 void purple_roomlist_show_with_account(PurpleAccount *account)
93 if (ops && ops->show_with_account)
94 ops->show_with_account(account);
97 PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list)
99 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
101 g_return_val_if_fail(priv != NULL, NULL);
103 return priv->account;
106 void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
108 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
110 g_return_if_fail(priv != NULL);
112 priv->fields = fields;
114 if (ops && ops->set_fields)
115 ops->set_fields(list, fields);
117 g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
120 void purple_roomlist_set_in_progress(PurpleRoomlist *list, gboolean in_progress)
122 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
124 g_return_if_fail(priv != NULL);
126 priv->in_progress = in_progress;
128 if (ops && ops->in_progress)
129 ops->in_progress(list, in_progress);
131 g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_IN_PROGRESS]);
134 gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list)
136 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
138 g_return_val_if_fail(priv != NULL, FALSE);
140 return priv->in_progress;
143 void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room)
145 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
147 g_return_if_fail(priv != NULL);
148 g_return_if_fail(room != NULL);
150 priv->rooms = g_list_append(priv->rooms, room);
152 if (ops && ops->add_room)
153 ops->add_room(list, room);
156 PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc)
158 PurpleProtocol *protocol = NULL;
160 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
161 g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL);
163 protocol = purple_connection_get_protocol(gc);
165 if(protocol)
166 return purple_protocol_roomlist_iface_get_list(protocol, gc);
168 return NULL;
171 void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
173 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
174 PurpleProtocol *protocol = NULL;
175 PurpleConnection *gc;
177 g_return_if_fail(priv != NULL);
179 gc = purple_account_get_connection(priv->account);
181 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
183 if(gc)
184 protocol = purple_connection_get_protocol(gc);
186 if(protocol)
187 purple_protocol_roomlist_iface_cancel(protocol, list);
190 void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
192 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
193 PurpleProtocol *protocol = NULL;
194 PurpleConnection *gc;
196 g_return_if_fail(priv != NULL);
197 g_return_if_fail(category != NULL);
198 g_return_if_fail(category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
200 gc = purple_account_get_connection(priv->account);
201 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
203 if(gc)
204 protocol = purple_connection_get_protocol(gc);
206 if(protocol)
207 purple_protocol_roomlist_iface_expand_category(protocol, list, category);
210 GList * purple_roomlist_get_fields(PurpleRoomlist *list)
212 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
214 g_return_val_if_fail(priv != NULL, NULL);
216 return priv->fields;
219 gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
221 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
223 g_return_val_if_fail(priv != NULL, NULL);
225 return priv->proto_data;
228 void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
230 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
232 g_return_if_fail(priv != NULL);
234 priv->proto_data = proto_data;
237 gpointer purple_roomlist_get_ui_data(PurpleRoomlist *list)
239 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
241 return list->ui_data;
244 void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data)
246 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
248 list->ui_data = ui_data;
251 /**************************************************************************/
252 /* Room List GObject code */
253 /**************************************************************************/
255 /* Set method for GObject properties */
256 static void
257 purple_roomlist_set_property(GObject *obj, guint param_id, const GValue *value,
258 GParamSpec *pspec)
260 PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
261 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
263 switch (param_id) {
264 case PROP_ACCOUNT:
265 priv->account = g_value_get_object(value);
266 break;
267 case PROP_FIELDS:
268 purple_roomlist_set_fields(list, g_value_get_pointer(value));
269 break;
270 case PROP_IN_PROGRESS:
271 purple_roomlist_set_in_progress(list, g_value_get_boolean(value));
272 break;
273 default:
274 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
275 break;
279 /* Get method for GObject properties */
280 static void
281 purple_roomlist_get_property(GObject *obj, guint param_id, GValue *value,
282 GParamSpec *pspec)
284 PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
286 switch (param_id) {
287 case PROP_ACCOUNT:
288 g_value_set_object(value, purple_roomlist_get_account(list));
289 break;
290 case PROP_FIELDS:
291 g_value_set_pointer(value, purple_roomlist_get_fields(list));
292 break;
293 case PROP_IN_PROGRESS:
294 g_value_set_boolean(value, purple_roomlist_get_in_progress(list));
295 break;
296 default:
297 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
298 break;
302 /* Called when done constructing */
303 static void
304 purple_roomlist_constructed(GObject *object)
306 PurpleRoomlist *list = PURPLE_ROOMLIST(object);
308 parent_class->constructed(object);
310 if (ops && ops->create)
311 ops->create(list);
314 /* GObject finalize function */
315 static void
316 purple_roomlist_finalize(GObject *object)
318 PurpleRoomlist *list = PURPLE_ROOMLIST(object);
319 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
320 GList *l;
322 purple_debug_misc("roomlist", "destroying list %p\n", list);
324 if (ops && ops->destroy)
325 ops->destroy(list);
327 for (l = priv->rooms; l; l = l->next) {
328 PurpleRoomlistRoom *r = l->data;
329 purple_roomlist_room_destroy(list, r);
331 g_list_free(priv->rooms);
333 g_list_foreach(priv->fields, (GFunc)purple_roomlist_field_free, NULL);
334 g_list_free(priv->fields);
336 parent_class->finalize(object);
339 /* Class initializer function */
340 static void
341 purple_roomlist_class_init(PurpleRoomlistClass *klass)
343 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
345 parent_class = g_type_class_peek_parent(klass);
347 obj_class->finalize = purple_roomlist_finalize;
348 obj_class->constructed = purple_roomlist_constructed;
350 /* Setup properties */
351 obj_class->get_property = purple_roomlist_get_property;
352 obj_class->set_property = purple_roomlist_set_property;
354 g_type_class_add_private(klass, sizeof(PurpleRoomlistPrivate));
356 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
357 "The account for the room list.",
358 PURPLE_TYPE_ACCOUNT,
359 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
360 G_PARAM_STATIC_STRINGS);
362 properties[PROP_FIELDS] = g_param_spec_pointer("fields", "Fields",
363 "The list of fields for a roomlist.",
364 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
366 properties[PROP_IN_PROGRESS] = g_param_spec_boolean("in-progress",
367 "In progress",
368 "Whether the room list is being fetched.", FALSE,
369 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
371 g_object_class_install_properties(obj_class, PROP_LAST, properties);
374 GType
375 purple_roomlist_get_type(void)
377 static GType type = 0;
379 if(type == 0) {
380 static const GTypeInfo info = {
381 sizeof(PurpleRoomlistClass),
382 NULL,
383 NULL,
384 (GClassInitFunc)purple_roomlist_class_init,
385 NULL,
386 NULL,
387 sizeof(PurpleRoomlist),
389 NULL,
390 NULL,
393 type = g_type_register_static(G_TYPE_OBJECT, "PurpleRoomlist",
394 &info, 0);
397 return type;
400 PurpleRoomlist *purple_roomlist_new(PurpleAccount *account)
402 PurpleRoomlist *list;
403 PurpleProtocol *protocol;
405 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
407 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
409 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
411 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, roomlist_new))
412 list = purple_protocol_factory_iface_roomlist_new(protocol, account);
413 else
414 list = g_object_new(PURPLE_TYPE_ROOMLIST,
415 "account", account,
416 NULL
419 g_return_val_if_fail(list != NULL, NULL);
421 return list;
424 /**************************************************************************/
425 /* Room API */
426 /**************************************************************************/
428 PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const gchar *name,
429 PurpleRoomlistRoom *parent)
431 PurpleRoomlistRoom *room;
433 g_return_val_if_fail(name != NULL, NULL);
435 room = g_new0(PurpleRoomlistRoom, 1);
436 room->type = type;
437 room->name = g_strdup(name);
438 room->parent = parent;
440 return room;
443 void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field)
445 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
446 PurpleRoomlistField *f;
448 g_return_if_fail(priv != NULL);
449 g_return_if_fail(room != NULL);
450 g_return_if_fail(priv->fields != NULL);
452 /* If this is the first call for this room, grab the first field in
453 * the Roomlist's fields. Otherwise, grab the field that is one
454 * more than the number of fields already present for the room.
455 * (This works because g_list_nth_data() is zero-indexed and
456 * g_list_length() is one-indexed.) */
457 if (!room->fields)
458 f = priv->fields->data;
459 else
460 f = g_list_nth_data(priv->fields, g_list_length(room->fields));
462 g_return_if_fail(f != NULL);
464 switch(f->type) {
465 case PURPLE_ROOMLIST_FIELD_STRING:
466 room->fields = g_list_append(room->fields, g_strdup(field));
467 break;
468 case PURPLE_ROOMLIST_FIELD_BOOL:
469 case PURPLE_ROOMLIST_FIELD_INT:
470 room->fields = g_list_append(room->fields, GINT_TO_POINTER(field));
471 break;
474 g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
477 void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room)
479 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
480 GHashTable *components;
481 GList *l, *j;
482 PurpleConnection *gc;
484 g_return_if_fail(priv != NULL);
485 g_return_if_fail(room != NULL);
487 gc = purple_account_get_connection(priv->account);
488 if (!gc)
489 return;
491 components = g_hash_table_new(g_str_hash, g_str_equal);
493 g_hash_table_replace(components, "name", room->name);
494 for (l = priv->fields, j = room->fields; l && j; l = l->next, j = j->next) {
495 PurpleRoomlistField *f = l->data;
497 g_hash_table_replace(components, f->name, j->data);
500 purple_serv_join_chat(gc, components);
502 g_hash_table_destroy(components);
505 PurpleRoomlistRoomType purple_roomlist_room_get_room_type(PurpleRoomlistRoom *room)
507 return room->type;
510 const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room)
512 return room->name;
515 PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room)
517 return room->parent;
520 gboolean purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom *room)
522 g_return_val_if_fail(room != NULL, FALSE);
524 return room->expanded_once;
527 void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom *room, gboolean expanded_once)
529 g_return_if_fail(room != NULL);
531 room->expanded_once = expanded_once;
534 GList *purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
536 return room->fields;
539 static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r)
541 PurpleRoomlistPrivate *priv = PURPLE_ROOMLIST_GET_PRIVATE(list);
542 GList *l, *j;
544 for (l = priv->fields, j = r->fields; l && j; l = l->next, j = j->next) {
545 PurpleRoomlistField *f = l->data;
546 if (f->type == PURPLE_ROOMLIST_FIELD_STRING)
547 g_free(j->data);
550 g_list_free(r->fields);
551 g_free(r->name);
552 g_free(r);
555 /**************************************************************************/
556 /* Room GBoxed code */
557 /**************************************************************************/
559 static PurpleRoomlistRoom *purple_roomlist_room_copy(PurpleRoomlistRoom *r)
561 g_return_val_if_fail(r != NULL, NULL);
563 return purple_roomlist_room_new(r->type, r->name, r->parent);
566 static void purple_roomlist_room_free(PurpleRoomlistRoom *r)
568 g_return_if_fail(r != NULL);
570 g_list_free(r->fields);
571 g_free(r->name);
572 g_free(r);
575 GType purple_roomlist_room_get_type(void)
577 static GType type = 0;
579 if (type == 0) {
580 type = g_boxed_type_register_static("PurpleRoomlistRoom",
581 (GBoxedCopyFunc)purple_roomlist_room_copy,
582 (GBoxedFreeFunc)purple_roomlist_room_free);
585 return type;
588 /**************************************************************************/
589 /* Room Field API */
590 /**************************************************************************/
592 PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
593 const gchar *label, const gchar *name,
594 gboolean hidden)
596 PurpleRoomlistField *f;
598 g_return_val_if_fail(label != NULL, NULL);
599 g_return_val_if_fail(name != NULL, NULL);
601 f = g_new0(PurpleRoomlistField, 1);
603 f->type = type;
604 f->label = g_strdup(label);
605 f->name = g_strdup(name);
606 f->hidden = hidden;
608 return f;
611 PurpleRoomlistFieldType purple_roomlist_field_get_field_type(PurpleRoomlistField *field)
613 return field->type;
616 const char * purple_roomlist_field_get_label(PurpleRoomlistField *field)
618 return field->label;
621 gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field)
623 return field->hidden;
626 /**************************************************************************/
627 /* Room Field GBoxed code */
628 /**************************************************************************/
630 static PurpleRoomlistField *purple_roomlist_field_copy(PurpleRoomlistField *f)
632 g_return_val_if_fail(f != NULL, NULL);
634 return purple_roomlist_field_new(f->type, f->label, f->name, f->hidden);
637 static void purple_roomlist_field_free(PurpleRoomlistField *f)
639 g_return_if_fail(f != NULL);
641 g_free(f->label);
642 g_free(f->name);
643 g_free(f);
646 GType purple_roomlist_field_get_type(void)
648 static GType type = 0;
650 if (type == 0) {
651 type = g_boxed_type_register_static("PurpleRoomlistField",
652 (GBoxedCopyFunc)purple_roomlist_field_copy,
653 (GBoxedFreeFunc)purple_roomlist_field_free);
656 return type;
659 /**************************************************************************/
660 /* UI Registration Functions */
661 /**************************************************************************/
663 void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps *ui_ops)
665 ops = ui_ops;
668 PurpleRoomlistUiOps *purple_roomlist_get_ui_ops(void)
670 return ops;
673 /**************************************************************************
674 * UI Ops GBoxed code
675 **************************************************************************/
677 static PurpleRoomlistUiOps *
678 purple_roomlist_ui_ops_copy(PurpleRoomlistUiOps *ops)
680 PurpleRoomlistUiOps *ops_new;
682 g_return_val_if_fail(ops != NULL, NULL);
684 ops_new = g_new(PurpleRoomlistUiOps, 1);
685 *ops_new = *ops;
687 return ops_new;
690 GType
691 purple_roomlist_ui_ops_get_type(void)
693 static GType type = 0;
695 if (type == 0) {
696 type = g_boxed_type_register_static("PurpleRoomlistUiOps",
697 (GBoxedCopyFunc)purple_roomlist_ui_ops_copy,
698 (GBoxedFreeFunc)g_free);
701 return type;