~ubuntu-branches/ubuntu/natty/empathy/natty

« back to all changes in this revision

Viewing changes to .pc/00git_folks_api.patch/libempathy-gtk/empathy-individual-store.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell, Omer Akram
  • Date: 2011-01-06 14:32:31 UTC
  • Revision ID: james.westby@ubuntu.com-20110106143231-ol0gyfqi7h1ch8kv
Tags: 2.32.2-0ubuntu3
* debian/patches/00git_folks_api.patch:
  - Update to latest folks API

[ Omer Akram ]
* debian/patches/enable_pidgin_imported_contacts.patch:
  - Enable pidgin imported accounts by default. (LP: #622215)
* debian/patches/reword_subscription_dailog_to_be_less_technical.patch:
  - Reword subscription request dialog to be less technical. (LP: #670197)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * Copyright (C) 2005-2007 Imendio AB
 
4
 * Copyright (C) 2007-2010 Collabora Ltd.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License as
 
8
 * published by the Free Software Foundation; either version 2 of the
 
9
 * License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public
 
17
 * License along with this program; if not, write to the
 
18
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 
19
 * Boston, MA  02110-1301  USA
 
20
 *
 
21
 * Authors: Mikael Hallendal <micke@imendio.com>
 
22
 *          Martyn Russell <martyn@imendio.com>
 
23
 *          Xavier Claessens <xclaesse@gmail.com>
 
24
 *          Travis Reitter <travis.reitter@collabora.co.uk>
 
25
 */
 
26
 
 
27
#include "config.h"
 
28
 
 
29
#include <string.h>
 
30
 
 
31
#include <glib.h>
 
32
#include <glib/gi18n-lib.h>
 
33
#include <gtk/gtk.h>
 
34
 
 
35
#include <folks/folks.h>
 
36
#include <folks/folks-telepathy.h>
 
37
#include <telepathy-glib/util.h>
 
38
 
 
39
#include <libempathy/empathy-utils.h>
 
40
#include <libempathy/empathy-enum-types.h>
 
41
#include <libempathy/empathy-individual-manager.h>
 
42
 
 
43
#include "empathy-individual-store.h"
 
44
#include "empathy-ui-utils.h"
 
45
#include "empathy-gtk-enum-types.h"
 
46
 
 
47
#define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
 
48
#include <libempathy/empathy-debug.h>
 
49
 
 
50
/* Active users are those which have recently changed state
 
51
 * (e.g. online, offline or from normal to a busy state).
 
52
 */
 
53
 
 
54
/* Time in seconds user is shown as active */
 
55
#define ACTIVE_USER_SHOW_TIME 7
 
56
 
 
57
/* Time in seconds after connecting which we wait before active users are enabled */
 
58
#define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5
 
59
 
 
60
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualStore)
 
61
typedef struct
 
62
{
 
63
  EmpathyIndividualManager *manager;
 
64
  gboolean show_avatars;
 
65
  gboolean show_groups;
 
66
  gboolean is_compact;
 
67
  gboolean show_protocols;
 
68
  gboolean show_active;
 
69
  EmpathyIndividualStoreSort sort_criterium;
 
70
  guint inhibit_active;
 
71
  guint setup_idle_id;
 
72
  gboolean dispose_has_run;
 
73
  GHashTable *status_icons;
 
74
  /* List of owned GCancellables for each pending avatar load operation */
 
75
  GList *avatar_cancellables;
 
76
} EmpathyIndividualStorePriv;
 
77
 
 
78
typedef struct
 
79
{
 
80
  GtkTreeIter iter;
 
81
  const gchar *name;
 
82
  gboolean found;
 
83
} FindGroup;
 
84
 
 
85
typedef struct
 
86
{
 
87
  FolksIndividual *individual;
 
88
  gboolean found;
 
89
  GList *iters;
 
90
} FindContact;
 
91
 
 
92
typedef struct
 
93
{
 
94
  EmpathyIndividualStore *self;
 
95
  FolksIndividual *individual;
 
96
  gboolean remove;
 
97
  guint timeout;
 
98
} ShowActiveData;
 
99
 
 
100
enum
 
101
{
 
102
  PROP_0,
 
103
  PROP_INDIVIDUAL_MANAGER,
 
104
  PROP_SHOW_AVATARS,
 
105
  PROP_SHOW_PROTOCOLS,
 
106
  PROP_SHOW_GROUPS,
 
107
  PROP_IS_COMPACT,
 
108
  PROP_SORT_CRITERIUM
 
109
};
 
110
 
 
111
/* prototypes to break cycles */
 
112
static void individual_store_contact_update (EmpathyIndividualStore *self,
 
113
    FolksIndividual *individual);
 
114
 
 
115
G_DEFINE_TYPE (EmpathyIndividualStore, empathy_individual_store,
 
116
    GTK_TYPE_TREE_STORE);
 
117
 
 
118
/* Calculate whether the Individual can do audio or video calls.
 
119
 * FIXME: We can remove this once libfolks has grown capabilities support
 
120
 * again: bgo#626179. */
 
121
static void
 
122
individual_can_audio_video_call (FolksIndividual *individual,
 
123
    gboolean *can_audio_call,
 
124
    gboolean *can_video_call)
 
125
{
 
126
  GList *personas, *l;
 
127
  gboolean can_audio = FALSE, can_video = FALSE;
 
128
 
 
129
  personas = folks_individual_get_personas (individual);
 
130
  for (l = personas; l != NULL; l = l->next)
 
131
    {
 
132
      TpContact *tp_contact;
 
133
      EmpathyContact *contact;
 
134
 
 
135
      if (!TPF_IS_PERSONA (l->data))
 
136
        continue;
 
137
 
 
138
      tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
 
139
      contact = empathy_contact_dup_from_tp_contact (tp_contact);
 
140
      empathy_contact_set_persona (contact, FOLKS_PERSONA (l->data));
 
141
 
 
142
      can_audio = can_audio || empathy_contact_get_capabilities (contact) &
 
143
          EMPATHY_CAPABILITIES_AUDIO;
 
144
      can_video = can_video || empathy_contact_get_capabilities (contact) &
 
145
          EMPATHY_CAPABILITIES_VIDEO;
 
146
 
 
147
      g_object_unref (contact);
 
148
 
 
149
      if (can_audio && can_video)
 
150
        break;
 
151
    }
 
152
 
 
153
  *can_audio_call = can_audio;
 
154
  *can_video_call = can_video;
 
155
}
 
156
 
 
157
static void
 
158
add_individual_to_store (GtkTreeStore *self,
 
159
    GtkTreeIter *iter,
 
160
    GtkTreeIter *parent,
 
161
    FolksIndividual *individual,
 
162
    EmpathyIndividualManagerFlags flags)
 
163
{
 
164
  gboolean can_audio_call, can_video_call;
 
165
 
 
166
  individual_can_audio_video_call (individual, &can_audio_call,
 
167
      &can_video_call);
 
168
 
 
169
  gtk_tree_store_insert_with_values (self, iter, parent, 0,
 
170
      EMPATHY_INDIVIDUAL_STORE_COL_NAME,
 
171
      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)),
 
172
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, individual,
 
173
      EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, FALSE,
 
174
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
 
175
      EMPATHY_INDIVIDUAL_STORE_COL_FLAGS, flags,
 
176
      EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
 
177
      EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
 
178
      -1);
 
179
}
 
180
 
 
181
static gboolean
 
182
individual_store_get_group_foreach (GtkTreeModel *model,
 
183
    GtkTreePath *path,
 
184
    GtkTreeIter *iter,
 
185
    FindGroup *fg)
 
186
{
 
187
  gchar *str;
 
188
  gboolean is_group;
 
189
 
 
190
  /* Groups are only at the top level. */
 
191
  if (gtk_tree_path_get_depth (path) != 1)
 
192
    return FALSE;
 
193
 
 
194
  gtk_tree_model_get (model, iter,
 
195
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &str,
 
196
      EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group, -1);
 
197
 
 
198
  if (is_group && !tp_strdiff (str, fg->name))
 
199
    {
 
200
      fg->found = TRUE;
 
201
      fg->iter = *iter;
 
202
    }
 
203
 
 
204
  g_free (str);
 
205
 
 
206
  return fg->found;
 
207
}
 
208
 
 
209
static void
 
210
individual_store_get_group (EmpathyIndividualStore *self,
 
211
    const gchar *name,
 
212
    GtkTreeIter *iter_group_to_set,
 
213
    GtkTreeIter *iter_separator_to_set,
 
214
    gboolean *created,
 
215
    gboolean is_fake_group)
 
216
{
 
217
  EmpathyIndividualStorePriv *priv;
 
218
  GtkTreeModel *model;
 
219
  GtkTreeIter iter_group;
 
220
  GtkTreeIter iter_separator;
 
221
  FindGroup fg;
 
222
 
 
223
  priv = GET_PRIV (self);
 
224
 
 
225
  memset (&fg, 0, sizeof (fg));
 
226
 
 
227
  fg.name = name;
 
228
 
 
229
  model = GTK_TREE_MODEL (self);
 
230
  gtk_tree_model_foreach (model,
 
231
      (GtkTreeModelForeachFunc) individual_store_get_group_foreach, &fg);
 
232
 
 
233
  if (!fg.found)
 
234
    {
 
235
      if (created)
 
236
        *created = TRUE;
 
237
 
 
238
      gtk_tree_store_insert_with_values (GTK_TREE_STORE (self), &iter_group,
 
239
          NULL, 0,
 
240
          EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, NULL,
 
241
          EMPATHY_INDIVIDUAL_STORE_COL_NAME, name,
 
242
          EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, TRUE,
 
243
          EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE, FALSE,
 
244
          EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
 
245
          EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, is_fake_group,
 
246
          -1);
 
247
 
 
248
      if (iter_group_to_set)
 
249
        *iter_group_to_set = iter_group;
 
250
 
 
251
      gtk_tree_store_insert_with_values (GTK_TREE_STORE (self), &iter_separator,
 
252
          &iter_group, 0,
 
253
          EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, TRUE,
 
254
          -1);
 
255
 
 
256
      if (iter_separator_to_set)
 
257
        *iter_separator_to_set = iter_separator;
 
258
    }
 
259
  else
 
260
    {
 
261
      if (created)
 
262
        *created = FALSE;
 
263
 
 
264
      if (iter_group_to_set)
 
265
        *iter_group_to_set = fg.iter;
 
266
 
 
267
      iter_separator = fg.iter;
 
268
 
 
269
      if (gtk_tree_model_iter_next (model, &iter_separator))
 
270
        {
 
271
          gboolean is_separator;
 
272
 
 
273
          gtk_tree_model_get (model, &iter_separator,
 
274
              EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator, -1);
 
275
 
 
276
          if (is_separator && iter_separator_to_set)
 
277
            *iter_separator_to_set = iter_separator;
 
278
        }
 
279
    }
 
280
}
 
