Browse Source

Implement file reception, improve sending

pull/2/head
Tux3 / Mlkj / !Lev.uXFMLA 11 years ago
parent
commit
782bce2c05
  1. 41
      chatform.cpp
  2. 3
      chatform.h
  3. 429
      core.cpp
  4. 41
      core.h
  5. 120
      filetransfertwidget.cpp
  6. 17
      filetransfertwidget.h
  7. 7
      main.cpp
  8. 10
      widget.cpp
  9. 1
      widget.h

41
chatform.cpp

@ -41,11 +41,13 @@ ChatForm::ChatForm(Friend* chatFriend) @@ -41,11 +41,13 @@ ChatForm::ChatForm(Friend* chatFriend)
sendButton->setPalette(toxgreen);
sendButton->setAutoFillBackground(true);
sendButton->setFixedSize(50, 50);
sendButton->setIconSize(QSize(25,25));
fileButton->setIcon(QIcon("img/button icons/attach_2x.png"));
fileButton->setFlat(true);
fileButton->setPalette(toxgreen);
fileButton->setAutoFillBackground(true);
fileButton->setIconSize(QSize(40,40));
fileButton->setIconSize(QSize(20,20));
fileButton->setFixedSize(50,40);
main->setLayout(mainLayout);
mainLayout->addWidget(chatArea);
@ -178,9 +180,9 @@ void ChatForm::onSliderRangeChanged() @@ -178,9 +180,9 @@ void ChatForm::onSliderRangeChanged()
scroll->setValue(scroll->maximum());
}
void ChatForm::startFileSend(ToxFile *file)
void ChatForm::startFileSend(ToxFile file)
{
if (file->friendId != f->friendId)
if (file.friendId != f->friendId)
return;
QLabel *author = new QLabel(Widget::getInstance()->getUsername());
QLabel *date = new QLabel(QTime::currentTime().toString("mm:ss"));
@ -213,3 +215,36 @@ void ChatForm::startFileSend(ToxFile *file) @@ -213,3 +215,36 @@ void ChatForm::startFileSend(ToxFile *file)
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
}
void ChatForm::onFileRecvRequest(ToxFile file)
{
if (file.friendId != f->friendId)
return;
QLabel *author = new QLabel(f->getName());
QLabel *date = new QLabel(QTime::currentTime().toString("mm:ss"));
QScrollBar* scroll = chatArea->verticalScrollBar();
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
author->setAlignment(Qt::AlignTop | Qt::AlignRight);
date->setAlignment(Qt::AlignTop);
if (previousName.isEmpty() || previousName != author->text())
{
if (curRow)
{
mainChatLayout->setRowStretch(curRow, 0);
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
curRow++;
}
mainChatLayout->addWidget(author, curRow, 0);
}
FileTransfertWidget* fileTrans = new FileTransfertWidget(file);
previousName = author->text();
mainChatLayout->addWidget(fileTrans, curRow, 1);
mainChatLayout->addWidget(date, curRow, 3);
mainChatLayout->setRowStretch(curRow+1, 1);
mainChatLayout->setRowStretch(curRow, 0);
curRow++;
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransfertWidget::onFileTransferInfo);
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
}

3
chatform.h

@ -37,7 +37,8 @@ signals: @@ -37,7 +37,8 @@ signals:
void sendFile(int32_t, QString, QByteArray);
public slots:
void startFileSend(ToxFile* file);
void startFileSend(ToxFile file);
void onFileRecvRequest(ToxFile file);
private slots:
void onSendTriggered();

429
core.cpp

