~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to gnome/src/contacts/calltree.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2012-05-19 21:46:37 UTC
  • mfrom: (1.1.7)
  • Revision ID: package-import@ubuntu.com-20120519214637-la8rbrford5kj6m3
Tags: 1.1.0-1
* New upstream release 
  - Fixes "FTBFS with libccrtp-dev/2.0.2 from experimental" (Closes: #663282)
* NEW Maintainer: Debian VoIP Team - Thanks Francois for your work.
  - (Closes: #665789: O: sflphone -- SIP and IAX2 compatible VoIP phone)
* Added Build-Depends: libdbus-c++-bin
* Add gcc47-fixes.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
 
33
33
#include "calllist.h"
34
34
#include "calltree.h"
 
35
#include "str_utils.h"
 
36
#include <string.h>
35
37
#include <stdlib.h>
 
38
#include <gtk/gtk.h>
36
39
 
 
40
#include "gtk2_wrappers.h"
37
41
#include "eel-gconf-extensions.h"
38
42
#include "unused.h"
39
43
#include "dbus.h"
48
52
#include "imwindow.h"
49
53
#include "searchbar.h"
50
54
 
 
55
#if !GLIB_CHECK_VERSION(2, 30, 0)
 
56
#define G_VALUE_INIT  { 0, { { 0 } } }
 
57
#endif
 
58
 
 
59
typedef struct {
 
60
    gchar *source_ID;
 
61
    gchar *dest_ID;
 
62
} PopupData;
 
63
 
 
64
static PopupData *popup_data = NULL;
 
65
 
51
66
// Messages used in menu item
52
67
static const gchar * const SFL_CREATE_CONFERENCE = "Create conference";
53
68
static const gchar * const SFL_TRANSFER_CALL = "Transfer call to";
54
69
 
55
 
static GtkWidget *calltree_sw = NULL;
56
 
static GtkCellRenderer *calltree_rend = NULL;
57
 
static GtkTreeViewColumn *calltree_col = NULL;
58
 
static GtkTreeSelection *calltree_sel = NULL;
59
 
 
60
70
static GtkWidget *calltree_popupmenu = NULL;
61
71
static GtkWidget *calltree_menu_items = NULL;
62
72
 
63
 
static CallType calltree_dragged_type = A_INVALID;
64
 
static CallType calltree_selected_type = A_INVALID;
65
 
 
66
 
static const gchar *calltree_dragged_call_id = NULL;
67
 
static const gchar *calltree_selected_call_id = NULL;
68
 
static const gchar *calltree_dragged_path = NULL;
69
 
static const gchar *calltree_selected_path = NULL;
70
 
 
71
 
static gint calltree_dragged_path_depth = -1;
72
 
static gint calltree_selected_path_depth = -1;
73
 
 
74
 
static callable_obj_t *calltree_dragged_call = NULL;
75
 
static callable_obj_t *calltree_selected_call = NULL;
76
 
 
77
 
static conference_obj_t *calltree_dragged_conf = NULL;
78
 
static conference_obj_t *calltree_selected_conf = NULL;
79
 
 
80
 
static void drag_end_cb(GtkWidget *, GdkDragContext *, gpointer);
81
73
static void drag_data_received_cb(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
82
 
static void drag_history_received_cb(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
83
 
static void menuitem_response(gchar *);
 
74
static void menuitem_response(gchar * string);
84
75
 
85
 
enum {
86
 
    COLUMN_ACCOUNT_PIXBUF = 0,
87
 
    COLUMN_ACCOUNT_DESC,
88
 
    COLUMN_ACCOUNT_SECURITY_PIXBUF,
89
 
    COLUMN_ACCOUNT_PTR
 
76
static GtkTargetEntry target_list[] = {
 
77
    { "MY_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
90
78
};
91
79
 
 
80
static const guint n_targets = G_N_ELEMENTS(target_list);
 
81
 
92
82
/**
93
83
 * Show popup menu
94
84
 */
100
90
    return TRUE;
101
91
}
102
92
 
 
93
/* Returns TRUE if row contains a conference object pointer */
 
94
gboolean
 
95
is_conference(GtkTreeModel *model, GtkTreeIter *iter)
 
96
{
 
97
    gboolean result = FALSE;
 
98
    gtk_tree_model_get(model, iter, COLUMN_IS_CONFERENCE, &result, -1);
 
99
    return result;
 
100
}
 
101
 
103
102
/* Call back when the user click on a call in the list */
104
103
static void
105
104
call_selected_cb(GtkTreeSelection *sel, void* data UNUSED)
106
105
{
107
 
    DEBUG("CallTree: Selection callback");
108
 
    GtkTreeModel *model = GTK_TREE_MODEL(active_calltree_tab->store);
 
106
    GtkTreeModel *model = gtk_tree_view_get_model(gtk_tree_selection_get_tree_view(sel));
109
107
 
110
108
    GtkTreeIter iter;
111
 
 
112
109
    if (!gtk_tree_selection_get_selected(sel, &model, &iter))
113
110
        return;
114
111
 
115
112
    if (active_calltree_tab == history_tab)
116
 
        DEBUG("CallTree: Current call tree is history");
 
113
        DEBUG("Current call tree is history");
117
114
    else if (active_calltree_tab == current_calls_tab)
118
 
        DEBUG("CallTree: Current call tree is current calls");
119
 
 
120
 
    // store info for dragndrop
121
 
    GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
122
 
    gchar *string_path = gtk_tree_path_to_string(path);
123
 
    calltree_selected_path_depth = gtk_tree_path_get_depth(path);
124
 
 
125
 
    if (gtk_tree_model_iter_has_child(model, &iter)) {
126
 
        DEBUG("CallTree: Selected a conference");
127
 
        calltree_selected_type = A_CONFERENCE;
128
 
 
129
 
        GValue val;
130
 
        val.g_type = 0;
131
 
        gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
132
 
 
133
 
        calltree_selected_conf = (conference_obj_t*) g_value_get_pointer(&val);
134
 
        g_value_unset(&val);
135
 
 
136
 
        if (calltree_selected_conf) {
 
115
        DEBUG("Current call tree is current calls");
 
116
 
 
117
    /* Get ID of selected object, may be a call or a conference */
 
118
    gchar *id;
 
119
    gtk_tree_model_get(model, &iter, COLUMN_ID, &id, -1);
 
120
 
 
121
    if (is_conference(model, &iter)) {
 
122
        DEBUG("Selected a conference");
 
123
 
 
124
        conference_obj_t *calltree_selected_conf = conferencelist_get(active_calltree_tab, id);
 
125
        g_free(id);
 
126
 
 
127
        if (calltree_selected_conf)
137
128
            calltab_select_conf(active_calltree_tab, calltree_selected_conf);
138
 
            calltree_selected_call_id = calltree_selected_conf->_confID;
139
 
            calltree_selected_path = string_path;
140
 
            calltree_selected_call = NULL;
141
 
 
142
 
            if (calltree_selected_conf->_im_widget)
143
 
                im_window_show_tab(calltree_selected_conf->_im_widget);
144
 
 
145
 
            DEBUG("CallTree: selected_path %s, selected_conf_id %s, selected_path_depth %d",
146
 
                  calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
147
 
        }
148
129
    } else {
149
 
        DEBUG("CallTree: Selected a call");
150
 
        calltree_selected_type = A_CALL;
151
 
 
152
 
        GValue val;
153
 
        val.g_type = 0;
154
 
        gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
155
 
 
156
 
        calltree_selected_call = g_value_get_pointer(&val);
157
 
        g_value_unset(&val);
158
 
 
159
 
        if (calltree_selected_call) {
160
 
            calltab_select_call(active_calltree_tab, calltree_selected_call);
161
 
            calltree_selected_call_id = calltree_selected_call->_callID;
162
 
            calltree_selected_path = string_path;
163
 
            calltree_selected_conf = NULL;
164
 
 
165
 
            if (calltree_selected_call->_im_widget)
166
 
                im_window_show_tab(calltree_selected_call->_im_widget);
167
 
 
168
 
            DEBUG("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d",
169
 
                  calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
170
 
        }
 
130
        DEBUG("Selected a call");
 
131
 
 
132
        callable_obj_t *selected_call = calllist_get_call(active_calltree_tab, id);
 
133
        g_free(id);
 
134
 
 
135
        if (selected_call)
 
136
            calltab_select_call(active_calltree_tab, selected_call);
171
137
    }
172
138
 
173
139
    update_actions();
174
140
}
175
141
 
176
142
/* A row is activated when it is double clicked */
177
 
void
178
 
row_activated(GtkTreeView *tree_view UNUSED,
179
 
              GtkTreePath *path UNUSED,
180
 
              GtkTreeViewColumn *column UNUSED,
181
 
              void * data UNUSED)
 
143
static void
 
144
row_activated_cb(GtkTreeView *tree_view UNUSED,
 
145
                 GtkTreePath *path UNUSED,
 
146
                 GtkTreeViewColumn *column UNUSED,
 
147
                 void * data UNUSED)
182
148
{
183
 
    DEBUG("CallTree: Double click action");
184
 
 
185
149
    if (calltab_get_selected_type(active_calltree_tab) == A_CALL) {
186
 
        DEBUG("CallTree: Selected a call");
 
150
        DEBUG("Selected a call");
187
151
        callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab);
188
152
 
189
153
        if (selectedCall) {
220
184
            }
221
185
        }
222
186
    } else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
223
 
        DEBUG("CallTree: Selected a conference");
 
187
        DEBUG("Selected a conference");
224
188
 
225
189
        if (active_calltree_tab == current_calls_tab) {
226
190
            conference_obj_t * selectedConf = calltab_get_selected_conf(current_calls_tab);
243
207
                }
244
208
            }
245
209
        } else
246
 
            WARN("CallTree: Selected a conference in history, should not be possible");
 
210
            WARN("Selected a conference in history, should not be possible");
247
211
    }
248
212
}
249
213
 
253
217
{
254
218
    gchar * displaySasOnce = NULL;
255
219
 
256
 
    DEBUG("CallTree: Single click action");
257
 
 
258
220
    callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab);
259
221
    conference_obj_t *selectedConf = calltab_get_selected_conf(active_calltree_tab);
260
222
 
261
223
    if (active_calltree_tab == current_calls_tab)
262
 
        DEBUG("CallTree: Active calltree is current_calls");
 
224
        DEBUG("Active calltree is current_calls");
263
225
    else if (active_calltree_tab == history_tab)
264
 
        DEBUG("CallTree: Active calltree is history");
 
226
        DEBUG("Active calltree is history");
265
227
 
266
228
    if (calltab_get_selected_type(active_calltree_tab) == A_CALL) {
267
 
        DEBUG("CallTree: Selected a call");
268
229
 
269
230
        if (selectedCall) {
270
231
            account_t *account_details = account_list_get_by_id(selectedCall->_accountID);
290
251
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
291
252
                        selectedCall->_srtp_state = SRTP_STATE_ZRTP_SAS_CONFIRMED;
292
253
 
293
 
                        if (g_strcasecmp(displaySasOnce, "true") == 0)
 
254
                        if (utf8_case_equal(displaySasOnce, "true"))
294
255
                            selectedCall->_zrtp_confirmed = TRUE;
295
256
 
296
257
                        dbus_confirm_sas(selectedCall);
308
269
            }
309
270
        }
310
271
    } else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
311
 
        DEBUG("CallTree: Selected a conference");
312
272
        if (selectedConf)
313
 
            DEBUG("CallTree: There is actually a selected conf");
 
273
            DEBUG("There is actually a selected conf");
314
274
    } else
315
 
        WARN("CallTree: Warning: Unknown selection type");
 
275
        WARN("Unknown selection type");
316
276
}
317
277
 
