chatview.cpp 22.5 KB
Newer Older
1
/*
Guillaume Roguez's avatar
Guillaume Roguez committed
2
 *  Copyright (C) 2016-2018 Savoir-faire Linux Inc.
3
 *  Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
aviau's avatar
aviau committed
4
 *  Author: Alexandre Viau <alexandre.viau@savoirfairelinux.com>
5
6
 *  Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>
 *  Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
7
 *  Author: Hugo Lefeuvre <hugo.lefeuvre@savoirfairelinux.com>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */

#include "chatview.h"

26
27
// std
#include <algorithm>
28

29
30
31
// GTK
#include <glib/gi18n.h>

32
33
34
35
// LRC
#include <api/contactmodel.h>
#include <api/conversationmodel.h>
#include <api/contact.h>
36

37
38
// Client
#include "utils/files.h"
39

40
41
42
43
44
45
46
47
48
49
50
51
52
53
struct _ChatView
{
    GtkBox parent;
};

struct _ChatViewClass
{
    GtkBoxClass parent_class;
};

typedef struct _ChatViewPrivate ChatViewPrivate;

struct _ChatViewPrivate
{
aviau's avatar
aviau committed
54
55
    GtkWidget *box_webkit_chat_container;
    GtkWidget *webkit_chat_container;
Stepan Salenikovich's avatar
Stepan Salenikovich committed
56

57
58
    GSettings *settings;

59
    lrc::api::conversation::Info* conversation_;
60
    AccountInfoPointer const * accountInfo_;
61

62
    QMetaObject::Connection new_interaction_connection;
63
    QMetaObject::Connection interaction_removed;
64
65
    QMetaObject::Connection update_interaction_connection;
    QMetaObject::Connection update_add_to_conversations;
66
67

    gulong webkit_ready;
68
    gulong webkit_send_text;
69
    gulong webkit_drag_drop;
70
71
72
73
74
75
76
77
};

G_DEFINE_TYPE_WITH_PRIVATE(ChatView, chat_view, GTK_TYPE_BOX);

#define CHAT_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CHAT_VIEW_TYPE, ChatViewPrivate))

enum {
    NEW_MESSAGES_DISPLAYED,
78
    HIDE_VIEW_CLICKED,
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    LAST_SIGNAL
};

static guint chat_view_signals[LAST_SIGNAL] = { 0 };

static void
chat_view_dispose(GObject *object)
{
    ChatView *view;
    ChatViewPrivate *priv;

    view = CHAT_VIEW(object);
    priv = CHAT_VIEW_GET_PRIVATE(view);

93
94
    QObject::disconnect(priv->new_interaction_connection);
    QObject::disconnect(priv->update_interaction_connection);
95
    QObject::disconnect(priv->interaction_removed);
96
    QObject::disconnect(priv->update_add_to_conversations);
aviau's avatar
aviau committed
97
98
99
100

    /* Destroying the box will also destroy its children, and we wouldn't
     * want that. So we remove the webkit_chat_container from the box. */
    if (priv->webkit_chat_container) {
101
102
103
        /* disconnect for webkit signals */
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_ready);
        priv->webkit_ready = 0;
104
105
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_send_text);
        priv->webkit_send_text = 0;
106
107
        g_signal_handler_disconnect(priv->webkit_chat_container, priv->webkit_drag_drop);
        priv->webkit_drag_drop = 0;
108

aviau's avatar
aviau committed
109
110
111
112
113
114
        gtk_container_remove(
            GTK_CONTAINER(priv->box_webkit_chat_container),
            GTK_WIDGET(priv->webkit_chat_container)
        );
        priv->webkit_chat_container = nullptr;
    }
115
116
117
118

    G_OBJECT_CLASS(chat_view_parent_class)->dispose(object);
}

119
120
121
122
123
124
static void
hide_chat_view(G_GNUC_UNUSED GtkWidget *widget, ChatView *self)
{
    g_signal_emit(G_OBJECT(self), chat_view_signals[HIDE_VIEW_CLICKED], 0);
}

125
126
127
128
129
130
131
132
133
134
135
136
static void
display_links_toggled(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    if (priv->webkit_chat_container) {
        webkit_chat_container_set_display_links(
            WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
            g_settings_get_boolean(priv->settings, "enable-display-links")
        );
    }
}

137
138
139
140
static void
placecall_clicked(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
141
    if (!priv->conversation_) return;
142
    (*priv->accountInfo_)->conversationModel->placeCall(priv->conversation_->uid);
143
144
}

145
146
147
148
149
150
151
152
static void
place_audio_call_clicked(ChatView *self)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);

    if (!priv->conversation_)
        return;

