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
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
23 #include "glibcompat.h"
26 #include "connection.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. */
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 */
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 /**************************************************************************/
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
);
169 return purple_protocol_roomlist_iface_get_list(protocol
, gc
);
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
));
188 protocol
= purple_connection_get_protocol(gc
);
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
));
210 protocol
= purple_connection_get_protocol(gc
);
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
);
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 */
266 purple_roomlist_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
269 PurpleRoomlist
*list
= PURPLE_ROOMLIST(obj
);
270 PurpleRoomlistPrivate
*priv
=
271 purple_roomlist_get_instance_private(list
);
275 priv
->account
= g_value_get_object(value
);
278 purple_roomlist_set_fields(list
, g_value_get_pointer(value
));
280 case PROP_IN_PROGRESS
:
281 purple_roomlist_set_in_progress(list
, g_value_get_boolean(value
));
284 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
289 /* Get method for GObject properties */
291 purple_roomlist_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
294 PurpleRoomlist
*list
= PURPLE_ROOMLIST(obj
);
298 g_value_set_object(value
, purple_roomlist_get_account(list
));
301 g_value_set_pointer(value
, purple_roomlist_get_fields(list
));
303 case PROP_IN_PROGRESS
:
304 g_value_set_boolean(value
, purple_roomlist_get_in_progress(list
));
307 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
313 purple_roomlist_init(PurpleRoomlist
*list
)
317 /* Called when done constructing */
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
)
329 /* GObject finalize function */
331 purple_roomlist_finalize(GObject
*object
)
333 PurpleRoomlist
*list
= PURPLE_ROOMLIST(object
);
334 PurpleRoomlistPrivate
*priv
=
335 purple_roomlist_get_instance_private(list
);
338 purple_debug_misc("roomlist", "destroying list %p\n", list
);
340 if (ops
&& ops
->destroy
)
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_free_full(priv
->fields
, (GDestroyNotify
)purple_roomlist_field_free
);
351 G_OBJECT_CLASS(purple_roomlist_parent_class
)->finalize(object
);
354 /* Class initializer function */
356 purple_roomlist_class_init(PurpleRoomlistClass
*klass
)
358 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
360 obj_class
->finalize
= purple_roomlist_finalize
;
361 obj_class
->constructed
= purple_roomlist_constructed
;
363 /* Setup properties */
364 obj_class
->get_property
= purple_roomlist_get_property
;
365 obj_class
->set_property
= purple_roomlist_set_property
;
367 properties
[PROP_ACCOUNT
] = g_param_spec_object("account", "Account",
368 "The account for the room list.",
370 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
371 G_PARAM_STATIC_STRINGS
);
373 properties
[PROP_FIELDS
] = g_param_spec_pointer("fields", "Fields",
374 "The list of fields for a roomlist.",
375 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
377 properties
[PROP_IN_PROGRESS
] = g_param_spec_boolean("in-progress",
379 "Whether the room list is being fetched.", FALSE
,
380 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
382 g_object_class_install_properties(obj_class
, PROP_LAST
, properties
);
385 PurpleRoomlist
*purple_roomlist_new(PurpleAccount
*account
)
387 PurpleRoomlist
*list
;
388 PurpleProtocol
*protocol
;
390 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
392 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
394 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol
), NULL
);
396 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol
, FACTORY
, roomlist_new
))
397 list
= purple_protocol_factory_iface_roomlist_new(protocol
, account
);
399 list
= g_object_new(PURPLE_TYPE_ROOMLIST
,
404 g_return_val_if_fail(list
!= NULL
, NULL
);
409 /**************************************************************************
410 * Protocol Roomlist Interface API
411 **************************************************************************/
412 #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
413 PurpleProtocolRoomlistInterface *roomlist_iface = \
414 PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
415 if (roomlist_iface && roomlist_iface->funcname) \
416 roomlist_iface->funcname(__VA_ARGS__);
418 #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
419 PurpleProtocolRoomlistInterface *roomlist_iface = \
420 PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
421 if (roomlist_iface && roomlist_iface->funcname) \
422 return roomlist_iface->funcname(__VA_ARGS__); \
424 return defaultreturn;
427 purple_protocol_roomlist_iface_get_type(void)
429 static GType type
= 0;
431 if (G_UNLIKELY(type
== 0)) {
432 static const GTypeInfo info
= {
433 .class_size
= sizeof(PurpleProtocolRoomlistInterface
),
436 type
= g_type_register_static(G_TYPE_INTERFACE
,
437 "PurpleProtocolRoomlistInterface", &info
, 0);
443 purple_protocol_roomlist_iface_get_list(PurpleProtocol
*protocol
,
444 PurpleConnection
*gc
)
446 DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol
, NULL
, get_list
, gc
);
450 purple_protocol_roomlist_iface_cancel(PurpleProtocol
*protocol
,
451 PurpleRoomlist
*list
)
453 DEFINE_PROTOCOL_FUNC(protocol
, cancel
, list
);
457 purple_protocol_roomlist_iface_expand_category(PurpleProtocol
*protocol
,
458 PurpleRoomlist
*list
, PurpleRoomlistRoom
*category
)
460 DEFINE_PROTOCOL_FUNC(protocol
, expand_category
, list
, category
);
464 purple_protocol_roomlist_iface_room_serialize(PurpleProtocol
*protocol
,
465 PurpleRoomlistRoom
*room
)
467 DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol
, NULL
, room_serialize
, room
);
470 #undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
471 #undef DEFINE_PROTOCOL_FUNC
473 /**************************************************************************/
475 /**************************************************************************/
477 PurpleRoomlistRoom
*purple_roomlist_room_new(PurpleRoomlistRoomType type
, const gchar
*name
,
478 PurpleRoomlistRoom
*parent
)
480 PurpleRoomlistRoom
*room
;
482 g_return_val_if_fail(name
!= NULL
, NULL
);
484 room
= g_new0(PurpleRoomlistRoom
, 1);
486 room
->name
= g_strdup(name
);
487 room
->parent
= parent
;
492 void purple_roomlist_room_add_field(PurpleRoomlist
*list
, PurpleRoomlistRoom
*room
, gconstpointer field
)
494 PurpleRoomlistPrivate
*priv
= NULL
;
495 PurpleRoomlistField
*f
;
497 g_return_if_fail(PURPLE_IS_ROOMLIST(list
));
498 g_return_if_fail(room
!= NULL
);
500 priv
= purple_roomlist_get_instance_private(list
);
501 g_return_if_fail(priv
->fields
!= NULL
);
503 /* If this is the first call for this room, grab the first field in
504 * the Roomlist's fields. Otherwise, grab the field that is one
505 * more than the number of fields already present for the room.
506 * (This works because g_list_nth_data() is zero-indexed and
507 * g_list_length() is one-indexed.) */
509 f
= priv
->fields
->data
;
511 f
= g_list_nth_data(priv
->fields
, g_list_length(room
->fields
));
513 g_return_if_fail(f
!= NULL
);
516 case PURPLE_ROOMLIST_FIELD_STRING
:
517 room
->fields
= g_list_append(room
->fields
, g_strdup(field
));
519 case PURPLE_ROOMLIST_FIELD_BOOL
:
520 case PURPLE_ROOMLIST_FIELD_INT
:
521 room
->fields
= g_list_append(room
->fields
, GINT_TO_POINTER(field
));
525 g_object_notify_by_pspec(G_OBJECT(list
), properties
[PROP_FIELDS
]);
528 void purple_roomlist_room_join(PurpleRoomlist
*list
, PurpleRoomlistRoom
*room
)
530 PurpleRoomlistPrivate
*priv
= NULL
;
531 GHashTable
*components
;
533 PurpleConnection
*gc
;
535 g_return_if_fail(PURPLE_IS_ROOMLIST(list
));
536 g_return_if_fail(room
!= NULL
);
538 priv
= purple_roomlist_get_instance_private(list
);
540 gc
= purple_account_get_connection(priv
->account
);
544 components
= g_hash_table_new(g_str_hash
, g_str_equal
);
546 g_hash_table_replace(components
, "name", room
->name
);
547 for (l
= priv
->fields
, j
= room
->fields
; l
&& j
; l
= l
->next
, j
= j
->next
) {
548 PurpleRoomlistField
*f
= l
->data
;
550 g_hash_table_replace(components
, f
->name
, j
->data
);
553 purple_serv_join_chat(gc
, components
);
555 g_hash_table_destroy(components
);
558 PurpleRoomlistRoomType
purple_roomlist_room_get_room_type(PurpleRoomlistRoom
*room
)
563 const char * purple_roomlist_room_get_name(PurpleRoomlistRoom
*room
)
568 PurpleRoomlistRoom
* purple_roomlist_room_get_parent(PurpleRoomlistRoom
*room
)
573 gboolean
purple_roomlist_room_get_expanded_once(PurpleRoomlistRoom
*room
)
575 g_return_val_if_fail(room
!= NULL
, FALSE
);
577 return room
->expanded_once
;
580 void purple_roomlist_room_set_expanded_once(PurpleRoomlistRoom
*room
, gboolean expanded_once
)
582 g_return_if_fail(room
!= NULL
);
584 room
->expanded_once
= expanded_once
;
587 GList
*purple_roomlist_room_get_fields(PurpleRoomlistRoom
*room
)
592 static void purple_roomlist_room_destroy(PurpleRoomlist
*list
, PurpleRoomlistRoom
*r
)
594 PurpleRoomlistPrivate
*priv
=
595 purple_roomlist_get_instance_private(list
);
598 for (l
= priv
->fields
, j
= r
->fields
; l
&& j
; l
= l
->next
, j
= j
->next
) {
599 PurpleRoomlistField
*f
= l
->data
;
600 if (f
->type
== PURPLE_ROOMLIST_FIELD_STRING
)
604 g_list_free(r
->fields
);
609 /**************************************************************************/
610 /* Room GBoxed code */
611 /**************************************************************************/
613 static PurpleRoomlistRoom
*purple_roomlist_room_copy(PurpleRoomlistRoom
*r
)
615 g_return_val_if_fail(r
!= NULL
, NULL
);
617 return purple_roomlist_room_new(r
->type
, r
->name
, r
->parent
);
620 static void purple_roomlist_room_free(PurpleRoomlistRoom
*r
)
622 g_return_if_fail(r
!= NULL
);
624 g_list_free(r
->fields
);
629 GType
purple_roomlist_room_get_type(void)
631 static GType type
= 0;
634 type
= g_boxed_type_register_static("PurpleRoomlistRoom",
635 (GBoxedCopyFunc
)purple_roomlist_room_copy
,
636 (GBoxedFreeFunc
)purple_roomlist_room_free
);
642 /**************************************************************************/
644 /**************************************************************************/
646 PurpleRoomlistField
*purple_roomlist_field_new(PurpleRoomlistFieldType type
,
647 const gchar
*label
, const gchar
*name
,
650 PurpleRoomlistField
*f
;
652 g_return_val_if_fail(label
!= NULL
, NULL
);
653 g_return_val_if_fail(name
!= NULL
, NULL
);
655 f
= g_new0(PurpleRoomlistField
, 1);
658 f
->label
= g_strdup(label
);
659 f
->name
= g_strdup(name
);
665 PurpleRoomlistFieldType
purple_roomlist_field_get_field_type(PurpleRoomlistField
*field
)
670 const char * purple_roomlist_field_get_label(PurpleRoomlistField
*field
)
675 gboolean
purple_roomlist_field_get_hidden(PurpleRoomlistField
*field
)
677 return field
->hidden
;
680 /**************************************************************************/
681 /* Room Field GBoxed code */
682 /**************************************************************************/
684 static PurpleRoomlistField
*purple_roomlist_field_copy(PurpleRoomlistField
*f
)
686 g_return_val_if_fail(f
!= NULL
, NULL
);
688 return purple_roomlist_field_new(f
->type
, f
->label
, f
->name
, f
->hidden
);
691 static void purple_roomlist_field_free(PurpleRoomlistField
*f
)
693 g_return_if_fail(f
!= NULL
);
700 GType
purple_roomlist_field_get_type(void)
702 static GType type
= 0;
705 type
= g_boxed_type_register_static("PurpleRoomlistField",
706 (GBoxedCopyFunc
)purple_roomlist_field_copy
,
707 (GBoxedFreeFunc
)purple_roomlist_field_free
);
713 /**************************************************************************/
714 /* UI Registration Functions */
715 /**************************************************************************/
717 void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps
*ui_ops
)
722 PurpleRoomlistUiOps
*purple_roomlist_get_ui_ops(void)
727 /**************************************************************************
729 **************************************************************************/
731 static PurpleRoomlistUiOps
*
732 purple_roomlist_ui_ops_copy(PurpleRoomlistUiOps
*ops
)
734 PurpleRoomlistUiOps
*ops_new
;
736 g_return_val_if_fail(ops
!= NULL
, NULL
);
738 ops_new
= g_new(PurpleRoomlistUiOps
, 1);
745 purple_roomlist_ui_ops_get_type(void)
747 static GType type
= 0;
750 type
= g_boxed_type_register_static("PurpleRoomlistUiOps",
751 (GBoxedCopyFunc
)purple_roomlist_ui_ops_copy
,
752 (GBoxedFreeFunc
)g_free
);