281
 
 
282
static gboolean
 
283
individual_store_find_contact_foreach (GtkTreeModel *model,
 
284
    GtkTreePath *path,
 
285
    GtkTreeIter *iter,
 
286
    FindContact *fc)
 
287
{
 
288
  FolksIndividual *individual;
 
289
 
 
290
  gtk_tree_model_get (model, iter,
 
291
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, -1);
 
292
 
 
293
  if (individual == fc->individual)
 
294
    {
 
295
      fc->found = TRUE;
 
296
      fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
 
297
    }
 
298
 
 
299
  tp_clear_object (&individual);
 
300
 
 
301
  return FALSE;
 
302
}
 
303
 
 
304
static GList *
 
305
individual_store_find_contact (EmpathyIndividualStore *self,
 
306
    FolksIndividual *individual)
 
307
{
 
308
  EmpathyIndividualStorePriv *priv;
 
309
  GtkTreeModel *model;
 
310
  GList *l = NULL;
 
311
  FindContact fc;
 
312
 
 
313
  priv = GET_PRIV (self);
 
314
 
 
315
  memset (&fc, 0, sizeof (fc));
 
316
 
 
317
  fc.individual = individual;
 
318
 
 
319
  model = GTK_TREE_MODEL (self);
 
320
  gtk_tree_model_foreach (model,
 
321
      (GtkTreeModelForeachFunc) individual_store_find_contact_foreach, &fc);
 
322
 
 
323
  if (fc.found)
 
324
    l = fc.iters;
 
325
 
 
326
  return l;
 
327
}
 
328
 
 
329
static void
 
330
free_iters (GList *iters)
 
331
{
 
332
  g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
 
333
  g_list_free (iters);
 
334
}
 
335
 
 
336
static void
 
337
individual_store_remove_individual (EmpathyIndividualStore *self,
 
338
    FolksIndividual *individual)
 
339
{
 
340
  EmpathyIndividualStorePriv *priv;
 
341
  GtkTreeModel *model;
 
342
  GList *iters, *l;
 
343
 
 
344
  priv = GET_PRIV (self);
 
345
 
 
346
  iters = individual_store_find_contact (self, individual);
 
347
  if (iters == NULL)
 
348
    return;
 
349
 
 
350
  /* Clean up model */
 
351
  model = GTK_TREE_MODEL (self);
 
352
 
 
353
  for (l = iters; l; l = l->next)
 
354
    {
 
355
      GtkTreeIter parent;
 
356
 
 
357
      /* NOTE: it is only <= 2 here because we have
 
358
       * separators after the group name, otherwise it
 
359
       * should be 1.
 
360
       */
 
361
      if (gtk_tree_model_iter_parent (model, &parent, l->data) &&
 
362
          gtk_tree_model_iter_n_children (model, &parent) <= 2)
 
363
        {
 
364
          gtk_tree_store_remove (GTK_TREE_STORE (self), &parent);
 
365
        }
 
366
      else
 
367
        {
 
368
          gtk_tree_store_remove (GTK_TREE_STORE (self), l->data);
 
369
        }
 
370
    }
 
371
 
 
372
  free_iters (iters);
 
373
}
 
374
 
 
375
static void
 
376
individual_store_add_individual (EmpathyIndividualStore *self,
 
377
    FolksIndividual *individual)
 
378
{
 
379
  EmpathyIndividualStorePriv *priv;
 
380
  GtkTreeIter iter;
 
381
  GHashTable *group_set = NULL;
 
382
  GList *groups = NULL, *l;
 
383
  EmpathyContact *contact;
 
384
  TpConnection *connection;
 
385
  EmpathyIndividualManagerFlags flags = 0;
 
386
  gchar *protocol_name;
 
387
 
 
388
  priv = GET_PRIV (self);
 
389
 
 
390
  if (EMP_STR_EMPTY (folks_aliasable_get_alias (FOLKS_ALIASABLE (individual))))
 
391
    return;
 
392
 
 
393
  if (priv->show_groups)
 
394
    {
 
395
      group_set = folks_groupable_get_groups (FOLKS_GROUPABLE (individual));
 
396
      groups = g_hash_table_get_keys (group_set);
 
397
    }
 
398
 
 
399
  contact = empathy_contact_dup_from_folks_individual (individual);
 
400
  connection = empathy_contact_get_connection (contact);
 
401
  flags = empathy_individual_manager_get_flags_for_connection (priv->manager,
 
402
      connection);
 
403
 
 
404
  tp_connection_parse_object_path (connection, &protocol_name, NULL);
 
405
 
 
406
  if (groups == NULL)
 
407
    {
 
408
      GtkTreeIter iter_group, *parent;
 
409
 
 
410
      parent = &iter_group;
 
411
 
 
412
      if (!priv->show_groups)
 
413
        parent = NULL;
 
414
      else if (!tp_strdiff (protocol_name, "local-xmpp"))
 
415
        {
 
416
          /* these are People Nearby */
 
417
          individual_store_get_group (self,
 
418
              EMPATHY_INDIVIDUAL_STORE_PEOPLE_NEARBY, &iter_group, NULL, NULL,
 
419
              TRUE);
 
420
        }
 
421
      else
 
422
        {
 
423
          individual_store_get_group (self,
 
424
              EMPATHY_INDIVIDUAL_STORE_UNGROUPED,
 
425
              &iter_group, NULL, NULL, TRUE);
 
426
        }
 
427
 
 
428
      add_individual_to_store (GTK_TREE_STORE (self), &iter, parent,
 
429
          individual, flags);
 
430
    }
 
431
 
 
432
  g_free (protocol_name);
 
433
 
 
434
  /* Else add to each group. */
 
435
  for (l = groups; l; l = l->next)
 
436
    {
 
437
      GtkTreeIter iter_group;
 
438
 
 
439
      individual_store_get_group (self, l->data, &iter_group, NULL, NULL,
 
440
          FALSE);
 
441
 
 
442
      add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
 
443
          individual, flags);
 
444
    }
 
445
  g_list_free (groups);
 
446
 
 
447
  if (priv->show_groups &&
 
448
      folks_favourite_get_is_favourite (FOLKS_FAVOURITE (individual)))
 
449
    {
 
450
      /* Add contact to the fake 'Favorites' group */
 
451
      GtkTreeIter iter_group;
 
452
 
 
453
      individual_store_get_group (self, EMPATHY_INDIVIDUAL_STORE_FAVORITE,
 
454
          &iter_group, NULL, NULL, TRUE);
 
455
 
 
456
      add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
 
457
          individual, flags);
 
458
    }
 
459
 
 
460
  individual_store_contact_update (self, individual);
 
461
 
 
462
  tp_clear_object (&contact);
 
463
}
 
464
 
 
465
static void
 
466
individual_store_contact_set_active (EmpathyIndividualStore *self,
 
467
    FolksIndividual *individual,
 
468
    gboolean active,
 
469
    gboolean set_changed)
 
470
{
 
471
  EmpathyIndividualStorePriv *priv;
 
472
  GtkTreeModel *model;
 
473
  GList *iters, *l;
 
474
 
 
475
  priv = GET_PRIV (self);
 
476
  model = GTK_TREE_MODEL (self);
 
477
 
 
478
  iters = individual_store_find_contact (self, individual);
 
479
  for (l = iters; l; l = l->next)
 
480
    {
 
481
      GtkTreePath *path;
 
482
 
 
483
      gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
 
484
          EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE, active,
 
485
          -1);
 
486
 
 
487
      DEBUG ("Set item %s", active ? "active" : "inactive");
 
488
 
 
489
      if (set_changed)
 
490
        {
 
491
          path = gtk_tree_model_get_path (model, l->data);
 
492
          gtk_tree_model_row_changed (model, path, l->data);
 
493
          gtk_tree_path_free (path);
 
494
        }
 
495
    }
 
496
 
 
497
  free_iters (iters);
 
498
}
 
499
 
 
500
static void individual_store_contact_active_free (ShowActiveData *data);
 
501
 
 
502
static void
 
503
individual_store_contact_active_invalidated (ShowActiveData *data,
 
504
    GObject *old_object)
 
505
{
 
506
  /* Remove the timeout and free the struct, since the individual or individual
 
507
   * store has disappeared. */
 
508
  g_source_remove (data->timeout);
 
509
 
 
510
  if (old_object == (GObject *) data->self)
 
511
    data->self = NULL;
 
512
  else if (old_object == (GObject *) data->individual)
 
513
    data->individual = NULL;
 
514
  else
 
515
    g_assert_not_reached ();
 
516
 
 
517
  individual_store_contact_active_free (data);
 
518
}
 
