~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to gnome/src/presencewindow.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
 
3
 *  Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
 
4
 *
 
5
 *  This program is free software; you can redistribute it and/or modify
 
6
 *  it under the terms of the GNU General Public License as published by
 
7
 *  the Free Software Foundation; either version 3 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 
18
 *
 
19
 *  Additional permission under GNU GPL version 3 section 7:
 
20
 *
 
21
 *  If you modify this program, or any covered work, by linking or
 
22
 *  combining it with the OpenSSL project's OpenSSL library (or a
 
23
 *  modified version of that library), containing parts covered by the
 
24
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 
25
 *  grants you additional permission to convey the resulting work.
 
26
 *  Corresponding Source for a non-source form of such a combination
 
27
 *  shall include the source code for the parts of OpenSSL used as well
 
28
 *  as that of the covered work.
 
29
 */
 
30
 
 
31
#ifdef HAVE_CONFIG_H
 
32
#include "config.h"
 
33
#endif
 
34
 
 
35
#include <gtk/gtk.h>
 
36
#include <string.h>
 
37
#include <glib/gi18n.h>
 
38
 
 
39
#include "mainwindow.h"
 
40
#include "presencewindow.h"
 
41
#include "account_schema.h"
 
42
#include "accountlist.h"
 
43
#include "actions.h"
 
44
#include "contacts/calltree.h"
 
45
#include "callable_obj.h"
 
46
#include "dbus.h"
 
47
#include "str_utils.h"
 
48
#include "statusicon.h"
 
49
 
 
50
static GtkWidget *presence_window;
 
51
static GtkTreeView *buddy_list_tree_view;
 
52
static GtkToggleAction *toggle_action;
 
53
static GtkWidget *presence_status_combo;
 
54
static GtkWidget *presence_status_bar;
 
55
 
 
56
static GtkTreeModel *create_and_fill_presence_tree(void);
 
57
static GtkTreeView *create_presence_view(void);
 
58
gboolean selection_changed(GtkTreeSelection *selection);
 
59
static gboolean presence_view_row_is_buddy(GtkTreeView *treeview, GtkTreePath *path);
 
60
static buddy_t *presence_view_row_get_buddy(GtkTreeView *treeview, GtkTreePath *path);
 
61
static gchar *presence_view_row_get_group(GtkTreeView *treeview, GtkTreePath *path);
 
62
 
 
63
static SFLPhoneClient *presence_client;
 
64
static buddy_t *tmp_buddy;
 
65
static gboolean show_all;
 
66
 
 
67
enum
 
68
{
 
69
    POPUP_MENU_TYPE_DEFAULT,
 
70
    POPUP_MENU_TYPE_BUDDY,
 
71
    POPUP_MENU_TYPE_GROUP
 
72
};
 
73
 
 
74
/***************************** tree view **********************************/
 
75
 
 
76
enum
 
77
{
 
78
    COLUMN_OVERVIEW,
 
79
    COLUMN_ALIAS,
 
80
    COLUMN_STATUS,
 
81
    COLUMN_NOTE,
 
82
    COLUMN_URI,
 
83
    COLUMN_SUBSCRIBED,
 
84
    COLUMN_ACCOUNTID,
 
85
    COLUMN_GROUP,
 
86
    N_COLUMN
 
87
};
 
88
 
 
89
 
 
90
/* User callback for "get"ing the data out of the row that was DnD'd */
 
91
void on_buddy_drag_data_get(GtkWidget *widget,
 
92
        G_GNUC_UNUSED GdkDragContext *drag_context,
 
93
        GtkSelectionData *sdata,
 
94
        G_GNUC_UNUSED guint info,
 
95
        G_GNUC_UNUSED guint time_,
 
96
        G_GNUC_UNUSED gpointer user_data)
 
97
{
 
98
    GtkTreeIter iter;
 
99
    GtkTreeModel *model;
 
100
    GtkTreeSelection *selector;
 
101
 
 
102
    /* Get the selector widget from the treeview in question */
 
103
    selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
 
104
 
 
105
    /* Get the tree model (list_store) and initialise the iterator */
 
106
    if (!gtk_tree_selection_get_selected(selector, &model, &iter))
 
107
        return;
 
108
 
 
109
    // TODO : get the path would be cleaner
 
110
    model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
 
111
    buddy_t *b = NULL;
 
112
    GValue val;
 
113
    memset(&val, 0, sizeof(val));
 
114
    gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
 
115
    b = presence_buddy_list_buddy_get_by_uri(g_value_dup_string(&val));
 
116
    g_value_unset(&val);
 
117
    if (!b)
 
118
        return;
 
119
 
 
120
    g_debug("Drag src from buddy list b->uri : %s", b->uri);
 
121
 
 
122
    gtk_selection_data_set(sdata,
 
123
            gdk_atom_intern ("struct buddy_t pointer", FALSE),
 
124
            8,           // Tell GTK how to pack the data (bytes)
 
125
            (void *)&b,  // The actual pointer that we just made
 
126
            sizeof (b)); // The size of the pointer
 
127
}
 
128
 
 
129
 
 
130
void on_buddy_drag_data_received(GtkWidget *widget,
 
131
        G_GNUC_UNUSED GdkDragContext *drag_context,
 
132
        gint x, gint y, GtkSelectionData *sdata,
 
133
        G_GNUC_UNUSED guint info,
 
134
        G_GNUC_UNUSED guint time_,
 
135
        G_GNUC_UNUSED gpointer user_data)
 
136
{
 
137
 
 
138
    // GOAL: grab the "group" field from the target row (pointed on drop)
 
139
    // and apply it on the  dragged src buddy
 
140
 
 
141
    GtkTreePath *path;
 
142
    const guchar *data = gtk_selection_data_get_data(sdata);
 
143
 
 
144
    if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path,
 
145
                NULL, NULL, NULL))
 
146
        return;
 
147
 
 
148
    //gchar * spath = gtk_tree_path_to_string(path);
 
149
    //g_debug("========= (%d,%d) => Path:%s\n", x, y, spath);
 
150
    gchar *gr_target = presence_view_row_get_group(GTK_TREE_VIEW(widget), path);
 
151
    gtk_tree_path_free(path);
 
152
    if (!gr_target) // set group to default
 
153
        gr_target = g_strdup(" ");
 
154
 
 
155
    buddy_t *b_src = NULL;
 
156
    memcpy(&b_src, data, sizeof(b_src));
 
157
    if (!b_src) {
 
158
        g_warning("Dragged src data not found");
 
159
        return;
 
160
    }
 
161
 
 
162
    buddy_t *b = presence_buddy_list_buddy_get_by_uri(b_src->uri);
 
163
    // the dragged source data refers to an existing buddy.
 
