blob: 75203f34de504ed93280949f626fda23864c2f3c [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"
Nicolas Jager759faea2017-03-22 16:22:00 -040022#include "utils/accounts.h"
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050023
24#include <gtk/gtk.h>
25#include <call.h>
26#include <callmodel.h>
27#include <contactmethod.h>
28#include <person.h>
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050029#include <media/text.h>
30#include <media/textrecording.h>
31#include "ringnotify.h"
aviau039001d2016-09-29 16:39:05 -040032#include "profilemodel.h"
33#include "profile.h"
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -050034#include "numbercategory.h"
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -050035#include <QtCore/QDateTime>
Stepan Salenikoviche9933242016-06-21 18:08:48 -040036#include "utils/calling.h"
aviau039001d2016-09-29 16:39:05 -040037#include "webkitchatcontainer.h"
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050038
Nicolas Jager759faea2017-03-22 16:22:00 -040039// LRC
40#include <account.h>
41
42
Stepan Salenikovichce06adb2016-02-19 12:53:53 -050043static constexpr GdkRGBA RING_BLUE = {0.0508, 0.594, 0.676, 1.0}; // outgoing msg color: (13, 152, 173)
44
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050045struct _ChatView
46{
47 GtkBox parent;
48};
49
50struct _ChatViewClass
51{
52 GtkBoxClass parent_class;
53};
54
55typedef struct _ChatViewPrivate ChatViewPrivate;
56
57struct _ChatViewPrivate
58{
aviau039001d2016-09-29 16:39:05 -040059 GtkWidget *box_webkit_chat_container;
60 GtkWidget *webkit_chat_container;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050061 GtkWidget *scrolledwindow_chat;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050062 GtkWidget *hbox_chat_info;
63 GtkWidget *label_peer;
64 GtkWidget *combobox_cm;
Stepan Salenikovich8043a562016-03-18 13:56:40 -040065 GtkWidget *button_close_chatview;
Stepan Salenikoviche9933242016-06-21 18:08:48 -040066 GtkWidget *button_placecall;
Nicolas Jager759faea2017-03-22 16:22:00 -040067 GtkWidget *button_send_invitation;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050068
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050069 /* only one of the three following pointers should be non void;
70 * either this is an in-call chat (and so the in-call chat APIs will be used)
aviau039001d2016-09-29 16:39:05 -040071 * or it is an out of call chat (and so the account chat APIs will be used) */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050072 Call *call;
73 Person *person;
74 ContactMethod *cm;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050075
76 QMetaObject::Connection new_message_connection;
aviau039001d2016-09-29 16:39:05 -040077 QMetaObject::Connection message_changed_connection;
AmarOk72e51242017-07-14 13:37:51 -040078 QMetaObject::Connection update_chat_info;
Stepan Salenikovich1f731212016-11-10 11:55:10 -050079
80 gulong webkit_ready;
AmarOkb4253242017-07-13 11:21:39 -040081 gulong webkit_send_text;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050082};
83
84G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
85
86#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))
87
88enum {
89 NEW_MESSAGES_DISPLAYED,
Stepan Salenikovich8043a562016-03-18 13:56:40 -040090 HIDE_VIEW_CLICKED,
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050091 LAST_SIGNAL
92};
93
94static guint chat_view_signals[LAST_SIGNAL] = { 0 };
95
96static void
97chat_view_dispose(GObject *object)
98{
99 ChatView *view;
100 ChatViewPrivate *priv;
101
102 view = CHAT_VIEW(object);
103 priv = CHAT_VIEW_GET_PRIVATE(view);
104
105 QObject::disconnect(priv->new_message_connection);
aviau039001d2016-09-29 16:39:05 -0400106 QObject::disconnect(priv->message_changed_connection);
AmarOk72e51242017-07-14 13:37:51 -0400107 QObject::disconnect(priv->update_chat_info);
aviau039001d2016-09-29 16:39:05 -0400108
109 /* Destroying the box will also destroy its children, and we wouldn't
110 * want that. So we remove the webkit_chat_container from the box. */
111 if (priv->webkit_chat_container) {
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500112 /* disconnect for webkit signals */
113 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
114 priv->webkit_ready = 0;
AmarOkb4253242017-07-13 11:21:39 -0400115 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_send_text);
116 priv->webkit_send_text = 0;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500117
aviau039001d2016-09-29 16:39:05 -0400118 gtk_container_remove(
119 GTK_CONTAINER(priv->box_webkit_chat_container),
120 GTK_WIDGET(priv->webkit_chat_container)
121 );
122 priv->webkit_chat_container = nullptr;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500123
aviau039001d2016-09-29 16:39:05 -0400124 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500125
126 G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
127}
128
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500129static void
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400130hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
131{
132 g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
133}
134
135static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400136placecall_clicked(ChatView *self)
137{
138 auto priv = CHAT_VIEW_GET_PRIVATE(self);
139
140 if (priv->person) {
141 // get the chosen cm
142 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
143 if (active >= 0) {
144 auto cm = priv->person->phoneNumbers().at(active);
145 place_new_call(cm);
146 } else {
147 g_warning("no ContactMethod chosen; cannot place call");
148 }
149 } else if (priv->cm) {
150 place_new_call(priv->cm);
151 } else {
152 g_warning("no Person or ContactMethod set; cannot place call");
153 }
154}
155
156static void
Nicolas Jager759faea2017-03-22 16:22:00 -0400157button_send_invitation_clicked(ChatView *self)
158{
159 auto priv = CHAT_VIEW_GET_PRIVATE(self);
160
161 // get the account associated to the selected cm
162 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
163
Guillaume Roguezcea60ca2017-06-02 16:38:25 -0400164 if (priv->person) {
165 auto& numbers = priv->person->phoneNumbers();
166 if (not numbers.isEmpty())
167 priv->cm = numbers.at(active);
168 }
Nicolas Jager759faea2017-03-22 16:22:00 -0400169
170 if (!priv->cm) {
171 g_warning("invalid contact, cannot send invitation!");
172 return;
173 }
174
175 // try first to use the account associated to the contact method
176 auto account = priv->cm->account();
177 if (not account) {
178
179 // get the choosen account
180 account = get_active_ring_account();
181
182 if (not account) {
183 g_warning("invalid account, cannot send invitation!");
184 return;
185 }
186 }
187
188 // perform the request
189 if (not account->sendContactRequest(priv->cm))
190 g_warning("contact request not forwarded, cannot send invitation!");
191
192 // TODO : add an entry in the conversation to tell the user an invitation was sent.
193}
194
195static void
AmarOkb4253242017-07-13 11:21:39 -0400196webkit_chat_container_send_text(G_GNUC_UNUSED GtkWidget* webview, gchar *message, ChatView* self)
197{
198 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
199
200 /* make sure there is more than just whitespace, but if so, send the original text */
201 const auto text = QString(message);
202 if (!text.trimmed().isEmpty()) {
203 QMap<QString, QString> messages;
204 messages["text/plain"] = text;
205
206 if (priv->call) {
207 // in call message
208 priv->call->addOutgoingMedia<Media::Text>()->send(messages);
209 } else if (priv->person) {
210 // get the chosen cm
211 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
212 if (active >= 0) {
213 auto cm = priv->person->phoneNumbers().at(active);
214 if (!cm->sendOfflineTextMessage(messages))
215 g_warning("message failed to send"); // TODO: warn the user about this in the UI
216 } else {
217 g_warning("no ContactMethod chosen; message not sent");
218 }
219 } else if (priv->cm) {
220 if (!priv->cm->sendOfflineTextMessage(messages))
221 g_warning("message failed to send"); // TODO: warn the user about this in the UI
222 } else {
223 g_warning("no Call, Person, or ContactMethod set; message not sent");
224 }
225 }
226}
227
228static void
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500229chat_view_init(ChatView *view)
230{
231 gtk_widget_init_template(GTK_WIDGET(view));
232
233 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
234
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400235 g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400236 g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
Nicolas Jager759faea2017-03-22 16:22:00 -0400237 g_signal_connect_swapped(priv->button_send_invitation, "clicked", G_CALLBACK(button_send_invitation_clicked), view);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500238}
239
240static void
241chat_view_class_init(ChatViewClass *klass)
242{
243 G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;
244
245 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
246 "/cx/ring/RingGnome/chatview.ui");
247
aviau039001d2016-09-29 16:39:05 -0400248 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500249 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, scrolledwindow_chat);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500250 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
251 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
252 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, combobox_cm);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400253 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400254 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
Nicolas Jager759faea2017-03-22 16:22:00 -0400255 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_send_invitation);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500256
257 chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
258 "new-messages-displayed",
259 G_TYPE_FROM_CLASS(klass),
260 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
261 0,
262 nullptr,
263 nullptr,
264 g_cclosure_marshal_VOID__VOID,
265 G_TYPE_NONE, 0);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400266
267 chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
268 "hide-view-clicked",
269 G_TYPE_FROM_CLASS(klass),
270 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
271 0,
272 nullptr,
273 nullptr,
274 g_cclosure_marshal_VOID__VOID,
275 G_TYPE_NONE, 0);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500276}
277
aviau039001d2016-09-29 16:39:05 -0400278
279
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500280static void
aviau039001d2016-09-29 16:39:05 -0400281print_message_to_buffer(ChatView* self, const QModelIndex &idx)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500282{
aviau039001d2016-09-29 16:39:05 -0400283 if (!idx.isValid()) {
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500284 g_warning("QModelIndex in im model is not valid");
aviau039001d2016-09-29 16:39:05 -0400285 return;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500286 }
aviau039001d2016-09-29 16:39:05 -0400287
288 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
289
290 webkit_chat_container_print_new_message(
291 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
292 idx
293 );
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500294}
295
aviaufc213552016-11-01 12:39:39 -0400296ContactMethod*
297get_active_contactmethod(ChatView *self)
298{
299 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
300
301 auto cms = priv->person->phoneNumbers();
302 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
303 if (active >= 0 && active < cms.size()) {
304 return cms.at(active);
305 } else {
306 return nullptr;
307 }
308}
309
310static void
311set_participant_images(ChatView* self)
312{
313 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
314
315 webkit_chat_container_clear_sender_images(
316 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
317 );
318
319 /* Set the sender image for the peer */
320 ContactMethod* sender_contact_method_peer;
321 QVariant photo_variant_peer;
322
323 if (priv->person)
324 {
325 photo_variant_peer = priv->person->photo();
326 sender_contact_method_peer = get_active_contactmethod(self);
327 }
328 else
329 {
330 if (priv->cm)
331 {
332 sender_contact_method_peer = priv->cm;
333 }
334 else
335 {
336 sender_contact_method_peer = priv->call->peerContactMethod();
337 }
338 photo_variant_peer = sender_contact_method_peer->roleData((int) Call::Role::Photo);
339 }
340
341 if (photo_variant_peer.isValid())
342 {
343 webkit_chat_container_set_sender_image(
344 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
345 sender_contact_method_peer,
346 photo_variant_peer
347 );
348 }
349
350 /* set sender image for "ME" */
351 auto profile = ProfileModel::instance().selectedProfile();
352 if (profile)
353 {
354 auto person = profile->person();
355 if (person)
356 {
357 auto photo_variant_me = person->photo();
358 if (photo_variant_me.isValid())
359 {
360 webkit_chat_container_set_sender_image(
361 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
362 nullptr,
363 photo_variant_me
364 );
365 }
366 }
367 }
368}
369
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500370static void
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500371print_text_recording(Media::TextRecording *recording, ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500372{
373 g_return_if_fail(IS_CHAT_VIEW(self));
374 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
375
aviaufc213552016-11-01 12:39:39 -0400376 /* set the photos of the chat participants */
377 set_participant_images(self);
378
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500379 /* only text messages are supported for now */
380 auto model = recording->instantTextMessagingModel();
381
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500382 /* new model, disconnect from the old model updates and clear the text buffer */
383 QObject::disconnect(priv->new_message_connection);
384
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500385 /* put all the messages in the im model into the text view */
386 for (int row = 0; row < model->rowCount(); ++row) {
387 QModelIndex idx = model->index(row, 0);
aviau039001d2016-09-29 16:39:05 -0400388 print_message_to_buffer(self, idx);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500389 }
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500390 /* mark all messages as read */
391 recording->setAllRead();
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500392
aviau039001d2016-09-29 16:39:05 -0400393
394 /* messages modified */
395 /* connecting on instantMessagingModel and not textMessagingModel */
396 priv->message_changed_connection = QObject::connect(
397 model,
398 &QAbstractItemModel::dataChanged,
399 [self, priv] (const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight)
400 {
401 webkit_chat_container_update_message(
402 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
403 topLeft
404 );
405 }
406 );
407
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500408 /* append new messages */
409 priv->new_message_connection = QObject::connect(
410 model,
411 &QAbstractItemModel::rowsInserted,
412 [self, priv, model] (const QModelIndex &parent, int first, int last) {
413 for (int row = first; row <= last; ++row) {
414 QModelIndex idx = model->index(row, 0, parent);
aviau039001d2016-09-29 16:39:05 -0400415 print_message_to_buffer(self, idx);
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500416 /* make sure these messages are marked as read */
417 model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500418 g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0);
419 }
420 }
421 );
422}
423
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500424static void
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500425selected_cm_changed(ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500426{
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500427 auto priv = CHAT_VIEW_GET_PRIVATE(self);
428
429 /* make sure the webkit view is ready, in case we get this signal before it is */
430 if (!webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
431 return;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500432
aviaufc213552016-11-01 12:39:39 -0400433 auto cm = get_active_contactmethod(self);
434 if (cm){
435 print_text_recording(cm->textRecording(), self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500436 } else {
437 g_warning("no valid ContactMethod selected to display chat conversation");
438 }
439}
440
441static void
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500442render_contact_method(G_GNUC_UNUSED GtkCellLayout *cell_layout,
443 GtkCellRenderer *cell,
444 GtkTreeModel *model,
445 GtkTreeIter *iter,
446 G_GNUC_UNUSED gpointer data)
447{
448 GValue value = G_VALUE_INIT;
449 gtk_tree_model_get_value(model, iter, 0, &value);
450 auto cm = (ContactMethod *)g_value_get_pointer(&value);
451
452 gchar *number = nullptr;
453 if (cm && cm->category()) {
454 // try to get the number category, eg: "home"
455 number = g_strdup_printf("(%s) %s", cm->category()->name().toUtf8().constData(),
Houminbeab3c92017-05-16 11:12:16 +0800456 cm->bestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500457 } else if (cm) {
Houminbeab3c92017-05-16 11:12:16 +0800458 number = g_strdup_printf("%s", cm->bestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500459 }
460
461 g_object_set(G_OBJECT(cell), "text", number, NULL);
462 g_free(number);
463}
464
465static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500466update_contact_methods(ChatView *self)
467{
468 g_return_if_fail(IS_CHAT_VIEW(self));
469 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
470
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500471 g_return_if_fail(priv->call || priv->person || priv->cm);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500472
473 /* model for the combobox for the choice of ContactMethods */
474 auto cm_model = gtk_list_store_new(
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500475 1, G_TYPE_POINTER
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500476 );
477
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400478 Person::ContactMethods cms;
479
480 if (priv->person)
481 cms = priv->person->phoneNumbers();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500482 else if (priv->cm)
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400483 cms << priv->cm;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500484 else if (priv->call)
485 cms << priv->call->peerContactMethod();
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400486
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500487 for (int i = 0; i < cms.size(); ++i) {
488 GtkTreeIter iter;
489 gtk_list_store_append(cm_model, &iter);
490 gtk_list_store_set(cm_model, &iter,
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500491 0, cms.at(i),
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500492 -1);
493 }
494
495 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_cm), GTK_TREE_MODEL(cm_model));
496 g_object_unref(cm_model);
497
498 auto renderer = gtk_cell_renderer_text_new();
499 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
500 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_cm), renderer, FALSE);
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500501 gtk_cell_layout_set_cell_data_func(
502 GTK_CELL_LAYOUT(priv->combobox_cm),
503 renderer,
504 (GtkCellLayoutDataFunc)render_contact_method,
505 nullptr, nullptr);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500506
507 /* select the last used cm */
508 if (!cms.isEmpty()) {
509 auto last_used_cm = cms.at(0);
510 int last_used_cm_idx = 0;
511 for (int i = 1; i < cms.size(); ++i) {
512 auto new_cm = cms.at(i);
513 if (difftime(new_cm->lastUsed(), last_used_cm->lastUsed()) > 0) {
514 last_used_cm = new_cm;
515 last_used_cm_idx = i;
516 }
517 }
518
519 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_cm), last_used_cm_idx);
Guillaume Roguezd35d6492017-06-26 16:40:09 -0400520
521 if (last_used_cm->isConfirmed())
522 gtk_widget_hide(priv->button_send_invitation);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500523 }
524
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400525 /* if there is only one CM, make the combo box insensitive */
526 if (cms.size() < 2)
527 gtk_widget_set_sensitive(priv->combobox_cm, FALSE);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400528
529 /* if no CMs make the call button insensitive */
530 gtk_widget_set_sensitive(priv->button_placecall, !cms.isEmpty());
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500531}
532
533static void
AmarOk72e51242017-07-14 13:37:51 -0400534update_chat_info(ChatView *self)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500535{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500536 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
537
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500538 g_return_if_fail(priv->person || priv->cm || priv->call);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500539
540 QString name;
541 if (priv->person) {
542 name = priv->person->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500543 } else if (priv->cm) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500544 name = priv->cm->roleData(static_cast<int>(Ring::Role::Name)).toString();
AmarOk72e51242017-07-14 13:37:51 -0400545 if (priv->cm->isConfirmed()) {
546 gtk_widget_hide(priv->button_send_invitation);
547 }
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500548 } else if (priv->call) {
549 name = priv->call->peerContactMethod()->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500550 }
551 gtk_label_set_text(GTK_LABEL(priv->label_peer), name.toUtf8().constData());
552}
553
aviau039001d2016-09-29 16:39:05 -0400554static void
aviau039001d2016-09-29 16:39:05 -0400555webkit_chat_container_ready(ChatView* self)
556{
557 /* The webkit chat container has loaded the javascript libraries, we can
558 * now use it. */
559
560 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
561
562 webkit_chat_container_clear(
563 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
564 );
565
aviau039001d2016-09-29 16:39:05 -0400566 /* print the text recordings */
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500567 if (priv->call) {
568 print_text_recording(priv->call->peerContactMethod()->textRecording(), self);
569 } else if (priv->cm) {
570 print_text_recording(priv->cm->textRecording(), self);
571 } else if (priv->person) {
572 /* get the selected cm and print the text recording */
573 selected_cm_changed(self);
aviau039001d2016-09-29 16:39:05 -0400574 }
aviau039001d2016-09-29 16:39:05 -0400575}
576
577static void
578build_chat_view(ChatView* self)
579{
580 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
581
582 gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
583 gtk_widget_show(priv->webkit_chat_container);
584
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500585 /* keep name updated */
586 if (priv->call) {
AmarOk72e51242017-07-14 13:37:51 -0400587 priv->update_chat_info = QObject::connect(
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500588 priv->call,
589 &Call::changed,
AmarOk72e51242017-07-14 13:37:51 -0400590 [self] () { update_chat_info(self); }
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500591 );
592 } else if (priv->cm) {
AmarOk72e51242017-07-14 13:37:51 -0400593 priv->update_chat_info = QObject::connect(
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500594 priv->cm,
595 &ContactMethod::changed,
AmarOk72e51242017-07-14 13:37:51 -0400596 [self] () { update_chat_info(self); }
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500597 );
598 } else if (priv->person) {
AmarOk72e51242017-07-14 13:37:51 -0400599 priv->update_chat_info = QObject::connect(
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500600 priv->person,
601 &Person::changed,
AmarOk72e51242017-07-14 13:37:51 -0400602 [self] () { update_chat_info(self); }
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500603 );
604 }
AmarOk72e51242017-07-14 13:37:51 -0400605 update_chat_info(self);
aviau039001d2016-09-29 16:39:05 -0400606
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500607 /* keep selected cm updated */
608 update_contact_methods(self);
609 g_signal_connect_swapped(priv->combobox_cm, "changed", G_CALLBACK(selected_cm_changed), self);
610
611 priv->webkit_ready = g_signal_connect_swapped(
aviau039001d2016-09-29 16:39:05 -0400612 priv->webkit_chat_container,
613 "ready",
614 G_CALLBACK(webkit_chat_container_ready),
615 self
616 );
617
AmarOkb4253242017-07-13 11:21:39 -0400618 priv->webkit_send_text = g_signal_connect(priv->webkit_chat_container,
619 "send-message",
620 G_CALLBACK(webkit_chat_container_send_text),
621 self);
622
aviau039001d2016-09-29 16:39:05 -0400623 if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
aviau039001d2016-09-29 16:39:05 -0400624 webkit_chat_container_ready(self);
aviau039001d2016-09-29 16:39:05 -0400625
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500626 /* we only show the chat info in the case of cm / person */
627 gtk_widget_set_visible(priv->hbox_chat_info, (priv->cm || priv->person));
aviau039001d2016-09-29 16:39:05 -0400628}
629
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500630GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400631chat_view_new_call(WebKitChatContainer *webkit_chat_container, Call *call)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500632{
633 g_return_val_if_fail(call, nullptr);
634
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500635 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500636
aviau039001d2016-09-29 16:39:05 -0400637 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
638 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500639 priv->call = call;
aviau039001d2016-09-29 16:39:05 -0400640
641 build_chat_view(self);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500642
643 return (GtkWidget *)self;
644}
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500645
646GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400647chat_view_new_cm(WebKitChatContainer *webkit_chat_container, ContactMethod *cm)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500648{
649 g_return_val_if_fail(cm, nullptr);
650
651 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500652
aviau039001d2016-09-29 16:39:05 -0400653 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
654 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500655 priv->cm = cm;
aviau039001d2016-09-29 16:39:05 -0400656
657 build_chat_view(self);
658
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500659 return (GtkWidget *)self;
660}
661
662GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400663chat_view_new_person(WebKitChatContainer *webkit_chat_container, Person *p)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500664{
665 g_return_val_if_fail(p, nullptr);
666
667 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500668
aviau039001d2016-09-29 16:39:05 -0400669 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
670 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500671 priv->person = p;
672
aviau039001d2016-09-29 16:39:05 -0400673 build_chat_view(self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500674
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500675 return (GtkWidget *)self;
676}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400677
678Call*
679chat_view_get_call(ChatView *self)
680{
681 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
682 auto priv = CHAT_VIEW_GET_PRIVATE(self);
683
684 return priv->call;
685}
686
687ContactMethod*
688chat_view_get_cm(ChatView *self)
689{
690 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
691 auto priv = CHAT_VIEW_GET_PRIVATE(self);
692
693 return priv->cm;
694}
695
696Person*
697chat_view_get_person(ChatView *self)
698{
699 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
700 auto priv = CHAT_VIEW_GET_PRIVATE(self);
701
702 return priv->person;
703}
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400704
705void
706chat_view_set_header_visible(ChatView *self, gboolean visible)
707{
708 auto priv = CHAT_VIEW_GET_PRIVATE(self);
709
710 gtk_widget_set_visible(priv->hbox_chat_info, visible);
711}