~ubuntu-branches/ubuntu/utopic/gossip/utopic

« back to all changes in this revision

Viewing changes to src/gossip-contact-list.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2007-05-30 21:16:05 UTC
  • mfrom: (1.1.19 upstream)
  • Revision ID: james.westby@ubuntu.com-20070530211605-fwsaooscfnmujkqd
Tags: 0.26-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 * License along with this program; if not, write to the
17
17
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
18
 * Boston, MA 02111-1307, USA.
19
 
 *
20
 
 * Authors: Mikael Hallendal <micke@imendio.com>
21
 
 *          Martyn Russell <martyn@imendio.com>
22
19
 */
23
20
 
24
21
#include "config.h"
35
32
#include <libgossip/gossip-session.h>
36
33
#include <libgossip/gossip-message.h>
37
34
#include <libgossip/gossip-event-manager.h>
 
35
#include <libgossip/gossip-stock.h>
38
36
#include <libgossip/gossip-utils.h>
39
37
 
40
38
#include "gossip-app.h"
45
43
#include "gossip-contact-info-dialog.h"
46
44
#include "gossip-contact-list.h"
47
45
#include "gossip-edit-contact-dialog.h"
 
46
#include "gossip-email.h"
48
47
#include "gossip-ft-window.h"
49
48
#include "gossip-log-window.h"
50
49
#include "gossip-marshal.h"
51
50
#include "gossip-sound.h"
52
 
#include "gossip-stock.h"
53
51
#include "gossip-ui-utils.h"
54
52
 
55
53
#define DEBUG_DOMAIN "ContactList"
56
54
 
57
 
/* Flashing delay for icons (milliseconds). */
58
 
#define FLASH_TIMEOUT 500
59
 
 
60
55
/* Active users are those which have recently changed state
61
56
 * (e.g. online, offline or from normal to a busy state).
62
57
 */
70
65
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CONTACT_LIST, GossipContactListPriv))
71
66
 
72
67
struct _GossipContactListPriv {
73
 
        GossipSession       *session;
74
 
 
75
 
        GHashTable          *groups;
76
 
        GHashTable          *flash_table;
77
 
 
78
 
        GtkUIManager        *ui;
79
 
 
80
 
        GtkTreeRowReference *drag_row;
81
 
 
82
 
        gboolean             show_offline;
83
 
        gboolean             show_avatars;
84
 
        gboolean             is_compact;
85
 
        gboolean             show_active;
 
68
        GossipSession         *session;
 
69
 
 
70
        GHashTable            *groups;
 
71
        GHashTable            *flash_table;
 
72
 
 
73
        GtkUIManager          *ui;
 
74
 
 
75
        GtkTreeRowReference   *drag_row;
 
76
 
 
77
        GtkTreeStore          *store;
 
78
        GtkTreeModel          *filter;
 
79
        gchar                 *filter_text;
 
80
 
 
81
        gboolean               show_offline;
 
82
        gboolean               show_avatars;
 
83
        gboolean               is_compact;
 
84
        gboolean               show_active;
 
85
 
 
86
        gboolean               flash_on;
 
87
        guint                  flash_heartbeat_id;
 
88
 
 
89
        GossipContactListSort sort_criterium;
86
90
};
87
91
 
88
92
typedef struct {
89
 
        gint            event_id;
90
 
        GossipEventType event_type;
91
 
        gboolean        flash_on;
92
 
        guint           flash_timeout_id;
93
 
} FlashData;
94
 
 
95
 
typedef struct {
96
 
        GossipContactList *list;
97
 
        GossipContact     *contact;
98
 
} FlashTimeoutData;
99
 
 
100
 
typedef struct {
 
93
        GtkTreeIter  iter;
101
94
        const gchar *name;
102
95
        gboolean     found;
103
 
        GtkTreeIter  iter;
104
96
} FindGroup;
105
97
 
106
98
typedef struct {
243
235
                                                              GossipContactList      *list);
244
236
static GtkWidget *contact_list_get_contact_menu              (GossipContactList      *list,
245
237
                                                              gboolean                can_send_file,
246
 
                                                              gboolean                can_show_log);
 
238
                                                              gboolean                can_show_log,
 
239
                                                              gboolean                can_email);
247
240
static gboolean contact_list_button_press_event_cb           (GossipContactList      *list,
248
241
                                                              GdkEventButton         *event,
249
242
                                                              gpointer                user_data);
255
248
                                                              GtkTreeIter            *iter,
256
249
                                                              GtkTreePath            *path,
257
250
                                                              gpointer                user_data);
258
 
static gint     contact_list_sort_func                       (GtkTreeModel           *model,
259
 
                                                              GtkTreeIter            *iter_a,
260
 
                                                              GtkTreeIter            *iter_b,
261
 
                                                              gpointer                user_data);
 
251
static gint     contact_list_name_sort_func                  (GtkTreeModel           *model,
 
252
                                                              GtkTreeIter            *iter_a,
 
253
                                                              GtkTreeIter            *iter_b,
 
254
                                                              gpointer                user_data);
 
255
static gint     contact_list_state_sort_func                 (GtkTreeModel           *model,
 
256
                                                              GtkTreeIter            *iter_a,
 
257
                                                              GtkTreeIter            *iter_b,
 
258
                                                              gpointer                user_data);
 
259
static gboolean contact_list_filter_func                     (GtkTreeModel           *model,
 
260
                                                              GtkTreeIter            *iter,
 
261
                                                              GossipContactList      *list);
262
262
static GList *  contact_list_find_contact                    (GossipContactList      *list,
263
263
                                                              GossipContact          *contact);
264
264
static gboolean contact_list_find_contact_foreach            (GtkTreeModel           *model,
277
277
static void     contact_list_action_remove_selected          (GossipContactList      *list);
278
278
static void     contact_list_action_invite_selected          (GossipContactList      *list);
279
279
static void     contact_list_action_rename_group_selected    (GossipContactList      *list);
280
 
static void     contact_list_free_flash_timeout_data         (FlashTimeoutData       *timeout_data);
281
 
static void     contact_list_flash_free_data                 (FlashData              *data);
282
 
static gboolean contact_list_flash_timeout_func              (FlashTimeoutData       *timeout_data);
283
280
static void     contact_list_event_added_cb                  (GossipEventManager     *manager,
284
281
                                                              GossipEvent            *event,
285
282
                                                              GossipContactList      *list);
290
287
                                                              GtkTreePath            *path,
291
288
                                                              GtkTreeIter            *iter,
292
289
                                                              GossipContactList      *list);
 
290
static void     contact_list_ensure_flash_heartbeat          (GossipContactList      *list);
 
291
static void     contact_list_flash_heartbeat_maybe_stop      (GossipContactList      *list);
293
292
 
294
293
enum {
295
294
        CONTACT_ACTIVATED,
319
318
        PROP_SHOW_OFFLINE,
320
319
        PROP_SHOW_AVATARS,
321
320
        PROP_IS_COMPACT,
 
321
        PROP_FILTER,
 
322
        PROP_SORT_CRITERIUM
322
323
};
323
324
 
324
325
static const GtkActionEntry entries[] = {
358
359
          N_("_Send File..."), NULL, N_("Send a file"),
359
360
          G_CALLBACK (contact_list_action_cb)
360
361
        },
 
362
        { "Email", NULL,
 
363
          N_("Emai_l..."), NULL, N_("Email contact"),
 
364
          G_CALLBACK (contact_list_action_cb)
 
365
        },
361
366
        { "Log", GTK_STOCK_JUSTIFY_LEFT,
362
367
          N_("_View Previous Conversations"), NULL, N_("View previous conversations with this contact"),
363
368
          G_CALLBACK (contact_list_action_cb)
372
377
        "    <menuitem action='Chat'/>"
373
378
        "    <menuitem action='Log'/>"
374
379
        "    <menuitem action='SendFile'/>"
 
380
        "    <menuitem action='Email'/>"
375
381
        "    <separator/>"
376
382
        "    <menuitem action='Invite'/>"
377
383
        "    <separator/>"
405
411
static GdkAtom drag_atoms_dest[G_N_ELEMENTS (drag_types_dest)];
406
412
static GdkAtom drag_atoms_source[G_N_ELEMENTS (drag_types_source)];
407
413
 
 
414
GType
 
415
gossip_contact_list_sort_get_type (void)
 
416
{
 
417
        static GType etype = 0;
 
418
 
 
419
        if (etype == 0) {
 
420
                static const GEnumValue values[] = {
 
421
                        { GOSSIP_CONTACT_LIST_SORT_NAME, 
 
422
                          "GOSSIP_CONTACT_LIST_SORT_NAME", 
 
423
                          "name" },
 
424
                        { GOSSIP_CONTACT_LIST_SORT_STATE, 
 
425
                          "GOSSIP_CONTACT_LIST_SORT_STATE", 
 
426
                          "state" },
 
427
                        { 0, NULL, NULL }
 
428
                };
 
429
 
 
430
                etype = g_enum_register_static ("GossipContactListSort", values);
 
431
        }
 
432
 
 
433
        return etype;
 
434
}
 
435
 
408
436
G_DEFINE_TYPE (GossipContactList, gossip_contact_list, GTK_TYPE_TREE_VIEW);
409
437
 
410
438
static void
452
480
                                                               FALSE,
453
481
                                                               G_PARAM_READWRITE));
