calltree.c 65.3 KB
Newer Older
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3
 *  Author: Pierre-Luc Beaudoin <pierre-luc.beaudoin@savoirfairelinux.com>
4
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
5
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
Emmanuel Milou's avatar
Emmanuel Milou committed
6
 *
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
7
8
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 3 of the License, or
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
10
 *  (at your option) any later version.
Emmanuel Milou's avatar
Emmanuel Milou committed
11
 *
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
12
13
14
15
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
Emmanuel Milou's avatar
Emmanuel Milou committed
16
 *
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
17
18
19
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21
22
23
24
25
26
27
28
29
30
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
31
32
 */

Julien Bonjean's avatar
Julien Bonjean committed
33
#include <calltree.h>
34
#include <stdlib.h>
35
#include <glib/gprintf.h>
Julien Bonjean's avatar
Julien Bonjean committed
36
#include <eel-gconf-extensions.h>
37
38
39
40
41
42
43

#include "dbus.h"
#include "calllist.h"
#include "conferencelist.h"
#include "mainwindow.h"
#include "history.h"
#include "calltree.h"
Julien Bonjean's avatar
Julien Bonjean committed
44
45
#include "uimanager.h"
#include "actions.h"
46
#include "imwindow.h"
47
#include "searchbar.h"
48

49
50
51
52
// Messages used in menu item
#define SFL_CREATE_CONFERENCE "Create conference"
#define SFL_TRANSFER_CALL "Transfer call to"

53
54
55
56
static GtkWidget *calltree_sw = NULL;
static GtkCellRenderer *calltree_rend = NULL;
static GtkTreeViewColumn *calltree_col = NULL;
static GtkTreeSelection *calltree_sel = NULL;
57

58
59
static GtkWidget *calltree_popupmenu = NULL;
static GtkWidget *calltree_menu_items = NULL;
60

61
62
static CallType calltree_dragged_type = A_INVALID;
static CallType calltree_selected_type = A_INVALID;
63

64
65
static gchar *calltree_dragged_call_id = NULL;
static gchar *calltree_selected_call_id;
66

67
68
static gchar *calltree_dragged_path = NULL;
static gchar *calltree_selected_path = NULL;
69

70
71
static gint calltree_dragged_path_depth = -1;
static gint calltree_selected_path_depth = -1;
72

73
74
static callable_obj_t *calltree_dragged_call = NULL;
static callable_obj_t *calltree_selected_call = NULL;
75

76
77
static conference_obj_t *calltree_dragged_conf = NULL;
static conference_obj_t *calltree_selected_conf = NULL;
78

79
static void calltree_add_history_conference(conference_obj_t *);
80