153
    (*priv->accountInfo_)->conversationModel->placeAudioOnlyCall(priv->conversation_->uid);
154
155
}

156
static void
157
button_add_to_conversations_clicked(ChatView *self)
158
159
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
160
    if (!priv->conversation_) return;
161
    (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
162
163
}

164
static gchar*
165
file_to_manipulate(GtkWindow* top_window, bool send)
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
{
    GtkWidget* dialog;
    GtkFileChooserAction action = send? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SAVE;
    gint res;
    gchar* filename = nullptr;

    dialog = gtk_file_chooser_dialog_new(send? _("Send File") : _("Save File"),
                                         top_window,
                                         action,
                                         _("_Cancel"),
                                         GTK_RESPONSE_CANCEL,
                                         send? _("_Open"): _("_Save"),
                                         GTK_RESPONSE_ACCEPT,
                                         nullptr);

    res = gtk_dialog_run (GTK_DIALOG(dialog));

    if (res == GTK_RESPONSE_ACCEPT) {
        auto chooser = GTK_FILE_CHOOSER(dialog);
        filename = gtk_file_chooser_get_filename(chooser);
    }
    gtk_widget_destroy (dialog);

    return filename;
}

Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
192
193
static void update_chatview_frame(ChatView *self);

194
static void
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
195
webkit_chat_container_script_dialog(GtkWidget* webview, gchar *interaction, ChatView* self)
196
{
197
198
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
    auto order = std::string(interaction);
199
    if (!priv->conversation_) return;
200
    if (order == "ACCEPT") {
201
        (*priv->accountInfo_)->conversationModel->makePermanent(priv->conversation_->uid);
202
    } else if (order == "REFUSE") {
203
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid);
204
    } else if (order == "BLOCK") {
205
        (*priv->accountInfo_)->conversationModel->removeConversation(priv->conversation_->uid, true);
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
206
207
208
209
210
    } else if (order == "UNBLOCK") {
        try {
            auto contactUri = priv->conversation_->participants.front();
            auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
            (*priv->accountInfo_)->contactModel->addContact(contact);
211
        } catch (std::out_of_range&) {
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
212
213
214
215
216
217
218
219
            g_debug("webkit_chat_container_script_dialog: oor while retrieving invalid contact info. Chatview bug ?");
        }
    } else if (order.find("PLACE_CALL") == 0) {
        placecall_clicked(self);
    } else if (order.find("PLACE_AUDIO_CALL") == 0) {
        place_audio_call_clicked(self);
    } else if (order.find("CLOSE_CHATVIEW") == 0) {
        hide_chat_view(webview, self);
220
221
222
    } else if (order.find("SEND:") == 0) {
        // Get text body
        auto toSend = order.substr(std::string("SEND:").size());
223
        (*priv->accountInfo_)->conversationModel->sendMessage(priv->conversation_->uid, toSend);
224
    } else if (order.find("SEND_FILE") == 0) {
225
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
226
227
228
229
            if (auto filename = file_to_manipulate(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(self))), true))
                model->sendFile(priv->conversation_->uid, filename, g_path_get_basename(filename));
        }
    } else if (order.find("ACCEPT_FILE:") == 0) {
230
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
231
232
            try {
                auto interactionId = std::stoull(order.substr(std::string("ACCEPT_FILE:").size()));
233
234

                lrc::api::datatransfer::Info info = {};
235
                (*priv->accountInfo_)->conversationModel->getTransferInfo(interactionId, info);
236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
                // get prefered directory destination.
                auto* download_directory_variant = g_settings_get_value(priv->settings, "download-folder");
                char* download_directory_value;
                g_variant_get(download_directory_variant, "&s", &download_directory_value);
                std::string default_download_dir = g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD);
                auto current_value = std::string(download_directory_value);
                if (current_value.empty()) {
                    g_settings_set_value(priv->settings, "download-folder", g_variant_new("s", default_download_dir.c_str()));
                }
                // get full path
                std::string filename = current_value.empty()? default_download_dir.c_str() : download_directory_value;
                if (!filename.empty() && filename.back() != '/') filename += "/";
                filename += info.displayName;

                model->acceptTransfer(priv->conversation_->uid, interactionId, filename);
252
253
254
255
256
            } catch (...) {
                // ignore
            }
        }
    } else if (order.find("REFUSE_FILE:") == 0) {
257
        if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
            try {
                auto interactionId = std::stoull(order.substr(std::string("REFUSE_FILE:").size()));
                model->cancelTransfer(priv->conversation_->uid, interactionId);
            } catch (...) {
                // ignore
            }
        }
    } else if (order.find("OPEN_FILE:") == 0) {
        // Get text body
        auto filename {"file://" + order.substr(std::string("OPEN_FILE:").size())};
        filename.erase(std::find_if(filename.rbegin(), filename.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), filename.end());
        GError* error = nullptr;
        if (!gtk_show_uri(nullptr, filename.c_str(), GDK_CURRENT_TIME, &error)) {
            g_debug("Could not open file: %s", error->message);
            g_error_free(error);
        }
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
275
276
    } else if (order.find("ADD_TO_CONVERSATIONS") == 0) {
        button_add_to_conversations_clicked(self);
277
278
279
280
281
282
283
284
    } else if (order.find("DELETE_INTERACTION:") == 0) {
        try {
            auto interactionId = std::stoull(order.substr(std::string("DELETE_INTERACTION:").size()));
            if (!priv->conversation_) return;
            (*priv->accountInfo_)->conversationModel->clearInteractionFromConversation(priv->conversation_->uid, interactionId);
        } catch (...) {
            g_warning("delete interaction failed: can't find %s", order.substr(std::string("DELETE_INTERACTION:").size()).c_str());
        }
285
286
287
288
289
290
291
292
    } else if (order.find("RETRY_INTERACTION:") == 0) {
        try {
            auto interactionId = std::stoull(order.substr(std::string("RETRY_INTERACTION:").size()));
            if (!priv->conversation_) return;
            (*priv->accountInfo_)->conversationModel->retryInteraction(priv->conversation_->uid, interactionId);
        } catch (...) {
            g_warning("delete interaction failed: can't find %s", order.substr(std::string("RETRY_INTERACTION:").size()).c_str());
        }
293
294
295
    }
}