454
482
 
 
483
        g_object_class_install_property (object_class,
 
484
                                         PROP_FILTER,
 
485
                                         g_param_spec_string ("filter",
 
486
                                                              "Filter",
 
487
                                                              "The text to use to filter the contact list",
 
488
                                                              NULL,
 
489
                                                              G_PARAM_READWRITE));
 
490
 
 
491
        g_object_class_install_property (object_class,
 
492
                                         PROP_SORT_CRITERIUM,
 
493
                                         g_param_spec_enum ("sort-criterium",
 
494
                                                             "Sort citerium",
 
495
                                                             "The sort criterium to use for sorting the contact list",
 
496
                                                             GOSSIP_TYPE_CONTACT_LIST_SORT,
 
497
                                                             GOSSIP_CONTACT_LIST_SORT_NAME,
 
498
                                                             G_PARAM_READWRITE));
 
499
 
455
500
        g_type_class_add_private (object_class, sizeof (GossipContactListPriv));
456
501
}
457
502
 
474
519
 
475
520
        priv->is_compact = FALSE;
476
521
 
 
522
        priv->flash_on = TRUE;
477
523
        priv->flash_table = g_hash_table_new_full (gossip_contact_hash,
478
524
                                                   gossip_contact_equal,
479
525
                                                   (GDestroyNotify) g_object_unref,
480
 
                                                   (GDestroyNotify) contact_list_flash_free_data);
 
526
                                                   (GDestroyNotify) g_object_unref);
481
527
 
482
528
        contact_list_create_model (list);
483
529
        contact_list_setup_view (list);
571
617
 
572
618
        g_hash_table_destroy (priv->flash_table);
573
619
 
 
620
        g_free (priv->filter_text);
 
621
 
574
622
        g_object_unref (priv->ui);
575
623
 
 
624
        g_object_unref (priv->store);
 
625
        g_object_unref (priv->filter);
 
626
 
576
627
        G_OBJECT_CLASS (gossip_contact_list_parent_class)->finalize (object);
577
628
}
578
629
 
596
647
        case PROP_IS_COMPACT:
597
648
                g_value_set_boolean (value, priv->is_compact);
598
649
                break;
 
650
        case PROP_FILTER:
 
651
                g_value_set_string (value, priv->filter_text);
 
652
                break;
 
653
        case PROP_SORT_CRITERIUM:
 
654
                g_value_set_enum (value, priv->sort_criterium);
 
655
                break;
599
656
        default:
600
657
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
601
658
                break;
625
682
                gossip_contact_list_set_is_compact (GOSSIP_CONTACT_LIST (object),
626
683
                                                    g_value_get_boolean (value));
627
684
                break;
 
685
        case PROP_FILTER:
 
686
                gossip_contact_list_set_filter (GOSSIP_CONTACT_LIST (object),
 
687
                                                g_value_get_string (value));
 
688
                break;
 
689
        case PROP_SORT_CRITERIUM:
 
690
                gossip_contact_list_set_sort_criterium (GOSSIP_CONTACT_LIST (object),
 
691
                                                        g_value_get_enum (value));
 
692
                break;
628
693
        default:
629
694
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
630
695
                break;
695
760
 
696
761
        priv = GET_PRIV (list);
697
762
 
698
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
763
        model = GTK_TREE_MODEL (priv->store);
699
764
 
700
765
        type = gossip_contact_get_type (contact);
701
766
        if (type != GOSSIP_CONTACT_TYPE_CONTACTLIST) {
783
848
 
784
849
                /* Get online state before. */
785
850
                if (iters && g_list_length (iters) > 0) {
786
 
                        GtkTreeIter *iter;
787
 
 
788
 
                        iter = g_list_nth_data (iters, 0);
789
 
                        gtk_tree_model_get (model, iter, COL_IS_ONLINE, &was_online, -1);
 
851
                        gtk_tree_model_get (model, iters->data, COL_IS_ONLINE, &was_online, -1);
790
852
                }
791
853
 
792
854
                /* Is this really an update or an online/offline. */
817
879
        }
818
880
 
819
881
        pixbuf_presence = gossip_pixbuf_for_contact (contact);
820
 
        pixbuf_composing = gossip_pixbuf_from_stock (GOSSIP_STOCK_TYPING,
821
 
                                                     GTK_ICON_SIZE_MENU);
822
 
        pixbuf_avatar = gossip_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
 
882
        pixbuf_composing = gossip_stock_create_pixbuf (gossip_app_get_window (),
 
883
                                                       GOSSIP_STOCK_TYPING,
 
884
                                                       GTK_ICON_SIZE_MENU);
 
885
        pixbuf_avatar = gossip_contact_get_avatar_pixbuf (contact);
 
886
        if (pixbuf_avatar) {
 
887
                g_object_ref (pixbuf_avatar);
 
888
        }
823
889
 
824
890
        pixbuf = pixbuf_presence;
825
891
 
832
898
                        pixbuf = pixbuf_composing;
833
899
                }
834
900
 