81
static void drag_end_cb (GtkWidget *, GdkDragContext *, gpointer);
82
83
static void drag_data_received_cb (GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
static void drag_history_received_cb (GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
84
static void menuitem_response (gchar *);
85
static void calltree_create_conf_from_participant_list (GSList *);
86

87
enum {
88
89
90
    COLUMN_ACCOUNT_STATE = 0,
    COLUMN_ACCOUNT_DESC,
    COLUMN_ACCOUNT_SECURITY,
91
    COLUMN_ACCOUNT_PTR
92
};
93

94
95
96
/**
 * Show popup menu
 */
97
static gboolean
98
popup_menu (GtkWidget *widget,
99
            gpointer   user_data UNUSED)
100
{
101
102
    show_popup_menu (widget, NULL);
    return TRUE;
Emmanuel Milou's avatar
Emmanuel Milou committed
103
}
Emmanuel Milou's avatar
Emmanuel Milou committed
104

105
/* Call back when the user click on a call in the list */
106
107
static void
call_selected_cb (GtkTreeSelection *sel, void* data UNUSED)
108
{
109
    DEBUG ("CallTree: Selection callback");
110

Alexandre Savard's avatar
Alexandre Savard committed
111
    GtkTreeIter iter;
112
113
    GtkTreeModel *model = (GtkTreeModel*) active_calltree->store;

114
    if (!gtk_tree_selection_get_selected (sel, &model, &iter))
Alexandre Savard's avatar
Alexandre Savard committed
115
        return;
116

117
118
119
120
    if (active_calltree == history)
        DEBUG("CallTree: Current call tree is history");
    else if(active_calltree == current_calls)
        DEBUG("CallTree: Current call tree is current calls");
121

Alexandre Savard's avatar
Alexandre Savard committed
122
    // store info for dragndrop
123
124
125
    GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
    gchar *string_path = gtk_tree_path_to_string (path);
    calltree_selected_path_depth = gtk_tree_path_get_depth (path);
126

127
    GValue val;
128
    if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (model), &iter)) {
129

130
        DEBUG ("CallTree: Selected a conference");
131
        calltree_selected_type = A_CONFERENCE;
Alexandre Savard's avatar
Alexandre Savard committed
132

133
134
        val.g_type = 0;
        gtk_tree_model_get_value (model, &iter, COLUMN_ACCOUNT_PTR, &val);
135

136
        calltab_select_conf ( active_calltree, (conference_obj_t*) g_value_get_pointer (&val));
137

138
        calltree_selected_conf = (conference_obj_t*) g_value_get_pointer (&val);
139

140
        if (calltree_selected_conf) {
141

142
143
144
            calltree_selected_call_id = calltree_selected_conf->_confID;
            calltree_selected_path = string_path;
            calltree_selected_call = NULL;
145

146
147
            if (calltree_selected_conf->_im_widget)
                im_window_show_tab (calltree_selected_conf->_im_widget);
148
        }
149

150
        DEBUG ("CallTree: selected_path %s, selected_conf_id %s, selected_path_depth %d",
151
               calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
152

153
154
155
    } else {

        DEBUG ("CallTree: Selected a call");
156
        calltree_selected_type = A_CALL;
157
158
159
160
161
162

        val.g_type = 0;
        gtk_tree_model_get_value (model, &iter, COLUMN_ACCOUNT_PTR, &val);

        calltab_select_call (active_calltree, (callable_obj_t*) g_value_get_pointer (&val));

163
        calltree_selected_call = (callable_obj_t*) g_value_get_pointer (&val);
164

165
        if (calltree_selected_call) {
166

167
168
169
            calltree_selected_call_id = calltree_selected_call->_callID;
            calltree_selected_path = string_path;
            calltree_selected_conf = NULL;
170

171
172
            if (calltree_selected_call->_im_widget)
                im_window_show_tab (calltree_selected_call->_im_widget);
173
174
175
        }

        DEBUG ("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d",
176
               calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
Alexandre Savard's avatar
Alexandre Savard committed
177
    }
178

179
    g_value_unset (&val);
Alexandre Savard's avatar
Alexandre Savard committed
180
    update_actions();
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
181
182
}

183
/* A row is activated when it is double clicked */
184
185
186
187
188
189
void
row_activated (GtkTreeView       *tree_view UNUSED,
               GtkTreePath       *path UNUSED,
               GtkTreeViewColumn *column UNUSED,
               void * data UNUSED)
{
190
    DEBUG ("CallTree: Double click action");
191
192
193

    if (calltab_get_selected_type (active_calltree) == A_CALL) {

194
        DEBUG("CallTree: Selected a call");
195

196
        callable_obj_t *selectedCall = calltab_get_selected_call (active_calltree);
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220

        if (selectedCall) {
            // Get the right event from the right calltree
            if (active_calltree == current_calls) {

                switch (selectedCall->_state) {
                    case CALL_STATE_INCOMING:
                        dbus_accept (selectedCall);
                        break;
                    case CALL_STATE_HOLD:
                        dbus_unhold (selectedCall);
                        break;
                    case CALL_STATE_RINGING:
                    case CALL_STATE_CURRENT:
                    case CALL_STATE_BUSY:
                    case CALL_STATE_FAILURE:
                        break;
                    case CALL_STATE_DIALING:
                        sflphone_place_call (selectedCall);
                        break;
                    default:
                        WARN ("Row activated - Should not happen!");
                        break;
                }
221
222
            } else {
                // If history or contact: double click action places a new call
223
                callable_obj_t* new_call = create_new_call (CALL, CALL_STATE_DIALING, "", selectedCall->_accountID, selectedCall->_peer_name, selectedCall->_peer_number);
224

225
                calllist_add_call(current_calls, new_call);
226
                calltree_add_call (current_calls, new_call, NULL);
227
                // Function sflphone_place_call (new_call) is processed in process_dialing
228
                sflphone_place_call(new_call);
229
                calltree_display (current_calls);
230
231
            }
        }
232
233
    } else if (calltab_get_selected_type (active_calltree) == A_CONFERENCE) {

234
        DEBUG("CallTree: Seleceted a conference");
235
236

        if (active_calltree == current_calls) {
237
            conference_obj_t * selectedConf = calltab_get_selected_conf (current_calls);
238
239
240
241
            if (selectedConf) {

                switch (selectedConf->_state) {
                    case CONFERENCE_STATE_ACTIVE_DETACHED:
242
                    case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD:
243
244
245
                        sflphone_add_main_participant (selectedConf);
                        break;
                    case CONFERENCE_STATE_HOLD:
246
                    case CONFERENCE_STATE_HOLD_RECORD:
247
248
                        sflphone_conference_off_hold (selectedConf);
                        break;
Julien Bonjean's avatar
Julien Bonjean committed
249
                    case CONFERENCE_STATE_ACTIVE_ATACHED:
250
                    case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD:
Julien Bonjean's avatar
Julien Bonjean committed
251
252
                    default:
                        break;
253
254
255
                }
            }
        }
256
257
258
259
260
261
262
263
264
265
        else if (active_calltree == history) {
            conference_obj_t* selectedConf = calltab_get_selected_conf(history);
            if (selectedConf == NULL) {
                ERROR("CallTree: Error: Could not get selected conference from history");
                return;
            }

            calltree_create_conf_from_participant_list(selectedConf->participant_list); 
            calltree_display(current_calls); 
        }
Alexandre Savard's avatar
Alexandre Savard committed
266
    }
267
}
268

269
static void 
270
271
calltree_create_conf_from_participant_list(GSList *list)
{
Alexandre Savard's avatar
Alexandre Savard committed
272
    gchar **participant_list;
273
274
275
276
277
278
    gint list_length = g_slist_length(list);

    DEBUG("CallTree: Create conference from participant list");

    participant_list = (void *) malloc(sizeof(void*));

279
    // concatenate 
280
281
282
283
    gint i, c;
    for(i = 0, c = 0; i < list_length; i++, c++) {
        gchar *number;
        gchar *participant_id = g_slist_nth_data(list, i);
284
        callable_obj_t *call = calllist_get_call(history, participant_id);
285

286
287
        if (c != 0)
            participant_list = (void *) realloc(participant_list, (c+1) * sizeof(void *));
288

289
290
        // allocate memory for the participant number
        number = g_strconcat(call->_peer_number, ",", call->_accountID, NULL);
291

292
        *(participant_list + c) = number;
293
294
    }

295
296
297
    participant_list = (void *) realloc(participant_list, (c+1) *sizeof(void*));
    *(participant_list+c) = NULL;

Alexandre Savard's avatar
Alexandre Savard committed
298
    dbus_create_conf_from_participant_list((const gchar **)participant_list);
299
}
300

301
/* Catch cursor-activated signal. That is, when the entry is single clicked */
302
static void
303
row_single_click (GtkTreeView *tree_view UNUSED, void * data UNUSED)
304
{
305
    callable_obj_t * selectedCall = NULL;
306
    conference_obj_t *selectedConf = NULL;
307
    gchar * displaySasOnce = NULL;
308

309
    DEBUG ("CallTree: Single click action");
310

311
    selectedCall = calltab_get_selected_call (active_calltree);
312
    selectedConf = calltab_get_selected_conf (active_calltree);
313

314
    if (active_calltree == current_calls)
315
        DEBUG("CallTree: Active calltree is current_calls");
316
    else if (active_calltree == history)
317
        DEBUG("CallTree: Active calltree is history");
318

319
    if(calltab_get_selected_type(active_calltree) == A_CALL) {
320

321
        DEBUG("CallTree: Selected a call");
322

323
        if (selectedCall) {
324
            account_t *account_details = account_list_get_by_id (selectedCall->_accountID);
325
            DEBUG ("AccountID %s", selectedCall->_accountID);
326

327
328
329
330
            if (account_details != NULL) {
                displaySasOnce = g_hash_table_lookup (account_details->properties, ACCOUNT_DISPLAY_SAS_ONCE);
                DEBUG ("Display SAS once %s", displaySasOnce);
            } else {
331
                GHashTable *properties = sflphone_get_ip2ip_properties();
332
333
334
335
336
                if (properties != NULL) {
                    displaySasOnce = g_hash_table_lookup (properties, ACCOUNT_DISPLAY_SAS_ONCE);
                    DEBUG ("IP2IP displaysasonce %s", displaySasOnce);
                }
            }
337

338
339
340
341
342
343
344
            /*  Make sure that we are not in the history tab since
             *  nothing is defined for it yet
             */
            if (active_calltree == current_calls) {
                switch (selectedCall->_srtp_state) {
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
                        selectedCall->_srtp_state = SRTP_STATE_ZRTP_SAS_CONFIRMED;
345

346
                        if (g_strcasecmp (displaySasOnce, "true") == 0)
347
                            selectedCall->_zrtp_confirmed = TRUE;
348

349
350
351
352
353
354
355
356
357
358
359
360
                        dbus_confirm_sas (selectedCall);
                        calltree_update_call (current_calls, selectedCall, NULL);
                        break;
                    case SRTP_STATE_ZRTP_SAS_CONFIRMED:
                        selectedCall->_srtp_state = SRTP_STATE_ZRTP_SAS_UNCONFIRMED;
                        dbus_reset_sas (selectedCall);
                        calltree_update_call (current_calls, selectedCall, NULL);
                        break;
                    default:
                        DEBUG ("Single click but no action");
                        break;
                }
361
362
            }
        }
Alexandre Savard's avatar
Alexandre Savard committed
363
    }
364
365
366
    else if(calltab_get_selected_type(active_calltree) == A_CONFERENCE) {
        DEBUG("CallTree: Selected a conference");

367
        if (selectedConf)
368
369
            DEBUG("CallTree: There is actually a selected conf");
    }
370
    else
371
        WARN("CallTree: Warning: Unknow selection type");
372
373
}

374
375
static gboolean
button_pressed (GtkWidget* widget, GdkEventButton *event, gpointer user_data UNUSED)
Julien Bonjean's avatar
Julien Bonjean committed
376
{
377
378
379
380
381
382
383
384
385
    if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
        return FALSE;

    if (active_calltree == current_calls)
        show_popup_menu (widget,  event);
    else if (active_calltree == history)
        show_popup_menu_history (widget,  event);
    else
        show_popup_menu_contacts (widget, event);
386

387
    return TRUE;
Julien Bonjean's avatar
Julien Bonjean committed
388
389
}

390

391
static gchar *
392
calltree_display_call_info (callable_obj_t * c, CallDisplayType display_type, const gchar *const audio_codec)
393
{
394
395
    gchar display_number[strlen(c->_peer_number) + 1];
    strcpy(display_number, c->_peer_number);
396

397
    if (c->_type != CALL || c->_history_state != OUTGOING) {
398
        // Get the hostname for this call (NULL if not existent)
399
        gchar * hostname = g_strrstr (c->_peer_number, "@");
400
401

        // Test if we are dialing a new number
402
403
        if (*c->_peer_number && hostname)
            display_number[hostname - c->_peer_number] = '\0';
404
    }
405

406
    // Different display depending on type
407
408
409
410
411
412
413
    const gchar *name, *details = NULL;
    if (*c->_peer_name) {
        name = c->_peer_name;
        details = display_number;
    } else {
        name = display_number;
        details = "";
414
    }
415

416
    gchar *desc = g_markup_printf_escaped ("<b>%s</b>   <i>%s</i>   ", name, details);
417
    gchar *suffix = NULL;
418

419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    switch (display_type) {
    case DISPLAY_TYPE_CALL:
        if (c->_state_code)
            suffix = g_markup_printf_escaped ("\n<i>%s (%d)</i>", c->_state_code_description, c->_state_code);
        break;
    case DISPLAY_TYPE_STATE_CODE :
        if (c->_state_code)
            suffix = g_markup_printf_escaped ("\n<i>%s (%d)</i>  <i>%s</i>",
                    c->_state_code_description, c->_state_code,
                    audio_codec);
        else
            suffix = g_markup_printf_escaped ("\n<i>%s</i>", audio_codec);
        break;
    case DISPLAY_TYPE_CALL_TRANSFER:
        suffix = g_markup_printf_escaped ("\n<i>Transfer to:%s</i> ", c->_trsft_to);
        break;
    case DISPLAY_TYPE_SAS:
        suffix = g_markup_printf_escaped ("\n<i>Confirm SAS <b>%s</b> ?</i>", c->_sas);
        break;
    case DISPLAY_TYPE_HISTORY :
    default:
        break;
    }
442

443
444
445
446
    gchar *msg = g_strconcat(desc, suffix, NULL);
    g_free(desc);
    g_free(suffix);
    return msg;
447
448
}

449
void
Julien Bonjean's avatar
Julien Bonjean committed
450
calltree_reset (calltab_t* tab)
Emmanuel Milou's avatar
Emmanuel Milou committed
451
{
452
    gtk_tree_store_clear (tab->store);
Emmanuel Milou's avatar
Emmanuel Milou committed
453
454
}

455
void
456
calltree_create (calltab_t* tab, gboolean searchbar_type)
areversat's avatar
areversat committed
457
{
458
459
460
    gchar *conference = SFL_CREATE_CONFERENCE;
    gchar *transfer = SFL_TRANSFER_CALL;

461
    tab->tree = gtk_vbox_new (FALSE, 10);
462

463
464
    // Fix bug #708 (resize)
    gtk_widget_set_size_request (tab->tree,100,80);
465

466
    gtk_container_set_border_width (GTK_CONTAINER (tab->tree), 0);
467

468
469
470
    calltree_sw = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (calltree_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (calltree_sw), GTK_SHADOW_IN);
471
472


473
    tab->store = gtk_tree_store_new (4,
474
475
476
477
478
            GDK_TYPE_PIXBUF,// Icon
            G_TYPE_STRING,  // Description
            GDK_TYPE_PIXBUF, // Security Icon
            G_TYPE_POINTER  // Pointer to the Object
            );
479

480
481
482
483
    tab->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tab->store));
    gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tab->view), FALSE);
    gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tab->view), FALSE);
    g_signal_connect (G_OBJECT (tab->view), "row-activated",
484
485
            G_CALLBACK (row_activated),
            NULL);
