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_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 */
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.",
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",
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
);
400 list
= g_object_new(PURPLE_TYPE_ROOMLIST
,
405 g_return_val_if_fail(list
!= NULL
, NULL
);
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__); \
425 return defaultreturn;
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);
444 purple_protocol_roomlist_iface_get_list(PurpleProtocol
*protocol
,
445 PurpleConnection
*gc
)
447 DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol
, NULL
, get_list
, gc
);
451 purple_protocol_roomlist_iface_cancel(PurpleProtocol
*protocol
,
452 PurpleRoomlist
*list
)
454 DEFINE_PROTOCOL_FUNC(protocol
, cancel
, list
);
458 purple_protocol_roomlist_iface_expand_category(PurpleProtocol
*protocol
,
459 PurpleRoomlist
*list
, PurpleRoomlistRoom
*category
)
461 DEFINE_PROTOCOL_FUNC(protocol
, expand_category
, list
, category
);
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 /**************************************************************************/
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);
487 room
->name
= g_strdup(name
);
488 room
->parent
= parent
;
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.) */
510 f
= priv
->fields
->data
;
512 f
= g_list_nth_data(priv
->fields
, g_list_length(room
->fields
));
514 g_return_if_fail(f
!= NULL
);
517 case PURPLE_ROOMLIST_FIELD_STRING
:
518 room
->fields
= g_list_append(room
->fields
, g_strdup(field
));
520 case PURPLE_ROOMLIST_FIELD_BOOL
:
521 case PURPLE_ROOMLIST_FIELD_INT
:
522 room
->fields
= g_list_append(room
->fields
, GINT_TO_POINTER(field
));
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
;
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
);
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
)
564 const char * purple_roomlist_room_get_name(PurpleRoomlistRoom
*room
)
569 PurpleRoomlistRoom
* purple_roomlist_room_get_parent(PurpleRoomlistRoom
*room
)
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
)
593 static void purple_roomlist_room_destroy(PurpleRoomlist
*list
, PurpleRoomlistRoom
*r
)
595 PurpleRoomlistPrivate
*priv
=
596 purple_roomlist_get_instance_private(list
);
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
)
605 g_list_free(r
->fields
);
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
);
630 GType
purple_roomlist_room_get_type(void)
632 static GType type
= 0;
635 type
= g_boxed_type_register_static("PurpleRoomlistRoom",
636 (GBoxedCopyFunc
)purple_roomlist_room_copy
,
637 (GBoxedFreeFunc
)purple_roomlist_room_free
);
643 /**************************************************************************/
645 /**************************************************************************/
647 PurpleRoomlistField
*purple_roomlist_field_new(PurpleRoomlistFieldType type
,
648 const gchar
*label
, const gchar
*name
,
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);
659 f
->label
= g_strdup(label
);
660 f
->name
= g_strdup(name
);
666 PurpleRoomlistFieldType
purple_roomlist_field_get_field_type(PurpleRoomlistField
*field
)
671 const char * purple_roomlist_field_get_label(PurpleRoomlistField
*field
)
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
);
701 GType
purple_roomlist_field_get_type(void)
703 static GType type
= 0;
706 type
= g_boxed_type_register_static("PurpleRoomlistField",
707 (GBoxedCopyFunc
)purple_roomlist_field_copy
,
708 (GBoxedFreeFunc
)purple_roomlist_field_free
);
714 /**************************************************************************/
715 /* UI Registration Functions */
716 /**************************************************************************/
718 void purple_roomlist_set_ui_ops(PurpleRoomlistUiOps
*ui_ops
)
723 PurpleRoomlistUiOps
*purple_roomlist_get_ui_ops(void)
728 /**************************************************************************
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);
746 purple_roomlist_ui_ops_get_type(void)
748 static GType type
= 0;
751 type
= g_boxed_type_register_static("PurpleRoomlistUiOps",
752 (GBoxedCopyFunc
)purple_roomlist_ui_ops_copy
,
753 (GBoxedFreeFunc
)g_free
);