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

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Francois Marier
  • Date: 2011-11-25 13:24:12 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20111125132412-dc4qvhyosk74cd42
Tags: 1.0.1-4
Don't assume that arch:all packages will get built (closes: #649726)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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>
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  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
 
10
 *  (at your option) any later version.
 
11
 *
 
12
 *  This program is distributed in the hope that it will be useful,
 
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 *  GNU General Public License for more details.
 
16
 *
 
17
 *  You should have received a copy of the GNU General Public License
 
18
 *  along with this program; if not, write to the Free Software
 
19
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
20
 *
 
21
 *  Additional permission under GNU GPL version 3 section 7:
 
22
 *
 
23
 *  If you modify this program, or any covered work, by linking or
 
24
 *  combining it with the OpenSSL project's OpenSSL library (or a
 
25
 *  modified version of that library), containing parts covered by the
 
26
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 
27
 *  grants you additional permission to convey the resulting work.
 
28
 *  Corresponding Source for a non-source form of such a combination
 
29
 *  shall include the source code for the parts of OpenSSL used as well
 
30
 *  as that of the covered work.
 
31
 */
 
32
 
 
33
#include "calllist.h"
 
34
#include "calltree.h"
 
35
#include <stdlib.h>
 
36
#include <glib/gprintf.h>
 
37
 
 
38
#include "eel-gconf-extensions.h"
 
39
#include "unused.h"
 
40
#include "dbus.h"
 
41
#include "calltab.h"
 
42
#include "logger.h"
 
43
#include "conferencelist.h"
 
44
#include "mainwindow.h"
 
45
#include "history.h"
 
46
#include "calltree.h"
 
47
#include "uimanager.h"
 
48
#include "actions.h"
 
49
#include "imwindow.h"
 
50
#include "searchbar.h"
 
51
 
 
52
// Messages used in menu item
 
53
static const gchar * const SFL_CREATE_CONFERENCE = "Create conference";
 
54
static const gchar * const SFL_TRANSFER_CALL = "Transfer call to";
 
55
 
 
56
static GtkWidget *calltree_sw = NULL;
 
57
static GtkCellRenderer *calltree_rend = NULL;
 
58
static GtkTreeViewColumn *calltree_col = NULL;
 
59
static GtkTreeSelection *calltree_sel = NULL;
 
60
 
 
61
static GtkWidget *calltree_popupmenu = NULL;
 
62
static GtkWidget *calltree_menu_items = NULL;
 
63
 
 
64
static CallType calltree_dragged_type = A_INVALID;
 
65
static CallType calltree_selected_type = A_INVALID;
 
66
 
 
67
static const gchar *calltree_dragged_call_id = NULL;
 
68
static const gchar *calltree_selected_call_id = NULL;
 
69
static const gchar *calltree_dragged_path = NULL;
 
70
static const gchar *calltree_selected_path = NULL;
 
71
 
 
72
static gint calltree_dragged_path_depth = -1;
 
73
static gint calltree_selected_path_depth = -1;
 
74
 
 
75
static callable_obj_t *calltree_dragged_call = NULL;
 
76
static callable_obj_t *calltree_selected_call = NULL;
 
77
 
 
78
static conference_obj_t *calltree_dragged_conf = NULL;
 
79
static conference_obj_t *calltree_selected_conf = NULL;
 
80
 
 
81
static void drag_end_cb(GtkWidget *, GdkDragContext *, gpointer);
 
82
static void drag_data_received_cb(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
 
83
static void drag_history_received_cb(GtkWidget *, GdkDragContext *, gint, gint, GtkSelectionData *, guint, guint, gpointer);
 
84
static void menuitem_response(gchar *);
 
85
 
 
86
enum {
 
87
    COLUMN_ACCOUNT_PIXBUF = 0,
 
88
    COLUMN_ACCOUNT_DESC,
 
89
    COLUMN_ACCOUNT_SECURITY_PIXBUF,
 
90
    COLUMN_ACCOUNT_PTR
 
91
};
 
92
 
 
93
/**
 
94
 * Show popup menu
 
95
 */
 
96
static gboolean
 
97
popup_menu(GtkWidget *widget,
 
98
           gpointer   user_data UNUSED)
 
99
{
 
100
    show_popup_menu(widget, NULL);
 
101
    return TRUE;
 
102
}
 
103
 
 
104
/* Call back when the user click on a call in the list */
 
105
static void
 
106
call_selected_cb(GtkTreeSelection *sel, void* data UNUSED)
 
107
{
 
108
    DEBUG("CallTree: Selection callback");
 
109
    GtkTreeModel *model = GTK_TREE_MODEL(active_calltree_tab->store);
 
110
 
 
111
    GtkTreeIter iter;
 
112
 
 
113
    if (!gtk_tree_selection_get_selected(sel, &model, &iter))
 
114
        return;
 
115
 
 
116
    if (active_calltree_tab == history_tab)
 
117
        DEBUG("CallTree: Current call tree is history");
 
118
    else if (active_calltree_tab == current_calls_tab)
 
119
        DEBUG("CallTree: Current call tree is current calls");
 
120
 
 
121
    // store info for dragndrop
 
122
    GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
 
123
    gchar *string_path = gtk_tree_path_to_string(path);
 
124
    calltree_selected_path_depth = gtk_tree_path_get_depth(path);
 
125
 
 
126
    if (gtk_tree_model_iter_has_child(model, &iter)) {
 
127
        DEBUG("CallTree: Selected a conference");
 
128
        calltree_selected_type = A_CONFERENCE;
 
129
 
 
130
        GValue val;
 
131
        val.g_type = 0;
 
132
        gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
 
133
 
 
134
        calltree_selected_conf = (conference_obj_t*) g_value_get_pointer(&val);
 
135
        g_value_unset(&val);
 
136
 
 
137
        if (calltree_selected_conf) {
 
138
            calltab_select_conf(active_calltree_tab, calltree_selected_conf);
 
139
            calltree_selected_call_id = calltree_selected_conf->_confID;
 
140
            calltree_selected_path = string_path;
 
141
            calltree_selected_call = NULL;
 
142
 
 
143
            if (calltree_selected_conf->_im_widget)
 
144
                im_window_show_tab(calltree_selected_conf->_im_widget);
 
145
 
 
146
            DEBUG("CallTree: selected_path %s, selected_conf_id %s, selected_path_depth %d",
 
147
                  calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
 
148
        }
 
149
    } else {
 
150
        DEBUG("CallTree: Selected a call");
 
151
        calltree_selected_type = A_CALL;
 
152
 
 
153
        GValue val;
 
154
        val.g_type = 0;
 
155
        gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
 
156
 
 
157
        calltree_selected_call = g_value_get_pointer(&val);
 
158
        g_value_unset(&val);
 
159
 
 
160
        if (calltree_selected_call) {
 
161
            calltab_select_call(active_calltree_tab, calltree_selected_call);
 
162
            calltree_selected_call_id = calltree_selected_call->_callID;
 
163
            calltree_selected_path = string_path;
 
164
            calltree_selected_conf = NULL;
 
165
 
 
166
            if (calltree_selected_call->_im_widget)
 
167
                im_window_show_tab(calltree_selected_call->_im_widget);
 
168
 
 
169
            DEBUG("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d",
 
170
                  calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
 
171
        }
 
172
    }
 
173
 
 
174
    update_actions();
 
175
}
 
176
 
 
177
/* A row is activated when it is double clicked */
 
178
void
 
179
row_activated(GtkTreeView *tree_view UNUSED,
 
180
              GtkTreePath *path UNUSED,
 
181
              GtkTreeViewColumn *column UNUSED,
 
182
              void * data UNUSED)
 
183
{
 
184
    DEBUG("CallTree: Double click action");
 
185
 
 
186
    if (calltab_get_selected_type(active_calltree_tab) == A_CALL) {
 
187
        DEBUG("CallTree: Selected a call");
 
188
        callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab);
 
189
 
 
190
        if (selectedCall) {
 
191
            // Get the right event from the right calltree
 
192
            if (active_calltree_tab == current_calls_tab) {
 
193
                switch (selectedCall->_state) {
 
194
                    case CALL_STATE_INCOMING:
 
195
                        dbus_accept(selectedCall);
 
196
                        break;
 
197
                    case CALL_STATE_HOLD:
 
198
                        dbus_unhold(selectedCall);
 
199
                        break;
 
200
                    case CALL_STATE_RINGING:
 
201
                    case CALL_STATE_CURRENT:
 
202
                    case CALL_STATE_BUSY:
 
203
                    case CALL_STATE_FAILURE:
 
204
                        break;
 
205
                    case CALL_STATE_DIALING:
 
206
                        sflphone_place_call(selectedCall);
 
207
                        break;
 
208
                    default:
 
209
                        WARN("Row activated - Should not happen!");
 
210
                        break;
 
211
                }
 
212
            } else {
 
213
                // If history or contact: double click action places a new call
 
214
                callable_obj_t* new_call = create_new_call(CALL, CALL_STATE_DIALING, "", selectedCall->_accountID, selectedCall->_peer_name, selectedCall->_peer_number);
 
215
 
 
216
                calllist_add_call(current_calls_tab, new_call);
 
217
                calltree_add_call(current_calls_tab, new_call, NULL);
 
218
                // Function sflphone_place_call (new_call) is processed in process_dialing
 
219
                sflphone_place_call(new_call);
 
220
                calltree_display(current_calls_tab);
 
221
            }
 
222
        }
 
223
    } else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
 
224
        DEBUG("CallTree: Selected a conference");
 
225
 
 
226
        if (active_calltree_tab == current_calls_tab) {
 
227
            conference_obj_t * selectedConf = calltab_get_selected_conf(current_calls_tab);
 
228
 
 
229
            if (selectedConf) {
 
230
 
 
231
                switch (selectedConf->_state) {
 
232
                    case CONFERENCE_STATE_ACTIVE_DETACHED:
 
233
                    case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD:
 
234
                        sflphone_add_main_participant(selectedConf);
 
235
                        break;
 
236
                    case CONFERENCE_STATE_HOLD:
 
237
                    case CONFERENCE_STATE_HOLD_RECORD:
 
238
                        dbus_unhold_conference(selectedConf);
 
239
                        break;
 
240
                    case CONFERENCE_STATE_ACTIVE_ATTACHED:
 
241
                    case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD:
 
242
                    default:
 
243
                        break;
 
244
                }
 
245
            }
 
246
        } else
 
247
            WARN("CallTree: Selected a conference in history, should not be possible");
 
248
    }
 
249
}
 
250
 
 
251
/* Catch cursor-activated signal. That is, when the entry is single clicked */
 
252
static void
 
253
row_single_click(GtkTreeView *tree_view UNUSED, void * data UNUSED)
 
254
{
 
255
    gchar * displaySasOnce = NULL;
 
256
 
 
257
    DEBUG("CallTree: Single click action");
 
258
 
 
259
    callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab);
 
260
    conference_obj_t *selectedConf = calltab_get_selected_conf(active_calltree_tab);
 
261
 
 
262
    if (active_calltree_tab == current_calls_tab)
 
263
        DEBUG("CallTree: Active calltree is current_calls");
 
264
    else if (active_calltree_tab == history_tab)
 
265
        DEBUG("CallTree: Active calltree is history");
 
266
 
 
267
    if (calltab_get_selected_type(active_calltree_tab) == A_CALL) {
 
268
        DEBUG("CallTree: Selected a call");
 
269
 
 
270
        if (selectedCall) {
 
271
            account_t *account_details = account_list_get_by_id(selectedCall->_accountID);
 
272
            DEBUG("AccountID %s", selectedCall->_accountID);
 
273
 
 
274
            if (account_details != NULL) {
 
275
                displaySasOnce = g_hash_table_lookup(account_details->properties, ACCOUNT_DISPLAY_SAS_ONCE);
 
276
                DEBUG("Display SAS once %s", displaySasOnce);
 
277
            } else {
 
278
                GHashTable *properties = sflphone_get_ip2ip_properties();
 
279
 
 
280
                if (properties != NULL) {
 
281
                    displaySasOnce = g_hash_table_lookup(properties, ACCOUNT_DISPLAY_SAS_ONCE);
 
282
                    DEBUG("IP2IP displaysasonce %s", displaySasOnce);
 
283
                }
 
284
            }
 
285
 
 
286
            /*  Make sure that we are not in the history tab since
 
287
             *  nothing is defined for it yet
 
288
             */
 
289
            if (active_calltree_tab == current_calls_tab) {
 
290
                switch (selectedCall->_srtp_state) {
 
291
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
 
292
                        selectedCall->_srtp_state = SRTP_STATE_ZRTP_SAS_CONFIRMED;
 
293
 
 
294
                        if (g_strcasecmp(displaySasOnce, "true") == 0)
 
295
                            selectedCall->_zrtp_confirmed = TRUE;
 
296
 
 
297
                        dbus_confirm_sas(selectedCall);
 
298
                        calltree_update_call(current_calls_tab, selectedCall);
 
299
                        break;
 
300
                    case SRTP_STATE_ZRTP_SAS_CONFIRMED:
 
301
                        selectedCall->_srtp_state = SRTP_STATE_ZRTP_SAS_UNCONFIRMED;
 
302
                        dbus_reset_sas(selectedCall);
 
303
                        calltree_update_call(current_calls_tab, selectedCall);
 
304
                        break;
 
305
                    default:
 
306
                        DEBUG("Single click but no action");
 
307
                        break;
 
308
                }
 
309
            }
 
310
        }
 
311
    } else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
 
312
        DEBUG("CallTree: Selected a conference");
 
313
        if (selectedConf)
 
314
            DEBUG("CallTree: There is actually a selected conf");
 
315
    } else
 