318
278
static gboolean
339
299
        name += (sizeof(SIP_PREFIX) - 1);
340
300
    else if (g_str_has_prefix(name, SIPS_PREFIX))
341
301
        name += (sizeof(SIPS_PREFIX) - 1);
 
302
 
 
303
    gchar * pos = g_strrstr(name, ">");
 
304
    if (pos)
 
305
        *pos = '\0';
342
306
    return name;
343
307
}
344
308
 
345
309
static gchar *
346
 
calltree_display_call_info(callable_obj_t * c, CallDisplayType display_type, const gchar *const audio_codec)
 
310
calltree_display_call_info(callable_obj_t * call, CallDisplayType display_type, const gchar *const audio_codec)
347
311
{
348
 
    gchar display_number[strlen(c->_peer_number) + 1];
349
 
    strcpy(display_number, c->_peer_number);
 
312
    gchar display_number[strlen(call->_peer_number) + 1];
 
313
    strcpy(display_number, call->_peer_number);
350
314
 
351
 
    if (c->_type != CALL || !call_was_outgoing(c)) {
 
315
    if (call->_type != CALL || !call_was_outgoing(call)) {
352
316
        // Get the hostname for this call (NULL if not existent)
353
 
        gchar * hostname = g_strrstr(c->_peer_number, "@");
 
317
        gchar * hostname = g_strrstr(call->_peer_number, "@");
354
318
 
355
319
        // Test if we are dialing a new number
356
 
        if (*c->_peer_number && hostname)
357
 
            display_number[hostname - c->_peer_number] = '\0';
 
320
        if (*call->_peer_number && hostname)
 
321
            display_number[hostname - call->_peer_number] = '\0';
358
322
    }
359
323
 
360
324
    // Different display depending on type
361
 
    gchar *name, *details = NULL;
 
325
    gchar *name = NULL;
 
326
    gchar *details = NULL;
362
327
 
363
 
    if (*c->_display_name) {
364
 
        name = c->_display_name;
 
328
    if (*call->_display_name) {
 
329
        name = call->_display_name;
365
330
        details = display_number;
366
331
    } else {
367
332
        name = display_number;
374
339
 
375
340
    switch (display_type) {
376
341
        case DISPLAY_TYPE_CALL:
377
 
            if (c->_state_code)
378
 
                suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>", c->_state_code_description, c->_state_code);
 
342
            if (call->_state_code)
 
343
                suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>", call->_state_code_description, call->_state_code);
379
344
            break;
380
345
        case DISPLAY_TYPE_STATE_CODE :
381
346
 
382
 
            if (c->_state_code)
 
347
            if (call->_state_code)
383
348
                suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>  <i>%s</i>",
384
 
                                                 c->_state_code_description, c->_state_code,
 
349
                                                 call->_state_code_description, call->_state_code,
385
350
                                                 audio_codec);
386
351
            else
387
352
                suffix = g_markup_printf_escaped("\n<i>%s</i>", audio_codec);
388
353
 
389
354
            break;
390
355
        case DISPLAY_TYPE_CALL_TRANSFER:
391
 
            suffix = g_markup_printf_escaped("\n<i>Transfer to:%s</i> ", c->_trsft_to);
 
356
            suffix = g_markup_printf_escaped("\n<i>Transfer to:%s</i> ", call->_trsft_to);
392
357
            break;
393
358
        case DISPLAY_TYPE_SAS:
394
 
            suffix = g_markup_printf_escaped("\n<i>Confirm SAS <b>%s</b> ?</i>", c->_sas);
 
359
            suffix = g_markup_printf_escaped("\n<i>Confirm SAS <b>%s</b> ?</i>", call->_sas);
395
360
            break;
396
361
        case DISPLAY_TYPE_HISTORY :
397
362
        default:
414
379
 
415
380
    gtk_container_set_border_width(GTK_CONTAINER(tab->tree), 0);
416
381
 
417
 
    calltree_sw = gtk_scrolled_window_new(NULL, NULL);
 
382
    GtkWidget *calltree_sw = gtk_scrolled_window_new(NULL, NULL);
 
383
 
418
384
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(calltree_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
419
385
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(calltree_sw), GTK_SHADOW_IN);
420
386
 
421
 
    tab->store = gtk_tree_store_new(4,
 
387
    tab->store = gtk_tree_store_new(COLUMNS_IN_TREE_STORE,
422
388
                                    GDK_TYPE_PIXBUF, /* Icon */
423
389
                                    G_TYPE_STRING,   /* Description */
424
390
                                    GDK_TYPE_PIXBUF, /* Security Icon */
425
 
                                    G_TYPE_POINTER   /* Pointer to the Object */
 
391
                                    G_TYPE_STRING,   /* ID of the object */
 
392
                                    G_TYPE_BOOLEAN   /* True if this is conference */
426
393
                                   );
427
394
 
428
395
    tab->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tab->store));
429
396
    gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tab->view), FALSE);
430
397
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tab->view), FALSE);
431
398
    g_signal_connect(G_OBJECT(tab->view), "row-activated",
432
 
                     G_CALLBACK(row_activated),
 
399
                     G_CALLBACK(row_activated_cb),
433
400
                     NULL);
434
401
 
435
402
    gtk_widget_set_can_focus(calltree_sw, TRUE);
447
414
                     G_CALLBACK(button_pressed),
448
415
                     NULL);
449
416
 
450
 
    if (tab != history_tab && tab != contacts_tab) {
451
 
        // Make calltree reordable for drag n drop
452
 
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tab->view), TRUE);
453
 
 
454
 
        // source widget drag n drop signals
455
 
        g_signal_connect(G_OBJECT(tab->view), "drag_end", G_CALLBACK(drag_end_cb), NULL);
456
 
 
 
417
    if (g_strcmp0(tab->_name, CURRENT_CALLS) == 0) {
 
418
 
 
419
        gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(tab->view), GDK_BUTTON1_MASK, target_list, n_targets, GDK_ACTION_DEFAULT | GDK_ACTION_MOVE);
 
420
        gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(tab->view), target_list, n_targets, GDK_ACTION_DEFAULT);
457
421
        // destination widget drag n drop signals
458
422
        g_signal_connect(G_OBJECT(tab->view), "drag_data_received", G_CALLBACK(drag_data_received_cb), NULL);
459
423
 
461
425
 
462
426
        calltree_menu_items = gtk_menu_item_new_with_label(SFL_TRANSFER_CALL);
463
427
        g_signal_connect_swapped(calltree_menu_items, "activate",
464
 
                                 G_CALLBACK(menuitem_response), (gpointer) g_strdup(SFL_TRANSFER_CALL));
 
428
                                 G_CALLBACK(menuitem_response), g_strdup(SFL_TRANSFER_CALL));
465
429
        gtk_menu_shell_append(GTK_MENU_SHELL(calltree_popupmenu), calltree_menu_items);
466
430
        gtk_widget_show(calltree_menu_items);
467
431
 
468
432
        calltree_menu_items = gtk_menu_item_new_with_label(SFL_CREATE_CONFERENCE);
469
433
        g_signal_connect_swapped(calltree_menu_items, "activate",
470
 
                                 G_CALLBACK(menuitem_response), (gpointer) g_strdup(SFL_CREATE_CONFERENCE));
 
434
                                 G_CALLBACK(menuitem_response), g_strdup(SFL_CREATE_CONFERENCE));
471
435
        gtk_menu_shell_append(GTK_MENU_SHELL(calltree_popupmenu), calltree_menu_items);
472
436
        gtk_widget_show(calltree_menu_items);
473
 
    } else if (tab == history_tab) {
474
 
        gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(tab->view), TRUE);
475
 
        g_signal_connect(G_OBJECT(tab->view), "drag_data_received", G_CALLBACK(drag_history_received_cb), NULL);
476
437
    }
477
438
 
478
439
    gtk_widget_grab_focus(GTK_WIDGET(tab->view));
479
440
 
480
 
    calltree_rend = gtk_cell_renderer_pixbuf_new();
481
 
    calltree_col = gtk_tree_view_column_new_with_attributes("Icon", calltree_rend, "pixbuf", COLUMN_ACCOUNT_PIXBUF,
482
 
                   NULL);
 
441
    GtkCellRenderer *calltree_rend = gtk_cell_renderer_pixbuf_new();
 
442
    GtkTreeViewColumn *calltree_col = gtk_tree_view_column_new_with_attributes("Icon", calltree_rend, "pixbuf", COLUMN_ACCOUNT_PIXBUF, NULL);
