calltree.c 65.7 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
450
}



Emmanuel Milou's avatar
Emmanuel Milou committed
451
452
453
/**
 * Reset call tree
 */
454
void
Julien Bonjean's avatar
Julien Bonjean committed
455
calltree_reset (calltab_t* tab)
Emmanuel Milou's avatar
Emmanuel Milou committed
456
{
457
    gtk_tree_store_clear (tab->store);
Emmanuel Milou's avatar
Emmanuel Milou committed
458
459
}

460
static void
461
462
463
focus_on_calltree_out()
{
    focus_is_on_calltree = FALSE;
464
465
}

466
static void
467
468
469
focus_on_calltree_in()
{
    focus_is_on_calltree = TRUE;
470
471
}

472
void
473
calltree_create (calltab_t* tab, gboolean searchbar_type)
areversat's avatar
areversat committed
474
{
475
476
477
    gchar *conference = SFL_CREATE_CONFERENCE;
    gchar *transfer = SFL_TRANSFER_CALL;

478
    tab->tree = gtk_vbox_new (FALSE, 10);
479

480
481
    // Fix bug #708 (resize)
    gtk_widget_set_size_request (tab->tree,100,80);
482

483
    gtk_container_set_border_width (GTK_CONTAINER (tab->tree), 0);
484

485
486
487
    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);
488
489


490
    tab->store = gtk_tree_store_new (4,
491
492
493
494
495
            GDK_TYPE_PIXBUF,// Icon
            G_TYPE_STRING,  // Description
            GDK_TYPE_PIXBUF, // Security Icon
            G_TYPE_POINTER  // Pointer to the Object
            );
496

497
498
499
500
    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",
501
502
            G_CALLBACK (row_activated),
            NULL);
503

504
505
    GTK_WIDGET_SET_FLAGS (GTK_WIDGET (calltree_sw),GTK_CAN_FOCUS);
    gtk_widget_grab_focus (GTK_WIDGET (calltree_sw));
506

507
    g_signal_connect (G_OBJECT (tab->view), "cursor-changed",
508
509
            G_CALLBACK (row_single_click),
            NULL);
510

511
512
    // Connect the popup menu
    g_signal_connect (G_OBJECT (tab->view), "popup-menu",
513
514
            G_CALLBACK (popup_menu),
            NULL);
515
    g_signal_connect (G_OBJECT (tab->view), "button-press-event",
516
517
            G_CALLBACK (button_pressed),
            NULL);
518

519
    g_signal_connect_after (G_OBJECT (tab->view), "focus-in-event",
520
            G_CALLBACK (focus_on_calltree_in), NULL);
521
    g_signal_connect_after (G_OBJECT (tab->view), "focus-out-event",
522
            G_CALLBACK (focus_on_calltree_out), NULL);
523
524


525
    if (tab != history && tab!=contacts) {
526

527
528
        // Make calltree reordable for drag n drop
        gtk_tree_view_set_reorderable (GTK_TREE_VIEW (tab->view), TRUE);
529

530
531
        // source widget drag n drop signals
        g_signal_connect (G_OBJECT (tab->view), "drag_end", G_CALLBACK (drag_end_cb), NULL);
532

533
534
        // destination widget drag n drop signals
        g_signal_connect (G_OBJECT (tab->view), "drag_data_received", G_CALLBACK (drag_data_received_cb), NULL);
535

536
        calltree_popupmenu = gtk_menu_new ();
537

538
539
540
541
542
        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);
543

544
545
546
547
548
        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);
549
    }
550

551
552
553
    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);
554
    }
555

556
557
    gtk_widget_grab_focus (GTK_WIDGET (tab->view));

558
559
560
561
    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);
562

563
564
565
566
567
568
569
    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);
570
571

    /* Security icon */
572
573
574
    calltree_rend = gtk_cell_renderer_pixbuf_new();
    calltree_col = gtk_tree_view_column_new_with_attributes ("Icon",
            calltree_rend,
575
576
            "pixbuf", COLUMN_ACCOUNT_SECURITY,
            NULL);
577
578
579
    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);
580
581

    g_object_unref (G_OBJECT (tab->store));
582
    gtk_container_add (GTK_CONTAINER (calltree_sw), tab->view);
583

584
585
586
587
    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);
588

589
    gtk_box_pack_start (GTK_BOX (tab->tree), calltree_sw, TRUE, TRUE, 0);
590
591
592
593

    // search bar if tab is either "history" or "addressbook"
    if (searchbar_type) {
        calltab_create_searchbar (tab);
594
        if(tab->searchbar != NULL)
595
            gtk_box_pack_start (GTK_BOX (tab->tree), tab->searchbar, FALSE, TRUE, 0);
596
    }
597

598
599
    gtk_widget_show (tab->tree);
}
600
601


602
603
604
605
606
void
calltree_remove_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
{
    GtkTreeIter iter;
    GtkTreeStore* store = tab->store;
607

608
609
    if (!c)
        ERROR ("CallTree: Error: Not a valid call");
610

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

613
    int nbChild = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), parent);
