]> git.0d.be Git - empathy.git/blob - libempathy/empathy-tp-roomlist.c
Introduce empathy_contact_equal, adapt themes
[empathy.git] / libempathy / empathy-tp-roomlist.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <telepathy-glib/channel.h>
27 #include <telepathy-glib/dbus.h>
28 #include <telepathy-glib/util.h>
29
30 #include <libmissioncontrol/mission-control.h>
31
32 #include "empathy-tp-roomlist.h"
33 #include "empathy-chatroom.h"
34 #include "empathy-utils.h"
35
36 #define DEBUG_FLAG EMPATHY_DEBUG_TP
37 #include "empathy-debug.h"
38
39 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTpRoomlist)
40 typedef struct {
41         TpConnection *connection;
42         TpChannel    *channel;
43         McAccount    *account;
44         gboolean      is_listing;
45         gboolean      start_requested;
46 } EmpathyTpRoomlistPriv;
47
48 enum {
49         NEW_ROOM,
50         DESTROY,
51         ERROR,
52         LAST_SIGNAL
53 };
54
55 enum {
56         PROP_0,
57         PROP_CONNECTION,
58         PROP_IS_LISTING,
59 };
60
61 static guint signals[LAST_SIGNAL];
62
63 G_DEFINE_TYPE (EmpathyTpRoomlist, empathy_tp_roomlist, G_TYPE_OBJECT);
64
65 static void
66 tp_roomlist_listing_cb (TpChannel *channel,
67                         gboolean   listing,
68                         gpointer   user_data,
69                         GObject   *list)
70 {
71         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
72
73         DEBUG ("Listing: %s", listing ? "Yes" : "No");
74         priv->is_listing = listing;
75         g_object_notify (list, "is-listing");
76 }
77
78 static void
79 tp_roomlist_chatrooms_free (gpointer data)
80 {
81         GSList *chatrooms = data;
82
83         g_slist_foreach (chatrooms, (GFunc) g_object_unref, NULL);
84         g_slist_free (chatrooms);
85 }
86
87 static void
88 tp_roomlist_inspect_handles_cb (TpConnection *connection,
89                                 const gchar **names,
90                                 const GError *error,
91                                 gpointer      user_data,
92                                 GObject      *list)
93 {
94         GSList *chatrooms = user_data;
95
96         if (error != NULL) {
97                 DEBUG ("Error: %s", error->message);
98                 return;
99         }
100
101         while (*names != NULL) {
102                 EmpathyChatroom *chatroom = chatrooms->data;
103
104                 empathy_chatroom_set_room (chatroom, *names);
105                 g_signal_emit (list, signals[NEW_ROOM], 0, chatroom);
106
107                 names++;
108                 chatrooms = chatrooms->next;
109         }
110 }
111
112 static void
113 tp_roomlist_got_rooms_cb (TpChannel       *channel,
114                           const GPtrArray *rooms,
115                           gpointer         user_data,
116                           GObject         *list)
117 {
118         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
119         EmpathyChatroom       *chatroom;
120         guint                  i;
121         GArray                *handles = NULL;
122         GSList                *chatrooms = NULL;
123
124         for (i = 0; i < rooms->len; i++) {
125                 const GValue *room_name_value;
126                 const GValue *handle_name_value;
127                 const GValue *room_members_value;
128                 const GValue *room_subject_value;
129                 const GValue *room_invite_value;
130                 const GValue *room_password_value;
131                 GValueArray  *room_struct;
132                 guint         handle;
133                 const gchar  *channel_type;
134                 GHashTable   *info;
135
136                 /* Get information */
137                 room_struct = g_ptr_array_index (rooms, i);
138                 handle = g_value_get_uint (g_value_array_get_nth (room_struct, 0));
139                 channel_type = g_value_get_string (g_value_array_get_nth (room_struct, 1));
140                 info = g_value_get_boxed (g_value_array_get_nth (room_struct, 2));
141                 room_name_value = g_hash_table_lookup (info, "name");
142                 handle_name_value = g_hash_table_lookup (info, "handle-name");
143                 room_subject_value = g_hash_table_lookup (info, "subject");
144                 room_members_value = g_hash_table_lookup (info, "members");
145                 room_invite_value = g_hash_table_lookup (info, "invite-only");
146                 room_password_value = g_hash_table_lookup (info, "password");
147
148                 if (tp_strdiff (channel_type, TP_IFACE_CHANNEL_TYPE_TEXT)) {
149                         continue;
150                 }
151
152                 chatroom = empathy_chatroom_new (priv->account);
153
154                 if (room_name_value != NULL) {
155                         empathy_chatroom_set_name (chatroom,
156                                                    g_value_get_string (room_name_value));
157                 }
158
159                 if (room_members_value != NULL) {
160                         empathy_chatroom_set_members_count (chatroom,
161                                                    g_value_get_uint (room_members_value));
162                 }
163
164                 if (room_subject_value != NULL) {
165                         empathy_chatroom_set_subject (chatroom,
166                                                    g_value_get_string (room_subject_value));
167                 }
168
169                 if (room_invite_value != NULL) {
170                         empathy_chatroom_set_invite_only (chatroom,
171                                                    g_value_get_boolean (room_invite_value));
172                 }
173
174                 if (room_password_value != NULL) {
175                         empathy_chatroom_set_need_password (chatroom,
176                                                    g_value_get_boolean (room_password_value));
177                 }
178
179                 if (handle_name_value != NULL) {
180                         empathy_chatroom_set_room (chatroom,
181                                                    g_value_get_string (handle_name_value));
182
183                         /* We have the room ID, we can directly emit it */
184                         g_signal_emit (list, signals[NEW_ROOM], 0, chatroom);
185                         g_object_unref (chatroom);
186                 } else {
187                         /* We don't have the room ID, we'll inspect all handles
188                          * at once and then emit rooms */
189                         if (handles == NULL) {
190                                 handles = g_array_new (FALSE, FALSE, sizeof (guint));
191                         }
192
193                         g_array_append_val (handles, handle);
194                         chatrooms = g_slist_prepend (chatrooms, chatroom);
195                 }
196         }
197
198         if (handles != NULL) {
199                 chatrooms = g_slist_reverse (chatrooms);
200                 tp_cli_connection_call_inspect_handles (priv->connection, -1,
201                                                        TP_HANDLE_TYPE_ROOM,
202                                                        handles,
203                                                        tp_roomlist_inspect_handles_cb,
204                                                        chatrooms,
205                                                        tp_roomlist_chatrooms_free,
206                                                        list);
207                 g_array_free (handles, TRUE);
208         }
209 }
210
211 static void
212 tp_roomlist_get_listing_rooms_cb (TpChannel    *channel,
213                                   gboolean      is_listing,
214                                   const GError *error,
215                                   gpointer      user_data,
216                                   GObject      *list)
217 {
218         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
219
220         if (error) {
221                 DEBUG ("Error geting listing rooms: %s", error->message);
222                 return;
223         }
224
225         priv->is_listing = is_listing;
226         g_object_notify (list, "is-listing");
227 }
228
229 static void
230 tp_roomlist_invalidated_cb (TpChannel         *channel,
231                             guint              domain,
232                             gint               code,
233                             gchar             *message,
234                             EmpathyTpRoomlist *list)
235 {
236         DEBUG ("Channel invalidated: %s", message);
237         g_signal_emit (list, signals[DESTROY], 0);
238 }
239
240 static void
241 call_list_rooms_cb (TpChannel *proxy,
242                     const GError *error,
243                     gpointer list,
244                     GObject *weak_object)
245 {
246         if (error != NULL) {
247                 DEBUG ("Error listing rooms: %s", error->message);
248                 g_signal_emit_by_name (list, "error::start", error);
249         }
250 }
251
252 static void
253 stop_listing_cb (TpChannel *proxy,
254                  const GError *error,
255                  gpointer list,
256                  GObject *weak_object)
257 {
258         if (error != NULL) {
259                 DEBUG ("Error on stop listing: %s", error->message);
260                 g_signal_emit_by_name (list, "error::stop", error);
261         }
262 }
263
264 static void
265 channel_ready_cb (TpChannel *channel,
266                   const GError *error,
267                   gpointer user_data)
268 {
269         EmpathyTpRoomlist *list = EMPATHY_TP_ROOMLIST (user_data);
270         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
271
272         if (error != NULL) {
273                 DEBUG ("Channel invalidated: %s", error->message);
274                 g_signal_emit (list, signals[DESTROY], 0);
275                 return;
276         }
277
278         g_signal_connect (priv->channel, "invalidated",
279                           G_CALLBACK (tp_roomlist_invalidated_cb),
280                           list);
281
282         tp_cli_channel_type_room_list_connect_to_listing_rooms (priv->channel,
283                                                                 tp_roomlist_listing_cb,
284                                                                 NULL, NULL,
285                                                                 G_OBJECT (list),
286                                                                 NULL);
287         tp_cli_channel_type_room_list_connect_to_got_rooms (priv->channel,
288                                                             tp_roomlist_got_rooms_cb,
289                                                             NULL, NULL,
290                                                             G_OBJECT (list),
291                                                             NULL);
292
293         tp_cli_channel_type_room_list_call_get_listing_rooms (priv->channel, -1,
294                                                               tp_roomlist_get_listing_rooms_cb,
295                                                               NULL, NULL,
296                                                               G_OBJECT (list));
297
298         if (priv->start_requested == TRUE) {
299                 tp_cli_channel_type_room_list_call_list_rooms (priv->channel, -1,
300                         call_list_rooms_cb, list, NULL, NULL);
301                 priv->start_requested = FALSE;
302         }
303 }
304
305 static void
306 tp_roomlist_request_channel_cb (TpConnection *connection,
307                                 const gchar  *object_path,
308                                 const GError *error,
309                                 gpointer      user_data,
310                                 GObject      *list)
311 {
312         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
313
314         if (error) {
315                 DEBUG ("Error requesting channel: %s", error->message);
316                 return;
317         }
318
319         priv->channel = tp_channel_new (priv->connection, object_path,
320                                         TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
321                                         TP_HANDLE_TYPE_NONE,
322                                         0, NULL);
323         tp_channel_call_when_ready (priv->channel, channel_ready_cb, list);
324 }
325
326 static void
327 tp_roomlist_finalize (GObject *object)
328 {
329         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
330
331         if (priv->channel) {
332                 DEBUG ("Closing channel...");
333                 g_signal_handlers_disconnect_by_func (priv->channel,
334                                                       tp_roomlist_invalidated_cb,
335                                                       object);
336                 tp_cli_channel_call_close (priv->channel, -1,
337                                            NULL, NULL, NULL, NULL);
338                 g_object_unref (priv->channel);
339         }
340
341         if (priv->account) {
342                 g_object_unref (priv->account);
343         }
344         if (priv->connection) {
345                 g_object_unref (priv->connection);
346         }
347
348         G_OBJECT_CLASS (empathy_tp_roomlist_parent_class)->finalize (object);
349 }
350
351 static void
352 tp_roomlist_constructed (GObject *list)
353 {
354         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
355         MissionControl        *mc;
356
357         mc = empathy_mission_control_dup_singleton ();
358         priv->account = mission_control_get_account_for_tpconnection (mc,
359                                                                       priv->connection,
360                                                                       NULL);
361         g_object_unref (mc);
362
363         tp_cli_connection_call_request_channel (priv->connection, -1,
364                                                 TP_IFACE_CHANNEL_TYPE_ROOM_LIST,
365                                                 TP_HANDLE_TYPE_NONE,
366                                                 0,
367                                                 TRUE,
368                                                 tp_roomlist_request_channel_cb,
369                                                 NULL, NULL,
370                                                 list);
371 }
372
373 static void
374 tp_roomlist_get_property (GObject    *object,
375                           guint       param_id,
376                           GValue     *value,
377                           GParamSpec *pspec)
378 {
379         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
380
381         switch (param_id) {
382         case PROP_CONNECTION:
383                 g_value_set_object (value, priv->connection);
384                 break;
385         case PROP_IS_LISTING:
386                 g_value_set_boolean (value, priv->is_listing);
387                 break;
388         default:
389                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
390                 break;
391         };
392 }
393
394 static void
395 tp_roomlist_set_property (GObject      *object,
396                           guint         param_id,
397                           const GValue *value,
398                           GParamSpec   *pspec)
399 {
400         EmpathyTpRoomlistPriv *priv = GET_PRIV (object);
401
402         switch (param_id) {
403         case PROP_CONNECTION:
404                 priv->connection = g_object_ref (g_value_get_object (value));
405                 break;
406         default:
407                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
408                 break;
409         };
410 }
411
412 static void
413 empathy_tp_roomlist_class_init (EmpathyTpRoomlistClass *klass)
414 {
415         GObjectClass *object_class = G_OBJECT_CLASS (klass);
416
417         object_class->finalize = tp_roomlist_finalize;
418         object_class->constructed = tp_roomlist_constructed;
419         object_class->get_property = tp_roomlist_get_property;
420         object_class->set_property = tp_roomlist_set_property;
421
422         g_object_class_install_property (object_class,
423                                          PROP_CONNECTION,
424                                          g_param_spec_object ("connection",
425                                                               "The Connection",
426                                                               "The connection on which it lists rooms",
427                                                               TP_TYPE_CONNECTION,
428                                                               G_PARAM_READWRITE |
429                                                               G_PARAM_CONSTRUCT_ONLY));
430         g_object_class_install_property (object_class,
431                                          PROP_IS_LISTING,
432                                          g_param_spec_boolean ("is-listing",
433                                                                "Is listing",
434                                                                "Are we listing rooms",
435                                                                FALSE,
436                                                                G_PARAM_READABLE));
437
438         signals[NEW_ROOM] =
439                 g_signal_new ("new-room",
440                               G_TYPE_FROM_CLASS (klass),
441                               G_SIGNAL_RUN_LAST,
442                               0,
443                               NULL, NULL,
444                               g_cclosure_marshal_VOID__OBJECT,
445                               G_TYPE_NONE,
446                               1, EMPATHY_TYPE_CHATROOM);
447
448         signals[DESTROY] =
449                 g_signal_new ("destroy",
450                               G_TYPE_FROM_CLASS (klass),
451                               G_SIGNAL_RUN_LAST,
452                               0,
453                               NULL, NULL,
454                               g_cclosure_marshal_VOID__VOID,
455                               G_TYPE_NONE,
456                               0);
457
458         signals[ERROR] =
459                 g_signal_new ("error",
460                               G_TYPE_FROM_CLASS (klass),
461                               G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
462                               0,
463                               NULL, NULL,
464                               g_cclosure_marshal_VOID__POINTER,
465                               G_TYPE_NONE,
466                               1, G_TYPE_POINTER);
467
468         g_type_class_add_private (object_class, sizeof (EmpathyTpRoomlistPriv));
469 }
470
471 static void
472 empathy_tp_roomlist_init (EmpathyTpRoomlist *list)
473 {
474         EmpathyTpRoomlistPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (list,
475                 EMPATHY_TYPE_TP_ROOMLIST, EmpathyTpRoomlistPriv);
476
477         list->priv = priv;
478         priv->start_requested = FALSE;
479         priv->is_listing = FALSE;
480 }
481
482 EmpathyTpRoomlist *
483 empathy_tp_roomlist_new (McAccount *account)
484 {
485         EmpathyTpRoomlist *list;
486         MissionControl    *mc;
487         TpConnection      *connection;
488
489         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
490
491         mc = empathy_mission_control_dup_singleton ();
492         connection = mission_control_get_tpconnection (mc, account, NULL);
493
494         list = g_object_new (EMPATHY_TYPE_TP_ROOMLIST,
495                              "connection", connection,
496                              NULL);
497
498         g_object_unref (mc);
499         g_object_unref (connection);
500
501         return list;
502 }
503
504 gboolean
505 empathy_tp_roomlist_is_listing (EmpathyTpRoomlist *list)
506 {
507         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
508
509         g_return_val_if_fail (EMPATHY_IS_TP_ROOMLIST (list), FALSE);
510
511         return priv->is_listing;
512 }
513
514 void
515 empathy_tp_roomlist_start (EmpathyTpRoomlist *list)
516 {
517         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
518
519         g_return_if_fail (EMPATHY_IS_TP_ROOMLIST (list));
520         if (priv->channel != NULL) {
521                 tp_cli_channel_type_room_list_call_list_rooms (priv->channel, -1,
522                         call_list_rooms_cb, list, NULL, NULL);
523         } else {
524                 priv->start_requested = TRUE;
525         }
526 }
527
528 void
529 empathy_tp_roomlist_stop (EmpathyTpRoomlist *list)
530 {
531         EmpathyTpRoomlistPriv *priv = GET_PRIV (list);
532
533         g_return_if_fail (EMPATHY_IS_TP_ROOMLIST (list));
534         g_return_if_fail (TP_IS_CHANNEL (priv->channel));
535
536         tp_cli_channel_type_room_list_call_stop_listing (priv->channel, -1,
537                                                          stop_listing_cb, list, NULL, NULL);
538 }
539