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:
parent
342ba443cb
commit
8aacd21405
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.pro.user*
|
36
chatform.cpp
36
chatform.cpp
|
@ -5,6 +5,7 @@
|
|||
#include <QFont>
|
||||
#include <QTime>
|
||||
#include <QScrollBar>
|
||||
#include <QFileDialog>
|
||||
|
||||
ChatForm::ChatForm(Friend* chatFriend)
|
||||
: f(chatFriend), curRow{0}, lockSliderToBottom{true}
|
||||
|
@ -15,7 +16,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
headTextLayout = new QVBoxLayout(), mainLayout = new QVBoxLayout();
|
||||
mainChatLayout = new QGridLayout();
|
||||
msgEdit = new ChatTextEdit();
|
||||
sendButton = new QPushButton();
|
||||
sendButton = new QPushButton(), fileButton = new QPushButton();
|
||||
chatArea = new QScrollArea();
|
||||
|
||||
QFont bold;
|
||||
|
@ -31,14 +32,19 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
mainChatLayout->setColumnStretch(1,1);
|
||||
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->setFlat(true);
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
|
||||
sendButton->setPalette(pal);
|
||||
sendButton->setPalette(toxgreen);
|
||||
sendButton->setAutoFillBackground(true);
|
||||
msgEdit->setFixedHeight(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);
|
||||
mainLayout->addWidget(chatArea);
|
||||
|
@ -52,6 +58,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
headLayout->addWidget(avatar);
|
||||
headLayout->addLayout(headTextLayout);
|
||||
headLayout->addStretch();
|
||||
headLayout->addWidget(fileButton);
|
||||
|
||||
headTextLayout->addStretch();
|
||||
headTextLayout->addWidget(name);
|
||||
|
@ -61,6 +68,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
chatArea->setWidget(chatAreaWidget);
|
||||
|
||||
connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered()));
|
||||
connect(fileButton, SIGNAL(clicked()), this, SLOT(onAttachClicked()));
|
||||
connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered()));
|
||||
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
|
||||
}
|
||||
|
@ -145,6 +153,24 @@ void ChatForm::addMessage(QLabel* author, QLabel* message, QLabel* date)
|
|||
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()
|
||||
{
|
||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
||||
|
|
|
@ -31,12 +31,13 @@ public:
|
|||
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
|
||||
void addMessage(QLabel* author, QLabel* message, QLabel* date);
|
||||
|
||||
|
||||
signals:
|
||||
void sendMessage(int, QString);
|
||||
void sendFile(int32_t, QString, QByteArray);
|
||||
|
||||
private slots:
|
||||
void onSendTriggered();
|
||||
void onAttachClicked();
|
||||
void onSliderRangeChanged();
|
||||
|
||||
private:
|
||||
|
@ -46,7 +47,7 @@ private:
|
|||
QGridLayout *mainChatLayout;
|
||||
QLabel *avatar, *name, *statusMessage;
|
||||
ChatTextEdit *msgEdit;
|
||||
QPushButton *sendButton;
|
||||
QPushButton *sendButton, *fileButton;
|
||||
QScrollArea *chatArea;
|
||||
QWidget *main, *head, *chatAreaWidget;
|
||||
QString previousName;
|
||||
|
|
127
core.cpp
127
core.cpp
|
@ -29,6 +29,7 @@
|
|||
#include "settings.h"
|
||||
|
||||
const QString Core::CONFIG_FILE_NAME = "tox_save";
|
||||
QList<ToxFile> Core::fileSendQueue;
|
||||
|
||||
Core::Core() :
|
||||
tox(nullptr)
|
||||
|
@ -37,8 +38,11 @@ Core::Core() :
|
|||
toxTimer->setSingleShot(true);
|
||||
saveTimer = new QTimer(this);
|
||||
saveTimer->start(TOX_SAVE_INTERVAL);
|
||||
fileTimer = new QTimer(this);
|
||||
fileTimer->start(TOX_FILE_INTERVAL);
|
||||
connect(toxTimer, &QTimer::timeout, this, &Core::process);
|
||||
connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration);
|
||||
connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (tox_del_friend(tox, friendId) == -1) {
|
||||
|
@ -263,6 +349,44 @@ void Core::process()
|
|||
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()
|
||||
{
|
||||
static bool isConnected = false;
|
||||
|
@ -400,6 +524,9 @@ void Core::start()
|
|||
tox_callback_group_invite(tox, onGroupInvite, this);
|
||||
tox_callback_group_message(tox, onGroupMessage, 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];
|
||||
tox_get_address(tox, friendAddress);
|
||||
|
|
39
core.h
39
core.h
|
@ -21,14 +21,17 @@
|
|||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <QByteArray>
|
||||
|
||||
#define GROUPCHAT_MAX_SIZE 32
|
||||
#define TOX_SAVE_INTERVAL 10*1000
|
||||
#define TOX_SAVE_INTERVAL 30*1000
|
||||
#define TOX_FILE_INTERVAL 50
|
||||
|
||||
struct DhtServer
|
||||
{
|
||||
|
@ -38,6 +41,27 @@ struct DhtServer
|
|||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -57,6 +81,11 @@ private:
|
|||
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 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();
|
||||
|
||||
|
@ -67,9 +96,10 @@ private:
|
|||
void checkLastOnline(int friendId);
|
||||
|
||||
Tox* tox;
|
||||
QTimer *toxTimer, *saveTimer;
|
||||
QTimer *toxTimer, *saveTimer, *fileTimer;
|
||||
QList<DhtServer> dhtServerList;
|
||||
int dhtServerId;
|
||||
static QList<ToxFile> fileSendQueue;
|
||||
|
||||
static const QString CONFIG_FILE_NAME;
|
||||
|
||||
|
@ -153,15 +183,18 @@ public slots:
|
|||
void removeGroup(int groupId);
|
||||
|
||||
void sendMessage(int friendId, const QString& message);
|
||||
void sendGroupMessage(int groupId, const QString& message);
|
||||
void sendAction(int friendId, const QString& action);
|
||||
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 setStatusMessage(const QString& message);
|
||||
void setStatus(Status status);
|
||||
|
||||
void process();
|
||||
void fileHeartbeat();
|
||||
|
||||
void bootstrapDht();
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ Widget::Widget(QWidget *parent) :
|
|||
ui->setupUi(this);
|
||||
ui->mainContent->setLayout(new QVBoxLayout());
|
||||
ui->mainHead->setLayout(new QVBoxLayout());
|
||||
ui->mainHead->layout()->setMargin(0);
|
||||
ui->mainHead->layout()->setSpacing(0);
|
||||
QWidget* friendListWidget = new QWidget();
|
||||
friendListWidget->setLayout(new QVBoxLayout());
|
||||
friendListWidget->layout()->setSpacing(0);
|
||||
|
@ -34,6 +36,7 @@ Widget::Widget(QWidget *parent) :
|
|||
|
||||
qRegisterMetaType<Status>("Status");
|
||||
qRegisterMetaType<uint8_t>("uint8_t");
|
||||
qRegisterMetaType<int32_t>("int32_t");
|
||||
|
||||
core = new Core();
|
||||
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(removeFriend(int)), this, SLOT(removeFriend(int)));
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user