486

487
488
    gtk_widget_set_can_focus (calltree_sw, TRUE);
    gtk_widget_grab_focus (calltree_sw);
489

490
    g_signal_connect (G_OBJECT (tab->view), "cursor-changed",
491
492
            G_CALLBACK (row_single_click),
            NULL);
493

494
495
    // Connect the popup menu
    g_signal_connect (G_OBJECT (tab->view), "popup-menu",
496
497
            G_CALLBACK (popup_menu),
            NULL);
498
    g_signal_connect (G_OBJECT (tab->view), "button-press-event",
499
500
            G_CALLBACK (button_pressed),
            NULL);
501

502
    if (tab != history && tab!=contacts) {
503

504
505
        // Make calltree reordable for drag n drop
        gtk_tree_view_set_reorderable (GTK_TREE_VIEW (tab->view), TRUE);
506

507
508
        // source widget drag n drop signals
        g_signal_connect (G_OBJECT (tab->view), "drag_end", G_CALLBACK (drag_end_cb), NULL);
509

510
511
        // destination widget drag n drop signals
        g_signal_connect (G_OBJECT (tab->view), "drag_data_received", G_CALLBACK (drag_data_received_cb), NULL);
512

513
        calltree_popupmenu = gtk_menu_new ();
514

515
516
517
518
519
        calltree_menu_items = gtk_menu_item_new_with_label (transfer);
        g_signal_connect_swapped (calltree_menu_items, "activate",
                G_CALLBACK (menuitem_response), (gpointer) g_strdup (transfer));
        gtk_menu_shell_append (GTK_MENU_SHELL (calltree_popupmenu), calltree_menu_items);
        gtk_widget_show (calltree_menu_items);
520

521
522
523
524
525
        calltree_menu_items = gtk_menu_item_new_with_label (conference);
        g_signal_connect_swapped (calltree_menu_items, "activate",
                G_CALLBACK (menuitem_response), (gpointer) g_strdup (conference));
        gtk_menu_shell_append (GTK_MENU_SHELL (calltree_popupmenu), calltree_menu_items);
        gtk_widget_show (calltree_menu_items);
526
    }