@ -23,11 +23,18 @@ @@ -23,11 +23,18 @@
#include <QStandardPaths>
#include <QtEndian>
#include <QThread>
#include <QtConcurrent/QtConcurrent>
#include "settings.h"
#define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 30*1000
#define TOX_FILE_INTERVAL 20
#define TOX_BOOTSTRAP_INTERVAL 10*1000
const QString Core::CONFIG_FILE_NAME = "tox_save";
QList<ToxFile> Core::fileSendQueue;
QList<ToxFile> Core::fileRecvQueue;
Core::Core() :
tox(nullptr)
@ -36,13 +43,13 @@ Core::Core() : @@ -36,13 +43,13 @@ Core::Core() :
toxTimer->setSingleShot(true);
saveTimer = new QTimer(this);
saveTimer->start(TOX_SAVE_INTERVAL);
fileTimer = new QTimer(this);
fileTimer->start(TOX_FILE_INTERVAL);
//fileTimer = new QTimer(this);
//fileTimer->start(TOX_FILE_INTERVAL);
bootstrapTimer = new QTimer(this);
bootstrapTimer->start(TOX_BOOTSTRAP_INTERVAL);
connect(toxTimer, &QTimer::timeout, this, &Core::process);
connect(saveTimer, &QTimer::timeout, this, &Core::saveConfiguration);
connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
//connect(fileTimer, &QTimer::timeout, this, &Core::fileHeartbeat);
connect(bootstrapTimer, &QTimer::timeout, this, &Core::onBootstrapTimer);
connect(&Settings::getInstance(), &Settings::dhtServerListChanged, this, &Core::bootstrapDht);
}
@ -137,71 +144,77 @@ void Core::onGroupNamelistChange(Tox*, int groupnumber, int peernumber, uint8_t @@ -137,71 +144,77 @@ 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)
void Core::onFileSendRequestCallback(Tox*, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
uint8_t *filename, uint16_t filename_length, void *core)
{
qDebug() << "Core: File send request callback";
qDebug() << QString("Core: Received file request %1 with friend %2").arg(filenumber).arg(friendnumber);
fileRecvQueue.append(ToxFile(filenumber, friendnumber, QByteArray(), filesize,
CString::toString(filename,filename_length).toUtf8(), ToxFile::RECEIVING));
emit static_cast<Core*>(core)->fileReceiveRequested(fileRecvQueue.last());
}
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 *core)
void Core::onFileControlCallback(Tox*, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
uint8_t control_type, uint8_t*, uint16_t, void *core)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
if (receive_send == 1)
{
if (f.fileNum == filenumber)
for (ToxFile& f : fileSendQueue)
{
file = &f;
break;
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
}
else
{
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
}
if (!file)
{
qWarning("Core::onFileControlCallback: No such file in queue");
// TODO: Warn the Friend* that we're aborting (emit)
return;
}
if (control_type == TOX_FILECONTROL_ACCEPT && receive_send == 1)
{
emit static_cast<Core*>(core)->fileTransferAccepted(file);
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferAccepted(*file);
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");
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
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");
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
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);
emit static_cast<Core*>(core)->fileTransferFinished(file);
}
else
{
file->status = ToxFile::TRANSMITTING;
}
}
file->sendFuture = QtConcurrent::run(sendAllFileData, static_cast<Core*>(core), file);
}
else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
file->sendFuture.waitForFinished(); // Wait for sendAllFileData to return before deleting the ToxFile
removeFileFromQueue(true, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_KILL)
{
qDebug() << QString("Core::onFileControlCallback: Transfer of file %1 cancelled by friend %2")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferCancelled(file);
emit static_cast<Core*>(core)->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
removeFileFromQueue(false, file->friendId, file->fileNum);
}
else if (receive_send == 0 && control_type == TOX_FILECONTROL_FINISHED)
{
qDebug() << QString("Core::onFileControlCallback: Reception of file %1 from %2 finished")
.arg(file->fileNum).arg(file->friendId);
file->status = ToxFile::STOPPED;
emit static_cast<Core*>(core)->fileTransferFinished(*file);
removeFileFromQueue(false, file->friendId, file->fileNum);
}
else
{
@ -210,9 +223,28 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive @@ -210,9 +223,28 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive
}
}
void Core::onFileDataCallback(Tox* tox, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata)
void Core::onFileDataCallback(Tox*, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *core)
{
qDebug() << "Core: File data callback";
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == filenumber && f.friendId == friendnumber)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::onFileDataCallback: No such file in queue");
return;
}
file->fileData.append((char*)data,length);
file->bytesSent += length;
//qDebug() << QString("Core::onFileDataCallback: received %1/%2 bytes").arg(file->fileData.size()).arg(file->filesize);
emit static_cast<Core*>(core)->fileTransferInfo(file->friendId, file->fileNum,
file->filesize, file->bytesSent, ToxFile::RECEIVING);
}
void Core::acceptFriendRequest(const QString& userId)
@ -227,11 +259,11 @@ void Core::acceptFriendRequest(const QString& userId) @@ -227,11 +259,11 @@ void Core::acceptFriendRequest(const QString& userId)
void Core::requestFriendship(const QString& friendAddress, const QString& message)
{
qDebug() << "Core: requesting friendship of "+friendAddress;
CString cMessage(message);
int friendId = tox_add_friend(tox, CFriendAddress(friendAddress).data(), cMessage.data(), cMessage.size());
const QString userId = friendAddress.mid(0, TOX_CLIENT_ID_SIZE * 2);
// TODO: better error handling
if (friendId < 0) {
emit failedToAddFriend(userId);
} else {
@ -277,17 +309,163 @@ void Core::sendFile(int32_t friendId, QString Filename, QByteArray data) @@ -277,17 +309,163 @@ void Core::sendFile(int32_t friendId, QString Filename, QByteArray data)
qWarning() << "Core::sendFile: Can't create the Tox file sender";
return;
}
qDebug() << QString("Core::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
fileSendQueue.append(ToxFile(fileNum, friendId, data, data.size(), fileName, ToxFile::SENDING));
emit fileSendStarted(fileSendQueue.last());
}
fileSendQueue.append(ToxFile(fileNum, friendId, data, fileName, ToxFile::SENDING));
void Core::pauseResumeFileSend(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileSend: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileSend: File is stopped";
}
emit fileSendStarted(&fileSendQueue.last());
void Core::pauseResumeFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit fileTransferPaused(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_PAUSE, nullptr, 0);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
else
qWarning() << "Core::pauseResumeFileRecv: File is stopped";
}
void Core::cancelFileSend(ToxFile* file)
void Core::cancelFileSend(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileSendQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileSend: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file);
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
file->sendFuture.waitForFinished(); // Wait until sendAllFileData returns before deleting
removeFileFromQueue(true, friendId, fileNum);
}
void Core::cancelFileRecv(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::cancelFileRecv: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::RECEIVING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, friendId, fileNum);
}
void Core::rejectFileRecvRequest(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::rejectFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(false, friendId, fileNum);
}
void Core::acceptFileRecvRequest(int friendId, int fileNum)
{
ToxFile* file{nullptr};
for (ToxFile& f : fileRecvQueue)
{
if (f.fileNum == fileNum && f.friendId == friendId)
{
file = &f;
break;
}
}
if (!file)
{
qWarning("Core::acceptFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::TRANSMITTING;
emit fileTransferAccepted(*file);
tox_file_send_control(tox, file->friendId, 1, file->fileNum, TOX_FILECONTROL_ACCEPT, nullptr, 0);
}
void Core::removeFriend(int friendId)
@ -360,7 +538,7 @@ void Core::bootstrapDht() @@ -360,7 +538,7 @@ void Core::bootstrapDht()
static int j = 0;
int i=0;
int listSize = dhtServerList.size();
while (i<2)
while (i<5)
{
const Settings::DhtServer& dhtServer = dhtServerList[j % listSize];
if (tox_bootstrap_from_address(tox, dhtServer.address.toLatin1().data(),
@ -386,46 +564,6 @@ void Core::process() @@ -386,46 +564,6 @@ void Core::process()
toxTimer->start(tox_do_interval(tox));
}
void Core::fileHeartbeat()
{
for (ToxFile& file : fileSendQueue)
{
if (file.status == ToxFile::TRANSMITTING)
{
emit fileTransferInfo(&file);
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;
emit fileTransferCancelled(&file);
tox_file_send_control(tox, file.friendId, 0, file.fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
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;
emit fileTransferFinished(&file);
}
else
qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file.bytesSent).arg(file.fileData.size());
}
}
}
}
void Core::checkConnection()
{
static bool isConnected = false;
@ -553,17 +691,6 @@ void Core::start() @@ -553,17 +691,6 @@ void Core::start()
loadConfiguration();
uint8_t friendAddress[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(tox, friendAddress);
emit friendAddressGenerated(CFriendAddress::toString(friendAddress));
CString cUsername(Settings::getInstance().getUsername());
tox_set_name(tox, cUsername.data(), cUsername.size());
CString cStatusMessage(Settings::getInstance().getStatusMessage());
tox_set_status_message(tox, cStatusMessage.data(), cStatusMessage.size());
tox_callback_friend_request(tox, onFriendRequest, this);
tox_callback_friend_message(tox, onFriendMessage, this);
tox_callback_friend_action(tox, onAction, this);
@ -579,6 +706,17 @@ void Core::start() @@ -579,6 +706,17 @@ void Core::start()
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);
emit friendAddressGenerated(CFriendAddress::toString(friendAddress));
CString cUsername(Settings::getInstance().getUsername());
tox_set_name(tox, cUsername.data(), cUsername.size());
CString cStatusMessage(Settings::getInstance().getStatusMessage());
tox_set_status_message(tox, cStatusMessage.data(), cStatusMessage.size());
bootstrapDht();
toxTimer->start(tox_do_interval(tox));
@ -733,3 +871,80 @@ void Core::quitGroupChat(int groupId) const @@ -733,3 +871,80 @@ void Core::quitGroupChat(int groupId) const
{
tox_del_groupchat(tox, groupId);
}
void Core::removeFileFromQueue(bool sendQueue, int friendId, int fileId)
{
bool found = false;
if (sendQueue)
{
for (int i=0; i<fileSendQueue.size();)
{
if (fileSendQueue[i].friendId == friendId && fileSendQueue[i].fileNum == fileId)
{
found = true;
fileSendQueue.removeAt(i);
continue;
}
i++;
}
}
else
{
for (int i=0; i<fileRecvQueue.size();)
{
if (fileRecvQueue[i].friendId == friendId && fileRecvQueue[i].fileNum == fileId)
{
found = true;
fileRecvQueue.removeAt(i);
continue;
}
i++;
}
}
if (!found)
qWarning() << "Core::removeFileFromQueue: No such file in queue";
}
void Core::sendAllFileData(Core *core, ToxFile* file)
{
while (file->bytesSent < file->fileData.size())
{
if (file->status == ToxFile::PAUSED)
{
QThread::sleep(0);
continue;
}
else if (file->status == ToxFile::STOPPED)
{
qWarning("Core::sendAllFileData: File is stopped");
return;
}
emit core->fileTransferInfo(file->friendId, file->fileNum, file->filesize, file->bytesSent, ToxFile::SENDING);
qApp->processEvents();
int chunkSize = tox_file_data_size(core->tox, file->friendId);
if (chunkSize == -1)
{
qWarning("Core::fileHeartbeat: Error getting preffered chunk size, aborting file send");
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(file->friendId, file->fileNum, ToxFile::SENDING);
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_KILL, nullptr, 0);
removeFileFromQueue(true, file->friendId, file->fileNum);
return;
}
chunkSize = std::min(chunkSize, file->fileData.size());
QByteArray toSend = file->fileData.mid(file->bytesSent, chunkSize);
if (tox_file_send_data(core->tox, file->friendId, file->fileNum, (uint8_t*)toSend.data(), toSend.size()) == -1)
{
qWarning("Core::fileHeartbeat: Error sending data chunk");
QThread::sleep(0);
continue;
}
file->bytesSent += chunkSize;
//qDebug() << QString("Core::fileHeartbeat: sent %1/%2 bytes").arg(file->bytesSent).arg(file->fileData.size());
}
qDebug("Core::fileHeartbeat: Transfer finished");
tox_file_send_control(core->tox, file->friendId, 0, file->fileNum, TOX_FILECONTROL_FINISHED, nullptr, 0);
file->status = ToxFile::STOPPED;
emit core->fileTransferFinished(*file);
removeFileFromQueue(true, file->friendId, file->fileNum);
}

41
core.h

@ -28,11 +28,7 @@ @@ -28,11 +28,7 @@
#include <QString>
#include <QList>
#include <QByteArray>
#define GROUPCHAT_MAX_SIZE 32
#define TOX_SAVE_INTERVAL 30*1000
#define TOX_FILE_INTERVAL 50
#define TOX_BOOTSTRAP_INTERVAL 10*1000
#include <QFuture>
struct DhtServer
{
@ -57,17 +53,20 @@ struct ToxFile @@ -57,17 +53,20 @@ struct ToxFile
RECEIVING
};
ToxFile(int FileNum, int FriendId, QByteArray FileData, QByteArray FileName, FileDirection Direction)
: fileNum(FileNum), friendId(FriendId), fileData{FileData},
fileName{FileName}, bytesSent{0}, status{STOPPED}, direction{Direction} {}
ToxFile()=default;
ToxFile(int FileNum, int FriendId, QByteArray FileData, long long Filesize, QByteArray FileName, FileDirection Direction)
: fileNum(FileNum), friendId(FriendId), fileData{FileData}, fileName{FileName},
bytesSent{0}, filesize(Filesize), status{STOPPED}, direction{Direction} {}
int fileNum;
int friendId;
QByteArray fileData;
QByteArray fileName;
long long bytesSent;
long long filesize;
FileStatus status;
FileDirection direction;
QFuture<void> sendFuture;
};
class Core : public QObject
@ -101,6 +100,9 @@ private: @@ -101,6 +100,9 @@ private:
void loadConfiguration();
void saveConfiguration();
void loadFriends();
static void sendAllFileData(Core* core, ToxFile* file);
static void removeFileFromQueue(bool sendQueue, int friendId, int fileId);
void checkLastOnline(int friendId);
@ -108,7 +110,7 @@ private: @@ -108,7 +110,7 @@ private:
QTimer *toxTimer, *saveTimer, *fileTimer, *bootstrapTimer;
QList<DhtServer> dhtServerList;
int dhtServerId;
static QList<ToxFile> fileSendQueue;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static const QString CONFIG_FILE_NAME;
@ -197,14 +199,18 @@ public slots: @@ -197,14 +199,18 @@ public slots:
void sendTyping(int friendId, bool typing);
void sendFile(int32_t friendId, QString Filename, QByteArray data);
void cancelFileSend(ToxFile* file);
void cancelFileSend(int friendId, int fileNum);
void cancelFileRecv(int friendId, int fileNum);
void rejectFileRecvRequest(int friendId, int fileNum);
void acceptFileRecvRequest(int friendId, int fileNum);
void pauseResumeFileSend(int friendId, int fileNum);
void pauseResumeFileRecv(int friendId, int fileNum);
void setUsername(const QString& username);
void setStatusMessage(const QString& message);
void setStatus(Status status);
void process();
void fileHeartbeat();
void bootstrapDht();
@ -253,12 +259,13 @@ signals: @@ -253,12 +259,13 @@ signals:
void failedToStart();
void fileSendStarted(ToxFile* file);
void fileTransferAccepted(ToxFile* file);
void fileTransferCancelled(ToxFile* file);
void fileTransferFinished(ToxFile* file);
void fileTransferPaused(ToxFile* file);
void fileTransferInfo(ToxFile* file);
void fileSendStarted(ToxFile file);
void fileReceiveRequested(ToxFile file);
void fileTransferAccepted(ToxFile file);
void fileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection direction);
void fileTransferFinished(ToxFile file);
void fileTransferPaused(int FriendId, int FileNum, ToxFile::FileDirection direction);
void fileTransferInfo(int FriendId, int FileNum, int Filesize, int BytesSent, ToxFile::FileDirection direction);
};
#endif // CORE_HPP