164
    if (b) {
 
165
        g_debug("Dragged src data found: %s, %s", b_src->alias, b_src->uri);
 
166
        buddy_t *backup = presence_buddy_copy(b);
 
167
 
 
168
        // set the group to the one under the mouse pointer
 
169
        g_free(b->group);
 
170
        b->group = g_strdup(gr_target);
 
171
 
 
172
        // The src may come from the history/contact window and the alias maybe change.
 
173
        // but if not, don't free b_src because b and b_src points to the same buddy.
 
174
        gchar *alias = g_strdup(b_src->alias);
 
175
        g_free(b->alias);
 
176
        b->alias = g_strdup(alias);
 
177
        presence_buddy_list_edit_buddy(b, backup);
 
178
        presence_buddy_delete(backup);
 
179
        // TODO change rank in tree view
 
180
    } else {
 
181
        // create the new buddy from the dragged source data
 
182
        g_free(b_src->group);
 
183
        b_src->group = g_strdup(gr_target);
 
184
        presence_buddy_list_add_buddy(b_src);
 
185
    }
 
186
    g_free(gr_target);
 
187
    update_presence_view();
 
188
}
 
189
 
 
190
static GtkTreeModel *
 
191
create_and_fill_presence_tree (void)
 
192
{
 
193
    GtkTreeStore *treestore;
 
194
    GtkTreeIter toplevel, child;
 
195
    GList * buddy_list = g_object_get_data(G_OBJECT(presence_window), "Buddy-List");
 
196
    buddy_t * buddy;
 
197
 
 
198
    treestore = gtk_tree_store_new(N_COLUMN, G_TYPE_STRING, // Group + photo
 
199
                                             G_TYPE_STRING, // Alias
 
200
                                             G_TYPE_STRING, // Group
 
201
                                             G_TYPE_STRING, // Status
 
202
                                             G_TYPE_STRING, // Note
 
203
                                             G_TYPE_STRING, // URI
 
204
                                             G_TYPE_STRING, // AccID
 
205
                                             G_TYPE_STRING);// subscribed
 
206
 
 
207
    // then display buddies with no group (==' ')
 
208
    for (guint j =  0; j < presence_buddy_list_get_size(buddy_list); j++) {
 
209
        buddy = presence_buddy_list_get_nth(j);
 
210
        account_t *acc = account_list_get_by_id(buddy->acc);
 
211
        if (acc == NULL)
 
212
            continue;
 
213
 
 
214
        if ((g_strcmp0(buddy->group, " ") == 0) &&
 
215
            ((g_strcmp0(buddy->note,"Not found") != 0) || show_all)) {
 
216
            gtk_tree_store_append(treestore, &toplevel, NULL);
 
217
            gtk_tree_store_set(treestore, &toplevel,
 
218
                    COLUMN_OVERVIEW, " ", // to be on the top of the sorted list
 
219
                    COLUMN_ALIAS, buddy->alias,
 
220
                    COLUMN_GROUP, buddy->group,
 
221
                    COLUMN_STATUS, (buddy->status)? GTK_STOCK_YES : GTK_STOCK_NO,
 
222
                    COLUMN_NOTE,  buddy->note,
 
223
                    COLUMN_URI,  buddy->uri,
 
224
                    COLUMN_ACCOUNTID, buddy->acc,
 
225
                    COLUMN_SUBSCRIBED, (buddy->subscribed)? "yes":"no",
 
226
                    -1);
 
227
        }
 
228
    }
 
229
 
 
230
    // then display the groups
 
231
    for (guint i = 0; i < presence_group_list_get_size(); i++) {
 
232
        gchar *group = presence_group_list_get_nth(i);
 
233
        gchar *tmp = g_markup_printf_escaped("<b>%s</b>", group);
 
234
 
 
235
        // display buddy with no group after
 
236
        if (g_strcmp0(group, " ") == 0)
 
237
            continue;
 
238
 
 
239
        gtk_tree_store_append(treestore, &toplevel, NULL);
 
240
        gtk_tree_store_set(treestore, &toplevel,
 
241
                COLUMN_OVERVIEW, tmp,
 
242
                COLUMN_ALIAS, "",
 
243
                COLUMN_GROUP, group,
 
244
                COLUMN_STATUS, "",
 
245
                COLUMN_NOTE, "",
 
246
                COLUMN_URI, "",
 
247
                COLUMN_ACCOUNTID, "",
 
248
                COLUMN_SUBSCRIBED, "",
 
249
                -1);
 
250
        g_free(tmp);
 
251
 
 
252
        for (guint j =  0; j < presence_buddy_list_get_size(buddy_list); j++) {
 
253
            buddy = presence_buddy_list_get_nth(j);
 
254
            account_t *acc = account_list_get_by_id(buddy->acc);
 
255
            if (acc == NULL)
 
256
                continue;
 
257
 
 
258
            if ((g_strcmp0(buddy->group, group) == 0) &&
 
259
               ((g_strcmp0(buddy->note,"Not found") != 0) || show_all)) {
 
260
                gtk_tree_store_append(treestore, &child, &toplevel);
 
261
                gtk_tree_store_set(treestore, &child,
 
262
                        COLUMN_OVERVIEW, "",
 
263
                        COLUMN_ALIAS, buddy->alias,
 
264
                        COLUMN_GROUP, buddy->group,
 
265
                        COLUMN_STATUS, (buddy->status)? GTK_STOCK_YES : GTK_STOCK_NO,
 
266
                        COLUMN_NOTE,  buddy->note,
 
267
                        COLUMN_URI,  buddy->uri,
 
268
                        COLUMN_ACCOUNTID, buddy->acc,
 
269
                        COLUMN_SUBSCRIBED, (buddy->subscribed) ? "yes" : "no",
 
270
                        -1);
 
271
            }
 
272
        }
 
273
    }
 
274
 
 
275
    // sort the groups and their buddies by name
 
276
    GtkTreeSortable * sortable = GTK_TREE_SORTABLE(treestore);
 
277
    gtk_tree_sortable_set_sort_column_id(sortable, COLUMN_OVERVIEW, GTK_SORT_ASCENDING);
 
278
    //gtk_tree_sortable_set_sort_column_id(sortable, COLUMN_ALIAS, GTK_SORT_ASCENDING);
 
279
 
 
280
    return GTK_TREE_MODEL(treestore);
 
281
}
 
282
 
 
283
void presence_view_cell_edited(G_GNUC_UNUSED GtkCellRendererText *renderer,
 
284
        gchar *path_str,
 
285
        gchar *new_text,
 
286
        GtkTreeView *treeview)
 
