1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Add file sending (silently, send only)

This commit is contained in:
Tux3 / Mlkj / !Lev.uXFMLA 2014-06-25 22:43:28 +02:00
parent 342ba443cb
commit 8aacd21405
6 changed files with 202 additions and 10 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pro.user*

View File

@ -5,6 +5,7 @@
#include <QFont> #include <QFont>
#include <QTime> #include <QTime>
#include <QScrollBar> #include <QScrollBar>
#include <QFileDialog>
ChatForm::ChatForm(Friend* chatFriend) ChatForm::ChatForm(Friend* chatFriend)
: f(chatFriend), curRow{0}, lockSliderToBottom{true} : f(chatFriend), curRow{0}, lockSliderToBottom{true}
@ -15,7 +16,7 @@ ChatForm::ChatForm(Friend* chatFriend)
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout(); headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout();
mainChatLayout = new QGridLayout(); mainChatLayout = new QGridLayout();
msgEdit = new ChatTextEdit(); msgEdit = new ChatTextEdit();
sendButton = new QPushButton(); sendButton = new QPushButton(), fileButton = new QPushButton();
chatArea = new QScrollArea(); chatArea = new QScrollArea();
QFont bold; QFont bold;
@ -31,14 +32,19 @@ ChatForm::ChatForm(Friend* chatFriend)
mainChatLayout->setColumnStretch(1,1); mainChatLayout->setColumnStretch(1,1);
mainChatLayout->setSpacing(10); mainChatLayout->setSpacing(10);
msgEdit->setFixedHeight(50);
QPalette toxgreen;
toxgreen.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
sendButton->setIcon(QIcon("img/button icons/sendmessage_2x.png")); sendButton->setIcon(QIcon("img/button icons/sendmessage_2x.png"));
sendButton->setFlat(true); sendButton->setFlat(true);
QPalette pal; sendButton->setPalette(toxgreen);
pal.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
sendButton->setPalette(pal);
sendButton->setAutoFillBackground(true); sendButton->setAutoFillBackground(true);
msgEdit->setFixedHeight(50);
sendButton->setFixedSize(50, 50); sendButton->setFixedSize(50, 50);
fileButton->setIcon(QIcon("img/button icons/attach_2x.png"));
fileButton->setFlat(true);
fileButton->setPalette(toxgreen);
fileButton->setAutoFillBackground(true);
fileButton->setIconSize(QSize(40,40));
main->setLayout(mainLayout); main->setLayout(mainLayout);
mainLayout->addWidget(chatArea); mainLayout->addWidget(chatArea);
@ -52,6 +58,7 @@ ChatForm::ChatForm(Friend* chatFriend)
headLayout->addWidget(avatar); headLayout->addWidget(avatar);
headLayout->addLayout(headTextLayout); headLayout->addLayout(headTextLayout);
headLayout->addStretch(); headLayout->addStretch();
headLayout->addWidget(fileButton);
headTextLayout->addStretch(); headTextLayout->addStretch();
headTextLayout->addWidget(name); headTextLayout->addWidget(name);
@ -61,6 +68,7 @@ ChatForm::ChatForm(Friend* chatFriend)
chatArea->setWidget(chatAreaWidget); chatArea->setWidget(chatAreaWidget);
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered())); connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
connect(fileButton, SIGNAL(clicked()), this, SLOT(onAttachClicked()));
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged())); connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
} }
@ -145,6 +153,24 @@ void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
curRow++; curRow++;
} }
void ChatForm::onAttachClicked()
{
QString path = QFileDialog::getOpenFileName(0,"Send a file");
if (path.isEmpty())
return;
QFile file(path);
if (!file.exists() || !file.open(QIODevice::ReadOnly))
return;
QByteArray fileData = file.readAll();
file.close();
QFileInfo fi(path);
// TODO: Show file send widget
emit sendFile(f->friendId, fi.fileName(), fileData);
}
void ChatForm::onSliderRangeChanged() void ChatForm::onSliderRangeChanged()
{ {
QScrollBar* scroll = chatArea->verticalScrollBar(); QScrollBar* scroll = chatArea->verticalScrollBar();

View File

@ -31,12 +31,13 @@ public:
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm")); void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
void addMessage(QLabel* author, QLabel* message, QLabel* date); void addMessage(QLabel* author, QLabel* message, QLabel* date);
signals: signals:
void sendMessage(int, QString); void sendMessage(int, QString);
void sendFile(int32_t, QString, QByteArray);
private slots: private slots:
void onSendTriggered(); void onSendTriggered();
void onAttachClicked();
void onSliderRangeChanged(); void onSliderRangeChanged();
private: private:
@ -46,7 +47,7 @@ private:
QGridLayout *mainChatLayout; QGridLayout *mainChatLayout;
QLabel *avatar, *name, *statusMessage; QLabel *avatar, *name, *statusMessage;
ChatTextEdit *msgEdit; ChatTextEdit *msgEdit;
QPushButton *sendButton; QPushButton *sendButton, *fileButton;
QScrollArea *chatArea; QScrollArea *chatArea;
QWidget *main, *head, *chatAreaWidget; QWidget *main, *head, *chatAreaWidget;
QString previousName; QString previousName;

127
core.cpp
View File

@ -29,6 +29,7 @@
#include "settings.h" #include "settings.h"
const QString Core::CONFIG_FILE_NAME = "tox_save"; const QString Core::CONFIG_FILE_NAME = "tox_save";
QList<ToxFile> Core::fileSendQueue;
Core::Core() : Core::Core() :
tox(nullptr) tox(nullptr)
@ -37,8 +38,11 @@ Core::Core() :
toxTimer->setSingleShot(true); toxTimer->setSingleShot(true);
saveTimer = new QTimer(this); saveTimer = new QTimer(this);
saveTimer->start(TOX_SAVE_INTERVAL); saveTimer->start(TOX_SAVE_INTERVAL);
fileTimer = new QTimer(this);
fileTimer->start(TOX_FILE_INTERVAL);
connect(toxTimer, &QTimer::timeout, this, &Core::process); connect(toxTimer, &QTimer::timeout, this, &Core::process);
connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration); connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration);
connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht); connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht);
} }
@ -126,6 +130,74 @@ void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t
emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change); emit static_cast<Core*>(core)->groupNamelistChanged(groupnumber, peernumber, change);
} }
void Core::onFileSendRequestCallback(Tox* tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
uint8_t *filename, uint16_t filename_length, void *userdata)
{
qDebug() << "Core: File send request callback";
}
void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
{
if (control_type == TOX_FILECONTROL_ACCEPT && receive_send == 1)
{
qDebug() << "Core: File control callback, file accepted";
int chunkSize = tox_file_data_size(tox, friendnumber);
if (chunkSize == -1)
{
qWarning("Core::onFileControlCallback: Error getting preffered chunk size, aborting file send");
// TODO: Warn the Friend* that we're aborting (emit)
return;
}
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == filenumber)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::onFileControlCallback: No such file in queue");
// TODO: Warn the Friend* that we're aborting (emit)
return;
}
chunkSize = std::min(chunkSize, file->fileData.size());
QByteArray toSend = file->fileData.mid(file->bytesSent, chunkSize);
if (tox_file_send_data(tox, friendnumber, filenumber, (uint8_t*)toSend.data(), toSend.size()) == -1)
{
qWarning("Core::onFileControlCallback: Error sending first data chunk, aborting");
// TODO: Warn the Friend* that we're aborting (emit)
return;
}
else
{
file->bytesSent += chunkSize;
if (file->bytesSent >= file->fileData.size())
{
qWarning("Core::onFileControlCallback: Transfer finished");
tox_file_send_control(tox, friendnumber, 0, filenumber, TOX_FILECONTROL_FINISHED, nullptr, 0);
// TODO: Notify the Friend* that we're done sending (emit)
}
else
{
file->status = ToxFile::TRANSMITTING;
}
}
}
else
{
qDebug() << QString("Core: File control callback, receive_send=%1, control_type=%2")
.arg(receive_send).arg(control_type);
}
}
void Core::onFileDataCallback(Tox* tox, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata)
{
qDebug() << "Core: File data callback";
}
void Core::acceptFriendRequest(const QString& userId) void Core::acceptFriendRequest(const QString& userId)
{ {
int friendId = tox_add_friend_norequest(tox, CUserId(userId).data()); int friendId = tox_add_friend_norequest(tox, CUserId(userId).data());
@ -179,6 +251,20 @@ void Core::sendGroupMessage(int groupId, const QString& message)
tox_group_message_send(tox, groupId, cMessage.data(), cMessage.size()); tox_group_message_send(tox, groupId, cMessage.data(), cMessage.size());
} }
void Core::sendFile(int32_t friendId, QString Filename, QByteArray data)
{
QByteArray fileName = Filename.toUtf8();
int fileNum = tox_new_file_sender(tox, friendId, data.size(), (uint8_t*)fileName.data(), fileName.size());
if (fileNum == -1)
{
qWarning() << "Core::sendFile: Can't create the Tox file sender";
// TODO: Notify Widget (with the friendId), Widget will notify the chatForm
return;
}
fileSendQueue.append(ToxFile(fileNum, friendId, data, fileName));
}
void Core::removeFriend(int friendId) void Core::removeFriend(int friendId)
{ {
if (tox_del_friend(tox, friendId) == -1) { if (tox_del_friend(tox, friendId) == -1) {
@ -263,6 +349,44 @@ void Core::process()
toxTimer->start(tox_do_interval(tox)); toxTimer->start(tox_do_interval(tox));
} }
void Core::fileHeartbeat()
{
for (ToxFile& file : fileSendQueue)
{
if (file.status == ToxFile::TRANSMITTING)
{
int chunkSize = tox_file_data_size(tox, file.friendId);
if (chunkSize == -1)
{
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
file.status = ToxFile::STOPPED;
// TODO: Warn the Friend* and the peer that we're aborting (emit and tox_control_...)
return;
}
chunkSize = std::min(chunkSize, file.fileData.size());
QByteArray toSend = file.fileData.mid(file.bytesSent, chunkSize);
if (tox_file_send_data(tox, file.friendId, file.fileNum, (uint8_t*)toSend.data(), toSend.size()) == -1)
{
qWarning("Core::fileHeartbeat: Error sending data chunk");
continue;
}
else
{
file.bytesSent += chunkSize;
if (file.bytesSent >= file.fileData.size())
{
qDebug("Core::fileHeartbeat: Transfer finished");
tox_file_send_control(tox, file.friendId, 0, file.fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
file.status = ToxFile::STOPPED; // TODO: Remove it from the list and return;
// TODO: Notify the Friend* that we're done sending (emit)
}
else
qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file.bytesSent).arg(file.fileData.size());
}
}
}
}
void Core::checkConnection() void Core::checkConnection()
{ {
static bool isConnected = false; static bool isConnected = false;
@ -400,6 +524,9 @@ void Core::start()
tox_callback_group_invite(tox, onGroupInvite, this); tox_callback_group_invite(tox, onGroupInvite, this);
tox_callback_group_message(tox, onGroupMessage, this); tox_callback_group_message(tox, onGroupMessage, this);
tox_callback_group_namelist_change(tox, onGroupNamelistChange, this); tox_callback_group_namelist_change(tox, onGroupNamelistChange, this);
tox_callback_file_send_request(tox, onFileSendRequestCallback, this);
tox_callback_file_control(tox, onFileControlCallback, this);
tox_callback_file_data(tox, onFileDataCallback, this);
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE]; uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, friendAddress); tox_get_address(tox, friendAddress);

39
core.h
View File

@ -21,14 +21,17 @@
#include <tox/tox.h> #include <tox/tox.h>
#include <cstdint>
#include <QDateTime> #include <QDateTime>
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include <QString> #include <QString>
#include <QList> #include <QList>
#include <QByteArray>
#define GROUPCHAT_MAX_SIZE 32 #define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 10*1000 #define TOX_SAVE_INTERVAL 30*1000
#define TOX_FILE_INTERVAL 50
struct DhtServer struct DhtServer
{ {
@ -38,6 +41,27 @@ struct DhtServer
int port; int port;
}; };
struct ToxFile
{
enum FileStatus
{
STOPPED,
PAUSED,
TRANSMITTING
};
ToxFile(int FileNum, int FriendId, QByteArray FileData, QByteArray FileName)
: fileNum(FileNum), friendId(FriendId), fileData{FileData},
fileName{FileName}, bytesSent{0}, status{STOPPED} {}
int fileNum;
int friendId;
QByteArray fileData;
QByteArray fileName;
long long bytesSent;
FileStatus status;
};
class Core : public QObject class Core : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -57,6 +81,11 @@ private:
static void onGroupInvite(Tox *tox, int friendnumber, uint8_t *group_public_key, void *userdata); static void onGroupInvite(Tox *tox, int friendnumber, uint8_t *group_public_key, void *userdata);
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata); static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, uint8_t * message, uint16_t length, void *userdata);
static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata); static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata);
static void onFileSendRequestCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
uint8_t *filename, uint16_t filename_length, void *userdata);
static void onFileControlCallback(Tox *tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t *data, uint16_t length, void *userdata);
static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
void checkConnection(); void checkConnection();
@ -67,9 +96,10 @@ private:
void checkLastOnline(int friendId); void checkLastOnline(int friendId);
Tox* tox; Tox* tox;
QTimer *toxTimer, *saveTimer; QTimer *toxTimer, *saveTimer, *fileTimer;
QList<DhtServer> dhtServerList; QList<DhtServer> dhtServerList;
int dhtServerId; int dhtServerId;
static QList<ToxFile> fileSendQueue;
static const QString CONFIG_FILE_NAME; static const QString CONFIG_FILE_NAME;
@ -153,15 +183,18 @@ public slots:
void removeGroup(int groupId); void removeGroup(int groupId);
void sendMessage(int friendId, const QString& message); void sendMessage(int friendId, const QString& message);
void sendGroupMessage(int groupId, const QString& message);
void sendAction(int friendId, const QString& action); void sendAction(int friendId, const QString& action);
void sendTyping(int friendId, bool typing); void sendTyping(int friendId, bool typing);
void sendGroupMessage(int groupId, const QString& message);
void sendFile(int32_t friendId, QString Filename, QByteArray data);
void setUsername(const QString& username); void setUsername(const QString& username);
void setStatusMessage(const QString& message); void setStatusMessage(const QString& message);
void setStatus(Status status); void setStatus(Status status);
void process(); void process();
void fileHeartbeat();
void bootstrapDht(); void bootstrapDht();

