blob: 6a790ec16e32bc3e8a31fe4ff3214b5c31bdda7c [file] [log] [blame]
Stepan Salenikovich69771842015-02-24 18:11:45 -05001/*
Guillaume Roguez2a6150d2017-07-19 18:24:47 -04002 * Copyright (C) 2015-2017 Savoir-faire Linux Inc.
Stepan Salenikovich69771842015-02-24 18:11:45 -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 Salenikovich69771842015-02-24 18:11:45 -050018 */
19
20#include "incomingcallview.h"
21
22#include <gtk/gtk.h>
Stepan Salenikovich69771842015-02-24 18:11:45 -050023#include <call.h>
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050024#include "utils/drawing.h"
25#include <callmodel.h>
Stepan Salenikovich6f687072015-03-26 10:43:37 -040026#include <contactmethod.h>
27#include <person.h>
Stepan Salenikovichbbd6c132015-08-20 15:21:48 -040028#include <globalinstances.h>
29#include "native/pixbufmanipulator.h"
Stepan Salenikovich86e91682016-05-04 18:03:37 -040030#include <itemdataroles.h>
31#include <numbercategory.h>
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040032#include "chatview.h"
33#include "utils/files.h"
Stepan Salenikovich69771842015-02-24 18:11:45 -050034
35struct _IncomingCallView
36{
37 GtkBox parent;
38};
39
40struct _IncomingCallViewClass
41{
42 GtkBoxClass parent_class;
43};
44
45typedef struct _IncomingCallViewPrivate IncomingCallViewPrivate;
46
47struct _IncomingCallViewPrivate
48{
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040049 GtkWidget *paned_call;
Stepan Salenikovich69771842015-02-24 18:11:45 -050050 GtkWidget *image_incoming;
Stepan Salenikovich86e91682016-05-04 18:03:37 -040051 GtkWidget *label_name;
Nicolas Jager2e467c32017-01-18 08:52:23 -050052 GtkWidget *label_bestId;
Stepan Salenikovich9d51d532015-06-15 17:47:42 -040053 GtkWidget *spinner_status;
54 GtkWidget *placeholder;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050055 GtkWidget *label_status;
56 GtkWidget *button_accept_incoming;
57 GtkWidget *button_reject_incoming;
58 GtkWidget *button_end_call;
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040059 GtkWidget *frame_chat;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050060
aviau039001d2016-09-29 16:39:05 -040061 /* The webkit_chat_container is created once, then reused for all chat
62 * views */
63 GtkWidget *webkit_chat_container;
64
Stepan Salenikovich09e0b782016-09-07 16:28:50 -040065 Call *call;
66
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050067 QMetaObject::Connection state_change_connection;
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -040068 QMetaObject::Connection cm_changed_connection;
69 QMetaObject::Connection person_changed_connection;
70 QMetaObject::Connection cm_person_changed_connection;
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040071
72 GSettings *settings;
Stepan Salenikovich69771842015-02-24 18:11:45 -050073};
74
75G_DEFINE_TYPE_WITH_PRIVATE(IncomingCallView, incoming_call_view, GTK_TYPE_BOX);
76
77#define INCOMING_CALL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), INCOMING_CALL_VIEW_TYPE, IncomingCallViewPrivate))
78
79static void
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040080incoming_call_view_dispose(GObject *object)
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050081{
82 IncomingCallView *view;
83 IncomingCallViewPrivate *priv;
84
85 view = INCOMING_CALL_VIEW(object);
86 priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
87
88 QObject::disconnect(priv->state_change_connection);
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -040089 QObject::disconnect(priv->cm_changed_connection);
90 QObject::disconnect(priv->person_changed_connection);
91 QObject::disconnect(priv->cm_person_changed_connection);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050092
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040093 g_clear_object(&priv->settings);
94
Stepan Salenikovich297b5d12015-02-26 17:51:13 -050095 G_OBJECT_CLASS(incoming_call_view_parent_class)->dispose(object);
96}
97
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -040098static gboolean
99map_boolean_to_orientation(GValue *value, GVariant *variant, G_GNUC_UNUSED gpointer user_data)
100{
101 if (g_variant_is_of_type(variant, G_VARIANT_TYPE_BOOLEAN)) {
102 if (g_variant_get_boolean(variant)) {
103 // true, chat should be horizontal (to the right)
104 g_value_set_enum(value, GTK_ORIENTATION_HORIZONTAL);
105 } else {
106 // false, chat should be vertical (at the bottom)
107 g_value_set_enum(value, GTK_ORIENTATION_VERTICAL);
108 }
109 return TRUE;
110 }
111 return FALSE;
112}
113
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500114static void
Stepan Salenikovich69771842015-02-24 18:11:45 -0500115incoming_call_view_init(IncomingCallView *view)
116{
117 gtk_widget_init_template(GTK_WIDGET(view));
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400118 // gtk_widget_add_events(GTK_WIDGET(view), GDK_KEY_PRESS_MASK);
119
AmarOke7c02972017-07-17 15:21:20 -0400120 auto provider = gtk_css_provider_new();
121 gtk_css_provider_load_from_data(provider,
122 ".flat-button { border: 0; border-radius: 50%; transition: all 0.3s ease; } \
123 .red-button { background: #dc3a37; } \
124 .green-button { background: #27ae60; } \
125 .red-button:hover { background: #dc2719; } \
126 .green-button:hover { background: #219d55; }",
127 -1, nullptr
128 );
129 gtk_style_context_add_provider_for_screen(gdk_display_get_default_screen(gdk_display_get_default()),
130 GTK_STYLE_PROVIDER(provider),
131 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
132
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400133 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
134
135 /* bind the chat orientation to the gsetting */
136 priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
137 g_settings_bind_with_mapping(priv->settings, "chat-pane-horizontal",
138 priv->paned_call, "orientation",
139 G_SETTINGS_BIND_GET,
140 map_boolean_to_orientation,
141 nullptr, nullptr, nullptr);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500142}
143
144static void
145incoming_call_view_class_init(IncomingCallViewClass *klass)
146{
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400147 G_OBJECT_CLASS(klass)->dispose = incoming_call_view_dispose;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500148
Stepan Salenikovich69771842015-02-24 18:11:45 -0500149 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
150 "/cx/ring/RingGnome/incomingcallview.ui");
151
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400152 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, paned_call);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500153 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, image_incoming);
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400154 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_name);
Nicolas Jager2e467c32017-01-18 08:52:23 -0500155 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_bestId);
Stepan Salenikovich9d51d532015-06-15 17:47:42 -0400156 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, spinner_status);
157 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, placeholder);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500158 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, label_status);
159 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_accept_incoming);
160 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_reject_incoming);
161 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, button_end_call);
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400162 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), IncomingCallView, frame_chat);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500163}
164
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500165static void
166update_state(IncomingCallView *view, Call *call)
167{
168 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
169
170 /* change state label */
171 Call::State state = call->state();
172
Stepan Salenikovich7ec8fe82015-06-02 18:26:39 -0400173 gchar *status = g_strdup_printf("%s", call->toHumanStateName().toUtf8().constData());
174 gtk_label_set_text(GTK_LABEL(priv->label_status), status);
175 g_free(status);
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500176
177 /* change button(s) displayed */
178 gtk_widget_hide(priv->button_accept_incoming);
179 gtk_widget_hide(priv->button_reject_incoming);
180 gtk_widget_hide(priv->button_end_call);
181
182 switch(state) {
183 case Call::State::INCOMING:
184 gtk_widget_show(priv->button_accept_incoming);
185 gtk_widget_show(priv->button_reject_incoming);
186 break;
Stepan Salenikovich46e3e992015-04-08 11:42:13 -0400187 case Call::State::NEW:
188 case Call::State::ABORTED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500189 case Call::State::RINGING:
190 case Call::State::CURRENT:
191 case Call::State::DIALING:
192 case Call::State::HOLD:
193 case Call::State::FAILURE:
194 case Call::State::BUSY:
195 case Call::State::TRANSFERRED:
196 case Call::State::TRANSF_HOLD:
197 case Call::State::OVER:
198 case Call::State::ERROR:
199 case Call::State::CONFERENCE:
200 case Call::State::CONFERENCE_HOLD:
201 case Call::State::INITIALIZATION:
Stepan Salenikovich4e409932015-04-24 12:12:39 -0400202 case Call::State::CONNECTED:
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500203 gtk_widget_show(priv->button_end_call);
204 break;
205 case Call::State::COUNT__:
206 break;
207 }
Stepan Salenikovich9d51d532015-06-15 17:47:42 -0400208
209 if (call->lifeCycleState() == Call::LifeCycleState::INITIALIZATION) {
210 gtk_widget_show(priv->spinner_status);
211 gtk_widget_hide(priv->placeholder);
212 } else {
213 gtk_widget_show(priv->placeholder);
214 gtk_widget_hide(priv->spinner_status);
215 }
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500216}
217
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400218static void
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400219update_name_and_photo(IncomingCallView *view)
220{
221 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400222
223 /* get call image */
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400224 QVariant var_i = GlobalInstances::pixmapManipulator().callPhoto(priv->call, QSize(110, 110), false);
Stepan Salenikovich6f687072015-03-26 10:43:37 -0400225 std::shared_ptr<GdkPixbuf> image = var_i.value<std::shared_ptr<GdkPixbuf>>();
226 gtk_image_set_from_pixbuf(GTK_IMAGE(priv->image_incoming), image.get());
Stepan Salenikovich69771842015-02-24 18:11:45 -0500227
Stepan Salenikovichc64523b2015-02-27 16:31:00 -0500228 /* get name */
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400229 auto name = priv->call->formattedName();
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400230 gtk_label_set_text(GTK_LABEL(priv->label_name), name.toUtf8().constData());
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400231
232 /* get uri, if different from name */
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400233 auto bestId = priv->call->peerContactMethod()->bestId();
Nicolas Jagerd0d1d812017-01-18 09:09:21 -0500234 if (name != bestId) {
Nicolas Jager2e467c32017-01-18 08:52:23 -0500235 auto cat_bestId = g_strdup_printf("(%s) %s"
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400236 ,priv->call->peerContactMethod()->category()->name().toUtf8().constData()
Nicolas Jagerd0d1d812017-01-18 09:09:21 -0500237 ,bestId.toUtf8().constData());
Nicolas Jager2e467c32017-01-18 08:52:23 -0500238 gtk_label_set_text(GTK_LABEL(priv->label_bestId), cat_bestId);
239 g_free(cat_bestId);
240 gtk_widget_show(priv->label_bestId);
Stepan Salenikovich86e91682016-05-04 18:03:37 -0400241 }
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400242}
243
244static void
245update_person(IncomingCallView *view, Person *new_person)
246{
247 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
248
249 update_name_and_photo(view);
250
251 QObject::disconnect(priv->person_changed_connection);
252 if (new_person) {
253 priv->person_changed_connection = QObject::connect(
254 new_person,
255 &Person::changed,
256 [view]() { update_name_and_photo(view); }
257 );
258 }
259}
260
261static void
262set_call_info(IncomingCallView *view, Call *call) {
263 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(view);
264
265 priv->call = call;
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500266
267 /* change some things depending on call state */
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500268 update_state(view, call);
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400269 update_person(view, priv->call->peerContactMethod()->contact());
Stepan Salenikovich297b5d12015-02-26 17:51:13 -0500270
271 priv->state_change_connection = QObject::connect(
272 call,
273 &Call::stateChanged,
274 [=]() { update_state(view, call); }
275 );
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400276
Stepan Salenikovichaa4ff432017-05-19 16:16:53 -0400277 priv->cm_changed_connection = QObject::connect(
278 priv->call->peerContactMethod(),
279 &ContactMethod::changed,
280 [view]() { update_name_and_photo(view); }
281 );
282
283 priv->cm_person_changed_connection = QObject::connect(
284 priv->call->peerContactMethod(),
285 &ContactMethod::contactChanged,
286 [view] (Person* newPerson, Person*) { update_person(view, newPerson); }
287 );
288
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400289 /* show chat */
aviau039001d2016-09-29 16:39:05 -0400290 auto chat_view = chat_view_new_cm(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), priv->call->peerContactMethod());
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400291 gtk_widget_show(chat_view);
292 chat_view_set_header_visible(CHAT_VIEW(chat_view), FALSE);
293 gtk_container_add(GTK_CONTAINER(priv->frame_chat), chat_view);
Stepan Salenikovich69771842015-02-24 18:11:45 -0500294}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400295
296GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400297incoming_call_view_new(Call *call, WebKitChatContainer *webkit_chat_container)
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400298{
299 auto self = g_object_new(INCOMING_CALL_VIEW_TYPE, NULL);
aviau039001d2016-09-29 16:39:05 -0400300
301 IncomingCallViewPrivate *priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
302 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
303
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400304 set_call_info(INCOMING_CALL_VIEW(self), call);
305
306 return GTK_WIDGET(self);
307}
308
309Call*
310incoming_call_view_get_call(IncomingCallView *self)
311{
312 g_return_val_if_fail(IS_INCOMING_CALL_VIEW(self), nullptr);
313 auto priv = INCOMING_CALL_VIEW_GET_PRIVATE(self);
314
315 return priv->call;
316}