287
{
 
288
    GtkTreeViewColumn *focus_column;
 
289
    gtk_tree_view_get_cursor(treeview, NULL, &focus_column);
 
290
    const gchar * col_name = gtk_tree_view_column_get_title(focus_column);
 
291
 
 
292
    g_debug("Presence: value of col: %s is set to %s", col_name, new_text);
 
293
    GtkTreePath * path = gtk_tree_path_new_from_string(path_str);
 
294
 
 
295
    // a buddy line was edited
 
296
    if (presence_view_row_is_buddy(treeview, path)) {
 
297
        buddy_t *b = presence_view_row_get_buddy(treeview, path);
 
298
        if (!b)
 
299
            return;
 
300
        buddy_t *backup = presence_buddy_copy(b);
 
301
 
 
302
        if ((g_strcmp0(col_name, "Alias") == 0) &&
 
303
            (g_strcmp0(new_text, backup->alias) != 0)) {
 
304
            // alias was edited and has changed
 
305
            g_free(b->alias);
 
306
            b->alias = g_strdup(new_text);
 
307
            presence_buddy_list_edit_buddy(b, backup);
 
308
        } else if ((g_strcmp0(col_name, "URI") == 0) &&
 
309
                (g_strcmp0(new_text, backup->uri) != 0) &&
 
310
                (!presence_buddy_list_buddy_get_by_uri(new_text))) {
 
311
            // uri was edited and has changed to a new uri
 
312
            g_free(b->uri);
 
313
            b->uri = g_strdup(new_text);
 
314
            presence_buddy_list_edit_buddy(b, backup);
 
315
        }
 
316
 
 
317
        presence_buddy_delete(backup);
 
318
    } else {
 
319
        // a group line was edited
 
320
        gchar *group = presence_view_row_get_group(treeview, path);
 
321
        if (g_strcmp0(new_text, group) != 0)
 
322
            presence_group_list_edit_group(new_text, group);
 
323
        g_free(group);
 
324
    }
 
325
 
 
326
    gtk_tree_path_free(path);
 
327
    update_presence_view();
 
328
}
 
329
 
 
330
void cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn *col,
 
331
        GtkCellRenderer   *renderer,
 
332
        GtkTreeModel      *model,
 
333
        GtkTreeIter       *iter,
 
334
        gpointer           userdata)
 
335
{
 
336
    guint col_ID = GPOINTER_TO_INT(userdata);
 
337
    // TODO: replace with get the path, get the group and get row_is_buddy
 
338
    GValue val;
 
339
    memset(&val, 0, sizeof(val));
 
340
    gtk_tree_model_get_value(model, iter, COLUMN_OVERVIEW, &val);
 
341
    gchar* group = g_value_dup_string(&val);
 
342
    g_value_unset(&val);
 
343
 
 
344
    // when the mouse pointer on a group field, set the cell editable
 
345
    // not a child buddy row and not an orphan buddy row
 
346
    if (g_strcmp0(group, "") != 0 && g_strcmp0(group, " ") != 0) {
 
347
        g_object_set(renderer, "editable", col_ID == COLUMN_OVERVIEW, NULL);
 
348
    } else {
 
349
        // set the cell editable when the mouse pointer is on a buddy alias or uri
 
350
        if (col_ID == COLUMN_ALIAS)
 
351
            g_object_set(renderer, "editable", TRUE, NULL);
 
352
        else if (col_ID == COLUMN_URI)
 
353
            g_object_set(renderer, "editable", TRUE, NULL);
 
354
        else
 
355
            g_object_set(renderer, "editable", FALSE, NULL);
 
356
    }
 
357
    g_free(group);
 
358
}
 
359
 
 
360
void icon_cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn *col,
 
361
                           GtkCellRenderer   *renderer,
 
362
                           GtkTreeModel      *model,
 
363
                           GtkTreeIter       *iter,
 
364
                           G_GNUC_UNUSED gpointer userdata)
 
365
{
 
366
    GValue val;
 
367
    memset(&val, 0, sizeof(val));
 
368
    gtk_tree_model_get_value(model, iter, COLUMN_STATUS, &val);
 
369
    gchar *icon_name = g_value_dup_string(&val);
 
370
    g_value_unset(&val);
 
371
    g_object_set(renderer, "icon-name", icon_name, NULL);
 
372
}
 
373
 
 
374
void
 
375
update_presence_view()
 
376
{
 
377
    if (!buddy_list_tree_view)
 
378
        return; // presence window not opend
 
379
 
 
380
    GtkTreeModel * model = create_and_fill_presence_tree();
 
381
    gtk_tree_view_set_model(buddy_list_tree_view, model);
 
382
    g_object_unref(model);
 
383
    gtk_tree_view_expand_all(buddy_list_tree_view);
 
384
#ifdef PRESENCE_DEBUG
 
385
    g_debug("Presence: view updated.");
 
386
#endif
 
387
}
 
388
 
 
389
static GtkTreeView *
 
390
create_presence_view (void)
 
391
{
 
392
    GtkTreeViewColumn *col;
 
393
    GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); // default text render
 
394
    GtkWidget * view_widget = gtk_tree_view_new();
 
395
    GtkTreeView *view = GTK_TREE_VIEW(view_widget);
 
396
 
 
397
    GtkCellRenderer *editable_cell = gtk_cell_renderer_text_new();
 
398
    g_signal_connect(editable_cell, "edited",
 
399
            G_CALLBACK(presence_view_cell_edited), view);
 
400
 
 
401
    col = gtk_tree_view_column_new_with_attributes(" ",
 
402
            editable_cell, "markup", COLUMN_OVERVIEW, NULL);
 
403
    gtk_tree_view_append_column(view, col);
 
404
    // pack with the defaul renderer to avoid GTK warning.
 
405
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
406
    gtk_tree_view_column_set_cell_data_func(col, editable_cell,
 
407
            cell_data_func, GINT_TO_POINTER(COLUMN_OVERVIEW), NULL);
 
408
 
 
409
    col = gtk_tree_view_column_new_with_attributes("Alias",
 
410
            editable_cell, "text", COLUMN_ALIAS, NULL);
 
411
    gtk_tree_view_append_column(view, col);
 
412
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
413
    gtk_tree_view_column_set_cell_data_func(col, editable_cell,
 
414
            cell_data_func, GINT_TO_POINTER(COLUMN_ALIAS), NULL);
 
415
 
 
416
    GtkCellRenderer * status_icon_renderer  = gtk_cell_renderer_pixbuf_new();
 
417
    col = gtk_tree_view_column_new_with_attributes("Status",
 
418
            status_icon_renderer, "text", COLUMN_STATUS, NULL);
 
419
    // FIXME: set type to "text" instead of "pixbuf". this is a work around because
 
420
    // the gtk stock icon is referenced as a string BUT there is still a warning
 
421
    gtk_tree_view_append_column(view, col);
 
422
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
423
    gtk_tree_view_column_set_cell_data_func(col, status_icon_renderer,
 
424
            icon_cell_data_func, GINT_TO_POINTER(COLUMN_STATUS), NULL);
 
425
 
 
426
    col = gtk_tree_view_column_new();
 
427
    gtk_tree_view_column_set_title(col, "Note");
 
428
    gtk_tree_view_append_column(view, col);
 
429
    renderer = gtk_cell_renderer_text_new();
 
430
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
431
    gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_NOTE);
 
432
 
 
433
    col = gtk_tree_view_column_new_with_attributes("URI",
 
434
            editable_cell, "text", COLUMN_URI, NULL);
 
435
    gtk_tree_view_append_column(view, col);
 
436
    renderer = gtk_cell_renderer_text_new();
 
437
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
438
    gtk_tree_view_column_set_cell_data_func(col, editable_cell,
 
439
            cell_data_func, GINT_TO_POINTER(COLUMN_URI), NULL);
 
440
#ifndef PRESENCE_DEBUG
 
