2
* Copyright (C) 2012 Savoir-Faire Linux Inc.
3
* Author: Emmanuel Lepage Vallee <emmanuel.lepage@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.
30
#include "message_tab.h"
32
#include "../dbus/dbus.h"
34
#include "../mainwindow.h"
37
static GtkWidget *tab_box = NULL ;
38
static GHashTable *tabs = NULL ;
39
static GtkPaned *paned = NULL ;
40
static int vpanes_s = -1 ;
41
static int skip_height = -3 ;
44
static GtkTextIter* start_link = NULL;
45
static GtkTextIter* end_link = NULL;
47
/////////////////////HELPERS/////////////////////////
50
/*I really don't know why we need this, but without, it doesn't work*/
52
force_lookup(const gchar *id)
54
GList *list = g_hash_table_get_keys(tabs);
55
for (guint k=0;k<g_list_length(list);k++) {
56
if (!strcmp(id,(gchar*)g_list_nth(list,k)->data)) {
57
return g_hash_table_lookup(tabs,(const gchar*)g_list_nth(list,k)->data);
64
disable_conference_calls(conference_obj_t *call)
67
guint size = g_slist_length(call->participant_list);
68
for (guint i = 0; i < size;i++) {
69
const gchar* id = (gchar*)g_slist_nth(call->participant_list,i)->data;
70
message_tab *tab = force_lookup(id);
72
gtk_widget_hide(tab->entry);
79
/////////////////////GETTERS/////////////////////////
81
GtkWidget *get_tab_box()
84
tab_box = gtk_notebook_new();
85
gtk_notebook_set_scrollable(GTK_NOTEBOOK(tab_box),TRUE);
92
/////////////////////SETTERS/////////////////////////
97
GtkWidget *box = get_tab_box();
98
if (gtk_widget_get_visible(box))
101
gtk_widget_hide(box);
108
gtk_widget_show(get_tab_box());
110
gtk_paned_set_position(GTK_PANED(paned),vpanes_s);
114
set_message_tab_height(GtkPaned* _paned, int height)
116
if ( skip_height >=0 || skip_height == -3 ) {
124
append_message(message_tab* self, const gchar* name, const gchar* message)
126
GtkTextIter current_end,new_end;
127
gtk_text_buffer_get_end_iter( self->buffer, ¤t_end );
128
gtk_text_buffer_insert ( self->buffer, ¤t_end, name, -1 );
129
gtk_text_buffer_insert ( self->buffer, ¤t_end, ": ", -1 );
131
gtk_text_buffer_get_end_iter(self->buffer, ¤t_end);
132
for (unsigned int i=0;i<strlen(name)+2;i++){
133
if (!gtk_text_iter_backward_char(¤t_end))
137
gtk_text_buffer_get_end_iter(self->buffer, &new_end);
138
gtk_text_buffer_apply_tag_by_name(self->buffer, "b", ¤t_end, &new_end);
140
gtk_text_buffer_insert ( self->buffer, &new_end, message, -1 );
141
gtk_text_buffer_insert ( self->buffer, &new_end, "\n" , -1 );
142
gtk_text_buffer_get_end_iter( self->buffer, &new_end );
143
gtk_text_view_scroll_to_iter( self->view , &new_end,FALSE,0,0,FALSE );
149
//////////////////////SLOTS//////////////////////////
152
on_enter(GtkEntry *entry, gpointer user_data)
156
message_tab *tab = (message_tab*)user_data;
157
append_message(tab,(gchar*)"Me",gtk_entry_get_text(entry));
159
dbus_send_text_message(tab->call->_callID,gtk_entry_get_text(entry));
161
dbus_send_text_message(tab->conf->_confID,gtk_entry_get_text(entry));
162
gtk_entry_set_text(entry,"");
166
on_close(G_GNUC_UNUSED GtkWidget *button, gpointer data)
168
message_tab *tab = (message_tab*)data;
169
gtk_widget_destroy(tab->widget);
171
g_hash_table_remove(tabs,tab->call->_callID);
173
g_hash_table_remove(tabs,tab->conf->_confID);
177
on_focus_in(G_GNUC_UNUSED GtkEntry *entry, G_GNUC_UNUSED gpointer user_data)
179
main_window_pause_keygrabber(TRUE);
183
on_focus_out(G_GNUC_UNUSED GtkEntry *entry, G_GNUC_UNUSED gpointer user_data)
185
main_window_pause_keygrabber(FALSE);
189
on_clicked(GtkTextBuffer *textbuffer, G_GNUC_UNUSED GtkTextIter *location, G_GNUC_UNUSED GtkTextMark *mark, SFLPhoneClient *client)
191
if (start_link && end_link && gtk_text_iter_compare(start_link,location) <= 0 && gtk_text_iter_compare(location,end_link) <= 0) {
192
gchar* text = gtk_text_buffer_get_text(textbuffer,start_link,end_link,FALSE);
196
gchar* url_command = g_settings_get_string(client->settings, "messaging-url-command");
197
if (!url_command || !strlen(url_command))
198
url_command = g_strdup("xdg-open");
200
const gchar* argv[] = {url_command, text, NULL};
201
g_spawn_async(NULL,(gchar**)argv,NULL,G_SPAWN_SEARCH_PATH|G_SPAWN_STDOUT_TO_DEV_NULL|G_SPAWN_STDERR_TO_DEV_NULL,NULL,NULL,NULL,NULL);
202
gtk_text_buffer_remove_all_tags(textbuffer,start_link,end_link );
211
on_cursor_motion(G_GNUC_UNUSED GtkTextView *view, GdkEvent *event, gpointer data)
213
/* Convert mouse position into text iterators*/
215
GtkTextIter cursor_pos,end_iter,end_match,start_match,end_match_b,start_match_b,start_real,end_real;
216
gtk_text_buffer_get_end_iter (((message_tab*) data)->buffer, &end_iter );
217
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW(view),GTK_TEXT_WINDOW_TEXT,((GdkEventMotion*)event)->x,((GdkEventMotion*)event)->y,&x,&y);
218
gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(view),&cursor_pos,x,y );
219
gboolean ret = gtk_text_iter_backward_search(&cursor_pos," ",GTK_TEXT_SEARCH_TEXT_ONLY | GTK_TEXT_SEARCH_VISIBLE_ONLY,&start_match_b,&end_match_b,NULL);
221
if (gtk_text_iter_forward_search(&cursor_pos," ",GTK_TEXT_SEARCH_TEXT_ONLY | GTK_TEXT_SEARCH_VISIBLE_ONLY,&start_match,&end_match,NULL)
222
&& gtk_text_iter_get_line(&end_match) == gtk_text_iter_get_line(&cursor_pos)) {
223
start_real = end_match_b;
224
end_real = start_match;
227
gtk_text_iter_forward_visible_line(&cursor_pos);
228
start_real = end_match_b;
229
end_real = cursor_pos ;
232
/*Get the word under cursor*/
233
gchar* text = gtk_text_buffer_get_text(((message_tab*) data)->buffer,&start_real,&end_real,FALSE);
236
GError *error = NULL;
237
gchar *pattern_string = "^[a-z]*\\://[a-zA-Z0-9\\-\\.]+\\.[a-zA-Z]{2,3}(/\\S*)?$";
238
GRegex *regex = g_regex_new( pattern_string, 0, 0, &error );
239
GMatchInfo *match_info = NULL;
240
GdkWindow *win = gtk_text_view_get_window(GTK_TEXT_VIEW(view),GTK_TEXT_WINDOW_TEXT);
242
g_regex_match( regex, text, 0, &match_info );
243
if (g_match_info_matches( match_info )) {
245
while( g_match_info_matches( match_info ) ) {
246
g_match_info_next( match_info, &error );
247
if (gtk_text_iter_get_buffer(&start_real) == ((message_tab*) data)->buffer && gtk_text_iter_get_buffer(&end_real) == ((message_tab*) data)->buffer) {
248
gtk_text_buffer_remove_all_tags(((message_tab*) data)->buffer,&start_real, &end_real);
249
gtk_text_buffer_apply_tag_by_name(((message_tab*) data)->buffer, "link", &start_real, &end_real);
252
GdkCursor *cur = gdk_cursor_new(GDK_HAND2);
253
start_link = gtk_text_iter_copy(&start_real);
254
end_link = gtk_text_iter_copy(&end_real);
255
gdk_window_set_cursor(win,cur);
258
/*Is not a link, cleaning previous link*/
259
GdkCursor *cur = gdk_cursor_new(GDK_XTERM);
260
gdk_window_set_cursor(win,cur);
261
if (start_link && end_link && gtk_text_iter_get_buffer(start_link) == ((message_tab*) data)->buffer && gtk_text_iter_get_buffer(end_link) == ((message_tab*) data)->buffer) {
262
gtk_text_buffer_remove_all_tags(((message_tab*) data)->buffer,start_link,end_link );
263
/*g_free(start_link);
273
/////////////////////MUTATORS////////////////////////
276
disable_messaging_tab(const gchar * id)
278
message_tab *tab = NULL;
280
tab = g_hash_table_lookup(tabs, id);
282
gtk_widget_hide(tab->entry);
283
if (!g_list_length(gtk_container_get_children(GTK_CONTAINER(get_tab_box()))))
284
gtk_widget_hide(get_tab_box());
288
create_messaging_tab_common(const gchar* call_id, const gchar *label, SFLPhoneClient *client)
291
/* Do not create a new tab if it already exist */
292
message_tab *tab = NULL;
294
tab = g_hash_table_lookup(tabs,call_id);
299
message_tab *self = g_new0(message_tab, 1);
301
/* Create the main layout */
302
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
303
GtkTextBuffer *text_buffer = gtk_text_buffer_new(NULL);
305
gtk_text_buffer_create_tag(text_buffer, "b", "weight", PANGO_WEIGHT_BOLD,NULL);
306
gtk_text_buffer_create_tag(text_buffer, "link", "foreground", "#0000FF","underline",PANGO_UNDERLINE_SINGLE,NULL);
309
/* Create the conversation history widget*/
310
GtkWidget *history_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2 );
311
GtkWidget *h_left_spacer = gtk_label_new ( "" );
312
GtkWidget *h_right_spacer = gtk_label_new ( "" );
313
GtkWidget *scoll_area = gtk_scrolled_window_new ( NULL,NULL );
314
GtkWidget *text_box_widget = gtk_text_view_new_with_buffer( text_buffer );
315
gtk_box_pack_start(GTK_BOX(history_hbox) , h_left_spacer , FALSE , FALSE , 0);
316
gtk_box_pack_start(GTK_BOX(history_hbox) , scoll_area , TRUE , TRUE , 0);
317
gtk_box_pack_start(GTK_BOX(history_hbox) , h_right_spacer , FALSE , FALSE , 0);
319
gtk_text_view_set_editable ( GTK_TEXT_VIEW(text_box_widget),FALSE );
320
gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(text_box_widget),GTK_WRAP_CHAR);
322
gtk_container_add(GTK_CONTAINER(scoll_area), text_box_widget);
324
g_signal_connect(G_OBJECT(text_box_widget), "motion-notify-event" , G_CALLBACK(on_cursor_motion), self);
325
g_signal_connect(G_OBJECT(text_buffer ), "mark-set" , G_CALLBACK(on_clicked ), client);
327
GtkWidget *line_edit = gtk_entry_new ( );
328
GtkWidget *hbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, 1 );
329
GtkWidget *left_spacer = gtk_label_new ( "" );
330
GtkWidget *right_spacer = gtk_label_new ( "" );
331
gtk_box_pack_start(GTK_BOX(hbox) , left_spacer , FALSE , FALSE , 0);
332
gtk_box_pack_start(GTK_BOX(hbox) , line_edit , TRUE , TRUE , 0);
333
gtk_box_pack_start(GTK_BOX(hbox) , right_spacer , FALSE , FALSE , 0);
335
g_signal_connect(G_OBJECT(line_edit), "activate" , G_CALLBACK(on_enter) , self);
336
g_signal_connect(G_OBJECT(line_edit), "focus-in-event" , G_CALLBACK(on_focus_in) , self);
337
g_signal_connect(G_OBJECT(line_edit), "focus-out-event" , G_CALLBACK(on_focus_out), self);
339
self->view = GTK_TEXT_VIEW(text_box_widget);
340
self->widget = vbox ;
341
self->buffer = text_buffer;
342
self->entry = line_edit ;
344
/* Setup the tab label */
345
GtkWidget *tab_label = gtk_label_new ( label );
346
GtkWidget *tab_label_vbox = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL, 0 );
347
GtkWidget *tab_close_button = gtk_button_new ( );
348
GtkWidget *button_image = gtk_image_new_from_stock( GTK_STOCK_CLOSE,GTK_ICON_SIZE_MENU );
349
gtk_box_set_spacing (GTK_BOX(tab_label_vbox),0);
351
/*TODO make it work*/
352
/* GtkRcStyle *style = gtk_rc_style_new();
353
style->xthickness = 0;
354
style->ythickness = 0;
355
gtk_widget_modify_style(tab_close_button,style);*/
356
gtk_button_set_image(GTK_BUTTON(tab_close_button),button_image);
357
g_signal_connect(G_OBJECT(tab_close_button), "clicked", G_CALLBACK(on_close), self);
359
/* Fill the layout ans show everything */
360
gtk_box_pack_start(GTK_BOX(vbox) , history_hbox , TRUE , TRUE , 0);
361
gtk_box_pack_start(GTK_BOX(vbox) , hbox , FALSE, FALSE, 0);
362
gtk_box_pack_start(GTK_BOX(tab_label_vbox), tab_label , TRUE , TRUE , 0);
363
gtk_box_pack_start(GTK_BOX(tab_label_vbox), tab_close_button, FALSE, FALSE, 0);
365
gtk_widget_show (tab_label );
366
gtk_widget_show (tab_close_button);
367
gtk_widget_show (tab_label_vbox );
368
gtk_widget_show (vbox );
369
gtk_widget_show (scoll_area );
370
gtk_widget_show (text_box_widget );
371
gtk_widget_show (history_hbox );
372
gtk_widget_show (h_left_spacer );
373
gtk_widget_show (h_right_spacer );
374
gtk_widget_show (hbox );
375
gtk_widget_show (line_edit );
376
gtk_widget_show (left_spacer );
377
gtk_widget_show (right_spacer );
379
self->index = gtk_notebook_append_page(GTK_NOTEBOOK(get_tab_box()),vbox,tab_label_vbox);
380
gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(get_tab_box()),vbox,TRUE);
381
gtk_notebook_set_current_page(GTK_NOTEBOOK(get_tab_box()),self->index);
383
/* Keep track of the tab */
385
tabs = g_hash_table_new(NULL,g_str_equal);
387
g_hash_table_insert(tabs,(gpointer)call_id,(gpointer)self);
393
new_text_message_common(const gchar* id, const gchar* message, const gchar *name, SFLPhoneClient *client)
395
gtk_widget_show(get_tab_box());
396
message_tab *tab = NULL;
398
tab = g_hash_table_lookup(tabs, id);
400
tab = create_messaging_tab_common(id, name, client);
401
append_message(tab, name, message);
406
new_text_message(callable_obj_t* call, const gchar* message, SFLPhoneClient *client)
409
if (g_strcmp0(call->_display_name, "") == 0)
410
label_text = call->_display_name;
413
message_tab *tab = new_text_message_common(call->_callID, message, label_text, client);
418
new_text_message_conf(conference_obj_t* conf, const gchar* message,const gchar* from, SFLPhoneClient *client)
420
disable_conference_calls(conf);
421
message_tab *tab = new_text_message_common(conf->_confID, message,
422
strlen(from) ? from : "Conference", client);
427
create_messaging_tab(callable_obj_t* call, SFLPhoneClient *client)
429
const gchar *confID = dbus_get_conference_id(call->_callID);
430
if (strlen(confID) > 0 && ((tabs && force_lookup(confID) == NULL) || !tabs)) {
431
return create_messaging_tab_common(confID, "Conference", client);
433
else if (strlen(confID) > 0 && tabs) {
434
return force_lookup(confID);
437
if (strcmp(call->_display_name,""))
438
label_text = call->_display_name;
440
label_text = call->_peer_number ;
441
message_tab* self = create_messaging_tab_common(call->_callID, label_text, client);
449
create_messaging_tab_conf(conference_obj_t* call, SFLPhoneClient *client)
451
if (call->_confID && strlen(call->_confID)) {
452
message_tab* self = create_messaging_tab_common(call->_confID, "Conference", client);
455
disable_conference_calls(call);