835
 
                gtk_tree_store_set (GTK_TREE_STORE (model), l->data,
 
901
                gtk_tree_store_set (priv->store, l->data,
836
902
                                    COL_PIXBUF_STATUS, pixbuf,
837
903
                                    COL_STATUS, gossip_contact_get_status (contact),
838
904
                                    COL_IS_ONLINE, now_online,
991
1057
 
992
1058
        priv = GET_PRIV (list);
993
1059
 
994
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
1060
        model = GTK_TREE_MODEL (priv->store);
995
1061
 
996
1062
        if (! g_hash_table_lookup (priv->flash_table, contact)) {
997
1063
                if (composing) {
998
 
                        pixbuf = gossip_pixbuf_from_stock (GOSSIP_STOCK_TYPING,
999
 
                                                           GTK_ICON_SIZE_MENU);
 
1064
                        pixbuf = gossip_stock_create_pixbuf (gossip_app_get_window (),
 
1065
                                                             GOSSIP_STOCK_TYPING,
 
1066
                                                             GTK_ICON_SIZE_MENU);
1000
1067
                } else {
1001
1068
                        pixbuf = gossip_pixbuf_for_contact (contact);
1002
1069
                }
1005
1072
        iters = contact_list_find_contact (list, contact);
1006
1073
        for (l = iters; l; l = l->next) {
1007
1074
                GtkTreePath *path;
1008
 
                GtkTreeIter *iter;
1009
 
 
1010
 
                iter = l->data;
1011
 
 
1012
 
                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
 
1075
 
 
1076
                gtk_tree_store_set (priv->store, l->data,
1013
1077
                                    COL_IS_COMPOSING, composing,
1014
1078
                                    -1);
1015
1079
 
1016
1080
                if (pixbuf) {
1017
 
                        gtk_tree_store_set (GTK_TREE_STORE (model), iter,
 
1081
                        gtk_tree_store_set (priv->store, l->data,
1018
1082
                                            COL_PIXBUF_STATUS, pixbuf,
1019
1083
                                            -1);
1020
1084
                }
1021
1085
 
1022
 
                path = gtk_tree_model_get_path (model, iter);
1023
 
                gtk_tree_model_row_changed (model, path, iter);
 
1086
                path = gtk_tree_model_get_path (model, l->data);
 
1087
                gtk_tree_model_row_changed (model, path, l->data);
1024
1088
                gtk_tree_path_free (path);
1025
1089
        }
1026
1090
 
1028
1092
                g_object_unref (pixbuf);
1029
1093
        }
1030
1094
 
1031
 
        g_list_foreach (iters, (GFunc)gtk_tree_iter_free, NULL);
 
1095
        g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1032
1096
        g_list_free (iters);
1033
1097
}
1034
1098
 
1038
1102
                                 gboolean           active,
1039
1103
                                 gboolean           set_changed)
1040
1104
{
1041
 
        GtkTreeModel *model;
1042
 
        GList        *iters, *l;
1043
 
 
1044
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
1105
        GossipContactListPriv *priv;
 
1106
        GtkTreeModel          *model;
 
1107
        GList                 *iters, *l;
 
1108
 
 
1109
        priv = GET_PRIV (list);
 
1110
 
 
1111
        model = GTK_TREE_MODEL (priv->store);
1045
1112
 
1046
1113
        iters = contact_list_find_contact (list, contact);
1047
1114
        for (l = iters; l; l = l->next) {
1048
1115
                GtkTreePath *path;
1049
 
                GtkTreeIter *iter;
1050
 
 
1051
 
                iter = l->data;
1052
 
 
1053
 
                gtk_tree_store_set (GTK_TREE_STORE (model), iter,
 
1116
 
 
1117
                gtk_tree_store_set (priv->store, l->data,
1054
1118
                                    COL_IS_ACTIVE, active,
1055
1119
                                    -1);
 
1120
 
1056
1121
                gossip_debug (DEBUG_DOMAIN, "Set item %s", active ? "active" : "inactive");
1057
1122
 
1058
1123
                if (set_changed) {
1059
 
                        path = gtk_tree_model_get_path (model, iter);
1060
 
                        gtk_tree_model_row_changed (model, path, iter);
 
1124
                        path = gtk_tree_model_get_path (model, l->data);
 
1125
                        gtk_tree_model_row_changed (model, path, l->data);
1061
1126
                        gtk_tree_path_free (path);
1062
1127
                }
1063
1128
        }
1181
1246
        return name;
1182
1247
}
1183
1248
 
 
1249
static gboolean
 
1250
contact_list_get_group_foreach (GtkTreeModel *model,
 
1251
                                GtkTreePath  *path,
 
1252
                                GtkTreeIter  *iter,
 
1253
                                FindGroup    *fg)
 
1254
{
 
1255
        gchar    *str;
 
1256
        gboolean  is_group;
 
1257
 
 
1258
        /* Groups are only at the top level. */
 
1259
        if (gtk_tree_path_get_depth (path) != 1) {
 
1260
                return FALSE;
 
1261
        }
 
1262
 
 
1263
        gtk_tree_model_get (model, iter,
 
1264
                            COL_NAME, &str,
 
1265
                            COL_IS_GROUP, &is_group,
 
1266
                            -1);
 
1267
 
 
1268
        if (is_group && strcmp (str, fg->name) == 0) {
 
1269
                fg->found = TRUE;
 
1270
                fg->iter = *iter;
 
1271
        }
 
1272
 
 
1273
        g_free (str);
 
1274
 
 
1275
        return fg->found;
 
1276
}
 
1277
 
1184
1278
static void
1185
1279
contact_list_get_group (GossipContactList *list,
1186
1280
                        const gchar       *name,
1188
1282
                        GtkTreeIter       *iter_separator_to_set,
1189
1283
                        gboolean          *created)
1190
1284
{
1191
 
        GtkTreeModel *model;
1192
 
        GtkTreeIter   iter_group, iter_separator;
1193
 
        FindGroup     fg;
 
1285
        GossipContactListPriv *priv;
 
1286
        GtkTreeModel          *model;
 
1287
        GtkTreeIter            iter_group, iter_separator;
 
1288
        FindGroup              fg;
 
1289
 
 
1290
        priv = GET_PRIV (list);
1194
1291
 
1195
1292
        memset (&fg, 0, sizeof (fg));
1196
1293
 
1197
1294
        fg.name = name;
1198
1295
 
1199
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
1296
        model = GTK_TREE_MODEL (priv->store);
1200
1297
        gtk_tree_model_foreach (model,
1201
1298
                                (GtkTreeModelForeachFunc) contact_list_get_group_foreach,
1202
1299
                                &fg);
1206
1303
                        *created = TRUE;
1207
1304
                }
1208
1305
 
1209
 
                gtk_tree_store_append (GTK_TREE_STORE (model), &iter_group, NULL);
1210
 
                gtk_tree_store_set (GTK_TREE_STORE (model), &iter_group,
 
1306
                gtk_tree_store_append (priv->store, &iter_group, NULL);
 
1307
                gtk_tree_store_set (priv->store, &iter_group,
1211
1308
                                    COL_PIXBUF_STATUS, NULL,
1212
1309
                                    COL_NAME, name,
1213
1310
                                    COL_IS_GROUP, TRUE,
1220
1317
                        *iter_group_to_set = iter_group;
1221
1318
                }
1222
1319
 
1223
 
                gtk_tree_store_append (GTK_TREE_STORE (model), 
 
1320
                gtk_tree_store_append (priv->store,
1224
1321
                                       &iter_separator, 
1225
1322
                                       &iter_group);
1226
 
                gtk_tree_store_set (GTK_TREE_STORE (model), &iter_separator,
 
1323
                gtk_tree_store_set (priv->store, &iter_separator,
1227
1324
                                    COL_IS_SEPARATOR, TRUE,
1228
1325
                                    -1);
1229
1326
 
1255
1352
        }
1256
1353
}
1257
1354
 
1258
 
static gboolean
1259
 
contact_list_get_group_foreach (GtkTreeModel *model,
1260
 
                                GtkTreePath  *path,
1261
 
                                GtkTreeIter  *iter,
1262
 
                                FindGroup    *fg)
1263
 
{
1264
 
        gchar    *str;
1265
 
        gboolean  is_group;
1266
 
 
1267
 
        /* Groups are only at the top level. */
1268
 
        if (gtk_tree_path_get_depth (path) != 1) {
1269
 
                return FALSE;
1270
 
        }
1271
 
 
1272
 
        gtk_tree_model_get (model, iter,
1273
 
                            COL_NAME, &str,
1274
 
                            COL_IS_GROUP, &is_group,
1275
 
                            -1);
1276
 
 
1277
 
        if (is_group && strcmp (str, fg->name) == 0) {
1278
 
                fg->found = TRUE;
1279
 
                fg->iter = *iter;
1280
 
        }
1281
 
 
1282
 
        g_free (str);
1283
 
 
1284
 
        return fg->found;
1285
 
}
1286
 
 
1287
1355
static void
1288
1356
contact_list_add_contact (GossipContactList *list,
1289
1357
                          GossipContact     *contact)
1305
1373
                gboolean   show_avatar = FALSE;
1306
1374
 
1307
1375
                pixbuf_status = gossip_pixbuf_for_contact (contact);
1308
 
                pixbuf_avatar = gossip_pixbuf_avatar_from_contact_scaled (
1309
 
                        contact, 32, 32);
 
1376
                pixbuf_avatar = gossip_contact_get_avatar_pixbuf (contact);
 
1377
                if (pixbuf_avatar) {
 
1378
                        g_object_ref (pixbuf_avatar);
 
1379
                }
1310
1380
 
1311
1381
                if (priv->show_avatars && !priv->is_compact) {
1312
1382
                        show_avatar = TRUE;
1313
1383
                }
1314
1384
 
1315
 
                gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
1316
 
                gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
 
1385
                gossip_debug (DEBUG_DOMAIN, "");
 
1386
                gossip_debug (DEBUG_DOMAIN, 
 
1387
                              "vvvvvvvvvvvvvvvv FIXME: Errors may follow below (since filter work) vvvvvvvvvvvvvvvv");
 
1388
 
 
1389
                gossip_debug (DEBUG_DOMAIN, 
 
1390
                              "**** GossipContact:%p, is GObject:%s, is GossipContact:%s, ADDING CONTACT #1",
 
1391
                              contact,
 
1392
                              G_IS_OBJECT (contact) ? "yes" : "no",
 
1393
                              GOSSIP_IS_CONTACT (contact) ? "yes" : "no");
 
1394
 
 
1395
                gtk_tree_store_append (priv->store, &iter, NULL);
 
1396
                gtk_tree_store_set (priv->store, &iter,
1317
1397
                                    COL_PIXBUF_STATUS, pixbuf_status,
1318
1398
                                    COL_PIXBUF_AVATAR, pixbuf_avatar,
1319
1399
                                    COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1328
1408
                                    COL_IS_SEPARATOR, FALSE,
1329
1409
                                    -1);
1330
1410
 
 
1411
                gossip_debug (DEBUG_DOMAIN, 
 
1412
                              "^^^^^^^^^^^^^^^^ FIXME: Errors may occur above  (since filter work) ^^^^^^^^^^^^^^^^");
 
1413
                gossip_debug (DEBUG_DOMAIN, "");
 
1414
 
1331
1415
                if (pixbuf_avatar) {
1332
1416
                        g_object_unref (pixbuf_avatar);
1333
1417
                }
1338
1422
        /* Else add to each group. */
1339
1423
        for (l = groups; l; l = l->next) {
1340
1424
                GtkTreePath *path;
 
1425
                GtkTreeIter  model_iter_group;
1341
1426
                GdkPixbuf   *pixbuf_status;
1342
1427
                GdkPixbuf   *pixbuf_avatar;
1343
1428
                const gchar *name;
1344
1429
                gboolean     created;
 
1430
                gboolean     found;
1345
1431
                gboolean     show_avatar = FALSE;
1346
1432
 
1347
1433
                name = l->data;
1350
1436
                }
1351
1437
 
1352
1438
                pixbuf_status = gossip_pixbuf_for_contact (contact);
1353
 
                pixbuf_avatar = gossip_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
 
1439
                pixbuf_avatar = gossip_contact_get_avatar_pixbuf (contact);
 
1440
                if (pixbuf_avatar) {
 
1441
                        g_object_ref (pixbuf_avatar);
 
1442
                }
1354
1443
 
1355
1444
                contact_list_get_group (list, name, &iter_group, &iter_separator, &created);
1356
1445
 
1358
1447
                        show_avatar = TRUE;
1359
1448
                }
1360
1449
 
1361
 
                /* Add separator */
1362
 
                gtk_tree_store_insert_after (GTK_TREE_STORE (model),
1363
 
                                             &iter, &iter_group, NULL);
1364
 
                gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
 
1450
                gossip_debug (DEBUG_DOMAIN, "");
 
1451
                gossip_debug (DEBUG_DOMAIN, 
 
1452
                              "vvvvvvvvvvvvvvvv FIXME: Errors may follow below (since filter work) vvvvvvvvvvvvvvvv");
 
1453
 
 
1454
                gossip_debug (DEBUG_DOMAIN, 
 
1455
                              "**** GossipContact:%p, is GObject:%s, is GossipContact:%s, ADDING CONTACT #2",
 
1456
                              contact,
 
1457
                              G_IS_OBJECT (contact) ? "yes" : "no",
 
1458
                              GOSSIP_IS_CONTACT (contact) ? "yes" : "no");
 
1459
 
 
1460
                gtk_tree_store_insert_after (priv->store, &iter, &iter_group, NULL);
 
1461
                gtk_tree_store_set (priv->store, &iter,
1365
1462
                                    COL_PIXBUF_STATUS, pixbuf_status,
1366
1463
                                    COL_PIXBUF_AVATAR, pixbuf_avatar,
1367
1464
                                    COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1376
1473
                                    COL_IS_SEPARATOR, FALSE,
1377
1474
                                    -1);
1378
1475
 
 
1476
                gossip_debug (DEBUG_DOMAIN, 
 
1477
                              "^^^^^^^^^^^^^^^^ FIXME: Errors may occur above  (since filter work) ^^^^^^^^^^^^^^^^");
 
1478
                gossip_debug (DEBUG_DOMAIN, "");
 
1479
 
1379
1480
                if (pixbuf_avatar) {
1380
1481
                        g_object_unref (pixbuf_avatar);
1381
1482
                }
1386
1487
                        continue;
1387
1488
                }
1388
1489
 
1389
 
                path = gtk_tree_model_get_path (model, &iter_group);
 
1490
                found = gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter),  
 