120
filetransfertwidget.cpp

@ -1,9 +1,12 @@ @@ -1,9 +1,12 @@
#include "filetransfertwidget.h"
#include "widget.h"
#include "core.h"
#include <QFileDialog>
#include <QPixmap>
FileTransfertWidget::FileTransfertWidget(ToxFile *File)
: file{File}, lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0}
FileTransfertWidget::FileTransfertWidget(ToxFile File)
: lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0},
fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction}
{
pic=new QLabel(), filename=new QLabel(), size=new QLabel(), speed=new QLabel(), eta=new QLabel();
topright = new QPushButton(), bottomright = new QPushButton();
@ -18,13 +21,15 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File) @@ -18,13 +21,15 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File)
setPalette(greybg);
setAutoFillBackground(true);
setFixedSize(250,50);
setMinimumSize(250,50);
setLayout(mainLayout);
mainLayout->setMargin(0);
filename->setText(file->fileName);
pic->setMaximumHeight(40);
pic->setContentsMargins(5,0,0,0);
filename->setText(File.fileName);
filename->setFont(prettysmall);
size->setText(getHumanReadableSize(file->fileData.size()));
size->setText(getHumanReadableSize(File.filesize));
size->setFont(prettysmall);
speed->setText("0B/s");
speed->setFont(prettysmall);
@ -35,11 +40,25 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File) @@ -35,11 +40,25 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File)
progress->setFont(prettysmall);
topright->setIcon(QIcon("img/button icons/no_2x.png"));
connect(topright, SIGNAL(clicked()), this, SLOT(cancelTransfer()));
if (file->direction == ToxFile::SENDING)
if (File.direction == ToxFile::SENDING)
{
bottomright->setIcon(QIcon("img/button icons/pause_2x.png"));
connect(topright, SIGNAL(clicked()), this, SLOT(cancelTransfer()));
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeSend()));
QPixmap preview;
if (preview.loadFromData(File.fileData))
{
preview = preview.scaledToHeight(40);
pic->setPixmap(preview);
}
}
else
{
bottomright->setIcon(QIcon("img/button icons/yes_2x.png"));
connect(topright, SIGNAL(clicked()), this, SLOT(rejectRecvRequest()));
connect(bottomright, SIGNAL(clicked()), this, SLOT(acceptRecvRequest()));
}
QPalette toxgreen;
toxgreen.setColor(QPalette::Button, QColor(107,194,96)); // Tox Green
@ -77,41 +96,47 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File) @@ -77,41 +96,47 @@ FileTransfertWidget::FileTransfertWidget(ToxFile *File)
QString FileTransfertWidget::getHumanReadableSize(int size)
{
static const char* suffix[] = {"B","kB","MB","GB","TB"};
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
int exp = 0;
if (size)
exp = std::min( (int) (log(size) / log(1000)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1));
return QString().setNum(size / pow(1000, exp),'g',3).append(suffix[exp]);
exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1));
return QString().setNum(size / pow(1024, exp),'g',3).append(suffix[exp]);
}
void FileTransfertWidget::onFileTransferInfo(ToxFile* File)
void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int Filesize, int BytesSent, ToxFile::FileDirection Direction)
{
if (File != file)
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
return;
QDateTime newtime = QDateTime::currentDateTime();
int timediff = lastUpdate.secsTo(newtime);
if (!timediff)
if (timediff <= 0)
return;
int diff = File->bytesSent - lastBytesSent;
int diff = BytesSent - lastBytesSent;
if (diff < 0)
diff = 0;
int rawspeed = diff / timediff;
speed->setText(getHumanReadableSize(rawspeed)+"/s");
size->setText(getHumanReadableSize(File->fileData.size()));
size->setText(getHumanReadableSize(Filesize));
if (!rawspeed)
return;
int etaSecs = (File->fileData.size() - File->bytesSent) / rawspeed;
int etaSecs = (Filesize - BytesSent) / rawspeed;
QTime etaTime(0,0);
etaTime = etaTime.addSecs(etaSecs);
eta->setText(etaTime.toString("mm:ss"));
progress->setValue(File->bytesSent*100/File->fileData.size());
if (!Filesize)
progress->setValue(0);
else
progress->setValue(BytesSent*100/Filesize);
lastUpdate = newtime;
lastBytesSent = File->bytesSent;
lastBytesSent = BytesSent;
}
void FileTransfertWidget::onFileTransferCancelled(ToxFile* File)
void FileTransfertWidget::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction)
{
if (File != file)
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
return;
disconnect(topright);
disconnect(Widget::getInstance()->getCore(),0,this,0);
progress->hide();
speed->hide();
eta->hide();
@ -122,11 +147,12 @@ void FileTransfertWidget::onFileTransferCancelled(ToxFile* File) @@ -122,11 +147,12 @@ void FileTransfertWidget::onFileTransferCancelled(ToxFile* File)
setPalette(toxred);
}
void FileTransfertWidget::onFileTransferFinished(ToxFile* File)
void FileTransfertWidget::onFileTransferFinished(ToxFile File)
{
if (File != file)
if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction)
return;
disconnect(topright);
topright->disconnect();
disconnect(Widget::getInstance()->getCore(),0,this,0);
progress->hide();
speed->hide();
eta->hide();
@ -137,9 +163,55 @@ void FileTransfertWidget::onFileTransferFinished(ToxFile* File) @@ -137,9 +163,55 @@ void FileTransfertWidget::onFileTransferFinished(ToxFile* File)
QPalette toxgreen;
toxgreen.setColor(QPalette::Window, QColor(107,194,96)); // Tox Green
setPalette(toxgreen);
if (File.direction == ToxFile::RECEIVING)
{
QFile saveFile(savePath);
if (!saveFile.open(QIODevice::WriteOnly))
return;
saveFile.write(File.fileData);
saveFile.close();
QPixmap preview;
if (preview.loadFromData(File.fileData))
{
preview = preview.scaledToHeight(40);
pic->setPixmap(preview);
}
}
}
void FileTransfertWidget::cancelTransfer()
{
Widget::getInstance()->getCore()->cancelFileSend(file);
Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum);
}
void FileTransfertWidget::rejectRecvRequest()
{
Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum);
onFileTransferCancelled(friendId, fileNum, direction);
}
void FileTransfertWidget::acceptRecvRequest()
{
QString path = QFileDialog::getSaveFileName(0,"Save a file",QDir::currentPath()+'/'+filename->text());
if (path.isEmpty())
return;
savePath = path;
bottomright->setIcon(QIcon("img/button icons/pause_2x.png"));
bottomright->disconnect();
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeRecv()));
Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum);
}
void FileTransfertWidget::pauseResumeRecv()
{
Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum);
}
void FileTransfertWidget::pauseResumeSend()
{
Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum);
}