316
        WARN("CallTree: Warning: Unknown selection type");
 
317
}
 
318
 
 
319
static gboolean
 
320
button_pressed(GtkWidget* widget, GdkEventButton *event, gpointer user_data UNUSED)
 
321
{
 
322
    if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
 
323
        return FALSE;
 
324
 
 
325
    if (active_calltree_tab == current_calls_tab)
 
326
        show_popup_menu(widget, event);
 
327
    else if (active_calltree_tab == history_tab)
 
328
        show_popup_menu_history(widget, event);
 
329
    else
 
330
        show_popup_menu_contacts(widget, event);
 
331
 
 
332
    return TRUE;
 
333
}
 
334
 
 
335
 
 
336
static gchar *
 
337
calltree_display_call_info(callable_obj_t * c, CallDisplayType display_type, const gchar *const audio_codec)
 
338
{
 
339
    gchar display_number[strlen(c->_peer_number) + 1];
 
340
    strcpy(display_number, c->_peer_number);
 
341
 
 
342
    if (c->_type != CALL || c->_history_state != OUTGOING) {
 
343
        // Get the hostname for this call (NULL if not existent)
 
344
        gchar * hostname = g_strrstr(c->_peer_number, "@");
 
345
 
 
346
        // Test if we are dialing a new number
 
347
        if (*c->_peer_number && hostname)
 
348
            display_number[hostname - c->_peer_number] = '\0';
 
349
    }
 
350
 
 
351
    // Different display depending on type
 
352
    const gchar *name, *details = NULL;
 
353
 
 
354
    if (*c->_peer_name) {
 
355
        name = c->_peer_name;
 
356
        details = display_number;
 
357
    } else {
 
358
        name = display_number;
 
359
        details = "";
 
360
    }
 
361
 
 
362
    gchar *desc = g_markup_printf_escaped("<b>%s</b>   <i>%s</i>   ", name, details);
 
363
    gchar *suffix = NULL;
 
364
 
 
365
    switch (display_type) {
 
366
        case DISPLAY_TYPE_CALL:
 
367
            if (c->_state_code)
 
368
                suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>", c->_state_code_description, c->_state_code);
 
369
            break;
 
370
        case DISPLAY_TYPE_STATE_CODE :
 
371
 
 
372
            if (c->_state_code)
 
373
                suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>  <i>%s</i>",
 
374
                                                 c->_state_code_description, c->_state_code,
 
375
                                                 audio_codec);
 
376
            else
 
377
                suffix = g_markup_printf_escaped("\n<i>%s</i>", audio_codec);
 
378
 
 
379
            break;
 
380
        case DISPLAY_TYPE_CALL_TRANSFER:
 
381
            suffix = g_markup_printf_escaped("\n<i>Transfer to:%s</i> ", c->_trsft_to);
 
382
            break;
 
383
        case DISPLAY_TYPE_SAS:
 
384
            suffix = g_markup_printf_escaped("\n<i>Confirm SAS <b>%s</b> ?</i>", c->_sas);
 
385
            break;
 
386
        case DISPLAY_TYPE_HISTORY :
 
387
        default:
 
388
            break;
 
389
    }
 
390
 
 
391
    gchar *msg = g_strconcat(desc, suffix, NULL);
 
392
    g_free(desc);
 
393
    g_free(suffix);
 
394
    return msg;
 
395
}
 
396
 
 
397
void
 
398
calltree_create(calltab_t* tab, int searchbar_type)
 