519
 
 
520
static ShowActiveData *
 
521
individual_store_contact_active_new (EmpathyIndividualStore *self,
 
522
    FolksIndividual *individual,
 
523
    gboolean remove_)
 
524
{
 
525
  ShowActiveData *data;
 
526
 
 
527
  DEBUG ("Individual'%s' now active, and %s be removed",
 
528
      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)),
 
529
      remove_ ? "WILL" : "WILL NOT");
 
530
 
 
531
  data = g_slice_new0 (ShowActiveData);
 
532
 
 
533
  /* We don't actually want to force either the IndividualStore or the
 
534
   * Individual to stay alive, since the user could quit Empathy or disable
 
535
   * the account before the contact_active timeout is fired. */
 
536
  g_object_weak_ref (G_OBJECT (self),
 
537
      (GWeakNotify) individual_store_contact_active_invalidated, data);
 
538
  g_object_weak_ref (G_OBJECT (individual),
 
539
      (GWeakNotify) individual_store_contact_active_invalidated, data);
 
540
 
 
541
  data->self = self;
 
542
  data->individual = individual;
 
543
  data->remove = remove_;
 
544
  data->timeout = 0;
 
545
 
 
546
  return data;
 
547
}
 
548
 
 
549
static void
 
550
individual_store_contact_active_free (ShowActiveData *data)
 
551
{
 
552
  if (data->self != NULL)
 
553
    {
 
554
      g_object_weak_unref (G_OBJECT (data->self),
 
555
          (GWeakNotify) individual_store_contact_active_invalidated, data);
 
556
    }
 
557
 
 
558
  if (data->individual != NULL)
 
559
    {
 
560
      g_object_weak_unref (G_OBJECT (data->individual),
 
561
          (GWeakNotify) individual_store_contact_active_invalidated, data);
 
562
    }
 
563
 
 
564
  g_slice_free (ShowActiveData, data);
 
565
}
 
566
 
 
567
static gboolean
 
568
individual_store_contact_active_cb (ShowActiveData *data)
 
569
{
 
570
  if (data->remove)
 
571
    {
 
572
      DEBUG ("Individual'%s' active timeout, removing item",
 
573
          folks_aliasable_get_alias (FOLKS_ALIASABLE (data->individual)));
 
574
      individual_store_remove_individual (data->self, data->individual);
 
575
    }
 
576
 
 
577
  DEBUG ("Individual'%s' no longer active",
 
578
      folks_aliasable_get_alias (FOLKS_ALIASABLE (data->individual)));
 
579
 
 
580
  individual_store_contact_set_active (data->self,
 
581
      data->individual, FALSE, TRUE);
 
582
 
 
583
  individual_store_contact_active_free (data);
 
584
 
 
585
  return FALSE;
 
586
}
 
587
 
 
588
typedef struct {
 
589
  EmpathyIndividualStore *store; /* weak */
 
590
  GCancellable *cancellable; /* owned */
 
591
} LoadAvatarData;
 
592
 
 
593
static void
 
594
individual_avatar_pixbuf_received_cb (FolksIndividual *individual,
 
595
    GAsyncResult *result,
 
596
    LoadAvatarData *data)
 
597
{
 
598
  GError *error = NULL;
 
599
  GdkPixbuf *pixbuf;
 
600
 
 
601
  pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (individual,
 
602
      result, &error);
 
603
 
 
604
  if (error != NULL)
 
605
    {
 
606
      DEBUG ("failed to retrieve pixbuf for individual %s: %s",
 
607
          folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)),
 
608
          error->message);
 
609
      g_clear_error (&error);
 
610
    }
 
611
  else if (data->store != NULL)
 
612
    {
 
613
      GList *iters, *l;
 
614
 
 
615
      iters = individual_store_find_contact (data->store, individual);
 
616
      for (l = iters; l; l = l->next)
 
617
        {
 
618
          gtk_tree_store_set (GTK_TREE_STORE (data->store), l->data,
 
619
              EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR, pixbuf,
 
620
              -1);
 
621
        }
 
622
 
 
623
      free_iters (iters);
 
624
    }
 
625
 
 
626
  /* Free things */
 
627
  if (data->store != NULL)
 
628
    {
 
629
      EmpathyIndividualStorePriv *priv = GET_PRIV (data->store);
 
630
 
 
631
      g_object_remove_weak_pointer (G_OBJECT (data->store),
 
632
          (gpointer *) &data->store);
 
633
      priv->avatar_cancellables = g_list_remove (priv->avatar_cancellables,
 
634
          data->cancellable);
 
635
    }
 
636
 
 
637
  g_object_unref (data->cancellable);
 
638
  g_slice_free (LoadAvatarData, data);
 
639
}
 
640
 
 
641
static void
 
642
individual_store_contact_update (EmpathyIndividualStore *self,
 
643
    FolksIndividual *individual)
 
644
{
 
645
  EmpathyIndividualStorePriv *priv;
 
646
  ShowActiveData *data;
 
647
  GtkTreeModel *model;
 
648
  GList *iters, *l;
 
649
  gboolean in_list;
 
650
  gboolean was_online = TRUE;
 
651
  gboolean now_online = FALSE;
 
652
  gboolean set_model = FALSE;
 
653
  gboolean do_remove = FALSE;
 
654
  gboolean do_set_active = FALSE;
 
655
  gboolean do_set_refresh = FALSE;
 
656
  gboolean show_avatar = FALSE;
 
657
  GdkPixbuf *pixbuf_status;
 
658
  LoadAvatarData *load_avatar_data;
 
659
 
 
660
  priv = GET_PRIV (self);
 
661
 
 
662
  model = GTK_TREE_MODEL (self);
 
663
 
 
664
  iters = individual_store_find_contact (self, individual);
 
665
  if (!iters)
 
666
    {
 
667
      in_list = FALSE;
 
668
    }
 
669
  else
 
670
    {
 
671
      in_list = TRUE;
 
672
    }
 
673
 
 
674
  /* Get online state now. */
 
675
  now_online = folks_presence_is_online (FOLKS_PRESENCE (individual));
 
676
 
 
677
  if (!in_list)
 
678
    {
 
679
      DEBUG ("Individual'%s' in list:NO, should be:YES",
 
680
          folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
 
681
 
 
682
      individual_store_add_individual (self, individual);
 
683
 
 
684
      if (priv->show_active)
 
685
        {
 
686
          do_set_active = TRUE;
 
687
 
 
688
          DEBUG ("Set active (individual added)");
 
689
        }
 
690
    }
 
691
  else
 
692
    {
 
693
      DEBUG ("Individual'%s' in list:YES, should be:YES",
 
694
          folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
 
695
 
 
696
      /* Get online state before. */
 
697
      if (iters && g_list_length (iters) > 0)
 
698
        {
 
699
          gtk_tree_model_get (model, iters->data,
 
700
              EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &was_online, -1);
 
701
        }
 
702
 
 
703
      /* Is this really an update or an online/offline. */
 
704
      if (priv->show_active)
 
705
        {
 
706
          if (was_online != now_online)
 
707
            {
 
708
              do_set_active = TRUE;
 
709
              do_set_refresh = TRUE;
 
710
 
 
711
              DEBUG ("Set active (individual updated %s)",
 
712
                  was_online ? "online  -> offline" : "offline -> online");
 
713
            }
 
714
          else
 
715
            {
 
716
              /* Was TRUE for presence updates. */
 
717
              /* do_set_active = FALSE;  */
 
718
              do_set_refresh = TRUE;
 
719
 
 
720
              DEBUG ("Set active (individual updated)");
 
721
            }
 
722
        }
 
723
 
 
724
      set_model = TRUE;
 
725
    }
 
726
 
 
727
  if (priv->show_avatars && !priv->is_compact)
 
728
    {
 
729
      show_avatar = TRUE;
 
730
    }
 
731
 
 
732
  /* Load the avatar asynchronously */
 
733
  load_avatar_data = g_slice_new (LoadAvatarData);
 
734
  load_avatar_data->store = self;
 
735
  g_object_add_weak_pointer (G_OBJECT (self),
 
736
      (gpointer *) &load_avatar_data->store);
 
737
  load_avatar_data->cancellable = g_cancellable_new ();
 
738
 
 
739
  priv->avatar_cancellables = g_list_prepend (priv->avatar_cancellables,
 
740
      load_avatar_data->cancellable);
 
741
  empathy_pixbuf_avatar_from_individual_scaled_async (individual, 32, 32,
 
742
      load_avatar_data->cancellable,
 
743
      (GAsyncReadyCallback) individual_avatar_pixbuf_received_cb,
 
744
      load_avatar_data);
 
745
 
 
746
  pixbuf_status =
 
747
      empathy_individual_store_get_individual_status_icon (self, individual);
 
748
 
 
749
  for (l = iters; l && set_model; l = l->next)
 
750
    {
 
751
      gboolean can_audio_call, can_video_call;
 
752
 
 
753
      individual_can_audio_video_call (individual, &can_audio_call,
 
754
          &can_video_call);
 
755
 
 
756
      gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
 
757
          EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf_status,
 
758
          EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
 
759
          EMPATHY_INDIVIDUAL_STORE_COL_NAME,
 
760
            folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)),
 
761
          EMPATHY_INDIVIDUAL_STORE_COL_PRESENCE_TYPE,
 
762
            folks_presence_get_presence_type (FOLKS_PRESENCE (individual)),
 
763
          EMPATHY_INDIVIDUAL_STORE_COL_STATUS,
 
764
            folks_presence_get_presence_message (FOLKS_PRESENCE (individual)),
 
765
          EMPATHY_INDIVIDUAL_STORE_COL_COMPACT, priv->is_compact,
 
766
          EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, FALSE,
 
767
          EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, now_online,
 
768
          EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
 
769
          EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
 
770
          EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
 
771
          -1);
 
772
    }
 
