blob: 1e564b012cc7e2e1b152f9e769458940a5856e97 [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>
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.
18 */
19
20#include "chatview.h"
21
22#include <gtk/gtk.h>
23#include <call.h>
24#include <callmodel.h>
25#include <contactmethod.h>
26#include <person.h>
27#include <media/media.h>
28#include <media/text.h>
29#include <media/textrecording.h>
30#include "ringnotify.h"
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -050031#include "numbercategory.h"
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -050032#include <QtCore/QDateTime>
Stepan Salenikoviche9933242016-06-21 18:08:48 -040033#include "utils/calling.h"
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050034
Stepan Salenikovichce06adb2016-02-19 12:53:53 -050035static constexpr GdkRGBA RING_BLUE = {0.0508, 0.594, 0.676, 1.0}; // outgoing msg color: (13, 152, 173)
36
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050037struct _ChatView
38{
39 GtkBox parent;
40};
41
42struct _ChatViewClass
43{
44 GtkBoxClass parent_class;
45};
46
47typedef struct _ChatViewPrivate ChatViewPrivate;
48
49struct _ChatViewPrivate
50{
51 GtkWidget *textview_chat;
52 GtkWidget *button_chat_input;
53 GtkWidget *entry_chat_input;
54 GtkWidget *scrolledwindow_chat;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050055 GtkWidget *hbox_chat_info;
56 GtkWidget *label_peer;
57 GtkWidget *combobox_cm;
Stepan Salenikovich8043a562016-03-18 13:56:40 -040058 GtkWidget *button_close_chatview;
Stepan Salenikoviche9933242016-06-21 18:08:48 -040059 GtkWidget *button_placecall;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050060
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -050061 /* only one of the three following pointers should be non void;
62 * either this is an in-call chat (and so the in-call chat APIs will be used)
63 * or it is an out of call chat (and so the account chat APIs will be used)
64 */
65 Call *call;
66 Person *person;
67 ContactMethod *cm;
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050068
69 QMetaObject::Connection new_message_connection;
70};
71
72G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);
73
74#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))
75
76enum {
77 NEW_MESSAGES_DISPLAYED,
Stepan Salenikovich8043a562016-03-18 13:56:40 -040078 HIDE_VIEW_CLICKED,
Stepan Salenikovichd2cad062016-01-08 13:43:49 -050079 LAST_SIGNAL
80};
81
82static guint chat_view_signals[LAST_SIGNAL] = { 0 };
83
84static void
85chat_view_dispose(GObject *object)
86{
87 ChatView *view;
88 ChatViewPrivate *priv;
89
90 view = CHAT_VIEW(object);
91 priv = CHAT_VIEW_GET_PRIVATE(view);
92
93 QObject::disconnect(priv->new_message_connection);
94
95 G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
96}
97
98
99static void
100send_chat(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
101{
102 g_return_if_fail(IS_CHAT_VIEW(self));
103 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
104
105 /* make sure there is text to send */
106 const gchar *text = gtk_entry_get_text(GTK_ENTRY(priv->entry_chat_input));
107 if (text && strlen(text) > 0) {
108 QMap<QString, QString> messages;
109 messages["text/plain"] = text;
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500110
111 if (priv->call) {
112 // in call message
113 priv->call->addOutgoingMedia<Media::Text>()->send(messages);
114 } else if (priv->person) {
115 // get the chosen cm
116 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
117 if (active >= 0) {
118 auto cm = priv->person->phoneNumbers().at(active);
119 if (!cm->sendOfflineTextMessage(messages))
120 g_warning("message failed to send"); // TODO: warn the user about this in the UI
121 } else {
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400122 g_warning("no ContactMethod chosen; message not sent");
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500123 }
124 } else if (priv->cm) {
125 if (!priv->cm->sendOfflineTextMessage(messages))
126 g_warning("message failed to send"); // TODO: warn the user about this in the UI
127 } else {
128 g_warning("no Call, Person, or ContactMethod set; message not sent");
129 }
130
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500131 /* clear the entry */
132 gtk_entry_set_text(GTK_ENTRY(priv->entry_chat_input), "");
133 }
134}
135
136static void
137scroll_to_bottom(GtkAdjustment *adjustment, G_GNUC_UNUSED gpointer user_data)
138{
139 gtk_adjustment_set_value(adjustment,
140 gtk_adjustment_get_upper(adjustment) - gtk_adjustment_get_page_size(adjustment));
141}
142
143static void
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400144hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
145{
146 g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
147}
148
149static void
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400150placecall_clicked(ChatView *self)
151{
152 auto priv = CHAT_VIEW_GET_PRIVATE(self);
153
154 if (priv->person) {
155 // get the chosen cm
156 auto active = gtk_combo_box_get_active(GTK_COMBO_BOX(priv->combobox_cm));
157 if (active >= 0) {
158 auto cm = priv->person->phoneNumbers().at(active);
159 place_new_call(cm);
160 } else {
161 g_warning("no ContactMethod chosen; cannot place call");
162 }
163 } else if (priv->cm) {
164 place_new_call(priv->cm);
165 } else {
166 g_warning("no Person or ContactMethod set; cannot place call");
167 }
168}
169
170static void
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500171chat_view_init(ChatView *view)
172{
173 gtk_widget_init_template(GTK_WIDGET(view));
174
175 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
176
177 g_signal_connect(priv->button_chat_input, "clicked", G_CALLBACK(send_chat), view);
178 g_signal_connect(priv->entry_chat_input, "activate", G_CALLBACK(send_chat), view);
179
180 /* the adjustment params will change only when the model is created and when
181 * new messages are added; in these cases we want to scroll to the bottom of
182 * the chat treeview */
183 GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(priv->scrolledwindow_chat));
184 g_signal_connect(adjustment, "changed", G_CALLBACK(scroll_to_bottom), NULL);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400185
186 g_signal_connect(priv->button_close_chatview, "clicked", G_CALLBACK(hide_chat_view), view);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400187
188 g_signal_connect_swapped(priv->button_placecall, "clicked", G_CALLBACK(placecall_clicked), view);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500189}
190
191static void
192chat_view_class_init(ChatViewClass *klass)
193{
194 G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;
195
196 gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
197 "/cx/ring/RingGnome/chatview.ui");
198
199 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, textview_chat);
200 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_chat_input);
201 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, entry_chat_input);
202 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, scrolledwindow_chat);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500203 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, hbox_chat_info);
204 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, label_peer);
205 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, combobox_cm);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400206 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_close_chatview);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400207 gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, button_placecall);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500208
209 chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
210 "new-messages-displayed",
211 G_TYPE_FROM_CLASS(klass),
212 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
213 0,
214 nullptr,
215 nullptr,
216 g_cclosure_marshal_VOID__VOID,
217 G_TYPE_NONE, 0);
Stepan Salenikovich8043a562016-03-18 13:56:40 -0400218
219 chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
220 "hide-view-clicked",
221 G_TYPE_FROM_CLASS(klass),
222 (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
223 0,
224 nullptr,
225 nullptr,
226 g_cclosure_marshal_VOID__VOID,
227 G_TYPE_NONE, 0);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500228}
229
230static void
231print_message_to_buffer(const QModelIndex &idx, GtkTextBuffer *buffer)
232{
233 if (idx.isValid()) {
234 auto message = idx.data().value<QString>().toUtf8();
235 auto sender = idx.data(static_cast<int>(Media::TextRecording::Role::AuthorDisplayname)).value<QString>().toUtf8();
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -0500236 auto timestamp = idx.data(static_cast<int>(Media::TextRecording::Role::Timestamp)).value<time_t>();
237 auto datetime = QDateTime::fromTime_t(timestamp);
Stepan Salenikovichce06adb2016-02-19 12:53:53 -0500238 auto direction = idx.data(static_cast<int>(Media::TextRecording::Role::Direction)).value<Media::Media::Direction>();
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500239
240 GtkTextIter iter;
241
242 /* unless its the very first message, insert a new line */
243 if (idx.row() != 0) {
244 gtk_text_buffer_get_end_iter(buffer, &iter);
245 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
246 }
247
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -0500248 /* if it is the very first row, we print the current date;
249 * otherwise we print the date every time it is different from the previous message */
250 auto date = datetime.date();
251 gchar* new_date = nullptr;
252 if (idx.row() == 0) {
253 new_date = g_strconcat("-- ", date.toString().toUtf8().constData(), " --\n", NULL);
254 } else {
255 auto prev_timestamp = idx.sibling(idx.row() - 1, 0).data(static_cast<int>(Media::TextRecording::Role::Timestamp)).value<time_t>();
256 auto prev_date = QDateTime::fromTime_t(prev_timestamp).date();
257 if (date != prev_date) {
258 new_date = g_strconcat("-- ", date.toString().toUtf8().constData(), " --\n", NULL);
259 }
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500260 }
261
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -0500262 if (new_date) {
263 gtk_text_buffer_get_end_iter(buffer, &iter);
264 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, new_date, -1, "center", NULL);
265 }
266
267 /* insert time */
268 gtk_text_buffer_get_end_iter(buffer, &iter);
269 gtk_text_buffer_insert(buffer, &iter, datetime.time().toString().toUtf8().constData(), -1);
270
271 /* insert sender */
272 auto format_sender = g_strconcat(" ", sender.constData(), ": ", NULL);
273 gtk_text_buffer_get_end_iter(buffer, &iter);
Stepan Salenikovichce06adb2016-02-19 12:53:53 -0500274 if (direction == Media::Media::Direction::OUT)
275 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, format_sender, -1, "bold-blue", NULL);
276 else
277 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, format_sender, -1, "bold", NULL);
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -0500278 g_free(format_sender);
279
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500280 gtk_text_buffer_get_end_iter(buffer, &iter);
Stepan Salenikovichce06adb2016-02-19 12:53:53 -0500281 if (direction == Media::Media::Direction::OUT)
282 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, message.constData(), -1, "blue", NULL);
283 else
284 gtk_text_buffer_insert(buffer, &iter, message.constData(), -1);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500285
286 } else {
287 g_warning("QModelIndex in im model is not valid");
288 }
289}
290
291static void
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500292print_text_recording(Media::TextRecording *recording, ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500293{
294 g_return_if_fail(IS_CHAT_VIEW(self));
295 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
296
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500297 /* only text messages are supported for now */
298 auto model = recording->instantTextMessagingModel();
299
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500300 /* new model, disconnect from the old model updates and clear the text buffer */
301 QObject::disconnect(priv->new_message_connection);
302
303 GtkTextBuffer *new_buffer = gtk_text_buffer_new(NULL);
304 gtk_text_view_set_buffer(GTK_TEXT_VIEW(priv->textview_chat), new_buffer);
305
306 /* add tags to the buffer */
307 gtk_text_buffer_create_tag(new_buffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
Stepan Salenikovich1b7100a2016-02-19 11:50:46 -0500308 gtk_text_buffer_create_tag(new_buffer, "center", "justification", GTK_JUSTIFY_CENTER, NULL);
Stepan Salenikovichce06adb2016-02-19 12:53:53 -0500309 gtk_text_buffer_create_tag(new_buffer, "bold-blue", "weight", PANGO_WEIGHT_BOLD, "foreground-rgba", &RING_BLUE, NULL);
310 gtk_text_buffer_create_tag(new_buffer, "blue", "foreground-rgba", &RING_BLUE, NULL);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500311
312 g_object_unref(new_buffer);
313
314 /* put all the messages in the im model into the text view */
315 for (int row = 0; row < model->rowCount(); ++row) {
316 QModelIndex idx = model->index(row, 0);
317 print_message_to_buffer(idx, new_buffer);
318 }
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500319 /* mark all messages as read */
320 recording->setAllRead();
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500321
322 /* append new messages */
323 priv->new_message_connection = QObject::connect(
324 model,
325 &QAbstractItemModel::rowsInserted,
326 [self, priv, model] (const QModelIndex &parent, int first, int last) {
327 for (int row = first; row <= last; ++row) {
328 QModelIndex idx = model->index(row, 0, parent);
329 print_message_to_buffer(idx, gtk_text_view_get_buffer(GTK_TEXT_VIEW(priv->textview_chat)));
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500330 /* make sure these messages are marked as read */
331 model->setData(idx, true, static_cast<int>(Media::TextRecording::Role::IsRead));
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500332 g_signal_emit(G_OBJECT(self), chat_view_signals[NEW_MESSAGES_DISPLAYED], 0);
333 }
334 }
335 );
336}
337
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500338static void
339selected_cm_changed(GtkComboBox *box, ChatView *self)
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500340{
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500341 g_return_if_fail(IS_CHAT_VIEW(self));
342 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
343
344 auto cms = priv->person->phoneNumbers();
345 auto active = gtk_combo_box_get_active(box);
346 if (active >= 0 && active < cms.size()) {
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500347 print_text_recording(cms.at(active)->textRecording(), self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500348 } else {
349 g_warning("no valid ContactMethod selected to display chat conversation");
350 }
351}
352
353static void
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500354render_contact_method(G_GNUC_UNUSED GtkCellLayout *cell_layout,
355 GtkCellRenderer *cell,
356 GtkTreeModel *model,
357 GtkTreeIter *iter,
358 G_GNUC_UNUSED gpointer data)
359{
360 GValue value = G_VALUE_INIT;
361 gtk_tree_model_get_value(model, iter, 0, &value);
362 auto cm = (ContactMethod *)g_value_get_pointer(&value);
363
364 gchar *number = nullptr;
365 if (cm && cm->category()) {
366 // try to get the number category, eg: "home"
367 number = g_strdup_printf("(%s) %s", cm->category()->name().toUtf8().constData(),
368 cm->uri().toUtf8().constData());
369 } else if (cm) {
370 number = g_strdup_printf("%s", cm->uri().toUtf8().constData());
371 }
372
373 g_object_set(G_OBJECT(cell), "text", number, NULL);
374 g_free(number);
375}
376
377static void
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500378update_contact_methods(ChatView *self)
379{
380 g_return_if_fail(IS_CHAT_VIEW(self));
381 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
382
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400383 g_return_if_fail(priv->person || priv->cm);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500384
385 /* model for the combobox for the choice of ContactMethods */
386 auto cm_model = gtk_list_store_new(
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500387 1, G_TYPE_POINTER
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500388 );
389
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400390 Person::ContactMethods cms;
391
392 if (priv->person)
393 cms = priv->person->phoneNumbers();
394 else
395 cms << priv->cm;
396
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500397 for (int i = 0; i < cms.size(); ++i) {
398 GtkTreeIter iter;
399 gtk_list_store_append(cm_model, &iter);
400 gtk_list_store_set(cm_model, &iter,
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500401 0, cms.at(i),
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500402 -1);
403 }
404
405 gtk_combo_box_set_model(GTK_COMBO_BOX(priv->combobox_cm), GTK_TREE_MODEL(cm_model));
406 g_object_unref(cm_model);
407
408 auto renderer = gtk_cell_renderer_text_new();
409 g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
410 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->combobox_cm), renderer, FALSE);
Stepan Salenikovich5039c9b2016-02-12 14:09:51 -0500411 gtk_cell_layout_set_cell_data_func(
412 GTK_CELL_LAYOUT(priv->combobox_cm),
413 renderer,
414 (GtkCellLayoutDataFunc)render_contact_method,
415 nullptr, nullptr);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500416
417 /* select the last used cm */
418 if (!cms.isEmpty()) {
419 auto last_used_cm = cms.at(0);
420 int last_used_cm_idx = 0;
421 for (int i = 1; i < cms.size(); ++i) {
422 auto new_cm = cms.at(i);
423 if (difftime(new_cm->lastUsed(), last_used_cm->lastUsed()) > 0) {
424 last_used_cm = new_cm;
425 last_used_cm_idx = i;
426 }
427 }
428
429 gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combobox_cm), last_used_cm_idx);
430 }
431
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400432 /* if there is only one CM, make the combo box insensitive */
433 if (cms.size() < 2)
434 gtk_widget_set_sensitive(priv->combobox_cm, FALSE);
Stepan Salenikoviche9933242016-06-21 18:08:48 -0400435
436 /* if no CMs make the call button insensitive */
437 gtk_widget_set_sensitive(priv->button_placecall, !cms.isEmpty());
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500438}
439
440static void
441update_name(ChatView *self)
442{
443 g_return_if_fail(IS_CHAT_VIEW(self));
444 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
445
446 g_return_if_fail(priv->person || priv->cm);
447
448 QString name;
449 if (priv->person) {
450 name = priv->person->roleData(static_cast<int>(Ring::Role::Name)).toString();
451 } else {
452 name = priv->cm->roleData(static_cast<int>(Ring::Role::Name)).toString();
453 }
454 gtk_label_set_text(GTK_LABEL(priv->label_peer), name.toUtf8().constData());
455}
456
457GtkWidget *
458chat_view_new_call(Call *call)
459{
460 g_return_val_if_fail(call, nullptr);
461
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500462 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
463 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
464
465 priv->call = call;
466 auto cm = priv->call->peerContactMethod();
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500467 print_text_recording(cm->textRecording(), self);
Stepan Salenikovichd2cad062016-01-08 13:43:49 -0500468
469 return (GtkWidget *)self;
470}
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500471
472GtkWidget *
473chat_view_new_cm(ContactMethod *cm)
474{
475 g_return_val_if_fail(cm, nullptr);
476
477 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
478 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
479
480 priv->cm = cm;
Stepan Salenikovichd8765072016-01-14 10:58:51 -0500481 print_text_recording(priv->cm->textRecording(), self);
Stepan Salenikovichbbfa4402016-05-04 15:07:29 -0400482 update_contact_methods(self);
Stepan Salenikovichc6a3b982016-01-11 18:11:39 -0500483 update_name(self);
484
485 gtk_widget_show(priv->hbox_chat_info);
486
487 return (GtkWidget *)self;
488}
489
490GtkWidget *
491chat_view_new_person(Person *p)
492{
493 g_return_val_if_fail(p, nullptr);
494
495 ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));
496 ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
497
498 priv->person = p;
499
500 /* connect to the changed signal before setting the cm combo box, so that the correct
501 * conversation will get displayed */
502 g_signal_connect(priv->combobox_cm, "changed", G_CALLBACK(selected_cm_changed), self);
503 update_contact_methods(self);
504 update_name(self);
505
506 gtk_widget_show(priv->hbox_chat_info);
507
508 return (GtkWidget *)self;
509}
Stepan Salenikovich09e0b782016-09-07 16:28:50 -0400510
511Call*
512chat_view_get_call(ChatView *self)
513{
514 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
515 auto priv = CHAT_VIEW_GET_PRIVATE(self);
516
517 return priv->call;
518}
519
520ContactMethod*
521chat_view_get_cm(ChatView *self)
522{
523 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
524 auto priv = CHAT_VIEW_GET_PRIVATE(self);
525
526 return priv->cm;
527}
528
529Person*
530chat_view_get_person(ChatView *self)
531{
532 g_return_val_if_fail(IS_CHAT_VIEW(self), nullptr);
533 auto priv = CHAT_VIEW_GET_PRIVATE(self);
534
535 return priv->person;
536}
Stepan Salenikovichdaf3cb32016-10-12 16:39:42 -0400537
538void
539chat_view_set_header_visible(ChatView *self, gboolean visible)
540{
541 auto priv = CHAT_VIEW_GET_PRIVATE(self);
542
543 gtk_widget_set_visible(priv->hbox_chat_info, visible);
544}