399
{
 
400
    tab->tree = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
 
401
 
 
402
    // Fix bug #708 (resize)
 
403
    gtk_widget_set_size_request(tab->tree,100,80);
 
404
 
 
405
    gtk_container_set_border_width(GTK_CONTAINER(tab->tree), 0);
 
406
 
 
407
    calltree_sw = gtk_scrolled_window_new(NULL, NULL);
 
408
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(calltree_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
409
    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(calltree_sw), GTK_SHADOW_IN);
 
410
 
 
411
    tab->store = gtk_tree_store_new(4,
 
412
                                    GDK_TYPE_PIXBUF, /* Icon */
 
413
                                    G_TYPE_STRING,   /* Description */
 
414
                                    GDK_TYPE_PIXBUF, /* Security Icon */
 
415
                                    G_TYPE_POINTER   /* Pointer to the Object */
 
416
                                   );
 
417
 
 
418
    tab->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tab->store));
 
419
    gtk_tree_view_set_enable_search(GTK_TREE_VIEW(tab->view), FALSE);
 
420
    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tab->view), FALSE);
 
421
    g_signal_connect(G_OBJECT(tab->view), "row-activated",
 
422
                     G_CALLBACK(row_activated),
 
423
                     NULL);
 
424
 
 
425
    gtk_widget_set_can_focus(calltree_sw, TRUE);
 
426
    gtk_widget_grab_focus(calltree_sw);
 
427
 
 
428
    g_signal_connect(G_OBJECT(tab->view), "cursor-changed",
 
429
                     G_CALLBACK(row_single_click),
 
430
                     NULL);
 
431
 
 
432
    // Connect the popup menu
 
433
    g_signal_connect(G_OBJECT(tab->view), "popup-menu",
 
434
                     G_CALLBACK(popup_menu),
 
435
                     NULL);
 
436
    g_signal_connect(G_OBJECT(tab->view), "button-press-event",
 
437
                     G_CALLBACK(button_pressed),
 
438
                     NULL);
 
439
 
 
440
    if (tab != history_tab && tab != contacts_tab) {
 
441
        // Make calltree reordable for drag n drop
 
442
        gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tab->view), TRUE);
 
443
 
 
444
        // source widget drag n drop signals
 
445
        g_signal_connect(G_OBJECT(tab->view), "drag_end", G_CALLBACK(drag_end_cb), NULL);
 
446
 
 
447
        // destination widget drag n drop signals
 
448
        g_signal_connect(G_OBJECT(tab->view), "drag_data_received", G_CALLBACK(drag_data_received_cb), NULL);
 
449
 
 
450
        calltree_popupmenu = gtk_menu_new();
 
451
 
 
452
        calltree_menu_items = gtk_menu_item_new_with_label(SFL_TRANSFER_CALL);
 
453
        g_signal_connect_swapped(calltree_menu_items, "activate",
 
454
                                 G_CALLBACK(menuitem_response), (gpointer) g_strdup(SFL_TRANSFER_CALL));
 
455
        gtk_menu_shell_append(GTK_MENU_SHELL(calltree_popupmenu), calltree_menu_items);
 
456
        gtk_widget_show(calltree_menu_items);
 
457
 
 
458
        calltree_menu_items = gtk_menu_item_new_with_label(SFL_CREATE_CONFERENCE);
 
459
        g_signal_connect_swapped(calltree_menu_items, "activate",
 
460
                                 G_CALLBACK(menuitem_response), (gpointer) g_strdup(SFL_CREATE_CONFERENCE));
 
461
        gtk_menu_shell_append(GTK_MENU_SHELL(calltree_popupmenu), calltree_menu_items);
 
462
        gtk_widget_show(calltree_menu_items);
 
463
    } else if (tab == history_tab) {
 
464
        gtk_tree_view_set_show_expanders(GTK_TREE_VIEW(tab->view), TRUE);
 
465
        g_signal_connect(G_OBJECT(tab->view), "drag_data_received", G_CALLBACK(drag_history_received_cb), NULL);
 
466
    }
 
467
 
 
468
    gtk_widget_grab_focus(GTK_WIDGET(tab->view));
 
469
 
 
470
    calltree_rend = gtk_cell_renderer_pixbuf_new();
 
471
    calltree_col = gtk_tree_view_column_new_with_attributes("Icon", calltree_rend, "pixbuf", COLUMN_ACCOUNT_PIXBUF,
 
472
                   NULL);
 
473
    gtk_tree_view_append_column(GTK_TREE_VIEW(tab->view), calltree_col);
 
474
 
 
475
    calltree_rend = gtk_cell_renderer_text_new();
 
476
    calltree_col = gtk_tree_view_column_new_with_attributes("Description", calltree_rend,
 
477
                   "markup", COLUMN_ACCOUNT_DESC,
 
478
                   NULL);
 
479
    g_object_set(calltree_rend, "wrap-mode", (PangoWrapMode) PANGO_WRAP_WORD_CHAR, NULL);
 
480
 
 
481
    static const gint SFLPHONE_HIG_MARGIN = 10;
 
482
    static const gint CALLTREE_CALL_ICON_WIDTH = 24;
 
483
    static const gint CALLTREE_SECURITY_ICON_WIDTH = 24;
 
484
    gint CALLTREE_TEXT_WIDTH = (MAIN_WINDOW_WIDTH -
 
485
                                CALLTREE_SECURITY_ICON_WIDTH -
 
486
                                CALLTREE_CALL_ICON_WIDTH - (2 * SFLPHONE_HIG_MARGIN));
 
487
    g_object_set(calltree_rend, "wrap-width", CALLTREE_TEXT_WIDTH, NULL);
 
488
    gtk_tree_view_append_column(GTK_TREE_VIEW(tab->view), calltree_col);
 
489
 
 
490
    /* Security icon */
 
491
    calltree_rend = gtk_cell_renderer_pixbuf_new();
 
492
    calltree_col = gtk_tree_view_column_new_with_attributes("Icon",
 
493
                   calltree_rend,
 
494
                   "pixbuf", COLUMN_ACCOUNT_SECURITY_PIXBUF,
 
495
                   NULL);
 
496
    g_object_set(calltree_rend, "xalign", (gfloat) 1.0, NULL);
 
497
    g_object_set(calltree_rend, "yalign", (gfloat) 0.0, NULL);
 
498
    gtk_tree_view_append_column(GTK_TREE_VIEW(tab->view), calltree_col);
 
499
 
 
500
    g_object_unref(G_OBJECT(tab->store));
 
501
    gtk_container_add(GTK_CONTAINER(calltree_sw), tab->view);
 
502
 
 
503
    calltree_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view));
 
504
    g_signal_connect(G_OBJECT(calltree_sel), "changed",
 
505
                     G_CALLBACK(call_selected_cb),
 
506
                     NULL);
 
507
 
 
508
    gtk_box_pack_start(GTK_BOX(tab->tree), calltree_sw, TRUE, TRUE, 0);
 
509
 
 
510
    // search bar if tab is either "history" or "addressbook"
 
511
    if (searchbar_type) {
 
512
        calltab_create_searchbar(tab);
 
513
 
 
514
        if (tab->searchbar != NULL)
 
515
            gtk_box_pack_start(GTK_BOX(tab->tree), tab->searchbar, FALSE, TRUE, 0);
 
516
    }
 
517
 
 
518
    gtk_widget_show(tab->tree);
 
519
}
 
520
 
 
521
 
 
522
static void
 
523
calltree_remove_call_recursive(calltab_t* tab, callable_obj_t * callable, GtkTreeIter *parent)
 
524
{
 
525
    GtkTreeStore *store = tab->store;
 
526
    GtkTreeModel *model = GTK_TREE_MODEL(store);
 
527
 
 
528
    if (!callable)
 
529
        ERROR("CallTree: Error: Not a valid call");
 
530
 
 
531
    DEBUG("CallTree: Remove call %s", callable->_callID);
 
532
 
 
533
    int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
 
534
 
 
535
    for (int i = 0; i < nbChild; i++) {
 
536
        GtkTreeIter child;
 
537
 
 
538
        if (gtk_tree_model_iter_nth_child(model, &child, parent, i)) {
 
539
            if (gtk_tree_model_iter_has_child(model, &child))
 
540
                calltree_remove_call_recursive(tab, callable, &child);
 
541
 
 
542
            GValue val = { .g_type = 0 };
 
543
            gtk_tree_model_get_value(model, &child, COLUMN_ACCOUNT_PTR, &val);
 
544
 
 
545
            callable_obj_t * iterCall = g_value_get_pointer(&val);
 
546
            g_value_unset(&val);
 
547
 
 
548
            if (iterCall == callable)
 
549
                gtk_tree_store_remove(store, &child);
 
550
        }
 
551
    }
 
552
 
 
553
    if (calltab_get_selected_call(tab) == callable)
 
554
        calltab_select_call(tab, NULL);
 
555
 
 
556
    update_actions();
 
557
 
 
558
    statusbar_update_clock("");
 
559
}
 
560
 
 
561
void
 
562
calltree_remove_call(calltab_t* tab, callable_obj_t * c)
 