17
filetransfertwidget.h

@ -17,15 +17,19 @@ class FileTransfertWidget : public QWidget @@ -17,15 +17,19 @@ class FileTransfertWidget : public QWidget
{
Q_OBJECT
public:
FileTransfertWidget(ToxFile* File);
FileTransfertWidget(ToxFile File);
public slots:
void onFileTransferInfo(ToxFile* File);
void onFileTransferCancelled(ToxFile* File);
void onFileTransferFinished(ToxFile* File);
void onFileTransferInfo(int FriendId, int FileNum, int Filesize, int BytesSent, ToxFile::FileDirection Direction);
void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction);
void onFileTransferFinished(ToxFile File);
private slots:
void cancelTransfer();
void rejectRecvRequest();
void acceptRecvRequest();
void pauseResumeRecv();
void pauseResumeSend();
private:
QString getHumanReadableSize(int size);
@ -34,11 +38,14 @@ private: @@ -34,11 +38,14 @@ private:
QLabel *pic, *filename, *size, *speed, *eta;
QPushButton *topright, *bottomright;
QProgressBar *progress;
ToxFile* file;
QHBoxLayout *mainLayout, *textLayout;
QVBoxLayout *infoLayout, *buttonLayout;
QDateTime lastUpdate;
long long lastBytesSent;
int fileNum;
int friendId;
QString savePath;
ToxFile::FileDirection direction;
};
#endif // FILETRANSFERTWIDGET_H