773
 
 
774
  if (priv->show_active && do_set_active)
 
775
    {
 
776
      individual_store_contact_set_active (self, individual, do_set_active,
 
777
          do_set_refresh);
 
778
 
 
779
      if (do_set_active)
 
780
        {
 
781
          data =
 
782
              individual_store_contact_active_new (self, individual,
 
783
              do_remove);
 
784
          data->timeout = g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME,
 
785
              (GSourceFunc) individual_store_contact_active_cb, data);
 
786
        }
 
787
    }
 
788
 
 
789
  /* FIXME: when someone goes online then offline quickly, the
 
790
   * first timeout sets the user to be inactive and the second
 
791
   * timeout removes the user from the contact list, really we
 
792
   * should remove the first timeout.
 
793
   */
 
794
  free_iters (iters);
 
795
}
 
796
 
 
797
static void
 
798
individual_store_individual_updated_cb (FolksIndividual *individual,
 
799
    GParamSpec *param,
 
800
    EmpathyIndividualStore *self)
 
801
{
 
802
  DEBUG ("Individual'%s' updated, checking roster is in sync...",
 
803
      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual)));
 
804
 
 
805
  individual_store_contact_update (self, individual);
 
806
}
 
807
 
 
808
static void
 
809
individual_store_contact_updated_cb (EmpathyContact *contact,
 
810
    GParamSpec *pspec,
 
811
    EmpathyIndividualStore *self)
 
812
{
 
813
  FolksIndividual *individual;
 
814
 
 
815
  DEBUG ("Contact '%s' updated, checking roster is in sync...",
 
816
      empathy_contact_get_alias (contact));
 
817
 
 
818
  individual = g_object_get_data (G_OBJECT (contact), "individual");
 
819
  if (individual == NULL)
 
820
    return;
 
821
 
 
822
  individual_store_contact_update (self, individual);
 
823
}
 
824
 
 
825
static void
 
826
individual_personas_changed_cb (FolksIndividual *individual,
 
827
    GList *added,
 
828
    GList *removed,
 
829
    EmpathyIndividualStore *self)
 
830
{
 
831
  GList *l;
 
832
 
 
833
  DEBUG ("Individual '%s' personas-changed.",
 
834
      folks_individual_get_id (individual));
 
835
 
 
836
  /* FIXME: libfolks hasn't grown capabilities support yet, so we have to go
 
837
   * through the EmpathyContacts for them. */
 
838
  for (l = removed; l != NULL; l = l->next)
 
839
    {
 
840
      TpContact *tp_contact;
 
841
      EmpathyContact *contact;
 
842
 
 
843
      if (!TPF_IS_PERSONA (l->data))
 
844
        continue;
 
845
 
 
846
      tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
 
847
      contact = empathy_contact_dup_from_tp_contact (tp_contact);
 
848
      empathy_contact_set_persona (contact, FOLKS_PERSONA (l->data));
 
849
 
 
850
      g_object_set_data (G_OBJECT (contact), "individual", NULL);
 
851
      g_signal_handlers_disconnect_by_func (contact,
 
852
          (GCallback) individual_store_contact_updated_cb, self);
 
853
 
 
854
      g_object_unref (contact);
 
855
    }
 
856
 
 
857
  for (l = added; l != NULL; l = l->next)
 
858
    {
 
859
      TpContact *tp_contact;
 
860
      EmpathyContact *contact;
 
861
 
 
862
      if (!TPF_IS_PERSONA (l->data))
 
863
        continue;
 
864
 
 
865
      tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
 
866
      contact = empathy_contact_dup_from_tp_contact (tp_contact);
 
867
      empathy_contact_set_persona (contact, FOLKS_PERSONA (l->data));
 
868
 
 
869
      g_object_set_data (G_OBJECT (contact), "individual", individual);
 
870
      g_signal_connect (contact, "notify::capabilities",
 
871
          (GCallback) individual_store_contact_updated_cb, self);
 
872
 
 
873
      g_object_unref (contact);
 
874
    }
 
875
}
 
876
 
 
877
static void
 
878
individual_store_add_individual_and_connect (EmpathyIndividualStore *self,
 
879
    FolksIndividual *individual)
 
880
{
 
881
  individual_store_add_individual (self, individual);
 
882
 
 
883
  g_signal_connect (individual, "notify::avatar",
 
884
      (GCallback) individual_store_individual_updated_cb, self);
 
885
  g_signal_connect (individual, "notify::presence-type",
 
886
      (GCallback) individual_store_individual_updated_cb, self);
 
887
  g_signal_connect (individual, "notify::presence-message",
 
888
      (GCallback) individual_store_individual_updated_cb, self);
 
889
  g_signal_connect (individual, "notify::alias",
 
890
      (GCallback) individual_store_individual_updated_cb, self);
 
891
  g_signal_connect (individual, "personas-changed",
 
892
      (GCallback) individual_personas_changed_cb, self);
 
893
 
 
894
  individual_personas_changed_cb (individual,
 
895
      folks_individual_get_personas (individual), NULL, self);
 
896
}
 
897
 
 
898
static void
 
899
individual_store_disconnect_individual (EmpathyIndividualStore *self,
 
900
    FolksIndividual *individual)
 
901
{
 
902
  individual_personas_changed_cb (individual, NULL,
 
903
      folks_individual_get_personas (individual), self);
 
904
 
 
905
  g_signal_handlers_disconnect_by_func (individual,
 
906
      (GCallback) individual_store_individual_updated_cb, self);
 
907
  g_signal_handlers_disconnect_by_func (individual,
 
908
      (GCallback) individual_personas_changed_cb, self);
 
909
}
 
910
 
 
911
static void
 
912
individual_store_remove_individual_and_disconnect (
 
913
    EmpathyIndividualStore *self,
 
914
    FolksIndividual *individual)
 
915
{
 
916
  individual_store_disconnect_individual (self, individual);
 
917
  individual_store_remove_individual (self, individual);
 
918
}
 
919
 
 
920
static void
 
921
individual_store_members_changed_cb (EmpathyIndividualManager *manager,
 
922
    const gchar *message,
 
923
    GList *added,
 
924
    GList *removed,
 
925
    guint reason,
 
926
    EmpathyIndividualStore *self)
 
927
{
 
928
  GList *l;
 
929
 
 
930
  for (l = added; l; l = l->next)
 
931
    {
 
932
      DEBUG ("Individual %s %s", folks_individual_get_id (l->data), "added");
 
933
 
 
934
      individual_store_add_individual_and_connect (self, l->data);
 
935
    }
 
936
  for (l = removed; l; l = l->next)
 
937
    {
 
938
      DEBUG ("Individual %s %s",
 
939
          folks_individual_get_id (l->data), "removed");
 
940
 
 
941
      individual_store_remove_individual_and_disconnect (self, l->data);
 
942
    }
 
943
}
 
944
 
 
945
static void
 
946
individual_store_favourites_changed_cb (EmpathyIndividualManager *manager,
 
947
    FolksIndividual *individual,
 
948
    gboolean is_favourite,
 
949
    EmpathyIndividualStore *self)
 
950
{
 
951
  EmpathyIndividualStorePriv *priv;
 
952
 
 
953
  priv = GET_PRIV (self);
 
954
 
 
955
  DEBUG ("Individual %s is %s a favourite",
 
956
      folks_individual_get_id (individual),
 
957
      is_favourite ? "now" : "no longer");
 
958
 
 
959
  individual_store_remove_individual (self, individual);
 
960
  individual_store_add_individual (self, individual);
 
961
}
 
962
 
 
963
static void
 
964
individual_store_groups_changed_cb (EmpathyIndividualManager *manager,
 
965
    FolksIndividual *individual,
 
966
    gchar *group,
 
967
    gboolean is_member,
 
968
    EmpathyIndividualStore *self)
 
969
{
 
970
  EmpathyIndividualStorePriv *priv;
 
971
  gboolean show_active;
 
972
 
 
973
  priv = GET_PRIV (self);
 
974
 
 
975
  DEBUG ("Updating groups for individual %s",
 
976
      folks_individual_get_id (individual));
 
977
 
 
978
  /* We do this to make sure the groups are correct, if not, we
 
979
   * would have to check the groups already set up for each
 
980
   * contact and then see what has been updated.
 
981
   */
 
982
  show_active = priv->show_active;
 
983
  priv->show_active = FALSE;
 
984
  individual_store_remove_individual (self, individual);
 
985
  individual_store_add_individual (self, individual);
 
986
  priv->show_active = show_active;
 
987
}
 
988
 
 
989
static gboolean
 
990
individual_store_manager_setup (gpointer user_data)
 
991
{
 
992
  EmpathyIndividualStore *self = user_data;
 
993
  EmpathyIndividualStorePriv *priv = GET_PRIV (self);
 
994
  GList *individuals;
 
995
 
 
996
  /* Signal connection. */
 
997
 
 
998
  /* TODO: implement */
 
999
  DEBUG ("handling individual renames unimplemented");
 
1000
 
 
1001
  g_signal_connect (priv->manager,
 
1002
      "members-changed",
 
1003
      G_CALLBACK (individual_store_members_changed_cb), self);
 
1004
 
 
1005
  g_signal_connect (priv->manager,
 
1006
      "favourites-changed",
 
1007
      G_CALLBACK (individual_store_favourites_changed_cb), self);
 
1008
 
 
1009
  g_signal_connect (priv->manager,
 
1010
      "groups-changed",
 
1011
      G_CALLBACK (individual_store_groups_changed_cb), self);
 
1012
 
 
1013
  /* Add contacts already created. */
 
1014
  individuals = empathy_individual_manager_get_members (priv->manager);
 
1015
  if (individuals != NULL && FOLKS_IS_INDIVIDUAL (individuals->data))
 
1016
    {
 
1017
      individual_store_members_changed_cb (priv->manager, "initial add",
 
1018
          individuals, NULL, 0, self);
 
1019
      g_list_free (individuals);
 
1020
    }
 
1021
 
 
1022
  priv->setup_idle_id = 0;
 
1023
  return FALSE;
 
1024
}
 
