blob: e9e5841195d728a6e6a360ee022f35973dad879e [file] [log] [blame]
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001/*
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -04002 * Copyright (C) 2015 Savoir-faire Linux Inc.
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05003 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
4 *
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.
9 *
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.
14 *
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.
18 *
19 * Additional permission under GNU GPL version 3 section 7:
20 *
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
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040024 * terms of the OpenSSL or SSLeay licenses, Savoir-faire Linux Inc.
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050025 * 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.
29 */
30
31#include "currentcallview.h"
32
33#include <gtk/gtk.h>
34#include <call.h>
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050035#include <callmodel.h>
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050036#include "utils/drawing.h"
37#include "video/video_widget.h"
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -040038#include <video/previewmanager.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040039#include <contactmethod.h>
40#include <person.h>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040041#include <globalinstances.h>
42#include "native/pixbufmanipulator.h"
Stepan Salenikovicha448f602015-05-29 13:33:06 -040043#include <media/media.h>
44#include <media/text.h>
45#include <media/textrecording.h>
46#include "models/gtkqtreemodel.h"
Stepan Salenikovichb94873c2015-06-02 16:53:18 -040047#include "video/videowindow.h"
Stepan Salenikovich67112d12015-06-16 16:57:06 -040048#include "ringnotify.h"
Stepan Salenikovichf6f42652015-07-15 12:46:14 -040049#include <audio/codecmodel.h>
50#include <account.h>
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050051
52struct _CurrentCallView
53{
54 GtkBox parent;
55};
56
57struct _CurrentCallViewClass
58{
59 GtkBoxClass parent_class;
60};
61
62typedef struct _CurrentCallViewPrivate CurrentCallViewPrivate;
63
64struct _CurrentCallViewPrivate
65{
66 GtkWidget *image_peer;
67 GtkWidget *label_identity;
68 GtkWidget *label_status;
69 GtkWidget *label_duration;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050070 GtkWidget *frame_video;
71 GtkWidget *video_widget;
Stepan Salenikovicha448f602015-05-29 13:33:06 -040072 GtkWidget *revealer_chat;
73 GtkWidget *togglebutton_chat;
74 GtkWidget *textview_chat;
75 GtkWidget *button_chat_input;
76 GtkWidget *entry_chat_input;
77 GtkWidget *scrolledwindow_chat;
Stepan Salenikovichb94873c2015-06-02 16:53:18 -040078 GtkWidget *fullscreen_window;
Stepan Salenikovich77baa522015-07-07 15:29:14 -040079 GtkWidget *buttonbox_call_controls;
80 GtkWidget *button_hangup;
Stepan Salenikovichf6f42652015-07-15 12:46:14 -040081 GtkWidget *scalebutton_quality;
82
83 /* flag used to keep track of the video quality scale pressed state;
84 * we do not want to update the codec bitrate until the user releases the
85 * scale button */
86 gboolean quality_scale_pressed;
Stepan Salenikovicha448f602015-05-29 13:33:06 -040087
88 Call *call;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050089
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050090 QMetaObject::Connection state_change_connection;
91 QMetaObject::Connection call_details_connection;
Stepan Salenikovichc5f08152015-03-19 00:53:23 -040092 QMetaObject::Connection local_renderer_connection;
93 QMetaObject::Connection remote_renderer_connection;
Stepan Salenikovicha448f602015-05-29 13:33:06 -040094 QMetaObject::Connection media_added_connection;
95 QMetaObject::Connection new_message_connection;
Stepan Salenikovich7b60b592015-06-16 12:29:07 -040096 QMetaObject::Connection incoming_msg_connection;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050097};
98
99G_DEFINE_TYPE_WITH_PRIVATE(CurrentCallView, current_call_view, GTK_TYPE_BOX);
100
101#define CURRENT_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CURRENT_CALL_VIEW_TYPE, CurrentCallViewPrivate))
102
103static void
104current_call_view_dispose(GObject *object)
105{
106 CurrentCallView *view;
107 CurrentCallViewPrivate *priv;
108
109 view = CURRENT_CALL_VIEW(object);
110 priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
111
112 QObject::disconnect(priv->state_change_connection);
113 QObject::disconnect(priv->call_details_connection);
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400114 QObject::disconnect(priv->local_renderer_connection);
115 QObject::disconnect(priv->remote_renderer_connection);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400116 QObject::disconnect(priv->media_added_connection);
117 QObject::disconnect(priv->new_message_connection);
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400118 QObject::disconnect(priv->incoming_msg_connection);
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400119
Stepan Salenikovichb94873c2015-06-02 16:53:18 -0400120 if (priv->fullscreen_window) {
121 gtk_widget_destroy(priv->fullscreen_window);
122 priv->fullscreen_window = NULL;
123 }
124
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500125 G_OBJECT_CLASS(current_call_view_parent_class)->dispose(object);
126}
127
128static void
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400129chat_toggled(GtkToggleButton *togglebutton, CurrentCallView *self)
130{
131 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
132 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
133
134 gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer_chat),
135 gtk_toggle_button_get_active(togglebutton));
136
137 if (gtk_toggle_button_get_active(togglebutton)) {
138 /* create an outgoing media to bring up chat history, if any */
139 priv->call->addOutgoingMedia<Media::Text>();
Stepan Salenikovichbffaf582015-06-22 14:10:40 -0400140 /* change focus to the chat entry */
141 gtk_widget_grab_focus(priv->entry_chat_input);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400142 }
143}
144
145static void
146send_chat(G_GNUC_UNUSED GtkWidget *widget, CurrentCallView *self)
147{
148 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
149 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
150
151 /* make sure there is text to send */
152 const gchar *text = gtk_entry_get_text(GTK_ENTRY(priv->entry_chat_input));
153 if (text && strlen(text) > 0) {
Stepan Salenikovich6af88db2015-07-17 12:41:29 -0400154 QMap<QString, QString> messages;
155 messages["text/plain"] = text;
156 priv->call->addOutgoingMedia<Media::Text>()->send(messages);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400157 /* clear the entry */
158 gtk_entry_set_text(GTK_ENTRY(priv->entry_chat_input), "");
159 }
160}
161
162static void
163scroll_to_bottom(GtkAdjustment *adjustment, G_GNUC_UNUSED gpointer user_data)
164{
165 gtk_adjustment_set_value(adjustment,
166 gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
167}
168
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400169/**
170 * This gets the GtkScaleButtonScale widget (which is a GtkScale) from the
171 * given GtkScaleButton in order to be able to modify its properties and connect
172 * to its signals
173 */
174static GtkScale *
175gtk_scale_button_get_scale(GtkScaleButton *button)
176{
177 GtkScale *scale = NULL;
178 GtkWidget *dock = gtk_scale_button_get_popup(button);
179
Stepan Salenikovich9b8f5452015-07-16 10:36:07 -0400180 // the dock is a popover which contains a box
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400181 // which contains the + button, scale, and - button
182 // we want to get the scale
183 if (GtkWidget *box = gtk_bin_get_child(GTK_BIN(dock))) {
Stepan Salenikovich9b8f5452015-07-16 10:36:07 -0400184 if (GTK_IS_FRAME(box)) {
185 // support older versions of gtk; the box used to be in a frame
186 box = gtk_bin_get_child(GTK_BIN(box));
187 }
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400188 GList *children = gtk_container_get_children(GTK_CONTAINER(box));
189 for (GList *c = children; c && !scale; c = c->next) {
190 if (GTK_IS_SCALE(c->data))
191 scale = GTK_SCALE(c->data);
192 }
193 g_list_free(children);
194 }
195
196 return scale;
197}
198
199static void
200quality_changed(GtkScaleButton *button, G_GNUC_UNUSED gdouble value, CurrentCallView *self)
201{
202 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
203 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
204
205 /* only update if the scale button is released, to reduce the number of updates */
206 if (priv->quality_scale_pressed) return;
207
208 /* we get the value directly from the widget, in case this function is not
209 * called from the event */
210 unsigned int bitrate = (unsigned int)gtk_scale_button_get_value(button);
211
212 if (const auto& codecModel = priv->call->account()->codecModel()) {
213 const auto& videoCodecs = codecModel->videoCodecs();
214 for (int i=0; i < videoCodecs->rowCount();i++) {
215 const auto& idx = videoCodecs->index(i,0);
216 g_debug("setting codec bitrate to %u", bitrate);
217 videoCodecs->setData(idx, QString::number(bitrate), CodecModel::Role::BITRATE);
218 }
219 codecModel << CodecModel::EditAction::SAVE;
220 }
221}
222
223static gboolean
224quality_button_pressed(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, CurrentCallView *self)
225{
226 g_debug("button pressed");
227 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), FALSE);
228 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
229
230 priv->quality_scale_pressed = TRUE;
231
232 return FALSE; // propogate the event
233}
234
235static gboolean
236quality_button_released(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event, CurrentCallView *self)
237{
238 g_debug("button released");
239 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), FALSE);
240 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
241
242 priv->quality_scale_pressed = FALSE;
243
244 /* now make sure the quality gets updated */
245 quality_changed(GTK_SCALE_BUTTON(priv->scalebutton_quality), 0, self);
246
247 return FALSE; // propogate the event
248}
249
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400250static void
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500251current_call_view_init(CurrentCallView *view)
252{
253 gtk_widget_init_template(GTK_WIDGET(view));
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400254
255 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
256
257 g_signal_connect(priv->togglebutton_chat, "toggled", G_CALLBACK(chat_toggled), view);
258 g_signal_connect(priv->button_chat_input, "clicked", G_CALLBACK(send_chat), view);
259 g_signal_connect(priv->entry_chat_input, "activate", G_CALLBACK(send_chat), view);
260
261 /* the adjustment params will change only when the model is created and when
262 * new messages are added; in these cases we want to scroll to the bottom of
263 * the chat treeview */
264 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(priv->scrolledwindow_chat));
265 g_signal_connect(adjustment, "changed", G_CALLBACK(scroll_to_bottom), NULL);
Stepan Salenikovich77baa522015-07-07 15:29:14 -0400266
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400267 /* customize the quality button scale */
268 if (GtkScale *scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality))) {
269 gtk_scale_set_draw_value(scale, TRUE);
270 gtk_scale_set_value_pos(scale, GTK_POS_RIGHT);
271 gtk_scale_set_digits(scale, 0);
272 }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500273}
274
275static void
276current_call_view_class_init(CurrentCallViewClass *klass)
277{
278 G_OBJECT_CLASS(klass)->dispose = current_call_view_dispose;
279
280 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
281 "/cx/ring/RingGnome/currentcallview.ui");
282
283 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, image_peer);
284 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_identity);
285 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_status);
286 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_duration);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500287 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, frame_video);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400288 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, revealer_chat);
289 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_chat);
290 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, textview_chat);
291 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, button_chat_input);
292 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, entry_chat_input);
293 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scrolledwindow_chat);
Stepan Salenikovich77baa522015-07-07 15:29:14 -0400294 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, buttonbox_call_controls);
295 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, button_hangup);
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400296 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scalebutton_quality);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500297}
298
299GtkWidget *
300current_call_view_new(void)
301{
302 return (GtkWidget *)g_object_new(CURRENT_CALL_VIEW_TYPE, NULL);
303}
304
305static void
306update_state(CurrentCallView *view, Call *call)
307{
308 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
309
Stepan Salenikovich7ec8fe82015-06-02 18:26:39 -0400310 gchar *status = g_strdup_printf("%s", call->toHumanStateName().toUtf8().constData());
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500311
Stepan Salenikovich7ec8fe82015-06-02 18:26:39 -0400312 gtk_label_set_text(GTK_LABEL(priv->label_status), status);
313
314 g_free(status);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500315}
316
317static void
318update_details(CurrentCallView *view, Call *call)
319{
320 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
321
322 /* update call duration */
323 QByteArray ba_length = call->length().toLocal8Bit();
324 gtk_label_set_text(GTK_LABEL(priv->label_duration), ba_length.constData());
325}
326
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400327static void
Stepan Salenikovichb94873c2015-06-02 16:53:18 -0400328on_fullscreen_destroy(CurrentCallView *view)
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400329{
330 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
331 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
332
Stepan Salenikovichb94873c2015-06-02 16:53:18 -0400333 /* fullscreen is being destroyed, clear the pointer and un-pause the rendering
334 * in this window */
335 priv->fullscreen_window = NULL;
336 video_widget_pause_rendering(VIDEO_WIDGET(priv->video_widget), FALSE);
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400337}
338
339static gboolean
340on_button_press_in_video_event(GtkWidget *self, GdkEventButton *event, CurrentCallView *view)
341{
342 g_return_val_if_fail(IS_VIDEO_WIDGET(self), FALSE);
343 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
344 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
345
346 /* on double click */
347 if (event->type == GDK_2BUTTON_PRESS) {
Stepan Salenikovichb94873c2015-06-02 16:53:18 -0400348 if (priv->fullscreen_window) {
349 /* destroy the fullscreen */
350 gtk_widget_destroy(priv->fullscreen_window);
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400351 } else {
Stepan Salenikovichb94873c2015-06-02 16:53:18 -0400352 /* pause rendering in this window and create fullscreen */
353 video_widget_pause_rendering(VIDEO_WIDGET(priv->video_widget), TRUE);
354
355 priv->fullscreen_window = video_window_new(priv->call,
356 GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
357
358 /* connect to destruction of fullscreen so we know when to un-pause
359 * the rendering in thiw window */
360 g_signal_connect_swapped(priv->fullscreen_window,
361 "destroy",
362 G_CALLBACK(on_fullscreen_destroy),
363 view);
364
365 /* present the fullscreen widnow */
366 gtk_window_present(GTK_WINDOW(priv->fullscreen_window));
367 gtk_window_fullscreen(GTK_WINDOW(priv->fullscreen_window));
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400368 }
369 }
370
371 /* the event has been fully handled */
372 return TRUE;
373}
374
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400375static void
376print_message_to_buffer(const QModelIndex &idx, GtkTextBuffer *buffer)
377{
378 if (idx.isValid()) {
Stepan Salenikovichdeae6252015-06-22 12:51:52 -0400379 auto message = idx.data().value<QString>().toUtf8();
380 auto sender = idx.data(static_cast<int>(Media::TextRecording::Role::AuthorDisplayname)).value<QString>().toUtf8();
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400381
382 GtkTextIter iter;
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400383
Stepan Salenikovichdeae6252015-06-22 12:51:52 -0400384 /* unless its the very first message, insert a new line */
385 if (idx.row() != 0) {
386 gtk_text_buffer_get_end_iter(buffer, &iter);
387 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
388 }
389
390 auto format_sender = g_strconcat(sender.constData(), ": ", NULL);
391 gtk_text_buffer_get_end_iter(buffer, &iter);
392 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
393 format_sender, -1,
394 "bold", NULL);
395 g_free(format_sender);
396
397 /* if the sender name is too long, insert a new line after it */
398 if (sender.length() > 20) {
399 gtk_text_buffer_get_end_iter(buffer, &iter);
400 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
401 }
402
403 gtk_text_buffer_get_end_iter(buffer, &iter);
404 gtk_text_buffer_insert(buffer, &iter, message.constData(), -1);
405
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400406 } else {
407 g_warning("QModelIndex in im model is not valid");
408 }
409}
410
411static void
412parse_chat_model(QAbstractItemModel *model, CurrentCallView *self)
413{
414 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
415 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
416
417 /* new model, disconnect from the old model updates and clear the text buffer */
418 QObject::disconnect(priv->new_message_connection);
419
420 GtkTextBuffer *new_buffer = gtk_text_buffer_new(NULL);
421 gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->textview_chat), new_buffer);
Stepan Salenikovichdeae6252015-06-22 12:51:52 -0400422
423 /* add tags to the buffer */
424 gtk_text_buffer_create_tag(new_buffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
425
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400426 g_object_unref(new_buffer);
427
428 /* put all the messages in the im model into the text view */
429 for (int row = 0; row < model->rowCount(); ++row) {
430 QModelIndex idx = model->index(row, 0);
431 print_message_to_buffer(idx, new_buffer);
432 }
433
434 /* append new messages */
435 priv->new_message_connection = QObject::connect(
436 model,
437 &QAbstractItemModel::rowsInserted,
438 [priv, model] (const QModelIndex &parent, int first, int last) {
439 for (int row = first; row <= last; ++row) {
440 QModelIndex idx = model->index(row, 0, parent);
441 print_message_to_buffer(idx, gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview_chat)));
442 }
443 }
444 );
445}
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400446
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500447void
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400448monitor_incoming_message(CurrentCallView *self, Media::Text *media)
449{
450 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
451 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
452
453 /* connect to incoming chat messages to open the chat view */
454 QObject::disconnect(priv->incoming_msg_connection);
455 priv->incoming_msg_connection = QObject::connect(
456 media,
457 &Media::Text::messageReceived,
Stepan Salenikovich6af88db2015-07-17 12:41:29 -0400458 [priv] (G_GNUC_UNUSED const QMap<QString,QString>& m) {
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400459 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->togglebutton_chat), TRUE);
460 }
461 );
462}
463
464void
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500465current_call_view_set_call_info(CurrentCallView *view, const QModelIndex& idx) {
466 CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
467
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400468 priv->call = CallModel::instance().getCall(idx);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400469
470 /* get call image */
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -0400471 QVariant var_i = GlobalInstances::pixmapManipulator().callPhoto(priv->call, QSize(60, 60), false);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400472 std::shared_ptr<GdkPixbuf> image = var_i.value<std::shared_ptr<GdkPixbuf>>();
473 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_peer), image.get());
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500474
475 /* get name */
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400476 QVariant var = idx.model()->data(idx, static_cast<int>(Call::Role::Name));
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400477 QByteArray ba_name = var.toString().toUtf8();
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500478 gtk_label_set_text(GTK_LABEL(priv->label_identity), ba_name.constData());
479
480 /* change some things depending on call state */
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400481 update_state(view, priv->call);
482 update_details(view, priv->call);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500483
484 priv->state_change_connection = QObject::connect(
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400485 priv->call,
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500486 &Call::stateChanged,
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400487 [view, priv]() { update_state(view, priv->call); }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500488 );
489
490 priv->call_details_connection = QObject::connect(
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400491 priv->call,
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500492 static_cast<void (Call::*)(void)>(&Call::changed),
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400493 [view, priv]() { update_details(view, priv->call); }
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500494 );
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500495
496 /* video widget */
497 priv->video_widget = video_widget_new();
498 gtk_container_add(GTK_CONTAINER(priv->frame_video), priv->video_widget);
499 gtk_widget_show_all(priv->frame_video);
500
501 /* check if we already have a renderer */
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400502 video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400503 priv->call->videoRenderer(),
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400504 VIDEO_RENDERER_REMOTE);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500505
Stepan Salenikovich8e5c9d02015-03-11 14:07:10 -0400506 /* callback for remote renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400507 priv->remote_renderer_connection = QObject::connect(
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400508 priv->call,
Stepan Salenikovich9c1f6682015-03-09 16:21:28 -0400509 &Call::videoStarted,
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400510 [priv](Video::Renderer *renderer) {
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400511 video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
512 renderer,
513 VIDEO_RENDERER_REMOTE);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -0500514 }
515 );
Stepan Salenikovich4ac89f12015-03-10 16:48:47 -0400516
Stepan Salenikovich8e5c9d02015-03-11 14:07:10 -0400517 /* local renderer */
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400518 if (Video::PreviewManager::instance().isPreviewing())
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400519 video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400520 Video::PreviewManager::instance().previewRenderer(),
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400521 VIDEO_RENDERER_LOCAL);
Stepan Salenikovich8e5c9d02015-03-11 14:07:10 -0400522
523 /* callback for local renderer */
Stepan Salenikovichc5f08152015-03-19 00:53:23 -0400524 priv->local_renderer_connection = QObject::connect(
Guillaume Roguez5d1514b2015-10-22 15:55:31 -0400525 &Video::PreviewManager::instance(),
Stepan Salenikovich8e5c9d02015-03-11 14:07:10 -0400526 &Video::PreviewManager::previewStarted,
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400527 [priv](Video::Renderer *renderer) {
Stepan Salenikovich0f693232015-04-22 10:45:08 -0400528 video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
529 renderer,
530 VIDEO_RENDERER_LOCAL);
Stepan Salenikovich8e5c9d02015-03-11 14:07:10 -0400531 }
532 );
Stepan Salenikovichbfe9ac62015-03-11 12:49:20 -0400533
534 /* catch double click to make full screen */
535 g_signal_connect(priv->video_widget, "button-press-event",
536 G_CALLBACK(on_button_press_in_video_event),
537 view);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400538
539 /* check if text media is already present */
540 if (priv->call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
541 Media::Text *text = priv->call->firstMedia<Media::Text>(Media::Media::Direction::IN);
542 parse_chat_model(text->recording()->instantMessagingModel(), view);
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400543 monitor_incoming_message(view, text);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400544 } else if (priv->call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
545 Media::Text *text = priv->call->firstMedia<Media::Text>(Media::Media::Direction::OUT);
546 parse_chat_model(text->recording()->instantMessagingModel(), view);
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400547 monitor_incoming_message(view, text);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400548 } else {
549 /* monitor media for messaging text messaging */
550 priv->media_added_connection = QObject::connect(
551 priv->call,
552 &Call::mediaAdded,
553 [view, priv] (Media::Media* media) {
554 if (media->type() == Media::Media::Type::TEXT) {
555 parse_chat_model(((Media::Text*)media)->recording()->instantMessagingModel(), view);
Stepan Salenikovich7b60b592015-06-16 12:29:07 -0400556 monitor_incoming_message(view, (Media::Text*)media);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400557 QObject::disconnect(priv->media_added_connection);
558 }
559 }
560 );
561 }
Stepan Salenikovich67112d12015-06-16 16:57:06 -0400562
563 /* check if there were any chat notifications and open the chat view if so */
564 if (ring_notify_close_chat_notification(priv->call))
565 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->togglebutton_chat), TRUE);
Stepan Salenikovichf6f42652015-07-15 12:46:14 -0400566
567 /* get the current codec quality and set that as the initial slider value
568 * for now we assume that all codecs have the same quality */
569 if (const auto& codecModel = priv->call->account()->codecModel()) {
570 const auto& videoCodecs = codecModel->videoCodecs();
571 if (videoCodecs->rowCount() > 0) {
572 const auto& idx = videoCodecs->index(0,0);
573 double value = idx.data(static_cast<int>(CodecModel::Role::BITRATE)).toDouble();
574 gtk_scale_button_set_value(GTK_SCALE_BUTTON(priv->scalebutton_quality), value);
575 }
576 }
577 g_signal_connect(priv->scalebutton_quality, "value-changed", G_CALLBACK(quality_changed), view);
578 g_signal_connect(gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality)),
579 "button-press-event", G_CALLBACK(quality_button_pressed), view);
580 g_signal_connect(gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality)),
581 "button-release-event", G_CALLBACK(quality_button_released), view);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500582}