441
    gtk_tree_view_column_set_visible(col, FALSE);
 
442
#else
 
443
    gtk_tree_view_column_set_visible(col, TRUE);
 
444
 
 
445
    col = gtk_tree_view_column_new();
 
446
    gtk_tree_view_column_set_title(col, "Suscribed");
 
447
    gtk_tree_view_append_column(view, col);
 
448
    renderer = gtk_cell_renderer_text_new();
 
449
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
450
    gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_SUBSCRIBED);
 
451
 
 
452
    col = gtk_tree_view_column_new();
 
453
    gtk_tree_view_column_set_title(col, "AccID");
 
454
    gtk_tree_view_append_column(view, col);
 
455
    renderer = gtk_cell_renderer_text_new();
 
456
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
457
    gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_ACCOUNTID);
 
458
 
 
459
    col = gtk_tree_view_column_new();
 
460
    gtk_tree_view_column_set_title(col, "Group");
 
461
    gtk_tree_view_append_column(view, col);
 
462
    renderer = gtk_cell_renderer_text_new();
 
463
    gtk_tree_view_column_pack_start(col, renderer, TRUE);
 
464
    gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_GROUP);
 
465
#endif
 
466
 
 
467
    return view;
 
468
}
 
469
 
 
470
/***************************** dialog win **********************************/
 
471
 
 
472
 
 
473
static gboolean
 
474
field_error_dialog(const gchar *error_string)
 
475
{
 
476
    gchar *msg;
 
477
    msg = g_markup_printf_escaped("Field error %s.",error_string);// TODO: use _()
 
478
 
 
479
    /* Create the widgets */
 
480
    GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
 
481
            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
482
            GTK_MESSAGE_ERROR,
 
483
            GTK_BUTTONS_OK,
 
484
            "%s", msg);
 
485
 
 
486
    gtk_window_set_title(GTK_WINDOW(dialog), _("Field error"));
 
487
 
 
488
    const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
 
489
 
 
490
    gtk_widget_destroy(dialog);
 
491
    g_free(msg);
 
492
 
 
493
    return response == GTK_RESPONSE_OK;
 
494
}
 
495
 
 
496
gboolean
 
497
show_buddy_info_dialog(const gchar *title, buddy_t *b)
 
498
{
 
499
    GtkWidget *dialog = gtk_dialog_new_with_buttons((title),
 
500
                        GTK_WINDOW(presence_window),
 
501
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
502
                        GTK_STOCK_CANCEL,
 
503
                        GTK_RESPONSE_CANCEL,
 
504
                        GTK_STOCK_APPLY,
 
505
                        GTK_RESPONSE_APPLY,
 
506
                        NULL);
 
507
 
 
508
    GtkWidget *grid = gtk_grid_new();
 
509
    gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
 
510
    gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
 
511
    gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
 
512
            grid, TRUE, TRUE, 0);
 
513
 
 
514
    gint row = 0;
 
515
    GtkWidget *label;
 
516
 
 
517
    // TODO: this is ugly but temporary
 
518
    guint group_index = 0;
 
519
    guint group_count = 0;
 
520
    GtkWidget * combo_group;
 
521
    if (g_strcmp0(title, "Add new buddy") != 0)
 
522
    {
 
523
        label = gtk_label_new_with_mnemonic("_Group");
 
524
        gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
 
525
        gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
526
        combo_group = gtk_combo_box_text_new();
 
527
        // fill combox with existing groups
 
528
        gchar *group;
 
529
        for (guint i = 0; i < presence_group_list_get_size(); i++) {
 
530
            group = presence_group_list_get_nth(i);
 
531
            gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_group), group);
 
532
            group_count++;
 
533
            // set active group
 
534
            if (g_strcmp0(group, b->group) == 0)
 
535
                group_index = i;
 
536
        }
 
537
        gtk_combo_box_set_active(GTK_COMBO_BOX(combo_group), (gint)group_index);
 
538
 
 
539
        gtk_grid_attach(GTK_GRID(grid), combo_group, 1, row, 1, 1);
 
540
        row++;
 
541
    }
 
542
 
 
543
    label = gtk_label_new_with_mnemonic("_Alias");
 
544
    gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
 
545
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
546
    GtkWidget *entry_alias = gtk_entry_new();
 
547
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_alias);
 
548
    gtk_entry_set_text(GTK_ENTRY(entry_alias), b->alias);
 
549
    gtk_grid_attach(GTK_GRID(grid), entry_alias, 1, row, 1, 1);
 
550
 
 
551
    row++;
 
552
    label = gtk_label_new_with_mnemonic("_URI");
 
553
    gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
 
554
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
555
    GtkWidget *entry_uri = gtk_entry_new();
 
556
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_uri);
 
557
    gtk_entry_set_text(GTK_ENTRY(entry_uri), b->uri);
 
558
    gtk_grid_attach(GTK_GRID(grid), entry_uri, 1, row, 1, 1);
 
559
 
 
560
    gtk_widget_show_all(grid);
 
561
    gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
 
562
 
 
563
    gint response = gtk_dialog_run(GTK_DIALOG(dialog));
 
564
 
 
565
    // update buddy OK was pressed
 
566
    if (response == GTK_RESPONSE_APPLY)
 
567
    {
 
568
        // this is ugly but temporary
 
569
        if (g_strcmp0(title, "Add new buddy") != 0)
 
570
        {
 
571
            // FIXME: this function doesn't work as expected
 
572
            // if(gtk_combo_box_get_has_entry(GTK_COMBO_BOX(combo_group)))
 
573
            if (group_count > 0) {
 
574
                gchar * gr = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_group));
 
575
                g_free(b->group);
 
576
                b->group = g_strdup(gr);
 
577
                g_free(gr);
 
578
            }
 
579
        }
 
580
        g_free(b->alias);
 
581
        g_free(b->uri);
 
582
        b->alias = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_alias)));
 
583
        b->uri = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_uri)));
 
584
 
 
585
        // check the filed
 
586
        if (strlen(b->alias) == 0 || strlen(b->uri) == 0) {
 
587
            field_error_dialog("a field is empty");
 
588
            gtk_widget_destroy(dialog);
 
589
            gboolean res = show_buddy_info_dialog(title, b);// recursive call
 
590
            return res;
 
591
        }
 
592
 
 
593
        gtk_widget_destroy(dialog);
 
594
        return TRUE;
 
595
    }
 
596
 
 
597
    gtk_widget_destroy(dialog);
 
598
    return FALSE;
 
599
}
 
600
 
 
601
 
 
602
static gboolean
 
603
show_group_info_dialog(const gchar *title, gchar **group)
 
604
{
 
605
    GtkWidget *dialog = gtk_dialog_new_with_buttons((title),
 
606
                        GTK_WINDOW(presence_window),
 
607
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
608
                        GTK_STOCK_CANCEL,
 
609
                        GTK_RESPONSE_CANCEL,
 
610
                        GTK_STOCK_APPLY,
 
611
                        GTK_RESPONSE_APPLY,
 
612
                        NULL);
 
613
 
 
614
    GtkWidget *grid = gtk_grid_new();
 
615
    gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
 
616
    gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
 
617
    gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
 
618
            grid, TRUE, TRUE, 0);
 
