329
filetransferinstance.cpp
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre 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 COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "filetransferinstance.h"
|
||||||
|
#include "widget/widget.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "math.h"
|
||||||
|
#include "style.h"
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
uint FileTransferInstance::Idconter = 0;
|
||||||
|
|
||||||
|
FileTransferInstance::FileTransferInstance(ToxFile File)
|
||||||
|
: lastUpdate{QDateTime::currentDateTime()}, lastBytesSent{0},
|
||||||
|
fileNum{File.fileNum}, friendId{File.friendId}, direction{File.direction}
|
||||||
|
{
|
||||||
|
id = Idconter++;
|
||||||
|
state = tsPending;
|
||||||
|
|
||||||
|
filename = File.fileName;
|
||||||
|
size = getHumanReadableSize(File.filesize);
|
||||||
|
speed = "0B/s";
|
||||||
|
eta = "00:00";
|
||||||
|
if (File.direction == ToxFile::SENDING)
|
||||||
|
{
|
||||||
|
QImage preview;
|
||||||
|
File.file->seek(0);
|
||||||
|
if (preview.loadFromData(File.file->readAll()))
|
||||||
|
{
|
||||||
|
pic = preview.scaledToHeight(50);
|
||||||
|
}
|
||||||
|
File.file->seek(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::getHumanReadableSize(unsigned long long size)
|
||||||
|
{
|
||||||
|
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
|
||||||
|
int exp = 0;
|
||||||
|
if (size)
|
||||||
|
exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1));
|
||||||
|
return QString().setNum(size / pow(1024, exp),'f',2).append(suffix[exp]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction)
|
||||||
|
{
|
||||||
|
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = tsProcessing;
|
||||||
|
QDateTime newtime = QDateTime::currentDateTime();
|
||||||
|
int timediff = lastUpdate.secsTo(newtime);
|
||||||
|
if (timediff <= 0)
|
||||||
|
return;
|
||||||
|
qint64 diff = BytesSent - lastBytesSent;
|
||||||
|
if (diff < 0)
|
||||||
|
{
|
||||||
|
qWarning() << "FileTransferInstance::onFileTransferInfo: Negative transfer speed !";
|
||||||
|
diff = 0;
|
||||||
|
}
|
||||||
|
long rawspeed = diff / timediff;
|
||||||
|
speed = getHumanReadableSize(rawspeed)+"/s";
|
||||||
|
size = getHumanReadableSize(Filesize);
|
||||||
|
if (!rawspeed)
|
||||||
|
return;
|
||||||
|
int etaSecs = (Filesize - BytesSent) / rawspeed;
|
||||||
|
QTime etaTime(0,0);
|
||||||
|
etaTime = etaTime.addSecs(etaSecs);
|
||||||
|
eta = etaTime.toString("mm:ss");
|
||||||
|
lastUpdate = newtime;
|
||||||
|
lastBytesSent = BytesSent;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction)
|
||||||
|
{
|
||||||
|
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
||||||
|
return;
|
||||||
|
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
||||||
|
state = tsCanceled;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::onFileTransferFinished(ToxFile File)
|
||||||
|
{
|
||||||
|
if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction)
|
||||||
|
return;
|
||||||
|
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
||||||
|
|
||||||
|
if (File.direction == ToxFile::RECEIVING)
|
||||||
|
{
|
||||||
|
QImage preview;
|
||||||
|
QFile previewFile(File.filePath);
|
||||||
|
if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images
|
||||||
|
{
|
||||||
|
if (preview.loadFromData(previewFile.readAll()))
|
||||||
|
{
|
||||||
|
pic = preview.scaledToHeight(50);
|
||||||
|
}
|
||||||
|
previewFile.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = tsFinished;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::cancelTransfer()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum);
|
||||||
|
state = tsCanceled;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::rejectRecvRequest()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum);
|
||||||
|
onFileTransferCancelled(friendId, fileNum, direction);
|
||||||
|
state = tsCanceled;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist
|
||||||
|
// which makes it useless for our case
|
||||||
|
// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file
|
||||||
|
// surely this is a common problem that has a qt-implemented solution?
|
||||||
|
bool isFileWritable(QString& path)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
bool exists = file.exists();
|
||||||
|
bool out = file.open(QIODevice::WriteOnly);
|
||||||
|
file.close();
|
||||||
|
if (!exists)
|
||||||
|
file.remove();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::acceptRecvRequest()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
path = QFileDialog::getSaveFileName(Widget::getInstance(), tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename));
|
||||||
|
if (path.isEmpty())
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//bool savable = QFileInfo(path).isWritable();
|
||||||
|
//qDebug() << path << " is writable: " << savable;
|
||||||
|
//qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable();
|
||||||
|
if (isFileWritable(path))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
QMessageBox::warning(Widget::getInstance(), tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
savePath = path;
|
||||||
|
|
||||||
|
Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path);
|
||||||
|
state = tsProcessing;
|
||||||
|
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pauseResumeRecv()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum);
|
||||||
|
if (state == tsProcessing)
|
||||||
|
state = tsPaused;
|
||||||
|
else state = tsProcessing;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pauseResumeSend()
|
||||||
|
{
|
||||||
|
Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum);
|
||||||
|
if (state == tsProcessing)
|
||||||
|
state = tsPaused;
|
||||||
|
else state = tsProcessing;
|
||||||
|
emit stateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::QImage2base64(const QImage &img)
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
QBuffer buffer(&ba);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
img.save(&buffer, "PNG"); // writes image into ba in PNG format
|
||||||
|
return ba.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::getHtmlImage()
|
||||||
|
{
|
||||||
|
qDebug() << "QString FileTransferInstance::getHtmlImage() " << state;
|
||||||
|
|
||||||
|
QString res;
|
||||||
|
if (state == tsPending || state == tsProcessing || state == tsPaused)
|
||||||
|
{
|
||||||
|
QImage leftBtnImg(":/ui/fileTransferInstance/stopFileButton.png");
|
||||||
|
QImage rightBtnImg;
|
||||||
|
if (state == tsProcessing)
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/pauseFileButton.png");
|
||||||
|
else if (state == tsPaused)
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/resumeFileButton.png");
|
||||||
|
else
|
||||||
|
rightBtnImg = QImage(":/ui/fileTransferInstance/acceptFileButton.png");
|
||||||
|
|
||||||
|
res = draw2ButtonsForm("silver", leftBtnImg, rightBtnImg);
|
||||||
|
} else if (state == tsCanceled)
|
||||||
|
{
|
||||||
|
res = drawButtonlessForm("red");
|
||||||
|
} else if (state == tsFinished)
|
||||||
|
{
|
||||||
|
res = drawButtonlessForm("green");
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileTransferInstance::pressFromHtml(QString code)
|
||||||
|
{
|
||||||
|
if (state == tsFinished || state == tsCanceled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (direction == ToxFile::SENDING)
|
||||||
|
{
|
||||||
|
if (code == "btnA")
|
||||||
|
cancelTransfer();
|
||||||
|
else if (code == "btnB")
|
||||||
|
pauseResumeSend();
|
||||||
|
} else {
|
||||||
|
if (code == "btnA")
|
||||||
|
rejectRecvRequest();
|
||||||
|
else if (code == "btnB")
|
||||||
|
{
|
||||||
|
if (state == tsPending)
|
||||||
|
acceptRecvRequest();
|
||||||
|
else
|
||||||
|
pauseResumeRecv();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::drawButtonlessForm(const QString &type)
|
||||||
|
{
|
||||||
|
QString imgAStr;
|
||||||
|
QString imgBStr;
|
||||||
|
|
||||||
|
if (type == "red")
|
||||||
|
{
|
||||||
|
imgAStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyLRedFileButton.png")) + "\">";
|
||||||
|
imgBStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyRRedFileButton.png")) + "\">";
|
||||||
|
} else {
|
||||||
|
imgAStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyLGreenFileButton.png")) + "\">";
|
||||||
|
imgBStr = "<img src=\"data:placeholder/png;base64," + QImage2base64(QImage(":/ui/fileTransferInstance/emptyRGreenFileButton.png")) + "\">";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString content = "<p>" + filename + "</p><p>" + size + "</p>";
|
||||||
|
|
||||||
|
return wrapIntoForm(content, type, imgAStr, imgBStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::insertMiniature(const QString &type)
|
||||||
|
{
|
||||||
|
if (pic == QImage())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
QString widgetId = QString::number(getId());
|
||||||
|
|
||||||
|
QString res;
|
||||||
|
res = "<td><div class=" + type + ">\n";
|
||||||
|
res += "<img src=\"data:mini." + widgetId + "/png;base64," + QImage2base64(pic) + "\">";
|
||||||
|
res += "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB)
|
||||||
|
{
|
||||||
|
QString widgetId = QString::number(getId());
|
||||||
|
QString imgAstr = "<img src=\"data:ftrans." + widgetId + ".btnA/png;base64," + QImage2base64(imgA) + "\">";
|
||||||
|
QString imgBstr = "<img src=\"data:ftrans." + widgetId + ".btnB/png;base64," + QImage2base64(imgB) + "\">";
|
||||||
|
|
||||||
|
QString content;
|
||||||
|
content += "<p>" + filename + "</p>";
|
||||||
|
content += "<p>" + getHumanReadableSize(lastBytesSent) + " / " + size + " (" + speed + " ETA: " + eta + ")</p>\n";
|
||||||
|
|
||||||
|
return wrapIntoForm(content, type, imgAstr, imgBstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferInstance::wrapIntoForm(const QString& content, const QString &type, const QString &imgAstr, const QString &imgBstr)
|
||||||
|
{
|
||||||
|
QString res;
|
||||||
|
|
||||||
|
res = "<table widht=100% cellspacing=\"0\">\n";
|
||||||
|
res += "<tr valign=middle>\n";
|
||||||
|
res += insertMiniature(type);
|
||||||
|
res += "<td width=100%>\n";
|
||||||
|
res += "<div class=" + type + ">";
|
||||||
|
res += content;
|
||||||
|
res += "</div>\n";
|
||||||
|
res += "</td>\n";
|
||||||
|
res += "<td>\n";
|
||||||
|
res += "<div class=button>" + imgAstr + "<br>" + imgBstr+ "</div>\n";
|
||||||
|
res += "</td>\n";
|
||||||
|
res += "</tr>\n";
|
||||||
|
res += "</table>\n";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
|
@ -13,11 +13,10 @@
|
||||||
|
|
||||||
See the COPYING file for more details.
|
See the COPYING file for more details.
|
||||||
*/
|
*/
|
||||||
|
#ifndef FILETRANSFERINSTANCE_H
|
||||||
|
#define FILETRANSFERINSTANCE_H
|
||||||
|
|
||||||
#ifndef FILETRANSFERTWIDGET_H
|
#include <QObject>
|
||||||
#define FILETRANSFERTWIDGET_H
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
@ -29,17 +28,22 @@
|
||||||
|
|
||||||
struct ToxFile;
|
struct ToxFile;
|
||||||
|
|
||||||
class FileTransfertWidget : public QWidget
|
class FileTransferInstance : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileTransfertWidget(ToxFile File);
|
explicit FileTransferInstance(ToxFile File);
|
||||||
|
QString getHtmlImage();
|
||||||
|
uint getId(){return id;}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction);
|
void onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction);
|
||||||
void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction);
|
void onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction);
|
||||||
void onFileTransferFinished(ToxFile File);
|
void onFileTransferFinished(ToxFile File);
|
||||||
|
void pressFromHtml(QString);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void stateUpdated();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void cancelTransfer();
|
void cancelTransfer();
|
||||||
|
@ -50,14 +54,21 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getHumanReadableSize(unsigned long long size);
|
QString getHumanReadableSize(unsigned long long size);
|
||||||
|
QString QImage2base64(const QImage &img);
|
||||||
|
QString drawButtonlessForm(const QString &type);
|
||||||
|
QString draw2ButtonsForm(const QString &type, const QImage &imgA, const QImage &imgB);
|
||||||
|
QString insertMiniature(const QString &type);
|
||||||
|
QString wrapIntoForm(const QString &content, const QString &type, const QString &imgAstr, const QString &imgBstr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *pic, *filename, *size, *speed, *eta;
|
enum TransfState {tsPending, tsProcessing, tsPaused, tsFinished, tsCanceled};
|
||||||
QPushButton *topright, *bottomright;
|
|
||||||
QProgressBar *progress;
|
static uint Idconter;
|
||||||
QHBoxLayout *mainLayout, *textLayout;
|
uint id;
|
||||||
QVBoxLayout *infoLayout, *buttonLayout;
|
|
||||||
QWidget* buttonWidget;
|
TransfState state;
|
||||||
|
QImage pic;
|
||||||
|
QString filename, size, speed, eta;
|
||||||
QDateTime lastUpdate;
|
QDateTime lastUpdate;
|
||||||
long long lastBytesSent;
|
long long lastBytesSent;
|
||||||
int fileNum;
|
int fileNum;
|
||||||
|
@ -65,7 +76,6 @@ private:
|
||||||
QString savePath;
|
QString savePath;
|
||||||
ToxFile::FileDirection direction;
|
ToxFile::FileDirection direction;
|
||||||
QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet;
|
QString stopFileButtonStylesheet, pauseFileButtonStylesheet, acceptFileButtonStylesheet;
|
||||||
void paintEvent(QPaintEvent *);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILETRANSFERTWIDGET_H
|
#endif // FILETRANSFERINSTANCE_H
|
14
qtox.pro
|
@ -57,7 +57,7 @@ win32 {
|
||||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -lvpx -lopenal -lopencv_core -lopencv_highgui
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(JENKINS, YES) {
|
contains(JENKINS, YES) {
|
||||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a -lopencv_core -lopencv_highgui -lopenal
|
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a -lopencv_core -lopencv_highgui -lopenal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,6 @@ HEADERS += widget/form/addfriendform.h \
|
||||||
widget/form/filesform.h \
|
widget/form/filesform.h \
|
||||||
widget/tool/chattextedit.h \
|
widget/tool/chattextedit.h \
|
||||||
widget/tool/friendrequestdialog.h \
|
widget/tool/friendrequestdialog.h \
|
||||||
widget/filetransfertwidget.h \
|
|
||||||
widget/friendwidget.h \
|
widget/friendwidget.h \
|
||||||
widget/groupwidget.h \
|
widget/groupwidget.h \
|
||||||
widget/widget.h \
|
widget/widget.h \
|
||||||
|
@ -102,7 +101,10 @@ HEADERS += widget/form/addfriendform.h \
|
||||||
widget/croppinglabel.h \
|
widget/croppinglabel.h \
|
||||||
widget/friendlistwidget.h \
|
widget/friendlistwidget.h \
|
||||||
widget/genericchatroomwidget.h \
|
widget/genericchatroomwidget.h \
|
||||||
widget/form/genericchatform.h
|
widget/form/genericchatform.h \
|
||||||
|
widget/tool/chataction.h \
|
||||||
|
widget/chatareawidget.h \
|
||||||
|
filetransferinstance.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
widget/form/addfriendform.cpp \
|
widget/form/addfriendform.cpp \
|
||||||
|
@ -112,7 +114,6 @@ SOURCES += \
|
||||||
widget/form/filesform.cpp \
|
widget/form/filesform.cpp \
|
||||||
widget/tool/chattextedit.cpp \
|
widget/tool/chattextedit.cpp \
|
||||||
widget/tool/friendrequestdialog.cpp \
|
widget/tool/friendrequestdialog.cpp \
|
||||||
widget/filetransfertwidget.cpp \
|
|
||||||
widget/friendwidget.cpp \
|
widget/friendwidget.cpp \
|
||||||
widget/groupwidget.cpp \
|
widget/groupwidget.cpp \
|
||||||
widget/widget.cpp \
|
widget/widget.cpp \
|
||||||
|
@ -136,4 +137,7 @@ SOURCES += \
|
||||||
widget/friendlistwidget.cpp \
|
widget/friendlistwidget.cpp \
|
||||||
coreav.cpp \
|
coreav.cpp \
|
||||||
widget/genericchatroomwidget.cpp \
|
widget/genericchatroomwidget.cpp \
|
||||||
widget/form/genericchatform.cpp
|
widget/form/genericchatform.cpp \
|
||||||
|
widget/tool/chataction.cpp \
|
||||||
|
widget/chatareawidget.cpp \
|
||||||
|
filetransferinstance.cpp
|
||||||
|
|
11
res.qrc
|
@ -42,6 +42,7 @@
|
||||||
<file>ui/callButton/callButtonYellowHover.png</file>
|
<file>ui/callButton/callButtonYellowHover.png</file>
|
||||||
<file>ui/callButton/callButtonYellowPressed.png</file>
|
<file>ui/callButton/callButtonYellowPressed.png</file>
|
||||||
<file>ui/chatArea/chatArea.css</file>
|
<file>ui/chatArea/chatArea.css</file>
|
||||||
|
<file>ui/chatArea/innerStyle.css</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
<file>ui/chatArea/scrollBarDownArrow.png</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
<file>ui/chatArea/scrollBarDownArrowHover.png</file>
|
||||||
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
<file>ui/chatArea/scrollBarDownArrowPressed.png</file>
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
<file>ui/fileButton/fileButton.png</file>
|
<file>ui/fileButton/fileButton.png</file>
|
||||||
<file>ui/fileButton/fileButtonHover.png</file>
|
<file>ui/fileButton/fileButtonHover.png</file>
|
||||||
<file>ui/fileButton/fileButtonPressed.png</file>
|
<file>ui/fileButton/fileButtonPressed.png</file>
|
||||||
|
<file>ui/fileButton/fileButtonDisabled.png</file>
|
||||||
<file>ui/msgEdit/msgEdit.css</file>
|
<file>ui/msgEdit/msgEdit.css</file>
|
||||||
<file>ui/pauseFileButton/default.png</file>
|
<file>ui/pauseFileButton/default.png</file>
|
||||||
<file>ui/pauseFileButton/hover.png</file>
|
<file>ui/pauseFileButton/hover.png</file>
|
||||||
|
@ -124,6 +126,13 @@
|
||||||
<file>ui/micButton/micButtonPressed.png</file>
|
<file>ui/micButton/micButtonPressed.png</file>
|
||||||
<file>ui/micButton/micButton.css</file>
|
<file>ui/micButton/micButton.css</file>
|
||||||
<file>ui/volButton/volButton.css</file>
|
<file>ui/volButton/volButton.css</file>
|
||||||
<file>ui/fileButton/fileButtonDisabled.png</file>
|
<file>ui/fileTransferInstance/acceptFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/pauseFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/resumeFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/stopFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyLGreenFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyLRedFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyRGreenFileButton.png</file>
|
||||||
|
<file>ui/fileTransferInstance/emptyRRedFileButton.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
63
ui/chatArea/innerStyle.css
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
div.name_me {
|
||||||
|
color: #646464;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.name {
|
||||||
|
color: #000000;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.message {
|
||||||
|
color: #000000;
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.date {
|
||||||
|
color: #000000;
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.quote {
|
||||||
|
background-color: #6bc260;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.green {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #6bc260;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.silver {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #000000;
|
||||||
|
background-color: #d1d1d1;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.red {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
margin-left: 12px;
|
||||||
|
margin-right: 12px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: rgb(200,78,78);
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.button {
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-left: 0px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
BIN
ui/fileTransferInstance/acceptFileButton.png
Normal file
After Width: | Height: | Size: 449 B |
BIN
ui/fileTransferInstance/emptyLGreenFileButton.png
Normal file
After Width: | Height: | Size: 236 B |
BIN
ui/fileTransferInstance/emptyLRedFileButton.png
Normal file
After Width: | Height: | Size: 229 B |
BIN
ui/fileTransferInstance/emptyRGreenFileButton.png
Normal file
After Width: | Height: | Size: 216 B |
BIN
ui/fileTransferInstance/emptyRRedFileButton.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
ui/fileTransferInstance/pauseFileButton.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
ui/fileTransferInstance/resumeFileButton.png
Normal file
After Width: | Height: | Size: 244 B |
BIN
ui/fileTransferInstance/stopFileButton.png
Normal file
After Width: | Height: | Size: 516 B |
105
widget/chatareawidget.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre 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 COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chatareawidget.h"
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QScrollBar>
|
||||||
|
|
||||||
|
ChatAreaWidget::ChatAreaWidget(QWidget *parent) :
|
||||||
|
QTextEdit(parent)
|
||||||
|
{
|
||||||
|
setReadOnly(true);
|
||||||
|
viewport()->setCursor(Qt::ArrowCursor);
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatAreaWidget::~ChatAreaWidget()
|
||||||
|
{
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::mouseReleaseEvent(QMouseEvent * event)
|
||||||
|
{
|
||||||
|
QTextEdit::mouseReleaseEvent(event);
|
||||||
|
int pos = this->document()->documentLayout()->hitTest(event->pos(), Qt::ExactHit);
|
||||||
|
if (pos > 0)
|
||||||
|
{
|
||||||
|
QTextCursor cursor(document());
|
||||||
|
cursor.setPosition(pos);
|
||||||
|
if( ! cursor.atEnd() )
|
||||||
|
{
|
||||||
|
cursor.setPosition(pos+1);
|
||||||
|
|
||||||
|
QTextFormat format = cursor.charFormat();
|
||||||
|
if (format.isImageFormat())
|
||||||
|
{
|
||||||
|
QString imageName = format.toImageFormat().name();
|
||||||
|
if (QRegExp("^data:ftrans.*").exactMatch(imageName))
|
||||||
|
{
|
||||||
|
QString data = imageName.right(imageName.length() - 12);
|
||||||
|
int endpos = data.indexOf("/png;base64");
|
||||||
|
data = data.left(endpos);
|
||||||
|
int middlepos = data.indexOf(".");
|
||||||
|
QString widgetID = data.left(middlepos);
|
||||||
|
QString widgetBtn = data.right(data.length() - middlepos - 1);
|
||||||
|
emit onFileTranfertInterract(widgetID, widgetBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAreaWidget::getHtmledMessages()
|
||||||
|
{
|
||||||
|
QString res("<table width=100%>\n");
|
||||||
|
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
{
|
||||||
|
res += it->getHtml();
|
||||||
|
}
|
||||||
|
res += "</table>";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::insertMessage(ChatAction *msgAction)
|
||||||
|
{
|
||||||
|
if (msgAction == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
messages.append(msgAction);
|
||||||
|
updateChatContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::updateChatContent()
|
||||||
|
{
|
||||||
|
QScrollBar* scroll = verticalScrollBar();
|
||||||
|
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
||||||
|
|
||||||
|
setHtml(getHtmledMessages());
|
||||||
|
if (lockSliderToBottom)
|
||||||
|
sliderPosition = scroll->maximum();
|
||||||
|
|
||||||
|
scroll->setValue(sliderPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatAreaWidget::clearMessages()
|
||||||
|
{
|
||||||
|
for (ChatAction *it : messages)
|
||||||
|
delete it;
|
||||||
|
updateChatContent();
|
||||||
|
}
|
49
widget/chatareawidget.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre 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 COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATAREAWIDGET_H
|
||||||
|
#define CHATAREAWIDGET_H
|
||||||
|
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QList>
|
||||||
|
#include "widget/tool/chataction.h"
|
||||||
|
|
||||||
|
class ChatAreaWidget : public QTextEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ChatAreaWidget(QWidget *parent = 0);
|
||||||
|
virtual ~ChatAreaWidget();
|
||||||
|
void insertMessage(ChatAction *msgAction);
|
||||||
|
void clearMessages();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void onFileTranfertInterract(QString widgetName, QString buttonName);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mouseReleaseEvent(QMouseEvent * event);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void updateChatContent();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString getHtmledMessages();
|
||||||
|
QList<ChatAction*> messages;
|
||||||
|
bool lockSliderToBottom;
|
||||||
|
int sliderPosition;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATAREAWIDGET_H
|
|
@ -1,320 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
|
||||||
|
|
||||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
|
||||||
|
|
||||||
This program is libre 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 COPYING file for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "filetransfertwidget.h"
|
|
||||||
#include "widget.h"
|
|
||||||
#include "core.h"
|
|
||||||
#include "math.h"
|
|
||||||
#include "style.h"
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
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();
|
|
||||||
progress = new QProgressBar();
|
|
||||||
mainLayout = new QHBoxLayout(), textLayout = new QHBoxLayout();
|
|
||||||
infoLayout = new QVBoxLayout(), buttonLayout = new QVBoxLayout();
|
|
||||||
buttonWidget = new QWidget();
|
|
||||||
QFont prettysmall;
|
|
||||||
prettysmall.setPixelSize(10);
|
|
||||||
this->setObjectName("default");
|
|
||||||
this->setStyleSheet(Style::get(":/ui/fileTransferWidget/fileTransferWidget.css"));
|
|
||||||
QPalette greybg;
|
|
||||||
greybg.setColor(QPalette::Window, QColor(209,209,209));
|
|
||||||
greybg.setColor(QPalette::Base, QColor(150,150,150));
|
|
||||||
setPalette(greybg);
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
|
|
||||||
setMinimumSize(250,58);
|
|
||||||
setMaximumHeight(58);
|
|
||||||
setLayout(mainLayout);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
|
|
||||||
pic->setMaximumHeight(40);
|
|
||||||
pic->setContentsMargins(5,0,0,0);
|
|
||||||
filename->setText(File.fileName);
|
|
||||||
filename->setFont(prettysmall);
|
|
||||||
size->setText(getHumanReadableSize(File.filesize));
|
|
||||||
size->setFont(prettysmall);
|
|
||||||
speed->setText("0B/s");
|
|
||||||
speed->setFont(prettysmall);
|
|
||||||
eta->setText("00:00");
|
|
||||||
eta->setFont(prettysmall);
|
|
||||||
progress->setValue(0);
|
|
||||||
progress->setMinimumHeight(11);
|
|
||||||
progress->setFont(prettysmall);
|
|
||||||
progress->setTextVisible(false);
|
|
||||||
QPalette whitebg;
|
|
||||||
whitebg.setColor(QPalette::Window, QColor(255,255,255));
|
|
||||||
buttonWidget->setPalette(whitebg);
|
|
||||||
buttonWidget->setAutoFillBackground(true);
|
|
||||||
buttonWidget->setLayout(buttonLayout);
|
|
||||||
|
|
||||||
stopFileButtonStylesheet = Style::get(":/ui/stopFileButton/style.css");
|
|
||||||
pauseFileButtonStylesheet = Style::get(":/ui/pauseFileButton/style.css");
|
|
||||||
acceptFileButtonStylesheet = Style::get(":/ui/acceptFileButton/style.css");
|
|
||||||
|
|
||||||
topright->setStyleSheet(stopFileButtonStylesheet);
|
|
||||||
if (File.direction == ToxFile::SENDING)
|
|
||||||
{
|
|
||||||
bottomright->setStyleSheet(pauseFileButtonStylesheet);
|
|
||||||
connect(topright, SIGNAL(clicked()), this, SLOT(cancelTransfer()));
|
|
||||||
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeSend()));
|
|
||||||
|
|
||||||
QPixmap preview;
|
|
||||||
File.file->seek(0);
|
|
||||||
if (preview.loadFromData(File.file->readAll()))
|
|
||||||
{
|
|
||||||
preview = preview.scaledToHeight(40);
|
|
||||||
pic->setPixmap(preview);
|
|
||||||
}
|
|
||||||
File.file->seek(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bottomright->setStyleSheet(acceptFileButtonStylesheet);
|
|
||||||
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
|
|
||||||
topright->setIconSize(QSize(10,10));
|
|
||||||
topright->setMinimumSize(25,28);
|
|
||||||
topright->setFlat(true);
|
|
||||||
topright->setAutoFillBackground(true);
|
|
||||||
topright->setPalette(toxgreen);
|
|
||||||
bottomright->setIconSize(QSize(10,10));
|
|
||||||
bottomright->setMinimumSize(25,28);
|
|
||||||
bottomright->setFlat(true);
|
|
||||||
bottomright->setAutoFillBackground(true);
|
|
||||||
bottomright->setPalette(toxgreen);
|
|
||||||
|
|
||||||
mainLayout->addStretch();
|
|
||||||
mainLayout->addWidget(pic);
|
|
||||||
mainLayout->addLayout(infoLayout,3);
|
|
||||||
mainLayout->addStretch();
|
|
||||||
mainLayout->addWidget(buttonWidget);
|
|
||||||
mainLayout->setMargin(0);
|
|
||||||
mainLayout->setSpacing(0);
|
|
||||||
|
|
||||||
infoLayout->addWidget(filename);
|
|
||||||
infoLayout->addLayout(textLayout);
|
|
||||||
infoLayout->addWidget(progress);
|
|
||||||
infoLayout->setMargin(4);
|
|
||||||
infoLayout->setSpacing(4);
|
|
||||||
|
|
||||||
textLayout->addWidget(size);
|
|
||||||
textLayout->addStretch(0);
|
|
||||||
textLayout->addWidget(speed);
|
|
||||||
textLayout->addStretch(0);
|
|
||||||
textLayout->addWidget(eta);
|
|
||||||
textLayout->setMargin(2);
|
|
||||||
textLayout->setSpacing(5);
|
|
||||||
|
|
||||||
buttonLayout->addWidget(topright);
|
|
||||||
buttonLayout->addSpacing(2);
|
|
||||||
buttonLayout->addWidget(bottomright);
|
|
||||||
buttonLayout->setContentsMargins(2,0,0,0);
|
|
||||||
buttonLayout->setSpacing(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString FileTransfertWidget::getHumanReadableSize(unsigned long long size)
|
|
||||||
{
|
|
||||||
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
|
|
||||||
int exp = 0;
|
|
||||||
if (size)
|
|
||||||
exp = std::min( (int) (log(size) / log(1024)), (int) (sizeof(suffix) / sizeof(suffix[0]) - 1));
|
|
||||||
return QString().setNum(size / pow(1024, exp),'f',2).append(suffix[exp]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferInfo(int FriendId, int FileNum, int64_t Filesize, int64_t BytesSent, ToxFile::FileDirection Direction)
|
|
||||||
{
|
|
||||||
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
|
||||||
return;
|
|
||||||
QDateTime newtime = QDateTime::currentDateTime();
|
|
||||||
int timediff = lastUpdate.secsTo(newtime);
|
|
||||||
if (timediff <= 0)
|
|
||||||
return;
|
|
||||||
qint64 diff = BytesSent - lastBytesSent;
|
|
||||||
if (diff < 0)
|
|
||||||
{
|
|
||||||
qWarning() << "FileTransfertWidget::onFileTransferInfo: Negative transfer speed !";
|
|
||||||
diff = 0;
|
|
||||||
}
|
|
||||||
long rawspeed = diff / timediff;
|
|
||||||
speed->setText(getHumanReadableSize(rawspeed)+"/s");
|
|
||||||
size->setText(getHumanReadableSize(Filesize));
|
|
||||||
if (!rawspeed)
|
|
||||||
return;
|
|
||||||
int etaSecs = (Filesize - BytesSent) / rawspeed;
|
|
||||||
QTime etaTime(0,0);
|
|
||||||
etaTime = etaTime.addSecs(etaSecs);
|
|
||||||
eta->setText(etaTime.toString("mm:ss"));
|
|
||||||
if (!Filesize)
|
|
||||||
{
|
|
||||||
progress->setValue(0);
|
|
||||||
qDebug() << QString("FT: received %1 bytes of an empty file, stop sending sequential devices, zetok!").arg(BytesSent);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
progress->setValue(BytesSent*100/Filesize);
|
|
||||||
qDebug() << QString("FT: received %1/%2 bytes, progress is %3%").arg(BytesSent).arg(Filesize).arg(BytesSent*100/Filesize);
|
|
||||||
}
|
|
||||||
lastUpdate = newtime;
|
|
||||||
lastBytesSent = BytesSent;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferCancelled(int FriendId, int FileNum, ToxFile::FileDirection Direction)
|
|
||||||
{
|
|
||||||
if (FileNum != fileNum || FriendId != friendId || Direction != direction)
|
|
||||||
return;
|
|
||||||
buttonLayout->setContentsMargins(0,0,0,0);
|
|
||||||
disconnect(topright);
|
|
||||||
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
|
||||||
progress->hide();
|
|
||||||
speed->hide();
|
|
||||||
eta->hide();
|
|
||||||
topright->hide();
|
|
||||||
bottomright->hide();
|
|
||||||
QPalette whiteText;
|
|
||||||
whiteText.setColor(QPalette::WindowText, Qt::white);
|
|
||||||
filename->setPalette(whiteText);
|
|
||||||
size->setPalette(whiteText);
|
|
||||||
this->setObjectName("error");
|
|
||||||
this->style()->polish(this);
|
|
||||||
|
|
||||||
//Toggle window visibility to fix draw order bug
|
|
||||||
this->hide();
|
|
||||||
this->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::onFileTransferFinished(ToxFile File)
|
|
||||||
{
|
|
||||||
if (File.fileNum != fileNum || File.friendId != friendId || File.direction != direction)
|
|
||||||
return;
|
|
||||||
topright->disconnect();
|
|
||||||
disconnect(Widget::getInstance()->getCore(),0,this,0);
|
|
||||||
progress->hide();
|
|
||||||
speed->hide();
|
|
||||||
eta->hide();
|
|
||||||
topright->hide();
|
|
||||||
bottomright->hide();
|
|
||||||
buttonLayout->setContentsMargins(0,0,0,0);
|
|
||||||
QPalette whiteText;
|
|
||||||
whiteText.setColor(QPalette::WindowText, Qt::white);
|
|
||||||
filename->setPalette(whiteText);
|
|
||||||
size->setPalette(whiteText);
|
|
||||||
this->setObjectName("success");
|
|
||||||
this->style()->polish(this);
|
|
||||||
|
|
||||||
//Toggle window visibility to fix draw order bug
|
|
||||||
this->hide();
|
|
||||||
this->show();
|
|
||||||
|
|
||||||
if (File.direction == ToxFile::RECEIVING)
|
|
||||||
{
|
|
||||||
QPixmap preview;
|
|
||||||
QFile previewFile(File.filePath);
|
|
||||||
if (previewFile.open(QIODevice::ReadOnly) && previewFile.size() <= 1024*1024*25) // Don't preview big (>25MiB) images
|
|
||||||
{
|
|
||||||
if (preview.loadFromData(previewFile.readAll()))
|
|
||||||
{
|
|
||||||
preview = preview.scaledToHeight(40);
|
|
||||||
pic->setPixmap(preview);
|
|
||||||
}
|
|
||||||
previewFile.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::cancelTransfer()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->cancelFileSend(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::rejectRecvRequest()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->rejectFileRecvRequest(friendId, fileNum);
|
|
||||||
onFileTransferCancelled(friendId, fileNum, direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// for whatever the fuck reason, QFileInfo::isWritable() always fails for files that don't exist
|
|
||||||
// which makes it useless for our case
|
|
||||||
// since QDir doesn't have an isWritable(), the only option I can think of is to make/delete the file
|
|
||||||
// surely this is a common problem that has a qt-implemented solution?
|
|
||||||
bool isWritable(QString& path)
|
|
||||||
{
|
|
||||||
QFile file(path);
|
|
||||||
bool exists = file.exists();
|
|
||||||
bool out = file.open(QIODevice::WriteOnly);
|
|
||||||
file.close();
|
|
||||||
if (!exists)
|
|
||||||
file.remove();
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::acceptRecvRequest()
|
|
||||||
{
|
|
||||||
QString path;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
path = QFileDialog::getSaveFileName(this, tr("Save a file","Title of the file saving dialog"), QDir::current().filePath(filename->text()));
|
|
||||||
if (path.isEmpty())
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//bool savable = QFileInfo(path).isWritable();
|
|
||||||
//qDebug() << path << " is writable: " << savable;
|
|
||||||
//qDebug() << "/home/bill/bliss.pdf writable: " << QFileInfo("/home/bill/bliss.pdf").isWritable();
|
|
||||||
if (isWritable(path))
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
QMessageBox::warning(this, tr("Location not writable","Title of permissions popup"), tr("You do not have permission to write that location. Choose another, or cancel the save dialog.", "text of permissions popup"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
savePath = path;
|
|
||||||
|
|
||||||
bottomright->setStyleSheet(pauseFileButtonStylesheet);
|
|
||||||
bottomright->disconnect();
|
|
||||||
connect(bottomright, SIGNAL(clicked()), this, SLOT(pauseResumeRecv()));
|
|
||||||
Widget::getInstance()->getCore()->acceptFileRecvRequest(friendId, fileNum, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::pauseResumeRecv()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->pauseResumeFileRecv(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::pauseResumeSend()
|
|
||||||
{
|
|
||||||
Widget::getInstance()->getCore()->pauseResumeFileSend(friendId, fileNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileTransfertWidget::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
|
@ -17,8 +17,8 @@
|
||||||
#include "chatform.h"
|
#include "chatform.h"
|
||||||
#include "friend.h"
|
#include "friend.h"
|
||||||
#include "widget/friendwidget.h"
|
#include "widget/friendwidget.h"
|
||||||
|
#include "filetransferinstance.h"
|
||||||
#include "widget/widget.h"
|
#include "widget/widget.h"
|
||||||
#include "widget/filetransfertwidget.h"
|
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
@ -43,6 +43,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
||||||
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
connect(videoButton, &QPushButton::clicked, this, &ChatForm::onVideoCallTriggered);
|
||||||
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
connect(msgEdit, &ChatTextEdit::enterPressed, this, &ChatForm::onSendTriggered);
|
||||||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||||
|
connect(chatWidget, SIGNAL(onFileTranfertInterract(QString,QString)), this, SLOT(onFileTansBtnClicked(QString,QString)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatForm::~ChatForm()
|
ChatForm::~ChatForm()
|
||||||
|
@ -94,36 +95,20 @@ void ChatForm::startFileSend(ToxFile file)
|
||||||
if (file.friendId != f->friendId)
|
if (file.friendId != f->friendId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QLabel *author = new QLabel(Widget::getInstance()->getUsername());
|
FileTransferInstance* fileTrans = new FileTransferInstance(file);
|
||||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
ftransWidgets.insert(fileTrans->getId(), fileTrans);
|
||||||
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);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished);
|
||||||
|
connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent()));
|
||||||
|
|
||||||
|
QString name = Widget::getInstance()->getUsername();
|
||||||
|
if (name == previousName)
|
||||||
|
name = "";
|
||||||
|
previousName = Widget::getInstance()->getUsername();
|
||||||
|
|
||||||
|
chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onFileRecvRequest(ToxFile file)
|
void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
|
@ -131,33 +116,13 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
if (file.friendId != f->friendId)
|
if (file.friendId != f->friendId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QLabel *author = new QLabel(f->getName());
|
FileTransferInstance* fileTrans = new FileTransferInstance(file);
|
||||||
QLabel *date = new QLabel(QTime::currentTime().toString("hh:mm"));
|
ftransWidgets.insert(fileTrans->getId(), fileTrans);
|
||||||
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::fileTransferInfo, fileTrans, &FileTransferInstance::onFileTransferInfo);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransfertWidget::onFileTransferCancelled);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferCancelled, fileTrans, &FileTransferInstance::onFileTransferCancelled);
|
||||||
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransfertWidget::onFileTransferFinished);
|
connect(Widget::getInstance()->getCore(), &Core::fileTransferFinished, fileTrans, &FileTransferInstance::onFileTransferFinished);
|
||||||
|
connect(fileTrans, SIGNAL(stateUpdated()), chatWidget, SLOT(updateChatContent()));
|
||||||
|
|
||||||
Widget* w = Widget::getInstance();
|
Widget* w = Widget::getInstance();
|
||||||
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
if (!w->isFriendWidgetCurActiveWidget(f)|| w->getIsWindowMinimized() || !w->isActiveWindow())
|
||||||
|
@ -166,6 +131,13 @@ void ChatForm::onFileRecvRequest(ToxFile file)
|
||||||
f->hasNewEvents=true;
|
f->hasNewEvents=true;
|
||||||
f->widget->updateStatusLight();
|
f->widget->updateStatusLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString name = f->getName();
|
||||||
|
if (name == previousName)
|
||||||
|
name = "";
|
||||||
|
previousName = f->getName();
|
||||||
|
|
||||||
|
chatWidget->insertMessage(new FileTransferAction(fileTrans, name, QTime::currentTime().toString("hh:mm"), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
|
||||||
|
@ -461,3 +433,14 @@ void ChatForm::onMicMuteToggle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatForm::onFileTansBtnClicked(QString widgetName, QString buttonName)
|
||||||
|
{
|
||||||
|
uint id = widgetName.toUInt();
|
||||||
|
|
||||||
|
auto it = ftransWidgets.find(id);
|
||||||
|
if (it != ftransWidgets.end())
|
||||||
|
it.value()->pressFromHtml(buttonName);
|
||||||
|
else
|
||||||
|
qDebug() << "no filetransferwidget: " << id;
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "widget/netcamview.h"
|
#include "widget/netcamview.h"
|
||||||
|
|
||||||
struct Friend;
|
struct Friend;
|
||||||
|
class FileTransferInstance;
|
||||||
|
|
||||||
class ChatForm : public GenericChatForm
|
class ChatForm : public GenericChatForm
|
||||||
{
|
{
|
||||||
|
@ -63,6 +64,7 @@ private slots:
|
||||||
void onAnswerCallTriggered();
|
void onAnswerCallTriggered();
|
||||||
void onHangupCallTriggered();
|
void onHangupCallTriggered();
|
||||||
void onCancelCallTriggered();
|
void onCancelCallTriggered();
|
||||||
|
void onFileTansBtnClicked(QString widgetName, QString buttonName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Friend* f;
|
Friend* f;
|
||||||
|
@ -70,6 +72,8 @@ private:
|
||||||
NetCamView* netcam;
|
NetCamView* netcam;
|
||||||
bool audioInputFlag;
|
bool audioInputFlag;
|
||||||
int callId;
|
int callId;
|
||||||
|
|
||||||
|
QHash<uint, FileTransferInstance*> ftransWidgets;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CHATFORM_H
|
#endif // CHATFORM_H
|
||||||
|
|
|
@ -16,21 +16,19 @@
|
||||||
|
|
||||||
#include "genericchatform.h"
|
#include "genericchatform.h"
|
||||||
#include "ui_mainwindow.h"
|
#include "ui_mainwindow.h"
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QTextStream>
|
|
||||||
#include "smileypack.h"
|
#include "smileypack.h"
|
||||||
#include "widget/emoticonswidget.h"
|
#include "widget/emoticonswidget.h"
|
||||||
#include "style.h"
|
#include "style.h"
|
||||||
#include "widget/widget.h"
|
#include "widget/widget.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
GenericChatForm::GenericChatForm(QObject *parent) :
|
GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
lockSliderToBottom = true;
|
|
||||||
curRow = 0;
|
curRow = 0;
|
||||||
|
|
||||||
mainWidget = new QWidget(); headWidget = new QWidget(); chatAreaWidget = new QWidget();
|
mainWidget = new QWidget(); headWidget = new QWidget();
|
||||||
|
|
||||||
nameLabel = new QLabel();
|
nameLabel = new QLabel();
|
||||||
avatarLabel = new QLabel();
|
avatarLabel = new QLabel();
|
||||||
|
@ -38,7 +36,10 @@ GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
headTextLayout = new QVBoxLayout();
|
headTextLayout = new QVBoxLayout();
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout();
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
||||||
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
QVBoxLayout *footButtonsSmall = new QVBoxLayout(), *volMicLayout = new QVBoxLayout();
|
||||||
mainChatLayout = new QGridLayout();
|
|
||||||
|
chatWidget = new ChatAreaWidget();
|
||||||
|
chatWidget->document()->setDefaultStyleSheet(Style::get(":ui/chatArea/innerStyle.css"));
|
||||||
|
chatWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
||||||
|
|
||||||
msgEdit = new ChatTextEdit();
|
msgEdit = new ChatTextEdit();
|
||||||
|
|
||||||
|
@ -51,23 +52,10 @@ GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
volButton = new QPushButton();
|
volButton = new QPushButton();
|
||||||
micButton = new QPushButton();
|
micButton = new QPushButton();
|
||||||
|
|
||||||
chatArea = new QScrollArea();
|
|
||||||
|
|
||||||
QFont bold;
|
QFont bold;
|
||||||
bold.setBold(true);
|
bold.setBold(true);
|
||||||
nameLabel->setFont(bold);
|
nameLabel->setFont(bold);
|
||||||
|
|
||||||
chatAreaWidget->setLayout(mainChatLayout);
|
|
||||||
chatAreaWidget->setStyleSheet(Style::get(":/ui/chatArea/chatArea.css"));
|
|
||||||
|
|
||||||
chatArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
chatArea->setWidgetResizable(true);
|
|
||||||
chatArea->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
chatArea->setFrameStyle(QFrame::NoFrame);
|
|
||||||
|
|
||||||
mainChatLayout->setColumnStretch(1,1);
|
|
||||||
mainChatLayout->setSpacing(5);
|
|
||||||
|
|
||||||
footButtonsSmall->setSpacing(2);
|
footButtonsSmall->setSpacing(2);
|
||||||
|
|
||||||
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
msgEdit->setStyleSheet(Style::get(":/ui/msgEdit/msgEdit.css"));
|
||||||
|
@ -111,7 +99,7 @@ GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
micButton->setStyleSheet(micButtonStylesheet);
|
micButton->setStyleSheet(micButtonStylesheet);
|
||||||
|
|
||||||
mainWidget->setLayout(mainLayout);
|
mainWidget->setLayout(mainLayout);
|
||||||
mainLayout->addWidget(chatArea);
|
mainLayout->addWidget(chatWidget);
|
||||||
mainLayout->addLayout(mainFootLayout);
|
mainLayout->addLayout(mainFootLayout);
|
||||||
mainLayout->setMargin(0);
|
mainLayout->setMargin(0);
|
||||||
|
|
||||||
|
@ -138,8 +126,6 @@ GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
headTextLayout->addStretch();
|
headTextLayout->addStretch();
|
||||||
headTextLayout->addWidget(nameLabel);
|
headTextLayout->addWidget(nameLabel);
|
||||||
|
|
||||||
chatArea->setWidget(chatAreaWidget);
|
|
||||||
|
|
||||||
//Fix for incorrect layouts on OS X as per
|
//Fix for incorrect layouts on OS X as per
|
||||||
//https://bugreports.qt-project.org/browse/QTBUG-14591
|
//https://bugreports.qt-project.org/browse/QTBUG-14591
|
||||||
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
sendButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
|
@ -147,8 +133,7 @@ GenericChatForm::GenericChatForm(QObject *parent) :
|
||||||
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
emoteButton->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||||
|
|
||||||
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
connect(emoteButton, SIGNAL(clicked()), this, SLOT(onEmoteButtonClicked()));
|
||||||
connect(chatArea, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
connect(chatWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
||||||
connect(chatArea->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(onSliderRangeChanged()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::setName(const QString &newName)
|
void GenericChatForm::setName(const QString &newName)
|
||||||
|
@ -174,16 +159,9 @@ void GenericChatForm::onChatContextMenuRequested(QPoint pos)
|
||||||
menu.exec(pos);
|
menu.exec(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::onSliderRangeChanged()
|
|
||||||
{
|
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
|
||||||
if (lockSliderToBottom)
|
|
||||||
scroll->setValue(scroll->maximum());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GenericChatForm::onSaveLogClicked()
|
void GenericChatForm::onSaveLogClicked()
|
||||||
{
|
{
|
||||||
QString path = QFileDialog::getSaveFileName(0,tr("Save chat log"));
|
QString path = QFileDialog::getSaveFileName(0, tr("Save chat log"));
|
||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -192,90 +170,21 @@ void GenericChatForm::onSaveLogClicked()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
QString log;
|
QString log;
|
||||||
QList<QLabel*> labels = chatAreaWidget->findChildren<QLabel*>();
|
log = chatWidget->toPlainText();
|
||||||
int i=0;
|
|
||||||
for (QLabel* label : labels)
|
|
||||||
{
|
|
||||||
log += label->text();
|
|
||||||
if (i==2)
|
|
||||||
{
|
|
||||||
i=0;
|
|
||||||
log += '\n';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log += '\t';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.write(log.toUtf8());
|
file.write(log.toUtf8());
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GenericChatForm::addMessage(QString author, QString message, QString date)
|
void GenericChatForm::addMessage(QString author, QString message, QDateTime datetime)
|
||||||
{
|
{
|
||||||
QLabel *authorLabel = new QLabel(author);
|
QString date = datetime.toString(Settings::getInstance().getTimestampFormat());
|
||||||
QLabel *messageLabel = new QLabel();
|
bool isMe = (author == Widget::getInstance()->getUsername());
|
||||||
QLabel *dateLabel = new QLabel(date);
|
|
||||||
|
|
||||||
QScrollBar* scroll = chatArea->verticalScrollBar();
|
if (previousName == author)
|
||||||
lockSliderToBottom = scroll && scroll->value() == scroll->maximum();
|
chatWidget->insertMessage(new MessageAction("", message, date, isMe));
|
||||||
authorLabel->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
else chatWidget->insertMessage(new MessageAction(author , message, date, isMe));
|
||||||
dateLabel->setAlignment(Qt::AlignTop);
|
previousName = author;
|
||||||
messageLabel->setWordWrap(true);
|
|
||||||
messageLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
authorLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
dateLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
|
||||||
if (author == Widget::getInstance()->getUsername())
|
|
||||||
{
|
|
||||||
QPalette pal;
|
|
||||||
pal.setColor(QPalette::WindowText, QColor(100,100,100));
|
|
||||||
authorLabel->setPalette(pal);
|
|
||||||
messageLabel->setPalette(pal);
|
|
||||||
}
|
|
||||||
if (previousName.isEmpty() || previousName != author)
|
|
||||||
{
|
|
||||||
if (curRow)
|
|
||||||
{
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
mainChatLayout->addItem(new QSpacerItem(0,AUTHOR_CHANGE_SPACING),curRow,0,1,3);
|
|
||||||
}
|
|
||||||
previousName = author;
|
|
||||||
curRow++;
|
|
||||||
}
|
|
||||||
else if (curRow)// onSaveLogClicked expects 0 or 3 QLabel per line
|
|
||||||
authorLabel->setText("");
|
|
||||||
|
|
||||||
QColor greentext(61,204,61);
|
|
||||||
QString fontTemplate = "<font color='%1'>%2</font>";
|
|
||||||
|
|
||||||
QString finalMessage;
|
|
||||||
QStringList messageLines = message.split("\n");
|
|
||||||
for (QString& s : messageLines)
|
|
||||||
{
|
|
||||||
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
|
||||||
finalMessage += fontTemplate.arg(greentext.name(), toHtmlChars(s));
|
|
||||||
else
|
|
||||||
finalMessage += toHtmlChars(s);
|
|
||||||
finalMessage += "<br>";
|
|
||||||
}
|
|
||||||
messageLabel->setText(finalMessage.left(finalMessage.length()-4));
|
|
||||||
messageLabel->setText(SmileyPack::getInstance().smileyfied(messageLabel->text()));
|
|
||||||
messageLabel->setTextFormat(Qt::RichText);
|
|
||||||
|
|
||||||
mainChatLayout->addWidget(authorLabel, curRow, 0);
|
|
||||||
mainChatLayout->addWidget(messageLabel, curRow, 1);
|
|
||||||
mainChatLayout->addWidget(dateLabel, curRow, 3);
|
|
||||||
mainChatLayout->setRowStretch(curRow+1, 1);
|
|
||||||
mainChatLayout->setRowStretch(curRow, 0);
|
|
||||||
curRow++;
|
|
||||||
authorLabel->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
messageLabel->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
dateLabel->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
connect(authorLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(messageLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
connect(dateLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onChatContextMenuRequested(QPoint)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericChatForm::~GenericChatForm()
|
GenericChatForm::~GenericChatForm()
|
||||||
|
@ -310,14 +219,3 @@ void GenericChatForm::onEmoteInsertRequested(QString str)
|
||||||
|
|
||||||
msgEdit->setFocus(); // refocus so that we can continue typing
|
msgEdit->setFocus(); // refocus so that we can continue typing
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GenericChatForm::toHtmlChars(const QString &str)
|
|
||||||
{
|
|
||||||
static QList<QPair<QString, QString>> replaceList = {{"&","&"}, {" "," "}, {">",">"}, {"<","<"}};
|
|
||||||
QString res = str;
|
|
||||||
|
|
||||||
for (auto &it : replaceList)
|
|
||||||
res = res.replace(it.first,it.second);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,16 +18,14 @@
|
||||||
#define GENERICCHATFORM_H
|
#define GENERICCHATFORM_H
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QLabel>
|
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QGridLayout>
|
|
||||||
|
|
||||||
|
#include "widget/chatareawidget.h"
|
||||||
#include "widget/tool/chattextedit.h"
|
#include "widget/tool/chattextedit.h"
|
||||||
|
|
||||||
// Spacing in px inserted when the author of the last message changes
|
// Spacing in px inserted when the author of the last message changes
|
||||||
|
@ -46,7 +44,7 @@ public:
|
||||||
|
|
||||||
virtual void setName(const QString &newName);
|
virtual void setName(const QString &newName);
|
||||||
virtual void show(Ui::MainWindow &ui);
|
virtual void show(Ui::MainWindow &ui);
|
||||||
void addMessage(QString author, QString message, QString date=QTime::currentTime().toString("hh:mm"));
|
void addMessage(QString author, QString message, QDateTime datetime=QDateTime::currentDateTime());
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sendMessage(int, QString);
|
void sendMessage(int, QString);
|
||||||
|
@ -55,26 +53,20 @@ public slots:
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onChatContextMenuRequested(QPoint pos);
|
void onChatContextMenuRequested(QPoint pos);
|
||||||
void onSliderRangeChanged();
|
|
||||||
void onSaveLogClicked();
|
void onSaveLogClicked();
|
||||||
void onEmoteButtonClicked();
|
void onEmoteButtonClicked();
|
||||||
void onEmoteInsertRequested(QString str);
|
void onEmoteInsertRequested(QString str);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QLabel *nameLabel, *avatarLabel;
|
QLabel *nameLabel, *avatarLabel;
|
||||||
QWidget *mainWidget, *headWidget, *chatAreaWidget;
|
QWidget *mainWidget, *headWidget;
|
||||||
QScrollArea *chatArea;
|
|
||||||
QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
|
QPushButton *fileButton, *emoteButton, *callButton, *videoButton, *volButton, *micButton;
|
||||||
QGridLayout *mainChatLayout;
|
|
||||||
QVBoxLayout *headTextLayout;
|
QVBoxLayout *headTextLayout;
|
||||||
ChatTextEdit *msgEdit;
|
ChatTextEdit *msgEdit;
|
||||||
QPushButton *sendButton;
|
QPushButton *sendButton;
|
||||||
QString previousName;
|
QString previousName;
|
||||||
|
ChatAreaWidget *chatWidget;
|
||||||
int curRow;
|
int curRow;
|
||||||
bool lockSliderToBottom;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString toHtmlChars(const QString &str);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // GENERICCHATFORM_H
|
#endif // GENERICCHATFORM_H
|
||||||
|
|
|
@ -48,9 +48,6 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
|
||||||
|
|
||||||
msgEdit->setObjectName("group");
|
msgEdit->setObjectName("group");
|
||||||
|
|
||||||
mainChatLayout->setColumnStretch(1,1);
|
|
||||||
mainChatLayout->setHorizontalSpacing(10);
|
|
||||||
|
|
||||||
headTextLayout->addWidget(nusersLabel);
|
headTextLayout->addWidget(nusersLabel);
|
||||||
headTextLayout->addWidget(namesList);
|
headTextLayout->addWidget(namesList);
|
||||||
headTextLayout->setMargin(0);
|
headTextLayout->setMargin(0);
|
||||||
|
|
120
widget/tool/chataction.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre 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 COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chataction.h"
|
||||||
|
#include "smileypack.h"
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
QString ChatAction::toHtmlChars(const QString &str)
|
||||||
|
{
|
||||||
|
static QList<QPair<QString, QString>> replaceList = {{"&","&"}, {" "," "}, {">",">"}, {"<","<"}};
|
||||||
|
QString res = str;
|
||||||
|
|
||||||
|
for (auto &it : replaceList)
|
||||||
|
res = res.replace(it.first,it.second);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::QImage2base64(const QImage &img)
|
||||||
|
{
|
||||||
|
QByteArray ba;
|
||||||
|
QBuffer buffer(&ba);
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
img.save(&buffer, "PNG"); // writes image into ba in PNG format
|
||||||
|
return ba.toBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapName(const QString &name)
|
||||||
|
{
|
||||||
|
if (isMe)
|
||||||
|
return QString("<td><div class=name_me>" + name + "</div></td>\n");
|
||||||
|
else
|
||||||
|
return QString("<td><div class=name>" + name + "</div></td>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapDate(const QString &date)
|
||||||
|
{
|
||||||
|
QString res = "<td align=right><div class=date>" + date + "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapMessage(const QString &message)
|
||||||
|
{
|
||||||
|
QString res = "<td width=100%><div class=message>" + message + "</div></td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ChatAction::wrapWholeLine(const QString &line)
|
||||||
|
{
|
||||||
|
QString res = "<tr>\n" + line + "</tr>\n";
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageAction::MessageAction(const QString &author, const QString &message, const QString &date, const bool &me) :
|
||||||
|
ChatAction(me)
|
||||||
|
{
|
||||||
|
QString message_ = SmileyPack::getInstance().smileyfied(toHtmlChars(message));
|
||||||
|
|
||||||
|
QStringList messageLines = message_.split("\n");
|
||||||
|
message_ = "";
|
||||||
|
for (QString& s : messageLines)
|
||||||
|
{
|
||||||
|
if (QRegExp("^[ ]*>.*").exactMatch(s))
|
||||||
|
message_ += "<div class=quote>" + s.right(s.length()-4) + "</div><br>";
|
||||||
|
else
|
||||||
|
message_ += s + "<br>";
|
||||||
|
}
|
||||||
|
message_ = message_.left(message_.length()-4);
|
||||||
|
|
||||||
|
content = wrapWholeLine(wrapName(author) + wrapMessage(message_) + wrapDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageAction::getHtml()
|
||||||
|
{
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferAction::FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me) :
|
||||||
|
ChatAction(me),
|
||||||
|
sender(author),
|
||||||
|
timestamp(date)
|
||||||
|
{
|
||||||
|
w = widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTransferAction::~FileTransferAction()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferAction::getHtml()
|
||||||
|
{
|
||||||
|
QString widgetHtml;
|
||||||
|
if (w != nullptr)
|
||||||
|
widgetHtml = w->getHtmlImage();
|
||||||
|
else
|
||||||
|
widgetHtml = "<div class=quote>EMPTY CONTENT</div>";
|
||||||
|
QString res = wrapWholeLine(wrapName(sender) + wrapMessage(widgetHtml) + wrapDate(timestamp));;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileTransferAction::wrapMessage(const QString &message)
|
||||||
|
{
|
||||||
|
QString res = "<td width=100%>" + message + "</td>\n";
|
||||||
|
return res;
|
||||||
|
}
|
67
widget/tool/chataction.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||||
|
|
||||||
|
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||||
|
|
||||||
|
This program is libre 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 COPYING file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CHATACTION_H
|
||||||
|
#define CHATACTION_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "filetransferinstance.h"
|
||||||
|
|
||||||
|
class ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ChatAction(const bool &me) : isMe(me) {;}
|
||||||
|
virtual ~ChatAction(){;}
|
||||||
|
virtual QString getHtml() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString toHtmlChars(const QString &str);
|
||||||
|
QString QImage2base64(const QImage &img);
|
||||||
|
|
||||||
|
virtual QString wrapName(const QString &name);
|
||||||
|
virtual QString wrapDate(const QString &date);
|
||||||
|
virtual QString wrapMessage(const QString &message);
|
||||||
|
virtual QString wrapWholeLine(const QString &line);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool isMe;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageAction : public ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageAction(const QString &author, const QString &message, const QString &date, const bool &me);
|
||||||
|
virtual ~MessageAction(){;}
|
||||||
|
virtual QString getHtml();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString content;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileTransferAction : public ChatAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileTransferAction(FileTransferInstance *widget, const QString &author, const QString &date, const bool &me);
|
||||||
|
virtual ~FileTransferAction();
|
||||||
|
virtual QString getHtml();
|
||||||
|
virtual QString wrapMessage(const QString &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileTransferInstance *w;
|
||||||
|
QString sender, timestamp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATACTION_H
|