1025
 
 
1026
static void
 
1027
individual_store_set_individual_manager (EmpathyIndividualStore *self,
 
1028
    EmpathyIndividualManager *manager)
 
1029
{
 
1030
  EmpathyIndividualStorePriv *priv = GET_PRIV (self);
 
1031
 
 
1032
  priv->manager = g_object_ref (manager);
 
1033
 
 
1034
  /* Let a chance to have all properties set before populating */
 
1035
  priv->setup_idle_id = g_idle_add (individual_store_manager_setup, self);
 
1036
}
 
1037
 
 
1038
static void
 
1039
individual_store_member_renamed_cb (EmpathyIndividualManager *manager,
 
1040
    FolksIndividual *old_individual,
 
1041
    FolksIndividual *new_individual,
 
1042
    guint reason,
 
1043
    const gchar *message,
 
1044
    EmpathyIndividualStore *self)
 
1045
{
 
1046
  EmpathyIndividualStorePriv *priv;
 
1047
 
 
1048
  priv = GET_PRIV (self);
 
1049
 
 
1050
  DEBUG ("Individual %s renamed to %s",
 
1051
      folks_individual_get_id (old_individual),
 
1052
      folks_individual_get_id (new_individual));
 
1053
 
 
1054
  /* add the new contact */
 
1055
  individual_store_add_individual_and_connect (self, new_individual);
 
1056
 
 
1057
  /* remove old contact */
 
1058
  individual_store_remove_individual_and_disconnect (self, old_individual);
 
1059
}
 
1060
 
 
1061
static void
 
1062
individual_store_dispose (GObject *object)
 
1063
{
 
1064
  EmpathyIndividualStorePriv *priv = GET_PRIV (object);
 
1065
  GList *individuals, *l;
 
1066
 
 
1067
  if (priv->dispose_has_run)
 
1068
    return;
 
1069
  priv->dispose_has_run = TRUE;
 
1070
 
 
1071
  /* Cancel any pending avatar load operations */
 
1072
  for (l = priv->avatar_cancellables; l != NULL; l = l->next)
 
1073
    {
 
1074
      /* The cancellables are freed in individual_avatar_pixbuf_received_cb() */
 
1075
      g_cancellable_cancel (G_CANCELLABLE (l->data));
 
1076
    }
 
1077
  g_list_free (priv->avatar_cancellables);
 
1078
 
 
1079
  individuals = empathy_individual_manager_get_members (priv->manager);
 
1080
  for (l = individuals; l; l = l->next)
 
1081
    {
 
1082
      individual_store_disconnect_individual (EMPATHY_INDIVIDUAL_STORE (object),
 
1083
          FOLKS_INDIVIDUAL (l->data));
 
1084
    }
 
1085
  g_list_free (individuals);
 
1086
 
 
1087
  g_signal_handlers_disconnect_by_func (priv->manager,
 
1088
      G_CALLBACK (individual_store_member_renamed_cb), object);
 
1089
  g_signal_handlers_disconnect_by_func (priv->manager,
 
1090
      G_CALLBACK (individual_store_members_changed_cb), object);
 
1091
  g_signal_handlers_disconnect_by_func (priv->manager,
 
1092
      G_CALLBACK (individual_store_favourites_changed_cb), object);
 
1093
  g_signal_handlers_disconnect_by_func (priv->manager,
 
1094
      G_CALLBACK (individual_store_groups_changed_cb), object);
 
1095
  g_object_unref (priv->manager);
 
1096
 
 
1097
  if (priv->inhibit_active)
 
1098
    {
 
1099
      g_source_remove (priv->inhibit_active);
 
1100
    }
 
1101
 
 
1102
  if (priv->setup_idle_id != 0)
 
1103
    {
 
1104
      g_source_remove (priv->setup_idle_id);
 
1105
    }
 
1106
 
 
1107
  g_hash_table_destroy (priv->status_icons);
 
1108
  G_OBJECT_CLASS (empathy_individual_store_parent_class)->dispose (object);
 
1109
}
 
1110
 
 
1111
static void
 
1112
individual_store_get_property (GObject *object,
 
1113
    guint param_id,
 
1114
    GValue *value,
 
1115
    GParamSpec *pspec)
 
1116
{
 
1117
  EmpathyIndividualStorePriv *priv;
 
1118
 
 
1119
  priv = GET_PRIV (object);
 
1120
 
 
1121
  switch (param_id)
 
1122
    {
 
1123
    case PROP_INDIVIDUAL_MANAGER:
 
1124
      g_value_set_object (value, priv->manager);
 
1125
      break;
 
1126
    case PROP_SHOW_AVATARS:
 
1127
      g_value_set_boolean (value, priv->show_avatars);
 
1128
      break;
 
1129
    case PROP_SHOW_PROTOCOLS:
 
1130
      g_value_set_boolean (value, priv->show_protocols);
 
1131
      break;
 
1132
    case PROP_SHOW_GROUPS:
 
1133
      g_value_set_boolean (value, priv->show_groups);
 
1134
      break;
 
1135
    case PROP_IS_COMPACT:
 
1136
      g_value_set_boolean (value, priv->is_compact);
 
1137
      break;
 
1138
    case PROP_SORT_CRITERIUM:
 
1139
      g_value_set_enum (value, priv->sort_criterium);
 
1140
      break;
 
1141
    default:
 
1142
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
1143
      break;
 
1144
    };
 
1145
}
 
1146
 
 
1147
static void
 
1148
individual_store_set_property (GObject *object,
 
1149
    guint param_id,
 
1150
    const GValue *value,
 
1151
    GParamSpec *pspec)
 
1152
{
 
1153
  EmpathyIndividualStorePriv *priv;
 
1154
 
 
1155
  priv = GET_PRIV (object);
 
1156
 
 
1157
  switch (param_id)
 
1158
    {
 
1159
    case PROP_INDIVIDUAL_MANAGER:
 
1160
      individual_store_set_individual_manager (EMPATHY_INDIVIDUAL_STORE
 
1161
          (object), g_value_get_object (value));
 
1162
      break;
 
1163
    case PROP_SHOW_AVATARS:
 
1164
      empathy_individual_store_set_show_avatars (EMPATHY_INDIVIDUAL_STORE
 
1165
          (object), g_value_get_boolean (value));
 
1166
      break;
 
1167
    case PROP_SHOW_PROTOCOLS:
 
1168
      empathy_individual_store_set_show_protocols (EMPATHY_INDIVIDUAL_STORE
 
1169
          (object), g_value_get_boolean (value));
 
1170
      break;
 
1171
    case PROP_SHOW_GROUPS:
 
1172
      empathy_individual_store_set_show_groups (EMPATHY_INDIVIDUAL_STORE
 
1173
          (object), g_value_get_boolean (value));
 
1174
      break;
 
1175
    case PROP_IS_COMPACT:
 
1176
      empathy_individual_store_set_is_compact (EMPATHY_INDIVIDUAL_STORE
 
1177
          (object), g_value_get_boolean (value));
 
1178
      break;
 
1179
    case PROP_SORT_CRITERIUM:
 
1180
      empathy_individual_store_set_sort_criterium (EMPATHY_INDIVIDUAL_STORE
 
1181
          (object), g_value_get_enum (value));
 
1182
      break;
 
1183
    default:
 
1184
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 
1185
      break;
 
1186
    };
 
1187
}
 
1188
 
 
1189
static void
 
1190
empathy_individual_store_class_init (EmpathyIndividualStoreClass *klass)
 