619
 
 
620
    gint row = 0;
 
621
    GtkWidget *label = gtk_label_new_with_mnemonic("Group");
 
622
    gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
 
623
    gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
 
624
    GtkWidget *entry_group = gtk_entry_new();
 
625
    gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_group);
 
626
    gtk_entry_set_text(GTK_ENTRY(entry_group), *group);
 
627
    gtk_grid_attach(GTK_GRID(grid), entry_group, 1, row, 1, 1);
 
628
 
 
629
    gtk_widget_show_all(grid);
 
630
    gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
 
631
 
 
632
    gint response = gtk_dialog_run(GTK_DIALOG(dialog));
 
633
 
 
634
    // update buddy OK was pressed
 
635
    if (response == GTK_RESPONSE_APPLY)
 
636
    {
 
637
        g_free(*group);
 
638
        *group = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_group)));
 
639
        gtk_widget_destroy(dialog);
 
640
 
 
641
        // check that the group name isn't empty or default
 
642
        if ((g_strcmp0(*group, "") == 0) || (g_strcmp0(*group," ") == 0))
 
643
            return FALSE;
 
644
        else
 
645
            return TRUE;
 
646
    }
 
647
 
 
648
    gtk_widget_destroy(dialog);
 
649
    return FALSE;
 
650
}
 
651
 
 
652
 
 
653
static gboolean
 
654
confirm_buddy_deletion(buddy_t *b)
 
655
{
 
656
    gchar *msg;
 
657
    msg = g_markup_printf_escaped("Are you sure want to delete \"%s\"", b->alias);
 
658
 
 
659
    /* Create the widgets */
 
660
    GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
 
661
            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
662
            GTK_MESSAGE_WARNING,
 
663
            GTK_BUTTONS_CANCEL,
 
664
            "%s", msg);
 
665
 
 
666
    gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Remove"), GTK_RESPONSE_OK, NULL);
 
667
    gtk_window_set_title(GTK_WINDOW(dialog), _("Remove buddy"));
 
668
 
 
669
    const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
 
670
 
 
671
    gtk_widget_destroy(dialog);
 
672
    g_free(msg);
 
673
 
 
674
    return response == GTK_RESPONSE_OK;
 
675
}
 
676
 
 
677
static gboolean
 
678
confirm_group_deletion(gchar *group)
 
679
{
 
680
    gchar *msg;
 
681
    msg = g_markup_printf_escaped("Do you really want to delete the group %s", group);
 
682
 
 
683
    /* Create the widgets */
 
684
    GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
 
685
            GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
 
686
            GTK_MESSAGE_WARNING,
 
687
            GTK_BUTTONS_CANCEL,
 
688
            "%s", msg);
 
689
 
 
690
    gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Remove"), GTK_RESPONSE_OK, NULL);
 
691
    gtk_window_set_title(GTK_WINDOW(dialog), _("Remove buddy"));
 
692
 
 
693
    const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
 
694
 
 
695
    gtk_widget_destroy(dialog);
 
696
    g_free(msg);
 
697
 
 
698
    return response == GTK_RESPONSE_OK;
 
699
}
 
700
 
 
701
/*********************************  Contextual Menus ****************************/
 
702
 
 
703
static void
 