563
{
 
564
    calltree_remove_call_recursive(tab, c, NULL);
 
565
}
 
566
 
 
567
static void
 
568
calltree_update_call_recursive(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
 
569
{
 
570
    GdkPixbuf *pixbuf = NULL;
 
571
    GdkPixbuf *pixbuf_security = NULL;
 
572
    GtkTreeIter iter;
 
573
    GValue val;
 
574
    GtkTreeStore* store = tab->store;
 
575
 
 
576
    gchar* srtp_enabled = NULL;
 
577
    gboolean display_sas = TRUE;
 
578
    account_t* account_details=NULL;
 
579
 
 
580
    int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
 
581
 
 
582
    if (c) {
 
583
        account_details = account_list_get_by_id(c->_accountID);
 
584
 
 
585
        if (account_details != NULL) {
 
586
            srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
 
587
 
 
588
            if (g_strcasecmp(g_hash_table_lookup(account_details->properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
 
589
                display_sas = FALSE;
 
590
        } else {
 
591
            GHashTable * properties = sflphone_get_ip2ip_properties();
 
592
 
 
593
            if (properties != NULL) {
 
594
                srtp_enabled = g_hash_table_lookup(properties, ACCOUNT_SRTP_ENABLED);
 
595
 
 
596
                if (g_strcasecmp(g_hash_table_lookup(properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
 
597
                    display_sas = FALSE;
 
598
            }
 
599
        }
 
600
    }
 
601
 
 
602
    for (gint i = 0; i < nbChild; i++) {
 
603
 
 
604
        if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, parent, i)) {
 
605
 
 
606
            if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
 
607
                calltree_update_call_recursive(tab, c, &iter);
 
608
 
 
609
            val.g_type = 0;
 
610
            gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, COLUMN_ACCOUNT_PTR, &val);
 
611
 
 
612
            callable_obj_t * iterCall = (callable_obj_t*) g_value_get_pointer(&val);
 
613
            g_value_unset(&val);
 
614
 
 
615
            if (iterCall != c)
 
616
                continue;
 
617
 
 
618
            /* Update text */
 
619
            gchar * description = NULL;
 
620
            gchar * audio_codec = call_get_audio_codec(c);
 
621
 
 
622
            if (c->_state == CALL_STATE_TRANSFER)
 
623
                description = calltree_display_call_info(c, DISPLAY_TYPE_CALL_TRANSFER, "");
 
624
            else
 
625
                if (c->_sas && display_sas && c->_srtp_state == SRTP_STATE_ZRTP_SAS_UNCONFIRMED && !c->_zrtp_confirmed)
 
626
                    description = calltree_display_call_info(c, DISPLAY_TYPE_SAS, "");
 
627
                else
 
628
                    description = calltree_display_call_info(c, DISPLAY_TYPE_STATE_CODE, audio_codec);
 
629
 
 
630
            g_free(audio_codec);
 
631
 
 
632
            /* Update icons */
 
633
            if (tab == current_calls_tab) {
 
634
                DEBUG("Receiving in state %d", c->_state);
 
635
 
 
636
                switch (c->_state) {
 
637
                    case CALL_STATE_HOLD:
 
638
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL);
 
639
                        break;
 
640
                    case CALL_STATE_INCOMING:
 
641
                    case CALL_STATE_RINGING:
 
642
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
 
643
                        break;
 
644
                    case CALL_STATE_CURRENT:
 
645
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
 
646
                        break;
 
647
                    case CALL_STATE_DIALING:
 
648
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
 
649
                        break;
 
650
                    case CALL_STATE_FAILURE:
 
651
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL);
 
652
                        break;
 
653
                    case CALL_STATE_BUSY:
 
654
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/busy.svg", NULL);
 
655
                        break;
 
656
                    case CALL_STATE_TRANSFER:
 
657
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/transfer.svg", NULL);
 
658
                        break;
 
659
                    case CALL_STATE_RECORD:
 
660
                        pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
 
661
                        break;
 
662
                    default:
 
663
                        WARN("Update calltree - Should not happen!");
 
664
                }
 
665
 
 
666
                switch (c->_srtp_state) {
 
667
                    case SRTP_STATE_SDES_SUCCESS:
 
668
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
 
669
                        break;
 
670
                    case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
 
671
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_unconfirmed.svg", NULL);
 
672
                        if (c->_sas != NULL)
 
673
                            DEBUG("SAS is ready with value %s", c->_sas);
 
674
                        break;
 
675
                    case SRTP_STATE_ZRTP_SAS_CONFIRMED:
 
676
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
 
677
                        break;
 
678
                    case SRTP_STATE_ZRTP_SAS_SIGNED:
 
679
                        pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_certified.svg", NULL);
 
680
                        break;
 
681
                    case SRTP_STATE_UNLOCKED:
 
682
                        if (g_strcasecmp(srtp_enabled,"true") == 0)
 
683
                            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
 
684
                        break;
 
685
                    default:
 
686
                        WARN("Update calltree srtp state #%d- Should not happen!", c->_srtp_state);
 
687
                        if (g_strcasecmp(srtp_enabled, "true") == 0)
 
688
                            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
 
689
                }
 
690
 
 
691
            } else if (tab == history_tab) {
 
692
                if (parent == NULL) {
 
693
                    // parent is NULL this is not a conference participant
 
694
                    switch (c->_history_state) {
 
695
                        case INCOMING:
 
696
                            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/incoming.svg", NULL);
 
697
                            break;
 
698
                        case OUTGOING:
 
699
                            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/outgoing.svg", NULL);
 
700
                            break;
 
701
                        case MISSED:
 
702
                            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/missed.svg", NULL);
 
703
                            break;
 
704
                        default:
 
705
                            WARN("History - Should not happen!");
 
706
                    }
 
707
                } else // parent is not NULL this is a conference participant
 
708
                    pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
 
709
 
 
710
                g_free(description);
 
711
                description = calltree_display_call_info(c, DISPLAY_TYPE_HISTORY, "");
 
712
                gchar * date = get_formatted_start_timestamp(c->_time_start);
 
713
                gchar *duration = get_call_duration(c);
 
714
                gchar *full_duration = g_strconcat(date , duration , NULL);
 
715
                g_free(date);
 
716
                g_free(duration);
 
717
 
 
718
                gchar *old_description = description;
 
719
                description = g_strconcat(old_description , full_duration, NULL);
 
720
                g_free(full_duration);
 
721
                g_free(old_description);
 
722
            }
 
723
 
 
724
            gtk_tree_store_set(store, &iter,
 
725
                               COLUMN_ACCOUNT_PIXBUF, pixbuf,
 
726
                               COLUMN_ACCOUNT_DESC, description,
 
727
                               COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
 
728
                               COLUMN_ACCOUNT_PTR, c,
 
729
                               -1);
 
730
 
 
731
            g_free(description);
 
732
 
 
733
            if (pixbuf != NULL)
 
734
                g_object_unref(G_OBJECT(pixbuf));
 
735
            if (pixbuf_security != NULL)
 
736
                g_object_unref(G_OBJECT(pixbuf_security));
 
737
        }
 
738
    }
 
739
 
 
740
    update_actions();
 
741
}
 
742
 
 
743
void calltree_update_call(calltab_t* tab, callable_obj_t * c)
 
744
{
 
745
    return calltree_update_call_recursive(tab, c, NULL);
 
746
}
 
747
 
 
748
void calltree_add_call(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
 
749
{
 
750
    g_assert(tab != history_tab);
 
751
 
 
752
    account_t* account_details = NULL;
 
753
 
 
754
    GdkPixbuf *pixbuf = NULL;
 
755
    GdkPixbuf *pixbuf_security = NULL;
 
756
    GtkTreeIter iter;
 
757
    gchar* key_exchange = NULL;
 
758
    gchar* srtp_enabled = NULL;
 
759
 
 
760
    // New call in the list
 
761
 
 
762
    gchar *description = calltree_display_call_info(c, DISPLAY_TYPE_CALL, "");
 
763
 
 
764
    gtk_tree_store_prepend(tab->store, &iter, parent);
 
765
 
 
766
    if (c) {
 
767
        account_details = account_list_get_by_id(c->_accountID);
 
768
 
 
769
        if (account_details) {
 
770
            srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
 
771
            key_exchange = g_hash_table_lookup(account_details->properties, ACCOUNT_KEY_EXCHANGE);
 
772
        }
 
773
    }
 
774
 
 
775
    DEBUG("Added call key exchange is %s", key_exchange);
 
776
 
 
777
    if (tab == current_calls_tab) {
 
778
        switch (c->_state) {
 
779
            case CALL_STATE_INCOMING:
 
780
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
 
781
                break;
 
782
            case CALL_STATE_DIALING:
 
783
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
 
784
                break;
 
785
            case CALL_STATE_RINGING:
 
786
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
 
787
                break;
 
788
            case CALL_STATE_CURRENT:
 
789
                // If the call has been initiated by a another client and, when we start, it is already current
 
790
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
 
791
                break;
 
792
            case CALL_STATE_HOLD:
 
793
                // If the call has been initiated by a another client and, when we start, it is already current
 
794
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL);
 
795
                break;
 
796
            case CALL_STATE_RECORD:
 
797
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
 
798
                break;
 
799
            case CALL_STATE_FAILURE:
 
800
                // If the call has been initiated by a another client and, when we start, it is already current
 
801
                pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL);
 
802
                break;
 
803
            default:
 
804
                WARN("Update calltree add - Should not happen!");
 
805
        }
 
806
 
 
807
        if (srtp_enabled && g_strcasecmp(srtp_enabled, "true") == 0)
 
808
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/secure_off.svg", NULL);
 
809
 
 
810
    } else if (tab == contacts_tab)
 
811
        pixbuf = c->_contact_thumbnail;
 
812
    else
 
813
        WARN("CallTree: This widget doesn't exist - This is a bug in the application.");
 
814
 
 
815
    //Resize it
 
816
    if (pixbuf && (gdk_pixbuf_get_width(pixbuf) > 32 || gdk_pixbuf_get_height(pixbuf) > 32)) {
 
817
        GdkPixbuf *new = gdk_pixbuf_scale_simple(pixbuf, 32, 32, GDK_INTERP_BILINEAR);
 
818
        g_object_unref(pixbuf);
 
819
        pixbuf = new;
 
820
    }
 
821
 
 
822
    if (pixbuf_security && (gdk_pixbuf_get_width(pixbuf_security) > 32 || gdk_pixbuf_get_height(pixbuf_security) > 32)) {
 
823
        GdkPixbuf *new = gdk_pixbuf_scale_simple(pixbuf_security, 32, 32, GDK_INTERP_BILINEAR);
 
824
        g_object_unref(pixbuf_security);
 
825
        pixbuf_security = new;
 
826
    }
 
827
 
 
828
    gtk_tree_store_set(tab->store, &iter,
 
829
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
 
830
                       COLUMN_ACCOUNT_DESC, description,
 
831
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
 
832
                       COLUMN_ACCOUNT_PTR, c,
 
833
                       -1);
 
834
 
 
835
    g_free(description);
 
836
 
 
837
    if (pixbuf != NULL)
 
838
        g_object_unref(G_OBJECT(pixbuf));
 
839
 
 
840
    if (pixbuf_security != NULL)
 
841
        g_object_unref(G_OBJECT(pixbuf));
 
842
 
 
843
    gtk_tree_view_set_model(GTK_TREE_VIEW(history_tab->view), GTK_TREE_MODEL(history_tab->store));
 
844
 
 
845
    gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view)), &iter);
 
