blob: 4ab82f036895832f37b589c8147951b52412e533 [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 *button_chat_input;
62 GtkWidget *entry_chat_input;
63 GtkWidget *scrolledwindow_chat;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050064 GtkWidget *hbox_chat_info;
65 GtkWidget *label_peer;
66 GtkWidget *combobox_cm;
Stepan Salenikovich8043a562016-03-18 13:56:40 -040067 GtkWidget *button_close_chatview;
Stepan Salenikoviche9933242016-06-21 18:08:48 -040068 GtkWidget *button_placecall;
Nicolas Jager759faea2017-03-22 16:22:00 -040069 GtkWidget *button_send_invitation;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050070
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050071 /* only one of the three following pointers should be non void;
72 * either this is an in-call chat (and so the in-call chat APIs will be used)
aviau039001d2016-09-29 16:39:05 -040073 * or it is an out of call chat (and so the account chat APIs will be used) */
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050074 Call *call;
75 Person *person;
76 ContactMethod *cm;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050077
78 QMetaObject::Connection new_message_connection;
aviau039001d2016-09-29 16:39:05 -040079 QMetaObject::Connection message_changed_connection;
Stepan Salenikovich1f731212016-11-10 11:55:10 -050080 QMetaObject::Connection update_name;
81
82 gulong webkit_ready;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050083};
84
85G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
86
87#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))
88
89enum {
90 NEW_MESSAGES_DISPLAYED,
Stepan Salenikovich8043a562016-03-18 13:56:40 -040091 HIDE_VIEW_CLICKED,
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050092 LAST_SIGNAL
93};
94
95static guint chat_view_signals[LAST_SIGNAL] = { 0 };
96
97static void
98chat_view_dispose(GObject *object)
99{
100 ChatView *view;
101 ChatViewPrivate *priv;
102
103 view = CHAT_VIEW(object);
104 priv = CHAT_VIEW_GET_PRIVATE(view);
105
106 QObject::disconnect(priv->new_message_connection);
aviau039001d2016-09-29 16:39:05 -0400107 QObject::disconnect(priv->message_changed_connection);
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500108 QObject::disconnect(priv->update_name);
aviau039001d2016-09-29 16:39:05 -0400109
110 /* Destroying the box will also destroy its children, and we wouldn't
111 * want that. So we remove the webkit_chat_container from the box. */
112 if (priv->webkit_chat_container) {
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500113 /* disconnect for webkit signals */
114 g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
115 priv->webkit_ready = 0;
116
aviau039001d2016-09-29 16:39:05 -0400117 gtk_container_remove(
118 GTK_CONTAINER(priv->box_webkit_chat_container),
119 GTK_WIDGET(priv->webkit_chat_container)
120 );
121 priv->webkit_chat_container = nullptr;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500122
aviau039001d2016-09-29 16:39:05 -0400123 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500124
125 G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
126}
127
128
129static void
130send_chat(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
131{
132 g_return_if_fail(IS_CHAT_VIEW(self));
133 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
134
Stepan Salenikovich4fbc3bf2016-11-03 15:01:10 -0400135 /* make sure there is more than just whitespace, but if so, send the original text */
136 const auto text = QString(gtk_entry_get_text(GTK_ENTRY(priv->entry_chat_input)));
137 if (!text.trimmed().isEmpty()) {
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500138 QMap<QString, QString> messages;
139 messages["text/plain"] = text;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500140
141 if (priv->call) {
142 // in call message
143 priv->call->addOutgoingMedia<Media::Text>()->send(messages);
144 } else if (priv->person) {
145 // get the chosen cm
146 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
147 if (active >= 0) {
148 auto cm = priv->person->phoneNumbers().at(active);
149 if (!cm->sendOfflineTextMessage(messages))
150 g_warning("message failed to send"); // TODO: warn the user about this in the UI
151 } else {
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400152 g_warning("no ContactMethod chosen; message not sent");
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500153 }
154 } else if (priv->cm) {
155 if (!priv->cm->sendOfflineTextMessage(messages))
156 g_warning("message failed to send"); // TODO: warn the user about this in the UI
157 } else {
158 g_warning("no Call, Person, or ContactMethod set; message not sent");
159 }
160
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500161 /* clear the entry */
162 gtk_entry_set_text(GTK_ENTRY(priv->entry_chat_input), "");
163 }
164}
165
166static void
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400167hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
168{
169 g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
170}
171
172static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400173placecall_clicked(ChatView *self)
174{
175 auto priv = CHAT_VIEW_GET_PRIVATE(self);
176
177 if (priv->person) {
178 // get the chosen cm
179 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
180 if (active >= 0) {
181 auto cm = priv->person->phoneNumbers().at(active);
182 place_new_call(cm);
183 } else {
184 g_warning("no ContactMethod chosen; cannot place call");
185 }
186 } else if (priv->cm) {
187 place_new_call(priv->cm);
188 } else {
189 g_warning("no Person or ContactMethod set; cannot place call");
190 }
191}
192
193static void
Nicolas Jager759faea2017-03-22 16:22:00 -0400194button_send_invitation_clicked(ChatView *self)
195{
196 auto priv = CHAT_VIEW_GET_PRIVATE(self);
197
198 // get the account associated to the selected cm
199 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
200
Guillaume Roguezcea60ca2017-06-02 16:38:25 -0400201 if (priv->person) {
202 auto& numbers = priv->person->phoneNumbers();
203 if (not numbers.isEmpty())
204 priv->cm = numbers.at(active);
205 }
Nicolas Jager759faea2017-03-22 16:22:00 -0400206
207 if (!priv->cm) {
208 g_warning("invalid contact, cannot send invitation!");
209 return;
210 }
211
212 // try first to use the account associated to the contact method
213 auto account = priv->cm->account();
214 if (not account) {
215
216 // get the choosen account
217 account = get_active_ring_account();
218
219 if (not account) {
220 g_warning("invalid account, cannot send invitation!");
221 return;
222 }
223 }
224
225 // perform the request
226 if (not account->sendContactRequest(priv->cm))
227 g_warning("contact request not forwarded, cannot send invitation!");
228
229 // TODO : add an entry in the conversation to tell the user an invitation was sent.
230}
231
232static void
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500233chat_view_init(ChatView *view)
234{
235 gtk_widget_init_template(GTK_WIDGET(view));
236
237 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
238
239 g_signal_connect(priv->button_chat_input, "clicked", G_CALLBACK(send_chat), view);
240 g_signal_connect(priv->entry_chat_input, "activate", G_CALLBACK(send_chat), view);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400241 g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400242 g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
Nicolas Jager759faea2017-03-22 16:22:00 -0400243 g_signal_connect_swapped(priv->button_send_invitation, "clicked", G_CALLBACK(button_send_invitation_clicked), view);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500244}
245
246static void
247chat_view_class_init(ChatViewClass *klass)
248{
249 G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;
250
251 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
252 "/cx/ring/RingGnome/chatview.ui");
253
aviau039001d2016-09-29 16:39:05 -0400254 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500255 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_chat_input);
256 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, entry_chat_input);
257 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, scrolledwindow_chat);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500258 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
259 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
260 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, combobox_cm);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400261 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400262 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
Nicolas Jager759faea2017-03-22 16:22:00 -0400263 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_send_invitation);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500264
265 chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
266 "new-messages-displayed",
267 G_TYPE_FROM_CLASS(klass),
268 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
269 0,
270 nullptr,
271 nullptr,
272 g_cclosure_marshal_VOID__VOID,
273 G_TYPE_NONE, 0);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400274
275 chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
276 "hide-view-clicked",
277 G_TYPE_FROM_CLASS(klass),
278 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
279 0,
280 nullptr,
281 nullptr,
282 g_cclosure_marshal_VOID__VOID,
283 G_TYPE_NONE, 0);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500284}
285
aviau039001d2016-09-29 16:39:05 -0400286
287
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500288static void
aviau039001d2016-09-29 16:39:05 -0400289print_message_to_buffer(ChatView* self, const QModelIndex &idx)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500290{
aviau039001d2016-09-29 16:39:05 -0400291 if (!idx.isValid()) {
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500292 g_warning("QModelIndex in im model is not valid");
aviau039001d2016-09-29 16:39:05 -0400293 return;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500294 }
aviau039001d2016-09-29 16:39:05 -0400295
296 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
297
298 webkit_chat_container_print_new_message(
299 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
300 idx
301 );
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500302}
303
aviaufc213552016-11-01 12:39:39 -0400304ContactMethod*
305get_active_contactmethod(ChatView *self)
306{
307 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
308
309 auto cms = priv->person->phoneNumbers();
310 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
311 if (active >= 0 && active < cms.size()) {
312 return cms.at(active);
313 } else {
314 return nullptr;
315 }
316}
317
318static void
319set_participant_images(ChatView* self)
320{
321 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
322
323 webkit_chat_container_clear_sender_images(
324 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
325 );
326
327 /* Set the sender image for the peer */
328 ContactMethod* sender_contact_method_peer;
329 QVariant photo_variant_peer;
330
331 if (priv->person)
332 {
333 photo_variant_peer = priv->person->photo();
334 sender_contact_method_peer = get_active_contactmethod(self);
335 }
336 else
337 {
338 if (priv->cm)
339 {
340 sender_contact_method_peer = priv->cm;
341 }
342 else
343 {
344 sender_contact_method_peer = priv->call->peerContactMethod();
345 }
346 photo_variant_peer = sender_contact_method_peer->roleData((int) Call::Role::Photo);
347 }
348
349 if (photo_variant_peer.isValid())
350 {
351 webkit_chat_container_set_sender_image(
352 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
353 sender_contact_method_peer,
354 photo_variant_peer
355 );
356 }
357
358 /* set sender image for "ME" */
359 auto profile = ProfileModel::instance().selectedProfile();
360 if (profile)
361 {
362 auto person = profile->person();
363 if (person)
364 {
365 auto photo_variant_me = person->photo();
366 if (photo_variant_me.isValid())
367 {
368 webkit_chat_container_set_sender_image(
369 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
370 nullptr,
371 photo_variant_me
372 );
373 }
374 }
375 }
376}
377
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500378static void
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500379print_text_recording(Media::TextRecording *recording, ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500380{
381 g_return_if_fail(IS_CHAT_VIEW(self));
382 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
383
aviaufc213552016-11-01 12:39:39 -0400384 /* set the photos of the chat participants */
385 set_participant_images(self);
386
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500387 /* only text messages are supported for now */
388 auto model = recording->instantTextMessagingModel();
389
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500390 /* new model, disconnect from the old model updates and clear the text buffer */
391 QObject::disconnect(priv->new_message_connection);
392
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500393 /* put all the messages in the im model into the text view */
394 for (int row = 0; row < model->rowCount(); ++row) {
395 QModelIndex idx = model->index(row, 0);
aviau039001d2016-09-29 16:39:05 -0400396 print_message_to_buffer(self, idx);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500397 }
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500398 /* mark all messages as read */
399 recording->setAllRead();
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500400
aviau039001d2016-09-29 16:39:05 -0400401
402 /* messages modified */
403 /* connecting on instantMessagingModel and not textMessagingModel */
404 priv->message_changed_connection = QObject::connect(
405 model,
406 &QAbstractItemModel::dataChanged,
407 [self, priv] (const QModelIndex & topLeft, G_GNUC_UNUSED const QModelIndex & bottomRight)
408 {
409 webkit_chat_container_update_message(
410 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
411 topLeft
412 );
413 }
414 );
415
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500416 /* append new messages */
417 priv->new_message_connection = QObject::connect(
418 model,
419 &QAbstractItemModel::rowsInserted,
420 [self, priv, model] (const QModelIndex &parent, int first, int last) {
421 for (int row = first; row <= last; ++row) {
422 QModelIndex idx = model->index(row, 0, parent);
aviau039001d2016-09-29 16:39:05 -0400423 print_message_to_buffer(self, idx);
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500424 /* make sure these messages are marked as read */
425 model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500426 g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0);
427 }
428 }
429 );
430}
431
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500432static void
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500433selected_cm_changed(ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500434{
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500435 auto priv = CHAT_VIEW_GET_PRIVATE(self);
436
437 /* make sure the webkit view is ready, in case we get this signal before it is */
438 if (!webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
439 return;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500440
aviaufc213552016-11-01 12:39:39 -0400441 auto cm = get_active_contactmethod(self);
442 if (cm){
443 print_text_recording(cm->textRecording(), self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500444 } else {
445 g_warning("no valid ContactMethod selected to display chat conversation");
446 }
447}
448
449static void
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500450render_contact_method(G_GNUC_UNUSED GtkCellLayout *cell_layout,
451 GtkCellRenderer *cell,
452 GtkTreeModel *model,
453 GtkTreeIter *iter,
454 G_GNUC_UNUSED gpointer data)
455{
456 GValue value = G_VALUE_INIT;
457 gtk_tree_model_get_value(model, iter, 0, &value);
458 auto cm = (ContactMethod *)g_value_get_pointer(&value);
459
460 gchar *number = nullptr;
461 if (cm && cm->category()) {
462 // try to get the number category, eg: "home"
463 number = g_strdup_printf("(%s) %s", cm->category()->name().toUtf8().constData(),
Houminbeab3c92017-05-16 11:12:16 +0800464 cm->bestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500465 } else if (cm) {
Houminbeab3c92017-05-16 11:12:16 +0800466 number = g_strdup_printf("%s", cm->bestId().toUtf8().constData());
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500467 }
468
469 g_object_set(G_OBJECT(cell), "text", number, NULL);
470 g_free(number);
471}
472
473static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500474update_contact_methods(ChatView *self)
475{
476 g_return_if_fail(IS_CHAT_VIEW(self));
477 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
478
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500479 g_return_if_fail(priv->call || priv->person || priv->cm);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500480
481 /* model for the combobox for the choice of ContactMethods */
482 auto cm_model = gtk_list_store_new(
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500483 1, G_TYPE_POINTER
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500484 );
485
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400486 Person::ContactMethods cms;
487
488 if (priv->person)
489 cms = priv->person->phoneNumbers();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500490 else if (priv->cm)
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400491 cms << priv->cm;
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500492 else if (priv->call)
493 cms << priv->call->peerContactMethod();
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400494
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500495 for (int i = 0; i < cms.size(); ++i) {
496 GtkTreeIter iter;
497 gtk_list_store_append(cm_model, &iter);
498 gtk_list_store_set(cm_model, &iter,
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500499 0, cms.at(i),
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500500 -1);
501 }
502
503 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_cm), GTK_TREE_MODEL(cm_model));
504 g_object_unref(cm_model);
505
506 auto renderer = gtk_cell_renderer_text_new();
507 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
508 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_cm), renderer, FALSE);
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500509 gtk_cell_layout_set_cell_data_func(
510 GTK_CELL_LAYOUT(priv->combobox_cm),
511 renderer,
512 (GtkCellLayoutDataFunc)render_contact_method,
513 nullptr, nullptr);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500514
515 /* select the last used cm */
516 if (!cms.isEmpty()) {
517 auto last_used_cm = cms.at(0);
518 int last_used_cm_idx = 0;
519 for (int i = 1; i < cms.size(); ++i) {
520 auto new_cm = cms.at(i);
521 if (difftime(new_cm->lastUsed(), last_used_cm->lastUsed()) > 0) {
522 last_used_cm = new_cm;
523 last_used_cm_idx = i;
524 }
525 }
526
527 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_cm), last_used_cm_idx);
528 }
529
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400530 /* if there is only one CM, make the combo box insensitive */
531 if (cms.size() < 2)
532 gtk_widget_set_sensitive(priv->combobox_cm, FALSE);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400533
534 /* if no CMs make the call button insensitive */
535 gtk_widget_set_sensitive(priv->button_placecall, !cms.isEmpty());
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500536}
537
538static void
539update_name(ChatView *self)
540{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500541 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
542
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500543 g_return_if_fail(priv->person || priv->cm || priv->call);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500544
545 QString name;
546 if (priv->person) {
547 name = priv->person->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500548 } else if (priv->cm) {
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500549 name = priv->cm->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500550 } else if (priv->call) {
551 name = priv->call->peerContactMethod()->roleData(static_cast<int>(Ring::Role::Name)).toString();
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500552 }
553 gtk_label_set_text(GTK_LABEL(priv->label_peer), name.toUtf8().constData());
554}
555
aviau039001d2016-09-29 16:39:05 -0400556static void
aviau039001d2016-09-29 16:39:05 -0400557webkit_chat_container_ready(ChatView* self)
558{
559 /* The webkit chat container has loaded the javascript libraries, we can
560 * now use it. */
561
562 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
563
564 webkit_chat_container_clear(
565 WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
566 );
567
aviau039001d2016-09-29 16:39:05 -0400568 /* print the text recordings */
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500569 if (priv->call) {
570 print_text_recording(priv->call->peerContactMethod()->textRecording(), self);
571 } else if (priv->cm) {
572 print_text_recording(priv->cm->textRecording(), self);
573 } else if (priv->person) {
574 /* get the selected cm and print the text recording */
575 selected_cm_changed(self);
aviau039001d2016-09-29 16:39:05 -0400576 }
aviau039001d2016-09-29 16:39:05 -0400577}
578
579static void
580build_chat_view(ChatView* self)
581{
582 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
583
584 gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
585 gtk_widget_show(priv->webkit_chat_container);
586
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500587 /* keep name updated */
588 if (priv->call) {
589 priv->update_name = QObject::connect(
590 priv->call,
591 &Call::changed,
592 [self] () { update_name(self); }
593 );
594 } else if (priv->cm) {
595 priv->update_name = QObject::connect(
596 priv->cm,
597 &ContactMethod::changed,
598 [self] () { update_name(self); }
599 );
600 } else if (priv->person) {
601 priv->update_name = QObject::connect(
602 priv->person,
603 &Person::changed,
604 [self] () { update_name(self); }
605 );
606 }
607 update_name(self);
aviau039001d2016-09-29 16:39:05 -0400608
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500609 /* keep selected cm updated */
610 update_contact_methods(self);
611 g_signal_connect_swapped(priv->combobox_cm, "changed", G_CALLBACK(selected_cm_changed), self);
612
613 priv->webkit_ready = g_signal_connect_swapped(
aviau039001d2016-09-29 16:39:05 -0400614 priv->webkit_chat_container,
615 "ready",
616 G_CALLBACK(webkit_chat_container_ready),
617 self
618 );
619
620 if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
aviau039001d2016-09-29 16:39:05 -0400621 webkit_chat_container_ready(self);
aviau039001d2016-09-29 16:39:05 -0400622
Stepan Salenikovich1f731212016-11-10 11:55:10 -0500623 /* we only show the chat info in the case of cm / person */
624 gtk_widget_set_visible(priv->hbox_chat_info, (priv->cm || priv->person));
aviau039001d2016-09-29 16:39:05 -0400625}
626
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500627GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400628chat_view_new_call(WebKitChatContainer *webkit_chat_container, Call *call)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500629{
630 g_return_val_if_fail(call, nullptr);
631
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500632 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500633
aviau039001d2016-09-29 16:39:05 -0400634 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
635 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500636 priv->call = call;
aviau039001d2016-09-29 16:39:05 -0400637
638 build_chat_view(self);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500639
640 return (GtkWidget *)self;
641}
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500642
643GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400644chat_view_new_cm(WebKitChatContainer *webkit_chat_container, ContactMethod *cm)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500645{
646 g_return_val_if_fail(cm, nullptr);
647
648 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500649
aviau039001d2016-09-29 16:39:05 -0400650 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
651 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500652 priv->cm = cm;
aviau039001d2016-09-29 16:39:05 -0400653
654 build_chat_view(self);
655
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500656 return (GtkWidget *)self;
657}
658
659GtkWidget *
aviau039001d2016-09-29 16:39:05 -0400660chat_view_new_person(WebKitChatContainer *webkit_chat_container, Person *p)
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500661{
662 g_return_val_if_fail(p, nullptr);
663
664 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500665
aviau039001d2016-09-29 16:39:05 -0400666 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
667 priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500668 priv->person = p;
669
aviau039001d2016-09-29 16:39:05 -0400670 build_chat_view(self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500671
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500672 return (GtkWidget *)self;
673}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400674
675Call*
676chat_view_get_call(ChatView *self)
677{
678 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
679 auto priv = CHAT_VIEW_GET_PRIVATE(self);
680
681 return priv->call;
682}
683
684ContactMethod*
685chat_view_get_cm(ChatView *self)
686{
687 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
688 auto priv = CHAT_VIEW_GET_PRIVATE(self);
689
690 return priv->cm;
691}
692
693Person*
694chat_view_get_person(ChatView *self)
695{
696 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
697 auto priv = CHAT_VIEW_GET_PRIVATE(self);
698
699 return priv->person;
700}
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400701
702void
703chat_view_set_header_visible(ChatView *self, gboolean visible)
704{
705 auto priv = CHAT_VIEW_GET_PRIVATE(self);
706
707 gtk_widget_set_visible(priv->hbox_chat_info, visible);
708}