296
297
298
299
300
301
static void
chat_view_init(ChatView *view)
{
    gtk_widget_init_template(GTK_WIDGET(view));

    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(view);
302
    priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
303
304
305
306
307
308
309
310
311
312
}

static void
chat_view_class_init(ChatViewClass *klass)
{
    G_OBJECT_CLASS(klass)->dispose = chat_view_dispose;

    gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS (klass),
                                                "/cx/ring/RingGnome/chatview.ui");

aviau's avatar
aviau committed
313
    gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), ChatView, box_webkit_chat_container);
314
315

    chat_view_signals[NEW_MESSAGES_DISPLAYED] = g_signal_new (
316
        "new-interactions-displayed",
317
318
319
320
321
322
323
        G_TYPE_FROM_CLASS(klass),
        (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
        0,
        nullptr,
        nullptr,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE, 0);
324
325
326
327
328
329
330
331
332
333

    chat_view_signals[HIDE_VIEW_CLICKED] = g_signal_new (
        "hide-view-clicked",
        G_TYPE_FROM_CLASS(klass),
        (GSignalFlags) (G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
        0,
        nullptr,
        nullptr,
        g_cclosure_marshal_VOID__VOID,
        G_TYPE_NONE, 0);
334
335
}

aviau's avatar
aviau committed
336
static void
337
print_interaction_to_buffer(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
aviau's avatar
aviau committed
338
339
340
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

341
    if (!priv->conversation_) return;
342
    if (interaction.status == lrc::api::interaction::Status::UNREAD)
343
        (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, interactionId);
344
345

    webkit_chat_container_print_new_interaction(
aviau's avatar
aviau committed
346
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
347
        *(*priv->accountInfo_)->conversationModel,
348
349
        interactionId,
        interaction
aviau's avatar
aviau committed
350
    );
351
352
}

353
static void
354
update_interaction(ChatView* self, uint64_t interactionId, const lrc::api::interaction::Info& interaction)
355
356
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
357
358
    webkit_chat_container_update_interaction(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
359
        *(*priv->accountInfo_)->conversationModel,
360
361
        interactionId,
        interaction
362
    );
363
}
364

365
366
367
368
369
370
371
372
373
374
static void
remove_interaction(ChatView* self, uint64_t interactionId)
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    webkit_chat_container_remove_interaction(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
        interactionId
    );
}