1491
                                                                          &model_iter_group,  
 
1492
                                                                          &iter_group); 
 
1493
                if (!found) {
 
1494
                        continue;
 
1495
                }
 
1496
                
 
1497
                path = gtk_tree_model_get_path (model, &model_iter_group);
1390
1498
                if (!path) {
1391
1499
                        continue;
1392
1500
                }
1430
1538
        }
1431
1539
 
1432
1540
        /* Clean up model */
1433
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
1541
        model = GTK_TREE_MODEL (priv->store);
1434
1542
 
1435
1543
        for (l = iters; l; l = l->next) {
1436
1544
                GtkTreeIter parent;
1441
1549
                 */
1442
1550
                if (gtk_tree_model_iter_parent (model, &parent, l->data) &&
1443
1551
                    gtk_tree_model_iter_n_children (model, &parent) <= 2) {
1444
 
                        gtk_tree_store_remove (GTK_TREE_STORE (model), &parent);
 
1552
                        gtk_tree_store_remove (priv->store, &parent);
1445
1553
                } else {
1446
 
                        gtk_tree_store_remove (GTK_TREE_STORE (model), l->data);
 
1554
                        gtk_tree_store_remove (priv->store, l->data);
1447
1555
                }
1448
1556
        }
1449
1557
 
1450
 
        g_list_foreach (iters, (GFunc)gtk_tree_iter_free, NULL);
 
1558
        g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1451
1559
        g_list_free (iters);
1452
1560
 
1453
1561
        if (remove_flash) {
1458
1566
static void
1459
1567
contact_list_create_model (GossipContactList *list)
1460
1568
{
1461
 
        GtkTreeModel *model;
1462
 
 
1463
 
        model = GTK_TREE_MODEL (
1464
 
                gtk_tree_store_new (COL_COUNT,
1465
 
                                    GDK_TYPE_PIXBUF,     /* Status pixbuf */
1466
 
                                    GDK_TYPE_PIXBUF,     /* Avatar pixbuf */
1467
 
                                    G_TYPE_BOOLEAN,      /* Avatar pixbuf visible */
1468
 
                                    G_TYPE_STRING,       /* Name */
1469
 
                                    G_TYPE_STRING,       /* Status string */
1470
 
                                    G_TYPE_BOOLEAN,      /* Show status */
1471
 
                                    GOSSIP_TYPE_CONTACT, /* Contact type */
1472
 
                                    G_TYPE_BOOLEAN,      /* Is group */
1473
 
                                    G_TYPE_BOOLEAN,      /* Is active */
1474
 
                                    G_TYPE_BOOLEAN,      /* Is online */
1475
 
                                    G_TYPE_BOOLEAN,      /* Is composing */
1476
 
                                    G_TYPE_BOOLEAN));    /* Is separator */
1477
 
 
 
1569
        GossipContactListPriv *priv;
 
1570
        GtkTreeModel          *model;
 
1571
        
 
1572
        priv = GET_PRIV (list);
 
1573
 
 
1574
        if (priv->store) {
 
1575
                g_object_unref (priv->store);
 
1576
        }
 
1577
 
 
1578
        if (priv->filter) {
 
1579
                g_object_unref (priv->filter);
 
1580
        }
 
1581
 
 
1582
        priv->store = gtk_tree_store_new (COL_COUNT,
 
1583
                                          GDK_TYPE_PIXBUF,     /* Status pixbuf */
 
1584
                                          GDK_TYPE_PIXBUF,     /* Avatar pixbuf */
 
1585
                                          G_TYPE_BOOLEAN,      /* Avatar pixbuf visible */
 
1586
                                          G_TYPE_STRING,       /* Name */
 
1587
                                          G_TYPE_STRING,       /* Status string */
 
1588
                                          G_TYPE_BOOLEAN,      /* Show status */
 
1589
                                          GOSSIP_TYPE_CONTACT, /* Contact type */
 
1590
                                          G_TYPE_BOOLEAN,      /* Is group */
 
1591
                                          G_TYPE_BOOLEAN,      /* Is active */
 
1592
                                          G_TYPE_BOOLEAN,      /* Is online */
 
1593
                                          G_TYPE_BOOLEAN,      /* Is composing */
 
1594
                                          G_TYPE_BOOLEAN);     /* Is separator */
 
1595
 
 
1596
        /* Save normal model */
 
1597
        model = GTK_TREE_MODEL (priv->store);
 
1598
 
 
1599
        /* Set up sorting */
1478
1600
        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
1479
1601
                                         COL_NAME,
1480
 
                                         contact_list_sort_func,
1481
 
                                         list, NULL);
1482
 
 
1483
 
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
1484
 
                                              COL_NAME,
1485
 
                                              GTK_SORT_ASCENDING);
1486
 
 
1487
 
        gtk_tree_view_set_model (GTK_TREE_VIEW (list), model);
1488
 
 
1489
 
        g_object_unref (model);
 
1602
                                         contact_list_name_sort_func,
 
1603
                                         list, NULL);
 
1604
        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
 
1605
                                         COL_STATUS,
 
1606
                                         contact_list_state_sort_func,
 
1607
                                         list, NULL);
 
1608
 
 
1609
        gossip_contact_list_set_sort_criterium (list, priv->sort_criterium);
 
1610
 
 
1611
        /* Create filter */
 
1612
        priv->filter = gtk_tree_model_filter_new (model, NULL);
 
1613
 
 
1614
        gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter),
 
1615
                                                (GtkTreeModelFilterVisibleFunc)
 
1616
                                                contact_list_filter_func,
 
1617
                                                list, NULL);
 
1618
 
 
1619
        gtk_tree_view_set_model (GTK_TREE_VIEW (list), priv->filter);
1490
1620
}
1491
1621
 
1492
1622
static gboolean
1988
2118
                                      NULL);
1989
2119
                }