483
443
    gtk_tree_view_append_column(GTK_TREE_VIEW(tab->view), calltree_col);
484
 
 
485
444
    calltree_rend = gtk_cell_renderer_text_new();
486
445
    calltree_col = gtk_tree_view_column_new_with_attributes("Description", calltree_rend,
487
446
                   "markup", COLUMN_ACCOUNT_DESC,
510
469
    g_object_unref(G_OBJECT(tab->store));
511
470
    gtk_container_add(GTK_CONTAINER(calltree_sw), tab->view);
512
471
 
513
 
    calltree_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view));
 
472
    GtkTreeSelection *calltree_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view));
514
473
    g_signal_connect(G_OBJECT(calltree_sel), "changed",
515
474
                     G_CALLBACK(call_selected_cb),
516
475
                     NULL);
528
487
    gtk_widget_show(tab->tree);
529
488
}
530
489
 
531
 
 
532
 
static void
533
 
calltree_remove_call_recursive(calltab_t* tab, callable_obj_t * callable, GtkTreeIter *parent)
 
490
static gboolean
 
491
remove_element_if_match(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gpointer data)
 
492
{
 
493
    const gchar *target_id = (const gchar *) data;
 
494
    gchar *id;
 
495
    gtk_tree_model_get(model, iter, COLUMN_ID, &id, -1);
 
496
    gboolean result = FALSE;
 
497
    if (g_strcmp0(id, target_id) == 0) {
 
498
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
 
499
        result = TRUE;  // stop iterating, we found it
 
500
    }
 
501
    g_free(id);
 
502
 
 
503
    return result;
 
504
}
 
505
 
 
506
void
 
507
calltree_remove_call(calltab_t* tab, const gchar *target_id)
534
508
{
535
509
    GtkTreeStore *store = tab->store;
536
510
    GtkTreeModel *model = GTK_TREE_MODEL(store);
537
 
 
538
 
    if (!callable)
539
 
        ERROR("CallTree: Error: Not a valid call");
540
 
 
541
 
    DEBUG("CallTree: Remove call %s", callable->_callID);
542
 
 
543
 
    int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
544
 
 
545
 
    for (int i = 0; i < nbChild; i++) {
546
 
        GtkTreeIter child;
547
 
 
548
 
        if (gtk_tree_model_iter_nth_child(model, &child, parent, i)) {
549
 
            if (gtk_tree_model_iter_has_child(model, &child))
550
 
                calltree_remove_call_recursive(tab, callable, &child);
551
 
 
552
 
            GValue val = { .g_type = 0 };
553
 
            gtk_tree_model_get_value(model, &child, COLUMN_ACCOUNT_PTR, &val);
554
 
 
555
 
            callable_obj_t * iterCall = g_value_get_pointer(&val);
556
 
            g_value_unset(&val);
557
 
 
558
 
            if (iterCall == callable)
559
 
                gtk_tree_store_remove(store, &child);
560
 
        }
561
 
    }
562
 
 
563
 
    if (calltab_get_selected_call(tab) == callable)
 
511
    gtk_tree_model_foreach(model, remove_element_if_match, (gpointer) target_id);
 
512
 
 
513
    /* invalidate selected call if it was our target */
 
514
    callable_obj_t *sel = calltab_get_selected_call(tab);
 
515
    if (sel && g_strcmp0(sel->_callID, target_id) == 0)
564
516
        calltab_select_call(tab, NULL);
565
517
 
566
 
    update_actions();
567
 
 
568
518
    statusbar_update_clock("");
569
519
}
570
520
 
571
 
void
572
 
calltree_remove_call(calltab_t* tab, callable_obj_t * c)
573
 
{
574
 
    calltree_remove_call_recursive(tab, c, NULL);
575
 
}
576
 
 
577
521
GdkPixbuf *history_state_to_pixbuf(const gchar *history_state)
578
522
{
579
523
    gchar *svg_filename = g_strconcat(ICONS_DIR, "/", history_state, ".svg", NULL);
582
526
    return pixbuf;
583
527
}
584
528
 
585
 
static void
586
 