527

528
529
530
    if (tab == history) {
        gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(tab->view), TRUE);
        g_signal_connect (G_OBJECT (tab->view), "drag_data_received", G_CALLBACK (drag_history_received_cb), NULL);
531
    }
532

533
534
    gtk_widget_grab_focus (GTK_WIDGET (tab->view));

535
536
537
538
    calltree_rend = gtk_cell_renderer_pixbuf_new();
    calltree_col = gtk_tree_view_column_new_with_attributes ("Icon", calltree_rend, "pixbuf", 0,
                                                    NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (tab->view), calltree_col);
539

540
541
542
543
544
545
546
    calltree_rend = gtk_cell_renderer_text_new();
    calltree_col = gtk_tree_view_column_new_with_attributes ("Description", calltree_rend,
                                                    "markup", COLUMN_ACCOUNT_DESC,
                                                    NULL);
    g_object_set (calltree_rend, "wrap-mode", (PangoWrapMode) PANGO_WRAP_WORD_CHAR, NULL);
    g_object_set (calltree_rend, "wrap-width", (gint) CALLTREE_TEXT_WIDTH, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (tab->view), calltree_col);
547
548

    /* Security icon */
549
550
551
    calltree_rend = gtk_cell_renderer_pixbuf_new();
    calltree_col = gtk_tree_view_column_new_with_attributes ("Icon",
            calltree_rend,
552
553
            "pixbuf", COLUMN_ACCOUNT_SECURITY,
            NULL);
