blob: 41a5b0299ca6b1f541cd0e5d3d462989a958efce [file] [log] [blame]
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05001/*
2 * Copyright (C) 2016 Savoir-faire Linux Inc.
3 * Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
aviau039001d2016-09-29 16:39:05 -04004 * Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
Stepan Salenikovichd2cad062016-01-08 13:43:49 -05005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include "chatview.h"
22
23#include <gtk/gtk.h>
24#include <call.h>
25#include <callmodel.h>
26#include <contactmethod.h>
27#include <person.h>
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050028#include <media/text.h>
29#include <media/textrecording.h>
30#include "ringnotify.h"
aviau039001d2016-09-29 16:39:05 -040031#include "profilemodel.h"
32#include "profile.h"
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -050033#include "numbercategory.h"
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -050034#include <QtCore/QDateTime>
Stepan Salenikoviche9933242016-06-21 18:08:48 -040035#include "utils/calling.h"
aviau039001d2016-09-29 16:39:05 -040036#include "webkitchatcontainer.h"
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050037
Stepan Salenikovichce06adb2016-02-19 12:53:53 -050038static constexpr GdkRGBA RING_BLUE = {0.0508, 0.594, 0.676, 1.0}; // outgoing msg color: (13, 152, 173)
39
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050040struct _ChatView
41{
42 GtkBox parent;
43};
44
45struct _ChatViewClass
46{
47 GtkBoxClass parent_class;
48};
49
50typedef struct _ChatViewPrivate ChatViewPrivate;
51
52struct _ChatViewPrivate
53{
aviau039001d2016-09-29 16:39:05 -040054 GtkWidget *box_webkit_chat_container;
55 GtkWidget *webkit_chat_container;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050056 GtkWidget *button_chat_input;
57 GtkWidget *entry_chat_input;
58 GtkWidget *scrolledwindow_chat;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050059 GtkWidget *hbox_chat_info;
60 GtkWidget *label_peer;
61 GtkWidget *combobox_cm;
Stepan Salenikovich8043a562016-03-18 13:56:40 -040062 GtkWidget *button_close_chatview;
Stepan Salenikoviche9933242016-06-21 18:08:48 -040063 GtkWidget *button_placecall;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050064
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050065 /* only one of the three following pointers should be non void;
66 * either this is an in-call chat (and so the in-call chat APIs will be used)
aviau039001d2016-09-29 16:39:05 -040067 * or it is an out of call chat (and so the account chat APIs will be used) */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050068 Call *call;
69 Person *person;
70 ContactMethod *cm;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050071
72 QMetaObject::Connection new_message_connection;
aviau039001d2016-09-29 16:39:05 -040073 QMetaObject::Connection message_changed_connection;
Stepan Salenikovich1f731212016-11-10 11:55:10 -050074 QMetaObject::Connection update_name;
75
76 gulong webkit_ready;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050077};
78
79G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
80
81#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))
82
83enum {
84 NEW_MESSAGES_DISPLAYED,
Stepan Salenikovich8043a562016-03-18 13:56:40 -040085 HIDE_VIEW_CLICKED,
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050086 LAST_SIGNAL
87};
88
89static guint chat_view_signals[LAST_SIGNAL] = { 0 };
90
91static void
92chat_view_dispose(GObject *object)
93{
94 ChatView *view;
95 ChatViewPrivate *priv;
96
97 view = CHAT_VIEW(object);
98 priv = CHAT_VIEW_GET_PRIVATE(view);
99
100 QObject::disconnect(priv->new_message_connection);
aviau039001d2016-09-29 16:39:05 -0400101 QObject::disconnect(priv->message_changed_connection);
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500102 QObject::disconnect(priv->update_name);
aviau039001d2016-09-29 16:39:05 -0400103
104 /* Destroying the box will also destroy its children, and we wouldn't
105 * want that. So we remove the webkit_chat_container from the box. */
106 if (priv->webkit_chat_container) {
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500107 /* disconnect for webkit signals */
108 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
109 priv->webkit_ready = 0;
110
aviau039001d2016-09-29 16:39:05 -0400111 gtk_container_remove(
112 GTK_CONTAINER(priv->box_webkit_chat_container),
113 GTK_WIDGET(priv->webkit_chat_container)
114 );
115 priv->webkit_chat_container = nullptr;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500116
aviau039001d2016-09-29 16:39:05 -0400117 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500118
119 G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
120}
121
122
123static void
124send_chat(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
125{
126 g_return_if_fail(IS_CHAT_VIEW(self));
127 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
128
Stepan Salenikovich4fbc3bf2016-11-03 15:01:10 -0400129 /* make sure there is more than just whitespace, but if so, send the original text */
130 const auto text = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_chat_input)));
131 if (!text.trimmed().isEmpty()) {
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500132 QMap<QString, QString> messages;
133 messages["text/plain"] = text;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500134
135 if (priv->call) {
136 // in call message
137 priv->call->addOutgoingMedia<Media::Text>()->send(messages);
138 } else if (priv->person) {
139 // get the chosen cm
140 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
141 if (active >= 0) {
142 auto cm = priv->person->phoneNumbers().at(active);
143 if (!cm->sendOfflineTextMessage(messages))
144 g_warning("message failed to send"); // TODO: warn the user about this in the UI
145 } else {
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400146 g_warning("no ContactMethod chosen; message not sent");
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500147 }
148 } else if (priv->cm) {
149 if (!priv->cm->sendOfflineTextMessage(messages))
150 g_warning("message failed to send"); // TODO: warn the user about this in the UI
151 } else {
152 g_warning("no Call, Person, or ContactMethod set; message not sent");
153 }
154
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500155 /* clear the entry */
156 gtk_entry_set_text(GTK_ENTRY(priv->entry_chat_input), "");
157 }
158}
159
160static void
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400161hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
162{
163 g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
164}
165
166static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400167placecall_clicked(ChatView *self)
168{
169 auto priv = CHAT_VIEW_GET_PRIVATE(self);
170
171 if (priv->person) {
172 // get the chosen cm
173 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
174 if (active >= 0) {
175 auto cm = priv->person->phoneNumbers().at(active);
176 place_new_call(cm);
177 } else {
178 g_warning("no ContactMethod chosen; cannot place call");
179 }
180 } else if (priv->cm) {
181 place_new_call(priv->cm);
182 } else {
183 g_warning("no Person or ContactMethod set; cannot place call");
184 }
185}
186
187static void
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500188chat_view_init(ChatView *view)
189{
190 gtk_widget_init_template(GTK_WIDGET(view));
191
192 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
193
194 g_signal_connect(priv->button_chat_input, "clicked", G_CALLBACK(send_chat), view);
195 g_signal_connect(priv->entry_chat_input, "activate", G_CALLBACK(send_chat), view);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400196 g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400197 g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500198}
199
200static void
201chat_view_class_init(ChatViewClass *klass)
202{
203 G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;
204
205 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
206 "/cx/ring/RingGnome/chatview.ui");
207
aviau039001d2016-09-29 16:39:05 -0400208 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500209 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_chat_input);
210 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, entry_chat_input);
211 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, scrolledwindow_chat);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500212 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
213 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
214 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, combobox_cm);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400215 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400216 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500217
218 chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
219 "new-messages-displayed",
220 G_TYPE_FROM_CLASS(klass),
221 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
222 0,
223 nullptr,
224 nullptr,
225 g_cclosure_marshal_VOID__VOID,
226 G_TYPE_NONE, 0);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400227
228 chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
229 "hide-view-clicked",
230 G_TYPE_FROM_CLASS(klass),
231 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
232 0,
233 nullptr,
234 nullptr,
235 g_cclosure_marshal_VOID__VOID,
236 G_TYPE_NONE, 0);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500237}
238
aviau039001d2016-09-29 16:39:05 -0400239
240
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500241static void
aviau039001d2016-09-29 16:39:05 -0400242print_message_to_buffer(ChatView* self, const QModelIndex &idx)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500243{
aviau039001d2016-09-29 16:39:05 -0400244 if (!idx.isValid()) {
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500245 g_warning("QModelIndex in im model is not valid");
aviau039001d2016-09-29 16:39:05 -0400246 return;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500247 }
aviau039001d2016-09-29 16:39:05 -0400248
249 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
250
251 webkit_chat_container_print_new_message(
252 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
253 idx
254 );
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500255}
256
aviaufc213552016-11-01 12:39:39 -0400257ContactMethod*
258get_active_contactmethod(ChatView *self)
259{
260 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
261
262 auto cms = priv->person->phoneNumbers();
263 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
264 if (active >= 0 && active < cms.size()) {
265 return cms.at(active);
266 } else {
267 return nullptr;
268 }
269}
270
271static void
272set_participant_images(ChatView* self)
273{
274 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
275
276 webkit_chat_container_clear_sender_images(
277 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
278 );
279
280 /* Set the sender image for the peer */
281 ContactMethod* sender_contact_method_peer;
282 QVariant photo_variant_peer;
283
284 if (priv->person)
285 {
286 photo_variant_peer = priv->person->photo();
287 sender_contact_method_peer = get_active_contactmethod(self);
288 }
289 else
290 {
291 if (priv->cm)
292 {
293 sender_contact_method_peer = priv->cm;
294 }
295 else
296 {
297 sender_contact_method_peer = priv->call->peerContactMethod();
298 }
299 photo_variant_peer = sender_contact_method_peer->roleData((int) Call::Role::Photo);
300 }
301
302 if (photo_variant_peer.isValid())
303 {
304 webkit_chat_container_set_sender_image(
305 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
306 sender_contact_method_peer,
307 photo_variant_peer
308 );
309 }
310
311 /* set sender image for "ME" */
312 auto profile = ProfileModel::instance().selectedProfile();
313 if (profile)
314 {
315 auto person = profile->person();
316 if (person)
317 {
318 auto photo_variant_me = person->photo();
319 if (photo_variant_me.isValid())
320 {
321 webkit_chat_container_set_sender_image(
322 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
323 nullptr,
324 photo_variant_me
325 );
326 }
327 }
328 }
329}
330
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500331static void
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500332print_text_recording(Media::TextRecording *recording, ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500333{
334 g_return_if_fail(IS_CHAT_VIEW(self));
335 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
336
aviaufc213552016-11-01 12:39:39 -0400337 /* set the photos of the chat participants */
338 set_participant_images(self);
339
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500340 /* only text messages are supported for now */
341 auto model = recording->instantTextMessagingModel();
342
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500343 /* new model, disconnect from the old model updates and clear the text buffer */
344 QObject::disconnect(priv->new_message_connection);
345
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500346 /* put all the messages in the im model into the text view */
347 for (int row = 0; row < model->rowCount(); ++row) {
348 QModelIndex idx = model->index(row, 0);
aviau039001d2016-09-29 16:39:05 -0400349 print_message_to_buffer(self, idx);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500350 }
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500351 /* mark all messages as read */
352 recording->setAllRead();
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500353
aviau039001d2016-09-29 16:39:05 -0400354
355 /* messages modified */
356 /* connecting on instantMessagingModel and not textMessagingModel */
357 priv->message_changed_connection = QObject::connect(
358 model,
359 &QAbstractItemModel::dataChanged,
360 [self, priv] (const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight)
361 {
362 webkit_chat_container_update_message(
363 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
364 topLeft
365 );
366 }
367 );
368
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500369 /* append new messages */
370 priv->new_message_connection = QObject::connect(
371 model,
372 &QAbstractItemModel::rowsInserted,
373 [self, priv, model] (const QModelIndex &parent, int first, int last) {
374 for (int row = first; row <= last; ++row) {
375 QModelIndex idx = model->index(row, 0, parent);
aviau039001d2016-09-29 16:39:05 -0400376 print_message_to_buffer(self, idx);
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500377 /* make sure these messages are marked as read */
378 model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500379 g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0);
380 }
381 }
382 );
383}
384
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500385static void
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500386selected_cm_changed(ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500387{
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500388 auto priv = CHAT_VIEW_GET_PRIVATE(self);
389
390 /* make sure the webkit view is ready, in case we get this signal before it is */
391 if (!webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
392 return;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500393
aviaufc213552016-11-01 12:39:39 -0400394 auto cm = get_active_contactmethod(self);
395 if (cm){
396 print_text_recording(cm->textRecording(), self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500397 } else {
398 g_warning("no valid ContactMethod selected to display chat conversation");
399 }
400}
401
402static void
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500403render_contact_method(G_GNUC_UNUSED GtkCellLayout *cell_layout,
404 GtkCellRenderer *cell,
405 GtkTreeModel *model,
406 GtkTreeIter *iter,
407 G_GNUC_UNUSED gpointer data)
408{
409 GValue value = G_VALUE_INIT;
410 gtk_tree_model_get_value(model, iter, 0, &value);
411 auto cm = (ContactMethod *)g_value_get_pointer(&value);
412
413 gchar *number = nullptr;
414 if (cm && cm->category()) {
415 // try to get the number category, eg: "home"
416 number = g_strdup_printf("(%s) %s", cm->category()->name().toUtf8().constData(),
Nicolas Jager153b7652017-01-17 10:06:54 -0500417 cm->getBestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500418 } else if (cm) {
Nicolas Jager153b7652017-01-17 10:06:54 -0500419 number = g_strdup_printf("%s", cm->getBestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500420 }
421
422 g_object_set(G_OBJECT(cell), "text", number, NULL);
423 g_free(number);
424}
425
426static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500427update_contact_methods(ChatView *self)
428{
429 g_return_if_fail(IS_CHAT_VIEW(self));
430 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
431
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500432 g_return_if_fail(priv->call || priv->person || priv->cm);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500433
434 /* model for the combobox for the choice of ContactMethods */
435 auto cm_model = gtk_list_store_new(
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500436 1, G_TYPE_POINTER
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500437 );
438
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400439 Person::ContactMethods cms;
440
441 if (priv->person)
442 cms = priv->person->phoneNumbers();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500443 else if (priv->cm)
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400444 cms << priv->cm;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500445 else if (priv->call)
446 cms << priv->call->peerContactMethod();
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400447
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500448 for (int i = 0; i < cms.size(); ++i) {
449 GtkTreeIter iter;
450 gtk_list_store_append(cm_model, &iter);
451 gtk_list_store_set(cm_model, &iter,
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500452 0, cms.at(i),
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500453 -1);
454 }
455
456 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_cm), GTK_TREE_MODEL(cm_model));
457 g_object_unref(cm_model);
458
459 auto renderer = gtk_cell_renderer_text_new();
460 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
461 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_cm), renderer, FALSE);
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500462 gtk_cell_layout_set_cell_data_func(
463 GTK_CELL_LAYOUT(priv->combobox_cm),
464 renderer,
465 (GtkCellLayoutDataFunc)render_contact_method,
466 nullptr, nullptr);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500467
468 /* select the last used cm */
469 if (!cms.isEmpty()) {
470 auto last_used_cm = cms.at(0);
471 int last_used_cm_idx = 0;
472 for (int i = 1; i < cms.size(); ++i) {
473 auto new_cm = cms.at(i);
474 if (difftime(new_cm->lastUsed(), last_used_cm->lastUsed()) > 0) {
475 last_used_cm = new_cm;
476 last_used_cm_idx = i;
477 }
478 }
479
480 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_cm), last_used_cm_idx);
481 }
482
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400483 /* if there is only one CM, make the combo box insensitive */
484 if (cms.size() < 2)
485 gtk_widget_set_sensitive(priv->combobox_cm, FALSE);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400486
487 /* if no CMs make the call button insensitive */
488 gtk_widget_set_sensitive(priv->button_placecall, !cms.isEmpty());
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500489}
490
491static void
492update_name(ChatView *self)
493{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500494 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
495
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500496 g_return_if_fail(priv->person || priv->cm || priv->call);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500497
498 QString name;
499 if (priv->person) {
500 name = priv->person->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500501 } else if (priv->cm) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500502 name = priv->cm->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500503 } else if (priv->call) {
504 name = priv->call->peerContactMethod()->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500505 }
506 gtk_label_set_text(GTK_LABEL(priv->label_peer), name.toUtf8().constData());
507}
508
aviau039001d2016-09-29 16:39:05 -0400509static void
aviau039001d2016-09-29 16:39:05 -0400510webkit_chat_container_ready(ChatView* self)
511{
512 /* The webkit chat container has loaded the javascript libraries, we can
513 * now use it. */
514
515 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
516
517 webkit_chat_container_clear(
518 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
519 );
520
aviau039001d2016-09-29 16:39:05 -0400521 /* print the text recordings */
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500522 if (priv->call) {
523 print_text_recording(priv->call->peerContactMethod()->textRecording(), self);
524 } else if (priv->cm) {
525 print_text_recording(priv->cm->textRecording(), self);
526 } else if (priv->person) {
527 /* get the selected cm and print the text recording */
528 selected_cm_changed(self);
aviau039001d2016-09-29 16:39:05 -0400529 }
aviau039001d2016-09-29 16:39:05 -0400530}
531
532static void
533build_chat_view(ChatView* self)
534{
535 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
536
537 gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
538 gtk_widget_show(priv->webkit_chat_container);
539
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500540 /* keep name updated */
541 if (priv->call) {
542 priv->update_name = QObject::connect(
543 priv->call,
544 &Call::changed,
545 [self] () { update_name(self); }
546 );
547 } else if (priv->cm) {
548 priv->update_name = QObject::connect(
549 priv->cm,
550 &ContactMethod::changed,
551 [self] () { update_name(self); }
552 );
553 } else if (priv->person) {
554 priv->update_name = QObject::connect(
555 priv->person,
556 &Person::changed,
557 [self] () { update_name(self); }
558 );
559 }
560 update_name(self);
aviau039001d2016-09-29 16:39:05 -0400561
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500562 /* keep selected cm updated */
563 update_contact_methods(self);
564 g_signal_connect_swapped(priv->combobox_cm, "changed", G_CALLBACK(selected_cm_changed), self);
565
566 priv->webkit_ready = g_signal_connect_swapped(
aviau039001d2016-09-29 16:39:05 -0400567 priv->webkit_chat_container,
568 "ready",
569 G_CALLBACK(webkit_chat_container_ready),
570 self
571 );
572
573 if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
aviau039001d2016-09-29 16:39:05 -0400574 webkit_chat_container_ready(self);
aviau039001d2016-09-29 16:39:05 -0400575
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500576 /* we only show the chat info in the case of cm / person */
577 gtk_widget_set_visible(priv->hbox_chat_info, (priv->cm || priv->person));
aviau039001d2016-09-29 16:39:05 -0400578}
579
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500580GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400581chat_view_new_call(WebKitChatContainer *webkit_chat_container, Call *call)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500582{
583 g_return_val_if_fail(call, nullptr);
584
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500585 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500586
aviau039001d2016-09-29 16:39:05 -0400587 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
588 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500589 priv->call = call;
aviau039001d2016-09-29 16:39:05 -0400590
591 build_chat_view(self);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500592
593 return (GtkWidget *)self;
594}
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500595
596GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400597chat_view_new_cm(WebKitChatContainer *webkit_chat_container, ContactMethod *cm)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500598{
599 g_return_val_if_fail(cm, nullptr);
600
601 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500602
aviau039001d2016-09-29 16:39:05 -0400603 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
604 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500605 priv->cm = cm;
aviau039001d2016-09-29 16:39:05 -0400606
607 build_chat_view(self);
608
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500609 return (GtkWidget *)self;
610}
611
612GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400613chat_view_new_person(WebKitChatContainer *webkit_chat_container, Person *p)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500614{
615 g_return_val_if_fail(p, nullptr);
616
617 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500618
aviau039001d2016-09-29 16:39:05 -0400619 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
620 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500621 priv->person = p;
622
aviau039001d2016-09-29 16:39:05 -0400623 build_chat_view(self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500624
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500625 return (GtkWidget *)self;
626}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400627
628Call*
629chat_view_get_call(ChatView *self)
630{
631 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
632 auto priv = CHAT_VIEW_GET_PRIVATE(self);
633
634 return priv->call;
635}
636
637ContactMethod*
638chat_view_get_cm(ChatView *self)
639{
640 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
641 auto priv = CHAT_VIEW_GET_PRIVATE(self);
642
643 return priv->cm;
644}
645
646Person*
647chat_view_get_person(ChatView *self)
648{
649 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
650 auto priv = CHAT_VIEW_GET_PRIVATE(self);
651
652 return priv->person;
653}
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400654
655void
656chat_view_set_header_visible(ChatView *self, gboolean visible)
657{
658 auto priv = CHAT_VIEW_GET_PRIVATE(self);
659
660 gtk_widget_set_visible(priv->hbox_chat_info, visible);
661}