calltree_update_call_recursive(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
 
529
typedef struct {
 
530
    calltab_t *tab;
 
531
    callable_obj_t *call;
 
532
} CallUpdateCtx;
 
533
 
 
534
typedef struct {
 
535
    calltab_t *tab;
 
536
    const conference_obj_t *conf;
 
537
} ConferenceRemoveCtx;
 
538
 
 
539
static gboolean
 
540
update_call(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gpointer data)
587
541
{
588
542
    GdkPixbuf *pixbuf = NULL;
589
543
    GdkPixbuf *pixbuf_security = NULL;
590
 
    GtkTreeIter iter;
591
 
    GValue val;
 
544
    CallUpdateCtx *ctx = (CallUpdateCtx*) data;
 
545
    calltab_t *tab = ctx->tab;
 
546
    callable_obj_t *call = ctx->call;
592
547
    GtkTreeStore* store = tab->store;
593
548
 
594
549
    gchar* srtp_enabled = NULL;
595
550
    gboolean display_sas = TRUE;
596
 
    account_t* account_details=NULL;
597
 
 
598
 
    int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
599
 
 
600
 
    if (c) {
601
 
        account_details = account_list_get_by_id(c->_accountID);
602
 
 
603
 
        if (account_details != NULL) {
604
 
            srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
605
 
 
606
 
            if (g_strcasecmp(g_hash_table_lookup(account_details->properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
607
 
                display_sas = FALSE;
608
 
        } else {
609
 
            GHashTable * properties = sflphone_get_ip2ip_properties();
610
 
 
611
 
            if (properties != NULL) {
612
 
                srtp_enabled = g_hash_table_lookup(properties, ACCOUNT_SRTP_ENABLED);
613
 
 
614
 
                if (g_strcasecmp(g_hash_table_lookup(properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
615
 
                    display_sas = FALSE;
616
 
            }
617
 
        }
618
 
    }
619
 
 
620
 
    for (gint i = 0; i < nbChild; i++) {
621
 
 
622
 
        if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, parent, i)) {
623
 
 
624
 
            if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
625
 
                calltree_update_call_recursive(tab, c, &iter);
626
 
 
627
 
            val.g_type = 0;
628
 
            gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, COLUMN_ACCOUNT_PTR, &val);
629
 
 
630
 
            callable_obj_t * iterCall = (callable_obj_t*) g_value_get_pointer(&val);
631
 
            g_value_unset(&val);
632
 
 
633
 
            if (iterCall != c)
634
 
                continue;
635
 
 
636
 
            /* Update text */
637
 
            gchar * description = NULL;
638
 
            gchar * audio_codec = call_get_audio_codec(c);
639
 
 
640
 
            if (c->_state == CALL_STATE_TRANSFER)
641
 
                description = calltree_display_call_info(c, DISPLAY_TYPE_CALL_TRANSFER, "");
642
 
            else
643
 
                if (c->_sas && display_sas && c->_srtp_state == SRTP_STATE_ZRTP_SAS_UNCONFIRMED && !c->_zrtp_confirmed)
644
 
                    description = calltree_display_call_info(c, DISPLAY_TYPE_SAS, "");
645
 
                else
646
 
                    description = calltree_display_call_info(c, DISPLAY_TYPE_STATE_CODE, audio_codec);
647
 
 
648
 
            g_free(audio_codec);
649
 
 
650
 
            /* Update icons */
651
 
            if (tab == current_calls_tab) {
652
 
                DEBUG("Receiving in state %d", c->_state);
653
 
 
654
 
                switch (c->_state) {
655
 
                    case CALL_STATE_HOLD:
656
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL);
657
 
                        break;
658
 
                    case CALL_STATE_INCOMING:
659
 
                    case CALL_STATE_RINGING:
660
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
661
 
                        break;
662
 
                    case CALL_STATE_CURRENT:
663
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
664
 
                        break;
665
 
                    case CALL_STATE_DIALING:
666
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
667
 
                        break;
668
 
                    case CALL_STATE_FAILURE:
669
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL);
670
 
                        break;
671
 
                    case CALL_STATE_BUSY:
672
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/busy.svg", NULL);
673
 
                        break;
674
 
                    case CALL_STATE_TRANSFER:
675
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/transfer.svg", NULL);
676
 
                        break;
677
 
                    case CALL_STATE_RECORD:
678
 
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
679
 
                        break;
680
 
                    default:
681
 
                        WARN("Update calltree - Should not happen!");
682
 
                }
683
 
 
684
 
                switch (c->_srtp_state) {
685
 
                    case SRTP_STATE_SDES_SUCCESS:
686
 
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
687
 
                        break;
688
 
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
689
 
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_unconfirmed.svg", NULL);
690
 
                        if (c->_sas != NULL)
691
 
                            DEBUG("SAS is ready with value %s", c->_sas);
692
 
                        break;
693
 
                    case SRTP_STATE_ZRTP_SAS_CONFIRMED:
694
 
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
695
 
                        break;
696
 
                    case SRTP_STATE_ZRTP_SAS_SIGNED:
697
 
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_certified.svg", NULL);
698
 
                        break;
699
 
                    case SRTP_STATE_UNLOCKED:
700
 
                        if (g_strcasecmp(srtp_enabled,"true") == 0)
701
 
                            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
702
 
                        break;
703
 
                    default:
704
 
                        WARN("Update calltree srtp state #%d- Should not happen!", c->_srtp_state);
705
 
                        if (g_strcasecmp(srtp_enabled, "true") == 0)
706
 
                            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
707
 
                }
708
 
 
709
 
            } else if (tab == history_tab) {
710
 
                // parent is NULL this is not a conference participant
711
 
                if (parent == NULL)
712
 
                    pixbuf = history_state_to_pixbuf(c->_history_state);
713
 
                else // parent is not NULL this is a conference participant
714
 
                    pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
715
 
 
716
 
                g_free(description);
717
 
                description = calltree_display_call_info(c, DISPLAY_TYPE_HISTORY, "");
718
 
                gchar * date = get_formatted_start_timestamp(c->_time_start);
719
 
                gchar *duration = get_call_duration(c);
720
 
                gchar *full_duration = g_strconcat(date , duration , NULL);
721
 
                g_free(date);
722
 
                g_free(duration);
723
 
 
724
 
                gchar *old_description = description;
725
 
                description = g_strconcat(old_description, full_duration, NULL);
726
 
                g_free(full_duration);
727
 
                g_free(old_description);
728
 
            }
729
 
 
730
 
            gtk_tree_store_set(store, &iter,
731
 
                               COLUMN_ACCOUNT_PIXBUF, pixbuf,
732
 
                               COLUMN_ACCOUNT_DESC, description,
733
 
                               COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
734
 
                               COLUMN_ACCOUNT_PTR, c,
735
 
                               -1);
736
 
 
737
 
            g_free(description);
738
 
 
739
 
            if (pixbuf != NULL)
740
 
                g_object_unref(G_OBJECT(pixbuf));
741
 
            if (pixbuf_security != NULL)
742
 
                g_object_unref(G_OBJECT(pixbuf_security));
743
 
        }
744
 
    }
745
 
 
 
551
    account_t* account = NULL;
 
552
 
 
553
    account = account_list_get_by_id(call->_accountID);
 
554
 
 
555
    if (account != NULL) {
 
556
        srtp_enabled = account_lookup(account, ACCOUNT_SRTP_ENABLED);
 
557
        display_sas = utf8_case_equal(account_lookup(account, ACCOUNT_ZRTP_DISPLAY_SAS), "true");
 
558
    } else {
 
559
        GHashTable * properties = sflphone_get_ip2ip_properties();
 
560
        if (properties != NULL) {
 
561
            srtp_enabled = g_hash_table_lookup(properties, ACCOUNT_SRTP_ENABLED);
 
562
            display_sas = utf8_case_equal(g_hash_table_lookup(properties, ACCOUNT_ZRTP_DISPLAY_SAS), "true");
 
563
        }
 
564
    }
 
565
 
 
566
    gchar *id;
 
567
    gtk_tree_model_get(model, iter, COLUMN_ID, &id, -1);
 
568
 
 
569
    callable_obj_t * iterCall = calllist_get_call(tab, id);
 
570
    g_free(id);
 
571
 
 
572
    if (iterCall != call)
 
573
        return FALSE;
 
574
 
 
575
    /* Update text */
 
576
    gchar * description = NULL;
 
577
    gchar * audio_codec = call_get_audio_codec(call);
 
578
 
 
579
    if (call->_state == CALL_STATE_TRANSFER)
 
580
        description = calltree_display_call_info(call, DISPLAY_TYPE_CALL_TRANSFER, "");
 
581
    else
 
582
        if (call->_sas && display_sas && call->_srtp_state == SRTP_STATE_ZRTP_SAS_UNCONFIRMED && !call->_zrtp_confirmed)
 
583
            description = calltree_display_call_info(call, DISPLAY_TYPE_SAS, "");
 
584
        else
 
585
            description = calltree_display_call_info(call, DISPLAY_TYPE_STATE_CODE, audio_codec);
 
586
 
 
587
    g_free(audio_codec);
 
588
 
 
589
    /* Update icons */
 
590
    if (tab == current_calls_tab) {
 
591
        DEBUG("Receiving in state %d", call->_state);
 
592
 
 
593
        switch (call->_state) {
 
594
            case CALL_STATE_HOLD:
 
595
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL);
 
596
                break;
 
597
            case CALL_STATE_INCOMING:
 
598
            case CALL_STATE_RINGING:
 
599
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
 
600
                break;
 
601
            case CALL_STATE_CURRENT:
 
602
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
 
603
                break;
 
604
            case CALL_STATE_DIALING:
 
605
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
 
606
                break;
 
607
            case CALL_STATE_FAILURE:
 
608
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL);
 
609
                break;
 
610
            case CALL_STATE_BUSY:
 
611
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/busy.svg", NULL);
 
612
                break;
 
613
            case CALL_STATE_TRANSFER:
 
614
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/transfer.svg", NULL);
 
615
                break;
 
616
            case CALL_STATE_RECORD:
 
617
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
 
618
                break;
 
619
            default:
 
620
                WARN("Update calltree - Should not happen!");
 
621
        }
 
622
 
 
623
        switch (call->_srtp_state) {
 
624
            case SRTP_STATE_SDES_SUCCESS:
 
625
                pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
 
626
                break;
 
627
            case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
 
628
                pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_unconfirmed.svg", NULL);
 
629
                if (call->_sas != NULL)
 
630
                    DEBUG("SAS is ready with value %s", call->_sas);
 
631
                break;
 
632
            case SRTP_STATE_ZRTP_SAS_CONFIRMED:
 
633
                pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
 
634
                break;
 
635
            case SRTP_STATE_ZRTP_SAS_SIGNED:
 
636
                pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_certified.svg", NULL);
 
637
                break;
 
638
            case SRTP_STATE_UNLOCKED:
 
639
                if (utf8_case_equal(srtp_enabled, "true"))
 
640
                    pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
 
641
                break;
 
642
            default:
 
643
                WARN("Update calltree srtp state #%d- Should not happen!", call->_srtp_state);
 
644
                if (utf8_case_equal(srtp_enabled, "true"))
 
645
                    pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
 
646
        }
 
647
 
 
648
    } else if (tab == history_tab) {
 
649
        pixbuf = history_state_to_pixbuf(call->_history_state);
 
650
 
 
651
        g_free(description);
 
652
        description = calltree_display_call_info(call, DISPLAY_TYPE_HISTORY, "");
 
653
        gchar *date = get_formatted_start_timestamp(call->_time_start);
 
654
        gchar *duration = get_call_duration(call);
 
655
        gchar *full_duration = g_strconcat(date , duration , NULL);
 
656
        g_free(date);
 
657
        g_free(duration);
 
658
 
 
659
        gchar *old_description = description;
 
660
        description = g_strconcat(old_description, full_duration, NULL);
 
661
        g_free(full_duration);
 
662
        g_free(old_description);
 
663
    }
 
664
 
 
665
    gtk_tree_store_set(store, iter,
 
666
            COLUMN_ACCOUNT_PIXBUF, pixbuf,
 
667
            COLUMN_ACCOUNT_DESC, description,
 
668
            COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
 
669
            COLUMN_ID, call->_callID,
 
670
            COLUMN_IS_CONFERENCE, FALSE,
 
671
            -1);
 
672
 
 
673
    g_free(description);
 
674
 
 
675
    if (pixbuf != NULL)
 
676
        g_object_unref(G_OBJECT(pixbuf));
 
677
    if (pixbuf_security != NULL)
 
678
        g_object_unref(G_OBJECT(pixbuf_security));
 
679
    return TRUE;
 
680
}
 
681
 
 
682
void calltree_update_call(calltab_t* tab, callable_obj_t * call)
 
683
{
 
684
    if (!call) {
 
685
        ERROR("Call is NULL, ignoring");
 
686
        return;
 
687
    }
 
688
    CallUpdateCtx ctx = {tab, call};
 
689
    GtkTreeStore *store = tab->store;
 
690
    GtkTreeModel *model = GTK_TREE_MODEL(store);
 
691
    gtk_tree_model_foreach(model, update_call, (gpointer) &ctx);
746
692
    update_actions();
747
693
}
748
694
 
749
 
void calltree_update_call(calltab_t* tab, callable_obj_t * c)
750
 
{
751
 
    calltree_update_call_recursive(tab, c, NULL);
752
 
}
753
 
 
754
 
