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>
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.
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.
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.
21
* Additional permission under GNU GPL version 3 section 7:
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.
36
#include <glib/gprintf.h>
38
#include "eel-gconf-extensions.h"
43
#include "conferencelist.h"
44
#include "mainwindow.h"
47
#include "uimanager.h"
50
#include "searchbar.h"
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";
56
static GtkWidget *calltree_sw = NULL;
57
static GtkCellRenderer *calltree_rend = NULL;
58
static GtkTreeViewColumn *calltree_col = NULL;
59
static GtkTreeSelection *calltree_sel = NULL;
61
static GtkWidget *calltree_popupmenu = NULL;
62
static GtkWidget *calltree_menu_items = NULL;
64
static CallType calltree_dragged_type = A_INVALID;
65
static CallType calltree_selected_type = A_INVALID;
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;
72
static gint calltree_dragged_path_depth = -1;
73
static gint calltree_selected_path_depth = -1;
75
static callable_obj_t *calltree_dragged_call = NULL;
76
static callable_obj_t *calltree_selected_call = NULL;
78
static conference_obj_t *calltree_dragged_conf = NULL;
79
static conference_obj_t *calltree_selected_conf = NULL;
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 *);
87
COLUMN_ACCOUNT_PIXBUF = 0,
89
COLUMN_ACCOUNT_SECURITY_PIXBUF,
97
popup_menu(GtkWidget *widget,
98
gpointer user_data UNUSED)
100
show_popup_menu(widget, NULL);
104
/* Call back when the user click on a call in the list */
106
call_selected_cb(GtkTreeSelection *sel, void* data UNUSED)
108
DEBUG("CallTree: Selection callback");
109
GtkTreeModel *model = GTK_TREE_MODEL(active_calltree_tab->store);
113
if (!gtk_tree_selection_get_selected(sel, &model, &iter))
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");
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);
126
if (gtk_tree_model_iter_has_child(model, &iter)) {
127
DEBUG("CallTree: Selected a conference");
128
calltree_selected_type = A_CONFERENCE;
132
gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
134
calltree_selected_conf = (conference_obj_t*) g_value_get_pointer(&val);
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;
143
if (calltree_selected_conf->_im_widget)
144
im_window_show_tab(calltree_selected_conf->_im_widget);
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);
150
DEBUG("CallTree: Selected a call");
151
calltree_selected_type = A_CALL;
155
gtk_tree_model_get_value(model, &iter, COLUMN_ACCOUNT_PTR, &val);
157
calltree_selected_call = g_value_get_pointer(&val);
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;
166
if (calltree_selected_call->_im_widget)
167
im_window_show_tab(calltree_selected_call->_im_widget);
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);
177
/* A row is activated when it is double clicked */
179
row_activated(GtkTreeView *tree_view UNUSED,
180
GtkTreePath *path UNUSED,
181
GtkTreeViewColumn *column UNUSED,
184
DEBUG("CallTree: Double click action");
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);
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);
197
case CALL_STATE_HOLD:
198
dbus_unhold(selectedCall);
200
case CALL_STATE_RINGING:
201
case CALL_STATE_CURRENT:
202
case CALL_STATE_BUSY:
203
case CALL_STATE_FAILURE:
205
case CALL_STATE_DIALING:
206
sflphone_place_call(selectedCall);
209
WARN("Row activated - Should not happen!");
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);
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);
223
} else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
224
DEBUG("CallTree: Selected a conference");
226
if (active_calltree_tab == current_calls_tab) {
227
conference_obj_t * selectedConf = calltab_get_selected_conf(current_calls_tab);
231
switch (selectedConf->_state) {
232
case CONFERENCE_STATE_ACTIVE_DETACHED:
233
case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD:
234
sflphone_add_main_participant(selectedConf);
236
case CONFERENCE_STATE_HOLD:
237
case CONFERENCE_STATE_HOLD_RECORD:
238
dbus_unhold_conference(selectedConf);
240
case CONFERENCE_STATE_ACTIVE_ATTACHED:
241
case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD:
247
WARN("CallTree: Selected a conference in history, should not be possible");
251
/* Catch cursor-activated signal. That is, when the entry is single clicked */
253
row_single_click(GtkTreeView *tree_view UNUSED, void * data UNUSED)
255
gchar * displaySasOnce = NULL;
257
DEBUG("CallTree: Single click action");
259
callable_obj_t *selectedCall = calltab_get_selected_call(active_calltree_tab);
260
conference_obj_t *selectedConf = calltab_get_selected_conf(active_calltree_tab);
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");
267
if (calltab_get_selected_type(active_calltree_tab) == A_CALL) {
268
DEBUG("CallTree: Selected a call");
271
account_t *account_details = account_list_get_by_id(selectedCall->_accountID);
272
DEBUG("AccountID %s", selectedCall->_accountID);
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);
278
GHashTable *properties = sflphone_get_ip2ip_properties();
280
if (properties != NULL) {
281
displaySasOnce = g_hash_table_lookup(properties, ACCOUNT_DISPLAY_SAS_ONCE);
282
DEBUG("IP2IP displaysasonce %s", displaySasOnce);
286
/* Make sure that we are not in the history tab since
287
* nothing is defined for it yet
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;
294
if (g_strcasecmp(displaySasOnce, "true") == 0)
295
selectedCall->_zrtp_confirmed = TRUE;
297
dbus_confirm_sas(selectedCall);
298
calltree_update_call(current_calls_tab, selectedCall);
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);
306
DEBUG("Single click but no action");
311
} else if (calltab_get_selected_type(active_calltree_tab) == A_CONFERENCE) {
312
DEBUG("CallTree: Selected a conference");
314
DEBUG("CallTree: There is actually a selected conf");
316
WARN("CallTree: Warning: Unknown selection type");
320
button_pressed(GtkWidget* widget, GdkEventButton *event, gpointer user_data UNUSED)
322
if (event->button != 3 || event->type != GDK_BUTTON_PRESS)
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);
330
show_popup_menu_contacts(widget, event);
337
calltree_display_call_info(callable_obj_t * c, CallDisplayType display_type, const gchar *const audio_codec)
339
gchar display_number[strlen(c->_peer_number) + 1];
340
strcpy(display_number, c->_peer_number);
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, "@");
346
// Test if we are dialing a new number
347
if (*c->_peer_number && hostname)
348
display_number[hostname - c->_peer_number] = '\0';
351
// Different display depending on type
352
const gchar *name, *details = NULL;
354
if (*c->_peer_name) {
355
name = c->_peer_name;
356
details = display_number;
358
name = display_number;
362
gchar *desc = g_markup_printf_escaped("<b>%s</b> <i>%s</i> ", name, details);
363
gchar *suffix = NULL;
365
switch (display_type) {
366
case DISPLAY_TYPE_CALL:
368
suffix = g_markup_printf_escaped("\n<i>%s (%d)</i>", c->_state_code_description, c->_state_code);
370
case DISPLAY_TYPE_STATE_CODE :
373
suffix = g_markup_printf_escaped("\n<i>%s (%d)</i> <i>%s</i>",
374
c->_state_code_description, c->_state_code,
377
suffix = g_markup_printf_escaped("\n<i>%s</i>", audio_codec);
380
case DISPLAY_TYPE_CALL_TRANSFER:
381
suffix = g_markup_printf_escaped("\n<i>Transfer to:%s</i> ", c->_trsft_to);
383
case DISPLAY_TYPE_SAS:
384
suffix = g_markup_printf_escaped("\n<i>Confirm SAS <b>%s</b> ?</i>", c->_sas);
386
case DISPLAY_TYPE_HISTORY :
391
gchar *msg = g_strconcat(desc, suffix, NULL);
398
calltree_create(calltab_t* tab, int searchbar_type)
400
tab->tree = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
402
// Fix bug #708 (resize)
403
gtk_widget_set_size_request(tab->tree,100,80);
405
gtk_container_set_border_width(GTK_CONTAINER(tab->tree), 0);
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);
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 */
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),
425
gtk_widget_set_can_focus(calltree_sw, TRUE);
426
gtk_widget_grab_focus(calltree_sw);
428
g_signal_connect(G_OBJECT(tab->view), "cursor-changed",
429
G_CALLBACK(row_single_click),
432
// Connect the popup menu
433
g_signal_connect(G_OBJECT(tab->view), "popup-menu",
434
G_CALLBACK(popup_menu),
436
g_signal_connect(G_OBJECT(tab->view), "button-press-event",
437
G_CALLBACK(button_pressed),
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);
444
// source widget drag n drop signals
445
g_signal_connect(G_OBJECT(tab->view), "drag_end", G_CALLBACK(drag_end_cb), NULL);
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);
450
calltree_popupmenu = gtk_menu_new();
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);
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);
468
gtk_widget_grab_focus(GTK_WIDGET(tab->view));
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,
473
gtk_tree_view_append_column(GTK_TREE_VIEW(tab->view), calltree_col);
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,
479
g_object_set(calltree_rend, "wrap-mode", (PangoWrapMode) PANGO_WRAP_WORD_CHAR, NULL);
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);
491
calltree_rend = gtk_cell_renderer_pixbuf_new();
492
calltree_col = gtk_tree_view_column_new_with_attributes("Icon",
494
"pixbuf", COLUMN_ACCOUNT_SECURITY_PIXBUF,
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);
500
g_object_unref(G_OBJECT(tab->store));
501
gtk_container_add(GTK_CONTAINER(calltree_sw), tab->view);
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),
508
gtk_box_pack_start(GTK_BOX(tab->tree), calltree_sw, TRUE, TRUE, 0);
510
// search bar if tab is either "history" or "addressbook"
511
if (searchbar_type) {
512
calltab_create_searchbar(tab);
514
if (tab->searchbar != NULL)
515
gtk_box_pack_start(GTK_BOX(tab->tree), tab->searchbar, FALSE, TRUE, 0);
518
gtk_widget_show(tab->tree);
523
calltree_remove_call_recursive(calltab_t* tab, callable_obj_t * callable, GtkTreeIter *parent)
525
GtkTreeStore *store = tab->store;
526
GtkTreeModel *model = GTK_TREE_MODEL(store);
529
ERROR("CallTree: Error: Not a valid call");
531
DEBUG("CallTree: Remove call %s", callable->_callID);
533
int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
535
for (int i = 0; i < nbChild; i++) {
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);
542
GValue val = { .g_type = 0 };
543
gtk_tree_model_get_value(model, &child, COLUMN_ACCOUNT_PTR, &val);
545
callable_obj_t * iterCall = g_value_get_pointer(&val);
548
if (iterCall == callable)
549
gtk_tree_store_remove(store, &child);
553
if (calltab_get_selected_call(tab) == callable)
554
calltab_select_call(tab, NULL);
558
statusbar_update_clock("");
562
calltree_remove_call(calltab_t* tab, callable_obj_t * c)
564
calltree_remove_call_recursive(tab, c, NULL);
568
calltree_update_call_recursive(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
570
GdkPixbuf *pixbuf = NULL;
571
GdkPixbuf *pixbuf_security = NULL;
574
GtkTreeStore* store = tab->store;
576
gchar* srtp_enabled = NULL;
577
gboolean display_sas = TRUE;
578
account_t* account_details=NULL;
580
int nbChild = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), parent);
583
account_details = account_list_get_by_id(c->_accountID);
585
if (account_details != NULL) {
586
srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
588
if (g_strcasecmp(g_hash_table_lookup(account_details->properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
591
GHashTable * properties = sflphone_get_ip2ip_properties();
593
if (properties != NULL) {
594
srtp_enabled = g_hash_table_lookup(properties, ACCOUNT_SRTP_ENABLED);
596
if (g_strcasecmp(g_hash_table_lookup(properties, ACCOUNT_ZRTP_DISPLAY_SAS),"false") == 0)
602
for (gint i = 0; i < nbChild; i++) {
604
if (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, parent, i)) {
606
if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter))
607
calltree_update_call_recursive(tab, c, &iter);
610
gtk_tree_model_get_value(GTK_TREE_MODEL(store), &iter, COLUMN_ACCOUNT_PTR, &val);
612
callable_obj_t * iterCall = (callable_obj_t*) g_value_get_pointer(&val);
619
gchar * description = NULL;
620
gchar * audio_codec = call_get_audio_codec(c);
622
if (c->_state == CALL_STATE_TRANSFER)
623
description = calltree_display_call_info(c, DISPLAY_TYPE_CALL_TRANSFER, "");
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, "");
628
description = calltree_display_call_info(c, DISPLAY_TYPE_STATE_CODE, audio_codec);
633
if (tab == current_calls_tab) {
634
DEBUG("Receiving in state %d", c->_state);
637
case CALL_STATE_HOLD:
638
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/hold.svg", NULL);
640
case CALL_STATE_INCOMING:
641
case CALL_STATE_RINGING:
642
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
644
case CALL_STATE_CURRENT:
645
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
647
case CALL_STATE_DIALING:
648
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
650
case CALL_STATE_FAILURE:
651
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/fail.svg", NULL);
653
case CALL_STATE_BUSY:
654
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/busy.svg", NULL);
656
case CALL_STATE_TRANSFER:
657
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/transfer.svg", NULL);
659
case CALL_STATE_RECORD:
660
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
663
WARN("Update calltree - Should not happen!");
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);
670
case SRTP_STATE_ZRTP_SAS_UNCONFIRMED:
671
pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_unconfirmed.svg", NULL);
673
DEBUG("SAS is ready with value %s", c->_sas);
675
case SRTP_STATE_ZRTP_SAS_CONFIRMED:
676
pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_confirmed.svg", NULL);
678
case SRTP_STATE_ZRTP_SAS_SIGNED:
679
pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_certified.svg", NULL);
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);
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);
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) {
696
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/incoming.svg", NULL);
699
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/outgoing.svg", NULL);
702
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/missed.svg", NULL);
705
WARN("History - Should not happen!");
707
} else // parent is not NULL this is a conference participant
708
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/current.svg", NULL);
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);
718
gchar *old_description = description;
719
description = g_strconcat(old_description , full_duration, NULL);
720
g_free(full_duration);
721
g_free(old_description);
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,
734
g_object_unref(G_OBJECT(pixbuf));
735
if (pixbuf_security != NULL)
736
g_object_unref(G_OBJECT(pixbuf_security));
743
void calltree_update_call(calltab_t* tab, callable_obj_t * c)
745
return calltree_update_call_recursive(tab, c, NULL);
748
void calltree_add_call(calltab_t* tab, callable_obj_t * c, GtkTreeIter *parent)
750
g_assert(tab != history_tab);
752
account_t* account_details = NULL;
754
GdkPixbuf *pixbuf = NULL;
755
GdkPixbuf *pixbuf_security = NULL;
757
gchar* key_exchange = NULL;
758
gchar* srtp_enabled = NULL;
760
// New call in the list
762
gchar *description = calltree_display_call_info(c, DISPLAY_TYPE_CALL, "");
764
gtk_tree_store_prepend(tab->store, &iter, parent);
767
account_details = account_list_get_by_id(c->_accountID);
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);
775
DEBUG("Added call key exchange is %s", key_exchange);
777
if (tab == current_calls_tab) {
779
case CALL_STATE_INCOMING:
780
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
782
case CALL_STATE_DIALING:
783
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/dial.svg", NULL);
785
case CALL_STATE_RINGING:
786
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/ring.svg", NULL);
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);
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);
796
case CALL_STATE_RECORD:
797
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/icon_rec.svg", NULL);
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);
804
WARN("Update calltree add - Should not happen!");
807
if (srtp_enabled && g_strcasecmp(srtp_enabled, "true") == 0)
808
pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/secure_off.svg", NULL);
810
} else if (tab == contacts_tab)
811
pixbuf = c->_contact_thumbnail;
813
WARN("CallTree: This widget doesn't exist - This is a bug in the application.");
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);
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;
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,
838
g_object_unref(G_OBJECT(pixbuf));
840
if (pixbuf_security != NULL)
841
g_object_unref(G_OBJECT(pixbuf));
843
gtk_tree_view_set_model(GTK_TREE_VIEW(history_tab->view), GTK_TREE_MODEL(history_tab->store));
845
gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(tab->view)), &iter);
848
void calltree_add_history_entry(callable_obj_t *c)
850
if (!eel_gconf_get_integer(HISTORY_ENABLED))
853
// New call in the list
854
gchar * description = calltree_display_call_info(c, DISPLAY_TYPE_HISTORY, "");
857
gtk_tree_store_prepend(history_tab->store, &iter, NULL);
859
GdkPixbuf *pixbuf = NULL;
861
switch (c->_history_state) {
863
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/incoming.svg", NULL);
866
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/outgoing.svg", NULL);
869
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/missed.svg", NULL);
872
WARN("History - Should not happen!");
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);
880
gchar * full_description = g_strconcat(description, full_duration, NULL);
882
g_free(full_duration);
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);
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,
898
g_free(full_description);
901
g_object_unref(G_OBJECT(pixbuf));
903
gtk_tree_view_set_model(GTK_TREE_VIEW(history_tab->view), GTK_TREE_MODEL(history_tab->store));
909
void calltree_add_conference_to_current_calls(conference_obj_t* conf)
911
account_t *account_details = NULL;
914
ERROR("Calltree: Error: Conference is null");
918
DEBUG("Calltree: Add conference %s", conf->_confID);
921
gtk_tree_store_append(current_calls_tab->store, &iter, NULL);
923
GdkPixbuf *pixbuf = NULL;
925
switch (conf->_state) {
926
case CONFERENCE_STATE_ACTIVE_ATTACHED:
927
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersAttached.svg", NULL);
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);
934
case CONFERENCE_STATE_ACTIVE_ATTACHED_RECORD:
935
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersAttachedRec.svg", NULL);
937
case CONFERENCE_STATE_ACTIVE_DETACHED_RECORD:
938
pixbuf = gdk_pixbuf_new_from_file(ICONS_DIR "/usersDetachedRec.svg", NULL);
941
WARN("Update conference add - Should not happen!");
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);
952
DEBUG("Error no pixbuff for conference from %s", ICONS_DIR);
954
GdkPixbuf *pixbuf_security = NULL;
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;
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;
964
if (conf->participant_list) {
965
DEBUG("Calltree: Determine if at least one participant uses SRTP");
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);
972
ERROR("Calltree: Error: Could not find call %s in call list", call_id);
974
account_details = account_list_get_by_id(call->_accountID);
975
gchar *srtp_enabled = "";
977
if (!account_details)
978
ERROR("Calltree: Error: Could not find account %s in account list", call->_accountID);
980
srtp_enabled = g_hash_table_lookup(account_details->properties, ACCOUNT_SRTP_ENABLED);
982
if (g_strcasecmp(srtp_enabled, "true") == 0) {
983
DEBUG("Calltree: SRTP enabled for participant %s", call_id);
984
conf->_conf_srtp_enabled = TRUE;
987
DEBUG("Calltree: SRTP is not enabled for participant %s", call_id);
991
DEBUG("Calltree: Determine if all conference participants are secured");
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);
999
if (call->_srtp_state == SRTP_STATE_UNLOCKED) {
1000
DEBUG("Calltree: Participant %s is not secured", call_id);
1001
conf->_conference_secured = FALSE;
1004
DEBUG("Calltree: Participant %s is secured", call_id);
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);
1015
DEBUG("Calltree: Conference is not secured");
1016
pixbuf_security = gdk_pixbuf_new_from_file(ICONS_DIR "/lock_off.svg", NULL);
1020
DEBUG("Calltree: Add conference to tree store");
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,
1029
g_free(description);
1032
g_object_unref(pixbuf);
1034
if (pixbuf_security)
1035
g_object_unref(pixbuf_security);
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);
1041
calltree_remove_call(current_calls_tab, call);
1042
calltree_add_call(current_calls_tab, call, &iter);
1045
gtk_tree_view_set_model(GTK_TREE_VIEW(current_calls_tab->view),
1046
GTK_TREE_MODEL(current_calls_tab->store));
1048
GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(current_calls_tab->store), &iter);
1050
gtk_tree_view_expand_row(GTK_TREE_VIEW(current_calls_tab->view), path, FALSE);
1056
void calltree_remove_conference_recursive(calltab_t* tab, const conference_obj_t* conf, GtkTreeIter *parent)
1058
GtkTreeModel *model = GTK_TREE_MODEL(tab->store);
1059
int nbChildren = gtk_tree_model_iter_n_children(model, parent);
1061
for (int i = 0; i < nbChildren; i++) {
1062
GtkTreeIter iter_parent;
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)) {
1067
if (gtk_tree_model_iter_has_child(model, &iter_parent))
1068
calltree_remove_conference_recursive(tab, conf, &iter_parent);
1072
gtk_tree_model_get_value(model, &iter_parent, COLUMN_ACCOUNT_PTR, &confval);
1074
conference_obj_t *tempconf = (conference_obj_t*) g_value_get_pointer(&confval);
1075
g_value_unset(&confval);
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);
1082
for (int j = 0; j < nbParticipants; j++) {
1083
GtkTreeIter iter_child;
1085
if (gtk_tree_model_iter_nth_child(model, &iter_child, &iter_parent, j)) {
1088
gtk_tree_model_get_value(model, &iter_child, COLUMN_ACCOUNT_PTR, &callval);
1090
callable_obj_t *call = g_value_get_pointer(&callval);
1091
g_value_unset(&callval);
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);
1099
DEBUG("CallTree: Remove conference %s", conf->_confID);
1100
gtk_tree_store_remove(tab->store, &iter_parent);
1105
if (calltab_get_selected_conf(tab) == conf)
1106
calltab_select_conf(tab, NULL);
1111
void calltree_remove_conference(calltab_t* tab, const conference_obj_t* conf)
1113
DEBUG("CallTree: Remove conference %s", conf->_confID);
1114
calltree_remove_conference_recursive(tab, conf, NULL);
1115
DEBUG("CallTree: Finished Removing conference");
1118
void calltree_display(calltab_t *tab)
1120
/* If we already are displaying the specified calltree */
1121
if (active_calltree_tab == tab)
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);
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);
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);
1138
gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(contactButton_), TRUE);
1139
set_focus_on_addressbook_searchbar();
1141
ERROR("CallTree: Error: Not a valid call tab (%d, %s)", __LINE__, __FILE__);
1143
gtk_widget_hide(active_calltree_tab->tree);
1144
active_calltree_tab = tab;
1145
gtk_widget_show(active_calltree_tab->tree);
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");
1154
gboolean calltree_update_clock(gpointer data UNUSED)
1157
const gchar *msg = "";
1159
callable_obj_t *c = calltab_get_selected_call(current_calls_tab);
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:
1171
duration = difftime(time(NULL), c->_time_start);
1176
g_snprintf(timestr, sizeof(timestr), "%.2ld:%.2ld", duration / 60, duration % 60);
1181
statusbar_update_clock(msg);
1186
static void drag_end_cb(GtkWidget * widget UNUSED, GdkDragContext * context UNUSED, gpointer data UNUSED)
1188
if (active_calltree_tab == history_tab)
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);
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);
1203
GtkTreeIter parent_conference; // conference for which this call is attached
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");
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) {
1217
DEBUG("CallTree: Selected an invalid call");
1219
calltree_remove_call(current_calls_tab, calltree_selected_call);
1220
calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1222
calltree_dragged_call = NULL;
1227
if (calltree_dragged_call && (calltree_dragged_type == A_CALL)) {
1229
DEBUG("CallTree: Dragged on a call");
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) {
1237
DEBUG("CallTree: Dragged on an invalid call");
1239
calltree_remove_call(current_calls_tab, calltree_selected_call);
1241
if (calltree_selected_call->_confID) {
1243
gtk_tree_path_up(spath);
1244
gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, spath);
1246
calltree_add_call(current_calls_tab, calltree_selected_call, &parent_conference);
1248
calltree_add_call(current_calls_tab, calltree_selected_call, NULL);
1250
calltree_dragged_call = NULL;
1256
// Make sure a conference is only dragged on another conference
1257
if (calltree_selected_conf && (calltree_selected_type == A_CONFERENCE)) {
1259
DEBUG("CallTree: Selected a conference");
1261
if (!calltree_dragged_conf && (calltree_dragged_type == A_CALL)) {
1263
DEBUG("CallTree: Dragged on a call");
1264
conference_obj_t* conf = calltree_selected_conf;
1266
calltree_remove_conference(current_calls_tab, conf);
1267
calltree_add_conference_to_current_calls(conf);
1269
calltree_dragged_call = NULL;
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,
1287
} else if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CONFERENCE) {
1289
// dragged a single call on a conference
1290
if (!calltree_selected_call) {
1291
DEBUG("Error: call dragged on a conference is null");
1295
g_free(calltree_selected_call->_confID);
1296
calltree_selected_call->_confID = g_strdup(calltree_dragged_call_id);
1298
g_free(calltree_selected_call->_historyConfID);
1299
calltree_selected_call->_historyConfID = g_strdup(calltree_dragged_call_id);
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) {
1304
// dragged a conference on a single call
1305
conference_obj_t* conf = calltree_selected_conf;
1307
calltree_remove_conference(current_calls_tab, conf);
1308
calltree_add_conference_to_current_calls(conf);
1310
} else if (calltree_selected_type == A_CONFERENCE && calltree_dragged_type == A_CONFERENCE) {
1312
// dragged a conference on a conference
1313
if (gtk_tree_path_compare(dpath, spath) == 0) {
1315
if (!current_calls_tab) {
1316
DEBUG("Error while joining the same conference\n");
1320
DEBUG("Joined the same conference!\n");
1321
gtk_tree_view_expand_row(GTK_TREE_VIEW(current_calls_tab->view), path, FALSE);
1323
if (!calltree_selected_conf)
1324
DEBUG("Error: selected conference is null while joining 2 conference");
1326
if (!calltree_dragged_conf)
1327
DEBUG("Error: dragged conference is null while joining 2 conference");
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);
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)
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);
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);
1350
// TODO: dragged a single call on a NULL element
1351
// TODO: dragged a conference on a NULL element
1355
if (calltree_dragged_path_depth == 1) {
1356
if (calltree_selected_type == A_CALL && calltree_dragged_type == A_CALL) {
1358
// dragged a conference call on a call
1359
sflphone_detach_participant(calltree_selected_call_id);
1361
if (calltree_selected_call && calltree_dragged_call)
1362
gtk_menu_popup(GTK_MENU(calltree_popupmenu), NULL, NULL, NULL, NULL,
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);
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);
1374
// dragged a conference call on a NULL element
1375
sflphone_detach_participant(calltree_selected_call_id);
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)
1383
gtk_tree_path_up(path);
1384
gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &parent_conference, path);
1386
gtk_tree_path_up(dpath);
1387
gtk_tree_path_up(spath);
1389
if (gtk_tree_path_compare(dpath, spath) == 0) {
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,
1398
DEBUG("Dragged a conference call onto another conference call %s, %s", gtk_tree_path_to_string(dpath), gtk_tree_path_to_string(spath));
1400
conference_obj_t *conf = NULL;
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);
1408
g_value_unset(&val);
1410
sflphone_detach_participant(calltree_selected_call_id);
1413
sflphone_add_participant(calltree_selected_call_id, conf->_confID);
1415
DEBUG("didn't find a conf!");
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)
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)
1427
g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
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)
1432
GtkTreeView *tree_view = GTK_TREE_VIEW(widget);
1433
GtkTreePath *drop_path;
1434
GtkTreeViewDropPosition position;
1437
if (active_calltree_tab == history_tab) {
1438
g_signal_stop_emission_by_name(G_OBJECT(widget), "drag_data_received");
1442
GtkTreeModel* tree_model = gtk_tree_view_get_model(tree_view);
1447
gtk_tree_view_get_drag_dest_row(tree_view, &drop_path, &position);
1451
gtk_tree_model_get_iter(tree_model, &iter, drop_path);
1452
gtk_tree_model_get_value(tree_model, &iter, COLUMN_ACCOUNT_PTR, &val);
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;
1460
DEBUG("CallTree: Dragging on a call");
1461
calltree_dragged_type = A_CALL;
1462
calltree_dragged_conf = NULL;
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;
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);
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);
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);
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;
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);
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);
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);
1521
/* Print a string when a menu item is selected */
1523
static void menuitem_response(gchar *string)
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);
1535
DEBUG("CallTree: Error unknown option selected in menu %s", string);
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);
1541
DEBUG("%s", string);