2
* Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
3
* Author: Patrick Keroulas <patrick.keroulas@savoirfairelinux.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
* Additional permission under GNU GPL version 3 section 7:
21
* If you modify this program, or any covered work, by linking or
22
* combining it with the OpenSSL project's OpenSSL library (or a
23
* modified version of that library), containing parts covered by the
24
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
25
* grants you additional permission to convey the resulting work.
26
* Corresponding Source for a non-source form of such a combination
27
* shall include the source code for the parts of OpenSSL used as well
28
* as that of the covered work.
37
#include <glib/gi18n.h>
39
#include "mainwindow.h"
40
#include "presencewindow.h"
41
#include "account_schema.h"
42
#include "accountlist.h"
44
#include "contacts/calltree.h"
45
#include "callable_obj.h"
47
#include "str_utils.h"
48
#include "statusicon.h"
50
static GtkWidget *presence_window;
51
static GtkTreeView *buddy_list_tree_view;
52
static GtkToggleAction *toggle_action;
53
static GtkWidget *presence_status_combo;
54
static GtkWidget *presence_status_bar;
56
static GtkTreeModel *create_and_fill_presence_tree(void);
57
static GtkTreeView *create_presence_view(void);
58
gboolean selection_changed(GtkTreeSelection *selection);
59
static gboolean presence_view_row_is_buddy(GtkTreeView *treeview, GtkTreePath *path);
60
static buddy_t *presence_view_row_get_buddy(GtkTreeView *treeview, GtkTreePath *path);
61
static gchar *presence_view_row_get_group(GtkTreeView *treeview, GtkTreePath *path);
63
static SFLPhoneClient *presence_client;
64
static buddy_t *tmp_buddy;
65
static gboolean show_all;
69
POPUP_MENU_TYPE_DEFAULT,
70
POPUP_MENU_TYPE_BUDDY,
74
/***************************** tree view **********************************/
90
/* User callback for "get"ing the data out of the row that was DnD'd */
91
void on_buddy_drag_data_get(GtkWidget *widget,
92
G_GNUC_UNUSED GdkDragContext *drag_context,
93
GtkSelectionData *sdata,
94
G_GNUC_UNUSED guint info,
95
G_GNUC_UNUSED guint time_,
96
G_GNUC_UNUSED gpointer user_data)
100
GtkTreeSelection *selector;
102
/* Get the selector widget from the treeview in question */
103
selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
105
/* Get the tree model (list_store) and initialise the iterator */
106
if (!gtk_tree_selection_get_selected(selector, &model, &iter))
109
// TODO : get the path would be cleaner
110
model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
113
memset(&val, 0, sizeof(val));
114
gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
115
b = presence_buddy_list_buddy_get_by_uri(g_value_dup_string(&val));
120
g_debug("Drag src from buddy list b->uri : %s", b->uri);
122
gtk_selection_data_set(sdata,
123
gdk_atom_intern ("struct buddy_t pointer", FALSE),
124
8, // Tell GTK how to pack the data (bytes)
125
(void *)&b, // The actual pointer that we just made
126
sizeof (b)); // The size of the pointer
130
void on_buddy_drag_data_received(GtkWidget *widget,
131
G_GNUC_UNUSED GdkDragContext *drag_context,
132
gint x, gint y, GtkSelectionData *sdata,
133
G_GNUC_UNUSED guint info,
134
G_GNUC_UNUSED guint time_,
135
G_GNUC_UNUSED gpointer user_data)
138
// GOAL: grab the "group" field from the target row (pointed on drop)
139
// and apply it on the dragged src buddy
142
const guchar *data = gtk_selection_data_get_data(sdata);
144
if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path,
148
//gchar * spath = gtk_tree_path_to_string(path);
149
//g_debug("========= (%d,%d) => Path:%s\n", x, y, spath);
150
gchar *gr_target = presence_view_row_get_group(GTK_TREE_VIEW(widget), path);
151
gtk_tree_path_free(path);
152
if (!gr_target) // set group to default
153
gr_target = g_strdup(" ");
155
buddy_t *b_src = NULL;
156
memcpy(&b_src, data, sizeof(b_src));
158
g_warning("Dragged src data not found");
162
buddy_t *b = presence_buddy_list_buddy_get_by_uri(b_src->uri);
163
// the dragged source data refers to an existing buddy.
165
g_debug("Dragged src data found: %s, %s", b_src->alias, b_src->uri);
166
buddy_t *backup = presence_buddy_copy(b);
168
// set the group to the one under the mouse pointer
170
b->group = g_strdup(gr_target);
172
// The src may come from the history/contact window and the alias maybe change.
173
// but if not, don't free b_src because b and b_src points to the same buddy.
174
gchar *alias = g_strdup(b_src->alias);
176
b->alias = g_strdup(alias);
177
presence_buddy_list_edit_buddy(b, backup);
178
presence_buddy_delete(backup);
179
// TODO change rank in tree view
181
// create the new buddy from the dragged source data
182
g_free(b_src->group);
183
b_src->group = g_strdup(gr_target);
184
presence_buddy_list_add_buddy(b_src);
187
update_presence_view();
190
static GtkTreeModel *
191
create_and_fill_presence_tree (void)
193
GtkTreeStore *treestore;
194
GtkTreeIter toplevel, child;
195
GList * buddy_list = g_object_get_data(G_OBJECT(presence_window), "Buddy-List");
198
treestore = gtk_tree_store_new(N_COLUMN, G_TYPE_STRING, // Group + photo
199
G_TYPE_STRING, // Alias
200
G_TYPE_STRING, // Group
201
G_TYPE_STRING, // Status
202
G_TYPE_STRING, // Note
203
G_TYPE_STRING, // URI
204
G_TYPE_STRING, // AccID
205
G_TYPE_STRING);// subscribed
207
// then display buddies with no group (==' ')
208
for (guint j = 0; j < presence_buddy_list_get_size(buddy_list); j++) {
209
buddy = presence_buddy_list_get_nth(j);
210
account_t *acc = account_list_get_by_id(buddy->acc);
214
if ((g_strcmp0(buddy->group, " ") == 0) &&
215
((g_strcmp0(buddy->note,"Not found") != 0) || show_all)) {
216
gtk_tree_store_append(treestore, &toplevel, NULL);
217
gtk_tree_store_set(treestore, &toplevel,
218
COLUMN_OVERVIEW, " ", // to be on the top of the sorted list
219
COLUMN_ALIAS, buddy->alias,
220
COLUMN_GROUP, buddy->group,
221
COLUMN_STATUS, (buddy->status)? GTK_STOCK_YES : GTK_STOCK_NO,
222
COLUMN_NOTE, buddy->note,
223
COLUMN_URI, buddy->uri,
224
COLUMN_ACCOUNTID, buddy->acc,
225
COLUMN_SUBSCRIBED, (buddy->subscribed)? "yes":"no",
230
// then display the groups
231
for (guint i = 0; i < presence_group_list_get_size(); i++) {
232
gchar *group = presence_group_list_get_nth(i);
233
gchar *tmp = g_markup_printf_escaped("<b>%s</b>", group);
235
// display buddy with no group after
236
if (g_strcmp0(group, " ") == 0)
239
gtk_tree_store_append(treestore, &toplevel, NULL);
240
gtk_tree_store_set(treestore, &toplevel,
241
COLUMN_OVERVIEW, tmp,
247
COLUMN_ACCOUNTID, "",
248
COLUMN_SUBSCRIBED, "",
252
for (guint j = 0; j < presence_buddy_list_get_size(buddy_list); j++) {
253
buddy = presence_buddy_list_get_nth(j);
254
account_t *acc = account_list_get_by_id(buddy->acc);
258
if ((g_strcmp0(buddy->group, group) == 0) &&
259
((g_strcmp0(buddy->note,"Not found") != 0) || show_all)) {
260
gtk_tree_store_append(treestore, &child, &toplevel);
261
gtk_tree_store_set(treestore, &child,
263
COLUMN_ALIAS, buddy->alias,
264
COLUMN_GROUP, buddy->group,
265
COLUMN_STATUS, (buddy->status)? GTK_STOCK_YES : GTK_STOCK_NO,
266
COLUMN_NOTE, buddy->note,
267
COLUMN_URI, buddy->uri,
268
COLUMN_ACCOUNTID, buddy->acc,
269
COLUMN_SUBSCRIBED, (buddy->subscribed) ? "yes" : "no",
275
// sort the groups and their buddies by name
276
GtkTreeSortable * sortable = GTK_TREE_SORTABLE(treestore);
277
gtk_tree_sortable_set_sort_column_id(sortable, COLUMN_OVERVIEW, GTK_SORT_ASCENDING);
278
//gtk_tree_sortable_set_sort_column_id(sortable, COLUMN_ALIAS, GTK_SORT_ASCENDING);
280
return GTK_TREE_MODEL(treestore);
283
void presence_view_cell_edited(G_GNUC_UNUSED GtkCellRendererText *renderer,
286
GtkTreeView *treeview)
288
GtkTreeViewColumn *focus_column;
289
gtk_tree_view_get_cursor(treeview, NULL, &focus_column);
290
const gchar * col_name = gtk_tree_view_column_get_title(focus_column);
292
g_debug("Presence: value of col: %s is set to %s", col_name, new_text);
293
GtkTreePath * path = gtk_tree_path_new_from_string(path_str);
295
// a buddy line was edited
296
if (presence_view_row_is_buddy(treeview, path)) {
297
buddy_t *b = presence_view_row_get_buddy(treeview, path);
300
buddy_t *backup = presence_buddy_copy(b);
302
if ((g_strcmp0(col_name, "Alias") == 0) &&
303
(g_strcmp0(new_text, backup->alias) != 0)) {
304
// alias was edited and has changed
306
b->alias = g_strdup(new_text);
307
presence_buddy_list_edit_buddy(b, backup);
308
} else if ((g_strcmp0(col_name, "URI") == 0) &&
309
(g_strcmp0(new_text, backup->uri) != 0) &&
310
(!presence_buddy_list_buddy_get_by_uri(new_text))) {
311
// uri was edited and has changed to a new uri
313
b->uri = g_strdup(new_text);
314
presence_buddy_list_edit_buddy(b, backup);
317
presence_buddy_delete(backup);
319
// a group line was edited
320
gchar *group = presence_view_row_get_group(treeview, path);
321
if (g_strcmp0(new_text, group) != 0)
322
presence_group_list_edit_group(new_text, group);
326
gtk_tree_path_free(path);
327
update_presence_view();
330
void cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn *col,
331
GtkCellRenderer *renderer,
336
guint col_ID = GPOINTER_TO_INT(userdata);
337
// TODO: replace with get the path, get the group and get row_is_buddy
339
memset(&val, 0, sizeof(val));
340
gtk_tree_model_get_value(model, iter, COLUMN_OVERVIEW, &val);
341
gchar* group = g_value_dup_string(&val);
344
// when the mouse pointer on a group field, set the cell editable
345
// not a child buddy row and not an orphan buddy row
346
if (g_strcmp0(group, "") != 0 && g_strcmp0(group, " ") != 0) {
347
g_object_set(renderer, "editable", col_ID == COLUMN_OVERVIEW, NULL);
349
// set the cell editable when the mouse pointer is on a buddy alias or uri
350
if (col_ID == COLUMN_ALIAS)
351
g_object_set(renderer, "editable", TRUE, NULL);
352
else if (col_ID == COLUMN_URI)
353
g_object_set(renderer, "editable", TRUE, NULL);
355
g_object_set(renderer, "editable", FALSE, NULL);
360
void icon_cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn *col,
361
GtkCellRenderer *renderer,
364
G_GNUC_UNUSED gpointer userdata)
367
memset(&val, 0, sizeof(val));
368
gtk_tree_model_get_value(model, iter, COLUMN_STATUS, &val);
369
gchar *icon_name = g_value_dup_string(&val);
371
g_object_set(renderer, "icon-name", icon_name, NULL);
375
update_presence_view()
377
if (!buddy_list_tree_view)
378
return; // presence window not opend
380
GtkTreeModel * model = create_and_fill_presence_tree();
381
gtk_tree_view_set_model(buddy_list_tree_view, model);
382
g_object_unref(model);
383
gtk_tree_view_expand_all(buddy_list_tree_view);
384
#ifdef PRESENCE_DEBUG
385
g_debug("Presence: view updated.");
390
create_presence_view (void)
392
GtkTreeViewColumn *col;
393
GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); // default text render
394
GtkWidget * view_widget = gtk_tree_view_new();
395
GtkTreeView *view = GTK_TREE_VIEW(view_widget);
397
GtkCellRenderer *editable_cell = gtk_cell_renderer_text_new();
398
g_signal_connect(editable_cell, "edited",
399
G_CALLBACK(presence_view_cell_edited), view);
401
col = gtk_tree_view_column_new_with_attributes(" ",
402
editable_cell, "markup", COLUMN_OVERVIEW, NULL);
403
gtk_tree_view_append_column(view, col);
404
// pack with the defaul renderer to avoid GTK warning.
405
gtk_tree_view_column_pack_start(col, renderer, TRUE);
406
gtk_tree_view_column_set_cell_data_func(col, editable_cell,
407
cell_data_func, GINT_TO_POINTER(COLUMN_OVERVIEW), NULL);
409
col = gtk_tree_view_column_new_with_attributes("Alias",
410
editable_cell, "text", COLUMN_ALIAS, NULL);
411
gtk_tree_view_append_column(view, col);
412
gtk_tree_view_column_pack_start(col, renderer, TRUE);
413
gtk_tree_view_column_set_cell_data_func(col, editable_cell,
414
cell_data_func, GINT_TO_POINTER(COLUMN_ALIAS), NULL);
416
GtkCellRenderer * status_icon_renderer = gtk_cell_renderer_pixbuf_new();
417
col = gtk_tree_view_column_new_with_attributes("Status",
418
status_icon_renderer, "text", COLUMN_STATUS, NULL);
419
// FIXME: set type to "text" instead of "pixbuf". this is a work around because
420
// the gtk stock icon is referenced as a string BUT there is still a warning
421
gtk_tree_view_append_column(view, col);
422
gtk_tree_view_column_pack_start(col, renderer, TRUE);
423
gtk_tree_view_column_set_cell_data_func(col, status_icon_renderer,
424
icon_cell_data_func, GINT_TO_POINTER(COLUMN_STATUS), NULL);
426
col = gtk_tree_view_column_new();
427
gtk_tree_view_column_set_title(col, "Note");
428
gtk_tree_view_append_column(view, col);
429
renderer = gtk_cell_renderer_text_new();
430
gtk_tree_view_column_pack_start(col, renderer, TRUE);
431
gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_NOTE);
433
col = gtk_tree_view_column_new_with_attributes("URI",
434
editable_cell, "text", COLUMN_URI, NULL);
435
gtk_tree_view_append_column(view, col);
436
renderer = gtk_cell_renderer_text_new();
437
gtk_tree_view_column_pack_start(col, renderer, TRUE);
438
gtk_tree_view_column_set_cell_data_func(col, editable_cell,
439
cell_data_func, GINT_TO_POINTER(COLUMN_URI), NULL);
440
#ifndef PRESENCE_DEBUG
441
gtk_tree_view_column_set_visible(col, FALSE);
443
gtk_tree_view_column_set_visible(col, TRUE);
445
col = gtk_tree_view_column_new();
446
gtk_tree_view_column_set_title(col, "Suscribed");
447
gtk_tree_view_append_column(view, col);
448
renderer = gtk_cell_renderer_text_new();
449
gtk_tree_view_column_pack_start(col, renderer, TRUE);
450
gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_SUBSCRIBED);
452
col = gtk_tree_view_column_new();
453
gtk_tree_view_column_set_title(col, "AccID");
454
gtk_tree_view_append_column(view, col);
455
renderer = gtk_cell_renderer_text_new();
456
gtk_tree_view_column_pack_start(col, renderer, TRUE);
457
gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_ACCOUNTID);
459
col = gtk_tree_view_column_new();
460
gtk_tree_view_column_set_title(col, "Group");
461
gtk_tree_view_append_column(view, col);
462
renderer = gtk_cell_renderer_text_new();
463
gtk_tree_view_column_pack_start(col, renderer, TRUE);
464
gtk_tree_view_column_add_attribute(col, renderer,"text", COLUMN_GROUP);
470
/***************************** dialog win **********************************/
474
field_error_dialog(const gchar *error_string)
477
msg = g_markup_printf_escaped("Field error %s.",error_string);// TODO: use _()
479
/* Create the widgets */
480
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
481
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
486
gtk_window_set_title(GTK_WINDOW(dialog), _("Field error"));
488
const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
490
gtk_widget_destroy(dialog);
493
return response == GTK_RESPONSE_OK;
497
show_buddy_info_dialog(const gchar *title, buddy_t *b)
499
GtkWidget *dialog = gtk_dialog_new_with_buttons((title),
500
GTK_WINDOW(presence_window),
501
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
508
GtkWidget *grid = gtk_grid_new();
509
gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
510
gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
511
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
512
grid, TRUE, TRUE, 0);
517
// TODO: this is ugly but temporary
518
guint group_index = 0;
519
guint group_count = 0;
520
GtkWidget * combo_group;
521
if (g_strcmp0(title, "Add new buddy") != 0)
523
label = gtk_label_new_with_mnemonic("_Group");
524
gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
525
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
526
combo_group = gtk_combo_box_text_new();
527
// fill combox with existing groups
529
for (guint i = 0; i < presence_group_list_get_size(); i++) {
530
group = presence_group_list_get_nth(i);
531
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_group), group);
534
if (g_strcmp0(group, b->group) == 0)
537
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_group), (gint)group_index);
539
gtk_grid_attach(GTK_GRID(grid), combo_group, 1, row, 1, 1);
543
label = gtk_label_new_with_mnemonic("_Alias");
544
gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
545
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
546
GtkWidget *entry_alias = gtk_entry_new();
547
gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_alias);
548
gtk_entry_set_text(GTK_ENTRY(entry_alias), b->alias);
549
gtk_grid_attach(GTK_GRID(grid), entry_alias, 1, row, 1, 1);
552
label = gtk_label_new_with_mnemonic("_URI");
553
gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
554
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
555
GtkWidget *entry_uri = gtk_entry_new();
556
gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_uri);
557
gtk_entry_set_text(GTK_ENTRY(entry_uri), b->uri);
558
gtk_grid_attach(GTK_GRID(grid), entry_uri, 1, row, 1, 1);
560
gtk_widget_show_all(grid);
561
gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
563
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
565
// update buddy OK was pressed
566
if (response == GTK_RESPONSE_APPLY)
568
// this is ugly but temporary
569
if (g_strcmp0(title, "Add new buddy") != 0)
571
// FIXME: this function doesn't work as expected
572
// if(gtk_combo_box_get_has_entry(GTK_COMBO_BOX(combo_group)))
573
if (group_count > 0) {
574
gchar * gr = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_group));
576
b->group = g_strdup(gr);
582
b->alias = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_alias)));
583
b->uri = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_uri)));
586
if (strlen(b->alias) == 0 || strlen(b->uri) == 0) {
587
field_error_dialog("a field is empty");
588
gtk_widget_destroy(dialog);
589
gboolean res = show_buddy_info_dialog(title, b);// recursive call
593
gtk_widget_destroy(dialog);
597
gtk_widget_destroy(dialog);
603
show_group_info_dialog(const gchar *title, gchar **group)
605
GtkWidget *dialog = gtk_dialog_new_with_buttons((title),
606
GTK_WINDOW(presence_window),
607
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
614
GtkWidget *grid = gtk_grid_new();
615
gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
616
gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
617
gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
618
grid, TRUE, TRUE, 0);
621
GtkWidget *label = gtk_label_new_with_mnemonic("Group");
622
gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
623
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
624
GtkWidget *entry_group = gtk_entry_new();
625
gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry_group);
626
gtk_entry_set_text(GTK_ENTRY(entry_group), *group);
627
gtk_grid_attach(GTK_GRID(grid), entry_group, 1, row, 1, 1);
629
gtk_widget_show_all(grid);
630
gtk_container_set_border_width(GTK_CONTAINER(grid), 10);
632
gint response = gtk_dialog_run(GTK_DIALOG(dialog));
634
// update buddy OK was pressed
635
if (response == GTK_RESPONSE_APPLY)
638
*group = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry_group)));
639
gtk_widget_destroy(dialog);
641
// check that the group name isn't empty or default
642
if ((g_strcmp0(*group, "") == 0) || (g_strcmp0(*group," ") == 0))
648
gtk_widget_destroy(dialog);
654
confirm_buddy_deletion(buddy_t *b)
657
msg = g_markup_printf_escaped("Are you sure want to delete \"%s\"", b->alias);
659
/* Create the widgets */
660
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
661
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
666
gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Remove"), GTK_RESPONSE_OK, NULL);
667
gtk_window_set_title(GTK_WINDOW(dialog), _("Remove buddy"));
669
const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
671
gtk_widget_destroy(dialog);
674
return response == GTK_RESPONSE_OK;
678
confirm_group_deletion(gchar *group)
681
msg = g_markup_printf_escaped("Do you really want to delete the group %s", group);
683
/* Create the widgets */
684
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(presence_window),
685
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
690
gtk_dialog_add_buttons(GTK_DIALOG(dialog), _("Remove"), GTK_RESPONSE_OK, NULL);
691
gtk_window_set_title(GTK_WINDOW(dialog), _("Remove buddy"));
693
const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
695
gtk_widget_destroy(dialog);
698
return response == GTK_RESPONSE_OK;
701
/********************************* Contextual Menus ****************************/
704
presence_view_popup_menu_onCallBuddy(G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
706
buddy_t *b= (buddy_t*)userdata;
707
callable_obj_t * c = sflphone_new_call(presence_client);
709
g_free(c->_peer_number);
710
c->_peer_number = g_strdup(b->uri);
711
g_free(c->_accountID);
712
c->_accountID = g_strdup(b->acc);
714
calltree_update_call(current_calls_tab, c, presence_client, FALSE);
715
sflphone_place_call(c, presence_client);
717
/* Legacy system tray option, requires TopIcons GNOME extension */
718
status_tray_icon_blink(TRUE);
719
if (g_settings_get_boolean(presence_client->settings, "popup-main-window"))
720
popup_main_window(presence_client);
722
if (g_settings_get_boolean(presence_client->settings, "bring-window-to-front"))
723
main_window_bring_to_front(presence_client, c->_time_start);
727
presence_view_popup_menu_onAddBuddy(G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
729
buddy_t *b = presence_buddy_create();
731
// try to grab the selected group
732
if (userdata != NULL) {
733
// might be a group row or a buddy row
734
buddy_t *tmp_b = ((buddy_t*)userdata);
736
b->group = g_strdup(tmp_b->group);
739
account_t *acc = account_list_get_current();
741
acc = account_list_get_nth(1); //0 is IP2IP
743
g_warning("At least one account must exist to able to subscribe.");
748
b->acc = g_strdup(acc->accountID);
750
gchar * uri = g_strconcat("<sip:XXXX@", account_lookup(acc, CONFIG_ACCOUNT_HOSTNAME), ">", NULL);
752
b->uri = g_strdup(uri);
755
if (show_buddy_info_dialog(_("Add new buddy"), b)) {
756
presence_buddy_list_add_buddy(b);
757
update_presence_view();
759
presence_buddy_delete(b);
764
presence_view_popup_menu_onRemoveBuddy (G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
766
buddy_t *b = (buddy_t*) userdata;
767
if (confirm_buddy_deletion(b)) {
768
presence_buddy_list_remove_buddy(b);
769
update_presence_view();
774
presence_view_popup_menu_onAddGroup (G_GNUC_UNUSED GtkWidget *menuitem, G_GNUC_UNUSED gpointer userdata)
776
gchar *group = g_strdup("");
777
if (show_group_info_dialog(_("Add group"), &group)) {
778
presence_group_list_add_group(group);
779
update_presence_view();
785
presence_view_popup_menu_onRemoveGroup (G_GNUC_UNUSED GtkWidget *menuitem, gpointer userdata)
787
gchar *group = (gchar*) userdata;
788
if (confirm_group_deletion(group)) {
789
presence_group_list_remove_group(group);
790
update_presence_view();
795
presence_view_popup_menu(GdkEventButton *event, gpointer userdata, guint type)
797
GtkWidget *menu, *menuitem;
798
menu = gtk_menu_new(); //TODO free ???
799
gchar* gr = g_strdup(((buddy_t*)userdata)->group); // TODO; is it necessary to be free?
801
if (type == POPUP_MENU_TYPE_BUDDY) {
802
menuitem = gtk_menu_item_new_with_label(_("Call"));
803
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
804
g_signal_connect(menuitem, "activate",
805
G_CALLBACK(presence_view_popup_menu_onCallBuddy), userdata);
807
menuitem = gtk_separator_menu_item_new();
808
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
811
menuitem = gtk_menu_item_new_with_label(_("Add buddy"));
812
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
813
g_signal_connect(menuitem, "activate",
814
G_CALLBACK(presence_view_popup_menu_onAddBuddy), userdata);
816
if (type == POPUP_MENU_TYPE_BUDDY) {
817
menuitem = gtk_menu_item_new_with_label(_("Remove buddy"));
818
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
819
g_signal_connect(menuitem, "activate",
820
G_CALLBACK(presence_view_popup_menu_onRemoveBuddy), userdata);
823
menuitem = gtk_separator_menu_item_new(); // TODO:free?
824
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
826
menuitem = gtk_menu_item_new_with_label(_("Add group"));
827
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
828
g_signal_connect(menuitem, "activate",
829
G_CALLBACK(presence_view_popup_menu_onAddGroup), gr);
831
if (type == POPUP_MENU_TYPE_GROUP) {
832
menuitem = gtk_menu_item_new_with_label(_("Remove group"));
833
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
834
g_signal_connect(menuitem, "activate",
835
G_CALLBACK(presence_view_popup_menu_onRemoveGroup), gr);
838
gtk_widget_show_all(menu);
840
/* Note: event can be NULL here when called from view_onPopupMenu;
841
* gdk_event_get_time() accepts a NULL argument */
842
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
843
(event != NULL) ? event->button : 0,
844
gdk_event_get_time((GdkEvent*)event));
848
presence_view_row_is_buddy(GtkTreeView *treeview, GtkTreePath *path)
851
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
852
gboolean res = FALSE;
854
if (gtk_tree_model_get_iter(model, &iter, path)) {
856
memset(&val, 0, sizeof(val));
857
gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
858
gchar *uri = g_value_dup_string(&val);
859
res = strlen(uri) > 0;
867
presence_view_row_get_buddy(GtkTreeView *treeview, GtkTreePath *path)
869
GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
871
gtk_tree_selection_unselect_all(selection);
872
gtk_tree_selection_select_path(selection, path);
875
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
877
if (gtk_tree_model_get_iter(model, &iter, path)) {
879
memset(&val, 0, sizeof(val));
880
gtk_tree_model_get_value(model, &iter, COLUMN_URI, &val);
881
gchar *uri = g_value_dup_string(&val);
882
b = presence_buddy_list_buddy_get_by_uri(uri);
891
presence_view_row_get_group(GtkTreeView *treeview, GtkTreePath *path)
893
GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
895
gtk_tree_selection_unselect_all(selection);
896
gtk_tree_selection_select_path(selection, path);
899
GtkTreeModel *model = gtk_tree_view_get_model(treeview);
901
if (gtk_tree_model_get_iter(model, &iter, path)) {
903
memset(&val, 0, sizeof(val));
904
gtk_tree_model_get_value(model, &iter, COLUMN_GROUP, &val);
905
group = g_value_dup_string(&val);
913
presence_view_onButtonPressed(GtkTreeView *treeview, GdkEventButton *event)
915
/* single click with the right mouse button? */
916
if (event->type == GDK_BUTTON_PRESS && event->button == 3) {
917
GtkTreeSelection * selection = gtk_tree_view_get_selection(treeview);
919
/* Note: gtk_tree_selection_count_selected_rows() does not
920
* exist in gtk+-2.0, only in gtk+ >= v2.2 ! */
921
if (gtk_tree_selection_count_selected_rows(selection) == 1) {
923
/* Get tree path for row that was clicked */
924
if (gtk_tree_view_get_path_at_pos(treeview,
927
&path, NULL, NULL, NULL)) {
928
if (presence_view_row_is_buddy(treeview, path)) {
929
buddy_t *b = presence_view_row_get_buddy(treeview, path);
931
presence_view_popup_menu(event, b, POPUP_MENU_TYPE_BUDDY);
933
// a group row has been selected
934
// use a fake buddy as argument
935
g_free(tmp_buddy->group);
936
tmp_buddy->group = g_strdup(presence_view_row_get_group(treeview, path));
937
if (tmp_buddy->group != NULL)
938
presence_view_popup_menu(event, tmp_buddy, POPUP_MENU_TYPE_GROUP);
940
gtk_tree_path_free(path);
944
//else right click on the back ground
945
// use a fake buddy as argument
946
g_free(tmp_buddy->group);
947
tmp_buddy->group = g_strdup(" "); // default group
948
presence_view_popup_menu(event, tmp_buddy, POPUP_MENU_TYPE_DEFAULT);
957
view_row_activated_cb(GtkTreeView *treeview,
959
G_GNUC_UNUSED GtkTreeViewColumn *col)
961
buddy_t *b = view_get_buddy(treeview, path);
962
if(b) //"NULL if an group was selected instead of a buddy.
963
presence_view_popup_menu_onEditBuddy(NULL, b);
967
/******************************** Status bar *********************************/
970
* This function reads the status combo box and call the DBus presence
971
* publish method if enabled.
972
* @param combo The text combo box associated with the status to be published.
975
presence_statusbar_changed_cb(GtkComboBox *combo)
977
const gchar *status = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo));
978
gboolean b = g_strcmp0(status, PRESENCE_STATUS_ONLINE) == 0;
981
//send publish to every registered accounts with presence and publish enabled
982
for (guint i = 0; i < account_list_get_size(); i++) {
983
account = account_list_get_nth(i);
985
account_replace(account, CONFIG_PRESENCE_STATUS, status);
987
if ((g_strcmp0(account_lookup(account, CONFIG_PRESENCE_PUBLISH_SUPPORTED), "true") == 0) &&
988
(g_strcmp0(account_lookup(account, CONFIG_PRESENCE_ENABLED), "true") == 0) &&
989
(((g_strcmp0(account_lookup(account, CONFIG_ACCOUNT_ENABLE), "true") == 0) ||
990
(account_is_IP2IP(account))))) {
991
dbus_presence_publish(account->accountID, b);
992
g_debug("Presence: publish status of acc:%s => %s", account->accountID, status);
998
update_presence_statusbar()
1000
account_t * account;
1001
gboolean global_publish_enabled = FALSE;
1002
gboolean global_status = FALSE;
1004
if (!presence_status_bar) // presence window not opened
1007
/* Check if one of the registered accounts has Presence enabled */
1008
for (guint i = 0; i < account_list_get_size(); i++) {
1009
account = account_list_get_nth(i);
1012
if (!(account_is_IP2IP(account)) &&
1013
(g_strcmp0(account_lookup(account, CONFIG_PRESENCE_ENABLED), "true") == 0) &&
1014
(account->state == ACCOUNT_STATE_REGISTERED))
1016
if (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_STATUS), PRESENCE_STATUS_ONLINE) == 0)
1017
global_status = TRUE;
1019
if (g_strcmp0(account_lookup(account, CONFIG_PRESENCE_PUBLISH_SUPPORTED), "true") == 0)
1020
global_publish_enabled = TRUE;
1025
g_debug("Presence: statusbar, at least 1 account is registered.");
1026
if (global_publish_enabled)
1027
g_debug("Presence: statusbar, at least 1 account can publish.");
1029
gtk_combo_box_set_active(GTK_COMBO_BOX(presence_status_combo), global_status? 1:0);
1030
gtk_widget_set_sensitive(presence_status_combo, global_publish_enabled? TRUE:FALSE);
1034
create_presence_statusbar()
1036
GtkWidget *bar = gtk_statusbar_new();
1038
GtkWidget *label = gtk_label_new_with_mnemonic(_("Status:"));
1039
gtk_box_pack_start(GTK_BOX(bar), label, TRUE, TRUE, 0);
1041
/* Add presence status combo_box*/
1042
presence_status_combo = gtk_combo_box_text_new();
1043
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(presence_status_combo),
1044
_(PRESENCE_STATUS_OFFLINE));
1045
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(presence_status_combo),
1046
_(PRESENCE_STATUS_ONLINE));
1047
gtk_widget_set_sensitive(presence_status_combo, TRUE);
1048
gtk_box_pack_start(GTK_BOX(bar), presence_status_combo, TRUE, TRUE, 0);
1050
g_signal_connect(G_OBJECT(presence_status_combo), "changed",
1051
G_CALLBACK(presence_statusbar_changed_cb), NULL);
1052
//update_presence_statusbar();
1058
/*************************** Menu bar callback *******************************/
1061
file_on_addbuddy_cb(GtkWidget *w)
1063
presence_view_popup_menu_onAddBuddy(w, NULL);
1067
file_on_addgroup_cb(GtkWidget *w)
1069
presence_view_popup_menu_onAddGroup(w, NULL);
1073
view_allbuddies_toggled_cb(GtkWidget *toggle)
1075
/* Display all the buddies including 'Not found' ones.*/
1076
show_all = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle));
1077
update_presence_view();
1081
view_uri_toggled_cb(GtkWidget *toggle)
1083
// show or hide the uri column
1084
GtkTreeViewColumn *col = gtk_tree_view_get_column(buddy_list_tree_view, (gint)COLUMN_URI);
1086
gtk_tree_view_column_set_visible(col,
1087
gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(toggle)));
1089
update_presence_view();
1093
create_presence_menubar()
1095
GtkWidget *menu_bar = gtk_menu_bar_new();
1096
GtkWidget *item_file = gtk_menu_item_new_with_label("File");
1097
GtkWidget *item_view = gtk_menu_item_new_with_label("View");
1098
gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), item_file);
1099
gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), item_view);
1102
GtkWidget *menu_item;
1104
menu = gtk_menu_new();
1105
menu_item = gtk_menu_item_new_with_label("Add buddy...");
1106
g_signal_connect(G_OBJECT(menu_item), "activate",
1107
G_CALLBACK(file_on_addbuddy_cb), menu_item);
1108
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1109
menu_item = gtk_menu_item_new_with_label("Add group...");
1110
g_signal_connect(G_OBJECT(menu_item), "activate",
1111
G_CALLBACK(file_on_addgroup_cb), menu_item);
1112
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1113
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_file), menu);
1114
menu_item = gtk_separator_menu_item_new();
1115
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1116
menu_item = gtk_menu_item_new_with_label("Close");
1117
g_signal_connect(G_OBJECT(menu_item), "activate",
1118
G_CALLBACK(destroy_presence_window), NULL);
1119
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1122
menu = gtk_menu_new();
1123
menu_item = gtk_check_menu_item_new_with_label("All buddies");
1124
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), FALSE);
1125
g_signal_connect(G_OBJECT(menu_item), "activate",
1126
G_CALLBACK(view_allbuddies_toggled_cb), menu_item);
1127
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1128
menu_item = gtk_check_menu_item_new_with_label("Editable URIs");
1129
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item), FALSE);
1130
g_signal_connect(G_OBJECT(menu_item), "activate",
1131
G_CALLBACK(view_uri_toggled_cb), menu_item);
1132
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1133
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_view), menu);
1138
/******************************** window *********************************/
1140
buddy_subsribe_timer_cb()
1142
presence_buddy_list_init(presence_client);
1143
return TRUE; // this is necessary to keep the timer alive
1147
destroy_presence_window()
1149
g_debug("Destroy presence window ");
1150
buddy_list_tree_view = NULL;
1151
presence_status_bar = NULL;
1152
gtk_widget_destroy(presence_window);
1154
gtk_toggle_action_set_active(toggle_action, FALSE);
1155
//presence_buddy_list_flush();
1159
create_presence_window(SFLPhoneClient *client, GtkToggleAction *action)
1161
static const int PRESENCE_WINDOW_WIDTH = 280;
1162
static const int PRESENCE_WINDOW_HEIGHT = 320;
1164
/* keep track of the widget which opened that window and the SFL client */
1165
toggle_action = action;
1166
presence_client = client;
1168
const gchar * title = _("SFLphone Presence");
1169
g_debug("Create window : %s", title);
1171
/*--------------------- Window -------------------------------------*/
1172
presence_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1173
gtk_container_set_border_width(GTK_CONTAINER(presence_window), 0);
1174
gtk_window_set_title(GTK_WINDOW(presence_window), title);
1175
gtk_window_set_default_size(GTK_WINDOW(presence_window),
1176
PRESENCE_WINDOW_WIDTH, PRESENCE_WINDOW_HEIGHT);
1177
gtk_widget_set_name(presence_window, title);
1179
/*---------------- Instantiate vbox as homogeneous ----------------*/
1180
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1181
gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
1182
gtk_container_add(GTK_CONTAINER(presence_window), vbox);
1184
/*----------------------- Create the menu bar----------------------*/
1185
GtkWidget * menu_bar = create_presence_menubar();
1186
gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 3);
1188
/*---------------------- Create the tree view ---------------------*/
1189
buddy_list_tree_view = create_presence_view();
1190
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(buddy_list_tree_view), TRUE, TRUE, 5);
1191
gtk_tree_view_set_reorderable(buddy_list_tree_view, TRUE);
1192
gtk_tree_view_set_rules_hint(buddy_list_tree_view,TRUE);
1193
gtk_tree_view_set_headers_visible(buddy_list_tree_view, FALSE);
1195
/*---------------------- Drag-n-Drop ------------------------------*/
1196
//DnD, the treeview is one the drag sources to drop buddies into groups
1197
gtk_drag_source_set(GTK_WIDGET(buddy_list_tree_view), GDK_BUTTON1_MASK,
1198
&presence_drag_targets, 1, GDK_ACTION_COPY|GDK_ACTION_MOVE);
1199
g_signal_connect(GTK_WIDGET(buddy_list_tree_view), "drag-data-get",
1200
G_CALLBACK(on_buddy_drag_data_get), NULL);
1201
gtk_drag_dest_set(GTK_WIDGET(buddy_list_tree_view), GTK_DEST_DEFAULT_ALL,
1202
&presence_drag_targets, 1, GDK_ACTION_COPY|GDK_ACTION_MOVE);
1203
g_signal_connect(GTK_WIDGET(buddy_list_tree_view), "drag-data-received",
1204
G_CALLBACK(on_buddy_drag_data_received), NULL);
1206
/*-------------------------- Status bar--------------------------- */
1207
presence_status_bar = create_presence_statusbar();
1208
gtk_box_pack_start(GTK_BOX(vbox), presence_status_bar, FALSE, TRUE, 0);
1210
g_signal_connect(G_OBJECT(buddy_list_tree_view), "button-press-event",
1211
G_CALLBACK(presence_view_onButtonPressed), NULL);
1212
g_signal_connect(G_OBJECT(presence_window), "button-press-event",
1213
G_CALLBACK(presence_view_onButtonPressed), NULL);
1214
g_signal_connect_after(presence_window, "destroy",
1215
G_CALLBACK(destroy_presence_window), NULL);
1217
/*------------------------- Load presence -------------------------*/
1218
presence_buddy_list_init(presence_client);
1219
g_object_set_data(G_OBJECT(presence_window), "Buddy-List",
1220
(gpointer) presence_buddy_list_get());
1221
update_presence_view();
1222
tmp_buddy = presence_buddy_create();
1224
/*------------------ Timer to refresh the subscription------------ */
1225
g_timeout_add_seconds(120, (GSourceFunc) buddy_subsribe_timer_cb, NULL);
1227
/* make sure that everything, window and label, are visible */
1228
gtk_widget_show_all(presence_window);