void calltree_add_call(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
 
695
void calltree_add_call(calltab_t* tab, callable_obj_t * call, GtkTreeIter *parent)
755
696
{
756
697
    g_assert(tab != history_tab);
757
698
 
765
706
 
766
707
    // New call in the list
767
708
 
768
 
    gchar *description = calltree_display_call_info(c, DISPLAY_TYPE_CALL, "");
 
709
    gchar *description = calltree_display_call_info(call, DISPLAY_TYPE_CALL, "");
769
710
 
770
711
    gtk_tree_store_prepend(tab->store, &iter, parent);
771
712
 
772
 
    if (c) {
773
 
        account_details = account_list_get_by_id(c->_accountID);
 
713
    if (call) {
 
714
        account_details = account_list_get_by_id(call->_accountID);
774
715
 
775
716
        if (account_details) {
776
717
            srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
781
722
    DEBUG("Added call key exchange is %s", key_exchange);
782
723
 
783
724
    if (tab == current_calls_tab) {
784
 
        switch (c->_state) {
 
725
        switch (call->_state) {
785
726
            case CALL_STATE_INCOMING:
786
727
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
787
728
                break;
810
751
                WARN("Update calltree add - Should not happen!");
811
752
        }
812
753
 
813
 
        if (srtp_enabled && g_strcasecmp(srtp_enabled, "true") == 0)
 
754
        if (srtp_enabled && utf8_case_equal(srtp_enabled, "true"))
814
755
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/secure_off.svg", NULL);
815
756
 
816
757
    } else if (tab == contacts_tab)
817
 
        pixbuf = c->_contact_thumbnail;
 
758
        pixbuf = call->_contact_thumbnail;
818
759
    else
819
 
        WARN("CallTree: This widget doesn't exist - This is a bug in the application.");
 
760
        WARN("This widget doesn't exist - This is a bug in the application.");
820
761
 
821
762
    //Resize it
822
763
    if (pixbuf && (gdk_pixbuf_get_width(pixbuf) > 32 || gdk_pixbuf_get_height(pixbuf) > 32)) {
835
776
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
836
777
                       COLUMN_ACCOUNT_DESC, description,
837
778
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
838
 
                       COLUMN_ACCOUNT_PTR, c,
 
779
                       COLUMN_ID, call->_callID,
 
780
                       COLUMN_IS_CONFERENCE, FALSE,
839
781
                       -1);
840
782
 
841
783
    g_free(description);
851
793
    gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view)), &iter);
852
794
}
853
795
 
854
 
void calltree_add_history_entry(callable_obj_t *c)
 
796
void calltree_add_history_entry(callable_obj_t *call)
855
797
{
856
798
    if (!eel_gconf_get_integer(HISTORY_ENABLED))
857
799
        return;
858
800
 
859
801
    // New call in the list
860
 
    gchar * description = calltree_display_call_info(c, DISPLAY_TYPE_HISTORY, "");
 
802
    gchar * description = calltree_display_call_info(call, DISPLAY_TYPE_HISTORY, "");
861
803
 
862
804
    GtkTreeIter iter;
863
805
    gtk_tree_store_prepend(history_tab->store, &iter, NULL);
864
806
 
865
 
    GdkPixbuf *pixbuf = history_state_to_pixbuf(c->_history_state);
 
807
    GdkPixbuf *pixbuf = history_state_to_pixbuf(call->_history_state);
866
808
 
867
 
    gchar *date = get_formatted_start_timestamp(c->_time_start);
868
 
    gchar *duration = get_call_duration(c);
 
809
    gchar *date = get_formatted_start_timestamp(call->_time_start);
 
810
    gchar *duration = get_call_duration(call);
869
811
    gchar * full_duration = g_strconcat(date, duration, NULL);
870
812
    g_free(date);
871
813
    g_free(duration);
884
826
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
885
827
                       COLUMN_ACCOUNT_DESC, full_description,
886
828
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, NULL,
887
 
                       COLUMN_ACCOUNT_PTR, c,
 
829
                       COLUMN_ID, call->_callID,
 
830
                       COLUMN_IS_CONFERENCE, FALSE,
888
831
                       -1);
889
832
 
890
833
    g_free(full_description);
903
846
    account_t *account_details = NULL;
904
847
 
905
848
    if (!conf) {
906
 
        ERROR("Calltree: Error: Conference is null");
 
849
        ERROR("Conference is null");
 
850
        return;
 
851
    } else if (!conf->_confID) {
 
852
        ERROR("Conference ID is null");
907
853
        return;
908
854
    }
909
855
 
910
 
    DEBUG("Calltree: Add conference %s", conf->_confID);
 
856
    DEBUG("Add conference %s", conf->_confID);
911
857
 
912
858
    GtkTreeIter iter;
913
859
    gtk_tree_store_append(current_calls_tab->store, &iter, NULL);
954
900
    conf->_conference_secured = TRUE;
955
901
 
956
902
    if (conf->participant_list) {
957
 
        DEBUG("Calltree: Determine if at least one participant uses SRTP");
 
903
        DEBUG("Determine if at least one participant uses SRTP");
958
904
 
959
905
        for (GSList *part = conf->participant_list; part; part = g_slist_next(part)) {
960
 
            const gchar * const call_id = (gchar *) part->data;
 
906
            const gchar * const call_id = (const gchar *) part->data;
961
907
            callable_obj_t *call = calllist_get_call(current_calls_tab, call_id);
962
908
 
963
909
            if (call == NULL)
964
 
                ERROR("Calltree: Error: Could not find call %s in call list", call_id);
 
910
                ERROR("Could not find call %s in call list", call_id);
965
911
            else {
966
912
                account_details = account_list_get_by_id(call->_accountID);
967
913
                gchar *srtp_enabled = "";
968
914
 
969
915
                if (!account_details)
970
 
                    ERROR("Calltree: Error: Could not find account %s in account list", call->_accountID);
 
916
                    ERROR("Could not find account %s in account list", call->_accountID);
971
917
                else
972
918
                    srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
973
919
 
974
 
                if (g_strcasecmp(srtp_enabled, "true") == 0) {
975
 
                    DEBUG("Calltree: SRTP enabled for participant %s", call_id);
 
920
                if (utf8_case_equal(srtp_enabled, "true")) {
 
921
                    DEBUG("SRTP enabled for participant %s", call_id);
976
922
                    conf->_conf_srtp_enabled = TRUE;
977
923
                    break;
978
924
                } else
979
 
                    DEBUG("Calltree: SRTP is not enabled for participant %s", call_id);
 
925
                    DEBUG("SRTP is not enabled for participant %s", call_id);
980
926
            }
981
927
        }
982
928
 
983
 
        DEBUG("Calltree: Determine if all conference participants are secured");
 
929
        DEBUG("Determine if all conference participants are secured");
984
930
 
985
931
        if (conf->_conf_srtp_enabled) {
986
932
            for (GSList *part = conf->participant_list; part; part = g_slist_next(part)) {
989
935
 
990
936
                if (call) {
991
937
                    if (call->_srtp_state == SRTP_STATE_UNLOCKED) {
992
 
                        DEBUG("Calltree: Participant %s is not secured", call_id);
 
938
                        DEBUG("Participant %s is not secured", call_id);
993
939
                        conf->_conference_secured = FALSE;
994
940
                        break;
995
941
                    } else
996
 
                        DEBUG("Calltree: Participant %s is secured", call_id);
 
942
                        DEBUG("Participant %s is secured", call_id);
997
943
                }
998
944
            }
999
945
        }
1001
947
 
1002
948
    if (conf->_conf_srtp_enabled) {
1003
949
        if (conf->_conference_secured) {
1004
 
            DEBUG("Calltree: Conference is secured");
 
950
            DEBUG("Conference is secured");
1005
951
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
1006
952
        } else {
1007
 
            DEBUG("Calltree: Conference is not secured");
 
953
            DEBUG("Conference is not secured");
1008
954
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
1009
955
        }
1010
956
    }
1011
957
 
1012
 
    DEBUG("Calltree: Add conference to tree store");
1013
 
 
1014
958
    gchar *description = g_markup_printf_escaped("<b>%s</b>", "");
1015
959
    gtk_tree_store_set(current_calls_tab->store, &iter,
1016
960
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
1017
961
                       COLUMN_ACCOUNT_DESC, description,
1018
962
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
1019
 
                       COLUMN_ACCOUNT_PTR, conf,
 
963
                       COLUMN_ID, conf->_confID,
 
964
                       COLUMN_IS_CONFERENCE, TRUE,
1020
965
                       -1);
1021
966
    g_free(description);
1022
967
 
1030
975
        const gchar * const call_id = (gchar *) part->data;
1031
976
        callable_obj_t *call = calllist_get_call(current_calls_tab, call_id);
1032
977
 
1033
 
        calltree_remove_call(current_calls_tab, call);
 
978
        calltree_remove_call(current_calls_tab, call->_callID);
1034
979
        calltree_add_call(current_calls_tab, call, &iter);
1035
980
    }
1036
981
 
1045
990
}
1046
991
 
1047
992
static
1048
 
void calltree_remove_conference_recursive(calltab_t* tab, const conference_obj_t* conf, GtkTreeIter *parent)
 
993
gboolean
 
994
remove_conference(GtkTreeModel *model, GtkTreePath *path UNUSED, GtkTreeIter *iter, gpointer data)
1049
995
{
1050
 
    GtkTreeModel *model = GTK_TREE_MODEL(tab->store);
1051
 
    int nbChildren = gtk_tree_model_iter_n_children(model, parent);
1052
 
 
1053
 
    for (int i = 0; i < nbChildren; i++) {
1054
 
        GtkTreeIter iter_parent;
1055
 
 
1056
 
        /* if the nth child of parent has one or more children */
1057
 
        if (gtk_tree_model_iter_nth_child(model, &iter_parent, parent, i)) {
1058
 
            /* RECURSION! */
1059
 
            if (gtk_tree_model_iter_has_child(model, &iter_parent))
1060
 
                calltree_remove_conference_recursive(tab, conf, &iter_parent);
1061
 
 
1062
 
            GValue confval;
1063
 
            confval.g_type = 0;
1064
 
            gtk_tree_model_get_value(model, &iter_parent, COLUMN_ACCOUNT_PTR, &confval);
1065
 
 
1066
 
            conference_obj_t *tempconf = (conference_obj_t*) g_value_get_pointer(&confval);
1067
 
            g_value_unset(&confval);
1068
 
 
1069
 
            /* if this is the conference we want to remove */
1070
 
            if (tempconf == conf) {
1071
 
                int nbParticipants = gtk_tree_model_iter_n_children(model, &iter_parent);
1072
 
                DEBUG("CallTree: nbParticipants: %d", nbParticipants);
1073
 
 
1074
 
                for (int j = 0; j < nbParticipants; j++) {
1075
 
                    GtkTreeIter iter_child;
1076
 
 
1077
 
                    if (gtk_tree_model_iter_nth_child(model, &iter_child, &iter_parent, j)) {
1078
 
                        GValue callval;
1079
 
                        callval.g_type = 0;
1080
 
                        gtk_tree_model_get_value(model, &iter_child, COLUMN_ACCOUNT_PTR, &callval);
1081
 
 
1082
 
                        callable_obj_t *call = g_value_get_pointer(&callval);
1083
 
                        g_value_unset(&callval);
1084
 
 
1085
 
                        // do not add back call in history calltree when cleaning it
1086
 
                        if (call && tab != history_tab)
1087
 
                            calltree_add_call(tab, call, NULL);
1088
 
                    }
1089
 
                }
1090
 
 
1091
 
                DEBUG("CallTree: Remove conference %s", conf->_confID);
1092
 
                gtk_tree_store_remove(tab->store, &iter_parent);
1093
 
            }
 
996
    if (!is_conference(model, iter))
 
997
        return FALSE;
 
998
 
 
999
    gchar *conf_id;
 
1000
    gtk_tree_model_get(model, iter, COLUMN_ID, &conf_id, -1);
 
1001
 
 
1002
    ConferenceRemoveCtx * ctx = (ConferenceRemoveCtx *) data;
 
1003
    calltab_t *tab = ctx->tab;
 
1004
    conference_obj_t *tempconf = conferencelist_get(tab, conf_id);
 
1005
    g_free(conf_id);
 
1006
 
 
1007
    const conference_obj_t *conf = ctx->conf;
 
1008
    /* if this is not the conference we want to remove */
 
1009
    if (tempconf != conf)
 
1010
        return FALSE;
 
1011
 
 
1012
    int nbParticipants = gtk_tree_model_iter_n_children(model, iter);
 
1013
    DEBUG("nbParticipants: %d", nbParticipants);
 
1014
 
 
1015
    for (int j = 0; j < nbParticipants; j++) {
 
1016
        GtkTreeIter iter_child;
 
1017
 
 
1018
        if (gtk_tree_model_iter_nth_child(model, &iter_child, iter, j)) {
 
1019
            gchar *call_id;
 
1020
            gtk_tree_model_get(model, &iter_child, COLUMN_ID, &call_id, -1);
 
1021
 
 
1022
            callable_obj_t *call = calllist_get_call(tab, call_id);
 
1023
            g_free(call_id);
 
1024
 
 
1025
            // do not add back call in history calltree when cleaning it
 
1026
            if (call && tab != history_tab)
 
1027
                calltree_add_call(tab, call, NULL);
1094
1028
        }
1095
1029
    }
1096
1030
 
 
1031
    gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
 
1032
 
1097
1033
    if (calltab_get_selected_conf(tab) == conf)
1098
1034
        calltab_select_conf(tab, NULL);
1099
 
 
1100
 
    update_actions();
 
1035
    return TRUE;
1101
1036
}
1102
1037
 
1103
1038
void calltree_remove_conference(calltab_t* tab, const conference_obj_t* conf)
1104
1039
{
1105
 
    DEBUG("CallTree: Remove conference %s", conf->_confID);
1106
 
    calltree_remove_conference_recursive(tab, conf, NULL);
1107
 
    DEBUG("CallTree: Finished Removing conference");
 
1040
    if(conf == NULL) {
 
1041
        ERROR("Could not remove conference, conference pointer is NULL");
 
1042
        return;
 
1043
    }
 
1044
 
 
1045
    ConferenceRemoveCtx context = {tab, conf};
 
1046
    GtkTreeStore *store = tab->store;
 
1047
    GtkTreeModel *model = GTK_TREE_MODEL(store);
 
1048
    gtk_tree_model_foreach(model, remove_conference, (gpointer) &context);
 
1049
 
 
1050
    update_actions();
 
1051
    DEBUG("Finished removing conference %s", conf->_confID);
1108
1052
}
1109
1053
 
1110
1054
void calltree_display(calltab_t *tab)
1130
1074
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(contactButton_), TRUE);
1131
1075
        set_focus_on_addressbook_searchbar();
1132
1076
    } else
1133
 
        ERROR("CallTree: Error: Not a valid call tab  (%d, %s)", __LINE__, __FILE__);
 
1077
        ERROR("Not a valid call tab  (%d, %s)", __LINE__, __FILE__);
1134
1078
 
1135
1079
    gtk_widget_hide(active_calltree_tab->tree);
1136
1080
    active_calltree_tab = tab;
1137
1081
    gtk_widget_show(active_calltree_tab->tree);
1138
1082
 
1139
1083
    GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(active_calltree_tab->view));
1140
 
    DEBUG("CallTree: Emit signal changed from calltree_display");
1141
1084
    g_signal_emit_by_name(sel, "changed");
1142
1085
    update_actions();
1143
1086
}
1148
1091
    char timestr[20];