1990
2120
        } else {
 
2121
                g_object_set (cell,
 
2122
                              "cell-background-gdk", NULL,
 
2123
                              NULL);
1991
2124
#if 0
1992
2125
                gint color_sum_normal;
1993
2126
                gint color_sum_selected;
2138
2271
static GtkWidget *
2139
2272
contact_list_get_contact_menu (GossipContactList *list,
2140
2273
                               gboolean           can_send_file,
2141
 
                               gboolean           can_show_log)
 
2274
                               gboolean           can_show_log,
 
2275
                               gboolean           can_email)
2142
2276
{
2143
2277
        GossipContactListPriv *priv;
2144
2278
        GtkAction             *action;
2154
2288
        action = gtk_ui_manager_get_action (priv->ui, "/Contact/SendFile");
2155
2289
        gtk_action_set_visible (action, can_send_file);
2156
2290
 
 
2291
        action = gtk_ui_manager_get_action (priv->ui, "/Contact/Email");
 
2292
        gtk_action_set_sensitive (action, can_email);
 
2293
 
2157
2294
        widget = gtk_ui_manager_get_widget (priv->ui, "/Contact");
2158
2295
 
2159
2296
        return widget;
2178
2315
gossip_contact_list_get_contact_menu (GossipContactList *list,
2179
2316
                                      GossipContact     *contact)
2180
2317
{
2181
 
        GtkWidget *menu;
2182
 
        gboolean   can_show_log;
2183
 
        gboolean   can_send_file;
 
2318
        GtkWidget   *menu;
 
2319
        gboolean     can_show_log;
 
2320
        gboolean     can_send_file;
 
2321
        gboolean     can_email;
2184
2322
 
2185
2323
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), NULL);
2186
2324
        g_return_val_if_fail (GOSSIP_IS_CONTACT (contact), NULL);
2187
2325
 
2188
2326
        can_show_log = gossip_log_exists_for_contact (contact);
2189
2327
        can_send_file = FALSE;
 
2328
        can_email = gossip_email_available (contact);
2190
2329
 
2191
2330
        menu = contact_list_get_contact_menu (list,
2192
2331
                                              can_send_file,
2193
 
                                              can_show_log);
 
2332
                                              can_show_log,
 
2333
                                              can_email);
2194
2334
        return menu;
2195
2335
}
2196
2336
 
2301
2441
}
2302
2442
 
2303
2443
static gint
2304
 
contact_list_sort_func (GtkTreeModel *model,
2305
 
                        GtkTreeIter  *iter_a,
2306
 
                        GtkTreeIter  *iter_b,
2307
 
                        gpointer      user_data)
 
2444
contact_list_state_sort_func (GtkTreeModel *model,
 
2445
                              GtkTreeIter  *iter_a,
 
2446
                              GtkTreeIter  *iter_b,
 
2447
                              gpointer      user_data)
 
2448
{
 
2449
        gint                 ret_val = 0;
 
2450
        gchar               *name_a, *name_b;
 
2451
        gboolean             is_separator_a, is_separator_b;
 
2452
        GossipContact       *contact_a, *contact_b;
 
2453
        GossipPresence      *presence_a, *presence_b;
 
2454
        GossipPresenceState  state_a, state_b;
 
2455
 
 
2456
        gtk_tree_model_get (model, iter_a,
 
2457
                            COL_NAME, &name_a,
 
2458
                            COL_CONTACT, &contact_a,
 
2459
                            COL_IS_SEPARATOR, &is_separator_a,
 
2460
                            -1);
 
2461
        gtk_tree_model_get (model, iter_b,
 
2462
                            COL_NAME, &name_b,
 
2463
                            COL_CONTACT, &contact_b,
 
2464
                            COL_IS_SEPARATOR, &is_separator_b,
 
2465
                            -1);
 
2466
 
 
2467
        /* Separator or group? */
 
2468
        if (is_separator_a || is_separator_b) {
 
2469
                if (is_separator_a) {
 
2470
                        ret_val = -1;
 
2471
                } else if (is_separator_b) {
 
2472
                        ret_val = 1;
 
2473
                }
 
2474
        } else if (!contact_a && contact_b) {
 
2475
                ret_val = 1;
 
2476
        } else if (contact_a && !contact_b) {
 
2477
                ret_val = -1;
 
2478
        } else if (!contact_a && !contact_b) {
 
2479
                /* Handle groups */
 
2480
                ret_val = g_utf8_collate (name_a, name_b);
 
2481
        }
 
2482
 
 
2483
        if (ret_val) {
 
2484
                goto free_and_out;
 
2485
        }
 
2486
 
 
2487
        /* If we managed to get this far, we can start looking at
 
2488
         * the presences.
 
2489
         */
 
2490
        presence_a = gossip_contact_get_active_presence (GOSSIP_CONTACT (contact_a));
 
2491
        presence_b = gossip_contact_get_active_presence (GOSSIP_CONTACT (contact_b));
 
2492
 
 
2493
        if (!presence_a && presence_b) {
 
2494
                ret_val = 1;
 
2495
        } else if (presence_a && !presence_b) {
 
2496
                ret_val = -1;
 
2497
        } else if (!presence_a && !presence_b) {
 
2498
                /* Both offline, sort by name */
 
2499
                ret_val = g_utf8_collate (name_a, name_b);
 
2500
        } else {
 
2501
                state_a = gossip_presence_get_state (presence_a);
 
2502
                state_b = gossip_presence_get_state (presence_b);
 
2503
 
 
2504
                if (state_a < state_b) {
 
2505
                        ret_val = -1;
 
2506
                } else if (state_a > state_b) {
 
2507
                        ret_val = 1;
 
2508
                } else {
 
2509
                        /* Fallback: compare by name */
 
2510
                        ret_val = g_utf8_collate (name_a, name_b);
 
2511
                }
 
2512
        }
 
2513
 
 
2514
free_and_out:
 
2515
        g_free (name_a);
 
2516
        g_free (name_b);
 
2517
 
 
2518
        if (contact_a) {
 
2519
                g_object_unref (contact_a);
 
2520
        }
 
2521
 
 
2522
        if (contact_b) {
 
2523
                g_object_unref (contact_b);
 
2524
        }
 
2525
 
 
2526
        return ret_val;
 
2527
}
 
2528
 
 
2529
static gint
 
2530
contact_list_name_sort_func (GtkTreeModel *model,
 
2531
                             GtkTreeIter  *iter_a,
 
2532
                             GtkTreeIter  *iter_b,
 
2533
                             gpointer      user_data)
2308
2534
{
2309
2535
        gchar         *name_a, *name_b;
2310
2536
        GossipContact *contact_a, *contact_b;
2352
2578
        return ret_val;
2353
2579
}
2354
2580
 
 
2581
static gboolean 
 
2582
contact_list_filter_show_contact (GossipContact *contact,
 
2583
                                  const gchar   *filter)
 
2584
{
 
2585
        gchar    *str;
 
2586
        gboolean  visible;
 
2587
 
 
2588
        /* Check contact id */
 
2589
        str = g_utf8_casefold (gossip_contact_get_id (contact), -1);
 
2590
        visible = G_STR_EMPTY (str) || strstr (str, filter);
 
2591
        g_free (str);
 
2592
 
 
2593
        if (visible) {
 
2594
                return TRUE;
 
2595
        }
 
2596
 
 
2597
        /* Check contact name */
 
2598
        str = g_utf8_casefold (gossip_contact_get_name (contact), -1);
 
2599
        visible = G_STR_EMPTY (str) || strstr (str, filter);
 
2600
        g_free (str);
 
2601
        
 
2602
        return visible;
 
2603
}
 
2604
 
 
2605
static gboolean
 
2606
contact_list_filter_show_group (GossipContactList *list,
 
2607
                                const gchar       *group,
 
2608
                                const gchar       *filter)
 
2609
{
 
2610
        GossipContactListPriv *priv;
 
2611
        const GList           *contacts;
 
2612
        const GList           *l;
 
2613
        gchar                 *str;
 
2614
        gboolean               show_group = FALSE;
 
2615
 
 
2616
        priv = GET_PRIV (list);
 
2617
        
 
2618
        str = g_utf8_casefold (group, -1);
 
2619
        if (!str) {
 
2620
                return FALSE;
 
2621
        }
 
2622
 
 
2623
        /* If the filter is the partially the group name, we show the
 
2624
         * whole group.
 
2625
         */
 
2626
        if (strstr (str, filter)) {
 
2627
                g_free (str);
 
2628
                return TRUE;
 
2629
        }
 
2630
 
 
2631
        /* At this point, we need to check in advance if this
 
2632
         * group should be shown because a contact we want to
 
2633
         * show exists in it.
 
2634
         */
 
2635
        contacts = gossip_session_get_contacts (priv->session);
 
2636
        for (l = contacts; l && !show_group; l = l->next) {
 
2637
                if (!gossip_contact_is_in_group (l->data, group)) {
 
2638
                        continue;
 
2639
                }
 
2640
 
 
2641
                if (contact_list_filter_show_contact (l->data, filter)) {
 
2642
                        show_group = TRUE;
 
2643
                }
 
2644
        }
 
2645
 
 
2646
        g_free (str);
 
2647
 
 
2648
        return show_group;
 
2649
}
 