846
}
 
847
 
 
848
void calltree_add_history_entry(callable_obj_t *c)
 
849
{
 
850
    if (!eel_gconf_get_integer(HISTORY_ENABLED))
 
851
        return;
 
852
 
 
853
    // New call in the list
 
854
    gchar * description = calltree_display_call_info(c, DISPLAY_TYPE_HISTORY, "");
 
855
 
 
856
    GtkTreeIter iter;
 
857
    gtk_tree_store_prepend(history_tab->store, &iter, NULL);
 
858
 
 
859
    GdkPixbuf *pixbuf = NULL;
 
860
 
 
861
    switch (c->_history_state) {
 
862
        case INCOMING:
 
863
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/incoming.svg", NULL);
 
864
            break;
 
865
        case OUTGOING:
 
866
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/outgoing.svg", NULL);
 
867
            break;
 
868
        case MISSED:
 
869
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/missed.svg", NULL);
 
870
            break;
 
871
        default:
 
872
            WARN("History - Should not happen!");
 
873
    }
 
874
 
 
875
    gchar *date = get_formatted_start_timestamp(c->_time_start);
 
876
    gchar *duration = get_call_duration(c);
 
877
    gchar * full_duration = g_strconcat(date, duration, NULL);
 
878
    g_free(date);
 
879
    g_free(duration);
 
880
    gchar * full_description = g_strconcat(description, full_duration, NULL);
 
881
    g_free(description);
 
882
    g_free(full_duration);
 
883
 
 
884
    //Resize it
 
885
    if (pixbuf && (gdk_pixbuf_get_width(pixbuf) > 32 || gdk_pixbuf_get_height(pixbuf) > 32)) {
 
886
        GdkPixbuf *new = gdk_pixbuf_scale_simple(pixbuf, 32, 32, GDK_INTERP_BILINEAR);
 
887
        g_object_unref(pixbuf);
 
888
        pixbuf = new;
 
889
    }
 
890
 
 
891
    gtk_tree_store_set(history_tab->store, &iter,
 
892
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
 
893
                       COLUMN_ACCOUNT_DESC, full_description,
 
894
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, NULL,
 
895
                       COLUMN_ACCOUNT_PTR, c,
 
896
                       -1);
 
897
 
 
898
    g_free(full_description);
 
899
 
 
900
    if (pixbuf != NULL)
 
901
        g_object_unref(G_OBJECT(pixbuf));
 
902
 
 
903
    gtk_tree_view_set_model(GTK_TREE_VIEW(history_tab->view), GTK_TREE_MODEL(history_tab->store));
 
904
 
 
905
    history_search();
 
906
}
 
907
 
 
908
 
 
909
void calltree_add_conference_to_current_calls(conference_obj_t* conf)
 