1149
1092
    const gchar *msg = "";
1150
1093
    long duration;
1151
 
    callable_obj_t *c = calltab_get_selected_call(current_calls_tab);
 
1094
    callable_obj_t *call = calltab_get_selected_call(current_calls_tab);
1152
1095
 
1153
 
    if (c)
1154
 
        switch (c->_state) {
 
1096
    if (call)
 
1097
        switch (call->_state) {
1155
1098
            case CALL_STATE_INVALID:
1156
1099
            case CALL_STATE_INCOMING:
1157
1100
            case CALL_STATE_RINGING:
1160
1103
            case CALL_STATE_BUSY:
1161
1104
                break;
1162
1105
            default:
1163
 
                duration = difftime(time(NULL), c->_time_start);
 
1106
                duration = difftime(time(NULL), call->_time_start);
1164
1107
 
1165
1108
                if (duration < 0)
1166
1109
                    duration = 0;
1174
1117
    return TRUE;
1175
1118
}
1176
1119
 
1177
 
 
1178
 
static void drag_end_cb(GtkWidget * widget UNUSED, GdkDragContext * context UNUSED, gpointer data UNUSED)
1179
 
{
1180
 
    if (active_calltree_tab == history_tab)
1181
 
        return;
1182
 
 
1183
 
    DEBUG("CallTree: Drag end callback");
1184
 
    DEBUG("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d",
1185
 
          calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
1186
 
    DEBUG("CallTree: dragged path %s, dragged_call_id %s, dragged_path_depth %d",
1187
 
          calltree_dragged_path, calltree_dragged_call_id, calltree_dragged_path_depth);
1188
 
 
1189
 
    GtkTreeModel *model = (GtkTreeModel*) current_calls_tab->store;
1190
 
    GtkTreePath *path = gtk_tree_path_new_from_string(calltree_dragged_path);
1191
 
    GtkTreePath *dpath = gtk_tree_path_new_from_string(calltree_dragged_path);
1192
 
    GtkTreePath *spath = gtk_tree_path_new_from_string(calltree_selected_path);
1193
 
 
1194
 
    GtkTreeIter iter;
1195
 
    GtkTreeIter parent_conference; // conference for which this call is attached
1196
 
 
1197
 
    GValue val;
1198
 
 
1199
 
    // Make sure drag n drop does not imply a dialing call for either selected and dragged call
1200
 
    if (calltree_selected_call && (calltree_selected_type == A_CALL)) {
1201
 
        DEBUG("CallTree: Selected a call");
1202
 
 
1203
 
        if (calltree_selected_call->_state == CALL_STATE_DIALING ||
1204
 
                calltree_selected_call->_state == CALL_STATE_INVALID ||
1205
 
                calltree_selected_call->_state == CALL_STATE_FAILURE ||
1206
 
                calltree_selected_call->_state == CALL_STATE_BUSY ||
1207
 
                calltree_selected_call->_state == CALL_STATE_TRANSFER) {
1208
 
 
1209
 
            DEBUG("CallTree: Selected an invalid call");
1210
 
 
1211
 
            calltree_remove_call(current_calls_tab, calltree_selected_call);
1212
 
            calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1213
 
 
1214
 
            calltree_dragged_call = NULL;
1215
 
            return;
1216
 
        }
1217
 
 
1218
 
 
1219
 
        if (calltree_dragged_call && (calltree_dragged_type == A_CALL)) {
1220
 
 
1221
 
            DEBUG("CallTree: Dragged on a call");
1222
 
 
1223
 
            if (calltree_dragged_call->_state == CALL_STATE_DIALING ||
1224
 
                    calltree_dragged_call->_state == CALL_STATE_INVALID ||
1225
 
                    calltree_dragged_call->_state == CALL_STATE_FAILURE ||
1226
 
                    calltree_dragged_call->_state == CALL_STATE_BUSY ||
1227
 
                    calltree_dragged_call->_state == CALL_STATE_TRANSFER) {
1228
 
 
1229
 
                DEBUG("CallTree: Dragged on an invalid call");
1230
 
 
1231
 
                calltree_remove_call(current_calls_tab, calltree_selected_call);
1232
 
 
1233
 
                if (calltree_selected_call->_confID) {
1234
 
 
1235
 
                    gtk_tree_path_up(spath);
1236
 
                    gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, spath);
1237
 
 
1238
 
                    calltree_add_call(current_calls_tab, calltree_selected_call, &parent_conference);
1239
 
                } else
1240
 
                    calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1241
 
 
1242
 
                calltree_dragged_call = NULL;
1243
 
                return;
1244
 
            }
1245
 
        }
1246
 
    }
1247
 
 
1248
 
    // Make sure a conference is only dragged on another conference
1249
 
    if (calltree_selected_conf && (calltree_selected_type == A_CONFERENCE)) {
1250
 
 
1251
 
        DEBUG("CallTree: Selected a conference");
1252
 
 
1253
 
        if (!calltree_dragged_conf && (calltree_dragged_type == A_CALL)) {
1254
 
 
1255
 
            DEBUG("CallTree: Dragged on a call");
1256
 
            conference_obj_t* conf = calltree_selected_conf;
1257
 
 
1258
 
            calltree_remove_conference(current_calls_tab, conf);
1259
 
            calltree_add_conference_to_current_calls(conf);
1260
 
 
1261
 
            calltree_dragged_call = NULL;
1262
 
            return;
1263
 
        }
1264
 
    }
1265
 
 
1266
 
 
1267
 
    if (calltree_selected_path_depth == 1) {
1268
 
        if (calltree_dragged_path_depth == 1) {
1269
 
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
1270
 
                if (gtk_tree_path_compare(dpath, spath) != 0) {
1271
 
                    // dragged a single call on a single call
1272
 
                    if (calltree_selected_call != NULL && calltree_dragged_call != NULL) {
1273
 
                        calltree_remove_call(current_calls_tab, calltree_selected_call);
1274
 
                        calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1275
 
                        gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
1276
 
                                       0, 0);
1277
 
                    }
1278
 
                }
1279
 
            } else if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CONFERENCE) {
1280
 
 
1281
 
                // dragged a single call on a conference
1282
 
                if (!calltree_selected_call) {
1283
 
                    DEBUG("Error: call dragged on a conference is null");
1284
 
                    return;
1285
 
                }
1286
 
 
1287
 
                g_free(calltree_selected_call->_confID);
1288
 
                calltree_selected_call->_confID = g_strdup(calltree_dragged_call_id);
1289
 
 
1290
 
                g_free(calltree_selected_call->_historyConfID);
1291
 
                calltree_selected_call->_historyConfID = g_strdup(calltree_dragged_call_id);
1292
 
 
1293
 
                sflphone_add_participant(calltree_selected_call_id, calltree_dragged_call_id);
1294
 
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CALL) {
1295
 
 
1296
 
                // dragged a conference on a single call
1297
 
                conference_obj_t* conf = calltree_selected_conf;
1298
 
 
1299
 
                calltree_remove_conference(current_calls_tab, conf);
1300
 
                calltree_add_conference_to_current_calls(conf);
1301
 
 
1302
 
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CONFERENCE) {
1303
 
 
1304
 
                // dragged a conference on a conference
1305
 
                if (gtk_tree_path_compare(dpath, spath) == 0) {
1306
 
 
1307
 
                    if (!current_calls_tab) {
1308
 
                        DEBUG("Error while joining the same conference\n");
1309
 
                        return;
1310
 
                    }
1311
 
 
1312
 
                    DEBUG("Joined the same conference!\n");
1313
 
                    gtk_tree_view_expand_row(GTK_TREE_VIEW(current_calls_tab->view), path, FALSE);
1314
 
                } else {
1315
 
                    if (!calltree_selected_conf)
1316
 
                        DEBUG("Error: selected conference is null while joining 2 conference");
1317
 
 
1318
 
                    if (!calltree_dragged_conf)
1319
 
                        DEBUG("Error: dragged conference is null while joining 2 conference");
1320
 
 
1321
 
                    DEBUG("Joined conferences %s and %s!\n", calltree_dragged_path, calltree_selected_path);
1322
 
                    dbus_join_conference(calltree_selected_conf->_confID, calltree_dragged_conf->_confID);
1323
 
                }
1324
 
            }
1325
 
 
1326
 
            // TODO: dragged a single call on a NULL element (should do nothing)
1327
 
            // TODO: dragged a conference on a NULL element (should do nothing)
1328
 
 
 
1120
static void cleanup_popup_data(PopupData **data)
 
1121
{
 
1122
    if (data && *data) {
 
1123
        g_free((*data)->source_ID);
 
1124
        g_free((*data)->dest_ID);
 
1125
        g_free(*data);
 
1126
        *data = 0;
 
1127
    }
 
1128
}
 
1129
 
 
1130
static gboolean
 
1131
has_parent(GtkTreeModel *model, GtkTreeIter *child)
 
1132
{
 
1133
    GtkTreeIter parent;
 
1134
    return gtk_tree_model_iter_parent(model, &parent, child);
 
1135
}
 
1136
 
 
1137
static gboolean try_detach(GtkTreeModel *model, GtkTreeIter *source_iter, GtkTreeIter *dest_iter)
 
1138
{
 
1139
    gboolean result = FALSE;
 
1140
    if (has_parent(model, source_iter) && !has_parent(model, dest_iter)) {
 
1141
        GValue source_val = G_VALUE_INIT;
 
1142
        gtk_tree_model_get_value(model, source_iter, COLUMN_ID, &source_val);
 
1143
        const gchar *source_ID = g_value_get_string(&source_val);
 
1144
        sflphone_detach_participant(source_ID);
 
1145
        result = TRUE;
 
1146
        g_value_unset(&source_val);
 
1147
    }
 
1148
    return result;
 
1149
}
 
1150
 
 
1151
static gboolean
 
1152
handle_drop_into(GtkTreeModel *model, GtkTreeIter *source_iter, GtkTreeIter *dest_iter)
 
1153
{
 
1154
    GValue source_val = G_VALUE_INIT;
 
1155
    gtk_tree_model_get_value(model, source_iter, COLUMN_ID, &source_val);
 
1156
    const gchar *source_ID = g_value_get_string(&source_val);
 
1157
 
 
1158
    GValue dest_val = G_VALUE_INIT;
 
1159
    gtk_tree_model_get_value(model, dest_iter, COLUMN_ID, &dest_val);
 
1160
    const gchar *dest_ID = g_value_get_string(&dest_val);
 
1161
 
 
1162
    gboolean result = FALSE;
 
1163
 
 
1164
    if (has_parent(model, source_iter)) {
 
1165
        DEBUG("Source is participant, should only be detached");
 
1166
        result = FALSE;
 
1167
    } else if (!has_parent(model, dest_iter)) {
 
1168
        if (is_conference(model, dest_iter)) {
 
1169
            if (is_conference(model, source_iter)) {
 
1170
                DEBUG("dropped conference on conference, merging conferences");
 
1171
                dbus_join_conference(source_ID, dest_ID);
 
1172
                result = TRUE;
 
1173
            } else {
 
1174
                DEBUG("dropped call on conference, adding a call to a conference");
 
1175
                sflphone_add_participant(source_ID, dest_ID);
 
1176
                result = TRUE;
 
1177
            }
 
1178
        } else if (is_conference(model, source_iter)) {
 
1179
            DEBUG("dropped conference on call, merging call into conference");
 
1180
            sflphone_add_participant(dest_ID, source_ID);
 
1181
            result = TRUE;
1329
1182
        } else {
1330
 
            // dragged_path_depth == 2
1331
 
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
1332
 
                // TODO: dragged a call on a conference call
1333
 
                calltree_remove_call(current_calls_tab, calltree_selected_call);
1334
 
                calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1335
 
 
1336
 
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CALL) {
1337
 
                // TODO: dragged a conference on a conference call
1338
 
                calltree_remove_conference(current_calls_tab, calltree_selected_conf);
1339
 
                calltree_add_conference_to_current_calls(calltree_selected_conf);
1340
 
            }
1341
 
 
1342
 
            // TODO: dragged a single call on a NULL element
1343
 
            // TODO: dragged a conference on a NULL element
 
1183
            DEBUG("dropped call on call, creating new conference or transferring");
 
1184
            calltree_remove_call(current_calls_tab, source_ID);
 
1185
            callable_obj_t *source_call = calllist_get_call(current_calls_tab, source_ID);
 
1186
            calltree_add_call(current_calls_tab, source_call, NULL);
 
1187
            cleanup_popup_data(&popup_data);
 
1188
            popup_data = g_new0(PopupData, 1);
 
1189
            popup_data->source_ID = g_strdup(source_ID);
 
1190
            popup_data->dest_ID = g_strdup(dest_ID);
 
1191
            gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL, 0, 0);
 
1192
            result = TRUE;
1344
1193
        }
1345
1194
    } else {
1346
 
 
1347
 
        if (calltree_dragged_path_depth == 1) {
1348
 
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
1349
 
 
1350
 
                // dragged a conference call on a call
1351
 
                sflphone_detach_participant(calltree_selected_call_id);
1352
 
 
1353
 
                if (calltree_selected_call && calltree_dragged_call)
1354
 
                    gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
1355
 
                                   0, 0);
1356
 
 
1357
 
            } else if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CONFERENCE) {
1358
 
                // dragged a conference call on a conference
1359
 
                sflphone_detach_participant(calltree_selected_call_id);
1360
 
 
1361
 
                if (calltree_selected_call && calltree_dragged_conf) {
1362
 
                    DEBUG("Adding a participant, since dragged call on a conference");
1363
 
                    sflphone_add_participant(calltree_selected_call_id, calltree_dragged_call_id);
1364
 
                }
1365
 
            } else {
1366
 
                // dragged a conference call on a NULL element
1367
 
                sflphone_detach_participant(calltree_selected_call_id);
1368
 
            }