375
376
377
378
379
static void
load_participants_images(ChatView *self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
380

381
    // Contact
382
    if (!priv->conversation_) return;
383
    auto contactUri = priv->conversation_->participants.front();
384
    try{
385
        auto& contact = (*priv->accountInfo_)->contactModel->getContact(contactUri);
386
387
388
        if (!contact.profileInfo.avatar.empty()) {
            webkit_chat_container_set_sender_image(
                WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
389
                (*priv->accountInfo_)->contactModel->getContactProfileId(contactUri),
390
391
392
393
394
                contact.profileInfo.avatar
                );
        }
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
395
396
    }

397
    // For this account
398
    if (!(*priv->accountInfo_)->profileInfo.avatar.empty()) {
399
400
        webkit_chat_container_set_sender_image(
            WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
401
402
            (*priv->accountInfo_)->contactModel->getContactProfileId((*priv->accountInfo_)->profileInfo.uri),
            (*priv->accountInfo_)->profileInfo.avatar
403
        );
404
405
406
    }
}

407
static void
408
print_text_recording(ChatView *self)
409
410
411
412
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

413
    // Read interactions
414
    if (!priv->conversation_) return;
415
416
    for (const auto& it: priv->conversation_->interactions) {
        if (it.second.status == lrc::api::interaction::Status::UNREAD)
417
            (*priv->accountInfo_)->conversationModel->setInteractionRead(priv->conversation_->uid, it.first);
418
419
    }

420
421
    webkit_chat_container_print_history(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
422
        *(*priv->accountInfo_)->conversationModel,
423
424
        priv->conversation_->interactions
    );
425

426
    QObject::disconnect(priv->new_interaction_connection);
427
428
}

aviau's avatar
aviau committed
429
430
431
432
433
434
435
436
437
438
439
440
static void
webkit_chat_container_ready(ChatView* self)
{
    /* The webkit chat container has loaded the javascript libraries, we can
     * now use it. */

    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

    webkit_chat_container_clear(
        WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)
    );

441
    display_links_toggled(self);
442
443
444
445
    print_text_recording(self);
    load_participants_images(self);

    priv->new_interaction_connection = QObject::connect(
446
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::newInteraction,
447
    [self, priv](const std::string& uid, uint64_t interactionId, lrc::api::interaction::Info interaction) {
448
        if (!priv->conversation_) return;
449
        if (uid == priv->conversation_->uid) {
450
451
452
            print_interaction_to_buffer(self, interactionId, interaction);
        }
    });
453

454
    priv->update_interaction_connection = QObject::connect(
455
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionStatusUpdated,
456
    [self, priv](const std::string& uid, uint64_t msgId, lrc::api::interaction::Info msg) {
457
        if (!priv->conversation_) return;
458
        if (uid == priv->conversation_->uid) {
459
460
461
462
            update_interaction(self, msgId, msg);
        }
    });

463
464
465
466
467
468
469
470
471
    priv->interaction_removed = QObject::connect(
    &*(*priv->accountInfo_)->conversationModel, &lrc::api::ConversationModel::interactionRemoved,
    [self, priv](const std::string& convUid, uint64_t interactionId) {
        if (!priv->conversation_) return;
        if (convUid == priv->conversation_->uid) {
            remove_interaction(self, interactionId);
        }
    });

472
    if (!priv->conversation_) return;
473
    auto contactUri = priv->conversation_->participants.front();
474
    try {
475
        auto contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
476
477
478
479
480
481
482
483
484
485
486
487
        auto bestName = contactInfo.profileInfo.alias;
        if (bestName.empty())
            bestName = contactInfo.registeredName;
        if (bestName.empty())
            bestName = contactInfo.profileInfo.uri;
        bestName.erase(std::remove(bestName.begin(), bestName.end(), '\r'), bestName.end());
        webkit_chat_disable_send_interaction(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                             (contactInfo.profileInfo.type == lrc::api::profile::Type::SIP)
                                             && priv->conversation_->callId.empty());
    } catch (const std::out_of_range&) {
        // ContactModel::getContact() exception
    }
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519

    update_chatview_frame(self);
}

static void
update_chatview_frame(ChatView* self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    if (!priv->conversation_) return;

    auto contactUri = priv->conversation_->participants.front();

    lrc::api::contact::Info contactInfo;
    try {
        contactInfo = (*priv->accountInfo_)->contactModel->getContact(contactUri);
    } catch (const std::out_of_range&) {
        g_debug("update_chatview_frame: failed to retrieve contactInfo");
        return;
    }

    // get alias and bestName
    auto alias = contactInfo.profileInfo.alias;
    auto bestName = contactInfo.registeredName;
    if (bestName.empty())
        bestName = contactInfo.profileInfo.uri;
    bestName.erase(std::remove(bestName.begin(), bestName.end(), '\r'), bestName.end());
    alias.erase(std::remove(alias.begin(), alias.end(), '\r'), alias.end());

    // get temporary status
    bool temp = contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY || contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING;

520
521
522
    webkit_chat_update_chatview_frame(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                     (*priv->accountInfo_)->enabled,
                                     contactInfo.isBanned, temp, alias.c_str(), bestName.c_str());
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
523
524
525
526

    webkit_chat_container_set_invitation(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container),
                                             (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING),
                                             bestName);