7
main.cpp

@ -15,3 +15,10 @@ int main(int argc, char *argv[]) @@ -15,3 +15,10 @@ int main(int argc, char *argv[])
return errorcode;
}
/** TODO
*
* Sort the friend list by status, online first then busy then offline
* Don't do anything if a friend is disconnected, don't print to the chat
*
*/

10
widget.cpp

@ -37,6 +37,8 @@ Widget::Widget(QWidget *parent) : @@ -37,6 +37,8 @@ Widget::Widget(QWidget *parent) :
qRegisterMetaType<Status>("Status");
qRegisterMetaType<uint8_t>("uint8_t");
qRegisterMetaType<int32_t>("int32_t");
qRegisterMetaType<ToxFile>("ToxFile");
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
core = new Core();
coreThread = new QThread(this);
@ -51,6 +53,7 @@ Widget::Widget(QWidget *parent) : @@ -51,6 +53,7 @@ Widget::Widget(QWidget *parent) :
connect(core, &Core::statusMessageSet, this, &Widget::setStatusMessage);
connect(core, &Core::friendAddressGenerated, &settingsForm, &SettingsForm::setFriendAddress);
connect(core, &Core::friendAdded, this, &Widget::addFriend);
connect(core, &Core::failedToAddFriend, this, &Widget::addFriendFailed);
connect(core, &Core::friendStatusChanged, this, &Widget::onFriendStatusChanged);
connect(core, &Core::friendUsernameChanged, this, &Widget::onFriendUsernameChanged);
connect(core, &Core::friendStatusChanged, this, &Widget::onFriendStatusChanged);
@ -217,6 +220,7 @@ void Widget::setStatusMessage(const QString &statusMessage) @@ -217,6 +220,7 @@ void Widget::setStatusMessage(const QString &statusMessage)
void Widget::addFriend(int friendId, const QString &userId)
{
qDebug() << "Adding friend with id "+userId;
Friend* newfriend = FriendList::addFriend(friendId, userId);
QWidget* widget = ui->friendList->widget();
QLayout* layout = widget->layout();
@ -225,6 +229,12 @@ void Widget::addFriend(int friendId, const QString &userId) @@ -225,6 +229,12 @@ void Widget::addFriend(int friendId, const QString &userId)
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)));
connect(core, &Core::fileReceiveRequested, newfriend->chatForm, &ChatForm::onFileRecvRequest);
}
void Widget::addFriendFailed(const QString&)
{
QMessageBox::critical(0,"Error","Couldn't request friendship");
}
void Widget::onFriendStatusChanged(int friendId, Status status)

1
widget.h

@ -53,6 +53,7 @@ private slots: @@ -53,6 +53,7 @@ private slots:
void setUsername(const QString& username);
void setStatusMessage(const QString &statusMessage);
void addFriend(int friendId, const QString& userId);
void addFriendFailed(const QString& userId);
void onFriendStatusChanged(int friendId, Status status);
void onFriendStatusMessageChanged(int friendId, const QString& message);
void onFriendUsernameChanged(int friendId, const QString& username);

Loading…
Cancel
Save