1191
{
 
1192
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
1193
 
 
1194
  object_class->dispose = individual_store_dispose;
 
1195
  object_class->get_property = individual_store_get_property;
 
1196
  object_class->set_property = individual_store_set_property;
 
1197
 
 
1198
  g_object_class_install_property (object_class,
 
1199
      PROP_INDIVIDUAL_MANAGER,
 
1200
      g_param_spec_object ("individual-manager",
 
1201
          "The individual manager",
 
1202
          "The individual manager",
 
1203
          EMPATHY_TYPE_INDIVIDUAL_MANAGER,
 
1204
          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
1205
  g_object_class_install_property (object_class,
 
1206
      PROP_SHOW_AVATARS,
 
1207
      g_param_spec_boolean ("show-avatars",
 
1208
          "Show Avatars",
 
1209
          "Whether contact list should display "
 
1210
          "avatars for contacts", TRUE, G_PARAM_READWRITE));
 
1211
  g_object_class_install_property (object_class,
 
1212
      PROP_SHOW_PROTOCOLS,
 
1213
      g_param_spec_boolean ("show-protocols",
 
1214
          "Show Protocols",
 
1215
          "Whether contact list should display "
 
1216
          "protocols for contacts", FALSE, G_PARAM_READWRITE));
 
1217
  g_object_class_install_property (object_class,
 
1218
      PROP_SHOW_GROUPS,
 
1219
      g_param_spec_boolean ("show-groups",
 
1220
          "Show Groups",
 
1221
          "Whether contact list should display "
 
1222
          "contact groups", TRUE, G_PARAM_READWRITE));
 
1223
  g_object_class_install_property (object_class,
 
1224
      PROP_IS_COMPACT,
 
1225
      g_param_spec_boolean ("is-compact",
 
1226
          "Is Compact",
 
1227
          "Whether the contact list is in compact mode or not",
 
1228
          FALSE, G_PARAM_READWRITE));
 
1229
 
 
1230
  g_object_class_install_property (object_class,
 
1231
      PROP_SORT_CRITERIUM,
 
1232
      g_param_spec_enum ("sort-criterium",
 
1233
          "Sort citerium",
 
1234
          "The sort criterium to use for sorting the contact list",
 
1235
          EMPATHY_TYPE_INDIVIDUAL_STORE_SORT,
 
1236
          EMPATHY_INDIVIDUAL_STORE_SORT_NAME, G_PARAM_READWRITE));
 
1237
 
 
1238
  g_type_class_add_private (object_class,
 
1239
      sizeof (EmpathyIndividualStorePriv));
 
1240
}
 
1241
 
 
1242
static gint
 
1243
get_position (const char **strv,
 
1244
    const char *str)
 
1245
{
 
1246
  int i;
 
1247
 
 
1248
  for (i = 0; strv[i] != NULL; i++)
 
1249
    {
 
1250
      if (!tp_strdiff (strv[i], str))
 
1251
        return i;
 
1252
    }
 
1253
 
 
1254
  return -1;
 
1255
}
 
1256
 
 
1257
static gint
 
1258
compare_separator_and_groups (gboolean is_separator_a,
 
1259
    gboolean is_separator_b,
 
1260
    const gchar *name_a,
 
1261
    const gchar *name_b,
 
1262
    FolksIndividual *individual_a,
 
1263
    FolksIndividual *individual_b,
 
1264
    gboolean fake_group_a,
 
1265
    gboolean fake_group_b)
 
1266
{
 
1267
  /* these two lists are the sorted list of fake groups to include at the
 
1268
   * top and bottom of the roster */
 
1269
  const char *top_groups[] = {
 
1270
    EMPATHY_INDIVIDUAL_STORE_FAVORITE,
 
1271
    NULL
 
1272
  };
 
1273
 
 
1274
  const char *bottom_groups[] = {
 
1275
    EMPATHY_INDIVIDUAL_STORE_UNGROUPED,
 
1276
    NULL
 
1277
  };
 
1278
 
 
1279
  if (is_separator_a || is_separator_b)
 
1280
    {
 
1281
      /* We have at least one separator */
 
1282
      if (is_separator_a)
 
1283
        {
 
1284
          return -1;
 
1285
        }
 
1286
      else if (is_separator_b)
 
1287
        {
 
1288
          return 1;
 
1289
        }
 
1290
    }
 
1291
 
 
1292
  /* One group and one contact */
 
1293
  if (!individual_a && individual_b)
 
1294
    {
 
1295
      return 1;
 
1296
    }
 
1297
  else if (individual_a && !individual_b)
 
1298
    {
 
1299
      return -1;
 
1300
    }
 
1301
  else if (!individual_a && !individual_b)
 
1302
    {
 
1303
      gboolean a_in_top, b_in_top, a_in_bottom, b_in_bottom;
 
1304
 
 
1305
      a_in_top = fake_group_a && tp_strv_contains (top_groups, name_a);
 
1306
      b_in_top = fake_group_b && tp_strv_contains (top_groups, name_b);
 
1307
      a_in_bottom = fake_group_a && tp_strv_contains (bottom_groups, name_a);
 
1308
      b_in_bottom = fake_group_b && tp_strv_contains (bottom_groups, name_b);
 
1309
 
 
1310
      if (a_in_top && b_in_top)
 
1311
        {
 
1312
          /* compare positions */
 
1313
          return CLAMP (get_position (top_groups, name_a) -
 
1314
              get_position (top_groups, name_b), -1, 1);
 
1315
        }
 
1316
      else if (a_in_bottom && b_in_bottom)
 
1317
        {
 
1318
          /* compare positions */
 
1319
          return CLAMP (get_position (bottom_groups, name_a) -
 
1320
              get_position (bottom_groups, name_b), -1, 1);
 
1321
        }
 
1322
      else if (a_in_top || b_in_bottom)
 
1323
        {
 
1324
          return -1;
 
1325
        }
 
1326
      else if (b_in_top || a_in_bottom)
 
1327
        {
 
1328
          return 1;
 
1329
        }
 
1330
      else
 
1331
        {
 
1332
          return g_utf8_collate (name_a, name_b);
 
1333
        }
 
1334
    }
 
1335
 
 
1336
  /* Two contacts, ordering depends of the sorting policy */
 
1337
  return 0;
 
1338
}
 
1339
 
 
1340
static gint
 
1341
individual_store_contact_sort (FolksIndividual *individual_a,
 
1342
    FolksIndividual *individual_b)
 
1343
{
 
1344
  gint ret_val;
 
1345
  EmpathyContact *contact_a = NULL, *contact_b = NULL;
 
1346
  TpAccount *account_a, *account_b;
 
1347
 
 
1348
  g_return_val_if_fail (individual_a != NULL || individual_b != NULL, 0);
 
1349
 
 
1350
  /* alias */
 
1351
  ret_val = g_utf8_collate (
 
1352
      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual_a)),
 
1353
      folks_aliasable_get_alias (FOLKS_ALIASABLE (individual_b)));
 
1354
 
 
1355
  if (ret_val != 0)
 
1356
    goto out;
 
1357
 
 
1358
  contact_a = empathy_contact_dup_from_folks_individual (individual_a);
 
1359
  contact_b = empathy_contact_dup_from_folks_individual (individual_b);
 
1360
  account_a = empathy_contact_get_account (contact_a);
 
1361
  account_b = empathy_contact_get_account (contact_b);
 
1362
 
 
1363
  /* protocol */
 
1364
  ret_val = g_strcmp0 (tp_account_get_protocol (account_a),
 
1365
      tp_account_get_protocol (account_b));
 
1366
 
 
1367
  if (ret_val != 0)
 
1368
    goto out;
 
1369
 
 
1370
  /* account ID */
 
1371
  ret_val = g_strcmp0 (tp_proxy_get_object_path (account_a),
 
1372
      tp_proxy_get_object_path (account_b));
 
1373
 
 
1374
  if (ret_val != 0)
 
1375
    goto out;
 
1376
 
 
1377
  /* identifier */
 
1378
  ret_val = g_utf8_collate (folks_individual_get_id (individual_a),
 
1379
      folks_individual_get_id (individual_b));
 
1380
 
 
1381
out:
 
1382
  tp_clear_object (&contact_a);
 
1383
  tp_clear_object (&contact_b);
 
1384
 
 
1385
  return ret_val;
 
1386
}
 
1387
 
 
1388
static gint
 
1389
individual_store_state_sort_func (GtkTreeModel *model,
 
1390
    GtkTreeIter *iter_a,
 
1391
    GtkTreeIter *iter_b,
 
1392
    gpointer user_data)
 
1393
{
 
1394
  gint ret_val;
 
1395
  FolksIndividual *individual_a, *individual_b;
 
1396
  gchar *name_a, *name_b;
 
1397
  gboolean is_separator_a, is_separator_b;
 
1398
  gboolean fake_group_a, fake_group_b;
 
1399
  FolksPresenceType folks_presence_type_a, folks_presence_type_b;
 
1400
  TpConnectionPresenceType tp_presence_a, tp_presence_b;
 
1401
 
 
1402
  gtk_tree_model_get (model, iter_a,
 
1403
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_a,
 
1404
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_a,
 
1405
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_a,
 
1406
      EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1);
 
1407
  gtk_tree_model_get (model, iter_b,
 
1408
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_b,
 
1409
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_b,
 
1410
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_b,
 
1411
      EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1);
 
1412
 
 
1413
  if (individual_a == NULL || individual_b == NULL)
 
1414
    {
 
1415
      ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
 
1416
          name_a, name_b, individual_a, individual_b, fake_group_a,
 
1417
          fake_group_b);
 
1418
      goto free_and_out;
 
1419
    }
 
1420
 
 
1421
  /* If we managed to get this far, we can start looking at
 
1422
   * the presences.
 
1423
   */
 
1424
  folks_presence_type_a =
 
1425
      folks_presence_get_presence_type (FOLKS_PRESENCE (individual_a));
 
1426
  folks_presence_type_b =
 
1427
      folks_presence_get_presence_type (FOLKS_PRESENCE (individual_b));
 
1428
  tp_presence_a = empathy_folks_presence_type_to_tp (folks_presence_type_a);
 
1429
  tp_presence_b = empathy_folks_presence_type_to_tp (folks_presence_type_b);
 
1430
 
 
1431
  ret_val = -tp_connection_presence_type_cmp_availability (tp_presence_a,
 
1432
      tp_presence_b);
 
1433
 
 
1434
  if (ret_val == 0)
 
1435
    {
 
1436
      /* Fallback: compare by name et al. */
 
1437
      ret_val = individual_store_contact_sort (individual_a, individual_b);
 
1438
    }
 
1439
 
 
1440
free_and_out:
 
1441
  g_free (name_a);
 
1442
  g_free (name_b);
 
1443
  tp_clear_object (&individual_a);
 
1444
  tp_clear_object (&individual_b);
 
1445
 
 
1446
  return ret_val;
 
1447
}
 
1448
 
 
1449
static gint
 
1450
individual_store_name_sort_func (GtkTreeModel *model,
 
1451
    GtkTreeIter *iter_a,
 
1452
    GtkTreeIter *iter_b,
 
1453
    gpointer user_data)
 
1454
{
 
1455
  gchar *name_a, *name_b;
 
1456
  FolksIndividual *individual_a, *individual_b;
 
1457
  gboolean is_separator_a = FALSE, is_separator_b = FALSE;
 
1458
  gint ret_val;
 
1459
  gboolean fake_group_a, fake_group_b;
 
1460
 
 
1461
  gtk_tree_model_get (model, iter_a,
 
1462
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_a,
 
1463
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_a,
 
1464
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_a,
 
1465
      EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1);
 
1466
  gtk_tree_model_get (model, iter_b,
 
1467
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_b,
 
1468
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_b,
 
1469
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_b,
 
1470
      EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1);
 
1471
 
 
1472
  if (individual_a == NULL || individual_b == NULL)
 
1473
    ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
 
1474
        name_a, name_b, individual_a, individual_b, fake_group_a, fake_group_b);
 