910
{
 
911
    account_t *account_details = NULL;
 
912
 
 
913
    if (!conf) {
 
914
        ERROR("Calltree: Error: Conference is null");
 
915
        return;
 
916
    }
 
917
 
 
918
    DEBUG("Calltree: Add conference %s", conf->_confID);
 
919
 
 
920
    GtkTreeIter iter;
 
921
    gtk_tree_store_append(current_calls_tab->store, &iter, NULL);
 
922
 
 
923
    GdkPixbuf *pixbuf = NULL;
 
924
 
 
925
    switch (conf->_state) {
 
926
        case CONFERENCE_STATE_ACTIVE_ATTACHED:
 
927
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersAttached.svg", NULL);
 
928
            break;
 
929
        case CONFERENCE_STATE_ACTIVE_DETACHED:
 
930
        case CONFERENCE_STATE_HOLD:
 
931
        case CONFERENCE_STATE_HOLD_RECORD:
 
932
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersDetached.svg", NULL);
 
933
            break;
 
934
        case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD:
 
935
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersAttachedRec.svg", NULL);
 
936
            break;
 
937
        case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD:
 
938
            pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersDetachedRec.svg", NULL);
 
939
            break;
 
940
        default:
 
941
            WARN("Update conference add - Should not happen!");
 
942
    }
 
943
 
 
944
    //Resize it
 
945
    if (pixbuf) {
 
946
        if (gdk_pixbuf_get_width(pixbuf) > 32 || gdk_pixbuf_get_height(pixbuf) > 32) {
 
947
            GdkPixbuf *new = gdk_pixbuf_scale_simple(pixbuf, 32, 32, GDK_INTERP_BILINEAR);
 
948
            g_object_unref(pixbuf);
 
949
            pixbuf = new;
 
950
        }
 
951
    } else
 
952
        DEBUG("Error no pixbuff for conference from %s", ICONS_DIR);
 
953
 
 
954
    GdkPixbuf *pixbuf_security = NULL;
 
955
 
 
956
    // Used to determine if at least one participant use a security feature
 
957
    // If true (at least on call use a security feature) we need to display security icons
 
958
    conf->_conf_srtp_enabled = FALSE;
 
959
 
 
960
    // Used to determine if the conference is secured
 
961
    // Every participant to a conference must be secured, the conference is not secured elsewhere
 
962
    conf->_conference_secured = TRUE;
 
963
 
 
964
    if (conf->participant_list) {
 
965
        DEBUG("Calltree: Determine if at least one participant uses SRTP");
 
966
 
 
967
        for (GSList *part = conf->participant_list; part; part = g_slist_next(part)) {
 
968
            const gchar * const call_id = (gchar *) part->data;
 
969
            callable_obj_t *call = calllist_get_call(current_calls_tab, call_id);
 
970
 
 
971
            if (call == NULL)
 
972
                ERROR("Calltree: Error: Could not find call %s in call list", call_id);
 
973
            else {
 
974
                account_details = account_list_get_by_id(call->_accountID);
 
975
                gchar *srtp_enabled = "";
 
976
 
 
977
                if (!account_details)
 
978
                    ERROR("Calltree: Error: Could not find account %s in account list", call->_accountID);
 
979
                else
 
980
                    srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
 
981
 
 
982
                if (g_strcasecmp(srtp_enabled, "true") == 0) {
 
983
                    DEBUG("Calltree: SRTP enabled for participant %s", call_id);
 
984
                    conf->_conf_srtp_enabled = TRUE;
 
985
                    break;
 
986
                } else
 
987
                    DEBUG("Calltree: SRTP is not enabled for participant %s", call_id);
 
988
            }
 
989
        }
 
990
 
 
991
        DEBUG("Calltree: Determine if all conference participants are secured");
 
992
 
 
993
        if (conf->_conf_srtp_enabled) {
 
994
            for (GSList *part = conf->participant_list; part; part = g_slist_next(part)) {
 
995
                const gchar * const call_id = (gchar *) part->data;
 
996
                callable_obj_t *call = calllist_get_call(current_calls_tab, call_id);
 
997
 
 
998
                if (call) {
 
999
                    if (call->_srtp_state == SRTP_STATE_UNLOCKED) {
 
1000
                        DEBUG("Calltree: Participant %s is not secured", call_id);
 
1001
                        conf->_conference_secured = FALSE;
 
1002
                        break;
 
1003
                    } else
 
1004
                        DEBUG("Calltree: Participant %s is secured", call_id);
 
1005
                }
 
1006
            }
 
1007
        }
 
1008
    }
 
1009
 
 
1010
    if (conf->_conf_srtp_enabled) {
 
1011
        if (conf->_conference_secured) {
 
1012
            DEBUG("Calltree: Conference is secured");
 
1013
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
 
1014
        } else {
 
1015
            DEBUG("Calltree: Conference is not secured");
 
1016
            pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
 
1017
        }
 
1018
    }
 
1019
 
 
1020
    DEBUG("Calltree: Add conference to tree store");
 
1021
 
 
1022
    gchar *description = g_markup_printf_escaped("<b>%s</b>", "");
 
1023
    gtk_tree_store_set(current_calls_tab->store, &iter,
 
1024
                       COLUMN_ACCOUNT_PIXBUF, pixbuf,
 
1025
                       COLUMN_ACCOUNT_DESC, description,
 
1026
                       COLUMN_ACCOUNT_SECURITY_PIXBUF, pixbuf_security,
 
1027
                       COLUMN_ACCOUNT_PTR, conf,
 
1028
                       -1);
 
1029
    g_free(description);
 
1030
 
 
1031
    if (pixbuf)
 
1032
        g_object_unref(pixbuf);
 
1033
 
 
1034
    if (pixbuf_security)
 
1035
        g_object_unref(pixbuf_security);
 
1036
 
 
1037
    for (GSList *part = conf->participant_list; part; part = g_slist_next(part)) {
 
1038
        const gchar * const call_id = (gchar *) part->data;
 
1039
        callable_obj_t *call = calllist_get_call(current_calls_tab, call_id);
 
1040
 
 
1041
        calltree_remove_call(current_calls_tab, call);
 
1042
        calltree_add_call(current_calls_tab, call, &iter);
 
1043
    }
 
1044
 
 
1045
    gtk_tree_view_set_model(GTK_TREE_VIEW(current_calls_tab->view),
 
1046
                            GTK_TREE_MODEL(current_calls_tab->store));
 
1047
 
 
1048
    GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(current_calls_tab->store), &iter);
 
1049
 
 
1050
    gtk_tree_view_expand_row(GTK_TREE_VIEW(current_calls_tab->view), path, FALSE);
 
1051
 
 
1052
    update_actions();
 
1053
}
 
1054
 
 
1055
static
 
1056
void calltree_remove_conference_recursive(calltab_t* tab, const conference_obj_t* conf, GtkTreeIter *parent)
 
1057
{
 
1058
    GtkTreeModel *model = GTK_TREE_MODEL(tab->store);
 
1059
    int nbChildren = gtk_tree_model_iter_n_children(model, parent);
 
1060
 
 
1061
    for (int i = 0; i < nbChildren; i++) {
 
1062
        GtkTreeIter iter_parent;
 
1063
 
 
1064
        /* if the nth child of parent has one or more children */
 
1065
        if (gtk_tree_model_iter_nth_child(model, &iter_parent, parent, i)) {
 
1066
            /* RECURSION! */
 
1067
            if (gtk_tree_model_iter_has_child(model, &iter_parent))
 
1068
                calltree_remove_conference_recursive(tab, conf, &iter_parent);
 
1069
 
 
1070
            GValue confval;
 
1071
            confval.g_type = 0;
 
1072
            gtk_tree_model_get_value(model, &iter_parent, COLUMN_ACCOUNT_PTR, &confval);
 
1073
 
 
1074
            conference_obj_t *tempconf = (conference_obj_t*) g_value_get_pointer(&confval);
 
1075
            g_value_unset(&confval);
 
1076
 
 
1077
            /* if this is the conference we want to remove */
 
1078
            if (tempconf == conf) {
 
1079
                int nbParticipants = gtk_tree_model_iter_n_children(model, &iter_parent);
 
1080
                DEBUG("CallTree: nbParticipants: %d", nbParticipants);
 
1081
 
 
1082
                for (int j = 0; j < nbParticipants; j++) {
 
1083
                    GtkTreeIter iter_child;
 
1084
 
 
1085
                    if (gtk_tree_model_iter_nth_child(model, &iter_child, &iter_parent, j)) {
 
1086
                        GValue callval;
 
1087
                        callval.g_type = 0;
 
1088
                        gtk_tree_model_get_value(model, &iter_child, COLUMN_ACCOUNT_PTR, &callval);
 
1089
 
 
1090
                        callable_obj_t *call = g_value_get_pointer(&callval);
 
1091
                        g_value_unset(&callval);
 
1092
 
 
1093
                        // do not add back call in history calltree when cleaning it
 
1094
                        if (call && tab != history_tab)
 
1095
                            calltree_add_call(tab, call, NULL);
 
1096
                    }
 
1097
                }
 
1098
 
 
1099
                DEBUG("CallTree: Remove conference %s", conf->_confID);
 
1100
                gtk_tree_store_remove(tab->store, &iter_parent);
 
1101
            }
 
1102
        }
 
1103
    }
 
1104
 
 
1105
    if (calltab_get_selected_conf(tab) == conf)
 
1106
        calltab_select_conf(tab, NULL);
 
1107
 
 
1108
    update_actions();
 
1109
}
 
1110
 
 
1111
void calltree_remove_conference(calltab_t* tab, const conference_obj_t* conf)
 
1112
{
 
1113
    DEBUG("CallTree: Remove conference %s", conf->_confID);
 
1114
    calltree_remove_conference_recursive(tab, conf, NULL);
 
1115
    DEBUG("CallTree: Finished Removing conference");
 
1116
}
 
1117
 
 
1118
void calltree_display(calltab_t *tab)
 
1119
{
 
1120
    /* If we already are displaying the specified calltree */
 
1121
    if (active_calltree_tab == tab)
 
1122
        return;
 
1123
 
 
1124
    if (tab == current_calls_tab) {
 
1125
        if (active_calltree_tab == contacts_tab)
 
1126
            gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(contactButton_), FALSE);
 
1127
        else
 
1128
            gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(historyButton_), FALSE);
 
1129
    } else if (tab == history_tab) {
 
1130
        if (active_calltree_tab == contacts_tab)
 
1131
            gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(contactButton_), FALSE);
 
1132
 
 
1133
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(historyButton_), TRUE);
 
1134
    } else if (tab == contacts_tab) {
 
1135
        if (active_calltree_tab == history_tab)
 
1136
            gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(historyButton_), FALSE);
 
1137
 
 
1138
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(contactButton_), TRUE);
 
1139
        set_focus_on_addressbook_searchbar();
 
1140
    } else
 
1141
        ERROR("CallTree: Error: Not a valid call tab  (%d, %s)", __LINE__, __FILE__);
 
1142
 
 
1143
    gtk_widget_hide(active_calltree_tab->tree);
 
1144
    active_calltree_tab = tab;
 
1145
    gtk_widget_show(active_calltree_tab->tree);
 
1146
 
 
1147
    GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(active_calltree_tab->view));
 
1148
    DEBUG("CallTree: Emit signal changed from calltree_display");
 
1149
    g_signal_emit_by_name(sel, "changed");
 
1150
    update_actions();
 
1151
}
 
1152
 
 
1153
 
 
1154
gboolean calltree_update_clock(gpointer data UNUSED)
 