554
555
556
    g_object_set (calltree_rend, "xalign", (gfloat) 1.0, NULL);
    g_object_set (calltree_rend, "yalign", (gfloat) 0.0, NULL);
    gtk_tree_view_append_column (GTK_TREE_VIEW (tab->view), calltree_col);
557
558

    g_object_unref (G_OBJECT (tab->store));
559
    gtk_container_add (GTK_CONTAINER (calltree_sw), tab->view);
560

561
562
563
564
    calltree_sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tab->view));
    g_signal_connect (G_OBJECT (calltree_sel), "changed",
            G_CALLBACK (call_selected_cb),
            NULL);
565

566
    gtk_box_pack_start (GTK_BOX (tab->tree), calltree_sw, TRUE, TRUE, 0);
567
568
569
570

    // search bar if tab is either "history" or "addressbook"
    if (searchbar_type) {
        calltab_create_searchbar (tab);
571
        if(tab->searchbar != NULL)
572
            gtk_box_pack_start (GTK_BOX (tab->tree), tab->searchbar, FALSE, TRUE, 0);
573
    }
574

575
576
    gtk_widget_show (tab->tree);
}
577
578


579
580
581
582
583
void
calltree_remove_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
{
    GtkTreeIter iter;
    GtkTreeStore* store = tab->store;
584

585
586
    if (!c)
        ERROR ("CallTree: Error: Not a valid call");
587

588
    DEBUG ("CallTree: Remove call %s", c->_callID);
589

590
    int nbChild = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), parent);