614
    for (int i = 0; i < nbChild; i++) {
615
        if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, parent, i)) {
616
            if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter))
617
                calltree_remove_call (tab, c, &iter);
618

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

622
            callable_obj_t * iterCall = g_value_get_pointer (&val);
623
            g_value_unset (&val);
624

625
            if (iterCall == c)
626
627
628
                gtk_tree_store_remove (store, &iter);
        }
    }
629

630
    if (calltab_get_selected_call (tab) == c)
631
632
633
634
        calltab_select_call (tab, NULL);

    update_actions();

635
    statusbar_update_clock("");
636
}
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
637

638
void
639
calltree_update_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
640
{
641
642
    GdkPixbuf *pixbuf = NULL;
    GdkPixbuf *pixbuf_security = NULL;
643
644
645
646
    GtkTreeIter iter;
    GValue val;
    GtkTreeStore* store = tab->store;

647
    gchar* srtp_enabled = NULL;
648
649
650
    gboolean display_sas = TRUE;
    account_t* account_details=NULL;

651
    int nbChild = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), parent);
652
653
    int i;

654
    if (c) {
655
656
657
658
659
        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);

660
            if (g_strcasecmp (g_hash_table_lookup (account_details->properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
661
662
                display_sas = FALSE;
        } else {
663
            GHashTable * properties = sflphone_get_ip2ip_properties();
664
665
666
667

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

668
                if (g_strcasecmp (g_hash_table_lookup (properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
669
670
671
672
673
674
675
676
677
                    display_sas = FALSE;
            }
        }
    }

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

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

678
            if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (store), &iter))
679
680
681
682
683
                calltree_update_call (tab, c, &iter);

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

684
            callable_obj_t * iterCall = (callable_obj_t*) g_value_get_pointer (&val);
685
686
            g_value_unset (&val);

687
            if (iterCall != c)
688
689
690
                continue;

            /* Update text */
691
            gchar * description = NULL;
692
            gchar * audio_codec = call_get_audio_codec (c);
693

694
            if (c->_state == CALL_STATE_TRANSFER)
695
                description = calltree_display_call_info (c, DISPLAY_TYPE_CALL_TRANSFER, "");
696
            else {
697
698
                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, "");
699
700
                else
                    description = calltree_display_call_info (c, DISPLAY_TYPE_STATE_CODE, audio_codec);
701
702
            }

703
704
            g_free (audio_codec);

705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
            /* 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;
729
730
                    case CALL_STATE_TRANSFER:
                        pixbuf = gdk_pixbuf_new_from_file (ICONS_DIR "/transfer.svg", NULL);
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
                        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);

762
                        if (g_strcasecmp (srtp_enabled,"true") == 0)
763
764
765
766
767
                            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);

768
                        if (g_strcasecmp (srtp_enabled, "true") == 0)
769
770
771
772
                            pixbuf_security = gdk_pixbuf_new_from_file (ICONS_DIR "/lock_off.svg", NULL);
                }

            } else if (tab == history) {
773
774
                if (parent == NULL) {
                    // parent is NULL this is not a conference participant
775
776
777
778
779
780
781
782
783
784
785
786
787
                    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!");
                    }
788
789
790
                }
                else // parent is not NULL this is a conference participant
                    pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
791

792
793
794
                gchar *old_description = description;
                g_free (old_description);

795
                description = calltree_display_call_info (c, DISPLAY_TYPE_HISTORY, "");
796
797
798
799
800
801
802
803
804
805
                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);
806
807
808
            }

            gtk_tree_store_set (store, &iter,
809
810
811
812
813
814
815
                    0, pixbuf, // Icon
                    1, description, // Description
                    2, pixbuf_security,
                    3, c,
                    -1);

            g_free (description);
816
817
818
819

            if (pixbuf != NULL)
                g_object_unref (G_OBJECT (pixbuf));
        }
820
    }
821

822
    update_actions();
Emmanuel Milou's avatar
Emmanuel Milou committed
823
824
}

825

826
void calltree_add_call (calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
827
{
828
    DEBUG ("----------------------------------------------- CallTree: Add call to calltree id: %s, peer name: %s", c->_callID, c->_peer_name);
829
830

    if (tab == history) {
831
        calltree_add_history_entry (c, parent);
832
833
834
        return;
    }

835
    account_t* account_details = NULL;
836

837
838
    GdkPixbuf *pixbuf = NULL;
    GdkPixbuf *pixbuf_security = NULL;
839
    GtkTreeIter iter;
840
841
    gchar* key_exchange = NULL;
    gchar* srtp_enabled = NULL;
842
843
844

    // New call in the list

845
    const gchar * description = calltree_display_call_info (c, DISPLAY_TYPE_CALL, "");
846
847
848

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

849
    if (c) {
850
        account_details = account_list_get_by_id (c->_accountID);
851
        if (account_details) {
852
853
854
855
856
            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
857
    DEBUG ("Added call key exchange is %s", key_exchange);
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877

    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;
878
879
880
            case CALL_STATE_RECORD:
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
                break;
881
882