1155
{
 
1156
    char timestr[20];
 
1157
    const gchar *msg = "";
 
1158
    long duration;
 
1159
    callable_obj_t *c = calltab_get_selected_call(current_calls_tab);
 
1160
 
 
1161
    if (c)
 
1162
        switch (c->_state) {
 
1163
            case CALL_STATE_INVALID:
 
1164
            case CALL_STATE_INCOMING:
 
1165
            case CALL_STATE_RINGING:
 
1166
            case CALL_STATE_FAILURE:
 
1167
            case CALL_STATE_DIALING:
 
1168
            case CALL_STATE_BUSY:
 
1169
                break;
 
1170
            default:
 
1171
                duration = difftime(time(NULL), c->_time_start);
 
1172
 
 
1173
                if (duration < 0)
 
1174
                    duration = 0;
 
1175
 
 
1176
                g_snprintf(timestr, sizeof(timestr), "%.2ld:%.2ld", duration / 60, duration % 60);
 
1177
                msg = timestr;
 
1178
                break;
 
1179
        }
 
1180
 
 
1181
    statusbar_update_clock(msg);
 
1182
    return TRUE;
 
1183
}
 
1184
 
 
1185
 
 
1186
static void drag_end_cb(GtkWidget * widget UNUSED, GdkDragContext * context UNUSED, gpointer data UNUSED)
 
1187
{
 
1188
    if (active_calltree_tab == history_tab)
 
1189
        return;
 
1190
 
 
1191
    DEBUG("CallTree: Drag end callback");
 
1192
    DEBUG("CallTree: selected_path %s, selected_call_id %s, selected_path_depth %d",
 
1193
          calltree_selected_path, calltree_selected_call_id, calltree_selected_path_depth);
 
1194
    DEBUG("CallTree: dragged path %s, dragged_call_id %s, dragged_path_depth %d",
 
1195
          calltree_dragged_path, calltree_dragged_call_id, calltree_dragged_path_depth);
 
1196
 
 
1197
    GtkTreeModel *model = (GtkTreeModel*) current_calls_tab->store;
 
1198
    GtkTreePath *path = gtk_tree_path_new_from_string(calltree_dragged_path);
 
1199
    GtkTreePath *dpath = gtk_tree_path_new_from_string(calltree_dragged_path);
 
1200
    GtkTreePath *spath = gtk_tree_path_new_from_string(calltree_selected_path);
 
1201
 
 
1202
    GtkTreeIter iter;
 
1203
    GtkTreeIter parent_conference; // conference for which this call is attached
 
1204
 
 
1205
    GValue val;
 
1206
 
 
1207
    // Make sure drag n drop does not imply a dialing call for either selected and dragged call
 
1208
    if (calltree_selected_call && (calltree_selected_type == A_CALL)) {
 
1209
        DEBUG("CallTree: Selected a call");
 
1210
 
 
1211
        if (calltree_selected_call->_state == CALL_STATE_DIALING ||
 
1212
                calltree_selected_call->_state == CALL_STATE_INVALID ||
 
1213
                calltree_selected_call->_state == CALL_STATE_FAILURE ||
 
1214
                calltree_selected_call->_state == CALL_STATE_BUSY ||
 
1215
                calltree_selected_call->_state == CALL_STATE_TRANSFER) {
 
1216
 
 
1217
            DEBUG("CallTree: Selected an invalid call");
 
1218
 
 
1219
            calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1220
            calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
 
1221
 
 
1222
            calltree_dragged_call = NULL;
 
1223
            return;
 
1224
        }
 
1225
 
 
1226
 
 
1227
        if (calltree_dragged_call && (calltree_dragged_type == A_CALL)) {
 
1228
 
 
1229
            DEBUG("CallTree: Dragged on a call");
 
1230
 
 
1231
            if (calltree_dragged_call->_state == CALL_STATE_DIALING ||
 
1232
                    calltree_dragged_call->_state == CALL_STATE_INVALID ||
 
1233
                    calltree_dragged_call->_state == CALL_STATE_FAILURE ||
 
1234
                    calltree_dragged_call->_state == CALL_STATE_BUSY ||
 
1235
                    calltree_dragged_call->_state == CALL_STATE_TRANSFER) {
 
1236
 
 
1237
                DEBUG("CallTree: Dragged on an invalid call");
 
1238
 
 
1239
                calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1240
 
 
1241
                if (calltree_selected_call->_confID) {
 
1242
 
 
1243
                    gtk_tree_path_up(spath);
 
1244
                    gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, spath);
 
1245
 
 
1246
                    calltree_add_call(current_calls_tab, calltree_selected_call, &parent_conference);
 
1247
                } else
 
1248
                    calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
 
1249
 
 
1250
                calltree_dragged_call = NULL;
 
1251
                return;
 
1252
            }
 
1253
        }
 
1254
    }
 
1255
 
 
1256
    // Make sure a conference is only dragged on another conference
 
1257
    if (calltree_selected_conf && (calltree_selected_type == A_CONFERENCE)) {
 
1258
 
 
1259
        DEBUG("CallTree: Selected a conference");
 
1260
 
 
1261
        if (!calltree_dragged_conf && (calltree_dragged_type == A_CALL)) {
 
1262
 
 
1263
            DEBUG("CallTree: Dragged on a call");
 
1264
            conference_obj_t* conf = calltree_selected_conf;
 
1265
 
 
1266
            calltree_remove_conference(current_calls_tab, conf);
 
1267
            calltree_add_conference_to_current_calls(conf);
 
1268
 
 
1269
            calltree_dragged_call = NULL;
 
1270
            return;
 
1271
        }
 
1272
    }
 
1273
 
 
1274
 
 
1275
    if (calltree_selected_path_depth == 1) {
 
1276
        if (calltree_dragged_path_depth == 1) {
 
1277
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
 
1278
                if (gtk_tree_path_compare(dpath, spath) != 0) {
 
1279
                    // dragged a single call on a single call
 
1280
                    if (calltree_selected_call != NULL && calltree_dragged_call != NULL) {
 
1281
                        calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1282
                        calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
 
1283
                        gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
 
1284
                                       0, 0);
 
1285
                    }
 
1286
                }
 
1287
            } else if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CONFERENCE) {
 
1288
 
 
1289
                // dragged a single call on a conference
 
1290
                if (!calltree_selected_call) {
 
1291
                    DEBUG("Error: call dragged on a conference is null");
 
1292
                    return;
 
1293
                }
 
1294
 
 
1295
                g_free(calltree_selected_call->_confID);
 
1296
                calltree_selected_call->_confID = g_strdup(calltree_dragged_call_id);
 
1297
 
 
1298
                g_free(calltree_selected_call->_historyConfID);
 
1299
                calltree_selected_call->_historyConfID = g_strdup(calltree_dragged_call_id);
 
1300
 
 
1301
                sflphone_add_participant(calltree_selected_call_id, calltree_dragged_call_id);
 
1302
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CALL) {
 
1303
 
 
1304
                // dragged a conference on a single call
 
1305
                conference_obj_t* conf = calltree_selected_conf;
 
1306
 
 
1307
                calltree_remove_conference(current_calls_tab, conf);
 
1308
                calltree_add_conference_to_current_calls(conf);
 
1309
 
 
1310
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CONFERENCE) {
 
1311
 
 
1312
                // dragged a conference on a conference
 
1313
                if (gtk_tree_path_compare(dpath, spath) == 0) {
 
1314
 
 
1315
                    if (!current_calls_tab) {
 
1316
                        DEBUG("Error while joining the same conference\n");
 
1317
                        return;
 
1318
                    }
 
1319
 
 
1320
                    DEBUG("Joined the same conference!\n");
 
1321
                    gtk_tree_view_expand_row(GTK_TREE_VIEW(current_calls_tab->view), path, FALSE);
 
1322
                } else {
 
1323
                    if (!calltree_selected_conf)
 
1324
                        DEBUG("Error: selected conference is null while joining 2 conference");
 
1325
 
 
1326
                    if (!calltree_dragged_conf)
 
1327
                        DEBUG("Error: dragged conference is null while joining 2 conference");
 
1328
 
 
1329
                    DEBUG("Joined conferences %s and %s!\n", calltree_dragged_path, calltree_selected_path);
 
1330
                    dbus_join_conference(calltree_selected_conf->_confID, calltree_dragged_conf->_confID);
 
1331
                }
 
1332
            }
 
1333
 
 
1334
            // TODO: dragged a single call on a NULL element (should do nothing)
 
1335
            // TODO: dragged a conference on a NULL element (should do nothing)
 
1336
 
 
1337
        } else {
 
1338
            // dragged_path_depth == 2
 
1339
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
 
1340
                // TODO: dragged a call on a conference call
 
1341
                calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1342
                calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
 
1343
 
 
1344
            } else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CALL) {
 
1345
                // TODO: dragged a conference on a conference call
 
1346
                calltree_remove_conference(current_calls_tab, calltree_selected_conf);
 
1347
                calltree_add_conference_to_current_calls(calltree_selected_conf);
 
1348
            }
 