aviau's avatar
aviau committed
527
528
}

529
530
531
532
533
534
535
536
static void
on_webkit_drag_drop(GtkWidget*, gchar* data, ChatView* self)
{
    g_return_if_fail(IS_CHAT_VIEW(self));
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    if (!priv->conversation_) return;
    if (!data) return;

537
538
539
540
541
542
543
    GError *error = nullptr;
    std::string data_str = g_filename_from_uri(data, nullptr, &error);
    if (error) {
        g_warning("Unable to exec g_filename_from_uri on %s", data);
        g_error_free(error);
        return;
    }
544
545
546
    // Only take files
    if (data_str.find("\r\n") == std::string::npos) return;
    const auto LEN_END = std::string("\r\n").length();
547
548
549
550
551
552
    if (data_str.length() > LEN_END) {
        // remove \r\n from the string
        data_str = data_str.substr(0, data_str.length() - LEN_END);
    } else {
        // Nothing valid to drop, abort.
        return;
553
554
555
556
557
558
559
    }

    if (auto model = (*priv->accountInfo_)->conversationModel.get()) {
        model->sendFile(priv->conversation_->uid, data_str, g_path_get_basename(data_str.c_str()));
    }
}

aviau's avatar
aviau committed
560
561
562
563
564
565
566
567
static void
build_chat_view(ChatView* self)
{
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);

    gtk_container_add(GTK_CONTAINER(priv->box_webkit_chat_container), priv->webkit_chat_container);
    gtk_widget_show(priv->webkit_chat_container);

Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
568
    update_chatview_frame(self);
aviau's avatar
aviau committed
569

570
    priv->webkit_ready = g_signal_connect_swapped(
aviau's avatar
aviau committed
571
572
573
574
575
576
        priv->webkit_chat_container,
        "ready",
        G_CALLBACK(webkit_chat_container_ready),
        self
    );

577
    priv->webkit_send_text = g_signal_connect(priv->webkit_chat_container,
578
579
        "script-dialog",
        G_CALLBACK(webkit_chat_container_script_dialog),
580
581
        self);

582
583
584
585
586
587
588
    priv->webkit_drag_drop = g_signal_connect(
        priv->webkit_chat_container,
        "data-dropped",
        G_CALLBACK(on_webkit_drag_drop),
        self
    );

aviau's avatar
aviau committed
589
590
    if (webkit_chat_container_is_ready(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container)))
        webkit_chat_container_ready(self);
591
}
Stepan Salenikovich's avatar
Stepan Salenikovich committed
592
593

GtkWidget *
594
chat_view_new (WebKitChatContainer* webkit_chat_container,
595
               AccountInfoPointer const & accountInfo,
596
               lrc::api::conversation::Info* conversation)
Stepan Salenikovich's avatar
Stepan Salenikovich committed
597
598
599
{
    ChatView *self = CHAT_VIEW(g_object_new(CHAT_VIEW_TYPE, NULL));

aviau's avatar
aviau committed
600
601
    ChatViewPrivate *priv = CHAT_VIEW_GET_PRIVATE(self);
    priv->webkit_chat_container = GTK_WIDGET(webkit_chat_container);
602
    priv->conversation_ = conversation;
603
    priv->accountInfo_ = &accountInfo;
aviau's avatar
aviau committed
604
605

    build_chat_view(self);
Stepan Salenikovich's avatar
Stepan Salenikovich committed
606
607
608
    return (GtkWidget *)self;
}

609
void
610
chat_view_update_temporary(ChatView* self, bool showAddButton, bool showInvitation)
611
{
612
    g_return_if_fail(IS_CHAT_VIEW(self));
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
613
    update_chatview_frame(self);
614
615
}

616
617
lrc::api::conversation::Info
chat_view_get_conversation(ChatView *self)
618
{
619
    g_return_val_if_fail(IS_CHAT_VIEW(self), lrc::api::conversation::Info());
620
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
621
    return *priv->conversation_;
622
}
623
624
625
626
627

void
chat_view_set_header_visible(ChatView *self, gboolean visible)
{
    auto priv = CHAT_VIEW_GET_PRIVATE(self);
Hugo Lefeuvre's avatar
Hugo Lefeuvre committed
628
    webkit_chat_set_header_visible(WEBKIT_CHAT_CONTAINER(priv->webkit_chat_container), visible);
629
}