2650
 
 
2651
static gboolean
 
2652
contact_list_filter_func (GtkTreeModel      *model,
 
2653
                          GtkTreeIter       *iter,
 
2654
                          GossipContactList *list)
 
2655
{
 
2656
        GossipContactListPriv *priv;
 
2657
        gboolean               is_group;
 
2658
        gboolean               is_separator;
 
2659
        gboolean               visible = TRUE;
 
2660
 
 
2661
        priv = GET_PRIV (list);
 
2662
 
 
2663
        if (G_STR_EMPTY (priv->filter_text)) {
 
2664
                return TRUE;
 
2665
        }
 
2666
        
 
2667
        /* Check to see if iter matches any group names */
 
2668
        gtk_tree_model_get (model, iter,
 
2669
                            COL_IS_GROUP, &is_group,
 
2670
                            COL_IS_SEPARATOR, &is_separator,
 
2671
                            -1);
 
2672
 
 
2673
        if (is_group) {
 
2674
                gchar *name;
 
2675
 
 
2676
                gtk_tree_model_get (model, iter, COL_NAME, &name, -1);
 
2677
                visible &= contact_list_filter_show_group (list, 
 
2678
                                                           name, 
 
2679
                                                           priv->filter_text);
 
2680
                g_free (name);
 
2681
        } else if (is_separator) {
 
2682
                /* Do nothing here */
 
2683
        } else {
 
2684
                GossipContact *contact;
 
2685
 
 
2686
                /* Check contact id */
 
2687
                gtk_tree_model_get (model, iter, COL_CONTACT, &contact, -1);
 
2688
                visible &= contact_list_filter_show_contact (contact, 
 
2689
                                                             priv->filter_text);
 
2690
                g_object_unref (contact);
 
2691
        }
 
2692
 
 
2693
        return visible;
 
2694
}
 
2695
 
2355
2696
static gboolean
2356
2697
contact_list_iter_equal_contact (GtkTreeModel  *model,
2357
2698
                                 GtkTreeIter   *iter,
2374
2715
        return equal;
2375
2716
}
2376
2717
 
 
2718
static gboolean
 
2719
contact_list_find_contact_foreach (GtkTreeModel *model,
 
2720
                                   GtkTreePath  *path,
 
2721
                                   GtkTreeIter  *iter,
 
2722
                                   FindContact  *fc)
 
2723
{
 
2724
        if (contact_list_iter_equal_contact (model, iter, fc->contact)) {
 
2725
                fc->found = TRUE;
 
2726
                fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
 
2727
        }
 
2728
 
 
2729
        /* We want to find ALL contacts that match, this means if we
 
2730
         * have the same contact in 3 groups, all iters should be
 
2731
         * returned.
 
2732
         */
 
2733
        return FALSE;
 
2734
}
 
2735
 
2377
2736
static GList *
2378
2737
contact_list_find_contact (GossipContactList *list,
2379
2738
                           GossipContact     *contact)
2380
2739
{
2381
 
        GtkTreeModel *model;
2382
 
        FindContact   fc;
2383
 
        GList        *l = NULL;
 
2740
        GossipContactListPriv *priv;
 
2741
        GtkTreeModel          *model;
 
2742
        GList                 *l = NULL;
 
2743
        FindContact            fc;
 
2744
 
 
2745
        priv = GET_PRIV (list);
2384
2746
 
2385
2747
        memset (&fc, 0, sizeof (fc));
2386
2748
 
2387
2749
        fc.contact = contact;
2388
2750
 
2389
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
2751
        model = GTK_TREE_MODEL (priv->store);
2390
2752
        gtk_tree_model_foreach (model,
2391
2753
                                (GtkTreeModelForeachFunc) contact_list_find_contact_foreach,
2392
2754
                                &fc);
2398
2760
        return l;
2399
2761
}
2400
2762
 
2401
 
static gboolean
2402
 
contact_list_find_contact_foreach (GtkTreeModel *model,
2403
 
                                   GtkTreePath  *path,
2404
 
                                   GtkTreeIter  *iter,
2405
 
                                   FindContact  *fc)
2406
 
{
2407
 
        if (contact_list_iter_equal_contact (model, iter, fc->contact)) {
2408
 
                fc->found = TRUE;
2409
 
                fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
2410
 
        }
2411
 
 
2412
 
        /* We want to find ALL contacts that match, this means if we
2413
 
         * have the same contact in 3 groups, all iters should be
2414
 
         * returned.
2415
 
         */
2416
 
        return FALSE;
2417
 
}
2418
 
 
2419
2763
static void
2420
2764
contact_list_action_cb (GtkAction         *action,
2421
2765
                        GossipContactList *list)
2507
2851
 
2508
2852
                return;
2509
2853
        }
 
2854
 
 
2855
        if (strcmp (name, "Email") == 0) {
 
2856
                GossipContact *contact;
 
2857
 
 
2858
                contact = gossip_contact_list_get_selected (list);
 
2859
                if (contact) {
 
2860
                        gossip_email_open (contact);
 
2861
                        g_object_unref (contact);
 
2862
                }
 
2863
 
 
2864
                return;
 
2865
        }
2510
2866
}
2511
2867
 
2512
2868
static void
2514
2870
                               GossipContact     *contact)