1369
 
 
1370
 
        } else {
1371
 
            // dragged_path_depth == 2
1372
 
            // dragged a conference call on another conference call (same conference)
1373
 
            // TODO: dragged a conference call on another conference call (different conference)
1374
 
 
1375
 
            gtk_tree_path_up(path);
1376
 
            gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, path);
1377
 
 
1378
 
            gtk_tree_path_up(dpath);
1379
 
            gtk_tree_path_up(spath);
1380
 
 
1381
 
            if (gtk_tree_path_compare(dpath, spath) == 0) {
1382
 
 
1383
 
                DEBUG("Dragged a call in the same conference");
1384
 
                calltree_remove_call(current_calls_tab, calltree_selected_call);
1385
 
                calltree_add_call(current_calls_tab, calltree_selected_call, &parent_conference);
1386
 
                gtk_widget_hide(calltree_menu_items);
1387
 
                gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
1388
 
                               0, 0);
1389
 
            } else {
1390
 
                DEBUG("Dragged a conference call onto another conference call %s, %s", gtk_tree_path_to_string(dpath), gtk_tree_path_to_string(spath));
1391
 
 
1392
 
                conference_obj_t *conf = NULL;
1393
 
                val.g_type = 0;
1394
 
 
1395
 
                if (gtk_tree_model_get_iter(model, &iter, dpath)) {
1396
 
                    gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
1397
 
                    conf = (conference_obj_t*) g_value_get_pointer(&val);
1398
 
                }
1399
 
 
1400
 
                g_value_unset(&val);
1401
 
 
1402
 
                sflphone_detach_participant(calltree_selected_call_id);
1403
 
 
1404
 
                if (conf)
1405
 
                    sflphone_add_participant(calltree_selected_call_id, conf->_confID);
1406
 
                else
1407
 
                    DEBUG("didn't find a conf!");
1408
 
            }
1409
 
 
1410
 
            // TODO: dragged a conference call on another conference call (different conference)
1411
 
            // TODO: dragged a conference call on a NULL element (same conference)
1412
 
            // TODO: dragged a conference call on a NULL element (different conference)
 
1195
        // Happens when we drag a call on anther call which participate to a conference
 
1196
        callable_obj_t *dest_call = calllist_get_call(current_calls_tab, dest_ID);
 
1197
        if (dest_call) {
 
1198
            gchar *conf_ID = dbus_get_conference_id(dest_call->_callID);
 
1199
            if (g_strcmp0(conf_ID, "") != 0) {
 
1200
                sflphone_add_participant(source_ID, conf_ID);
 
1201
                result = TRUE;
 
1202
            }
1413
1203
        }
1414
1204
    }
1415
 
}
1416
 
 
1417
 
void drag_history_received_cb(GtkWidget *widget, GdkDragContext *context UNUSED, gint x UNUSED, gint y UNUSED, GtkSelectionData *selection_data UNUSED, guint info UNUSED, guint t UNUSED, gpointer data UNUSED)
1418
 
{
1419
 
    g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
1420
 
}
1421
 
 
1422
 
void drag_data_received_cb(GtkWidget *widget, GdkDragContext *context UNUSED, gint x UNUSED, gint y UNUSED, GtkSelectionData *selection_data UNUSED, guint info UNUSED, guint t UNUSED, gpointer data UNUSED)
 
