2
* Copyright (C) 2010 Savoir-Faire Linux Inc.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 3 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
* Additional permission under GNU GPL version 3 section 7:
20
* If you modify this program, or any covered work, by linking or
21
* combining it with the OpenSSL project's OpenSSL library (or a
22
* modified version of that library), containing parts covered by the
23
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
24
* grants you additional permission to convey the resulting work.
25
* Corresponding Source for a non-source form of such a combination
26
* shall include the source code for the parts of OpenSSL used as well
27
* as that of the covered work.
32
#include <icons/icon_factory.h>
33
#include <contacts/calltab.h>
34
#include <contacts/conferencelist.h>
35
#include <JavaScriptCore/JavaScript.h>
36
#include <gdk/gdkkeysyms.h>
38
#define WEBKIT_DIR "file://" DATA_DIR "/webkit/"
41
on_frame_loading_done (GObject *gobject UNUSED, GParamSpec *pspec UNUSED, gpointer user_data)
43
IMWidget *im = IM_WIDGET (user_data);
45
conference_obj_t *conf;
47
if (im->first_message && im->first_message_from) {
48
switch (webkit_web_frame_get_load_status (WEBKIT_WEB_FRAME (im->web_frame))) {
49
case WEBKIT_LOAD_PROVISIONAL:
50
case WEBKIT_LOAD_COMMITTED:
52
case WEBKIT_LOAD_FINISHED:
53
call = calllist_get (current_calls, im->call_id);
54
conf = conferencelist_get (im->call_id);
57
im_widget_add_message (im, im->first_message_from, im->first_message, 0);
60
im_widget_add_message (im, im->first_message_from, im->first_message, 0);
62
g_free (im->first_message);
63
g_free (im->first_message_from);
64
im->first_message = NULL;
65
im->first_message_from = NULL;
66
DEBUG ("JavaScrip loading frame finished");
68
case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT:
69
// case WEBKIT_LOAD_FAILED: // only available in webkit-1.0-2
77
escape_single_quotes (const gchar *message)
82
DEBUG ("message: %s", message);
84
if ( (ptr_token = g_strsplit (message, "'", 0))) {
86
string = g_strjoinv ("\\'", ptr_token);
93
im_widget_add_message (IMWidget *im, const gchar *from, const gchar *message, gint level)
98
/* Compute the date the message was sent */
99
gchar *msgtime = im_widget_add_message_time ();
101
/* Check for the message level */
102
gchar *css_class = (level == MESSAGE_LEVEL_ERROR) ? "error" : "";
104
gchar *message_escaped = escape_single_quotes (message);
106
/* Prepare and execute the Javascript code */
107
gchar *script = g_strdup_printf ("add_message('%s', '%s', '%s', '%s');", message_escaped, from, css_class, msgtime);
108
webkit_web_view_execute_script (WEBKIT_WEB_VIEW (im->web_view), script);
110
/* Mark it as used */
111
im->containText = TRUE;
115
g_free (message_escaped);
121
web_view_nav_requested_cb (
122
WebKitWebView *web_view UNUSED,
123
WebKitWebFrame *frame UNUSED,
124
WebKitNetworkRequest *request,
125
WebKitWebNavigationAction *navigation_action UNUSED,
126
WebKitWebPolicyDecision *policy_decision,
127
gpointer user_data UNUSED)
129
const gchar *uri = webkit_network_request_get_uri (request);
131
/* Always allow files we are serving ourselves. */
132
if (!strncmp (uri, WEBKIT_DIR, sizeof (WEBKIT_DIR) - 1)) {
133
webkit_web_policy_decision_use (policy_decision);
135
/* Running a system command to open the URL in the user's default browser */
136
gchar *cmd = g_strdup_printf ("x-www-browser %s", uri);
138
if (system (cmd) == -1)
139
ERROR ("Error executing command %s", cmd);
141
webkit_web_policy_decision_ignore (policy_decision);
149
on_Textview_changed (GtkWidget *widget UNUSED, GdkEventKey *event, gpointer user_data)
152
GtkTextIter start, end;
153
/* Get all the text in the buffer */
154
IMWidget *im = user_data;
156
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (im->textarea));
158
/* Catch the keyboard events */
159
if (event->type == GDK_KEY_PRESS) {
161
switch (event->keyval) {
165
/* We want to send the message on pressing ENTER */
166
if (gtk_text_buffer_get_char_count (buffer) != 0) {
167
/* Fetch the string text */
168
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
169
gchar *message = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
171
/* Display our own message in the chat window */
172
im_widget_add_message (im, "Me", message, MESSAGE_LEVEL_NORMAL);
174
/* Send the message to the peer */
175
im_widget_send_message (im->call_id, message);
177
/* Empty the buffer */
178
gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer), &start, &end);
190
im_widget_add_message_time ()
194
unsigned char str[100];
196
/* Compute the current time */
199
ptr = localtime (&now);
201
/* Get the time of the message. Format: HH:MM::SS */
202
strftime ( (char *) str, 100, "%R", (const struct tm *) ptr);
203
gchar *res = g_strdup ( (gchar *) str);
205
/* Return the new value */
210
im_widget_send_message (gchar *id, const gchar *message)
213
callable_obj_t *im_widget_call = calllist_get (current_calls, id);
214
conference_obj_t *im_widget_conf = conferencelist_get (id);
216
/* If the call has been hungup, it is not anymore in the current_calls calltab */
217
if (!im_widget_call) {
218
/* So try the history tab */
219
im_widget_call = calllist_get (history, id);
222
if (im_widget_conf) {
223
dbus_send_text_message (id, message);
225
/* First check if the call is in CURRENT state, otherwise it could not be sent */
226
else if (im_widget_call) {
227
if (im_widget_call->_type == CALL && (im_widget_call->_state == CALL_STATE_CURRENT ||
228
im_widget_call->_state == CALL_STATE_HOLD ||
229
im_widget_call->_state == CALL_STATE_RECORD)) {
230
/* Ship the message through D-Bus */
231
dbus_send_text_message (id, message);
233
/* Display an error message */
234
im_widget_add_message (IM_WIDGET (im_widget_call->_im_widget), "sflphoned", "Oups, something went wrong! Unable to send text messages outside a call.", MESSAGE_LEVEL_ERROR);
241
im_widget_class_init (IMWidgetClass *klass UNUSED)
246
im_widget_init (IMWidget *im)
248
/* A text view to enable users to enter text */
249
im->textarea = gtk_text_view_new ();
251
/* The webkit widget to display the message */
252
im->web_view = webkit_web_view_new();
253
GtkWidget *textscrollwin = gtk_scrolled_window_new (NULL, NULL);
254
GtkWidget *webscrollwin = gtk_scrolled_window_new (NULL, NULL);
255
im->info_bar = gtk_info_bar_new ();
257
/* A bar with the entry text and the button to send the message */
258
GtkWidget *hbox = gtk_hbox_new (FALSE, 10);
259
gtk_text_view_set_editable (GTK_TEXT_VIEW (im->textarea), TRUE);
260
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (textscrollwin), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
261
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (webscrollwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
262
gtk_widget_set_size_request (GTK_WIDGET (textscrollwin), -1, 20);
263
gtk_widget_set_size_request (GTK_WIDGET (im->textarea), -1, 20);
264
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (im->textarea), GTK_WRAP_CHAR);
265
// gtk_container_set_resize_mode(GTK_CONTAINER(im->textarea), GTK_RESIZE_PARENT);
267
gtk_container_add (GTK_CONTAINER (textscrollwin), im->textarea);
268
gtk_container_add (GTK_CONTAINER (webscrollwin), im->web_view);
269
gtk_container_add (GTK_CONTAINER (hbox), textscrollwin);
270
gtk_box_pack_start (GTK_BOX (im), im->info_bar, FALSE, FALSE, 2);
271
gtk_box_pack_start (GTK_BOX (im), webscrollwin, TRUE, TRUE, 5);
272
gtk_box_pack_end (GTK_BOX (im), hbox, FALSE, FALSE, 2);
273
g_signal_connect (im->web_view, "navigation-policy-decision-requested", G_CALLBACK (web_view_nav_requested_cb), NULL);
274
g_signal_connect (im->textarea, "key-press-event", G_CALLBACK (on_Textview_changed), im);
276
im->web_frame = webkit_web_view_get_main_frame (WEBKIT_WEB_VIEW (im->web_view));
277
im->js_context = webkit_web_frame_get_global_context (im->web_frame);
278
im->js_global = JSContextGetGlobalObject (im->js_context);
279
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (im->web_view), "file://" DATA_DIR "/webkit/im/im.html");
281
im->containText = FALSE;
283
g_signal_connect (G_OBJECT (im->web_frame), "notify", G_CALLBACK (on_frame_loading_done), im);
289
return GTK_WIDGET (g_object_new (IM_WIDGET_TYPE, NULL));
293
im_widget_new_with_first_message (const gchar *message UNUSED)
295
return GTK_WIDGET (g_object_new (IM_WIDGET_TYPE, NULL));
296
// return GTK_WIDGET (g_object_new (IM_WIDGET_TYPE, "first_message", message, NULL));
301
im_widget_get_type (void)
303
static GType im_widget_type = 0;
305
if (!im_widget_type) {
306
static const GTypeInfo im_widget_info = {
307
sizeof (IMWidgetClass),
308
NULL, /* base_init */
309
NULL, /* base_finalize */
310
(GClassInitFunc) im_widget_class_init,
311
NULL, /* class_finalize */
312
NULL, /* class_data */
315
(GInstanceInitFunc) im_widget_init,
316
NULL /* value_table */
319
im_widget_type = g_type_register_static (
326
return im_widget_type;
330
im_widget_display (IMWidget **im, const gchar *message, const gchar *id, const gchar *from)
333
/* Work with a copy of the object */
334
// callable_obj_t *tmp = *call;
336
/* Use the widget for this specific call, if exists */
338
IMWidget *imwidget = *im;// = IM_WIDGET (tmp->_im_widget);
341
DEBUG ("creating the im widget for this call\n");
343
/* Create the im object, first message must be created asynchronously */
345
imwidget = IM_WIDGET (im_widget_new ());
347
imwidget = IM_WIDGET (im_widget_new_with_first_message (message));
349
/* Keep a reference on this object in the call struct */
350
// tmp->_im_widget = im;
353
/* Update the widget with some useful call information: ie the call ID */
354
imwidget->call_id = id;
356
/* Create the GtkInfoBar, used to display call information, and status of the IM widget */
357
im_widget_infobar (imwidget);
359
/* Add it to the main instant messaging window */
360
im_window_add (GTK_WIDGET (imwidget));
362
/* Update the first message to appears at widget creation*/
364
imwidget->first_message = g_strdup (message);
367
imwidget->first_message_from = g_strdup (from);
373
DEBUG ("im widget exists for this call\n");
385
im_widget_infobar (IMWidget *im)
388
/* Fetch the GTKInfoBar of this very IM Widget */
389
GtkWidget *infobar = im->info_bar;
390
GtkWidget *content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar));
392
/* Fetch call/conference information */
393
callable_obj_t *im_widget_call = calllist_get (current_calls, im->call_id);
394
conference_obj_t *im_widget_conf = conferencelist_get (im->call_id);
396
/* Create the label widgets with the call information saved in the IM Widget struct */
400
msg1 = g_strdup_printf ("Calling %s %s", im_widget_call->_peer_number, im_widget_call->_peer_name);
401
else if (im_widget_conf)
402
msg1 = g_strdup_printf ("Conferencing"); // im_widget_conf->_confID);
404
msg1 = g_strdup ("");
406
GtkWidget *call_label = gtk_label_new (msg1);
409
im->info_state = call_state_image_widget (im_widget_call->_state);
412
im->info_state = conf_state_image_widget (im_widget_conf->_state);
414
/* Add a nice icon from our own icon factory */
415
GtkWidget *logoUser = gtk_image_new_from_stock (GTK_STOCK_USER, GTK_ICON_SIZE_LARGE_TOOLBAR);
418
gtk_container_add (GTK_CONTAINER (content_area), logoUser);
419
gtk_container_add (GTK_CONTAINER (content_area), call_label);
420
gtk_container_add (GTK_CONTAINER (content_area), im->info_state);
422
/* Message level by default: INFO */
423
gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO);
425
/* Show the info bar */
426
gtk_widget_show (infobar);
433
call_state_image_widget (call_state_t state)
439
case CALL_STATE_CURRENT:
440
case CALL_STATE_HOLD:
441
case CALL_STATE_RECORD:
442
image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR);
445
image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR);
454
conf_state_image_widget (conference_state_t state)
460
case CONFERENCE_STATE_ACTIVE_ATACHED:
461
case CONFERENCE_STATE_ACTIVE_DETACHED:
462
case CONFERENCE_STATE_RECORD:
463
case CONFERENCE_STATE_HOLD:
464
image = gtk_image_new_from_stock (GTK_STOCK_IM, GTK_ICON_SIZE_LARGE_TOOLBAR);
467
image = gtk_image_new_from_stock (GTK_STOCK_FAIL, GTK_ICON_SIZE_LARGE_TOOLBAR);
475
im_widget_update_state (IMWidget *im, gboolean active)
477
/* if active = true, it means that we are the call is in current state, so sflphone can send text messages */
479
gtk_widget_set_sensitive (im->info_state, TRUE);
480
gtk_info_bar_set_message_type (GTK_INFO_BAR (im->info_bar),
483
/* if active = false, the call is over, we can't send text messages anymore */
486
gtk_widget_set_sensitive (im->info_state, FALSE);
487
gtk_info_bar_set_message_type (GTK_INFO_BAR (im->info_bar),
488
GTK_MESSAGE_WARNING);
489
gtk_widget_set_tooltip_text (im->info_state, "Call has terminated");