591
    for (int i = 0; i < nbChild; i++) {
592
        if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, parent, i)) {
593
            if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter))
594
                calltree_remove_call (tab, c, &iter);
595

596
            GValue val = { .g_type = 0 };
597
            gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, COLUMN_ACCOUNT_PTR, &val);
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
598

599
            callable_obj_t * iterCall = g_value_get_pointer (&val);
600
            g_value_unset (&val);
601

602
            if (iterCall == c)
603
604
605
                gtk_tree_store_remove (store, &iter);
        }
    }
606

607
    if (calltab_get_selected_call (tab) == c)
608
609
610
611
        calltab_select_call (tab, NULL);

    update_actions();

612
    statusbar_update_clock("");
613
}
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
614

615
void
616
calltree_update_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
617
{
618
619
    GdkPixbuf *pixbuf = NULL;
    GdkPixbuf *pixbuf_security = NULL;
620
621
622
623
    GtkTreeIter iter;
    GValue val;
    GtkTreeStore* store = tab->store;

624
    gchar* srtp_enabled = NULL;
625
626
627
    gboolean display_sas = TRUE;
    account_t* account_details=NULL;

628
    int nbChild = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), parent);
629
630
    int i;

631
    if (c) {
632
633
634
635
636
        account_details = account_list_get_by_id (c->_accountID);

        if (account_details != NULL) {
            srtp_enabled = g_hash_table_lookup (account_details->properties, ACCOUNT_SRTP_ENABLED);

637
            if (g_strcasecmp (g_hash_table_lookup (account_details->properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
638
639
                display_sas = FALSE;
        } else {
640
            GHashTable * properties = sflphone_get_ip2ip_properties();
641
642
643
644

            if (properties != NULL) {
                srtp_enabled = g_hash_table_lookup (properties, ACCOUNT_SRTP_ENABLED);

645
                if (g_strcasecmp (g_hash_table_lookup (properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
646
647
648
649
650
651
652
653
654
                    display_sas = FALSE;
            }
        }
    }

    for (i = 0; i < nbChild; i++) {

        if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, parent, i)) {

655
            if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter))
656
657
658
659
660
                calltree_update_call (tab, c, &iter);

            val.g_type = 0;
            gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, COLUMN_ACCOUNT_PTR, &val);

661
            callable_obj_t * iterCall = (callable_obj_t*) g_value_get_pointer (&val);
662
663
            g_value_unset (&val);

664
            if (iterCall != c)
665
666
667
                continue;

            /* Update text */
668
            gchar * description = NULL;
669
            gchar * audio_codec = call_get_audio_codec (c);
670

671
            if (c->_state == CALL_STATE_TRANSFER)
672
                description = calltree_display_call_info (c, DISPLAY_TYPE_CALL_TRANSFER, "");
673
            else {
674
675
                if (c->_sas && display_sas && c->_srtp_state == SRTP_STATE_ZRTP_SAS_UNCONFIRMED && !c->_zrtp_confirmed)
                    description = calltree_display_call_info (c, DISPLAY_TYPE_SAS, "");
676
677
                else
                    description = calltree_display_call_info (c, DISPLAY_TYPE_STATE_CODE, audio_codec);
678
679
            }

680
681
            g_free (audio_codec);

682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
            /* Update icons */
            if (tab == current_calls) {
                DEBUG ("Receiving in state %d", c->_state);

                switch (c->_state) {
                    case CALL_STATE_HOLD:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/hold.svg", NULL);
                        break;
                    case CALL_STATE_INCOMING:
                    case CALL_STATE_RINGING:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/ring.svg", NULL);
                        break;
                    case CALL_STATE_CURRENT:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/current.svg", NULL);
                        break;
                    case CALL_STATE_DIALING:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/dial.svg", NULL);
                        break;
                    case CALL_STATE_FAILURE:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/fail.svg", NULL);
                        break;
                    case CALL_STATE_BUSY:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/busy.svg", NULL);
                        break;
706
707
                    case CALL_STATE_TRANSFER:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/transfer.svg", NULL);
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
                        break;
                    case CALL_STATE_RECORD:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/icon_rec.svg", NULL);
                        break;
                    default:
                        WARN ("Update calltree - Should not happen!");
                }

                switch (c->_srtp_state) {
                    case SRTP_STATE_SDES_SUCCESS:
                        DEBUG ("SDES negotiation succes");
                        pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_confirmed.svg", NULL);
                        break;
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
                        DEBUG ("Secure is ON");
                        pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_unconfirmed.svg", NULL);
                        if (c->_sas != NULL) {
                            DEBUG ("SAS is ready with value %s", c->_sas);
                        }
                        break;
                    case SRTP_STATE_ZRTP_SAS_CONFIRMED:
                        DEBUG ("SAS is confirmed");
                        pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_confirmed.svg", NULL);
                        break;
                    case SRTP_STATE_ZRTP_SAS_SIGNED:
                        DEBUG ("Secure is ON with SAS signed and verified");
                        pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_certified.svg", NULL);
                        break;
                    case SRTP_STATE_UNLOCKED:
                        DEBUG ("Secure is off calltree %d", c->_state);

739
                        if (g_strcasecmp (srtp_enabled,"true") == 0)
740
741
742
743
744
                            pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_off.svg", NULL);
                        break;
                    default:
                        WARN ("Update calltree srtp state #%d- Should not happen!", c->_srtp_state);

745
                        if (g_strcasecmp (srtp_enabled, "true") == 0)
746
747
748
749
                            pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_off.svg", NULL);
                }

            } else if (tab == history) {
750
751
                if (parent == NULL) {
                    // parent is NULL this is not a conference participant
752
753
754
755
756
757
758
759
760
761
762
763
764
                    switch (c->_history_state) {
                        case INCOMING:
                            pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/incoming.svg", NULL);
                            break;
                        case OUTGOING:
                            pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/outgoing.svg", NULL);
                            break;
                        case MISSED:
                            pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/missed.svg", NULL);
                            break;
                        default:
                            WARN ("History - Should not happen!");
                    }
765
766
767
                }
                else // parent is not NULL this is a conference participant
                    pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
768

769
770
771
                gchar *old_description = description;
                g_free (old_description);

772
                description = calltree_display_call_info (c, DISPLAY_TYPE_HISTORY, "");
773
774
775
776
777
778
779
780
781
782
                gchar * date = get_formatted_start_timestamp (c->_time_start);
                gchar *duration = get_call_duration (c);
                gchar *full_duration = g_strconcat (date , duration , NULL);
                g_free (date);
                g_free (duration);

                old_description = description;
                description = g_strconcat (old_description , full_duration, NULL);
                g_free (full_duration);
                g_free (old_description);
783
784
785
            }

            gtk_tree_store_set (store, &iter,
786
787
788
789
790
791
792
                    0, pixbuf, // Icon
                    1, description, // Description
                    2, pixbuf_security,
                    3, c,
                    -1);

            g_free (description);
793
794
795
796

            if (pixbuf != NULL)
                g_object_unref (G_OBJECT (pixbuf));
        }
797
    }
798

799
    update_actions();
Emmanuel Milou's avatar
Emmanuel Milou committed
800
801
}