1475
  else
 
1476
    ret_val = individual_store_contact_sort (individual_a, individual_b);
 
1477
 
 
1478
  tp_clear_object (&individual_a);
 
1479
  tp_clear_object (&individual_b);
 
1480
  g_free (name_a);
 
1481
  g_free (name_b);
 
1482
 
 
1483
  return ret_val;
 
1484
}
 
1485
 
 
1486
static void
 
1487
individual_store_setup (EmpathyIndividualStore *self)
 
1488
{
 
1489
  EmpathyIndividualStorePriv *priv;
 
1490
  GType types[] = {
 
1491
    GDK_TYPE_PIXBUF,            /* Status pixbuf */
 
1492
    GDK_TYPE_PIXBUF,            /* Avatar pixbuf */
 
1493
    G_TYPE_BOOLEAN,             /* Avatar pixbuf visible */
 
1494
    G_TYPE_STRING,              /* Name */
 
1495
    G_TYPE_UINT,                /* Presence type */
 
1496
    G_TYPE_STRING,              /* Status string */
 
1497
    G_TYPE_BOOLEAN,             /* Compact view */
 
1498
    FOLKS_TYPE_INDIVIDUAL,      /* Individual type */
 
1499
    G_TYPE_BOOLEAN,             /* Is group */
 
1500
    G_TYPE_BOOLEAN,             /* Is active */
 
1501
    G_TYPE_BOOLEAN,             /* Is online */
 
1502
    G_TYPE_BOOLEAN,             /* Is separator */
 
1503
    G_TYPE_BOOLEAN,             /* Can make audio calls */
 
1504
    G_TYPE_BOOLEAN,             /* Can make video calls */
 
1505
    EMPATHY_TYPE_INDIVIDUAL_MANAGER_FLAGS,      /* Flags */
 
1506
    G_TYPE_BOOLEAN,             /* Is a fake group */
 
1507
  };
 
1508
 
 
1509
  priv = GET_PRIV (self);
 
1510
 
 
1511
  gtk_tree_store_set_column_types (GTK_TREE_STORE (self),
 
1512
      EMPATHY_INDIVIDUAL_STORE_COL_COUNT, types);
 
1513
 
 
1514
  /* Set up sorting */
 
1515
  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self),
 
1516
      EMPATHY_INDIVIDUAL_STORE_COL_NAME,
 
1517
      individual_store_name_sort_func, self, NULL);
 
1518
  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self),
 
1519
      EMPATHY_INDIVIDUAL_STORE_COL_STATUS,
 
1520
      individual_store_state_sort_func, self, NULL);
 
1521
 
 
1522
  priv->sort_criterium = EMPATHY_INDIVIDUAL_STORE_SORT_NAME;
 
1523
  empathy_individual_store_set_sort_criterium (self, priv->sort_criterium);
 
1524
}
 
1525
 
 
1526
static gboolean
 
1527
individual_store_inhibit_active_cb (EmpathyIndividualStore *self)
 
1528
{
 
1529
  EmpathyIndividualStorePriv *priv;
 
1530
 
 
1531
  priv = GET_PRIV (self);
 
1532
 
 
1533
  priv->show_active = TRUE;
 
1534
  priv->inhibit_active = 0;
 
1535
 
 
1536
  return FALSE;
 
1537
}
 
1538
 
 
1539
static void
 
1540
empathy_individual_store_init (EmpathyIndividualStore *self)
 
1541
{
 
1542
  EmpathyIndividualStorePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
 
1543
      EMPATHY_TYPE_INDIVIDUAL_STORE, EmpathyIndividualStorePriv);
 
1544
 
 
1545
  self->priv = priv;
 
1546
  priv->show_avatars = TRUE;
 
1547
  priv->show_groups = TRUE;
 
1548
  priv->show_protocols = FALSE;
 
1549
  priv->inhibit_active =
 
1550
      g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME,
 
1551
      (GSourceFunc) individual_store_inhibit_active_cb, self);
 
1552
  priv->status_icons =
 
1553
      g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 
1554
  individual_store_setup (self);
 
1555
}
 
1556
 
 
1557
EmpathyIndividualStore *
 
1558
empathy_individual_store_new (EmpathyIndividualManager *manager)
 
1559
{
 
1560
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (manager), NULL);
 
1561
 
 
1562
  return g_object_new (EMPATHY_TYPE_INDIVIDUAL_STORE,
 
1563
      "individual-manager", manager, NULL);
 
1564
}
 
1565
 
 
1566
EmpathyIndividualManager *
 
1567
empathy_individual_store_get_manager (EmpathyIndividualStore *self)
 
1568
{
 
1569
  EmpathyIndividualStorePriv *priv;
 
1570
 
 
1571
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), FALSE);
 
1572
 
 
1573
  priv = GET_PRIV (self);
 
1574
 
 
1575
  return priv->manager;
 
1576
}
 
1577
 
 
1578
gboolean
 
1579
empathy_individual_store_get_show_avatars (EmpathyIndividualStore *self)
 
1580
{
 
1581
  EmpathyIndividualStorePriv *priv;
 
1582
 
 
1583
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
 
1584
 
 
1585
  priv = GET_PRIV (self);
 
1586
 
 
1587
  return priv->show_avatars;
 
1588
}
 
1589
 
 
1590
static gboolean
 
1591
individual_store_update_list_mode_foreach (GtkTreeModel *model,
 
1592
    GtkTreePath *path,
 
1593
    GtkTreeIter *iter,
 
1594
    EmpathyIndividualStore *self)
 
1595
{
 
1596
  EmpathyIndividualStorePriv *priv;
 
1597
  gboolean show_avatar = FALSE;
 
1598
  FolksIndividual *individual;
 
1599
  GdkPixbuf *pixbuf_status;
 
1600
 
 
1601
  priv = GET_PRIV (self);
 
1602
 
 
1603
  if (priv->show_avatars && !priv->is_compact)
 
1604
    {
 
1605
      show_avatar = TRUE;
 
1606
    }
 
1607
 
 
1608
  gtk_tree_model_get (model, iter,
 
1609
      EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, -1);
 
1610
 
 
1611
  if (individual == NULL)
 
1612
    {
 
1613
      return FALSE;
 
1614
    }
 
1615
  /* get icon from hash_table */
 
1616
  pixbuf_status =
 
1617
      empathy_individual_store_get_individual_status_icon (self, individual);
 
1618
 
 
1619
  gtk_tree_store_set (GTK_TREE_STORE (self), iter,
 
1620
      EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf_status,
 
1621
      EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
 
1622
      EMPATHY_INDIVIDUAL_STORE_COL_COMPACT, priv->is_compact, -1);
 
1623
 
 
1624
  g_object_unref (individual);
 
1625
 
 
1626
  return FALSE;
 
1627
}
 
1628
 
 
1629
void
 
1630
empathy_individual_store_set_show_avatars (EmpathyIndividualStore *self,
 
1631
    gboolean show_avatars)
 
1632
{
 
1633
  EmpathyIndividualStorePriv *priv;
 
1634
  GtkTreeModel *model;
 
1635
 
 
1636
  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
 
1637
 
 
1638
  priv = GET_PRIV (self);
 
1639
 
 
1640
  priv->show_avatars = show_avatars;
 
1641
 
 
1642
  model = GTK_TREE_MODEL (self);
 
1643
 
 
1644
  gtk_tree_model_foreach (model,
 
1645
      (GtkTreeModelForeachFunc)
 
1646
      individual_store_update_list_mode_foreach, self);
 
1647
 
 
1648
  g_object_notify (G_OBJECT (self), "show-avatars");
 
1649
}
 
1650
 
 
1651
gboolean
 
1652
empathy_individual_store_get_show_protocols (EmpathyIndividualStore *self)
 
1653
{
 
1654
  EmpathyIndividualStorePriv *priv;
 
1655
 
 
1656
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
 
1657
 
 
1658
  priv = GET_PRIV (self);
 
1659
 
 
1660
  return priv->show_protocols;
 
1661
}
 
1662
 
 
1663
void
 
1664
empathy_individual_store_set_show_protocols (EmpathyIndividualStore *self,
 
1665
    gboolean show_protocols)
 
1666
{
 
1667
  EmpathyIndividualStorePriv *priv;
 
1668
  GtkTreeModel *model;
 
1669
 
 
1670
  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
 
1671
 
 
1672
  priv = GET_PRIV (self);
 
1673
 
 
1674
  priv->show_protocols = show_protocols;
 
1675
 
 
1676
  model = GTK_TREE_MODEL (self);
 
1677
 
 
1678
  gtk_tree_model_foreach (model,
 
1679
      (GtkTreeModelForeachFunc)
 
1680
      individual_store_update_list_mode_foreach, self);
 
1681
 
 
1682
  g_object_notify (G_OBJECT (self), "show-protocols");
 
1683
}
 
1684
 
 
1685
gboolean
 
1686
empathy_individual_store_get_show_groups (EmpathyIndividualStore *self)
 
1687
{
 
1688
  EmpathyIndividualStorePriv *priv;
 
1689
 
 
1690
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
 
1691
 
 
1692
  priv = GET_PRIV (self);
 
1693
 
 
1694
  return priv->show_groups;
 
1695
}
 
1696
 
 
1697
void
 
1698
empathy_individual_store_set_show_groups (EmpathyIndividualStore *self,
 
1699
    gboolean show_groups)
 
