mark PurpleImageClass as private
[pidgin-git.git] / libpurple / roomlist.c
blob3793b2a0d38d0141c61c8da107ecf99a0f61683a
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 typedef struct _PurpleRoomlistPrivate PurpleRoomlistPrivate;
34 * Private data for a room list.
36 struct _PurpleRoomlistPrivate {
37 PurpleAccount *account; /* The account this list belongs to. */
38 GList *fields; /* The fields. */
39 GList *rooms; /* The list of rooms. */
40 gboolean in_progress; /* The listing is in progress. */
42 /* TODO Remove this and use protocol-specific subclasses. */
43 gpointer proto_data; /* Protocol private data. */
47 * Represents a room.
49 struct _PurpleRoomlistRoom {
50 PurpleRoomlistRoomType type; /* The type of room. */
51 gchar *name; /* The name of the room. */
52 GList *fields; /* Other fields. */
53 PurpleRoomlistRoom *parent; /* The parent room, or NULL. */
54 gboolean expanded_once; /* A flag the UI uses to avoid multiple expand protocol cbs. */
58 * A field a room might have.
60 struct _PurpleRoomlistField {
61 PurpleRoomlistFieldType type; /* The type of field. */
62 gchar *label; /* The i18n user displayed name of the field. */
63 gchar *name; /* The internal name of the field. */
64 gboolean hidden; /* Hidden? */
67 /* Room list property enums */
68 enum
70 PROP_0,
71 PROP_ACCOUNT,
72 PROP_FIELDS,
73 PROP_IN_PROGRESS,
74 PROP_LAST
77 static GParamSpec *properties[PROP_LAST];
78 static PurpleRoomlistUiOps *ops = NULL;
80 G_DEFINE_TYPE_WITH_PRIVATE(PurpleRoomlist, purple_roomlist, G_TYPE_OBJECT);
82 static void purple_roomlist_field_free(PurpleRoomlistField *f);
83 static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r);
85 /**************************************************************************/
86 /* Room List API */
87 /**************************************************************************/
89 void purple_roomlist_show_with_account(PurpleAccount *account)
91 if (ops && ops->show_with_account)
92 ops->show_with_account(account);
95 PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list)
97 PurpleRoomlistPrivate *priv = NULL;
99 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
101 priv = purple_roomlist_get_instance_private(list);
102 return priv->account;
105 void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
107 PurpleRoomlistPrivate *priv = NULL;
109 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
111 priv = purple_roomlist_get_instance_private(list);
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 = NULL;
124 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
126 priv = purple_roomlist_get_instance_private(list);
127 priv->in_progress = in_progress;
129 if (ops && ops->in_progress)
130 ops->in_progress(list, in_progress);
132 g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_IN_PROGRESS]);
135 gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list)
137 PurpleRoomlistPrivate *priv = NULL;
139 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), FALSE);
141 priv = purple_roomlist_get_instance_private(list);
142 return priv->in_progress;
145 void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room)
147 PurpleRoomlistPrivate *priv = NULL;
149 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
150 g_return_if_fail(room != NULL);
152 priv = purple_roomlist_get_instance_private(list);
153 priv->rooms = g_list_append(priv->rooms, room);
155 if (ops && ops->add_room)
156 ops->add_room(list, room);
159 PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc)
161 PurpleProtocol *protocol = NULL;
163 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
164 g_return_val_if_fail(PURPLE_CONNECTION_IS_CONNECTED(gc), NULL);
166 protocol = purple_connection_get_protocol(gc);
168 if(protocol)
169 return purple_protocol_roomlist_iface_get_list(protocol, gc);
171 return NULL;
174 void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
176 PurpleRoomlistPrivate *priv = NULL;
177 PurpleProtocol *protocol = NULL;
178 PurpleConnection *gc;
180 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
182 priv = purple_roomlist_get_instance_private(list);
184 gc = purple_account_get_connection(priv->account);
185 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
187 if(gc)
188 protocol = purple_connection_get_protocol(gc);
190 if(protocol)
191 purple_protocol_roomlist_iface_cancel(protocol, list);
194 void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
196 PurpleRoomlistPrivate *priv = NULL;
197 PurpleProtocol *protocol = NULL;
198 PurpleConnection *gc;
200 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
201 g_return_if_fail(category != NULL);
202 g_return_if_fail(category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
204 priv = purple_roomlist_get_instance_private(list);
206 gc = purple_account_get_connection(priv->account);
207 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
209 if(gc)
210 protocol = purple_connection_get_protocol(gc);
212 if(protocol)
213 purple_protocol_roomlist_iface_expand_category(protocol, list, category);
216 GList * purple_roomlist_get_fields(PurpleRoomlist *list)
218 PurpleRoomlistPrivate *priv = NULL;
220 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
222 priv = purple_roomlist_get_instance_private(list);
223 return priv->fields;
226 gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
228 PurpleRoomlistPrivate *priv = NULL;
230 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
232 priv = purple_roomlist_get_instance_private(list);
233 return priv->proto_data;
236 void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
238 PurpleRoomlistPrivate *priv = NULL;
240 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
242 priv = purple_roomlist_get_instance_private(list);
243 priv->proto_data = proto_data;
246 gpointer purple_roomlist_get_ui_data(PurpleRoomlist *list)
248 g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
250 return list->ui_data;
253 void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data)
255 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
257 list->ui_data = ui_data;
260 /**************************************************************************/
261 /* Room List GObject code */
262 /**************************************************************************/
264 /* Set method for GObject properties */
265 static void
266 purple_roomlist_set_property(GObject *obj, guint param_id, const GValue *value,
267 GParamSpec *pspec)
269 PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
270 PurpleRoomlistPrivate *priv =
271 purple_roomlist_get_instance_private(list);
273 switch (param_id) {
274 case PROP_ACCOUNT:
275 priv->account = g_value_get_object(value);
276 break;
277 case PROP_FIELDS:
278 purple_roomlist_set_fields(list, g_value_get_pointer(value));
279 break;
280 case PROP_IN_PROGRESS:
281 purple_roomlist_set_in_progress(list, g_value_get_boolean(value));
282 break;
283 default:
284 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
285 break;
289 /* Get method for GObject properties */
290 static void
291 purple_roomlist_get_property(GObject *obj, guint param_id, GValue *value,
292 GParamSpec *pspec)
294 PurpleRoomlist *list = PURPLE_ROOMLIST(obj);
296 switch (param_id) {
297 case PROP_ACCOUNT:
298 g_value_set_object(value, purple_roomlist_get_account(list));
299 break;
300 case PROP_FIELDS:
301 g_value_set_pointer(value, purple_roomlist_get_fields(list));
302 break;
303 case PROP_IN_PROGRESS:
304 g_value_set_boolean(value, purple_roomlist_get_in_progress(list));
305 break;
306 default:
307 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
308 break;
312 static void
313 purple_roomlist_init(PurpleRoomlist *list)
317 /* Called when done constructing */
318 static void
319 purple_roomlist_constructed(GObject *object)
321 PurpleRoomlist *list = PURPLE_ROOMLIST(object);
323 G_OBJECT_CLASS(purple_roomlist_parent_class)->constructed(object);
325 if (ops && ops->create)
326 ops->create(list);
329 /* GObject finalize function */
330 static void
331 purple_roomlist_finalize(GObject *object)
333 PurpleRoomlist *list = PURPLE_ROOMLIST(object);
334 PurpleRoomlistPrivate *priv =
335 purple_roomlist_get_instance_private(list);
336 GList *l;
338 purple_debug_misc("roomlist", "destroying list %p\n", list);
340 if (ops && ops->destroy)
341 ops->destroy(list);
343 for (l = priv->rooms; l; l = l->next) {
344 PurpleRoomlistRoom *r = l->data;
345 purple_roomlist_room_destroy(list, r);
347 g_list_free(priv->rooms);
349 g_list_foreach(priv->fields, (GFunc)purple_roomlist_field_free, NULL);
350 g_list_free(priv->fields);
352 G_OBJECT_CLASS(purple_roomlist_parent_class)->finalize(object);
355 /* Class initializer function */
356 static void
357 purple_roomlist_class_init(PurpleRoomlistClass *klass)
359 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
361 obj_class->finalize = purple_roomlist_finalize;
362 obj_class->constructed = purple_roomlist_constructed;
364 /* Setup properties */
365 obj_class->get_property = purple_roomlist_get_property;
366 obj_class->set_property = purple_roomlist_set_property;
368 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
369 "The account for the room list.",
370 PURPLE_TYPE_ACCOUNT,
371 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
372 G_PARAM_STATIC_STRINGS);
374 properties[PROP_FIELDS] = g_param_spec_pointer("fields", "Fields",
375 "The list of fields for a roomlist.",
376 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
378 properties[PROP_IN_PROGRESS] = g_param_spec_boolean("in-progress",
379 "In progress",
380 "Whether the room list is being fetched.", FALSE,
381 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
383 g_object_class_install_properties(obj_class, PROP_LAST, properties);
386 PurpleRoomlist *purple_roomlist_new(PurpleAccount *account)
388 PurpleRoomlist *list;
389 PurpleProtocol *protocol;
391 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
393 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
395 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
397 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, roomlist_new))
398 list = purple_protocol_factory_iface_roomlist_new(protocol, account);
399 else
400 list = g_object_new(PURPLE_TYPE_ROOMLIST,
401 "account", account,
402 NULL
405 g_return_val_if_fail(list != NULL, NULL);
407 return list;
410 /**************************************************************************
411 * Protocol Roomlist Interface API
412 **************************************************************************/
413 #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
414 PurpleProtocolRoomlistInterface *roomlist_iface = \
415 PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
416 if (roomlist_iface && roomlist_iface->funcname) \
417 roomlist_iface->funcname(__VA_ARGS__);
419 #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
420 PurpleProtocolRoomlistInterface *roomlist_iface = \
421 PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
422 if (roomlist_iface && roomlist_iface->funcname) \
423 return roomlist_iface->funcname(__VA_ARGS__); \
424 else \
425 return defaultreturn;
427 GType
428 purple_protocol_roomlist_iface_get_type(void)
430 static GType type = 0;
432 if (G_UNLIKELY(type == 0)) {
433 static const GTypeInfo info = {
434 .class_size = sizeof(PurpleProtocolRoomlistInterface),
437 type = g_type_register_static(G_TYPE_INTERFACE,
438 "PurpleProtocolRoomlistInterface", &info, 0);
440 return type;
443 PurpleRoomlist *
444 purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
445 PurpleConnection *gc)
447 DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_list, gc);
450 void
451 purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
452 PurpleRoomlist *list)
454 DEFINE_PROTOCOL_FUNC(protocol, cancel, list);
457 void
458 purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
459 PurpleRoomlist *list, PurpleRoomlistRoom *category)
461 DEFINE_PROTOCOL_FUNC(protocol, expand_category, list, category);
464 char *
465 purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
466 PurpleRoomlistRoom *room)
468 DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, room_serialize, room);
471 #undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
472 #undef DEFINE_PROTOCOL_FUNC
474 /**************************************************************************/
475 /* Room API */
476 /**************************************************************************/
478 PurpleRoomlistRoom *purple_roomlist_room_new(PurpleRoomlistRoomType type, const gchar *name,
479 PurpleRoomlistRoom *parent)
481 PurpleRoomlistRoom *room;
483 g_return_val_if_fail(name != NULL, NULL);
485 room = g_new0(PurpleRoomlistRoom, 1);
486 room->type = type;
487 room->name = g_strdup(name);
488 room->parent = parent;
490 return room;
493 void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field)
495 PurpleRoomlistPrivate *priv = NULL;
496 PurpleRoomlistField *f;
498 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
499 g_return_if_fail(room != NULL);
501 priv = purple_roomlist_get_instance_private(list);
502 g_return_if_fail(priv->fields != NULL);
504 /* If this is the first call for this room, grab the first field in
505 * the Roomlist's fields. Otherwise, grab the field that is one
506 * more than the number of fields already present for the room.
507 * (This works because g_list_nth_data() is zero-indexed and
508 * g_list_length() is one-indexed.) */
509 if (!room->fields)
510 f = priv->fields->data;
511 else
512 f = g_list_nth_data(priv->fields, g_list_length(room->fields));
514 g_return_if_fail(f != NULL);
516 switch(f->type) {
517 case PURPLE_ROOMLIST_FIELD_STRING:
518 room->fields = g_list_append(room->fields, g_strdup(field));
519 break;
520 case PURPLE_ROOMLIST_FIELD_BOOL:
521 case PURPLE_ROOMLIST_FIELD_INT:
522 room->fields = g_list_append(room->fields, GINT_TO_POINTER(field));
523 break;
526 g_object_notify_by_pspec(G_OBJECT(list), properties[PROP_FIELDS]);
529 void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room)
531 PurpleRoomlistPrivate *priv = NULL;
532 GHashTable *components;
533 GList *l, *j;
534 PurpleConnection *gc;
536 g_return_if_fail(PURPLE_IS_ROOMLIST(list));
537 g_return_if_fail(room != NULL);
539 priv = purple_roomlist_get_instance_private(list);
541 gc = purple_account_get_connection(priv->account);
542 if (!gc)
543 return;
545 components = g_hash_table_new(g_str_hash, g_str_equal);
547 g_hash_table_replace(components, "name", room->name);
548 for (l = priv->fields, j = room->fields; l && j; l = l->next, j = j->next) {
549 PurpleRoomlistField *f = l->data;
551 g_hash_table_replace(components, f->name, j->data);
554 purple_serv_join_chat(gc, components);
556 g_hash_table_destroy(components);
559 PurpleRoomlistRoomType purple_roomlist_room_get_room_type(PurpleRoomlistRoom *room)
561 return room->type;
564 const char * purple_roomlist_room_get_name(PurpleRoomlistRoom *room)
566 return room->name;
569 PurpleRoomlistRoom * purple_roomlist_room_get_parent(PurpleRoomlistRoom *room)
571 return room->parent;
574 gboolean purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom *room)
576 g_return_val_if_fail(room != NULL, FALSE);
578 return room->expanded_once;
581 void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom *room, gboolean expanded_once)
583 g_return_if_fail(room != NULL);
585 room->expanded_once = expanded_once;
588 GList *purple_roomlist_room_get_fields(PurpleRoomlistRoom *room)
590 return room->fields;
593 static void purple_roomlist_room_destroy(PurpleRoomlist *list, PurpleRoomlistRoom *r)
595 PurpleRoomlistPrivate *priv =
596 purple_roomlist_get_instance_private(list);
597 GList *l, *j;
599 for (l = priv->fields, j = r->fields; l && j; l = l->next, j = j->next) {
600 PurpleRoomlistField *f = l->data;
601 if (f->type == PURPLE_ROOMLIST_FIELD_STRING)
602 g_free(j->data);
605 g_list_free(r->fields);
606 g_free(r->name);
607 g_free(r);
610 /**************************************************************************/
611 /* Room GBoxed code */
612 /**************************************************************************/
614 static PurpleRoomlistRoom *purple_roomlist_room_copy(PurpleRoomlistRoom *r)
616 g_return_val_if_fail(r != NULL, NULL);
618 return purple_roomlist_room_new(r->type, r->name, r->parent);
621 static void purple_roomlist_room_free(PurpleRoomlistRoom *r)
623 g_return_if_fail(r != NULL);
625 g_list_free(r->fields);
626 g_free(r->name);
627 g_free(r);
630 GType purple_roomlist_room_get_type(void)
632 static GType type = 0;
634 if (type == 0) {
635 type = g_boxed_type_register_static("PurpleRoomlistRoom",
636 (GBoxedCopyFunc)purple_roomlist_room_copy,
637 (GBoxedFreeFunc)purple_roomlist_room_free);
640 return type;
643 /**************************************************************************/
644 /* Room Field API */
645 /**************************************************************************/
647 PurpleRoomlistField *purple_roomlist_field_new(PurpleRoomlistFieldType type,
648 const gchar *label, const gchar *name,
649 gboolean hidden)
651 PurpleRoomlistField *f;
653 g_return_val_if_fail(label != NULL, NULL);
654 g_return_val_if_fail(name != NULL, NULL);
656 f = g_new0(PurpleRoomlistField, 1);
658 f->type = type;
659 f->label = g_strdup(label);
660 f->name = g_strdup(name);
661 f->hidden = hidden;
663 return f;
666 PurpleRoomlistFieldType purple_roomlist_field_get_field_type(PurpleRoomlistField *field)
668 return field->type;
671 const char * purple_roomlist_field_get_label(PurpleRoomlistField *field)
673 return field->label;
676 gboolean purple_roomlist_field_get_hidden(PurpleRoomlistField *field)
678 return field->hidden;
681 /**************************************************************************/
682 /* Room Field GBoxed code */
683 /**************************************************************************/
685 static PurpleRoomlistField *purple_roomlist_field_copy(PurpleRoomlistField *f)
687 g_return_val_if_fail(f != NULL, NULL);
689 return purple_roomlist_field_new(f->type, f->label, f->name, f->hidden);
692 static void purple_roomlist_field_free(PurpleRoomlistField *f)
694 g_return_if_fail(f != NULL);
696 g_free(f->label);
697 g_free(f->name);
698 g_free(f);
701 GType purple_roomlist_field_get_type(void)
703 static GType type = 0;
705 if (type == 0) {
706 type = g_boxed_type_register_static("PurpleRoomlistField",
707 (GBoxedCopyFunc)purple_roomlist_field_copy,
708 (GBoxedFreeFunc)purple_roomlist_field_free);
711 return type;
714 /**************************************************************************/
715 /* UI Registration Functions */
716 /**************************************************************************/
718 void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps *ui_ops)
720 ops = ui_ops;
723 PurpleRoomlistUiOps *purple_roomlist_get_ui_ops(void)
725 return ops;
728 /**************************************************************************
729 * UI Ops GBoxed code
730 **************************************************************************/
732 static PurpleRoomlistUiOps *
733 purple_roomlist_ui_ops_copy(PurpleRoomlistUiOps *ops)
735 PurpleRoomlistUiOps *ops_new;
737 g_return_val_if_fail(ops != NULL, NULL);
739 ops_new = g_new(PurpleRoomlistUiOps, 1);
740 *ops_new = *ops;
742 return ops_new;
745 GType
746 purple_roomlist_ui_ops_get_type(void)
748 static GType type = 0;
750 if (type == 0) {
751 type = g_boxed_type_register_static("PurpleRoomlistUiOps",
752 (GBoxedCopyFunc)purple_roomlist_ui_ops_copy,
753 (GBoxedFreeFunc)g_free);
756 return type;