802

803
void calltree_add_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
804
{
805
    DEBUG ("----------------------------------------------- CallTree: Add call to calltree id: %s, peer name: %s", c->_callID, c->_peer_name);
806
807

    if (tab == history) {
808
        calltree_add_history_entry (c, parent);
809
810
811
        return;
    }

812
    account_t* account_details = NULL;
813

814
815
    GdkPixbuf *pixbuf = NULL;
    GdkPixbuf *pixbuf_security = NULL;
816
    GtkTreeIter iter;
817
818
    gchar* key_exchange = NULL;
    gchar* srtp_enabled = NULL;
819
820
821

    // New call in the list

822
    const gchar * description = calltree_display_call_info (c, DISPLAY_TYPE_CALL, "");
823
824
825

    gtk_tree_store_prepend (tab->store, &iter, parent);

826
    if (c) {
827
        account_details = account_list_get_by_id (c->_accountID);
828
        if (account_details) {
829
830
831
832
833
            srtp_enabled = g_hash_table_lookup (account_details->properties, ACCOUNT_SRTP_ENABLED);
            key_exchange = g_hash_table_lookup (account_details->properties, ACCOUNT_KEY_EXCHANGE);
        }
    }

Julien Bonjean's avatar
Julien Bonjean committed
834
    DEBUG ("Added call key exchange is %s", key_exchange);
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854

    if (tab == current_calls) {
        switch (c->_state) {
            case CALL_STATE_INCOMING:
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/ring.svg", NULL);
                break;
            case CALL_STATE_DIALING:
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/dial.svg", NULL);
                break;
            case CALL_STATE_RINGING:
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/ring.svg", NULL);
                break;
            case CALL_STATE_CURRENT:
                // If the call has been initiated by a another client and, when we start, it is already current
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/current.svg", NULL);
                break;
            case CALL_STATE_HOLD:
                // If the call has been initiated by a another client and, when we start, it is already current
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/hold.svg", NULL);
                break;
855
856
857
            case CALL_STATE_RECORD:
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
                break;
858
859
860
861
862
863
864
            case CALL_STATE_FAILURE:
                // If the call has been initiated by a another client and, when we start, it is already current
                pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/fail.svg", NULL);
                break;
            default:
                WARN ("Update calltree add - Should not happen!");
        }
865
        if (srtp_enabled && g_strcasecmp (srtp_enabled, "true") == 0)
866
867
            pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/secure_off.svg", NULL);

868
    } else if (tab == contacts) {
869
870
        pixbuf = c->_contact_thumbnail;
        description = g_strconcat (description , NULL);
871
    } else {
872
        WARN ("CallTree: This widget doesn't exist - This is a bug in the application.");
873
874
875
    }

    //Resize it
876
877
    if (pixbuf)
        if (gdk_pixbuf_get_width (pixbuf) > 32 || gdk_pixbuf_get_height (pixbuf) > 32)
878
879
            pixbuf =  gdk_pixbuf_scale_simple (pixbuf, 32, 32, GDK_INTERP_BILINEAR);

880
881
    if (pixbuf_security)
        if (gdk_pixbuf_get_width (pixbuf_security) > 32 || gdk_pixbuf_get_height (pixbuf_security) > 32)
882
883
884
            pixbuf_security =  gdk_pixbuf_scale_simple (pixbuf_security, 32, 32, GDK_INTERP_BILINEAR);

    gtk_tree_store_set (tab->store, &iter,
885