704
presence_view_popup_menu_onCallBuddy(G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
 
705
{
 
706
    buddy_t *b= (buddy_t*)userdata;
 
707
    callable_obj_t * c = sflphone_new_call(presence_client);
 
708
 
 
709
    g_free(c->_peer_number);
 
710
    c->_peer_number = g_strdup(b->uri);
 
711
    g_free(c->_accountID);
 
712
    c->_accountID = g_strdup(b->acc);
 
713
 
 
714
    calltree_update_call(current_calls_tab, c, presence_client, FALSE);
 
715
    sflphone_place_call(c, presence_client);
 
716
 
 
717
    /* Legacy system tray option, requires TopIcons GNOME extension */
 
718
    status_tray_icon_blink(TRUE);
 
719
    if (g_settings_get_boolean(presence_client->settings, "popup-main-window"))
 
720
        popup_main_window(presence_client);
 
721
 
 
722
    if (g_settings_get_boolean(presence_client->settings, "bring-window-to-front"))
 
723
        main_window_bring_to_front(presence_client, c->_time_start);
 
724
}
 
725
 
 
726
static void
 
727
presence_view_popup_menu_onAddBuddy(G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
 
728
{
 
729
    buddy_t *b = presence_buddy_create();
 
730
 
 
731
    // try to grab the selected group
 
732
    if (userdata != NULL) {
 
733
        // might be a group row or a buddy row
 
734
        buddy_t *tmp_b = ((buddy_t*)userdata);
 
735
        g_free(b->group);
 
736
        b->group = g_strdup(tmp_b->group);
 
737
    }
 
738
 
 
739
    account_t *acc = account_list_get_current();
 
740
    if (!acc) {
 
741
        acc = account_list_get_nth(1); //0 is IP2IP
 
742
        if (!acc) {
 
743
            g_warning("At least one account must exist to able to subscribe.");
 
744
            return;
 
745
        }
 
746
    }
 
747
    g_free(b->acc);
 
748
    b->acc = g_strdup(acc->accountID);
 
749
 
 
750
    gchar * uri = g_strconcat("<sip:XXXX@", account_lookup(acc, CONFIG_ACCOUNT_HOSTNAME), ">", NULL);
 
751
    g_free(b->uri);
 
752
    b->uri = g_strdup(uri);
 
753
    g_free(uri);
 
754
 
 
755
    if (show_buddy_info_dialog(_("Add new buddy"), b)) {
 
756
        presence_buddy_list_add_buddy(b);
 
757
        update_presence_view();
 
758
    } else {
 
759
        presence_buddy_delete(b);
 
760
    }
 
761
}
 
762
 
 
763
static void
 
764
presence_view_popup_menu_onRemoveBuddy (G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
 
765
{
 
766
    buddy_t *b = (buddy_t*) userdata;
 
767
    if (confirm_buddy_deletion(b)) {
 
768
        presence_buddy_list_remove_buddy(b);
 
769
        update_presence_view();
 
770
    }
 
771
}
 
772
 
 
773
static void
 
774
presence_view_popup_menu_onAddGroup (G_GNUC_UNUSED GtkWidget *menuitem, G_GNUC_UNUSED gpointer userdata)
 
775
{
 
776
    gchar *group = g_strdup("");
 
777
    if (show_group_info_dialog(_("Add group"), &group)) {
 
778
        presence_group_list_add_group(group);
 
779
        update_presence_view();
 
780
    }
 
781
    g_free(group);
 
782
}
 
783
 
 
784
static void
 
785
presence_view_popup_menu_onRemoveGroup (G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
 
786
{
 
787
    gchar *group = (gchar*) userdata;
 
788
    if (confirm_group_deletion(group)) {
 
789
        presence_group_list_remove_group(group);
 
790
        update_presence_view();
 
791
    }
 
792
}
 
793
 
 
794
static void
 
795
presence_view_popup_menu(GdkEventButton *event, gpointer userdata, guint type)
 
796
{
 
797
    GtkWidget *menu, *menuitem;
 
798
    menu = gtk_menu_new(); //TODO free ???
 
799
    gchar* gr = g_strdup(((buddy_t*)userdata)->group); // TODO; is it necessary to be free?
 
800
 
 
801
    if (type == POPUP_MENU_TYPE_BUDDY) {
 
802
        menuitem = gtk_menu_item_new_with_label(_("Call"));
 
803
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
804
        g_signal_connect(menuitem, "activate",
 
805
                G_CALLBACK(presence_view_popup_menu_onCallBuddy), userdata);
 
806
 
 
807
        menuitem = gtk_separator_menu_item_new();
 
808
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
809
    }
 
810
 
 
811
    menuitem = gtk_menu_item_new_with_label(_("Add buddy"));
 
812
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
813
    g_signal_connect(menuitem, "activate",
 
814
            G_CALLBACK(presence_view_popup_menu_onAddBuddy), userdata);
 
815
 
 
816
    if (type == POPUP_MENU_TYPE_BUDDY) {
 
817
        menuitem = gtk_menu_item_new_with_label(_("Remove buddy"));
 
818
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
819
        g_signal_connect(menuitem, "activate",
 
820
                G_CALLBACK(presence_view_popup_menu_onRemoveBuddy), userdata);
 
821
    }
 
822
 
 
823
    menuitem = gtk_separator_menu_item_new(); // TODO:free?
 
824
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
825
 
 
826
    menuitem = gtk_menu_item_new_with_label(_("Add group"));
 
827
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
828
    g_signal_connect(menuitem, "activate",
 
829
            G_CALLBACK(presence_view_popup_menu_onAddGroup), gr);
 
830
 
 
831
    if (type == POPUP_MENU_TYPE_GROUP) {
 
832
        menuitem = gtk_menu_item_new_with_label(_("Remove group"));
 
833
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
 
834
        g_signal_connect(menuitem, "activate",
 
835
                G_CALLBACK(presence_view_popup_menu_onRemoveGroup), gr);
 
836
    }
 
837
 
 
838
    gtk_widget_show_all(menu);
 
839
 
 
840
    /* Note: event can be NULL here when called from view_onPopupMenu;
 
841
     *  gdk_event_get_time() accepts a NULL argument */
 
842
    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
 
843
            (event != NULL) ? event->button : 0,
 
844
            gdk_event_get_time((GdkEvent*)event));
 
845
}
 
846
 
 
847
static gboolean
 
848
presence_view_row_is_buddy(GtkTreeView *treeview, GtkTreePath *path)
 
849
{
 
850
    GtkTreeIter   iter;
 
851
    GtkTreeModel *model = gtk_tree_view_get_model(treeview);
 
852
    gboolean res = FALSE;
 
853
 
 
854
    if (gtk_tree_model_get_iter(model, &iter, path)) {
 
855
        GValue val;
 
856
        memset(&val, 0, sizeof(val));
 
857
        gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
 
858
        gchar *uri = g_value_dup_string(&val);
 
859
        res = strlen(uri) > 0;
 
860
        g_value_unset(&val);
 
861
        g_free(uri);
 
862
    }
 
863
    return res;
 
864
}
 
865
 
 
866
static buddy_t *
 
867
presence_view_row_get_buddy(GtkTreeView *treeview, GtkTreePath *path)
 
868
{
 
869
    GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
 
870
    buddy_t *b = NULL;
 
871
    gtk_tree_selection_unselect_all(selection);
 
872
    gtk_tree_selection_select_path(selection, path);
 
873
 
 
874
    GtkTreeIter   iter;
 
875
    GtkTreeModel *model = gtk_tree_view_get_model(treeview);
 
876
 
 
877
    if (gtk_tree_model_get_iter(model, &iter, path)) {
 
878
        GValue val;
 
879
        memset(&val, 0, sizeof(val));
 
880
        gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
 
881
        gchar *uri = g_value_dup_string(&val);
 
882
        b = presence_buddy_list_buddy_get_by_uri(uri);
 
883
        g_value_unset(&val);
 
884
        g_free(uri);
 
885
    }
 
886
    g_assert(b);
 
887
    return b;
 
888
}
 
889
 
 
890
static gchar *
 
891
presence_view_row_get_group(GtkTreeView *treeview, GtkTreePath *path)
 
892
{
 
893
    GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
 
894
    gchar *group = NULL;
 
895
    gtk_tree_selection_unselect_all(selection);
 
896
    gtk_tree_selection_select_path(selection, path);
 
897
 
 
898
    GtkTreeIter   iter;
 
899
    GtkTreeModel *model = gtk_tree_view_get_model(treeview);
 
900
 
 
901
    if (gtk_tree_model_get_iter(model, &iter, path)) {
 
902
        GValue val;
 
903
        memset(&val, 0, sizeof(val));
 
904
        gtk_tree_model_get_value(model, &iter, COLUMN_GROUP, &val);
 
905
        group = g_value_dup_string(&val);
 
906
        g_value_unset(&val);
 
907
    }
 
908
    g_assert(group);
 
909
    return group;
 
910
}
 
911
 
 
912
static gboolean
 
913
presence_view_onButtonPressed(GtkTreeView *treeview, GdkEventButton *event)
 
914
{
 
915
    /* single click with the right mouse button? */
 
916
    if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
 
917
        GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
 
918
 
 
919
        /* Note: gtk_tree_selection_count_selected_rows() does not
 
920
         *   exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
 
921
        if (gtk_tree_selection_count_selected_rows(selection) == 1) {
 
922
            GtkTreePath *path;
 
923
            /* Get tree path for row that was clicked */
 
924
            if (gtk_tree_view_get_path_at_pos(treeview,
 
925
                        (gint) event->x,
 
926
                        (gint) event->y,
 
927
                        &path, NULL, NULL, NULL)) {
 
928
                if (presence_view_row_is_buddy(treeview, path)) {
 
929
                    buddy_t *b = presence_view_row_get_buddy(treeview, path);
 
930
                    if (b)
 
931
                        presence_view_popup_menu(event, b, POPUP_MENU_TYPE_BUDDY);
 
932
                } else {
 
933
                    // a group row has been selected
 
934
                    // use a fake buddy as argument
 
935
                    g_free(tmp_buddy->group);
 
936
                    tmp_buddy->group = g_strdup(presence_view_row_get_group(treeview, path));
 
937
                    if (tmp_buddy->group != NULL)
 
938
                        presence_view_popup_menu(event, tmp_buddy, POPUP_MENU_TYPE_GROUP);
 
939
                }
 
940
                gtk_tree_path_free(path);
 
941
                return TRUE;
 
942
            }
 
943
        }
 
944
        //else right click on the back ground
 
945
        // use a fake buddy as argument
 
946
        g_free(tmp_buddy->group);
 
947
        tmp_buddy->group = g_strdup(" "); // default group
 
948
        presence_view_popup_menu(event, tmp_buddy, POPUP_MENU_TYPE_DEFAULT);
 
949
 
 
950
        return TRUE;
 
951
    }
 
952
    return FALSE;
 
953
}
 
954
 
 
955
/*
 
956
static void
 
957
view_row_activated_cb(GtkTreeView *treeview,
 
958
        GtkTreePath *path,
 
959
        G_GNUC_UNUSED GtkTreeViewColumn *col)
 
960
{
 
961
    buddy_t *b = view_get_buddy(treeview, path);
 
962
    if(b) //"NULL if an group was selected instead of a buddy.
 
963
        presence_view_popup_menu_onEditBuddy(NULL, b);
 
964
}
 
965
*/
 
966
 
 
967
/******************************** Status bar *********************************/
 
968
 
 
969
/**
 
970
 * This function reads the status combo box and call the DBus presence
 
971
 * publish method if enabled.
 
972
 * @param combo The text combo box associated with the status to be published.
 
973
 */
 
974
static void
 
975
presence_statusbar_changed_cb(GtkComboBox *combo)
 
976
{
 
977
    const gchar *status = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
 
978
    gboolean b = g_strcmp0(status, PRESENCE_STATUS_ONLINE) == 0;
 
979
    account_t * account;
 
980
 
 
981
    //send publish to every registered accounts with presence and publish enabled
 
982
    for (guint i = 0; i < account_list_get_size(); i++) {
 
983
        account = account_list_get_nth(i);
 
984
        g_assert(account);
 
985
        account_replace(account, CONFIG_PRESENCE_STATUS, status);
 
986
 
 
987
        if ((g_strcmp0(account_lookup(account, CONFIG_PRESENCE_PUBLISH_SUPPORTED), "true") == 0) &&
 
988
            (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_ENABLED), "true") == 0) &&
 
989
            (((g_strcmp0(account_lookup(account, CONFIG_ACCOUNT_ENABLE), "true") == 0) ||
 
990
            (account_is_IP2IP(account))))) {
 
991
            dbus_presence_publish(account->accountID, b);
 
992
            g_debug("Presence: publish status of acc:%s => %s", account->accountID, status);
 
993
        }
 
994
    }
 
