From ec69308efc26c0eb2cb626b2be0df301f87578cc Mon Sep 17 00:00:00 2001 From: "Tux3 / Mlkj / !Lev.uXFMLA" Date: Thu, 26 Jun 2014 00:30:24 +0200 Subject: [PATCH] Simple file transfer widget --- chatform.cpp | 35 ++++++++++++++- chatform.h | 4 ++ core.cpp | 45 +++++++++++-------- core.h | 19 +++++++-- filetransfertwidget.cpp | 95 +++++++++++++++++++++++++++++++++++++++++ filetransfertwidget.h | 41 ++++++++++++++++++ toxgui.pro | 7 +-- 7 files changed, 220 insertions(+), 26 deletions(-) create mode 100644 filetransfertwidget.cpp create mode 100644 filetransfertwidget.h diff --git a/chatform.cpp b/chatform.cpp index fd1792c94..dae9d61ce 100644 --- a/chatform.cpp +++ b/chatform.cpp @@ -2,6 +2,7 @@ #include "friend.h" #include "friendwidget.h" #include "widget.h" +#include "filetransfertwidget.h" #include #include #include @@ -67,6 +68,7 @@ ChatForm::ChatForm(Friend* chatFriend) chatArea->setWidget(chatAreaWidget); + connect(Widget::getInstance()->getCore(), &Core::fileSendStarted, this, &ChatForm::startFileSend); connect(sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered())); connect(fileButton, SIGNAL(clicked()), this, SLOT(onAttachClicked())); connect(msgEdit, SIGNAL(enterPressed()), this, SLOT(onSendTriggered())); @@ -166,8 +168,6 @@ void ChatForm::onAttachClicked() file.close(); QFileInfo fi(path); - // TODO: Show file send widget - emit sendFile(f->friendId, fi.fileName(), fileData); } @@ -177,3 +177,34 @@ void ChatForm::onSliderRangeChanged() if (lockSliderToBottom) scroll->setValue(scroll->maximum()); } + +void ChatForm::startFileSend(ToxFile *file) +{ + QLabel *author = new QLabel(Widget::getInstance()->getUsername()), *date = new QLabel(); + QScrollBar* scroll = chatArea->verticalScrollBar(); + lockSliderToBottom = scroll && scroll->value() == scroll->maximum(); + author->setAlignment(Qt::AlignTop | Qt::AlignRight); + date->setAlignment(Qt::AlignTop); + QPalette pal; + pal.setColor(QPalette::WindowText, Qt::gray); + author->setPalette(pal); + 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); +} diff --git a/chatform.h b/chatform.h index 3a6607a14..772560f78 100644 --- a/chatform.h +++ b/chatform.h @@ -12,6 +12,7 @@ #include "chattextedit.h" #include "ui_widget.h" +#include "core.h" // Spacing in px inserted when the author of the last message changes #define AUTHOR_CHANGE_SPACING 5 @@ -35,6 +36,9 @@ signals: void sendMessage(int, QString); void sendFile(int32_t, QString, QByteArray); +public slots: + void startFileSend(ToxFile* file); + private slots: void onSendTriggered(); void onAttachClicked(); diff --git a/core.cpp b/core.cpp index 273e09b65..0ce49a740 100644 --- a/core.cpp +++ b/core.cpp @@ -136,10 +136,26 @@ void Core::onFileSendRequestCallback(Tox* tox, int32_t friendnumber, uint8_t fil 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) + uint8_t control_type, uint8_t *data, uint16_t length, void *core) { + 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; + } if (control_type == TOX_FILECONTROL_ACCEPT && receive_send == 1) { + emit static_cast(core)->fileTransferAccepted(file); qDebug() << "Core: File control callback, file accepted"; int chunkSize = tox_file_data_size(tox, friendnumber); if (chunkSize == -1) @@ -148,21 +164,6 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive // 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) @@ -178,7 +179,7 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive { 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) + emit static_cast(core)->fileTransferFinished(file); } else { @@ -186,6 +187,11 @@ void Core::onFileControlCallback(Tox* tox, int32_t friendnumber, uint8_t receive } } } + else if (receive_send == 1 && control_type == TOX_FILECONTROL_KILL) + { + file->status = ToxFile::STOPPED; + emit static_cast(core)->fileTransferCancelled(file); + } else { qDebug() << QString("Core: File control callback, receive_send=%1, control_type=%2") @@ -262,7 +268,9 @@ void Core::sendFile(int32_t friendId, QString Filename, QByteArray data) return; } - fileSendQueue.append(ToxFile(fileNum, friendId, data, fileName)); + fileSendQueue.append(ToxFile(fileNum, friendId, data, fileName, ToxFile::SENDING)); + + emit fileSendStarted(&fileSendQueue.last()); } void Core::removeFriend(int friendId) @@ -355,6 +363,7 @@ void Core::fileHeartbeat() { if (file.status == ToxFile::TRANSMITTING) { + emit fileTransferInfo(&file); int chunkSize = tox_file_data_size(tox, file.friendId); if (chunkSize == -1) { diff --git a/core.h b/core.h index ea7091a74..84ab624d7 100644 --- a/core.h +++ b/core.h @@ -50,9 +50,15 @@ struct ToxFile TRANSMITTING }; - ToxFile(int FileNum, int FriendId, QByteArray FileData, QByteArray FileName) + enum FileDirection : bool + { + SENDING, + RECEIVING + }; + + ToxFile(int FileNum, int FriendId, QByteArray FileData, QByteArray FileName, FileDirection Direction) : fileNum(FileNum), friendId(FriendId), fileData{FileData}, - fileName{FileName}, bytesSent{0}, status{STOPPED} {} + fileName{FileName}, bytesSent{0}, status{STOPPED}, direction{Direction} {} int fileNum; int friendId; @@ -60,6 +66,7 @@ struct ToxFile QByteArray fileName; long long bytesSent; FileStatus status; + FileDirection direction; }; class Core : public QObject @@ -84,7 +91,7 @@ private: 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); + uint8_t control_type, uint8_t *data, uint16_t length, void *core); static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata); void checkConnection(); @@ -243,6 +250,12 @@ 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); }; #endif // CORE_HPP diff --git a/filetransfertwidget.cpp b/filetransfertwidget.cpp new file mode 100644 index 000000000..0886806dc --- /dev/null +++ b/filetransfertwidget.cpp @@ -0,0 +1,95 @@ +#include "filetransfertwidget.h" + +FileTransfertWidget::FileTransfertWidget(ToxFile *File) + : file{File}, lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0} +{ + pic=new QLabel(), filename=new QLabel(), size=new QLabel(), speed=new QLabel(), eta=new QLabel(); + topright = new QPushButton(), bottomright = new QPushButton(); + progress = new QProgressBar(); + mainLayout = new QHBoxLayout(), textLayout = new QHBoxLayout(); + infoLayout = new QVBoxLayout(), buttonLayout = new QVBoxLayout(); + QFont prettysmall; + prettysmall.setPixelSize(10); + QPalette greybg; + greybg.setColor(QPalette::Window, Qt::gray); + setPalette(greybg); + setAutoFillBackground(true); + + setFixedSize(250,50); + setLayout(mainLayout); + + filename->setText(file->fileName); + filename->setFont(prettysmall); + size->setText(getHumanReadableSize(file->fileData.size())); + size->setFont(prettysmall); + speed->setText("0B/s"); + speed->setFont(prettysmall); + eta->setText("00:00"); + eta->setFont(prettysmall); + progress->setValue(0); + + topright->setIcon(QIcon("img/button icons/no_2x.png")); + if (file->direction == ToxFile::SENDING) + bottomright->setIcon(QIcon("img/button icons/pause_2x.png")); + else + bottomright->setIcon(QIcon("img/button icons/yes_2x.png")); + + topright->setIconSize(QSize(25,25)); + bottomright->setIconSize(QSize(25,25)); + + mainLayout->addWidget(pic); + mainLayout->addLayout(infoLayout); + mainLayout->addLayout(buttonLayout); + + infoLayout->addWidget(filename); + infoLayout->addLayout(textLayout); + infoLayout->addWidget(progress); + + textLayout->addWidget(size); + textLayout->addWidget(speed); + textLayout->addWidget(eta); + textLayout->setMargin(0); + textLayout->setSpacing(5); +} + +QString FileTransfertWidget::getHumanReadableSize(int size) +{ + static const char* suffix[] = {"B","kB","MB","GB","TB"}; + 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]); +} + +void FileTransfertWidget::onFileTransferInfo(ToxFile* File) +{ + if (File != file) + return; + QDateTime newtime = QDateTime::currentDateTime(); + int timediff = lastUpdate.secsTo(newtime); + if (!timediff) + return; + int diff = File->bytesSent - lastBytesSent; + int rawspeed = diff / timediff; + speed->setText(getHumanReadableSize(rawspeed)+"/s"); + size->setText(getHumanReadableSize(File->fileData.size())); + if (!rawspeed) + return; + int etaSecs = (File->fileData.size() - File->bytesSent) / rawspeed; + QTime etaTime(0,0); + etaTime = etaTime.addSecs(etaSecs); + eta->setText(etaTime.toString("mm:ss")); + progress->setValue(File->bytesSent*100/File->fileData.size()); + lastUpdate = newtime; + lastBytesSent = File->bytesSent; +} + +void FileTransfertWidget::onFileTransferCancelled(ToxFile* File) +{ + +} + +void FileTransfertWidget::onFileTransferFinished(ToxFile* File) +{ + +} diff --git a/filetransfertwidget.h b/filetransfertwidget.h new file mode 100644 index 000000000..b1d0ce1bf --- /dev/null +++ b/filetransfertwidget.h @@ -0,0 +1,41 @@ +#ifndef FILETRANSFERTWIDGET_H +#define FILETRANSFERTWIDGET_H + +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +class ToxFile; + +class FileTransfertWidget : public QWidget +{ + Q_OBJECT +public: + FileTransfertWidget(ToxFile* File); + +public slots: + void onFileTransferInfo(ToxFile* File); + void onFileTransferCancelled(ToxFile* File); + void onFileTransferFinished(ToxFile* File); + +private: + QString getHumanReadableSize(int size); + +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; +}; + +#endif // FILETRANSFERTWIDGET_H diff --git a/toxgui.pro b/toxgui.pro index 0de99c901..c4ed196aa 100644 --- a/toxgui.pro +++ b/toxgui.pro @@ -32,7 +32,8 @@ SOURCES += main.cpp\ groupwidget.cpp \ group.cpp \ grouplist.cpp \ - groupchatform.cpp + groupchatform.cpp \ + filetransfertwidget.cpp HEADERS += widget.h \ core.h \ @@ -43,7 +44,6 @@ HEADERS += widget.h \ editablelabelwidget.h \ elidelabel.h \ copyableelidelabel.h \ - elidelabel.h \ esclineedit.h \ friendlist.h \ friend.h \ @@ -54,7 +54,8 @@ HEADERS += widget.h \ groupwidget.h \ group.h \ grouplist.h \ - groupchatform.h + groupchatform.h \ + filetransfertwidget.h FORMS += widget.ui