blob: fe72f5721b7007f8e2b65f43247fbd3f54ed8204 [file] [log] [blame]
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001/*
Guillaume Roguez77c579d2018-01-30 15:54:02 -05002 * Copyright (C) 2015-2018 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.
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050018 */
19
20#include "currentcallview.h"
21
Sébastien Blin55bff9d2017-10-03 15:15:23 -040022// Gtk
23#include <clutter-gtk/clutter-gtk.h>
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050024#include <gtk/gtk.h>
Stepan Salenikovich7e283552015-12-21 16:17:52 -050025#include <glib/gi18n.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040026
27// Lrc
Stepan Salenikovichf6f42652015-07-15 12:46:14 -040028#include <account.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040029#include <api/conversationmodel.h>
30#include <api/contact.h>
31#include <api/contactmodel.h>
32#include <api/newcallmodel.h>
Sébastien Blin3667fa62017-11-23 09:11:53 -050033#include <callmodel.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040034#include <codecmodel.h>
35#include <globalinstances.h>
Olivier Gregoire66e4df72016-06-17 18:39:05 -040036#include <smartinfohub.h>
Sébastien Blin55bff9d2017-10-03 15:15:23 -040037#include <video/previewmanager.h>
38
39// Client
40#include "chatview.h"
41#include "native/pixbufmanipulator.h"
42#include "ringnotify.h"
43#include "utils/drawing.h"
44#include "utils/files.h"
45#include "video/video_widget.h"
46
Sébastien Blin784f2a32018-05-30 17:31:13 -040047#include <iostream>
48
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -050049namespace { namespace details
50{
51class CppImpl;
52}}
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -050053
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050054struct _CurrentCallView
55{
56 GtkBox parent;
57};
58
59struct _CurrentCallViewClass
60{
61 GtkBoxClass parent_class;
62};
63
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -050064struct CurrentCallViewPrivate
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050065{
Stepan Salenikoviche178e632015-11-06 13:31:19 -050066 GtkWidget *hbox_call_info;
67 GtkWidget *hbox_call_controls;
Olivier Gregoire66e4df72016-06-17 18:39:05 -040068 GtkWidget *vbox_call_smartInfo;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050069 GtkWidget *image_peer;
Stepan Salenikovich07107e92016-05-06 10:35:17 -040070 GtkWidget *label_name;
Nicolas Jager2e467c32017-01-18 08:52:23 -050071 GtkWidget *label_bestId;
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050072 GtkWidget *label_status;
73 GtkWidget *label_duration;
Olivier Gregoire66e4df72016-06-17 18:39:05 -040074 GtkWidget *label_smartinfo_description;
75 GtkWidget *label_smartinfo_value;
76 GtkWidget *label_smartinfo_general_information;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050077 GtkWidget *paned_call;
Stepan Salenikovich36c025c2015-03-03 19:06:44 -050078 GtkWidget *frame_video;
79 GtkWidget *video_widget;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050080 GtkWidget *frame_chat;
Stepan Salenikovicha448f602015-05-29 13:33:06 -040081 GtkWidget *togglebutton_chat;
AmarOke7c02972017-07-17 15:21:20 -040082 GtkWidget *togglebutton_muteaudio;
83 GtkWidget *togglebutton_mutevideo;
Sébastien Blin784f2a32018-05-30 17:31:13 -040084 GtkWidget *togglebutton_transfer;
85 GtkWidget* siptransfer_popover;
86 GtkWidget* siptransfer_filter_entry;
87 GtkWidget* list_conversations;
AmarOke7c02972017-07-17 15:21:20 -040088 GtkWidget *togglebutton_hold;
Sébastien Blin55bff9d2017-10-03 15:15:23 -040089 GtkWidget *togglebutton_record;
Stepan Salenikovich77baa522015-07-07 15:29:14 -040090 GtkWidget *button_hangup;
Stepan Salenikovich7e283552015-12-21 16:17:52 -050091 GtkWidget *scalebutton_quality;
92 GtkWidget *checkbutton_autoquality;
Sébastien Blin366243f2017-11-03 14:14:54 -040093 GtkWidget *chat_view;
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -050094 GtkWidget *webkit_chat_container; // The webkit_chat_container is created once, then reused for all chat views
Stepan Salenikovicha5e8e362015-11-05 16:50:48 -050095
96 GSettings *settings;
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -050097
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -050098 details::CppImpl* cpp; ///< Non-UI and C++ only code
Stepan Salenikovichc64523b2015-02-27 16:31:00 -050099};
100
101G_DEFINE_TYPE_WITH_PRIVATE(CurrentCallView, current_call_view, GTK_TYPE_BOX);
102
103#define CURRENT_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CURRENT_CALL_VIEW_TYPE, CurrentCallViewPrivate))
104
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500105enum {
106 VIDEO_DOUBLE_CLICKED,
107 LAST_SIGNAL
108};
109
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500110//==============================================================================
111
112namespace { namespace details
113{
114
115static constexpr int CONTROLS_FADE_TIMEOUT = 3000000; /* microseconds */
116static constexpr int FADE_DURATION = 500; /* miliseconds */
Stepan Salenikoviche1b54892015-12-13 22:18:44 -0500117static guint current_call_view_signals[LAST_SIGNAL] = { 0 };
118
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500119namespace // Helpers
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500120{
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400121
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400122static gboolean
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500123map_boolean_to_orientation(GValue* value, GVariant* variant, G_GNUC_UNUSED gpointer user_data)
Stepan Salenikovicha5e8e362015-11-05 16:50:48 -0500124{
125 if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
126 if (g_variant_get_boolean(variant)) {
127 // true, chat should be horizontal (to the right)
128 g_value_set_enum(value, GTK_ORIENTATION_HORIZONTAL);
129 } else {
130 // false, chat should be vertical (at the bottom)
131 g_value_set_enum(value, GTK_ORIENTATION_VERTICAL);
132 }
133 return TRUE;
134 }
135 return FALSE;
136}
137
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500138static ClutterTransition*
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500139create_fade_out_transition()
140{
141 auto transition = clutter_property_transition_new("opacity");
142 clutter_transition_set_from(transition, G_TYPE_UINT, 255);
143 clutter_transition_set_to(transition, G_TYPE_UINT, 0);
144 clutter_timeline_set_duration(CLUTTER_TIMELINE(transition), FADE_DURATION);
145 clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transition), 0);
146 clutter_timeline_set_progress_mode(CLUTTER_TIMELINE(transition), CLUTTER_EASE_IN_OUT_CUBIC);
147 return transition;
148}
149
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500150static GtkBox *
151gtk_scale_button_get_box(GtkScaleButton *button)
152{
153 GtkWidget *box = NULL;
154 if (auto dock = gtk_scale_button_get_popup(button)) {
155 // the dock is a popover which contains the box
156 box = gtk_bin_get_child(GTK_BIN(dock));
157 if (box) {
158 if (GTK_IS_FRAME(box)) {
159 // support older versions of gtk; the box used to be in a frame
160 box = gtk_bin_get_child(GTK_BIN(box));
161 }
162 }
163 }
164
165 return GTK_BOX(box);
166}
167
168/**
169 * This gets the GtkScaleButtonScale widget (which is a GtkScale) from the
170 * given GtkScaleButton in order to be able to modify its properties and connect
171 * to its signals
172 */
173static GtkScale *
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500174gtk_scale_button_get_scale(GtkScaleButton* button)
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500175{
176 GtkScale *scale = NULL;
177
178 if (auto box = gtk_scale_button_get_box(button)) {
179 GList *children = gtk_container_get_children(GTK_CONTAINER(box));
180 for (GList *c = children; c && !scale; c = c->next) {
181 if (GTK_IS_SCALE(c->data))
182 scale = GTK_SCALE(c->data);
183 }
184 g_list_free(children);
185 }
186
187 return scale;
188}
189
190static void
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500191set_call_quality(Call& call, bool auto_quality_on, double desired_quality)
Sébastien Blin3667fa62017-11-23 09:11:53 -0500192{
193 /* set auto quality true or false, also set the bitrate and quality values;
194 * the slider is from 0 to 100, use the min and max vals to scale each value accordingly */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500195 if (const auto& codecModel = call.account()->codecModel()) {
Sébastien Blin3667fa62017-11-23 09:11:53 -0500196 const auto& videoCodecs = codecModel->videoCodecs();
197
198 for (int i=0; i < videoCodecs->rowCount();i++) {
199 const auto& idx = videoCodecs->index(i,0);
200
201 if (auto_quality_on) {
202 // g_debug("enable auto quality");
203 videoCodecs->setData(idx, "true", CodecModel::Role::AUTO_QUALITY_ENABLED);
204 } else {
205 auto min_bitrate = idx.data(static_cast<int>(CodecModel::Role::MIN_BITRATE)).toInt();
206 auto max_bitrate = idx.data(static_cast<int>(CodecModel::Role::MAX_BITRATE)).toInt();
207 auto min_quality = idx.data(static_cast<int>(CodecModel::Role::MIN_QUALITY)).toInt();
208 auto max_quality = idx.data(static_cast<int>(CodecModel::Role::MAX_QUALITY)).toInt();
209
210 // g_debug("bitrate min: %d, max: %d, quality min: %d, max: %d", min_bitrate, max_bitrate, min_quality, max_quality);
211
212 double bitrate;
213 bitrate = min_bitrate + (double)(max_bitrate - min_bitrate)*(desired_quality/100.0);
214 if (bitrate < 0) bitrate = 0;
215
216 double quality;
217 // note: a lower value means higher quality
218 quality = (double)min_quality - (min_quality - max_quality)*(desired_quality/100.0);
219 if (quality < 0) quality = 0;
220
221 // g_debug("disable auto quality; %% quality: %d; bitrate: %d; quality: %d", (int)desired_quality, (int)bitrate, (int)quality);
222 videoCodecs->setData(idx, "false", CodecModel::Role::AUTO_QUALITY_ENABLED);
223 videoCodecs->setData(idx, QString::number((int)bitrate), CodecModel::Role::BITRATE);
224 videoCodecs->setData(idx, QString::number((int)quality), CodecModel::Role::QUALITY);
225 }
226 }
227 codecModel << CodecModel::EditAction::SAVE;
228 }
229}
230
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500231} // namespace
232
233class CppImpl
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500234{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500235public:
236 explicit CppImpl(CurrentCallView& widget);
237 ~CppImpl();
238
239 void init();
240 void setup(WebKitChatContainer* chat_widget,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400241 AccountInfoPointer const & account_info,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500242 lrc::api::conversation::Info* conversation);
Sébastien Blin784f2a32018-05-30 17:31:13 -0400243 void add_transfer_contact(const std::string& uri);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500244
245 void insertControls();
246 void checkControlsFading();
247
248 CurrentCallView* self = nullptr; // The GTK widget itself
249 CurrentCallViewPrivate* widgets = nullptr;
250
251 lrc::api::conversation::Info* conversation = nullptr;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400252 AccountInfoPointer const *accountInfo = nullptr;
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500253
254 QMetaObject::Connection state_change_connection;
255 QMetaObject::Connection local_renderer_connection;
256 QMetaObject::Connection remote_renderer_connection;
257 QMetaObject::Connection new_message_connection;
258 QMetaObject::Connection smartinfo_refresh_connection;
259
260 // for clutter animations and to know when to fade in/out the overlays
261 ClutterTransition* fade_info = nullptr;
262 ClutterTransition* fade_controls = nullptr;
263 gint64 time_last_mouse_motion = 0;
264 guint timer_fade = 0;
265
266 /* flag used to keep track of the video quality scale pressed state;
267 * we do not want to update the codec bitrate until the user releases the
268 * scale button */
269 gboolean quality_scale_pressed = FALSE;
270 gulong insert_controls_id = 0;
271 guint smartinfo_action = 0;
272
273private:
274 CppImpl() = delete;
275 CppImpl(const CppImpl&) = delete;
276 CppImpl& operator=(const CppImpl&) = delete;
277
278 void setCallInfo();
279 void updateDetails();
280 void updateState();
281 void updateNameAndPhoto();
282 void updateSmartInfo();
283};
284
285inline namespace gtk_callbacks
286{
287
288static void
289on_new_chat_interactions(CurrentCallView* view)
290{
291 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
292 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
293
294 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->togglebutton_chat), TRUE);
295}
296
297static void
Sébastien Blin31cc01e2018-06-08 10:51:03 -0400298set_record_animation(CurrentCallViewPrivate* priv)
299{
300 auto callToRender = priv->cpp->conversation->callId;
301 if (!priv->cpp->conversation->confId.empty())
302 callToRender = priv->cpp->conversation->confId;
303 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->togglebutton_record),
304 (*priv->cpp->accountInfo)->callModel->isRecording(callToRender));
305}
306
307static void
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500308on_togglebutton_chat_toggled(GtkToggleButton* widget, CurrentCallView* view)
309{
310 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
311 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
312
313 if (gtk_toggle_button_get_active(widget)) {
314 gtk_widget_show_all(priv->frame_chat);
315 gtk_widget_grab_focus(priv->frame_chat);
316 } else {
317 gtk_widget_hide(priv->frame_chat);
318 }
319}
320
321static gboolean
322on_timer_fade_timeout(CurrentCallView* view)
323{
324 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), G_SOURCE_REMOVE);
325 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
326 priv->cpp->checkControlsFading();
327 return G_SOURCE_CONTINUE;
328}
329
330static void
331on_size_allocate(CurrentCallView* view)
332{
333 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
334 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
335
336 priv->cpp->insertControls();
337}
338
339static void
340on_button_hangup_clicked(CurrentCallView* view)
341{
342 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
343 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
344
345 auto callToHangUp = priv->cpp->conversation->callId;
346 if (!priv->cpp->conversation->confId.empty())
347 callToHangUp = priv->cpp->conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400348 (*priv->cpp->accountInfo)->callModel->hangUp(callToHangUp);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500349}
350
351static void
352on_togglebutton_hold_clicked(CurrentCallView* view)
353{
354 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
355 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
356
357 auto callToHold = priv->cpp->conversation->callId;
358 if (!priv->cpp->conversation->confId.empty())
359 callToHold = priv->cpp->conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400360 (*priv->cpp->accountInfo)->callModel->togglePause(callToHold);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500361}
362
363static void
364on_togglebutton_record_clicked(CurrentCallView* view)
365{
366 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
367 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
368
369 auto callToRecord = priv->cpp->conversation->callId;
370 if (!priv->cpp->conversation->confId.empty())
371 callToRecord = priv->cpp->conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400372 (*priv->cpp->accountInfo)->callModel->toggleAudioRecord(callToRecord);
Sébastien Blin31cc01e2018-06-08 10:51:03 -0400373
374 set_record_animation(priv);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500375}
376
377static void
378on_togglebutton_muteaudio_clicked(CurrentCallView* view)
379{
380 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
381 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
382
383 auto callToMute = priv->cpp->conversation->callId;
384 if (!priv->cpp->conversation->confId.empty())
385 callToMute = priv->cpp->conversation->confId;
386 //auto muteAudioBtn = GTK_TOGGLE_BUTTON(priv->togglebutton_muteaudio);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400387 (*priv->cpp->accountInfo)->callModel->toggleMedia(callToMute,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500388 lrc::api::NewCallModel::Media::AUDIO);
389
390 auto togglebutton = GTK_TOGGLE_BUTTON(priv->togglebutton_muteaudio);
391 auto image = gtk_image_new_from_resource ("/cx/ring/RingGnome/mute_audio");
392 if (gtk_toggle_button_get_active(togglebutton))
393 image = gtk_image_new_from_resource ("/cx/ring/RingGnome/unmute_audio");
394 gtk_button_set_image(GTK_BUTTON(togglebutton), image);
395}
396
397static void
398on_togglebutton_mutevideo_clicked(CurrentCallView* view)
399{
400 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
401 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
402
403 auto callToMute = priv->cpp->conversation->callId;
404 if (!priv->cpp->conversation->confId.empty())
405 callToMute = priv->cpp->conversation->confId;
406 //auto muteVideoBtn = GTK_TOGGLE_BUTTON(priv->togglebutton_mutevideo);
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400407 (*priv->cpp->accountInfo)->callModel->toggleMedia(callToMute,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500408 lrc::api::NewCallModel::Media::VIDEO);
409
410 auto togglebutton = GTK_TOGGLE_BUTTON(priv->togglebutton_mutevideo);
411 auto image = gtk_image_new_from_resource ("/cx/ring/RingGnome/mute_video");
412 if (gtk_toggle_button_get_active(togglebutton))
413 image = gtk_image_new_from_resource ("/cx/ring/RingGnome/unmute_video");
414 gtk_button_set_image(GTK_BUTTON(togglebutton), image);
415}
416
417static gboolean
418on_mouse_moved(CurrentCallView* view)
419{
420 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
421 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
422
423 priv->cpp->time_last_mouse_motion = g_get_monotonic_time();
424
425 // since the mouse moved, make sure the controls are shown
426 if (clutter_timeline_get_direction(CLUTTER_TIMELINE(priv->cpp->fade_info)) == CLUTTER_TIMELINE_FORWARD) {
427 clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->cpp->fade_info), CLUTTER_TIMELINE_BACKWARD);
428 clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->cpp->fade_controls), CLUTTER_TIMELINE_BACKWARD);
429 if (!clutter_timeline_is_playing(CLUTTER_TIMELINE(priv->cpp->fade_info))) {
430 clutter_timeline_rewind(CLUTTER_TIMELINE(priv->cpp->fade_info));
431 clutter_timeline_rewind(CLUTTER_TIMELINE(priv->cpp->fade_controls));
432 clutter_timeline_start(CLUTTER_TIMELINE(priv->cpp->fade_info));
433 clutter_timeline_start(CLUTTER_TIMELINE(priv->cpp->fade_controls));
434 }
435 }
436
437 return FALSE; // propogate event
438}
439
440static void
441on_autoquality_toggled(GtkToggleButton* button, CurrentCallView* view)
442{
443 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
444 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500445
446 gboolean auto_quality_on = gtk_toggle_button_get_active(button);
447
448 auto scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality));
449 auto plus_button = gtk_scale_button_get_plus_button(GTK_SCALE_BUTTON(priv->scalebutton_quality));
450 auto minus_button = gtk_scale_button_get_minus_button(GTK_SCALE_BUTTON(priv->scalebutton_quality));
451
452 gtk_widget_set_sensitive(GTK_WIDGET(scale), !auto_quality_on);
453 gtk_widget_set_sensitive(plus_button, !auto_quality_on);
454 gtk_widget_set_sensitive(minus_button, !auto_quality_on);
Sébastien Blin3667fa62017-11-23 09:11:53 -0500455
456 double desired_quality = gtk_scale_button_get_value(GTK_SCALE_BUTTON(priv->scalebutton_quality));
457
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500458 auto callToRender = priv->cpp->conversation->callId;
459 if (!priv->cpp->conversation->confId.empty())
460 callToRender = priv->cpp->conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400461 auto renderer = (*priv->cpp->accountInfo)->callModel->getRenderer(callToRender);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500462 for (auto* activeCall: CallModel::instance().getActiveCalls()) {
463 if (activeCall and activeCall->videoRenderer() == renderer)
464 set_call_quality(*activeCall, auto_quality_on, desired_quality);
465 }
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500466}
467
468static void
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500469on_quality_changed(G_GNUC_UNUSED GtkScaleButton *button, G_GNUC_UNUSED gdouble value,
470 CurrentCallView* view)
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500471{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500472 g_return_if_fail(IS_CURRENT_CALL_VIEW(view));
473 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500474
475 /* no need to upate quality if auto quality is enabled */
476 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->checkbutton_autoquality))) return;
477
478 /* only update if the scale button is released, to reduce the number of updates */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500479 if (priv->cpp->quality_scale_pressed) return;
Sébastien Blin3667fa62017-11-23 09:11:53 -0500480
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500481 auto callToRender = priv->cpp->conversation->callId;
482 if (!priv->cpp->conversation->confId.empty())
483 callToRender = priv->cpp->conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400484 auto renderer = (*priv->cpp->accountInfo)->callModel->getRenderer(callToRender);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500485 for (auto* activeCall: CallModel::instance().getActiveCalls())
486 if (activeCall and activeCall->videoRenderer() == renderer)
487 set_call_quality(*activeCall, false, gtk_scale_button_get_value(button));
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500488}
489
490static gboolean
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500491on_quality_button_pressed(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event,
492 CurrentCallView* view)
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500493{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500494 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
495 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
496
497 priv->cpp->quality_scale_pressed = TRUE;
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500498
499 return GDK_EVENT_PROPAGATE;
500}
501
502static gboolean
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500503on_quality_button_released(G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEvent *event,
504 CurrentCallView* view)
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500505{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500506 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
507 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
508
509 priv->cpp->quality_scale_pressed = FALSE;
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500510
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400511 // now make sure the quality gets updated
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500512 on_quality_changed(GTK_SCALE_BUTTON(priv->scalebutton_quality), 0, view);
513
514 return GDK_EVENT_PROPAGATE;
515}
516
517static gboolean
518on_video_widget_focus(GtkWidget* widget, GtkDirectionType direction, CurrentCallView* view)
519{
520 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
521 auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
522
523 // if this widget already has focus, we want the focus to move to the next widget, otherwise we
524 // will get stuck in a focus loop on the buttons
525 if (gtk_widget_has_focus(widget))
526 return FALSE;
527
528 // otherwise we want the focus to go to and change between the call control buttons
529 if (gtk_widget_child_focus(GTK_WIDGET(priv->hbox_call_controls), direction)) {
530 // selected a child, make sure call controls are shown
531 on_mouse_moved(view);
532 return TRUE;
533 }
534
535 // did not select the next child, propogate the event
536 return FALSE;
537}
538
539static gboolean
540on_button_press_in_video_event(GtkWidget* widget, GdkEventButton *event, CurrentCallView* view)
541{
542 g_return_val_if_fail(IS_VIDEO_WIDGET(widget), FALSE);
543 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(view), FALSE);
544
545 // on double click
546 if (event->type == GDK_2BUTTON_PRESS) {
547 g_debug("double click in video");
548 g_signal_emit(G_OBJECT(view), current_call_view_signals[VIDEO_DOUBLE_CLICKED], 0);
549 }
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500550
551 return GDK_EVENT_PROPAGATE;
552}
553
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400554static void
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500555on_toggle_smartinfo(GSimpleAction* action, G_GNUC_UNUSED GVariant* state, GtkWidget* vbox_call_smartInfo)
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400556{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500557 if (g_variant_get_boolean(g_action_get_state(G_ACTION(action)))) {
558 gtk_widget_show(vbox_call_smartInfo);
559 } else {
560 gtk_widget_hide(vbox_call_smartInfo);
561 }
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400562}
563
Sébastien Blin784f2a32018-05-30 17:31:13 -0400564static void
565transfer_to_peer(CurrentCallViewPrivate* priv, const std::string& peerUri)
566{
567 if (peerUri == priv->cpp->conversation->participants.front()) {
568 g_warning("avoid to transfer to the same call, abort.");
569#if GTK_CHECK_VERSION(3,22,0)
570 gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
571#else
572 gtk_widget_hide(GTK_WIDGET(priv->siptransfer_popover));
573#endif
574 return;
575 }
576 try {
577 // If a call is already present with a peer, try an attended transfer.
578 auto callInfo = (*priv->cpp->accountInfo)->callModel->getCallFromURI(peerUri, true);
579 (*priv->cpp->accountInfo)->callModel->transferToCall(
580 priv->cpp->conversation->callId, callInfo.id);
581 } catch (std::out_of_range&) {
582 // No current call found with this URI, perform a blind transfer
583 (*priv->cpp->accountInfo)->callModel->transfer(
584 priv->cpp->conversation->callId, peerUri);
585 }
586#if GTK_CHECK_VERSION(3,22,0)
587 gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
588#else
589 gtk_widget_hide(GTK_WIDGET(priv->siptransfer_popover));
590#endif
591}
592
593static void
594on_siptransfer_filter_activated(CurrentCallView* self)
595{
596 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
597 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
598
599 transfer_to_peer(priv, gtk_entry_get_text(GTK_ENTRY(priv->siptransfer_filter_entry)));
600}
601
602static GtkLabel*
603get_sip_address_label(GtkListBoxRow* row)
604{
605 auto* row_children = gtk_container_get_children(GTK_CONTAINER(row));
606 auto* box_infos = g_list_first(row_children)->data;
607 auto* children = gtk_container_get_children(GTK_CONTAINER(box_infos));
608 return GTK_LABEL(g_list_last(children)->data);
609}
610
611static void
612transfer_to_conversation(GtkListBox*, GtkListBoxRow* row, CurrentCallView* self)
613{
614 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
615 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
616 auto* sip_address = get_sip_address_label(row);
617 transfer_to_peer(priv, gtk_label_get_text(GTK_LABEL(sip_address)));
618}
619
620static void
621filter_transfer_list(CurrentCallView *self)
622{
623 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
624 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
625
626 std::string currentFilter = gtk_entry_get_text(GTK_ENTRY(priv->siptransfer_filter_entry));
627
628 auto row = 0;
629 while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(priv->list_conversations), row))) {
630 auto* sip_address = get_sip_address_label(GTK_LIST_BOX_ROW(children));;
631 if (row == 0) {
632 // Update searching item
633 if (currentFilter.empty() || currentFilter == priv->cpp->conversation->participants.front()) {
634 // Hide temporary item if filter is empty or same number
635 gtk_widget_hide(children);
636 } else {
637 // Else, show the temporary item (and select it)
638 gtk_label_set_text(GTK_LABEL(sip_address), currentFilter.c_str());
639 gtk_widget_show_all(children);
640 gtk_list_box_select_row(GTK_LIST_BOX(priv->list_conversations), GTK_LIST_BOX_ROW(children));
641 }
642 } else {
643 // It's a contact
644 std::string item_address = gtk_label_get_text(GTK_LABEL(sip_address));
645
646 if (item_address == priv->cpp->conversation->participants.front())
647 // if item is the current conversation, hide it
648 gtk_widget_hide(children);
649 else if (currentFilter.empty())
650 // filter is empty, show all items
651 gtk_widget_show_all(children);
652 else if (item_address.find(currentFilter) == std::string::npos || item_address == currentFilter)
653 // avoid duplicates and unwanted numbers
654 gtk_widget_hide(children);
655 else
656 // Item is filtered
657 gtk_widget_show_all(children);
658 }
659 ++row;
660 }
661}
662
663static void
664on_button_transfer_clicked(CurrentCallView *self)
665{
666 // Show and init list
667 g_return_if_fail(IS_CURRENT_CALL_VIEW(self));
668 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
669 gtk_popover_set_relative_to(GTK_POPOVER(priv->siptransfer_popover), GTK_WIDGET(priv->togglebutton_transfer));
670#if GTK_CHECK_VERSION(3,22,0)
671 gtk_popover_popdown(GTK_POPOVER(priv->siptransfer_popover));
672#else
673 gtk_widget_show_all(GTK_WIDGET(priv->siptransfer_popover));
674#endif
675 gtk_widget_show_all(priv->siptransfer_popover);
676 filter_transfer_list(self);
677}
678
679static void
680on_siptransfer_text_changed(GtkSearchEntry*, CurrentCallView* self)
681{
682 filter_transfer_list(self);
683}
684
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500685} // namespace gtk_callbacks
686
687CppImpl::CppImpl(CurrentCallView& widget)
688 : self {&widget}
689 , widgets {CURRENT_CALL_VIEW_GET_PRIVATE(&widget)}
690{}
691
692CppImpl::~CppImpl()
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400693{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500694 QObject::disconnect(state_change_connection);
695 QObject::disconnect(local_renderer_connection);
696 QObject::disconnect(remote_renderer_connection);
697 QObject::disconnect(smartinfo_refresh_connection);
698 QObject::disconnect(new_message_connection);
699 g_clear_object(&widgets->settings);
700
701 g_source_remove(timer_fade);
702
703 auto* display_smartinfo = g_action_map_lookup_action(G_ACTION_MAP(g_application_get_default()),
704 "display-smartinfo");
705 g_signal_handler_disconnect(display_smartinfo, smartinfo_action);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400706}
707
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500708void
709CppImpl::init()
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400710{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500711 // CSS styles
712 auto provider = gtk_css_provider_new();
713 gtk_css_provider_load_from_data(provider,
Sébastien Blin784f2a32018-05-30 17:31:13 -0400714 ".search-entry-style { border: 0; border-radius: 0; } \
715 .smartinfo-block-style { color: #8ae234; background-color: rgba(1, 1, 1, 0.33); } \
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500716 @keyframes blink { 0% {opacity: 1;} 49% {opacity: 1;} 50% {opacity: 0;} 100% {opacity: 0;} } \
717 .record-button { background: rgba(0, 0, 0, 1); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
718 .record-button:checked { animation: blink 1s; animation-iteration-count: infinite; } \
719 .call-button { background: rgba(0, 0, 0, 0.35); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
720 .call-button:hover { background: rgba(0, 0, 0, 0.2); } \
721 .call-button:disabled { opacity: 0.2; } \
722 .can-be-disabled:checked { background: rgba(219, 58, 55, 1); } \
723 .hangup-button-style { background: rgba(219, 58, 55, 1); border-radius: 50%; border: 0; transition: all 0.3s ease; } \
724 .hangup-button-style:hover { background: rgba(219, 39, 25, 1); }",
725 -1, nullptr
726 );
727 gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
728 GTK_STYLE_PROVIDER(provider),
729 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
730
731 widgets->video_widget = video_widget_new();
732 gtk_container_add(GTK_CONTAINER(widgets->frame_video), widgets->video_widget);
733 gtk_widget_show_all(widgets->frame_video);
734
735 // add the overlay controls only once the view has been allocated a size to prevent size
736 // allocation warnings in the log
737 insert_controls_id = g_signal_connect(self, "size-allocate", G_CALLBACK(on_size_allocate), nullptr);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400738}
739
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500740void
741CppImpl::setup(WebKitChatContainer* chat_widget,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400742 AccountInfoPointer const & account_info,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500743 lrc::api::conversation::Info* conv_info)
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400744{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500745 widgets->webkit_chat_container = GTK_WIDGET(chat_widget);
746 conversation = conv_info;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400747 accountInfo = &account_info;
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500748 setCallInfo();
Sébastien Blin784f2a32018-05-30 17:31:13 -0400749
750 if ((*accountInfo)->profileInfo.type == lrc::api::profile::Type::RING)
751 gtk_widget_hide(widgets->togglebutton_transfer);
752 else {
753 // Remove previous list
754 while (GtkWidget* children = GTK_WIDGET(gtk_list_box_get_row_at_index(GTK_LIST_BOX(widgets->list_conversations), 10)))
755 gtk_container_remove(GTK_CONTAINER(widgets->list_conversations), children);
756 // Fill with SIP contacts
757 add_transfer_contact(""); // Temporary item
758 for (const auto& c : (*accountInfo)->conversationModel->getFilteredConversations(lrc::api::profile::Type::SIP))
759 add_transfer_contact(c.participants.front());
760 gtk_widget_show_all(widgets->list_conversations);
761 gtk_widget_show(widgets->togglebutton_transfer);
762 }
Sébastien Blin31cc01e2018-06-08 10:51:03 -0400763
764 set_record_animation(widgets);
Sébastien Blin784f2a32018-05-30 17:31:13 -0400765}
766
767void
768CppImpl::add_transfer_contact(const std::string& uri)
769{
770 auto* box_item = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
771 auto pixbufmanipulator = Interfaces::PixbufManipulator();
772 auto image_buf = pixbufmanipulator.generateAvatar("", uri.empty() ? uri : "sip" + uri);
773 auto scaled = pixbufmanipulator.scaleAndFrame(image_buf.get(), QSize(48, 48));
774 auto* avatar = gtk_image_new_from_pixbuf(scaled.get());
775 auto* address = gtk_label_new(uri.c_str());
776 gtk_container_add(GTK_CONTAINER(box_item), GTK_WIDGET(avatar));
777 gtk_container_add(GTK_CONTAINER(box_item), GTK_WIDGET(address));
778 gtk_list_box_insert(GTK_LIST_BOX(widgets->list_conversations), GTK_WIDGET(box_item), -1);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400779}
780
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500781void
782CppImpl::setCallInfo()
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400783{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500784 // change some things depending on call state
785 updateState();
786 updateDetails();
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400787
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500788 // NOTE/TODO we need to rewrite the video_widget file to use the new LRC.
789 g_signal_connect(widgets->video_widget, "button-press-event",
790 G_CALLBACK(video_widget_on_button_press_in_screen_event), nullptr);
791
792 // check if we already have a renderer
793 auto callToRender = conversation->callId;
794 if (!conversation->confId.empty())
795 callToRender = conversation->confId;
796 video_widget_push_new_renderer(VIDEO_WIDGET(widgets->video_widget),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400797 (*accountInfo)->callModel->getRenderer(callToRender),
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500798 VIDEO_RENDERER_REMOTE);
799
800 // local renderer
801 if (Video::PreviewManager::instance().isPreviewing())
802 video_widget_push_new_renderer(VIDEO_WIDGET(widgets->video_widget),
803 Video::PreviewManager::instance().previewRenderer(),
804 VIDEO_RENDERER_LOCAL);
805
806 // callback for local renderer
807 local_renderer_connection = QObject::connect(
808 &Video::PreviewManager::instance(),
809 &Video::PreviewManager::previewStarted,
810 [this] (Video::Renderer* renderer) {
811 video_widget_push_new_renderer(VIDEO_WIDGET(widgets->video_widget),
812 renderer,
813 VIDEO_RENDERER_LOCAL);
814 }
815 );
816
817 smartinfo_refresh_connection = QObject::connect(
818 &SmartInfoHub::instance(),
819 &SmartInfoHub::changed,
820 [this] { updateSmartInfo(); }
821 );
822
823 remote_renderer_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400824 &*(*accountInfo)->callModel,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500825 &lrc::api::NewCallModel::remotePreviewStarted,
826 [this] (const std::string& callId, Video::Renderer* renderer) {
827 if (conversation->callId == callId) {
828 video_widget_push_new_renderer(VIDEO_WIDGET(widgets->video_widget),
829 renderer,
830 VIDEO_RENDERER_REMOTE);
831 }
832 });
833
834 state_change_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400835 &*(*accountInfo)->callModel,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500836 &lrc::api::NewCallModel::callStatusChanged,
837 [this] (const std::string& callId) {
838 if (callId == conversation->callId) {
839 updateState();
840 updateNameAndPhoto();
841 }
842 });
843
844 new_message_connection = QObject::connect(
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400845 &*(*accountInfo)->conversationModel,
Nicolas Jager6635b0d2018-01-24 12:25:28 -0500846 &lrc::api::ConversationModel::newInteraction,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500847 [this] (const std::string& uid, uint64_t msgId, lrc::api::interaction::Info msg) {
848 Q_UNUSED(uid)
849 Q_UNUSED(msgId)
850 Q_UNUSED(msg)
851 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets->togglebutton_chat), TRUE);
852 });
853
854 // catch double click to make full screen
855 g_signal_connect(widgets->video_widget, "button-press-event",
856 G_CALLBACK(on_button_press_in_video_event), self);
857
858 // handle smartinfo in right click menu
859 auto display_smartinfo = g_action_map_lookup_action(G_ACTION_MAP(g_application_get_default()),
860 "display-smartinfo");
861 smartinfo_action = g_signal_connect(display_smartinfo,
862 "notify::state",
863 G_CALLBACK(on_toggle_smartinfo),
864 widgets->vbox_call_smartInfo);
865
866 // init chat view
867 widgets->chat_view = chat_view_new(WEBKIT_CHAT_CONTAINER(widgets->webkit_chat_container),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400868 *accountInfo, conversation);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500869 gtk_container_add(GTK_CONTAINER(widgets->frame_chat), widgets->chat_view);
870
871 g_signal_connect_swapped(widgets->chat_view, "new-interactions-displayed",
872 G_CALLBACK(on_new_chat_interactions), self);
873 chat_view_set_header_visible(CHAT_VIEW(widgets->chat_view), FALSE);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400874}
875
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500876void
877CppImpl::insertControls()
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500878{
Stepan Salenikovich88092932017-05-15 18:19:00 -0400879 /* only add the controls once */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500880 g_signal_handler_disconnect(self, insert_controls_id);
881 insert_controls_id = 0;
Stepan Salenikoviche178e632015-11-06 13:31:19 -0500882
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500883 auto stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(widgets->video_widget));
884 auto actor_info = gtk_clutter_actor_new_with_contents(widgets->hbox_call_info);
885 auto actor_controls = gtk_clutter_actor_new_with_contents(widgets->hbox_call_controls);
886 auto actor_smartInfo = gtk_clutter_actor_new_with_contents(widgets->vbox_call_smartInfo);
Stepan Salenikoviche178e632015-11-06 13:31:19 -0500887
888 clutter_actor_add_child(stage, actor_info);
889 clutter_actor_set_x_align(actor_info, CLUTTER_ACTOR_ALIGN_FILL);
890 clutter_actor_set_y_align(actor_info, CLUTTER_ACTOR_ALIGN_START);
891
892 clutter_actor_add_child(stage, actor_controls);
893 clutter_actor_set_x_align(actor_controls, CLUTTER_ACTOR_ALIGN_CENTER);
894 clutter_actor_set_y_align(actor_controls, CLUTTER_ACTOR_ALIGN_END);
895
Olivier Gregoire66e4df72016-06-17 18:39:05 -0400896 clutter_actor_add_child(stage, actor_smartInfo);
897 clutter_actor_set_x_align(actor_smartInfo, CLUTTER_ACTOR_ALIGN_END);
898 clutter_actor_set_y_align(actor_smartInfo, CLUTTER_ACTOR_ALIGN_START);
899 ClutterMargin clutter_margin_smartInfo;
900 clutter_margin_smartInfo.top = 50;
901 clutter_margin_smartInfo.right = 10;
philippegorleya7337942017-07-04 15:29:42 -0400902 clutter_margin_smartInfo.left = 10;
903 clutter_margin_smartInfo.bottom = 10;
Olivier Gregoire66e4df72016-06-17 18:39:05 -0400904 clutter_actor_set_margin (actor_smartInfo, &clutter_margin_smartInfo);
905
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500906 /* add fade in and out states to the info and controls */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500907 time_last_mouse_motion = g_get_monotonic_time();
908 fade_info = create_fade_out_transition();
909 fade_controls = create_fade_out_transition();
910 clutter_actor_add_transition(actor_info, "fade_info", fade_info);
911 clutter_actor_add_transition(actor_controls, "fade_controls", fade_controls);
912 clutter_timeline_set_direction(CLUTTER_TIMELINE(fade_info), CLUTTER_TIMELINE_BACKWARD);
913 clutter_timeline_set_direction(CLUTTER_TIMELINE(fade_controls), CLUTTER_TIMELINE_BACKWARD);
914 clutter_timeline_stop(CLUTTER_TIMELINE(fade_info));
915 clutter_timeline_stop(CLUTTER_TIMELINE(fade_controls));
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500916
917 /* have a timer check every 1 second if the controls should fade out */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500918 timer_fade = g_timeout_add(1000, (GSourceFunc)on_timer_fade_timeout, self);
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500919
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400920 /* connect the controllers (new model) */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500921 g_signal_connect_swapped(widgets->button_hangup, "clicked", G_CALLBACK(on_button_hangup_clicked), self);
Sébastien Blin784f2a32018-05-30 17:31:13 -0400922 g_signal_connect_swapped(widgets->togglebutton_transfer, "clicked", G_CALLBACK(on_button_transfer_clicked), self);
923 g_signal_connect_swapped(widgets->siptransfer_filter_entry, "activate", G_CALLBACK(on_siptransfer_filter_activated), self);
924 g_signal_connect(widgets->siptransfer_filter_entry, "search-changed", G_CALLBACK(on_siptransfer_text_changed), self);
925 g_signal_connect(widgets->list_conversations, "row-activated", G_CALLBACK(transfer_to_conversation), self);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500926 g_signal_connect_swapped(widgets->togglebutton_hold, "clicked", G_CALLBACK(on_togglebutton_hold_clicked), self);
927 g_signal_connect_swapped(widgets->togglebutton_muteaudio, "clicked", G_CALLBACK(on_togglebutton_muteaudio_clicked), self);
928 g_signal_connect_swapped(widgets->togglebutton_record, "clicked", G_CALLBACK(on_togglebutton_record_clicked), self);
929 g_signal_connect_swapped(widgets->togglebutton_mutevideo, "clicked", G_CALLBACK(on_togglebutton_mutevideo_clicked), self);
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400930
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500931 /* connect to the mouse motion event to reset the last moved time */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500932 g_signal_connect_swapped(widgets->video_widget, "motion-notify-event", G_CALLBACK(on_mouse_moved), self);
933 g_signal_connect_swapped(widgets->video_widget, "button-press-event", G_CALLBACK(on_mouse_moved), self);
934 g_signal_connect_swapped(widgets->video_widget, "button-release-event", G_CALLBACK(on_mouse_moved), self);
Stepan Salenikovich0c7aa2a2015-11-06 17:00:08 -0500935
Stepan Salenikovich5ed1b492015-11-13 14:03:31 -0500936 /* manually handle the focus of the video widget to be able to focus on the call controls */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500937 g_signal_connect(widgets->video_widget, "focus", G_CALLBACK(on_video_widget_focus), self);
Stepan Salenikovich5ed1b492015-11-13 14:03:31 -0500938
Sébastien Blin55bff9d2017-10-03 15:15:23 -0400939
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500940 /* toggle whether or not the chat is displayed */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500941 g_signal_connect(widgets->togglebutton_chat, "toggled", G_CALLBACK(on_togglebutton_chat_toggled), self);
Stepan Salenikovicha448f602015-05-29 13:33:06 -0400942
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500943 /* bind the chat orientation to the gsetting */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500944 widgets->settings = g_settings_new_full(get_ring_schema(), nullptr, nullptr);
945 g_settings_bind_with_mapping(widgets->settings, "chat-pane-horizontal",
946 widgets->paned_call, "orientation",
Stepan Salenikovicha5e8e362015-11-05 16:50:48 -0500947 G_SETTINGS_BIND_GET,
948 map_boolean_to_orientation,
949 nullptr, nullptr, nullptr);
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500950
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500951 g_signal_connect(widgets->scalebutton_quality, "value-changed", G_CALLBACK(on_quality_changed), self);
952
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500953 /* customize the quality button scale */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500954 if (auto scale_box = gtk_scale_button_get_box(GTK_SCALE_BUTTON(widgets->scalebutton_quality))) {
955 widgets->checkbutton_autoquality = gtk_check_button_new_with_label(C_("Enable automatic video quality",
956 "Auto"));
957 gtk_widget_show(widgets->checkbutton_autoquality);
958 gtk_box_pack_start(GTK_BOX(scale_box), widgets->checkbutton_autoquality, FALSE, TRUE, 0);
959 g_signal_connect(widgets->checkbutton_autoquality, "toggled", G_CALLBACK(on_autoquality_toggled), self);
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500960 }
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500961 if (auto scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(widgets->scalebutton_quality))) {
962 g_signal_connect(scale, "button-press-event", G_CALLBACK(on_quality_button_pressed), self);
963 g_signal_connect(scale, "button-release-event", G_CALLBACK(on_quality_button_released), self);
Stepan Salenikovich7e283552015-12-21 16:17:52 -0500964 }
Stepan Salenikovich88092932017-05-15 18:19:00 -0400965
Sébastien Blin3667fa62017-11-23 09:11:53 -0500966 /* by this time we should have the call already set, but we check to make sure */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500967 auto callToRender = conversation->callId;
968 if (!conversation->confId.empty())
969 callToRender = conversation->confId;
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -0400970 auto renderer = (*accountInfo)->callModel->getRenderer(callToRender);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500971 for (auto* activeCall: CallModel::instance().getActiveCalls())
972 if (activeCall and activeCall->videoRenderer() == renderer) {
973 g_signal_connect(widgets->video_widget, "drag-data-received",
974 G_CALLBACK(video_widget_on_drag_data_received), activeCall);
Sébastien Blin3667fa62017-11-23 09:11:53 -0500975 /* check if auto quality is enabled or not */
976 if (const auto& codecModel = activeCall->account()->codecModel()) {
977 const auto& videoCodecs = codecModel->videoCodecs();
978 if (videoCodecs->rowCount() > 0) {
979 /* we only need to check the first codec since by default it is ON for all, and the
980 * gnome client sets its ON or OFF for all codecs as well */
981 const auto& idx = videoCodecs->index(0,0);
982 auto auto_quality_enabled = idx.data(static_cast<int>(CodecModel::Role::AUTO_QUALITY_ENABLED)).toString() == "true";
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500983 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets->checkbutton_autoquality),
984 auto_quality_enabled);
Sébastien Blin3667fa62017-11-23 09:11:53 -0500985
986 // TODO: save the manual quality setting in the client and set the slider to that value here;
987 // the daemon resets the bitrate/quality between each call, and the default may be
988 // different for each codec, so there is no reason to check it here
989 }
990 }
991 } else {
992 /* Auto-quality is off by default */
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500993 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets->checkbutton_autoquality), FALSE);
Sébastien Blin3667fa62017-11-23 09:11:53 -0500994 }
995
Sébastien Blin4514eeb2017-07-25 14:17:01 -0400996 // Get if the user wants to show the smartInfo box
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -0500997 auto display_smartinfo = g_action_map_lookup_action(G_ACTION_MAP(g_application_get_default()),
998 "display-smartinfo");
Sébastien Blin4514eeb2017-07-25 14:17:01 -0400999 if (g_variant_get_boolean(g_action_get_state(G_ACTION(display_smartinfo)))) {
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001000 gtk_widget_show(widgets->vbox_call_smartInfo);
Sébastien Blin4514eeb2017-07-25 14:17:01 -04001001 } else {
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001002 gtk_widget_hide(widgets->vbox_call_smartInfo);
Sébastien Blin4514eeb2017-07-25 14:17:01 -04001003 }
Stepan Salenikovich88092932017-05-15 18:19:00 -04001004}
1005
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001006void
1007CppImpl::updateDetails()
1008{
1009 auto callRendered = conversation->callId;
1010
1011 if (!conversation->confId.empty())
1012 callRendered = conversation->confId;
1013
1014 gtk_label_set_text(GTK_LABEL(widgets->label_duration),
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001015 (*accountInfo)->callModel->getFormattedCallDuration(callRendered).c_str());
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001016
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001017 auto call = (*accountInfo)->callModel->getCall(callRendered);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001018 gtk_widget_set_sensitive(GTK_WIDGET(widgets->togglebutton_muteaudio),
1019 (call.type != lrc::api::call::Type::CONFERENCE));
1020 gtk_widget_set_sensitive(GTK_WIDGET(widgets->togglebutton_mutevideo),
1021 (call.type != lrc::api::call::Type::CONFERENCE));
1022}
1023
1024void
1025CppImpl::updateState()
1026{
1027 if (conversation) return;
1028
1029 auto callId = conversation->callId;
1030
1031 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001032 auto call = (*accountInfo)->callModel->getCall(callId);
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001033
1034 auto pauseBtn = GTK_TOGGLE_BUTTON(widgets->togglebutton_hold);
1035 auto image = gtk_image_new_from_resource ("/cx/ring/RingGnome/pause");
1036 if (call.status == lrc::api::call::Status::PAUSED)
1037 image = gtk_image_new_from_resource ("/cx/ring/RingGnome/play");
1038 gtk_button_set_image(GTK_BUTTON(pauseBtn), image);
1039
1040 auto audioButton = GTK_TOGGLE_BUTTON(widgets->togglebutton_muteaudio);
1041 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets->togglebutton_muteaudio), call.audioMuted);
1042 auto imageMuteAudio = gtk_image_new_from_resource ("/cx/ring/RingGnome/mute_audio");
1043 if (call.audioMuted)
1044 imageMuteAudio = gtk_image_new_from_resource ("/cx/ring/RingGnome/unmute_audio");
1045 gtk_button_set_image(GTK_BUTTON(audioButton), imageMuteAudio);
1046
1047 auto videoButton = GTK_TOGGLE_BUTTON(widgets->togglebutton_mutevideo);
1048 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets->togglebutton_mutevideo), call.videoMuted);
1049 auto imageMuteVideo = gtk_image_new_from_resource ("/cx/ring/RingGnome/mute_video");
1050 if (call.videoMuted)
1051 imageMuteVideo = gtk_image_new_from_resource ("/cx/ring/RingGnome/unmute_video");
1052 gtk_button_set_image(GTK_BUTTON(videoButton), imageMuteVideo);
1053
1054 gchar *status = g_strdup_printf("%s", lrc::api::call::to_string(call.status).c_str());
1055 gtk_label_set_text(GTK_LABEL(widgets->label_status), status);
1056 g_free(status);
1057 } catch (std::out_of_range& e) {
1058 g_warning("Can't update state for callId=%s", callId.c_str());
1059 }
1060}
1061
1062void
1063CppImpl::updateNameAndPhoto()
1064{
1065 QVariant var_i = GlobalInstances::pixmapManipulator().conversationPhoto(
1066 *conversation,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001067 **(accountInfo),
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001068 QSize(60, 60),
1069 false
1070 );
1071 std::shared_ptr<GdkPixbuf> image = var_i.value<std::shared_ptr<GdkPixbuf>>();
1072 gtk_image_set_from_pixbuf(GTK_IMAGE(widgets->image_peer), image.get());
1073
1074 try {
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001075 auto contactInfo = (*accountInfo)->contactModel->getContact(conversation->participants.front());
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001076 auto name = contactInfo.profileInfo.alias;
1077 gtk_label_set_text(GTK_LABEL(widgets->label_name), name.c_str());
1078
1079 auto bestId = contactInfo.registeredName;
1080 if (name != bestId) {
1081 gtk_label_set_text(GTK_LABEL(widgets->label_bestId), bestId.c_str());
1082 gtk_widget_show(widgets->label_bestId);
1083 }
1084 } catch (const std::out_of_range&) {
1085 // ContactModel::getContact() exception
1086 }
1087}
1088
1089void
1090CppImpl::updateSmartInfo()
1091{
1092 if (!SmartInfoHub::instance().isConference()) {
1093 gchar* general_information = g_strdup_printf(
1094 "Call ID: %s", SmartInfoHub::instance().callID().toStdString().c_str());
1095 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_general_information), general_information);
1096 g_free(general_information);
1097
1098 gchar* description = g_strdup_printf("You\n"
1099 "Framerate:\n"
1100 "Video codec:\n"
1101 "Audio codec:\n"
1102 "Resolution:\n\n"
1103 "Peer\n"
1104 "Framerate:\n"
1105 "Video codec:\n"
1106 "Audio codec:\n"
1107 "Resolution:");
1108 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_description),description);
1109 g_free(description);
1110
1111 gchar* value = g_strdup_printf("\n%f\n%s\n%s\n%dx%d\n\n\n%f\n%s\n%s\n%dx%d",
1112 (double)SmartInfoHub::instance().localFps(),
1113 SmartInfoHub::instance().localVideoCodec().toStdString().c_str(),
1114 SmartInfoHub::instance().localAudioCodec().toStdString().c_str(),
1115 SmartInfoHub::instance().localWidth(),
1116 SmartInfoHub::instance().localHeight(),
1117 (double)SmartInfoHub::instance().remoteFps(),
1118 SmartInfoHub::instance().remoteVideoCodec().toStdString().c_str(),
1119 SmartInfoHub::instance().remoteAudioCodec().toStdString().c_str(),
1120 SmartInfoHub::instance().remoteWidth(),
1121 SmartInfoHub::instance().remoteHeight());
1122 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_value),value);
1123 g_free(value);
1124 } else {
1125 gchar* general_information = g_strdup_printf(
1126 "Conference ID: %s", SmartInfoHub::instance().callID().toStdString().c_str());
1127 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_general_information), general_information);
1128 g_free(general_information);
1129
1130 gchar* description = g_strdup_printf("You\n"
1131 "Framerate:\n"
1132 "Video codec:\n"
1133 "Audio codec:\n"
1134 "Resolution:");
1135 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_description),description);
1136 g_free(description);
1137
1138 gchar* value = g_strdup_printf("\n%f\n%s\n%s\n%dx%d",
1139 (double)SmartInfoHub::instance().localFps(),
1140 SmartInfoHub::instance().localVideoCodec().toStdString().c_str(),
1141 SmartInfoHub::instance().localAudioCodec().toStdString().c_str(),
1142 SmartInfoHub::instance().localWidth(),
1143 SmartInfoHub::instance().localHeight());
1144 gtk_label_set_text(GTK_LABEL(widgets->label_smartinfo_value),value);
1145 g_free(value);
1146 }
1147}
1148
1149void
1150CppImpl::checkControlsFading()
1151{
1152 auto current_time = g_get_monotonic_time();
1153 if (current_time - time_last_mouse_motion >= CONTROLS_FADE_TIMEOUT) {
1154 // timeout has passed, hide the controls
1155 if (clutter_timeline_get_direction(CLUTTER_TIMELINE(fade_info)) == CLUTTER_TIMELINE_BACKWARD) {
1156 clutter_timeline_set_direction(CLUTTER_TIMELINE(fade_info), CLUTTER_TIMELINE_FORWARD);
1157 clutter_timeline_set_direction(CLUTTER_TIMELINE(fade_controls), CLUTTER_TIMELINE_FORWARD);
1158 if (!clutter_timeline_is_playing(CLUTTER_TIMELINE(fade_info))) {
1159 clutter_timeline_rewind(CLUTTER_TIMELINE(fade_info));
1160 clutter_timeline_rewind(CLUTTER_TIMELINE(fade_controls));
1161 clutter_timeline_start(CLUTTER_TIMELINE(fade_info));
1162 clutter_timeline_start(CLUTTER_TIMELINE(fade_controls));
1163 }
1164 }
1165 }
1166
1167 updateDetails();
1168}
1169
1170}} // namespace <anonymous>::details
1171
1172//==============================================================================
1173
1174lrc::api::conversation::Info
1175current_call_view_get_conversation(CurrentCallView *self)
1176{
1177 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), lrc::api::conversation::Info());
1178 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
1179 return *priv->cpp->conversation;
1180}
1181
1182GtkWidget *
1183current_call_view_get_chat_view(CurrentCallView *self)
1184{
1185 g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), nullptr);
1186 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
1187 return priv->chat_view;
1188}
1189
1190//==============================================================================
1191
Stepan Salenikovich88092932017-05-15 18:19:00 -04001192static void
1193current_call_view_init(CurrentCallView *view)
1194{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001195 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich88092932017-05-15 18:19:00 -04001196 gtk_widget_init_template(GTK_WIDGET(view));
1197
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001198 // CppImpl ctor
1199 priv->cpp = new details::CppImpl {*view};
1200 priv->cpp->init();
1201}
Stepan Salenikovich88092932017-05-15 18:19:00 -04001202
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001203static void
1204current_call_view_dispose(GObject *object)
1205{
1206 auto* view = CURRENT_CALL_VIEW(object);
1207 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich88092932017-05-15 18:19:00 -04001208
Hugo Lefeuvreedad8832018-05-14 16:36:06 -04001209 // navbar was hidden during setCallInfo, we need to make it visible again before view destruction
1210 auto children = gtk_container_get_children(GTK_CONTAINER(priv->frame_chat));
1211 auto chat_view = children->data;
1212 chat_view_set_header_visible(CHAT_VIEW(chat_view), TRUE);
1213
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001214 delete priv->cpp;
1215 priv->cpp = nullptr;
Stepan Salenikovich88092932017-05-15 18:19:00 -04001216
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001217 G_OBJECT_CLASS(current_call_view_parent_class)->dispose(object);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001218}
1219
1220static void
1221current_call_view_class_init(CurrentCallViewClass *klass)
1222{
1223 G_OBJECT_CLASS(klass)->dispose = current_call_view_dispose;
1224
1225 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
1226 "/cx/ring/RingGnome/currentcallview.ui");
1227
Stepan Salenikoviche178e632015-11-06 13:31:19 -05001228 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, hbox_call_info);
1229 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, hbox_call_controls);
Olivier Gregoire66e4df72016-06-17 18:39:05 -04001230 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, vbox_call_smartInfo);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001231 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, image_peer);
Stepan Salenikovich07107e92016-05-06 10:35:17 -04001232 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_name);
Nicolas Jager2e467c32017-01-18 08:52:23 -05001233 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_bestId);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001234 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_status);
1235 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_duration);
Olivier Gregoire66e4df72016-06-17 18:39:05 -04001236 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_smartinfo_description);
1237 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_smartinfo_value);
1238 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, label_smartinfo_general_information);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05001239 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, paned_call);
Stepan Salenikovich36c025c2015-03-03 19:06:44 -05001240 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, frame_video);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05001241 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, frame_chat);
Stepan Salenikovicha448f602015-05-29 13:33:06 -04001242 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_chat);
Sébastien Blin784f2a32018-05-30 17:31:13 -04001243 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_transfer);
AmarOke7c02972017-07-17 15:21:20 -04001244 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_hold);
1245 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_muteaudio);
Sébastien Blin55bff9d2017-10-03 15:15:23 -04001246 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_record);
AmarOke7c02972017-07-17 15:21:20 -04001247 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, togglebutton_mutevideo);
Stepan Salenikovich77baa522015-07-07 15:29:14 -04001248 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, button_hangup);
Stepan Salenikovich7e283552015-12-21 16:17:52 -05001249 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, scalebutton_quality);
Sébastien Blin784f2a32018-05-30 17:31:13 -04001250 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, siptransfer_popover);
1251 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, siptransfer_filter_entry);
1252 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), CurrentCallView, list_conversations);
Stepan Salenikoviche1b54892015-12-13 22:18:44 -05001253
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001254 details::current_call_view_signals[VIDEO_DOUBLE_CLICKED] = g_signal_new (
Stepan Salenikoviche1b54892015-12-13 22:18:44 -05001255 "video-double-clicked",
1256 G_TYPE_FROM_CLASS(klass),
1257 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
1258 0,
1259 nullptr,
1260 nullptr,
1261 g_cclosure_marshal_VOID__VOID,
1262 G_TYPE_NONE, 0);
Stepan Salenikovichc64523b2015-02-27 16:31:00 -05001263}
1264
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001265GtkWidget *
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001266current_call_view_new(WebKitChatContainer* chat_widget,
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001267 AccountInfoPointer const & accountInfo,
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001268 lrc::api::conversation::Info* conversation)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001269{
Guillaume Roguezaaf8b0f2018-01-24 14:38:43 -05001270 auto* self = g_object_new(CURRENT_CALL_VIEW_TYPE, NULL);
1271 auto* priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);
1272
Hugo Lefeuvre6f2ceb12018-04-18 15:08:01 -04001273 priv->cpp->setup(chat_widget, accountInfo, conversation);
Stepan Salenikovich09e0b782016-09-07 16:28:50 -04001274 return GTK_WIDGET(self);
1275}