995
}
 
996
 
 
997
void
 
998
update_presence_statusbar()
 
999
{
 
1000
    account_t * account;
 
1001
    gboolean global_publish_enabled = FALSE;
 
1002
    gboolean global_status = FALSE;
 
1003
 
 
1004
    if (!presence_status_bar) // presence window not opened
 
1005
        return;
 
1006
 
 
1007
    /* Check if one of the registered accounts has Presence enabled */
 
1008
    for (guint i = 0; i < account_list_get_size(); i++) {
 
1009
        account = account_list_get_nth(i);
 
1010
        g_assert(account);
 
1011
 
 
1012
        if (!(account_is_IP2IP(account)) &&
 
1013
                (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_ENABLED), "true") == 0) &&
 
1014
                (account->state == ACCOUNT_STATE_REGISTERED))
 
1015
        {
 
1016
            if (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_STATUS), PRESENCE_STATUS_ONLINE) == 0)
 
1017
                global_status = TRUE;
 
1018
 
 
1019
            if (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_PUBLISH_SUPPORTED), "true") == 0)
 
1020
                global_publish_enabled = TRUE;
 
1021
        }
 
1022
    }
 
1023
 
 
1024
    if (global_status)
 
1025
        g_debug("Presence: statusbar, at least 1 account is registered.");
 
1026
    if (global_publish_enabled)
 
1027
        g_debug("Presence: statusbar, at least 1 account can publish.");
 
1028
 
 
1029
    gtk_combo_box_set_active(GTK_COMBO_BOX(presence_status_combo),  global_status? 1:0);
 
1030
    gtk_widget_set_sensitive(presence_status_combo, global_publish_enabled? TRUE:FALSE);
 
1031
}
 
1032
 
 
1033
GtkWidget*
 
1034
create_presence_statusbar()
 
1035
{
 
1036
    GtkWidget *bar = gtk_statusbar_new();
 
1037
 
 
1038
    GtkWidget *label = gtk_label_new_with_mnemonic(_("Status:"));
 
1039
    gtk_box_pack_start(GTK_BOX(bar), label, TRUE, TRUE, 0);
 
1040
 
 
1041
    /* Add presence status combo_box*/
 
1042
    presence_status_combo = gtk_combo_box_text_new();
 
1043
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(presence_status_combo),
 
1044
            _(PRESENCE_STATUS_OFFLINE));
 
1045
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(presence_status_combo),
 
1046
            _(PRESENCE_STATUS_ONLINE));
 
1047
    gtk_widget_set_sensitive(presence_status_combo, TRUE);
 
1048
    gtk_box_pack_start(GTK_BOX(bar), presence_status_combo, TRUE, TRUE, 0);
 
1049
 
 
1050
    g_signal_connect(G_OBJECT(presence_status_combo), "changed",
 
1051
            G_CALLBACK(presence_statusbar_changed_cb), NULL);
 
1052
    //update_presence_statusbar();
 
1053
 
 
1054
    return bar;
 
1055
}
 
1056
 
 
1057
 
 
1058
/*************************** Menu bar callback *******************************/
 
1059
 
 
1060
void
 
1061
file_on_addbuddy_cb(GtkWidget *w)
 
1062
{
 
1063
    presence_view_popup_menu_onAddBuddy(w, NULL);
 
1064
}
 
1065
 
 
1066
void
 
1067
file_on_addgroup_cb(GtkWidget *w)
 
1068
{
 
1069
    presence_view_popup_menu_onAddGroup(w, NULL);
 
1070
}
 
1071
 
 
1072
void
 
1073
view_allbuddies_toggled_cb(GtkWidget *toggle)
 
1074
{
 
1075
    /* Display all the buddies including 'Not found' ones.*/
 
1076
    show_all = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle));
 
1077
    update_presence_view();
 
1078
}
 
1079
 
 
1080
void
 
1081
view_uri_toggled_cb(GtkWidget *toggle)
 
1082
{
 
1083
    // show or hide the uri column
 
1084
    GtkTreeViewColumn *col = gtk_tree_view_get_column(buddy_list_tree_view, (gint)COLUMN_URI);
 
1085
 
 
1086
    gtk_tree_view_column_set_visible(col,
 
1087
            gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle)));
 
1088
 
 
1089
    update_presence_view();
 
1090
}
 
1091
 
 
1092
GtkWidget *
 
1093
create_presence_menubar()
 
1094
{
 
1095
    GtkWidget *menu_bar = gtk_menu_bar_new();
 
1096
    GtkWidget *item_file = gtk_menu_item_new_with_label("File");
 
1097
    GtkWidget *item_view = gtk_menu_item_new_with_label("View");
 
1098
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), item_file);
 
1099
    gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), item_view);
 
1100
 
 
1101
    GtkWidget *menu;
 
1102
    GtkWidget *menu_item;
 
1103
 
 
1104
    menu = gtk_menu_new();
 