2515
2871
{
2516
2872
        GossipContactListPriv *priv;
2517
 
        FlashData             *data;
 
2873
        GossipEvent           *event;
2518
2874
        gint                   event_id = 0;
2519
2875
 
2520
2876
        priv = GET_PRIV (list);
2521
2877
 
2522
 
        data = g_hash_table_lookup (priv->flash_table, contact);
2523
 
        if (data) {
2524
 
                event_id = data->event_id;
 
2878
        event = g_hash_table_lookup (priv->flash_table, contact);
 
2879
        if (event) {
 
2880
                event_id = gossip_event_get_id (event);
2525
2881
        }
2526
2882
 
2527
2883
        g_signal_emit (list, signals[CONTACT_ACTIVATED], 0, contact, event_id);
2695
3051
}
2696
3052
 
2697
3053
static void
2698
 
contact_list_free_flash_timeout_data (FlashTimeoutData *timeout_data)
2699
 
{
2700
 
        g_return_if_fail (timeout_data != NULL);
2701
 
 
2702
 
        gossip_debug (DEBUG_DOMAIN,
2703
 
                      "Contact:'%s' Cleaning up event flash data",
2704
 
                      gossip_contact_get_name (timeout_data->contact));
2705
 
 
2706
 
        g_object_unref (timeout_data->contact);
2707
 
 
2708
 
        g_slice_free (FlashTimeoutData, timeout_data);
2709
 
}
2710
 
 
2711
 
static void
2712
 
contact_list_flash_free_data (FlashData *data)
2713
 
{
2714
 
        g_return_if_fail (data != NULL);
2715
 
 
2716
 
        if (data->flash_timeout_id) {
2717
 
                g_source_remove (data->flash_timeout_id);
2718
 
        }
2719
 
 
2720
 
        g_slice_free (FlashData, data);
2721
 
}
2722
 
 
 
3054
contact_list_foreach_contact_flash (GossipContact     *contact,
 
3055
                                    GossipEvent       *event,
 
3056
                                    GossipContactList *list)
 
3057
{
 
3058
        GossipContactListPriv *priv;
 
3059
        GList                 *l, *iters;
 
3060
        GtkTreeModel          *model;
 
3061
        GdkPixbuf             *pixbuf = NULL;
 
3062
        const gchar           *stock_id = NULL;
 
3063
 
 
3064
        priv = GET_PRIV (list);
 
3065
 
 
3066
        iters = contact_list_find_contact (list, contact);
 
3067
        if (!iters) {
 
3068
                gossip_debug (DEBUG_DOMAIN,
 
3069
                              "Contact:'%s' not found in treeview?",
 
3070
                              gossip_contact_get_name (contact));
 
3071
 
 
3072
                return;
 
3073
        }
 
3074
 
 
3075
        if (priv->flash_on) {
 
3076
                stock_id = gossip_event_get_stock_id (event);
 
3077
        }
 
3078
 
 
3079
        if (stock_id) {
 
3080
                pixbuf = gossip_stock_create_pixbuf (gossip_app_get_window (),
 
3081
                                                     stock_id, 
 
3082
                                                     GTK_ICON_SIZE_MENU);
 
3083
        } else {
 
3084
                pixbuf = gossip_pixbuf_for_contact (contact);
 
3085
        }
 
3086
 
 
3087
        model = GTK_TREE_MODEL (priv->store);
 
3088
 
 
3089
        for (l = iters; l; l = l->next) {
 
3090
                gtk_tree_store_set (priv->store, l->data,
 
3091
                                    COL_PIXBUF_STATUS, pixbuf,
 
3092
                                    -1);
 
3093
        }
 
3094
 
 
3095
        g_object_unref (pixbuf);
 
3096
 
 
3097
        g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
 
3098
        g_list_free (iters);
 
3099
}
 
3100
 
 
3101
#if 0
2723
3102
static gboolean
2724
 
contact_list_flash_timeout_func (FlashTimeoutData *timeout_data)
 
3103
contact_list_flash (FlashTimeoutData *timeout_data)
2725
3104
{
2726
3105
        GossipContactList     *list;
2727
3106
        GossipContactListPriv *priv;
2753
3132
                retval = TRUE;
2754
3133
 
2755
3134
                if (data->flash_on) {
2756
 
                        switch (data->event_type) {
2757
 
                        case GOSSIP_EVENT_NEW_MESSAGE:
2758
 
                        case GOSSIP_EVENT_SERVER_MESSAGE:
2759
 
                                stock_id = GOSSIP_STOCK_MESSAGE;
2760
 
                                break;
2761
 
                        case GOSSIP_EVENT_FILE_TRANSFER_REQUEST:
2762
 
                        case GOSSIP_EVENT_SUBSCRIPTION_REQUEST:
2763
 
                                stock_id = GTK_STOCK_DIALOG_QUESTION;
2764
 
                                break;
2765
 
                        default:
2766
 
                                /* Shouldn't happen */
2767
 
                                stock_id = GTK_STOCK_DIALOG_WARNING;
2768
 
                                break;
2769
 
                        }
 
3135
                        stock_id = gossip_event_get_stock_id (data->event);
2770
3136
                }
2771
3137
        } else {
2772
3138
                gossip_debug (DEBUG_DOMAIN,
2780
3146
                pixbuf = gossip_pixbuf_for_contact (contact);
2781
3147
        }
2782
3148
 
2783
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
3149
        model = GTK_TREE_MODEL (priv->store);
2784
3150
 
2785
3151
        for (l = iters; l; l = l->next) {
2786
 
                gtk_tree_store_set (GTK_TREE_STORE (model), l->data,
 
3152
                gtk_tree_store_set (priv->store, l->data,
2787
3153
                                    COL_PIXBUF_STATUS, pixbuf,
2788
3154
                                    -1);
2789
3155
        }
2794
3160
        g_list_free (iters);
2795
3161
 
2796
3162
        return retval;
 
3163
 
 
3164
}
 
3165
#endif
 
3166
 
 
3167
static gboolean
 
3168
contact_list_flash_heartbeat_func (GossipHeartbeat  *heartbeat,
 
3169
                                   gpointer          user_data)
 
3170
{
 
3171
        GossipContactListPriv *priv;
 
3172
 
 
3173
        priv = GET_PRIV (user_data);
 
3174
        
 
3175
        priv->flash_on = !priv->flash_on;
 
3176
 
 
3177
        g_hash_table_foreach (priv->flash_table,
 
3178
                              (GHFunc) contact_list_foreach_contact_flash,
 
3179
                              GOSSIP_CONTACT_LIST (user_data));
 
3180
 
 
3181
        return TRUE;
2797
3182
}
2798
3183
 
2799
3184
static void
2802
3187
                             GossipContactList  *list)
2803
3188
{
2804
3189
        GossipContactListPriv *priv;
2805
 
        GossipMessage         *message;
2806
 
        GossipFT              *ft;
2807
3190
        GossipContact         *contact;
2808
 
        GossipEventType        type;
2809
 
        FlashData             *data;
2810
 
        FlashTimeoutData      *timeout_data;
2811
3191
 
2812
3192
        priv = GET_PRIV (list);
2813
3193
 
2814
 
        type = gossip_event_get_type (event);
2815
 
 
2816
 
        switch (type) {
2817
 
        case GOSSIP_EVENT_NEW_MESSAGE:
2818
 
                message = GOSSIP_MESSAGE (gossip_event_get_data (event));
2819
 
                contact = gossip_message_get_sender (message);
2820
 
                break;
2821
 
 
2822
 
        case GOSSIP_EVENT_SUBSCRIPTION_REQUEST:
2823
 
                contact = GOSSIP_CONTACT (gossip_event_get_data (event));
2824
 
                break;
2825
 
 
2826
 
        case GOSSIP_EVENT_FILE_TRANSFER_REQUEST:
2827
 
                ft = GOSSIP_FT (gossip_event_get_data (event));
2828
 
                contact = gossip_ft_get_contact (ft);
2829
 
                break;
2830
 
 
2831
 
        default:
2832
 
                /* Not handled */
 
3194
        contact = gossip_event_get_contact (event);
 
3195
        if (!contact) {
2833
3196
                gossip_debug (DEBUG_DOMAIN,
2834
 
                              "Event type not added to the flashing event table",
2835
 
                              type);
 
3197
                              "Event type not added to the flashing event table");
2836
3198
                return;
2837
3199
        }
2838
3200
 
2839
 
        data = g_hash_table_lookup (priv->flash_table, contact);
2840
 
        if (data) {
 
3201
        if (g_hash_table_lookup (priv->flash_table, contact)) {
2841
3202
                /* Already flashing this item. */
2842
3203
                gossip_debug (DEBUG_DOMAIN,
2843
3204
                              "Event already flashing for contact:'%s'",
2845
3206
                return;
2846
3207
        }
2847
3208
 
2848
 
        timeout_data = g_slice_new0 (FlashTimeoutData);
2849
 
 
2850
 
        timeout_data->list = list;
2851
 
        timeout_data->contact = g_object_ref (contact);
2852
 
 
2853
 
        data = g_slice_new0 (FlashData);
2854
 
 
2855
 
        data->event_id = gossip_event_get_id (event);
2856
 
        data->event_type = type;
2857
 
 
2858
 
        data->flash_on = TRUE;
2859
 
        data->flash_timeout_id =
2860
 
                g_timeout_add_full (G_PRIORITY_DEFAULT, FLASH_TIMEOUT,
2861
 
                                    (GSourceFunc) contact_list_flash_timeout_func,
2862
 
                                    timeout_data,
2863
 
                                    (GDestroyNotify) contact_list_free_flash_timeout_data);
2864
 
 
2865
 
        g_hash_table_insert (priv->flash_table, g_object_ref (contact), data);
 
3209
        g_hash_table_insert (priv->flash_table, 
 
3210
                             g_object_ref (contact),
 
3211
                             g_object_ref (event));
 
3212
 
 
3213
        contact_list_ensure_flash_heartbeat (list);
2866
3214
 
2867
3215
        gossip_debug (DEBUG_DOMAIN,
2868
3216
                      "Contact:'%s' added to the flashing event table",
2881
3229
                               GossipContactList  *list)
2882
3230
{
2883
3231
        GossipContactListPriv *priv;
2884
 
        GossipMessage         *message;
2885
 
        GossipFT              *ft;
2886
3232
        GossipContact         *contact;
2887
 
        GossipEventType        type;
2888
 
        FlashData             *data;
2889
3233
 
2890
3234
        priv = GET_PRIV (list);
2891
3235
 
2892
 
        type = gossip_event_get_type (event);
2893
 
 
2894
 
        switch (type) {
2895
 
        case GOSSIP_EVENT_NEW_MESSAGE:
2896
 
                message = GOSSIP_MESSAGE (gossip_event_get_data (event));
2897
 
                contact = gossip_message_get_sender (message);
2898
 
                break;
2899
 
 
2900
 
        case GOSSIP_EVENT_SUBSCRIPTION_REQUEST:
2901
 
                contact = GOSSIP_CONTACT (gossip_event_get_data (event));
2902
 
                break;
2903
 
 
2904
 
        case GOSSIP_EVENT_FILE_TRANSFER_REQUEST:
2905
 
                ft = GOSSIP_FT (gossip_event_get_data (event));
2906
 
                contact = gossip_ft_get_contact (ft);
2907
 
                break;
2908
 
 
2909
 
        default:
2910
 
                /* Not handled */
 
3236
        contact = gossip_event_get_contact (event);
 
3237
        if (!contact) {
 
3238
                /* Only events with contacts is added to the list */
 
3239
                /* Do nothing */
2911
3240
                return;
2912
3241
        }
2913
3242
 
2914
 
        data = g_hash_table_lookup (priv->flash_table, contact);
2915
 
        if (!data) {
 
3243
        if (!g_hash_table_lookup (priv->flash_table, contact)) {
2916
3244
                /* Not flashing this contact. */
2917
3245
                return;
2918
3246
        }
2919
3247
 
2920
3248
        g_hash_table_remove (priv->flash_table, contact);
 
3249
        
 
3250
        contact_list_flash_heartbeat_maybe_stop (list);
2921
3251
 
2922
3252
        gossip_debug (DEBUG_DOMAIN,
2923
3253
                      "Contact:'%s' removed from flashing event table",
2941
3271
 
2942
3272
        pixbuf = pixbuf_presence;
2943
3273
 
2944
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
3274
        model = GTK_TREE_MODEL (priv->store);
2945
3275
 
2946
3276
        for (l = iters; l; l = l->next) {
2947
3277
                gtk_tree_model_get (model, l->data,
2952
3282
                        pixbuf = pixbuf_composing;
2953
3283
                }
2954
3284
 
2955
 
                gtk_tree_store_set (GTK_TREE_STORE (model),
2956
 
                                    l->data,
 
3285
                gtk_tree_store_set (priv->store, l->data,
2957
3286
                                    COL_PIXBUF_STATUS, pixbuf,
2958
3287
                                    -1);
2959
3288
        }
2981
3310
                show_avatar = TRUE;
2982
3311
        }
2983
3312
 
2984
 
        gtk_tree_store_set (GTK_TREE_STORE (model), iter,
 
3313
        gtk_tree_store_set (priv->store, iter,
2985
3314
                            COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
2986
3315
                            COL_STATUS_VISIBLE, !priv->is_compact,
2987
3316
                            -1);
2989
3318
        return FALSE;
2990
3319
}
2991
3320
 
 
3321
static void
 
3322
contact_list_ensure_flash_heartbeat (GossipContactList *list)
 
3323
{
 
3324
        GossipContactListPriv *priv;
 
3325
 
 
3326
        priv = GET_PRIV (list);
 
3327
 
 
3328
        if (priv->flash_heartbeat_id) {
 
3329
                return;
 
3330
        }
 
3331
 
 
3332
        priv->flash_heartbeat_id = 
 
3333
                gossip_heartbeat_callback_add (gossip_app_get_flash_heartbeat (),
 
3334
                                               contact_list_flash_heartbeat_func,
 
3335
                                               list);
 
3336
}
 
3337
 
 
3338
static void
 
3339
contact_list_flash_heartbeat_maybe_stop (GossipContactList *list)
 
3340
{
 
3341
        GossipContactListPriv *priv;
 
3342
 
 
3343
        priv = GET_PRIV (list);
 
3344
 
 
3345
        if (priv->flash_heartbeat_id == 0) {
 
3346
                return;
 
3347
        }
 
3348
 
 
3349
        if (g_hash_table_size (priv->flash_table) > 0) {
 
3350
                return;
 
3351
        }
 
3352
 
 
3353
        gossip_heartbeat_callback_remove (gossip_app_get_flash_heartbeat (),
 
3354
                                          priv->flash_heartbeat_id);
 
3355
        priv->flash_heartbeat_id = 0;
 
3356
}
 
3357
 
2992
3358
GossipContactList *
2993
3359
gossip_contact_list_new (void)
2994
3360
{
3050
3416
        return name;
3051
3417
}
3052
3418
 
 
3419
 
3053
3420
gboolean
3054
3421
gossip_contact_list_get_show_offline (GossipContactList *list)
3055
3422
{
3062
3429
        return priv->show_offline;
3063
3430
}
3064
3431
 
 
3432
 
 
3433
gboolean
 
3434
gossip_contact_list_get_show_avatars (GossipContactList *list)
 
3435
{
 
3436
        GossipContactListPriv *priv;
 
3437
 
 
3438
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), TRUE);
 
3439
 
 
3440
        priv = GET_PRIV (list);
 
3441
 
 
3442
        return priv->show_avatars;
 
3443
}
 
3444
 
 
3445
gboolean
 
3446
gossip_contact_list_get_is_compact (GossipContactList *list)
 
3447
{
 
3448
        GossipContactListPriv *priv;
 
3449
 
 
3450
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), TRUE);
 
3451
 
 
3452
        priv = GET_PRIV (list);
 
3453
 
 
3454
        return priv->is_compact;
 
3455
}
 
3456
 
 
3457
GossipContactListSort
 
3458
gossip_contact_list_get_sort_criterium (GossipContactList *list)
 
3459
{
 
3460
        GossipContactListPriv *priv;
 
3461
 
 
3462
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), 0);
 
3463
 
 
3464
        priv = GET_PRIV (list);
 
3465
 
 
3466
        return priv->sort_criterium;
 
3467
}
 
3468
 
3065
3469
void
3066
3470
gossip_contact_list_set_show_offline (GossipContactList *list,
3067
3471
                                      gboolean           show_offline)
3083
3487
 
3084
3488
        contacts = gossip_session_get_contacts (priv->session);
3085
3489
        for (l = contacts; l; l = l->next) {
3086
 
                GossipContact *contact;
3087
 
 
3088
 
                contact = GOSSIP_CONTACT (l->data);
3089
 
 
3090
 
                contact_list_contact_update (list, contact);
 
3490
                contact_list_contact_update (list, l->data);
3091
3491
        }
3092
3492
 
3093
3493
        /* Restore to original setting. */
3094
3494
        priv->show_active = show_active;
3095
3495
}
3096
3496
 
3097
 
gboolean
3098
 
gossip_contact_list_get_show_avatars (GossipContactList *list)
3099
 
{
3100
 
        GossipContactListPriv *priv;
3101
 
 
3102
 
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), TRUE);
3103
 
 
3104
 
        priv = GET_PRIV (list);