1349
 
 
1350
            // TODO: dragged a single call on a NULL element
 
1351
            // TODO: dragged a conference on a NULL element
 
1352
        }
 
1353
    } else {
 
1354
 
 
1355
        if (calltree_dragged_path_depth == 1) {
 
1356
            if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
 
1357
 
 
1358
                // dragged a conference call on a call
 
1359
                sflphone_detach_participant(calltree_selected_call_id);
 
1360
 
 
1361
                if (calltree_selected_call && calltree_dragged_call)
 
1362
                    gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
 
1363
                                   0, 0);
 
1364
 
 
1365
            } else if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CONFERENCE) {
 
1366
                // dragged a conference call on a conference
 
1367
                sflphone_detach_participant(calltree_selected_call_id);
 
1368
 
 
1369
                if (calltree_selected_call && calltree_dragged_conf) {
 
1370
                    DEBUG("Adding a participant, since dragged call on a conference");
 
1371
                    sflphone_add_participant(calltree_selected_call_id, calltree_dragged_call_id);
 
1372
                }
 
1373
            } else {
 
1374
                // dragged a conference call on a NULL element
 
1375
                sflphone_detach_participant(calltree_selected_call_id);
 
1376
            }
 
1377
 
 
1378
        } else {
 
1379
            // dragged_path_depth == 2
 
1380
            // dragged a conference call on another conference call (same conference)
 
1381
            // TODO: dragged a conference call on another conference call (different conference)
 
1382
 
 
1383
            gtk_tree_path_up(path);
 
1384
            gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, path);
 
1385
 
 
1386
            gtk_tree_path_up(dpath);
 
1387
            gtk_tree_path_up(spath);
 
1388
 
 
1389
            if (gtk_tree_path_compare(dpath, spath) == 0) {
 
1390
 
 
1391
                DEBUG("Dragged a call in the same conference");
 
1392
                calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1393
                calltree_add_call(current_calls_tab, calltree_selected_call, &parent_conference);
 
1394
                gtk_widget_hide(calltree_menu_items);
 
1395
                gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
 
1396
                               0, 0);
 
1397
            } else {
 
1398
                DEBUG("Dragged a conference call onto another conference call %s, %s", gtk_tree_path_to_string(dpath), gtk_tree_path_to_string(spath));
 
1399
 
 
1400
                conference_obj_t *conf = NULL;
 
1401
                val.g_type = 0;
 
1402
 
 
1403
                if (gtk_tree_model_get_iter(model, &iter, dpath)) {
 
1404
                    gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
 
1405
                    conf = (conference_obj_t*) g_value_get_pointer(&val);
 
1406
                }
 
1407
 
 
1408
                g_value_unset(&val);
 
1409
 
 
1410
                sflphone_detach_participant(calltree_selected_call_id);
 
1411
 
 
1412
                if (conf)
 
1413
                    sflphone_add_participant(calltree_selected_call_id, conf->_confID);
 
1414
                else
 
1415
                    DEBUG("didn't find a conf!");
 
1416
            }
 
1417
 
 
1418
            // TODO: dragged a conference call on another conference call (different conference)
 
1419
            // TODO: dragged a conference call on a NULL element (same conference)
 
1420
            // TODO: dragged a conference call on a NULL element (different conference)
 
1421
        }
 
1422
    }
 
1423
}
 
1424
 
 
1425
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)
 
1426
{
 
1427
    g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
 
1428
}
 
1429
 
 
1430
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)
 
1431
{
 
1432
    GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
 
1433
    GtkTreePath *drop_path;
 
1434
    GtkTreeViewDropPosition position;
 
1435
    GValue val;
 
1436
 
 
1437
    if (active_calltree_tab == history_tab) {
 
1438
        g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
 
1439
        return;
 
1440
    }
 
1441
 
 
1442
    GtkTreeModel* tree_model = gtk_tree_view_get_model(tree_view);
 
1443
 
 
1444
    GtkTreeIter iter;
 
1445
 
 
1446
    val.g_type = 0;
 
1447
    gtk_tree_view_get_drag_dest_row(tree_view, &drop_path, &position);
 
1448
 
 
1449
    if (drop_path) {
 
1450
 
 
1451
        gtk_tree_model_get_iter(tree_model, &iter, drop_path);
 
1452
        gtk_tree_model_get_value(tree_model, &iter, COLUMN_ACCOUNT_PTR, &val);
 
1453
 
 
1454
 
 
1455
        if (gtk_tree_model_iter_has_child(tree_model, &iter)) {
 
1456
            DEBUG("CallTree: Dragging on a conference");
 
1457
            calltree_dragged_type = A_CONFERENCE;
 
1458
            calltree_dragged_call = NULL;
 
1459
        } else {
 
1460
            DEBUG("CallTree: Dragging on a call");
 
1461
            calltree_dragged_type = A_CALL;
 
1462
            calltree_dragged_conf = NULL;
 
1463
        }
 
1464
 
 
1465
        switch (position)  {
 
1466
 
 
1467
            case GTK_TREE_VIEW_DROP_AFTER:
 
1468
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_AFTER");
 
1469
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
 
1470
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
 
1471
                calltree_dragged_call_id = "NULL";
 
1472
                calltree_dragged_call = NULL;
 
1473
                calltree_dragged_conf = NULL;
 
1474
                break;
 
1475
 
 
1476
            case GTK_TREE_VIEW_DROP_INTO_OR_AFTER:
 
1477
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_INTO_OR_AFTER");
 
1478
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
 
1479
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
 
1480
 
 
1481
                if (calltree_dragged_type == A_CALL) {
 
1482
                    calltree_dragged_call_id = ((callable_obj_t*) g_value_get_pointer(&val))->_callID;
 
1483
                    calltree_dragged_call = (callable_obj_t*) g_value_get_pointer(&val);
 
1484
                } else {
 
1485
                    calltree_dragged_call_id = ((conference_obj_t*) g_value_get_pointer(&val))->_confID;
 
1486
                    calltree_dragged_conf = (conference_obj_t*) g_value_get_pointer(&val);
 
1487
                }
 
1488
 
 
1489
                break;
 
1490
 
 
1491
            case GTK_TREE_VIEW_DROP_BEFORE:
 
1492
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_BEFORE");
 
1493
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
 
1494
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
 
1495
                calltree_dragged_call_id = "NULL";
 
1496
                calltree_dragged_call = NULL;
 
1497
                calltree_dragged_conf = NULL;
 
1498
                break;
 
1499
 
 
1500
            case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
 
1501
                DEBUG("CallTree: GTK_TREE_VIEW_DROP_INTO_OR_BEFORE");
 
1502
                calltree_dragged_path = gtk_tree_path_to_string(drop_path);
 
1503
                calltree_dragged_path_depth = gtk_tree_path_get_depth(drop_path);
 
1504
 
 
1505
                if (calltree_dragged_type == A_CALL) {
 
1506
                    calltree_dragged_call_id = ((callable_obj_t*) g_value_get_pointer(&val))->_callID;
 
1507
                    calltree_dragged_call = (callable_obj_t*) g_value_get_pointer(&val);
 
1508
                } else {
 
1509
                    calltree_dragged_call_id = ((conference_obj_t*) g_value_get_pointer(&val))->_confID;
 
1510
                    calltree_dragged_conf = (conference_obj_t*) g_value_get_pointer(&val);
 
1511
                }
 
1512
 
 
1513
                break;
 
1514
 
 
1515
            default:
 
1516
                break;
 
1517
        }
 
1518
    }
 
1519
}
 
1520
 
 
1521
/* Print a string when a menu item is selected */
 
1522
 
 
1523
static void menuitem_response(gchar *string)
 
1524
{
 
1525
    if (g_strcmp0(string, SFL_CREATE_CONFERENCE) == 0)
 
1526
        dbus_join_participant(calltree_selected_call->_callID,
 
1527
                              calltree_dragged_call->_callID);
 
1528
    else if (g_strcmp0(string, SFL_TRANSFER_CALL) == 0) {
 
1529
        DEBUG("Calltree: Transfering call %s, to %s",
 
1530
              calltree_selected_call->_peer_number,
 
1531
              calltree_dragged_call->_peer_number);
 
1532
        dbus_attended_transfer(calltree_selected_call, calltree_dragged_call);
 
1533
        calltree_remove_call(current_calls_tab, calltree_selected_call);
 
1534
    } else
 
1535
        DEBUG("CallTree: Error unknown option selected in menu %s", string);
 
1536
 
 
1537
    // Make sure the create conference opetion will appear next time the menu pops
 
1538
    // The create conference option will hide if tow call from the same conference are draged on each other
 
1539
    gtk_widget_show(calltree_menu_items);
 
1540
 
 
1541
    DEBUG("%s", string);
 
1542
}
 
1543