1205
    g_value_unset(&source_val);
 
1206
    g_value_unset(&dest_val);
 
1207
    return result;
 
1208
}
 
1209
 
 
1210
static gboolean valid_drop(GtkTreeModel *model, GtkTreeIter *source_iter, GtkTreePath *dest_path)
 
1211
{
 
1212
    gboolean result = TRUE;
 
1213
    GtkTreePath *source_path = gtk_tree_model_get_path(model, source_iter);
 
1214
    if (!gtk_tree_path_compare(source_path, dest_path)) {
 
1215
        ERROR("invalid drop: source and destination are the same");
 
1216
        result = FALSE;
 
1217
    } else if (gtk_tree_path_is_ancestor(source_path, dest_path)) {
 
1218
        ERROR("invalid drop: source is ancestor of destination");
 
1219
        result = FALSE;
 
1220
    } else if (gtk_tree_path_is_descendant(source_path, dest_path)) {
 
1221
        ERROR("invalid drop: source is descendant of destination");
 
1222
        result = FALSE;
 
1223
    }
 
1224
    gtk_tree_path_free(source_path);
 
1225
    return result;
 
1226
}
 
1227
 
 
1228
static gboolean
 
1229
render_drop(GtkTreeModel *model, GtkTreePath *dest_path, GtkTreeViewDropPosition dest_pos,
 
1230
            GtkTreeIter *source_iter)
 
1231
{
 
1232
    GtkTreeIter dest_iter;
 
1233
    if (!gtk_tree_model_get_iter(model, &dest_iter, dest_path)) {
 
1234
        ERROR("Could not get destination iterator");
 
1235
        return FALSE;
 
1236
    }
 
1237
 
 
1238
    gboolean result = FALSE;
 
1239
    switch (dest_pos) {
 
1240
        case GTK_TREE_VIEW_DROP_BEFORE:
 
1241
        case GTK_TREE_VIEW_DROP_AFTER:
 
1242
            DEBUG("dropped at position %d, detaching if appropriate", dest_pos);
 
1243
            result = try_detach(model, source_iter, &dest_iter);
 
1244
            break;
 
1245
 
 
1246
        case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
 
1247
        case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
 
1248
            DEBUG("DROP_INTO");
 
1249
            if (valid_drop(model, source_iter, dest_path))
 
1250
                result = handle_drop_into(model, source_iter, &dest_iter);
 
1251
            break;
 
1252
    }
 
1253
    return result;
 
1254
}
 
1255
 
 
1256
void drag_data_received_cb(GtkWidget *widget, GdkDragContext *context, gint x UNUSED,
 
1257
                           gint y UNUSED, GtkSelectionData *selection_data UNUSED, guint target_type UNUSED, guint etime, gpointer data UNUSED)
1423
1258
{
1424
1259
    GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
1425
 
    GtkTreePath *drop_path;
1426
 
    GtkTreeViewDropPosition position;
1427
 
    GValue val;
1428
 
 
1429
 
    if (active_calltree_tab == history_tab) {
1430
 
        g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
1431
 
        return;
1432
 
    }
1433
 
 
1434
 
    GtkTreeModel* tree_model = gtk_tree_view_get_model(tree_view);
1435
 
 
1436
 
    GtkTreeIter iter;
1437
 
 
1438
 
    val.g_type = 0;
1439
 
    gtk_tree_view_get_drag_dest_row(tree_view, &drop_path, &position);
1440
 
 
1441
 
    if (drop_path) {
1442
 
 
1443
 
        gtk_tree_model_get_iter(tree_model, &iter, drop_path);
1444
 
        gtk_tree_model_get_value(tree_model, &iter, COLUMN_ACCOUNT_PTR, &val);
1445
 
 
1446
 
 
1447
 
        if (gtk_tree_model_iter_has_child(tree_model, &iter)) {
1448
 
            DEBUG("CallTree: Dragging on a conference");
1449
 
            calltree_dragged_type = A_CONFERENCE;
1450
 
            calltree_dragged_call = NULL;
1451
 
        } else {
1452
 
            DEBUG("CallTree: Dragging on a call");
1453
 
            calltree_dragged_type = A_CALL;
1454
 
            calltree_dragged_conf = NULL;
1455
 
        }
1456
 
 
1457
 
        switch (position)  {
1458
 
 
1459
 
            case GTK_TREE_VIEW_DROP_AFTER:
1460
 
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_AFTER");
1461
 
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
1462
 
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
1463
 
                calltree_dragged_call_id = "NULL";
1464
 
                calltree_dragged_call = NULL;
1465
 
                calltree_dragged_conf = NULL;
1466
 
                break;
1467
 
 
1468
 
            case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
1469
 
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_INTO_OR_AFTER");
1470
 
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
1471
 
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
1472
 
 
1473
 
                if (calltree_dragged_type == A_CALL) {
1474
 
                    calltree_dragged_call_id = ((callable_obj_t*) g_value_get_pointer(&val))->_callID;
1475
 
                    calltree_dragged_call = (callable_obj_t*) g_value_get_pointer(&val);
1476
 
                } else {
1477
 
                    calltree_dragged_call_id = ((conference_obj_t*) g_value_get_pointer(&val))->_confID;
1478
 
                    calltree_dragged_conf = (conference_obj_t*) g_value_get_pointer(&val);
1479
 
                }
1480
 
 
1481
 
                break;
1482
 
 
1483
 
            case GTK_TREE_VIEW_DROP_BEFORE:
1484
 
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_BEFORE");
1485
 
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
1486
 
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
1487
 
                calltree_dragged_call_id = "NULL";
1488
 
                calltree_dragged_call = NULL;
1489
 
                calltree_dragged_conf = NULL;
1490
 
                break;
1491
 
 
1492
 
            case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
1493
 
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_INTO_OR_BEFORE");
1494
 
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
1495
 
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
1496
 
 
1497
 
                if (calltree_dragged_type == A_CALL) {
1498
 
                    calltree_dragged_call_id = ((callable_obj_t*) g_value_get_pointer(&val))->_callID;
1499
 
                    calltree_dragged_call = (callable_obj_t*) g_value_get_pointer(&val);
1500
 
                } else {
1501
 
                    calltree_dragged_call_id = ((conference_obj_t*) g_value_get_pointer(&val))->_confID;
1502
 
                    calltree_dragged_conf = (conference_obj_t*) g_value_get_pointer(&val);
1503
 
                }
1504
 
 
1505
 
                break;
1506
 
 
1507
 
            default:
1508
 
                break;
1509
 
        }
1510
 
    }
 
1260
    GtkTreeModel *model = GTK_TREE_MODEL(gtk_tree_view_get_model(tree_view));
 
1261
    GtkTreeSelection *tree_selection = gtk_tree_view_get_selection(tree_view);
 
1262
    GtkTreeIter source_iter;
 
1263
    if (!gtk_tree_selection_get_selected(tree_selection, NULL, &source_iter)) {
 
1264
        ERROR("No tree element selected");
 
1265
        return;
 
1266
    }
 
1267
    GtkTreePath *dest_path;
 
1268
    GtkTreeViewDropPosition dest_pos;
 
1269
    if (!gtk_tree_view_get_dest_row_at_pos(tree_view, x, y, &dest_path, &dest_pos)) {
 
1270
        ERROR("No row at given position");
 
1271
        return;
 
1272
    }
 
1273
 
 
1274
    gboolean success = render_drop(model, dest_path, dest_pos, &source_iter);
 
1275
    if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
 
1276
        gtk_drag_finish(context, success, TRUE, etime);
1511
1277
}
1512
1278
 
1513
1279
/* Print a string when a menu item is selected */
1514
1280
 
1515
 
static void menuitem_response(gchar *string)
 
1281
static void
 
1282
menuitem_response(gchar * string)
1516
1283
{
1517
 
    if (g_strcmp0(string, SFL_CREATE_CONFERENCE) == 0)
1518
 
        dbus_join_participant(calltree_selected_call->_callID,
1519
 
                              calltree_dragged_call->_callID);
1520
 
    else if (g_strcmp0(string, SFL_TRANSFER_CALL) == 0) {
1521
 
        DEBUG("Calltree: Transferring call %s, to %s",
1522
 
              calltree_selected_call->_peer_number,
1523
 
              calltree_dragged_call->_peer_number);
1524
 
        dbus_attended_transfer(calltree_selected_call, calltree_dragged_call);
1525
 
        calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1284
    if (g_strcmp0(string, SFL_CREATE_CONFERENCE) == 0) {
 
1285
        dbus_join_participant(popup_data->source_ID,
 
1286
                              popup_data->dest_ID);
 
1287
        calltree_remove_call(current_calls_tab, popup_data->source_ID);
 
1288
        calltree_remove_call(current_calls_tab, popup_data->dest_ID);
 
1289
        update_actions();
 
1290
    } else if (g_strcmp0(string, SFL_TRANSFER_CALL) == 0) {
 
1291
        callable_obj_t * source_call = calllist_get_call(current_calls_tab, popup_data->source_ID);
 
1292
        callable_obj_t * dest_call = calllist_get_call(current_calls_tab, popup_data->dest_ID);
 
1293
        DEBUG("Transferring call %s, to %s",
 
1294
              source_call->_peer_number,
 
1295
              dest_call->_peer_number);
 
1296
        dbus_attended_transfer(source_call, dest_call);
 
1297
        calltree_remove_call(current_calls_tab, popup_data->source_ID);
1526
1298
    } else
1527
 
        DEBUG("CallTree: Error unknown option selected in menu %s", string);
 
1299
        ERROR("Unknown option in menu %s", string);
1528
1300
 
1529
 
    // Make sure the create conference opetion will appear next time the menu pops
 
1301
    // Make sure the create conference option will appear next time the menu pops
1530
1302
    // The create conference option will hide if tow call from the same conference are draged on each other
1531
1303
    gtk_widget_show(calltree_menu_items);
1532
1304
 
 
1305
    cleanup_popup_data(&popup_data);
 
1306
 
1533
1307
    DEBUG("%s", string);
1534
1308
}
1535
1309