3105
 
 
3106
 
        return priv->show_avatars;
3107
 
}
3108
 
 
3109
3497
void
3110
3498
gossip_contact_list_set_show_avatars (GossipContactList *list,
3111
3499
                                      gboolean           show_avatars)
3127
3515
                                list);
3128
3516
}
3129
3517
 
3130
 
gboolean
3131
 
gossip_contact_list_get_is_compact (GossipContactList *list)
3132
 
{
3133
 
        GossipContactListPriv *priv;
3134
 
 
3135
 
        g_return_val_if_fail (GOSSIP_IS_CONTACT_LIST (list), TRUE);
3136
 
 
3137
 
        priv = GET_PRIV (list);
3138
 
 
3139
 
        return priv->is_compact;
3140
 
}
3141
 
 
3142
3518
void
3143
3519
gossip_contact_list_set_is_compact (GossipContactList *list,
3144
3520
                                    gboolean           is_compact)
3152
3528
 
3153
3529
        priv->is_compact = is_compact;
3154
3530
 
3155
 
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
 
3531
        model = GTK_TREE_MODEL (priv->store);
3156
3532
 
3157
3533
        gtk_tree_model_foreach (model,
3158
3534
                                (GtkTreeModelForeachFunc)
3160
3536
                                list);
3161
3537
}
3162
3538
 
 
3539
void
 
3540
gossip_contact_list_set_sort_criterium (GossipContactList     *list,
 
3541
                                        GossipContactListSort  sort_criterium)
 
3542
{
 
3543
        GossipContactListPriv *priv;
 
3544
 
 
3545
        g_return_if_fail (GOSSIP_IS_CONTACT_LIST (list));
 
3546
 
 
3547
        priv = GET_PRIV (list);
 
3548
 
 
3549
        priv->sort_criterium = sort_criterium;
 
3550
 
 
3551
        switch (sort_criterium) {
 
3552
        case GOSSIP_CONTACT_LIST_SORT_STATE:
 
3553
                gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store),
 
3554
                                                      COL_STATUS,
 
3555
                                                      GTK_SORT_ASCENDING);
 
3556
                break;
 
3557
                
 
3558
        case GOSSIP_CONTACT_LIST_SORT_NAME:
 
3559
                gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store),
 
3560
                                                      COL_NAME,
 
3561
                                                      GTK_SORT_ASCENDING);
 
3562
                break;
 
3563
        }
 
3564
}
 
3565
 
 
3566
void
 
3567
gossip_contact_list_set_filter (GossipContactList *list,
 
3568
                                const gchar       *filter)
 
3569
{
 
3570
        GossipContactListPriv *priv;
 
3571
 
 
3572
        g_return_if_fail (GOSSIP_IS_CONTACT_LIST (list));
 
3573
 
 
3574
        priv = GET_PRIV (list);
 
3575
 
 
3576
        g_free (priv->filter_text);
 
3577
        if (filter) {
 
3578
                priv->filter_text = g_utf8_casefold (filter, -1);
 
3579
        } else {
 
3580
                priv->filter_text = NULL;
 
3581
        }
 
3582
 
 
3583
        gossip_debug (DEBUG_DOMAIN, "Refiltering with filter:'%s' (case folded)", filter);
 
3584
        gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter));
 
3585
}