View File

@ -21,6 +21,8 @@ Widget::Widget(QWidget *parent) :
ui->setupUi(this); ui->setupUi(this);
ui->mainContent->setLayout(new QVBoxLayout()); ui->mainContent->setLayout(new QVBoxLayout());
ui->mainHead->setLayout(new QVBoxLayout()); ui->mainHead->setLayout(new QVBoxLayout());
ui->mainHead->layout()->setMargin(0);
ui->mainHead->layout()->setSpacing(0);
QWidget* friendListWidget = new QWidget(); QWidget* friendListWidget = new QWidget();
friendListWidget->setLayout(new QVBoxLayout()); friendListWidget->setLayout(new QVBoxLayout());
friendListWidget->layout()->setSpacing(0); friendListWidget->layout()->setSpacing(0);
@ -34,6 +36,7 @@ Widget::Widget(QWidget *parent) :
qRegisterMetaType<Status>("Status"); qRegisterMetaType<Status>("Status");
qRegisterMetaType<uint8_t>("uint8_t"); qRegisterMetaType<uint8_t>("uint8_t");
qRegisterMetaType<int32_t>("int32_t");
core = new Core(); core = new Core();
coreThread = new QThread(this); coreThread = new QThread(this);
@ -223,6 +226,7 @@ void Widget::addFriend(int friendId, const QString &userId)
connect(newfriend->widget, SIGNAL(friendWidgetClicked(FriendWidget*)), this, SLOT(onFriendWidgetClicked(FriendWidget*))); connect(newfriend->widget, SIGNAL(friendWidgetClicked(FriendWidget*)), this, SLOT(onFriendWidgetClicked(FriendWidget*)));
connect(newfriend->widget, SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int))); connect(newfriend->widget, SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
connect(newfriend->chatForm, SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString))); connect(newfriend->chatForm, SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString)));
connect(newfriend->chatForm, SIGNAL(sendFile(int32_t,QString,QByteArray)), core, SLOT(sendFile(int32_t,QString,QByteArray)));
} }
void Widget::onFriendStatusChanged(int friendId, Status status) void Widget::onFriendStatusChanged(int friendId, Status status)