1700
{
 
1701
  EmpathyIndividualStorePriv *priv;
 
1702
 
 
1703
  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
 
1704
 
 
1705
  priv = GET_PRIV (self);
 
1706
 
 
1707
  if (priv->show_groups == show_groups)
 
1708
    {
 
1709
      return;
 
1710
    }
 
1711
 
 
1712
  priv->show_groups = show_groups;
 
1713
 
 
1714
  if (priv->setup_idle_id == 0)
 
1715
    {
 
1716
      /* Remove all contacts and add them back, not optimized but
 
1717
       * that's the easy way :)
 
1718
       *
 
1719
       * This is only done if there's not a pending setup idle
 
1720
       * callback, otherwise it will race and the contacts will get
 
1721
       * added twice */
 
1722
      GList *contacts;
 
1723
 
 
1724
      gtk_tree_store_clear (GTK_TREE_STORE (self));
 
1725
      contacts = empathy_individual_manager_get_members (priv->manager);
 
1726
 
 
1727
      individual_store_members_changed_cb (priv->manager,
 
1728
          "re-adding members: toggled group visibility",
 
1729
          contacts, NULL, 0, self);
 
1730
      g_list_free (contacts);
 
1731
    }
 
1732
 
 
1733
  g_object_notify (G_OBJECT (self), "show-groups");
 
1734
}
 
1735
 
 
1736
gboolean
 
1737
empathy_individual_store_get_is_compact (EmpathyIndividualStore *self)
 
1738
{
 
1739
  EmpathyIndividualStorePriv *priv;
 
1740
 
 
1741
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
 
1742
 
 
1743
  priv = GET_PRIV (self);
 
1744
 
 
1745
  return priv->is_compact;
 
1746
}
 
1747
 
 
1748
void
 
1749
empathy_individual_store_set_is_compact (EmpathyIndividualStore *self,
 
1750
    gboolean is_compact)
 
1751
{
 
1752
  EmpathyIndividualStorePriv *priv;
 
1753
  GtkTreeModel *model;
 
1754
 
 
1755
  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
 
1756
 
 
1757
  priv = GET_PRIV (self);
 
1758
 
 
1759
  priv->is_compact = is_compact;
 
1760
 
 
1761
  model = GTK_TREE_MODEL (self);
 
1762
 
 
1763
  gtk_tree_model_foreach (model,
 
1764
      (GtkTreeModelForeachFunc)
 
1765
      individual_store_update_list_mode_foreach, self);
 
1766
 
 
1767
  g_object_notify (G_OBJECT (self), "is-compact");
 
1768
}
 
1769
 
 
1770
EmpathyIndividualStoreSort
 
1771
empathy_individual_store_get_sort_criterium (EmpathyIndividualStore *self)
 
1772
{
 
1773
  EmpathyIndividualStorePriv *priv;
 
1774
 
 
1775
  g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), 0);
 
1776
 
 
1777
  priv = GET_PRIV (self);
 
1778
 
 
1779
  return priv->sort_criterium;
 
1780
}
 
1781
 
 
1782
void
 
1783
empathy_individual_store_set_sort_criterium (EmpathyIndividualStore *self,
 
1784
    EmpathyIndividualStoreSort sort_criterium)
 
1785
{
 
1786
  EmpathyIndividualStorePriv *priv;
 
1787
 
 
1788
  g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
 
1789
 
 
1790
  priv = GET_PRIV (self);
 
1791
 
 
1792
  priv->sort_criterium = sort_criterium;
 
1793
 
 
1794
  switch (sort_criterium)
 
1795
    {
 
1796
    case EMPATHY_INDIVIDUAL_STORE_SORT_STATE:
 
1797
      gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
 
1798
          EMPATHY_INDIVIDUAL_STORE_COL_STATUS, GTK_SORT_ASCENDING);
 
1799
      break;
 
1800
 
 
1801
    case EMPATHY_INDIVIDUAL_STORE_SORT_NAME:
 
1802
      gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
 
1803
          EMPATHY_INDIVIDUAL_STORE_COL_NAME, GTK_SORT_ASCENDING);
 
1804
      break;
 
1805
 
 
1806
    default:
 
1807
      g_assert_not_reached ();
 
1808
    }
 
1809
 
 
1810
  g_object_notify (G_OBJECT (self), "sort-criterium");
 
1811
}
 
1812
 
 
1813
gboolean
 
1814
empathy_individual_store_row_separator_func (GtkTreeModel *model,
 
1815
    GtkTreeIter *iter,
 
1816
    gpointer data)
 
1817
{
 
1818
  gboolean is_separator = FALSE;
 
1819
 
 
1820
  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
 
1821
 
 
1822
  gtk_tree_model_get (model, iter,
 
1823
      EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator, -1);
 
1824
 
 
1825
  return is_separator;
 
1826
}
 
1827
 
 
1828
gchar *
 
1829
empathy_individual_store_get_parent_group (GtkTreeModel *model,
 
1830
    GtkTreePath *path,
 
1831
    gboolean *path_is_group,
 
1832
    gboolean *is_fake_group)
 
1833
{
 
1834
  GtkTreeIter parent_iter, iter;
 
1835
  gchar *name = NULL;
 
1836
  gboolean is_group;
 
1837
  gboolean fake;
 
1838
 
 
1839
  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
 
1840
 
 
1841
  if (path_is_group)
 
1842
    {
 
1843
      *path_is_group = FALSE;
 
1844
    }
 
1845
 
 
1846
  if (!gtk_tree_model_get_iter (model, &iter, path))
 
1847
    {
 
1848
      return NULL;
 
1849
    }
 
1850
 
 
1851
  gtk_tree_model_get (model, &iter,
 
1852
      EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group,
 
1853
      EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name, -1);
 
1854
 
 
1855
  if (!is_group)
 
1856
    {
 
1857
      g_free (name);
 
1858
      name = NULL;
 
1859
 
 
1860
      if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
 
1861
        {
 
1862
          return NULL;
 
1863
        }
 
1864
 
 
1865
      iter = parent_iter;
 
1866
 
 
1867
      gtk_tree_model_get (model, &iter,
 
1868
          EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group,
 
1869
          EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name,
 
1870
          EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake, -1);
 
1871
      if (!is_group)
 
1872
        {
 
1873
          g_free (name);
 
1874
          return NULL;
 
1875
        }
 
1876
    }
 
1877
 
 
1878
  if (path_is_group)
 
1879
    {
 
1880
      *path_is_group = TRUE;
 
1881
    }
 
1882
 
 
1883
  if (is_fake_group != NULL)
 
1884
    *is_fake_group = fake;
 
1885
 
 
1886
  return name;
 
1887
}
 
1888
 
 
1889
static GdkPixbuf *
 
1890
individual_store_get_individual_status_icon_with_icon_name (
 
1891
    EmpathyIndividualStore *self,
 
1892
    FolksIndividual *individual,
 
1893
    const gchar *status_icon_name)
 
1894
{
 
1895
  GdkPixbuf *pixbuf_status = NULL;
 
1896
  EmpathyIndividualStorePriv *priv;
 
1897
  const gchar *protocol_name = NULL;
 
1898
  gchar *icon_name = NULL;
 
1899
  GList *personas, *l;
 
1900
  guint contact_count;
 
1901
  EmpathyContact *contact = NULL;
 
1902
  gboolean show_protocols_here;
 
1903
 
 
1904
  priv = GET_PRIV (self);
 
1905
 
 
1906
  personas = folks_individual_get_personas (individual);
 
1907
  for (l = personas, contact_count = 0; l; l = l->next)
 
1908
    {
 
1909
      if (TPF_IS_PERSONA (l->data))
 
1910
        contact_count++;
 
1911
 
 
1912
      if (contact_count > 1)
 
1913
        break;
 
1914
    }
 
1915
 
 
1916
  show_protocols_here = (priv->show_protocols && (contact_count == 1));
 
1917
  if (show_protocols_here)
 
1918
    {
 
1919
      contact = empathy_contact_dup_from_folks_individual (individual);
 
1920
      protocol_name = empathy_protocol_name_for_contact (contact);
 
1921
      icon_name = g_strdup_printf ("%s-%s", status_icon_name, protocol_name);
 
1922
    }
 
1923
  else
 
1924
    {
 
1925
      icon_name = g_strdup_printf ("%s", status_icon_name);
 
1926
    }
 
1927
  if (pixbuf_status == NULL)
 
1928
    {
 
1929
      pixbuf_status =
 
1930
          empathy_pixbuf_contact_status_icon_with_icon_name (contact,
 
1931
          status_icon_name, show_protocols_here);
 
1932
      if (pixbuf_status != NULL)
 
1933
        {
 
1934
          g_hash_table_insert (priv->status_icons,
 
1935
              g_strdup (icon_name), pixbuf_status);
 
1936
        }
 
1937
    }
 
1938
 
 
1939
  g_free (icon_name);
 
1940
  tp_clear_object (&contact);
 
1941
 
 
1942
  return pixbuf_status;
 
1943
}
 
1944
 
 
1945
GdkPixbuf *
 
1946
empathy_individual_store_get_individual_status_icon (
 
1947
    EmpathyIndividualStore *self,
 
1948
    FolksIndividual *individual)
 
1949
{
 
1950
  GdkPixbuf *pixbuf_status = NULL;
 
1951
  const gchar *status_icon_name = NULL;
 
1952
 
 
1953
  status_icon_name = empathy_icon_name_for_individual (individual);
 
1954
  if (status_icon_name == NULL)
 
1955
    return NULL;
 
1956
 
 
1957
  pixbuf_status =
 
1958
      individual_store_get_individual_status_icon_with_icon_name (self,
 
1959
      individual, status_icon_name);
 
1960
 
 
1961
  return pixbuf_status;
 
1962
}