1105
    menu_item = gtk_menu_item_new_with_label("Add buddy...");
 
1106
    g_signal_connect(G_OBJECT(menu_item), "activate",
 
1107
            G_CALLBACK(file_on_addbuddy_cb), menu_item);
 
1108
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1109
    menu_item = gtk_menu_item_new_with_label("Add group...");
 
1110
    g_signal_connect(G_OBJECT(menu_item), "activate",
 
1111
            G_CALLBACK(file_on_addgroup_cb), menu_item);
 
1112
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1113
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_file), menu);
 
1114
    menu_item = gtk_separator_menu_item_new();
 
1115
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1116
    menu_item = gtk_menu_item_new_with_label("Close");
 
1117
    g_signal_connect(G_OBJECT(menu_item), "activate",
 
1118
            G_CALLBACK(destroy_presence_window), NULL);
 
1119
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1120
 
 
1121
 
 
1122
    menu = gtk_menu_new();
 
1123
    menu_item = gtk_check_menu_item_new_with_label("All buddies");
 
1124
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), FALSE);
 
1125
    g_signal_connect(G_OBJECT(menu_item), "activate",
 
1126
            G_CALLBACK(view_allbuddies_toggled_cb), menu_item);
 
1127
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1128
    menu_item = gtk_check_menu_item_new_with_label("Editable URIs");
 
1129
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), FALSE);
 
1130
    g_signal_connect(G_OBJECT(menu_item), "activate",
 
1131
            G_CALLBACK(view_uri_toggled_cb), menu_item);
 
1132
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
 
1133
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_view), menu);
 
1134
 
 
1135
    return menu_bar;
 
1136
}
 
1137
 
 
1138
/******************************** window  *********************************/
 
1139
gboolean
 
1140
buddy_subsribe_timer_cb()
 
1141
{
 
1142
    presence_buddy_list_init(presence_client);
 
1143
    return TRUE; // this is necessary to keep the timer alive
 
1144
}
 
1145
 
 
1146
void
 
1147
destroy_presence_window()
 
1148
{
 
1149
    g_debug("Destroy presence window ");
 
1150
    buddy_list_tree_view = NULL;
 
1151
    presence_status_bar = NULL;
 
1152
    gtk_widget_destroy(presence_window);
 
1153
 
 
1154
    gtk_toggle_action_set_active(toggle_action, FALSE);
 
1155
    //presence_buddy_list_flush();
 
1156
}
 
1157
 
 
1158
void
 
1159
create_presence_window(SFLPhoneClient *client, GtkToggleAction *action)
 
1160
{
 
1161
    static const int PRESENCE_WINDOW_WIDTH = 280;
 
1162
    static const int PRESENCE_WINDOW_HEIGHT = 320;
 
1163
 
 
1164
    /* keep track of the widget which opened that window and the SFL client */
 
1165
    toggle_action = action;
 
1166
    presence_client = client;
 
1167
 
 
1168
    const gchar * title = _("SFLphone Presence");
 
1169
    g_debug("Create window : %s", title);
 
1170
 
 
1171
    /*--------------------- Window -------------------------------------*/
 
1172
    presence_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
1173
    gtk_container_set_border_width(GTK_CONTAINER(presence_window), 0);
 
1174
    gtk_window_set_title(GTK_WINDOW(presence_window), title);
 
1175
    gtk_window_set_default_size(GTK_WINDOW(presence_window),
 
1176
            PRESENCE_WINDOW_WIDTH, PRESENCE_WINDOW_HEIGHT);
 
1177
    gtk_widget_set_name(presence_window, title);
 
1178
 
 
1179
    /*---------------- Instantiate vbox as homogeneous ----------------*/
 
1180
    GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 
1181
    gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
 
1182
    gtk_container_add(GTK_CONTAINER(presence_window), vbox);
 
1183
 
 
1184
    /*----------------------- Create the menu bar----------------------*/
 
1185
    GtkWidget * menu_bar = create_presence_menubar();
 
1186
    gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 3);
 
1187
 
 
1188
    /*---------------------- Create the tree view ---------------------*/
 
1189
    buddy_list_tree_view = create_presence_view();
 
1190
    gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(buddy_list_tree_view), TRUE, TRUE, 5);
 
1191
    gtk_tree_view_set_reorderable(buddy_list_tree_view, TRUE);
 
1192
    gtk_tree_view_set_rules_hint(buddy_list_tree_view,TRUE);
 
1193
    gtk_tree_view_set_headers_visible(buddy_list_tree_view, FALSE);
 
1194
 
 
1195
    /*---------------------- Drag-n-Drop ------------------------------*/
 
1196
    //DnD, the treeview is one the drag sources to drop buddies into groups
 
1197
    gtk_drag_source_set(GTK_WIDGET(buddy_list_tree_view), GDK_BUTTON1_MASK,
 
1198
            &presence_drag_targets, 1, GDK_ACTION_COPY|GDK_ACTION_MOVE);
 
1199
    g_signal_connect(GTK_WIDGET(buddy_list_tree_view), "drag-data-get",
 
1200
            G_CALLBACK(on_buddy_drag_data_get), NULL);
 
1201
    gtk_drag_dest_set(GTK_WIDGET(buddy_list_tree_view), GTK_DEST_DEFAULT_ALL,
 
1202
         &presence_drag_targets, 1, GDK_ACTION_COPY|GDK_ACTION_MOVE);
 
1203
    g_signal_connect(GTK_WIDGET(buddy_list_tree_view), "drag-data-received",
 
1204
            G_CALLBACK(on_buddy_drag_data_received), NULL);
 
1205
 
 
1206
    /*-------------------------- Status bar--------------------------- */
 
1207
    presence_status_bar = create_presence_statusbar();
 
1208
    gtk_box_pack_start(GTK_BOX(vbox), presence_status_bar, FALSE, TRUE, 0);
 
1209
 
 
1210
    g_signal_connect(G_OBJECT(buddy_list_tree_view), "button-press-event",
 
1211
            G_CALLBACK(presence_view_onButtonPressed), NULL);
 
1212
    g_signal_connect(G_OBJECT(presence_window), "button-press-event",
 
1213
            G_CALLBACK(presence_view_onButtonPressed), NULL);
 
1214
    g_signal_connect_after(presence_window, "destroy",
 
1215
            G_CALLBACK(destroy_presence_window), NULL);
 
1216
 
 
1217
    /*------------------------- Load presence -------------------------*/
 
1218
    presence_buddy_list_init(presence_client);
 
1219
    g_object_set_data(G_OBJECT(presence_window), "Buddy-List",
 
1220
            (gpointer) presence_buddy_list_get());
 
1221
    update_presence_view();
 
1222
    tmp_buddy = presence_buddy_create();
 
1223
 
 
1224
    /*------------------ Timer to refresh the subscription------------ */
 
1225
    g_timeout_add_seconds(120, (GSourceFunc) buddy_subsribe_timer_cb, NULL);
 
1226
 
 
1227
    /* make sure that everything, window and label, are visible */
 
1228
    gtk_widget_show_all(presence_window);
 
1229
}