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

Merge of the new API compatible qTox branch

Merge branch 'back_in_the_game'
This commit is contained in:
tux3 2015-04-26 17:47:53 +02:00
commit 2fed6724b2
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
73 changed files with 3963 additions and 3430 deletions

View File

@ -409,10 +409,6 @@ contains(ENABLE_SYSTRAY_GTK_BACKEND, NO) {
SOURCES += \ SOURCES += \
src/audio.cpp \ src/audio.cpp \
src/core.cpp \
src/coreav.cpp \
src/coreencryption.cpp \
src/corestructs.cpp \
src/historykeeper.cpp \ src/historykeeper.cpp \
src/main.cpp \ src/main.cpp \
src/nexus.cpp \ src/nexus.cpp \
@ -428,14 +424,22 @@ SOURCES += \
src/video/videoframe.cpp \ src/video/videoframe.cpp \
src/widget/gui.cpp \ src/widget/gui.cpp \
src/toxme.cpp \ src/toxme.cpp \
src/misc/qrwidget.cpp src/misc/qrwidget.cpp \
src/core/core.cpp \
src/core/coreav.cpp \
src/core/coreencryption.cpp \
src/core/corefile.cpp \
src/core/corestructs.cpp \
src/profilelocker.cpp \
src/avatarbroadcaster.cpp
HEADERS += \ HEADERS += \
src/audio.h \ src/audio.h \
src/core.h \ src/core/core.h \
src/corestructs.h \ src/core/coreav.h \
src/coredefines.h \ src/core/coredefines.h \
src/coreav.h \ src/core/corefile.h \
src/core/corestructs.h \
src/historykeeper.h \ src/historykeeper.h \
src/nexus.h \ src/nexus.h \
src/misc/cdata.h \ src/misc/cdata.h \
@ -448,6 +452,9 @@ HEADERS += \
src/video/cameraworker.h \ src/video/cameraworker.h \
src/video/videoframe.h \ src/video/videoframe.h \
src/video/videosource.h \ src/video/videosource.h \
src/video/netvideosource.h \
src/widget/gui.h \ src/widget/gui.h \
src/toxme.h \ src/toxme.h \
src/misc/qrwidget.h src/misc/qrwidget.h \
src/profilelocker.h \
src/avatarbroadcaster.h

View File

@ -2,7 +2,9 @@
if which apt-get; then if which apt-get; then
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \ sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev libqt5svg5* libqrencode-dev libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev \
qttools5-dev-tools qtchooser libxss-dev libqt5svg5* libqrencode-dev \
libqt5opengl5-dev
elif which pacman; then elif which pacman; then
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg qrencode sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg qrencode
elif which yum; then elif which yum; then

View File

@ -24,7 +24,7 @@
#define FIX_SND_PCM_PREPARE_BUG 0 #define FIX_SND_PCM_PREPARE_BUG 0
#include "audio.h" #include "audio.h"
#include "src/core.h" #include "src/core/core.h"
#include <QDebug> #include <QDebug>
#include <QThread> #include <QThread>
@ -72,6 +72,7 @@ Audio::~Audio()
audioThread->wait(); audioThread->wait();
if (audioThread->isRunning()) if (audioThread->isRunning())
audioThread->terminate(); audioThread->terminate();
delete audioThread; delete audioThread;
delete audioInLock; delete audioInLock;
delete audioOutLock; delete audioOutLock;
@ -123,6 +124,7 @@ void Audio::openInput(const QString& inDevDescr)
alInDev = nullptr; alInDev = nullptr;
if (tmp) if (tmp)
alcCaptureCloseDevice(tmp); alcCaptureCloseDevice(tmp);
int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; int stereoFlag = av_DefaultSettings.audio_channels==1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
if (inDevDescr.isEmpty()) if (inDevDescr.isEmpty())
alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag, alInDev = alcCaptureOpenDevice(nullptr,av_DefaultSettings.audio_sample_rate, stereoFlag,
@ -163,6 +165,7 @@ void Audio::openOutput(const QString& outDevDescr)
alOutDev = alcOpenDevice(nullptr); alOutDev = alcOpenDevice(nullptr);
else else
alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str()); alOutDev = alcOpenDevice(outDevDescr.toStdString().c_str());
if (!alOutDev) if (!alOutDev)
{ {
qWarning() << "Audio: Cannot open output audio device"; qWarning() << "Audio: Cannot open output audio device";
@ -171,8 +174,10 @@ void Audio::openOutput(const QString& outDevDescr)
{ {
if (alContext && alcMakeContextCurrent(nullptr) == ALC_TRUE) if (alContext && alcMakeContextCurrent(nullptr) == ALC_TRUE)
alcDestroyContext(alContext); alcDestroyContext(alContext);
if (tmp) if (tmp)
alcCloseDevice(tmp); alcCloseDevice(tmp);
alContext=alcCreateContext(alOutDev,nullptr); alContext=alcCreateContext(alOutDev,nullptr);
if (!alcMakeContextCurrent(alContext)) if (!alcMakeContextCurrent(alContext))
{ {
@ -180,7 +185,9 @@ void Audio::openOutput(const QString& outDevDescr)
alcCloseDevice(alOutDev); alcCloseDevice(alOutDev);
} }
else else
{
alGenSources(1, &alMainSource); alGenSources(1, &alMainSource);
}
qDebug() << "Audio: Opening audio output "<<outDevDescr; qDebug() << "Audio: Opening audio output "<<outDevDescr;
@ -217,13 +224,9 @@ void Audio::closeOutput()
if (alOutDev) if (alOutDev)
{ {
if (alcCloseDevice(alOutDev) == ALC_TRUE) if (alcCloseDevice(alOutDev) == ALC_TRUE)
{
alOutDev = nullptr; alOutDev = nullptr;
}
else else
{
qWarning() << "Audio: Failed to close output"; qWarning() << "Audio: Failed to close output";
}
} }
} }

41
src/avatarbroadcaster.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "avatarbroadcaster.h"
#include "src/core/core.h"
#include <QObject>
#include <QDebug>
QByteArray AvatarBroadcaster::avatarData;
QMap<uint32_t, bool> AvatarBroadcaster::friendsSentTo;
static QMetaObject::Connection autoBroadcastConn;
static auto autoBroadcast = [](uint32_t friendId, Status)
{
AvatarBroadcaster::sendAvatarTo(friendId);
};
void AvatarBroadcaster::setAvatar(QByteArray data)
{
avatarData = data;
friendsSentTo.clear();
QVector<uint32_t> friends = Core::getInstance()->getFriendList();
for (uint32_t friendId : friends)
sendAvatarTo(friendId);
}
void AvatarBroadcaster::sendAvatarTo(uint32_t friendId)
{
if (friendsSentTo.contains(friendId) && friendsSentTo[friendId])
return;
if (!Core::getInstance()->isFriendOnline(friendId))
return;
qDebug() << "AvatarBroadcaster: Sending avatar to "<<friendId;
Core::getInstance()->sendAvatarFile(friendId, avatarData);
friendsSentTo[friendId] = true;
}
void AvatarBroadcaster::enableAutoBroadcast(bool state)
{
QObject::disconnect(autoBroadcastConn);
if (state)
autoBroadcastConn = QObject::connect(Core::getInstance(), &Core::friendStatusChanged, autoBroadcast);
}

28
src/avatarbroadcaster.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef AVATARBROADCASTER_H
#define AVATARBROADCASTER_H
#include <QByteArray>
#include <QMap>
/// Takes care of broadcasting avatar changes to our friends in a smart way
/// Cache a copy of our current avatar and friends who have received it
/// so we don't spam avatar transfers to a friend who already has it.
class AvatarBroadcaster
{
private:
AvatarBroadcaster()=delete;
public:
/// Set our current avatar
static void setAvatar(QByteArray data);
/// Send our current avatar to this friend, if not already sent
static void sendAvatarTo(uint32_t friendId);
/// If true, we automatically broadcast our avatar to friends when they come online
static void enableAutoBroadcast(bool state = true);
private:
static QByteArray avatarData;
static QMap<uint32_t, bool> friendsSentTo;
};
#endif // AVATARBROADCASTER_H

View File

@ -244,17 +244,17 @@ void ChatLine::moveBy(qreal deltaY)
bbox.moveTop(bbox.top() + deltaY); bbox.moveTop(bbox.top() + deltaY);
} }
bool ChatLine::lessThanBSRectTop(const ChatLine::Ptr lhs, const qreal rhs) bool ChatLine::lessThanBSRectTop(const ChatLine::Ptr& lhs, const qreal& rhs)
{ {
return lhs->sceneBoundingRect().top() < rhs; return lhs->sceneBoundingRect().top() < rhs;
} }
bool ChatLine::lessThanBSRectBottom(const ChatLine::Ptr lhs, const qreal rhs) bool ChatLine::lessThanBSRectBottom(const ChatLine::Ptr& lhs, const qreal& rhs)
{ {
return lhs->sceneBoundingRect().bottom() < rhs; return lhs->sceneBoundingRect().bottom() < rhs;
} }
bool ChatLine::lessThanRowIndex(const ChatLine::Ptr lhs, const ChatLine::Ptr rhs) bool ChatLine::lessThanRowIndex(const ChatLine::Ptr& lhs, const ChatLine::Ptr& rhs)
{ {
return lhs->getRow() < rhs->getRow(); return lhs->getRow() < rhs->getRow();
} }

View File

@ -82,9 +82,9 @@ public:
bool isOverSelection(QPointF scenePos); bool isOverSelection(QPointF scenePos);
//comparators //comparators
static bool lessThanBSRectTop(const ChatLine::Ptr lhs, const qreal rhs); static bool lessThanBSRectTop(const ChatLine::Ptr& lhs, const qreal& rhs);
static bool lessThanBSRectBottom(const ChatLine::Ptr lhs, const qreal rhs); static bool lessThanBSRectBottom(const ChatLine::Ptr& lhs, const qreal& rhs);
static bool lessThanRowIndex(const ChatLine::Ptr lhs, const ChatLine::Ptr rhs); static bool lessThanRowIndex(const ChatLine::Ptr& lhs, const ChatLine::Ptr& rhs);
protected: protected:
friend class ChatLog; friend class ChatLog;

View File

@ -177,33 +177,17 @@ void ChatLog::mousePressEvent(QMouseEvent* ev)
{ {
QGraphicsView::mousePressEvent(ev); QGraphicsView::mousePressEvent(ev);
QPointF scenePos = mapToScene(ev->pos());
if(ev->button() == Qt::LeftButton) if(ev->button() == Qt::LeftButton)
{ {
clickPos = ev->pos(); clickPos = ev->pos();
clearSelection(); clearSelection();
} }
if(ev->button() == Qt::RightButton)
{
if(!isOverSelection(scenePos))
clearSelection();
}
} }
void ChatLog::mouseReleaseEvent(QMouseEvent* ev) void ChatLog::mouseReleaseEvent(QMouseEvent* ev)
{ {
QGraphicsView::mouseReleaseEvent(ev); QGraphicsView::mouseReleaseEvent(ev);
QPointF scenePos = mapToScene(ev->pos());
if(ev->button() == Qt::RightButton)
{
if(!isOverSelection(scenePos))
clearSelection();
}
selectionScrollDir = NoDirection; selectionScrollDir = NoDirection;
} }

View File

@ -67,7 +67,7 @@ ChatMessage::Ptr ChatMessage::createChatMessage(const QString &sender, const QSt
// Note: Eliding cannot be enabled for RichText items. (QTBUG-17207) // Note: Eliding cannot be enabled for RichText items. (QTBUG-17207)
msg->addColumn(new Text(senderText, isMe ? Style::getFont(Style::BigBold) : Style::getFont(Style::Big), true, sender, type == ACTION ? actionColor : Qt::black), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Text(senderText, isMe ? Style::getFont(Style::BigBold) : Style::getFont(Style::Big), true, sender, type == ACTION ? actionColor : Qt::black), ColumnFormat(NAME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
msg->addColumn(new Text(text, Style::getFont(Style::Big), false, type == ACTION ? QString("*%1 %2*").arg(sender, rawMessage) : rawMessage), ColumnFormat(1.0, ColumnFormat::VariableSize)); msg->addColumn(new Text(text, Style::getFont(Style::Big), false, type == (ACTION && isMe) ? QString("%1 %2").arg(sender, rawMessage) : rawMessage), ColumnFormat(1.0, ColumnFormat::VariableSize));
msg->addColumn(new Spinner(":/ui/chatArea/spinner.svg", QSize(16, 16), 360.0/1.6), ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right)); msg->addColumn(new Spinner(":/ui/chatArea/spinner.svg", QSize(16, 16), 360.0/1.6), ColumnFormat(TIME_COL_WIDTH, ColumnFormat::FixedSize, ColumnFormat::Right));
if(!date.isNull()) if(!date.isNull())

View File

@ -18,7 +18,7 @@
#define CHATMESSAGE_H #define CHATMESSAGE_H
#include "chatline.h" #include "chatline.h"
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include <QDateTime> #include <QDateTime>
class QGraphicsScene; class QGraphicsScene;

View File

@ -18,7 +18,7 @@
#include "ui_filetransferwidget.h" #include "ui_filetransferwidget.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include "src/widget/widget.h" #include "src/widget/widget.h"
@ -77,6 +77,8 @@ FileTransferWidget::FileTransferWidget(QWidget *parent, ToxFile file)
connect(Core::getInstance(), &Core::fileTransferCancelled, this, &FileTransferWidget::onFileTransferCancelled); connect(Core::getInstance(), &Core::fileTransferCancelled, this, &FileTransferWidget::onFileTransferCancelled);
connect(Core::getInstance(), &Core::fileTransferPaused, this, &FileTransferWidget::onFileTransferPaused); connect(Core::getInstance(), &Core::fileTransferPaused, this, &FileTransferWidget::onFileTransferPaused);
connect(Core::getInstance(), &Core::fileTransferFinished, this, &FileTransferWidget::onFileTransferFinished); connect(Core::getInstance(), &Core::fileTransferFinished, this, &FileTransferWidget::onFileTransferFinished);
connect(Core::getInstance(), &Core::fileTransferRemotePausedUnpaused, this, &FileTransferWidget::fileTransferRemotePausedUnpaused);
connect(Core::getInstance(), &Core::fileTransferBrokenUnbroken, this, &FileTransferWidget::fileTransferBrokenUnbroken);
setupButtons(); setupButtons();
@ -87,7 +89,9 @@ FileTransferWidget::FileTransferWidget(QWidget *parent, ToxFile file)
ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget")); ui->progressLabel->setText(tr("Waiting to send...", "file transfer widget"));
} }
else else
{
ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget")); ui->progressLabel->setText(tr("Accept to receive this file", "file transfer widget"));
}
setFixedHeight(78); setFixedHeight(78);
} }
@ -114,7 +118,7 @@ void FileTransferWidget::autoAcceptTransfer(const QString &path)
//Do not automatically accept the file-transfer if the path is not writable. //Do not automatically accept the file-transfer if the path is not writable.
//The user can still accept it manually. //The user can still accept it manually.
if (Nexus::isFilePathWritable(filepath)) if (Nexus::tryRemoveFile(filepath))
Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath); Core::getInstance()->acceptFileRecvRequest(fileInfo.friendId, fileInfo.fileNum, filepath);
else else
qDebug() << "Warning: Cannot write to " << filepath; qDebug() << "Warning: Cannot write to " << filepath;
@ -126,7 +130,7 @@ void FileTransferWidget::acceptTransfer(const QString &filepath)
return; return;
//test if writable //test if writable
if(!Nexus::isFilePathWritable(filepath)) if(!Nexus::tryRemoveFile(filepath))
{ {
QMessageBox::warning(0, QMessageBox::warning(0,
tr("Location not writable","Title of permissions popup"), tr("Location not writable","Title of permissions popup"),
@ -186,6 +190,7 @@ void FileTransferWidget::paintEvent(QPaintEvent *)
// draw background // draw background
if(drawButtonAreaNeeded()) if(drawButtonAreaNeeded())
painter.setClipRect(QRect(0,0,width()-buttonFieldWidth,height())); painter.setClipRect(QRect(0,0,width()-buttonFieldWidth,height()));
painter.setBrush(QBrush(backgroundColor)); painter.setBrush(QBrush(backgroundColor));
painter.drawRoundRect(geometry(), r * ratio, r); painter.drawRoundRect(geometry(), r * ratio, r);
@ -222,7 +227,10 @@ void FileTransferWidget::onFileTransferInfo(ToxFile file)
// ETA, speed // ETA, speed
qreal deltaSecs = dt / 1000.0; qreal deltaSecs = dt / 1000.0;
qint64 deltaBytes = qMax(file.bytesSent - lastBytesSent, qint64(0)); // (can't use ::abs or ::max on unsigned types substraction, they'd just overflow)
quint64 deltaBytes = file.bytesSent > lastBytesSent
? file.bytesSent - lastBytesSent
: lastBytesSent - file.bytesSent;
qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs); qreal bytesPerSec = static_cast<int>(static_cast<qreal>(deltaBytes) / deltaSecs);
// calculate mean // calculate mean
@ -306,6 +314,26 @@ void FileTransferWidget::onFileTransferPaused(ToxFile file)
setupButtons(); setupButtons();
} }
void FileTransferWidget::onFileTransferResumed(ToxFile file)
{
if(fileInfo != file)
return;
fileInfo = file;
ui->etaLabel->setText("");
ui->progressLabel->setText(tr("Resuming...", "file transfer widget"));
// reset mean
meanIndex = 0;
for(size_t i=0; i<TRANSFER_ROLLING_AVG_COUNT; ++i)
meanData[i] = 0.0;
setBackgroundColor(Style::getColor(Style::LightGrey), false);
setupButtons();
}
void FileTransferWidget::onFileTransferFinished(ToxFile file) void FileTransferWidget::onFileTransferFinished(ToxFile file)
{ {
if(fileInfo != file) if(fileInfo != file)
@ -320,10 +348,12 @@ void FileTransferWidget::onFileTransferFinished(ToxFile file)
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg"));
ui->topButton->setObjectName("ok"); ui->topButton->setObjectName("ok");
ui->topButton->setToolTip(tr("Open file."));
ui->topButton->show(); ui->topButton->show();
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/dir.svg")); ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/dir.svg"));
ui->bottomButton->setObjectName("dir"); ui->bottomButton->setObjectName("dir");
ui->bottomButton->setToolTip(tr("Open file directory."));
ui->bottomButton->show(); ui->bottomButton->show();
// preview // preview
@ -333,6 +363,21 @@ void FileTransferWidget::onFileTransferFinished(ToxFile file)
disconnect(Core::getInstance(), 0, this, 0); disconnect(Core::getInstance(), 0, this, 0);
} }
void FileTransferWidget::fileTransferRemotePausedUnpaused(ToxFile file, bool paused)
{
if (paused)
onFileTransferPaused(file);
else
onFileTransferResumed(file);
}
void FileTransferWidget::fileTransferBrokenUnbroken(ToxFile file, bool broken)
{
/// TODO: Handle broken transfer differently once we have resuming code
if (broken)
onFileTransferCancelled(file);
}
QString FileTransferWidget::getHumanReadableSize(qint64 size) QString FileTransferWidget::getHumanReadableSize(qint64 size)
{ {
static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"}; static const char* suffix[] = {"B","kiB","MiB","GiB","TiB"};
@ -360,9 +405,11 @@ void FileTransferWidget::setupButtons()
case ToxFile::TRANSMITTING: case ToxFile::TRANSMITTING:
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel"); ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg")); ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->bottomButton->setObjectName("pause"); ui->bottomButton->setObjectName("pause");
ui->bottomButton->setToolTip(tr("Pause transfer"));
setButtonColor(Style::getColor(Style::Green)); setButtonColor(Style::getColor(Style::Green));
@ -370,9 +417,11 @@ void FileTransferWidget::setupButtons()
case ToxFile::PAUSED: case ToxFile::PAUSED:
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel"); ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/arrow_white.svg")); ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/arrow_white.svg"));
ui->bottomButton->setObjectName("resume"); ui->bottomButton->setObjectName("resume");
ui->bottomButton->setToolTip(tr("Resume transfer"));
setButtonColor(Style::getColor(Style::LightGrey)); setButtonColor(Style::getColor(Style::LightGrey));
@ -381,16 +430,19 @@ void FileTransferWidget::setupButtons()
case ToxFile::BROKEN: //TODO: ? case ToxFile::BROKEN: //TODO: ?
ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg")); ui->topButton->setIcon(QIcon(":/ui/fileTransferInstance/no.svg"));
ui->topButton->setObjectName("cancel"); ui->topButton->setObjectName("cancel");
ui->topButton->setToolTip(tr("Cancel transfer"));
if(fileInfo.direction == ToxFile::SENDING) if(fileInfo.direction == ToxFile::SENDING)
{ {
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg")); ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/pause.svg"));
ui->bottomButton->setObjectName("pause"); ui->bottomButton->setObjectName("pause");
ui->bottomButton->setToolTip(tr("Pause transfer"));
} }
else else
{ {
ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg")); ui->bottomButton->setIcon(QIcon(":/ui/fileTransferInstance/yes.svg"));
ui->bottomButton->setObjectName("accept"); ui->bottomButton->setObjectName("accept");
ui->bottomButton->setToolTip(tr("Accept transfer"));
} }
break; break;
} }

View File

@ -20,8 +20,8 @@
#include <QWidget> #include <QWidget>
#include <QTime> #include <QTime>
#include "../chatlinecontent.h" #include "src/chatlog/chatlinecontent.h"
#include "../../corestructs.h" #include "src/core/corestructs.h"
namespace Ui { namespace Ui {
@ -45,7 +45,10 @@ protected slots:
void onFileTransferAccepted(ToxFile file); void onFileTransferAccepted(ToxFile file);
void onFileTransferCancelled(ToxFile file); void onFileTransferCancelled(ToxFile file);
void onFileTransferPaused(ToxFile file); void onFileTransferPaused(ToxFile file);
void onFileTransferResumed(ToxFile file);
void onFileTransferFinished(ToxFile file); void onFileTransferFinished(ToxFile file);
void fileTransferRemotePausedUnpaused(ToxFile file, bool paused);
void fileTransferBrokenUnbroken(ToxFile file, bool broken);
protected: protected:
QString getHumanReadableSize(qint64 size); QString getHumanReadableSize(qint64 size);
@ -69,7 +72,7 @@ private:
Ui::FileTransferWidget *ui; Ui::FileTransferWidget *ui;
ToxFile fileInfo; ToxFile fileInfo;
QTime lastTick; QTime lastTick;
qint64 lastBytesSent = 0; quint64 lastBytesSent = 0;
QVariantAnimation* backgroundColorAnimation = nullptr; QVariantAnimation* backgroundColorAnimation = nullptr;
QVariantAnimation* buttonColorAnimation = nullptr; QVariantAnimation* buttonColorAnimation = nullptr;
QColor backgroundColor; QColor backgroundColor;

File diff suppressed because it is too large Load Diff

1344
src/core/core.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include <QMutex> #include <QMutex>
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h>
#include "corestructs.h" #include "corestructs.h"
#include "coreav.h" #include "coreav.h"
@ -56,15 +57,17 @@ public:
QString getPeerName(const ToxID& id) const; QString getPeerName(const ToxID& id) const;
QVector<uint32_t> getFriendList() const; ///< Returns the list of friendIds in our friendlist, an empty list on error
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
QString getGroupPeerName(int groupId, int peerId) const; ///< Get the name of a peer of a group QString getGroupPeerName(int groupId, int peerId) const; ///< Get the name of a peer of a group
ToxID getGroupPeerToxID(int groupId, int peerId) const; ///< Get the ToxID of a peer of a group ToxID getGroupPeerToxID(int groupId, int peerId) const; ///< Get the ToxID of a peer of a group
QList<QString> getGroupPeerNames(int groupId) const; ///< Get the names of the peers of a group QList<QString> getGroupPeerNames(int groupId) const; ///< Get the names of the peers of a group
QString getFriendAddress(int friendNumber) const; ///< Get the full address if known, or Tox ID of a friend QString getFriendAddress(uint32_t friendId) const; ///< Get the full address if known, or Tox ID of a friend
QString getFriendUsername(int friendNumber) const; ///< Get the username of a friend QString getFriendUsername(uint32_t friendId) const; ///< Get the username of a friend
bool isFriendOnline(uint32_t friendId) const; ///< Check if a friend is online. Unknown friends are considered offline.
bool hasFriendWithAddress(const QString &addr) const; ///< Check if we have a friend by address bool hasFriendWithAddress(const QString &addr) const; ///< Check if we have a friend by address
bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key bool hasFriendWithPublicKey(const QString &pubkey) const; ///< Check if we have a friend by public key
int joinGroupchat(int32_t friendNumber, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite int joinGroupchat(int32_t friendId, uint8_t type, const uint8_t* pubkey,uint16_t length) const; ///< Accept a groupchat invite
void quitGroupChat(int groupId) const; ///< Quit a groupchat void quitGroupChat(int groupId) const; ///< Quit a groupchat
QString getIDString() const; ///< Get the 12 first characters of our Tox ID QString getIDString() const; ///< Get the 12 first characters of our Tox ID
@ -93,43 +96,46 @@ public slots:
void acceptFriendRequest(const QString& userId); void acceptFriendRequest(const QString& userId);
void requestFriendship(const QString& friendAddress, const QString& message); void requestFriendship(const QString& friendAddress, const QString& message);
void groupInviteFriend(int friendId, int groupId); void groupInviteFriend(uint32_t friendId, int groupId);
void createGroup(uint8_t type = TOX_GROUPCHAT_TYPE_AV); void createGroup(uint8_t type = TOX_GROUPCHAT_TYPE_AV);
void removeFriend(int friendId, bool fake = false); void removeFriend(uint32_t friendId, bool fake = false);
void removeGroup(int groupId, bool fake = false); void removeGroup(int groupId, bool fake = false);
void setStatus(Status status); void setStatus(Status status);
void setUsername(const QString& username); void setUsername(const QString& username);
void setStatusMessage(const QString& message); void setStatusMessage(const QString& message);
void setAvatar(uint8_t format, const QByteArray& data); void setAvatar(const QByteArray& data);
int sendMessage(int friendId, const QString& message); int sendMessage(uint32_t friendId, const QString& message);
void sendGroupMessage(int groupId, const QString& message); void sendGroupMessage(int groupId, const QString& message);
void sendGroupAction(int groupId, const QString& message); void sendGroupAction(int groupId, const QString& message);
void changeGroupTitle(int groupId, const QString& title); void changeGroupTitle(int groupId, const QString& title);
int sendAction(int friendId, const QString& action); int sendAction(uint32_t friendId, const QString& action);
void sendTyping(int friendId, bool typing); void sendTyping(uint32_t friendId, bool typing);
void sendFile(int32_t friendId, QString Filename, QString FilePath, long long filesize); void sendFile(uint32_t friendId, QString Filename, QString FilePath, long long filesize);
void cancelFileSend(int friendId, int fileNum); void sendAvatarFile(uint32_t friendId, const QByteArray& data);
void cancelFileRecv(int friendId, int fileNum); void cancelFileSend(uint32_t friendId, uint32_t fileNum);
void rejectFileRecvRequest(int friendId, int fileNum); void cancelFileRecv(uint32_t friendId, uint32_t fileNum);
void acceptFileRecvRequest(int friendId, int fileNum, QString path); void rejectFileRecvRequest(uint32_t friendId, uint32_t fileNum);
void pauseResumeFileSend(int friendId, int fileNum); void acceptFileRecvRequest(uint32_t friendId, uint32_t fileNum, QString path);
void pauseResumeFileRecv(int friendId, int fileNum); void pauseResumeFileSend(uint32_t friendId, uint32_t fileNum);
void pauseResumeFileRecv(uint32_t friendId, uint32_t fileNum);
void answerCall(int callId); void answerCall(int callId);
void rejectCall(int callId); void rejectCall(int callId);
void hangupCall(int callId); void hangupCall(int callId);
void startCall(int friendId, bool video=false); void startCall(uint32_t friendId, bool video=false);
void cancelCall(int callId, int friendId); void cancelCall(int callId, uint32_t friendId);
void micMuteToggle(int callId); void micMuteToggle(int callId);
void volMuteToggle(int callId); void volMuteToggle(int callId);
void setNospam(uint32_t nospam); void setNospam(uint32_t nospam);
bool isGroupAvEnabled(int groupId); ///< True for AV groups, false for text-only groups
static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread. static void joinGroupCall(int groupId); ///< Starts a call in an existing AV groupchat. Call from the GUI thread.
static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread. static void leaveGroupCall(int groupId); ///< Will not leave the group, just stop the call. Call from the GUI thread.
static void disableGroupCallMic(int groupId); static void disableGroupCallMic(int groupId);
@ -151,23 +157,23 @@ signals:
void blockingClearContacts(); void blockingClearContacts();
void friendRequestReceived(const QString& userId, const QString& message); void friendRequestReceived(const QString& userId, const QString& message);
void friendMessageReceived(int friendId, const QString& message, bool isAction); void friendMessageReceived(uint32_t friendId, const QString& message, bool isAction);
void friendAdded(int friendId, const QString& userId); void friendAdded(uint32_t friendId, const QString& userId);
void friendStatusChanged(int friendId, Status status); void friendStatusChanged(uint32_t friendId, Status status);
void friendStatusMessageChanged(int friendId, const QString& message); void friendStatusMessageChanged(uint32_t friendId, const QString& message);
void friendUsernameChanged(int friendId, const QString& username); void friendUsernameChanged(uint32_t friendId, const QString& username);
void friendTypingChanged(int friendId, bool isTyping); void friendTypingChanged(uint32_t friendId, bool isTyping);
void friendAvatarChanged(int friendId, const QPixmap& pic); void friendAvatarChanged(uint32_t friendId, const QPixmap& pic);
void friendAvatarRemoved(int friendId); void friendAvatarRemoved(uint32_t friendId);
void friendRemoved(int friendId); void friendRemoved(uint32_t friendId);
void friendLastSeenChanged(int friendId, const QDateTime& dateTime); void friendLastSeenChanged(uint32_t friendId, const QDateTime& dateTime);
void emptyGroupCreated(int groupnumber); void emptyGroupCreated(int groupnumber);
void groupInviteReceived(int friendnumber, uint8_t type, QByteArray publicKey); void groupInviteReceived(uint32_t friendId, uint8_t type, QByteArray publicKey);
void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction); void groupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change); void groupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void groupTitleChanged(int groupnumber, const QString& author, const QString& title); void groupTitleChanged(int groupnumber, const QString& author, const QString& title);
@ -179,14 +185,14 @@ signals:
void idSet(const QString& id); void idSet(const QString& id);
void selfAvatarChanged(const QPixmap& pic); void selfAvatarChanged(const QPixmap& pic);
void messageSentResult(int friendId, const QString& message, int messageId); void messageSentResult(uint32_t friendId, const QString& message, int messageId);
void groupSentResult(int groupId, const QString& message, int result); void groupSentResult(int groupId, const QString& message, int result);
void actionSentResult(int friendId, const QString& action, int success); void actionSentResult(uint32_t friendId, const QString& action, int success);
void receiptRecieved(int friedId, int receipt); void receiptRecieved(int friedId, int receipt);
void failedToAddFriend(const QString& userId, const QString& errorInfo = QString()); void failedToAddFriend(const QString& userId, const QString& errorInfo = QString());
void failedToRemoveFriend(int friendId); void failedToRemoveFriend(uint32_t friendId);
void failedToSetUsername(const QString& username); void failedToSetUsername(const QString& username);
void failedToSetStatusMessage(const QString& message); void failedToSetStatusMessage(const QString& message);
void failedToSetStatus(Status status); void failedToSetStatus(Status status);
@ -207,45 +213,45 @@ signals:
void fileTransferRemotePausedUnpaused(ToxFile file, bool paused); void fileTransferRemotePausedUnpaused(ToxFile file, bool paused);
void fileTransferBrokenUnbroken(ToxFile file, bool broken); void fileTransferBrokenUnbroken(ToxFile file, bool broken);
void fileSendFailed(int FriendId, const QString& fname); void fileSendFailed(uint32_t friendId, const QString& fname);
void avInvite(int friendId, int callIndex, bool video); void avInvite(uint32_t friendId, int callIndex, bool video);
void avStart(int friendId, int callIndex, bool video); void avStart(uint32_t friendId, int callIndex, bool video);
void avCancel(int friendId, int callIndex); void avCancel(uint32_t friendId, int callIndex);
void avEnd(int friendId, int callIndex); void avEnd(uint32_t friendId, int callIndex);
void avRinging(int friendId, int callIndex, bool video); void avRinging(uint32_t friendId, int callIndex, bool video);
void avStarting(int friendId, int callIndex, bool video); void avStarting(uint32_t friendId, int callIndex, bool video);
void avEnding(int friendId, int callIndex); void avEnding(uint32_t friendId, int callIndex);
void avRequestTimeout(int friendId, int callIndex); void avRequestTimeout(uint32_t friendId, int callIndex);
void avPeerTimeout(int friendId, int callIndex); void avPeerTimeout(uint32_t friendId, int callIndex);
void avMediaChange(int friendId, int callIndex, bool videoEnabled); void avMediaChange(uint32_t friendId, int callIndex, bool videoEnabled);
void avCallFailed(int friendId); void avCallFailed(uint32_t friendId);
void avRejected(int friendId, int callIndex); void avRejected(uint32_t friendId, int callIndex);
void videoFrameReceived(vpx_image* frame); void videoFrameReceived(vpx_image* frame);
private: private:
static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage, uint16_t cMessageSize, void* core); static void onFriendRequest(Tox* tox, const uint8_t* cUserId, const uint8_t* cMessage,
static void onFriendMessage(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core); size_t cMessageSize, void* core);
static void onFriendNameChange(Tox* tox, int friendId, const uint8_t* cName, uint16_t cNameSize, void* core); static void onFriendMessage(Tox* tox, uint32_t friendId, TOX_MESSAGE_TYPE type,
static void onFriendTypingChange(Tox* tox, int friendId, uint8_t isTyping, void* core); const uint8_t* cMessage, size_t cMessageSize, void* core);
static void onStatusMessageChanged(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core); static void onFriendNameChange(Tox* tox, uint32_t friendId, const uint8_t* cName,
static void onUserStatusChanged(Tox* tox, int friendId, uint8_t userstatus, void* core); size_t cNameSize, void* core);
static void onConnectionStatusChanged(Tox* tox, int friendId, uint8_t status, void* core); static void onFriendTypingChange(Tox* tox, uint32_t friendId, bool isTyping, void* core);
static void onAction(Tox* tox, int friendId, const uint8_t* cMessage, uint16_t cMessageSize, void* core); static void onStatusMessageChanged(Tox* tox, uint32_t friendId, const uint8_t* cMessage,
static void onGroupAction(Tox* tox, int groupnumber, int peernumber, const uint8_t * action, uint16_t length, void* core); size_t cMessageSize, void* core);
static void onGroupInvite(Tox *tox, int friendnumber, uint8_t type, const uint8_t *data, uint16_t length,void *userdata); static void onUserStatusChanged(Tox* tox, uint32_t friendId, TOX_USER_STATUS userstatus, void* core);
static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber, const uint8_t * message, uint16_t length, void *userdata); static void onConnectionStatusChanged(Tox* tox, uint32_t friendId, TOX_CONNECTION status, void* core);
static void onGroupNamelistChange(Tox *tox, int groupnumber, int peernumber, uint8_t change, void *userdata); static void onGroupAction(Tox* tox, int groupnumber, int peernumber, const uint8_t * action,
static void onGroupTitleChange(Tox*, int groupnumber, int peernumber, const uint8_t* title, uint8_t len, void* _core); uint16_t length, void* core);
static void onFileSendRequestCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, static void onGroupInvite(Tox *tox, int32_t friendId, uint8_t type, const uint8_t *data,
const uint8_t *filename, uint16_t filename_length, void *userdata); uint16_t length, void *userdata);
static void onFileControlCallback(Tox *tox, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, static void onGroupMessage(Tox *tox, int groupnumber, int friendgroupnumber,
uint8_t control_type, const uint8_t *data, uint16_t length, void *core); const uint8_t * message, uint16_t length, void *userdata);
static void onFileDataCallback(Tox *tox, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata); static void onGroupNamelistChange(Tox *tox, int groupId, int peerId, uint8_t change, void *core);
static void onAvatarInfoCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, void *userdata); static void onGroupTitleChange(Tox*, int groupnumber, int peernumber,
static void onAvatarDataCallback(Tox* tox, int32_t friendnumber, uint8_t format, uint8_t *hash, uint8_t *data, uint32_t datalen, void *userdata); const uint8_t* title, uint8_t len, void* _core);
static void onReadReceiptCallback(Tox *tox, int32_t friendnumber, uint32_t receipt, void *core); static void onReadReceiptCallback(Tox *tox, uint32_t friendId, uint32_t receipt, void *core);
static void onAvInvite(void* toxav, int32_t call_index, void* core); static void onAvInvite(void* toxav, int32_t call_index, void* core);
static void onAvStart(void* toxav, int32_t call_index, void* core); static void onAvStart(void* toxav, int32_t call_index, void* core);
@ -259,32 +265,28 @@ private:
static void sendGroupCallAudio(int groupId, ToxAv* toxav); static void sendGroupCallAudio(int groupId, ToxAv* toxav);
static void prepareCall(int friendId, int callId, ToxAv *toxav, bool videoEnabled); static void prepareCall(uint32_t friendId, int callId, ToxAv *toxav, bool videoEnabled);
static void cleanupCall(int callId); static void cleanupCall(int callId);
static void playCallAudio(void *toxav, int32_t callId, const int16_t *data, uint16_t samples, void *user_data); // Callback static void playCallAudio(void *toxav, int32_t callId, const int16_t *data,
uint16_t samples, void *user_data); // Callback
static void sendCallAudio(int callId, ToxAv* toxav); static void sendCallAudio(int callId, ToxAv* toxav);
static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples, unsigned channels, int sampleRate); static void playAudioBuffer(ALuint alSource, const int16_t *data, int samples,
unsigned channels, int sampleRate);
static void playCallVideo(void *toxav, int32_t callId, const vpx_image_t* img, void *user_data); static void playCallVideo(void *toxav, int32_t callId, const vpx_image_t* img, void *user_data);
void sendCallVideo(int callId); void sendCallVideo(int callId);
bool checkConnection(); bool checkConnection();
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise QByteArray loadToxSave(QString path);
bool loadEncryptedSave(QByteArray& data); bool loadEncryptedSave(QByteArray& data);
void checkEncryptedHistory(); void checkEncryptedHistory();
void make_tox(); void make_tox(QByteArray savedata);
void loadFriends(); void loadFriends();
static void sendAllFileData(Core* core, ToxFile* file); void checkLastOnline(uint32_t friendId);
static void removeFileFromQueue(bool sendQueue, int friendId, int fileId);
void checkLastOnline(int friendId);
void deadifyTox(); void deadifyTox();
private slots:
void onFileTransferFinished(ToxFile file);
private: private:
Tox* tox; Tox* tox;
ToxAv* toxav; ToxAv* toxav;
@ -293,23 +295,22 @@ private:
QString loadPath; // meaningless after start() is called QString loadPath; // meaningless after start() is called
QList<DhtServer> dhtServerList; QList<DhtServer> dhtServerList;
int dhtServerId; int dhtServerId;
static QList<ToxFile> fileSendQueue, fileRecvQueue;
static ToxCall calls[TOXAV_MAX_CALLS]; static ToxCall calls[TOXAV_MAX_CALLS];
#ifdef QTOX_FILTER_AUDIO #ifdef QTOX_FILTER_AUDIO
static AudioFilterer * filterer[TOXAV_MAX_CALLS]; static AudioFilterer * filterer[TOXAV_MAX_CALLS];
#endif #endif
static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls static QHash<int, ToxGroupCall> groupCalls; // Maps group IDs to ToxGroupCalls
QMutex fileSendMutex, messageSendMutex; QMutex messageSendMutex;
bool ready; bool ready;
uint8_t* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw" TOX_PASS_KEY* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw"
// Hack for reloading current profile if switching to an encrypted one fails. // Hack for reloading current profile if switching to an encrypted one fails.
// Testing the passwords before killing the current profile is perfectly doable, // Testing the passwords before killing the current profile is perfectly doable,
// however it would require major refactoring; // however it would require major refactoring;
// the Core class as a whole also requires major refactoring (especially to support multiple IDs at once), // the Core class as a whole also requires major refactoring (especially to support multiple IDs at once),
// so I'm punting on this until then, when it would get fixed anyways // so I'm punting on this until then, when it would get fixed anyways
uint8_t* backupkeys[PasswordType::ptCounter] = {nullptr}; TOX_PASS_KEY* backupkeys[PasswordType::ptCounter] = {nullptr};
QString* backupProfile = nullptr; QString* backupProfile = nullptr;
void saveCurrentInformation(); void saveCurrentInformation();
QString loadOldInformation(); QString loadOldInformation();
@ -320,6 +321,7 @@ private:
static QThread *coreThread; static QThread *coreThread;
friend class Audio; ///< Audio can access our calls directly to reduce latency friend class Audio; ///< Audio can access our calls directly to reduce latency
friend class CoreFile; ///< CoreFile can access tox* and emit our signals
}; };
#endif // CORE_HPP #endif // CORE_HPP

View File

@ -15,12 +15,12 @@
*/ */
#include "core.h" #include "core.h"
#include "video/camera.h" #include "src/video/camera.h"
#include "audio.h" #include "src/audio.h"
#ifdef QTOX_FILTER_AUDIO #ifdef QTOX_FILTER_AUDIO
#include "audiofilterer.h" #include "src/audiofilterer.h"
#endif #endif
#include "misc/settings.h" #include "src/misc/settings.h"
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
@ -34,12 +34,14 @@ uint8_t* Core::videobuf;
bool Core::anyActiveCalls() bool Core::anyActiveCalls()
{ {
for (auto& call : calls) for (auto& call : calls)
{
if (call.active) if (call.active)
return true; return true;
}
return false; return false;
} }
void Core::prepareCall(int friendId, int callId, ToxAv* toxav, bool videoEnabled) void Core::prepareCall(uint32_t friendId, int32_t callId, ToxAv* toxav, bool videoEnabled)
{ {
qDebug() << QString("Core: preparing call %1").arg(callId); qDebug() << QString("Core: preparing call %1").arg(callId);
calls[callId].callId = callId; calls[callId].callId = callId;
@ -93,6 +95,7 @@ void Core::onAvMediaChange(void* toxav, int32_t callId, void* core)
int friendId; int friendId;
if (toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &settings) < 0) if (toxav_get_peer_csettings((ToxAv*)toxav, callId, 0, &settings) < 0)
goto fail; goto fail;
friendId = toxav_get_peer_id((ToxAv*)toxav, callId, 0); friendId = toxav_get_peer_id((ToxAv*)toxav, callId, 0);
if (friendId < 0) if (friendId < 0)
goto fail; goto fail;
@ -120,7 +123,7 @@ fail: // Centralized error handling
return; return;
} }
void Core::answerCall(int callId) void Core::answerCall(int32_t callId)
{ {
int friendId = toxav_get_peer_id(toxav, callId, 0); int friendId = toxav_get_peer_id(toxav, callId, 0);
if (friendId < 0) if (friendId < 0)
@ -152,23 +155,23 @@ void Core::answerCall(int callId)
delete transSettings; delete transSettings;
} }
void Core::hangupCall(int callId) void Core::hangupCall(int32_t callId)
{ {
qDebug() << QString("Core: hanging up call %1").arg(callId); qDebug() << QString("Core: hanging up call %1").arg(callId);
calls[callId].active = false; calls[callId].active = false;
toxav_hangup(toxav, callId); toxav_hangup(toxav, callId);
} }
void Core::rejectCall(int callId) void Core::rejectCall(int32_t callId)
{ {
qDebug() << QString("Core: rejecting call %1").arg(callId); qDebug() << QString("Core: rejecting call %1").arg(callId);
calls[callId].active = false; calls[callId].active = false;
toxav_reject(toxav, callId, nullptr); toxav_reject(toxav, callId, nullptr);
} }
void Core::startCall(int friendId, bool video) void Core::startCall(uint32_t friendId, bool video)
{ {
int callId; int32_t callId;
ToxAvCSettings cSettings = av_DefaultSettings; ToxAvCSettings cSettings = av_DefaultSettings;
cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH; cSettings.max_video_width = TOXAV_MAX_VIDEO_WIDTH;
cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT; cSettings.max_video_height = TOXAV_MAX_VIDEO_HEIGHT;
@ -204,14 +207,14 @@ void Core::startCall(int friendId, bool video)
} }
} }
void Core::cancelCall(int callId, int friendId) void Core::cancelCall(int32_t callId, uint32_t friendId)
{ {
qDebug() << QString("Core: Cancelling call with %1").arg(friendId); qDebug() << QString("Core: Cancelling call with %1").arg(friendId);
calls[callId].active = false; calls[callId].active = false;
toxav_cancel(toxav, callId, friendId, nullptr); toxav_cancel(toxav, callId, friendId, nullptr);
} }
void Core::cleanupCall(int callId) void Core::cleanupCall(int32_t callId)
{ {
qDebug() << QString("Core: cleaning up call %1").arg(callId); qDebug() << QString("Core: cleaning up call %1").arg(callId);
calls[callId].active = false; calls[callId].active = false;
@ -220,6 +223,7 @@ void Core::cleanupCall(int callId)
calls[callId].sendVideoTimer->stop(); calls[callId].sendVideoTimer->stop();
if (calls[callId].videoEnabled) if (calls[callId].videoEnabled)
Camera::getInstance()->unsubscribe(); Camera::getInstance()->unsubscribe();
Audio::unsuscribeInput(); Audio::unsuscribeInput();
toxav_kill_transmission(Core::getInstance()->toxav, callId); toxav_kill_transmission(Core::getInstance()->toxav, callId);
} }
@ -239,7 +243,7 @@ void Core::playCallAudio(void* toxav, int32_t callId, const int16_t *data, uint1
playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate); playAudioBuffer(calls[callId].alSource, data, samples, dest.audio_channels, dest.audio_sample_rate);
} }
void Core::sendCallAudio(int callId, ToxAv* toxav) void Core::sendCallAudio(int32_t callId, ToxAv* toxav)
{ {
if (!calls[callId].active) if (!calls[callId].active)
return; return;
@ -286,9 +290,7 @@ void Core::sendCallAudio(int callId, ToxAv* toxav)
} }
if ((r = toxav_send_audio(toxav, callId, dest, r)) < 0) if ((r = toxav_send_audio(toxav, callId, dest, r)) < 0)
{
qDebug() << "Core: toxav_send_audio error"; qDebug() << "Core: toxav_send_audio error";
}
} }
calls[callId].sendAudioTimer->start(); calls[callId].sendAudioTimer->start();
} }
@ -303,7 +305,7 @@ void Core::playCallVideo(void*, int32_t callId, const vpx_image_t* img, void *us
calls[callId].videoSource.pushVPXFrame(img); calls[callId].videoSource.pushVPXFrame(img);
} }
void Core::sendCallVideo(int callId) void Core::sendCallVideo(int32_t callId)
{ {
if (!calls[callId].active || !calls[callId].videoEnabled) if (!calls[callId].active || !calls[callId].videoEnabled)
return; return;
@ -333,15 +335,13 @@ void Core::sendCallVideo(int callId)
calls[callId].sendVideoTimer->start(); calls[callId].sendVideoTimer->start();
} }
void Core::micMuteToggle(int callId) void Core::micMuteToggle(int32_t callId)
{ {
if (calls[callId].active) if (calls[callId].active)
{
calls[callId].muteMic = !calls[callId].muteMic; calls[callId].muteMic = !calls[callId].muteMic;
}
} }
void Core::volMuteToggle(int callId) void Core::volMuteToggle(int32_t callId)
{ {
if (calls[callId].active) if (calls[callId].active)
{ {

View File

@ -3,7 +3,7 @@
#include <QHash> #include <QHash>
#include <tox/toxav.h> #include <tox/toxav.h>
#include "video/netvideosource.h" #include "src/video/netvideosource.h"
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#include <OpenAL/al.h> #include <OpenAL/al.h>
@ -19,8 +19,8 @@ struct ToxCall
{ {
ToxAvCSettings codecSettings; ToxAvCSettings codecSettings;
QTimer *sendAudioTimer, *sendVideoTimer; QTimer *sendAudioTimer, *sendVideoTimer;
int callId; int32_t callId;
int friendId; uint32_t friendId;
bool videoEnabled; bool videoEnabled;
bool active; bool active;
bool muteMic; bool muteMic;

View File

@ -20,17 +20,18 @@
#include "core.h" #include "core.h"
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/misc/settings.h"
#include "src/misc/cstring.h"
#include "src/historykeeper.h"
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
#include "src/misc/settings.h"
#include "misc/cstring.h"
#include "historykeeper.h"
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QSaveFile> #include <QSaveFile>
#include <QFile> #include <QFile>
#include <QThread> #include <QThread>
#include <algorithm>
#include <cassert>
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt) void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
{ {
@ -38,31 +39,30 @@ void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
if (password.isEmpty()) if (password.isEmpty())
return; return;
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()]; pwsaltedkeys[passtype] = new TOX_PASS_KEY;
CString str(password); CString str(password);
if (salt) if (salt)
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]); tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype], nullptr);
else else
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]); tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype], nullptr);
password.clear(); password.clear();
} }
#include <algorithm>
void Core::useOtherPassword(PasswordType type) void Core::useOtherPassword(PasswordType type)
{ {
clearPassword(type); clearPassword(type);
pwsaltedkeys[type] = new uint8_t[tox_pass_key_length()]; pwsaltedkeys[type] = new TOX_PASS_KEY;
PasswordType other = (type == ptMain) ? ptHistory : ptMain; PasswordType other = (type == ptMain) ? ptHistory : ptMain;
std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+tox_pass_key_length(), pwsaltedkeys[type]); std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+1, pwsaltedkeys[type]);
} }
void Core::clearPassword(PasswordType passtype) void Core::clearPassword(PasswordType passtype)
{ {
delete[] pwsaltedkeys[passtype]; delete pwsaltedkeys[passtype];
pwsaltedkeys[passtype] = nullptr; pwsaltedkeys[passtype] = nullptr;
} }
@ -71,13 +71,13 @@ void Core::saveCurrentInformation()
{ {
if (pwsaltedkeys[ptMain]) if (pwsaltedkeys[ptMain])
{ {
backupkeys[ptMain] = new uint8_t[tox_pass_key_length()]; backupkeys[ptMain] = new TOX_PASS_KEY;
std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+tox_pass_key_length(), backupkeys[ptMain]); std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+1, backupkeys[ptMain]);
} }
if (pwsaltedkeys[ptHistory]) if (pwsaltedkeys[ptHistory])
{ {
backupkeys[ptHistory] = new uint8_t[tox_pass_key_length()]; backupkeys[ptHistory] = new TOX_PASS_KEY;
std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+tox_pass_key_length(), backupkeys[ptHistory]); std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+1, backupkeys[ptHistory]);
} }
backupProfile = new QString(Settings::getInstance().getCurrentProfile()); backupProfile = new QString(Settings::getInstance().getCurrentProfile());
} }
@ -107,23 +107,26 @@ QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
{ {
if (!pwsaltedkeys[passtype]) if (!pwsaltedkeys[passtype])
return QByteArray(); return QByteArray();
uint8_t encrypted[data.size() + tox_pass_encryption_extra_length()];
if (tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1) uint8_t encrypted[data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
if (!tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
pwsaltedkeys[passtype], encrypted, nullptr))
{ {
qWarning() << "Core::encryptData: encryption failed"; qWarning() << "Core::encryptData: encryption failed";
return QByteArray(); return QByteArray();
} }
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length()); return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
} }
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype) QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
{ {
if (!pwsaltedkeys[passtype]) if (!pwsaltedkeys[passtype])
return QByteArray(); return QByteArray();
int sz = data.size() - tox_pass_encryption_extra_length();
int sz = data.size() - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
uint8_t decrypted[sz]; uint8_t decrypted[sz];
int decr_size = tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted); if (!tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(),
if (decr_size != sz) pwsaltedkeys[passtype], decrypted, nullptr))
{ {
qWarning() << "Core::decryptData: decryption failed"; qWarning() << "Core::decryptData: decryption failed";
return QByteArray(); return QByteArray();
@ -147,18 +150,17 @@ QByteArray Core::getSaltFromFile(QString filename)
qWarning() << "Core: file" << filename << "doesn't exist"; qWarning() << "Core: file" << filename << "doesn't exist";
return QByteArray(); return QByteArray();
} }
QByteArray data = file.read(tox_pass_encryption_extra_length()); QByteArray data = file.read(TOX_PASS_ENCRYPTION_EXTRA_LENGTH);
file.close(); file.close();
uint8_t *salt = new uint8_t[tox_pass_salt_length()]; uint8_t *salt = new uint8_t[TOX_PASS_SALT_LENGTH];
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt); if (!tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt))
if (err)
{ {
qWarning() << "Core: can't get salt from" << filename << "header"; qWarning() << "Core: can't get salt from" << filename << "header";
return QByteArray(); return QByteArray();
} }
QByteArray res = QByteArray::fromRawData(reinterpret_cast<const char*>(salt), tox_pass_salt_length()); QByteArray res(reinterpret_cast<const char*>(salt), TOX_PASS_SALT_LENGTH);
delete[] salt; delete[] salt;
return res; return res;
} }
@ -168,6 +170,7 @@ bool Core::loadEncryptedSave(QByteArray& data)
if (!Settings::getInstance().getEncryptTox()) if (!Settings::getInstance().getEncryptTox())
GUI::showWarning(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless.")); GUI::showWarning(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
size_t fileSize = data.size();
int error = -1; int error = -1;
QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile())); QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile()));
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()")); QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
@ -175,18 +178,23 @@ bool Core::loadEncryptedSave(QByteArray& data)
if (pwsaltedkeys[ptMain]) // password set, try it if (pwsaltedkeys[ptMain]) // password set, try it
{ {
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]); QByteArray newData(fileSize-TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0);
if (!error) if (tox_pass_key_decrypt((uint8_t*)data.data(), fileSize, pwsaltedkeys[ptMain],
(uint8_t*)newData.data(), nullptr))
{ {
data = newData;
Settings::getInstance().setEncryptTox(true); Settings::getInstance().setEncryptTox(true);
return true; return true;
} }
dialogtxt = tr("The profile password failed. Please try another?", "used only when pw set before load() doesn't work"); dialogtxt = tr("The profile password failed. Please try another?", "used only when pw set before load() doesn't work");
} }
else else
{
dialogtxt = a; dialogtxt = a;
}
uint8_t salt[tox_pass_salt_length()]; uint8_t salt[TOX_PASS_SALT_LENGTH];
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt); tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
do do
@ -199,9 +207,16 @@ bool Core::loadEncryptedSave(QByteArray& data)
return false; return false;
} }
else else
{
setPassword(pw, ptMain, salt); setPassword(pw, ptMain, salt);
}
QByteArray newData(fileSize-TOX_PASS_ENCRYPTION_EXTRA_LENGTH, 0);
error = !tox_pass_key_decrypt((uint8_t*)data.data(), data.size(), pwsaltedkeys[ptMain],
(uint8_t*)newData.data(), nullptr);
if (!error)
data = newData;
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
dialogtxt = a + "\n" + b; dialogtxt = a + "\n" + b;
} while (error != 0); } while (error != 0);
@ -233,10 +248,14 @@ void Core::checkEncryptedHistory()
{ {
if (!exists || HistoryKeeper::checkPassword()) if (!exists || HistoryKeeper::checkPassword())
return; return;
dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work"); dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work");
} }
else else
{
dialogtxt = a; dialogtxt = a;
}
dialogtxt += "\n" + c; dialogtxt += "\n" + c;
if (pwsaltedkeys[ptMain]) if (pwsaltedkeys[ptMain])
@ -283,20 +302,19 @@ void Core::saveConfiguration(const QString& path)
} }
QSaveFile configurationFile(path); QSaveFile configurationFile(path);
if (!configurationFile.open(QIODevice::WriteOnly)) { if (!configurationFile.open(QIODevice::WriteOnly))
{
qCritical() << "File " << path << " cannot be opened"; qCritical() << "File " << path << " cannot be opened";
return; return;
} }
qDebug() << "Core: writing tox_save to " << path; qDebug() << "Core: writing tox_save to " << path;
uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox(); uint32_t fileSize = tox_get_savedata_size(tox);
if (encrypt) bool encrypt = Settings::getInstance().getEncryptTox();
fileSize = tox_encrypted_size(tox);
else
fileSize = tox_size(tox);
if (fileSize > 0 && fileSize <= std::numeric_limits<int32_t>::max()) { if (fileSize > 0 && fileSize <= std::numeric_limits<int32_t>::max())
{
uint8_t *data = new uint8_t[fileSize]; uint8_t *data = new uint8_t[fileSize];
if (encrypt) if (encrypt)
@ -306,20 +324,32 @@ void Core::saveConfiguration(const QString& path)
// probably zero chance event // probably zero chance event
GUI::showWarning(tr("NO Password"), tr("Local file encryption is enabled, but there is no password! It will be disabled.")); GUI::showWarning(tr("NO Password"), tr("Local file encryption is enabled, but there is no password! It will be disabled."));
Settings::getInstance().setEncryptTox(false); Settings::getInstance().setEncryptTox(false);
tox_save(tox, data); tox_get_savedata(tox, data);
} }
else else
{ {
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]); tox_get_savedata(tox, data);
if (ret == -1) uint8_t* newData = new uint8_t[fileSize+TOX_PASS_ENCRYPTION_EXTRA_LENGTH];
if (tox_pass_key_encrypt(data, fileSize, pwsaltedkeys[ptMain], newData, nullptr))
{ {
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!"; delete[] data;
data = newData;
fileSize+=TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
}
else
{
delete[] newData;
delete[] data;
qCritical() << "Core::saveConfiguration(QString): Encryption failed, couldn't save";
configurationFile.cancelWriting();
return; return;
} }
} }
} }
else else
tox_save(tox, data); {
tox_get_savedata(tox, data);
}
configurationFile.write(reinterpret_cast<char *>(data), fileSize); configurationFile.write(reinterpret_cast<char *>(data), fileSize);
configurationFile.commit(); configurationFile.commit();

415
src/core/corefile.cpp Normal file
View File

@ -0,0 +1,415 @@
#include "core.h"
#include "corefile.h"
#include "corestructs.h"
#include "src/misc/cstring.h"
#include "src/misc/settings.h"
#include <QDebug>
#include <QFile>
#include <QThread>
#include <QDir>
#include <memory>
QMutex CoreFile::fileSendMutex;
QHash<uint64_t, ToxFile> CoreFile::fileMap;
using namespace std;
void CoreFile::sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data)
{
QMutexLocker mlocker(&fileSendMutex);
uint8_t filename[TOX_HASH_LENGTH];
tox_hash(filename, (uint8_t*)data.data(), data.size());
uint64_t filesize = data.size();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_AVATAR, filesize,
nullptr, filename, TOX_HASH_LENGTH, nullptr);
if (fileNum == UINT32_MAX)
{
qWarning() << "CoreFile::sendAvatarFile: Can't create the Tox file sender";
return;
}
//qDebug() << QString("CoreFile::sendAvatarFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
ToxFile file{fileNum, friendId, "", "", ToxFile::SENDING};
file.filesize = filesize;
file.fileName = QByteArray((char*)filename, TOX_HASH_LENGTH);
file.fileKind = TOX_FILE_KIND_AVATAR;
file.avatarData = data;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
addFile(friendId, fileNum, file);
}
void CoreFile::sendFile(Core* core, uint32_t friendId, QString Filename, QString FilePath, long long filesize)
{
QMutexLocker mlocker(&fileSendMutex);
QByteArray fileName = Filename.toUtf8();
uint32_t fileNum = tox_file_send(core->tox, friendId, TOX_FILE_KIND_DATA, filesize, nullptr,
(uint8_t*)fileName.data(), fileName.size(), nullptr);
if (fileNum == UINT32_MAX)
{
qWarning() << "CoreFile::sendFile: Can't create the Tox file sender";
emit core->fileSendFailed(friendId, Filename);
return;
}
qDebug() << QString("CoreFile::sendFile: Created file sender %1 with friend %2").arg(fileNum).arg(friendId);
ToxFile file{fileNum, friendId, fileName, FilePath, ToxFile::SENDING};
file.filesize = filesize;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileNum, (uint8_t*)file.resumeFileId.data(), nullptr);
if (!file.open(false))
{
qWarning() << QString("CoreFile::sendFile: Can't open file, error: %1").arg(file.file->errorString());
}
addFile(friendId, fileNum, file);
emit core->fileSendStarted(file);
}
void CoreFile::pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::pauseResumeFileSend: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
qWarning() << "CoreFile::pauseResumeFileSend: File is stopped";
}
void CoreFile::pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileRecv: No such file in queue");
return;
}
if (file->status == ToxFile::TRANSMITTING)
{
file->status = ToxFile::PAUSED;
emit core->fileTransferPaused(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_PAUSE, nullptr);
}
else if (file->status == ToxFile::PAUSED)
{
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
else
qWarning() << "CoreFile::pauseResumeFileRecv: File is stopped or broken";
}
void CoreFile::cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileSend: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
while (file->sendTimer) QThread::msleep(1); // Wait until sendAllFileData returns before deleting
removeFile(friendId, fileId);
}
void CoreFile::cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::cancelFileRecv: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
void CoreFile::rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::rejectFileRecvRequest: No such file in queue");
return;
}
file->status = ToxFile::STOPPED;
emit core->fileTransferCancelled(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
}
void CoreFile::acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::acceptFileRecvRequest: No such file in queue");
return;
}
file->setFilePath(path);
if (!file->open(true))
{
qWarning() << "CoreFile::acceptFileRecvRequest: Unable to open file";
return;
}
file->status = ToxFile::TRANSMITTING;
emit core->fileTransferAccepted(*file);
tox_file_control(core->tox, file->friendId, file->fileNum, TOX_FILE_CONTROL_RESUME, nullptr);
}
ToxFile* CoreFile::findFile(uint32_t friendId, uint32_t fileId)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (!fileMap.contains(key))
{
qWarning() << "CoreFile::findFile: File transfer with ID "<<friendId<<':'<<fileId<<" doesn't exist";
return nullptr;
}
else
return &fileMap[key];
}
void CoreFile::addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (fileMap.contains(key))
qWarning() << "CoreFile::addFile: Overwriting existing file transfer with same ID "<<friendId<<':'<<fileId;
fileMap.insert(key, file);
}
void CoreFile::removeFile(uint32_t friendId, uint32_t fileId)
{
uint64_t key = ((uint64_t)friendId<<32) + (uint64_t)fileId;
if (!fileMap.contains(key))
{
qWarning() << "CoreFile::removeFile: No such file in queue";
return;
}
fileMap[key].file->close();
fileMap.remove(key);
}
void CoreFile::onFileReceiveCallback(Tox*, uint32_t friendId, uint32_t fileId, uint32_t kind,
uint64_t filesize, const uint8_t *fname, size_t fnameLen, void *_core)
{
Core* core = static_cast<Core*>(_core);
qDebug() << QString("CoreFile: Received file request %1:%2 kind %3")
.arg(friendId).arg(fileId).arg(kind);
if (kind == TOX_FILE_KIND_AVATAR)
{
QString friendAddr = core->getFriendAddress(friendId);
if (!filesize)
{
// Avatars of size 0 means explicitely no avatar
emit core->friendAvatarRemoved(friendId);
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+friendAddr.left(64)+".png"));
QFile::remove(QDir(Settings::getSettingsDirPath()).filePath("avatars/"+friendAddr.left(64)+".hash"));
return;
}
else if (Settings::getInstance().getAvatarHash(friendAddr) == QByteArray((char*)fname, fnameLen))
{
// If it's an avatar but we already have it cached, cancel
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
else
{
// It's an avatar and we don't have it, autoaccept the transfer
tox_file_control(core->tox, friendId, fileId, TOX_FILE_CONTROL_RESUME, nullptr);
}
}
ToxFile file{fileId, friendId,
CString::toString(fname,fnameLen).toUtf8(), "", ToxFile::RECEIVING};
file.filesize = filesize;
file.fileKind = kind;
file.resumeFileId.resize(TOX_FILE_ID_LENGTH);
tox_file_get_file_id(core->tox, friendId, fileId, (uint8_t*)file.resumeFileId.data(), nullptr);
addFile(friendId, fileId, file);
if (kind != TOX_FILE_KIND_AVATAR)
emit core->fileReceiveRequested(file);
}
void CoreFile::onFileControlCallback(Tox*, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void *core)
{
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileControlCallback: No such file in queue");
return;
}
if (control == TOX_FILE_CONTROL_CANCEL)
{
qDebug() << "CoreFile::onFileControlCallback: Received cancel for file "<<friendId<<":"<<fileId;
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
removeFile(friendId, fileId);
}
else if (control == TOX_FILE_CONTROL_PAUSE)
{
qDebug() << "CoreFile::onFileControlCallback: Received pause for file "<<friendId<<":"<<fileId;
file->status = ToxFile::PAUSED;
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, true);
}
else if (control == TOX_FILE_CONTROL_RESUME)
{
qDebug() << "CoreFile::onFileControlCallback: Received pause for file "<<friendId<<":"<<fileId;
file->status = ToxFile::TRANSMITTING;
emit static_cast<Core*>(core)->fileTransferRemotePausedUnpaused(*file, false);
}
else
{
qWarning() << "Unhandled file control "<<control<<" for file "<<friendId<<':'<<fileId;
}
}
void CoreFile::onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
uint64_t pos, size_t length, void* core)
{
//qDebug() << "File data req of "<<length<<" at "<<pos<<" for file "<<friendId<<':'<<fileId;
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileDataCallback: No such file in queue");
return;
}
// If we reached EOF, ack and cleanup the transfer
if (!length)
{
//qDebug("CoreFile::onFileDataCallback: File sending completed");
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferFinished(*file);
removeFile(friendId, fileId);
return;
}
unique_ptr<uint8_t[]> data(new uint8_t[length]);
int64_t nread;
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
QByteArray chunk = file->avatarData.mid(pos, length);
nread = chunk.size();
memcpy(data.get(), chunk.data(), nread);
}
else
{
file->file->seek(pos);
nread = file->file->read((char*)data.get(), length);
if (nread <= 0)
{
qWarning("CoreFile::onFileDataCallback: Failed to read from file");
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
tox_file_send_chunk(tox, friendId, fileId, pos, nullptr, 0, nullptr);
removeFile(friendId, fileId);
return;
}
file->bytesSent += length;
}
if (!tox_file_send_chunk(tox, friendId, fileId, pos, data.get(), nread, nullptr))
{
qWarning("CoreFile::onFileDataCallback: Failed to send data chunk");
return;
}
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferInfo(*file);
}
void CoreFile::onFileRecvChunkCallback(Tox *tox, uint32_t friendId, uint32_t fileId, uint64_t position,
const uint8_t *data, size_t length, void *core)
{
//qDebug() << QString("CoreFile: Received chunk for %1:%2 pos %3 size %4")
// .arg(friendId).arg(fileId).arg(position).arg(length);
ToxFile* file = findFile(friendId, fileId);
if (!file)
{
qWarning("CoreFile::onFileRecvChunkCallback: No such file in queue");
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
return;
}
if (file->bytesSent != position)
{
/// TODO: Allow ooo receiving for non-stream transfers, with very careful checking
qWarning("CoreFile::onFileRecvChunkCallback: Received a chunk out-of-order, aborting transfer");
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferCancelled(*file);
tox_file_control(tox, friendId, fileId, TOX_FILE_CONTROL_CANCEL, nullptr);
removeFile(friendId, fileId);
return;
}
if (!length)
{
if (file->fileKind == TOX_FILE_KIND_AVATAR)
{
QPixmap pic;
pic.loadFromData(file->avatarData);
if (!pic.isNull())
{
qDebug() << "Core: Got avatar data from" << static_cast<Core*>(core)->getFriendUsername(friendId);
Settings::getInstance().saveAvatar(pic, static_cast<Core*>(core)->getFriendAddress(friendId));
Settings::getInstance().saveAvatarHash(file->fileName, static_cast<Core*>(core)->getFriendAddress(friendId));
emit static_cast<Core*>(core)->friendAvatarChanged(friendId, pic);
}
}
else
{
emit static_cast<Core*>(core)->fileTransferFinished(*file);
}
removeFile(friendId, fileId);
return;
}
if (file->fileKind == TOX_FILE_KIND_AVATAR)
file->avatarData.append((char*)data, length);
else
file->file->write((char*)data,length);
file->bytesSent += length;
if (file->fileKind != TOX_FILE_KIND_AVATAR)
emit static_cast<Core*>(core)->fileTransferInfo(*file);
}
void CoreFile::onConnectionStatusChanged(Core* core, uint32_t friendId, bool online)
{
/// TODO: Actually resume broken file transfers
/// We need to:
/// - Start a new file transfer with the same 32byte file ID with toxcore
/// - Seek to the correct position again
/// - Update the fileNum in our ToxFile
/// - Update the users of our signals to check the 32byte tox file ID, not the uint32_t file_num (fileId)
ToxFile::FileStatus status = online ? ToxFile::TRANSMITTING : ToxFile::BROKEN;
for (uint64_t key : fileMap.keys())
{
if (key>>32 != friendId)
continue;
fileMap[key].status = status;
emit core->fileTransferBrokenUnbroken(fileMap[key], !online);
}
}

57
src/core/corefile.h Normal file
View File

@ -0,0 +1,57 @@
#ifndef COREFILE_H
#define COREFILE_H
#include <cstdint>
#include <cstddef>
#include <memory>
#include <tox/tox.h>
#include "corestructs.h"
#include <QString>
#include <QMutex>
#include <QHash>
struct Tox;
class Core;
/// Implements Core's file transfer callbacks
/// Avoids polluting core.h with private internal callbacks
class CoreFile
{
friend class Core;
private:
CoreFile()=delete;
private:
static void sendFile(Core *core, uint32_t friendId, QString Filename, QString FilePath, long long filesize);
static void sendAvatarFile(Core* core, uint32_t friendId, const QByteArray& data);
static void pauseResumeFileSend(Core* core, uint32_t friendId, uint32_t fileId);
static void pauseResumeFileRecv(Core* core, uint32_t friendId, uint32_t fileId);
static void cancelFileSend(Core* core, uint32_t friendId, uint32_t fileId);
static void cancelFileRecv(Core* core, uint32_t friendId, uint32_t fileId);
static void rejectFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId);
static void acceptFileRecvRequest(Core* core, uint32_t friendId, uint32_t fileId, QString path);
static ToxFile *findFile(uint32_t friendId, uint32_t fileId);
static void addFile(uint32_t friendId, uint32_t fileId, const ToxFile& file);
static void removeFile(uint32_t friendId, uint32_t fileId);
private:
static void onFileReceiveCallback(Tox*, uint32_t friendnumber, uint32_t fileId, uint32_t kind,
uint64_t filesize, const uint8_t *fname, size_t fnameLen, void *core);
static void onFileControlCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
TOX_FILE_CONTROL control, void *core);
static void onFileDataCallback(Tox *tox, uint32_t friendId, uint32_t fileId,
uint64_t pos, size_t length, void *core);
static void onFileRecvChunkCallback(Tox *tox, uint32_t friendId, uint32_t fileId, uint64_t position,
const uint8_t *data, size_t length, void *core);
static void onConnectionStatusChanged(Core* core, uint32_t friendId, bool online);
private:
static QMutex fileSendMutex;
static QHash<uint64_t, ToxFile> fileMap;
/// TODO: Replace the two queues by a hash map uint64_t -> unique_ptr<ToxFile>
};
#endif // COREFILE_H

View File

@ -1,14 +1,15 @@
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include "src/core.h" #include "src/core/core.h"
#include <tox/tox.h> #include <tox/tox.h>
#include <QFile> #include <QFile>
#include <QRegularExpression> #include <QRegularExpression>
#define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE #define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE
ToxFile::ToxFile(int FileNum, int FriendId, QByteArray FileName, QString FilePath, FileDirection Direction) ToxFile::ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString FilePath, FileDirection Direction)
: fileNum(FileNum), friendId(FriendId), fileName{FileName}, filePath{FilePath}, file{new QFile(filePath)}, : fileKind{TOX_FILE_KIND_DATA}, fileNum(FileNum), friendId(FriendId), fileName{FileName},
bytesSent{0}, filesize{0}, status{STOPPED}, direction{Direction}, sendTimer{nullptr} filePath{FilePath}, file{new QFile(filePath)}, bytesSent{0}, filesize{0},
status{STOPPED}, direction{Direction}, sendTimer{nullptr}
{ {
} }
@ -78,5 +79,5 @@ void ToxID::clear()
bool ToxID::isToxId(const QString& value) bool ToxID::isToxId(const QString& value)
{ {
const QRegularExpression hexRegExp("^[A-Fa-f0-9]+$"); const QRegularExpression hexRegExp("^[A-Fa-f0-9]+$");
return value.length() == TOX_ID_LENGTH && value.contains(hexRegExp); return value.length() == TOX_HEX_ID_LENGTH && value.contains(hexRegExp);
} }

View File

@ -5,6 +5,7 @@
// They should include this file directly instead to reduce compilation times // They should include this file directly instead to reduce compilation times
#include <QString> #include <QString>
#include <memory>
class QFile; class QFile;
class QTimer; class QTimer;
@ -58,7 +59,7 @@ struct ToxFile
}; };
ToxFile()=default; ToxFile()=default;
ToxFile(int FileNum, int FriendId, QByteArray FileName, QString FilePath, FileDirection Direction); ToxFile(uint32_t FileNum, uint32_t FriendId, QByteArray FileName, QString FilePath, FileDirection Direction);
~ToxFile(){} ~ToxFile(){}
bool operator==(const ToxFile& other) const; bool operator==(const ToxFile& other) const;
@ -67,16 +68,19 @@ struct ToxFile
void setFilePath(QString path); void setFilePath(QString path);
bool open(bool write); bool open(bool write);
int fileNum; uint8_t fileKind; ///< Data file (default) or avatar
int friendId; uint32_t fileNum;
uint32_t friendId;
QByteArray fileName; QByteArray fileName;
QString filePath; QString filePath;
QFile* file; std::shared_ptr<QFile> file;
qint64 bytesSent; quint64 bytesSent;
qint64 filesize; quint64 filesize;
FileStatus status; FileStatus status;
FileDirection direction; FileDirection direction;
QTimer* sendTimer; QTimer* sendTimer;
QByteArray avatarData;
QByteArray resumeFileId;
}; };
#endif // CORESTRUCTS_H #endif // CORESTRUCTS_H

View File

@ -19,10 +19,10 @@
#include "widget/friendwidget.h" #include "widget/friendwidget.h"
#include "widget/form/chatform.h" #include "widget/form/chatform.h"
#include "widget/gui.h" #include "widget/gui.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
Friend::Friend(int FriendId, const ToxID &UserId) Friend::Friend(uint32_t FriendId, const ToxID &UserId)
: userName{Core::getInstance()->getPeerName(UserId)}, : userName{Core::getInstance()->getPeerName(UserId)},
userID{UserId}, friendId{FriendId} userID{UserId}, friendId{FriendId}
{ {
@ -35,11 +35,6 @@ Friend::Friend(int FriendId, const ToxID &UserId)
widget = new FriendWidget(friendId, getDisplayedName()); widget = new FriendWidget(friendId, getDisplayedName());
chatForm = new ChatForm(this); chatForm = new ChatForm(this);
if (Settings::getInstance().getEnableLogging())
{
chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
widget->historyLoaded = true;
}
} }
Friend::~Friend() Friend::~Friend()
@ -48,6 +43,15 @@ Friend::~Friend()
delete widget; delete widget;
} }
void Friend::loadHistory()
{
if (Settings::getInstance().getEnableLogging())
{
chatForm->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
widget->historyLoaded = true;
}
}
void Friend::setName(QString name) void Friend::setName(QString name)
{ {
userName = name; userName = name;
@ -87,6 +91,7 @@ QString Friend::getDisplayedName() const
{ {
if (userAlias.size() == 0) if (userAlias.size() == 0)
return userName; return userName;
return userAlias; return userAlias;
} }
@ -95,7 +100,7 @@ const ToxID &Friend::getToxID() const
return userID; return userID;
} }
int Friend::getFriendID() const uint32_t Friend::getFriendID() const
{ {
return friendId; return friendId;
} }

View File

@ -19,7 +19,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include "corestructs.h" #include "src/core/corestructs.h"
struct FriendWidget; struct FriendWidget;
class ChatForm; class ChatForm;
@ -28,11 +28,14 @@ class Friend : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Friend(int FriendId, const ToxID &UserId); Friend(uint32_t FriendId, const ToxID &UserId);
Friend(const Friend& other)=delete; Friend(const Friend& other)=delete;
~Friend(); ~Friend();
Friend& operator=(const Friend& other)=delete; Friend& operator=(const Friend& other)=delete;
/// Loads the friend's chat history if enabled
void loadHistory();
void setName(QString name); void setName(QString name);
void setAlias(QString name); void setAlias(QString name);
QString getDisplayedName() const; QString getDisplayedName() const;
@ -43,7 +46,7 @@ public:
int getEventFlag() const; int getEventFlag() const;
const ToxID &getToxID() const; const ToxID &getToxID() const;
int getFriendID() const; uint32_t getFriendID() const;
void setStatus(Status s); void setStatus(Status s);
Status getStatus() const; Status getStatus() const;
@ -57,7 +60,7 @@ signals:
private: private:
QString userAlias, userName; QString userAlias, userName;
ToxID userID; ToxID userID;
int friendId; uint32_t friendId;
int hasNewEvents; int hasNewEvents;
Status friendStatus; Status friendStatus;

View File

@ -34,6 +34,10 @@ Friend* FriendList::addFriend(int friendId, const ToxID& userId)
friendList[friendId] = newfriend; friendList[friendId] = newfriend;
tox2id[userId.publicKey] = friendId; tox2id[userId.publicKey] = friendId;
// Must be done AFTER adding to the friendlist
// or we won't find the friend and history will have blank names
newfriend->loadHistory();
return newfriend; return newfriend;
} }

View File

@ -19,7 +19,7 @@
template <class T> class QList; template <class T> class QList;
template <class A, class B> class QHash; template <class A, class B> class QHash;
struct Friend; class Friend;
class QString; class QString;
struct ToxID; struct ToxID;

View File

@ -19,7 +19,7 @@
#include "widget/form/groupchatform.h" #include "widget/form/groupchatform.h"
#include "friendlist.h" #include "friendlist.h"
#include "friend.h" #include "friend.h"
#include "core.h" #include "src/core/core.h"
#include "widget/gui.h" #include "widget/gui.h"
#include <QDebug> #include <QDebug>
#include <QTimer> #include <QTimer>
@ -102,6 +102,7 @@ void Group::regeneratePeerList()
ToxID id = Core::getInstance()->getGroupPeerToxID(groupId, i); ToxID id = Core::getInstance()->getGroupPeerToxID(groupId, i);
if (id.isMine()) if (id.isMine())
selfPeerNum = i; selfPeerNum = i;
QString toxid = id.publicKey; QString toxid = id.publicKey;
toxids[toxid] = peers[i]; toxids[toxid] = peers[i];
Friend *f = FriendList::findFriend(id); Friend *f = FriendList::findFriend(id);
@ -177,9 +178,7 @@ QString Group::resolveToxID(const ToxID &id) const
auto it = toxids.find(key); auto it = toxids.find(key);
if (it != toxids.end()) if (it != toxids.end())
{
return *it; return *it;
}
return QString(); return QString();
} }

View File

@ -23,7 +23,7 @@
#define RETRY_PEER_INFO_INTERVAL 500 #define RETRY_PEER_INFO_INTERVAL 500
struct Friend; class Friend;
class GroupWidget; class GroupWidget;
class GroupChatForm; class GroupChatForm;
struct ToxID; struct ToxID;

View File

@ -16,7 +16,7 @@
#include "historykeeper.h" #include "historykeeper.h"
#include "misc/settings.h" #include "misc/settings.h"
#include "core.h" #include "src/core/core.h"
#include <QSqlError> #include <QSqlError>
#include <QFile> #include <QFile>
@ -56,7 +56,9 @@ HistoryKeeper *HistoryKeeper::getInstance()
historyInstance = new HistoryKeeper(dbIntf); historyInstance = new HistoryKeeper(dbIntf);
return historyInstance; return historyInstance;
} else { }
else
{
path = getHistoryPath(); path = getHistoryPath();
} }
} }
@ -111,9 +113,7 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
QSqlQuery ret = db->exec("SELECT seq FROM sqlite_sequence WHERE name=\"sent_status\";"); QSqlQuery ret = db->exec("SELECT seq FROM sqlite_sequence WHERE name=\"sent_status\";");
int idCur = 0; int idCur = 0;
if (ret.first()) if (ret.first())
{
idCur = ret.value(0).toInt(); idCur = ret.value(0).toInt();
}
if (idCur != idMax) if (idCur != idMax)
{ {
@ -145,6 +145,7 @@ qint64 HistoryKeeper::addChatEntry(const QString& chat, const QString& message,
db->exec("BEGIN TRANSACTION;"); db->exec("BEGIN TRANSACTION;");
for (auto &it : cmds) for (auto &it : cmds)
db->exec(it); db->exec(it);
db->exec("COMMIT TRANSACTION;"); db->exec("COMMIT TRANSACTION;");
messageID++; messageID++;
@ -167,7 +168,9 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::C
dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") + dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") +
QString("INNER JOIN aliases ON history.sender = aliases.id AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;") QString("INNER JOIN aliases ON history.sender = aliases.id AND timestamp BETWEEN %1 AND %2 AND chat_id = %3;")
.arg(time64_from).arg(time64_to).arg(chat_id)); .arg(time64_from).arg(time64_to).arg(chat_id));
} else { }
else
{
// no groupchats yet // no groupchats yet
} }
@ -372,7 +375,8 @@ void HistoryKeeper::setSyncType(Db::syncType sType)
{ {
QString syncCmd; QString syncCmd;
switch (sType) { switch (sType)
{
case Db::syncType::stFull: case Db::syncType::stFull:
syncCmd = "FULL"; syncCmd = "FULL";
break; break;
@ -413,5 +417,6 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessagesDeleteFile(int en
qDebug() << "HistoryKeeper: count" << msgs.size() << "messages exported"; qDebug() << "HistoryKeeper: count" << msgs.size() << "messages exported";
if (!removeHistory(encrypted)) if (!removeHistory(encrypted))
qWarning() << "HistoryKeeper: couldn't delete old log file!"; qWarning() << "HistoryKeeper: couldn't delete old log file!";
return msgs; return msgs;
} }

View File

@ -134,7 +134,14 @@ bool IPC::isCurrentOwner()
{ {
if (globalMemory.lock()) if (globalMemory.lock())
{ {
bool isOwner = ((*(uint64_t*)globalMemory.data()) == globalId); void* data = globalMemory.data();
if (!data)
{
qWarning() << "IPC: isCurrentOwner failed to access the memory, returning false";
globalMemory.unlock();
return false;
}
bool isOwner = ((*(uint64_t*)data) == globalId);
globalMemory.unlock(); globalMemory.unlock();
return isOwner; return isOwner;
} }

View File

@ -21,6 +21,7 @@
#include "src/widget/toxuri.h" #include "src/widget/toxuri.h"
#include "src/widget/toxsave.h" #include "src/widget/toxsave.h"
#include "src/autoupdate.h" #include "src/autoupdate.h"
#include "src/profilelocker.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QDateTime> #include <QDateTime>
@ -82,6 +83,8 @@ int main(int argc, char *argv[])
a.setAttribute(Qt::AA_UseHighDpiPixmaps, true); a.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
#endif #endif
qsrand(time(0));
// Process arguments // Process arguments
QCommandLineParser parser; QCommandLineParser parser;
parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: " + __TIME__ + " " + __DATE__); parser.setApplicationDescription("qTox, version: " + QString(GIT_VERSION) + "\nBuilt: " + __TIME__ + " " + __DATE__);
@ -213,6 +216,13 @@ int main(int argc, char *argv[])
ipc.registerEventHandler("save", &toxSaveEventHandler); ipc.registerEventHandler("save", &toxSaveEventHandler);
ipc.registerEventHandler("activate", &toxActivateEventHandler); ipc.registerEventHandler("activate", &toxActivateEventHandler);
// If we're the IPC owner and we just started, then
// either we're the only running instance or any other instance
// is already so frozen it lost ownership.
// It's safe to remove any potential stale locks in this situation.
if (ipc.isCurrentOwner())
ProfileLocker::clearAllLocks();
if (parser.positionalArguments().size() > 0) if (parser.positionalArguments().size() > 0)
{ {
QString firstParam(parser.positionalArguments()[0]); QString firstParam(parser.positionalArguments()[0]);
@ -254,7 +264,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
else if (!ipc.isCurrentOwner()) else if (!ipc.isCurrentOwner() && !parser.isSet("p"))
{ {
uint32_t dest = 0; uint32_t dest = 0;
if (parser.isSet("p")) if (parser.isSet("p"))

View File

@ -72,7 +72,7 @@ QString CUserId::toString(const uint8_t* cUserId)
// CFriendAddress // CFriendAddress
const uint16_t CFriendAddress::SIZE{TOX_FRIEND_ADDRESS_SIZE}; const uint16_t CFriendAddress::SIZE{TOX_ADDRESS_SIZE};
CFriendAddress::CFriendAddress(const QString &friendAddress) : CFriendAddress::CFriendAddress(const QString &friendAddress) :
CData(friendAddress, SIZE) CData(friendAddress, SIZE)

View File

@ -16,7 +16,7 @@
#include "encrypteddb.h" #include "encrypteddb.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/core.h" #include "src/core/core.h"
#include <tox/toxencryptsave.h> #include <tox/toxencryptsave.h>
@ -25,7 +25,7 @@
#include <QSqlError> #include <QSqlError>
qint64 EncryptedDb::encryptedChunkSize = 4096; qint64 EncryptedDb::encryptedChunkSize = 4096;
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - tox_pass_encryption_extra_length(); qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) : EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
PlainDb(":memory:", initList), fileName(fname) PlainDb(":memory:", initList), fileName(fname)
@ -41,7 +41,9 @@ EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
encrFile.close(); encrFile.close();
} }
else else
{
chunkPosition = 0; chunkPosition = 0;
}
encrFile.setFileName(fileName); encrFile.setFileName(fileName);
@ -90,7 +92,9 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
if (buf.size() > 0) if (buf.size() > 0)
{ {
fileContent += buf; fileContent += buf;
} else { }
else
{
qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted"; qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted";
buf = QByteArray(); buf = QByteArray();
return false; return false;
@ -108,9 +112,8 @@ bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
PlainDb::exec("BEGIN TRANSACTION;"); PlainDb::exec("BEGIN TRANSACTION;");
for (auto line : sqlCmds) for (auto line : sqlCmds)
{
QSqlQuery r = PlainDb::exec(line); QSqlQuery r = PlainDb::exec(line);
}
PlainDb::exec("COMMIT TRANSACTION;"); PlainDb::exec("COMMIT TRANSACTION;");
dbFile.close(); dbFile.close();
@ -144,9 +147,8 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
QByteArray encr = Core::getInstance()->encryptData(buffer, Core::ptHistory); QByteArray encr = Core::getInstance()->encryptData(buffer, Core::ptHistory);
if (encr.size() > 0) if (encr.size() > 0)
{
encrFile.write(encr); encrFile.write(encr);
}
encrFile.flush(); encrFile.flush();
} }
@ -161,10 +163,10 @@ bool EncryptedDb::check(const QString &fname)
QByteArray encrChunk = file.read(encryptedChunkSize); QByteArray encrChunk = file.read(encryptedChunkSize);
QByteArray buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory); QByteArray buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
if (buf.size() == 0) if (buf.size() == 0)
{
state = false; state = false;
} }
} else { else
{
file.close(); file.close();
file.open(QIODevice::WriteOnly); file.open(QIODevice::WriteOnly);
} }

View File

@ -154,18 +154,18 @@ QByteArray rangedSingleToData(float value, float min, float max, int numberOfBit
numberOfBits -= 8; numberOfBits -= 8;
if (numberOfBits <= 8) if (numberOfBits <= 8)
{ {
data += (unsigned char)source>>8; data += (unsigned char)(source>>8);
return data; return data;
} }
data += (unsigned char)source>>8; data += (unsigned char)(source>>8);
numberOfBits -= 8; numberOfBits -= 8;
if (numberOfBits <= 8) if (numberOfBits <= 8)
{ {
data += (unsigned char)source>>16; data += (unsigned char)(source>>16);
return data; return data;
} }
data += (unsigned char)source>>16; data += (unsigned char)(source>>16);
data += (unsigned char)source>>24; data += (unsigned char)(source>>24);
return data; return data;
} }

View File

@ -16,10 +16,11 @@
#include "settings.h" #include "settings.h"
#include "smileypack.h" #include "smileypack.h"
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include "src/misc/db/plaindb.h" #include "src/misc/db/plaindb.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/profilelocker.h"
#ifdef QTOX_PLATFORM_EXT #ifdef QTOX_PLATFORM_EXT
#include "src/platform/autorun.h" #include "src/platform/autorun.h"
#endif #endif
@ -54,6 +55,7 @@ Settings& Settings::getInstance()
{ {
if (!settings) if (!settings)
settings = new Settings(); settings = new Settings();
return *settings; return *settings;
} }
@ -66,9 +68,23 @@ void Settings::switchProfile(const QString& profile)
// If this instance is not main instance previous save did not happen therefore // If this instance is not main instance previous save did not happen therefore
// we manually set profile again and load profile settings // we manually set profile again and load profile settings
setCurrentProfile(profile); setCurrentProfile(profile);
loaded = false;
load(); load();
} }
QString Settings::genRandomProfileName()
{
QDir dir(getSettingsDirPath());
QString basename = "imported_";
QString randname;
do {
randname = QString().setNum(qrand()*qrand()*qrand(), 16);
randname.truncate(6);
randname = basename + randname;
} while (QFile(dir.filePath(randname)).exists());
return randname;
}
QString Settings::detectProfile() QString Settings::detectProfile()
{ {
QDir dir(getSettingsDirPath()); QDir dir(getSettingsDirPath());
@ -83,15 +99,27 @@ QString Settings::detectProfile()
path = dir.filePath(Core::CONFIG_FILE_NAME); path = dir.filePath(Core::CONFIG_FILE_NAME);
QFile file(path); QFile file(path);
if (file.exists()) if (file.exists())
return path; {
profile = genRandomProfileName();
setCurrentProfile(profile);
file.rename(profile + Core::TOX_EXT);
return profile;
}
else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
return path; {
profile = genRandomProfileName();
setCurrentProfile(profile);
QFile(path).rename(profile + Core::TOX_EXT);
return profile;
}
else else
#endif #endif
{ {
profile = askProfiles(); profile = askProfiles();
if (profile.isEmpty()) if (profile.isEmpty())
{
return ""; return "";
}
else else
{ {
switchProfile(profile); switchProfile(profile);
@ -100,7 +128,9 @@ QString Settings::detectProfile()
} }
} }
else else
{
return path; return path;
}
} }
QList<QString> Settings::searchProfiles() QList<QString> Settings::searchProfiles()
@ -111,6 +141,7 @@ QList<QString> Settings::searchProfiles()
dir.setNameFilters(QStringList("*.tox")); dir.setNameFilters(QStringList("*.tox"));
for (QFileInfo file : dir.entryInfoList()) for (QFileInfo file : dir.entryInfoList())
out += file.completeBaseName(); out += file.completeBaseName();
return out; return out;
} }
@ -153,7 +184,9 @@ void Settings::load()
ps.endGroup(); ps.endGroup();
} }
else else
{
makeToxPortable = false; makeToxPortable = false;
}
QDir dir(getSettingsDirPath()); QDir dir(getSettingsDirPath());
QString filePath = dir.filePath(FILENAME); QString filePath = dir.filePath(FILENAME);
@ -177,7 +210,8 @@ void Settings::load()
useCustomDhtList = true; useCustomDhtList = true;
qDebug() << "Using custom bootstrap nodes list"; qDebug() << "Using custom bootstrap nodes list";
int serverListSize = s.beginReadArray("dhtServerList"); int serverListSize = s.beginReadArray("dhtServerList");
for (int i = 0; i < serverListSize; i ++) { for (int i = 0; i < serverListSize; i ++)
{
s.setArrayIndex(i); s.setArrayIndex(i);
DhtServer server; DhtServer server;
server.name = s.value("name").toString(); server.name = s.value("name").toString();
@ -189,7 +223,9 @@ void Settings::load()
s.endArray(); s.endArray();
} }
else else
{
useCustomDhtList=false; useCustomDhtList=false;
}
s.endGroup(); s.endGroup();
s.beginGroup("General"); s.beginGroup("General");
@ -203,8 +239,11 @@ void Settings::load()
setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt()); setProxyType(s.value("proxyType", static_cast<int>(ProxyType::ptNone)).toInt());
proxyAddr = s.value("proxyAddr", "").toString(); proxyAddr = s.value("proxyAddr", "").toString();
proxyPort = s.value("proxyPort", 0).toInt(); proxyPort = s.value("proxyPort", 0).toInt();
currentProfile = s.value("currentProfile", "").toString(); if (currentProfile.isEmpty())
currentProfileId = makeProfileId(currentProfile); {
currentProfile = s.value("currentProfile", "").toString();
currentProfileId = makeProfileId(currentProfile);
}
autoAwayTime = s.value("autoAwayTime", 10).toInt(); autoAwayTime = s.value("autoAwayTime", 10).toInt();
checkUpdates = s.value("checkUpdates", false).toBool(); checkUpdates = s.value("checkUpdates", false).toBool();
showWindow = s.value("showWindow", true).toBool(); showWindow = s.value("showWindow", true).toBool();
@ -227,9 +266,9 @@ void Settings::load()
s.beginGroup("Widgets"); s.beginGroup("Widgets");
QList<QString> objectNames = s.childKeys(); QList<QString> objectNames = s.childKeys();
for (const QString& name : objectNames) { for (const QString& name : objectNames)
widgetSettings[name] = s.value(name).toByteArray(); widgetSettings[name] = s.value(name).toByteArray();
}
s.endGroup(); s.endGroup();
s.beginGroup("GUI"); s.beginGroup("GUI");
@ -282,7 +321,8 @@ void Settings::load()
QSettings rcs(":/conf/settings.ini", QSettings::IniFormat); QSettings rcs(":/conf/settings.ini", QSettings::IniFormat);
rcs.beginGroup("DHT Server"); rcs.beginGroup("DHT Server");
int serverListSize = rcs.beginReadArray("dhtServerList"); int serverListSize = rcs.beginReadArray("dhtServerList");
for (int i = 0; i < serverListSize; i ++) { for (int i = 0; i < serverListSize; i ++)
{
rcs.setArrayIndex(i); rcs.setArrayIndex(i);
DhtServer server; DhtServer server;
server.name = rcs.value("name").toString(); server.name = rcs.value("name").toString();
@ -297,7 +337,6 @@ void Settings::load()
loaded = true; loaded = true;
if (!currentProfile.isEmpty()) // new profile in Core::switchConfiguration
{ {
// load from a profile specific friend data list if possible // load from a profile specific friend data list if possible
QString tmp = dir.filePath(currentProfile + ".ini"); QString tmp = dir.filePath(currentProfile + ".ini");
@ -357,7 +396,8 @@ void Settings::saveGlobal(QString path)
s.beginGroup("DHT Server"); s.beginGroup("DHT Server");
s.setValue("useCustomList", useCustomDhtList); s.setValue("useCustomList", useCustomDhtList);
s.beginWriteArray("dhtServerList", dhtServerList.size()); s.beginWriteArray("dhtServerList", dhtServerList.size());
for (int i = 0; i < dhtServerList.size(); i ++) { for (int i = 0; i < dhtServerList.size(); i ++)
{
s.setArrayIndex(i); s.setArrayIndex(i);
s.setValue("name", dhtServerList[i].name); s.setValue("name", dhtServerList[i].name);
s.setValue("userId", dhtServerList[i].userId); s.setValue("userId", dhtServerList[i].userId);
@ -398,9 +438,9 @@ void Settings::saveGlobal(QString path)
s.beginGroup("Widgets"); s.beginGroup("Widgets");
const QList<QString> widgetNames = widgetSettings.keys(); const QList<QString> widgetNames = widgetSettings.keys();
for (const QString& name : widgetNames) { for (const QString& name : widgetNames)
s.setValue(name, widgetSettings.value(name)); s.setValue(name, widgetSettings.value(name));
}
s.endGroup(); s.endGroup();
s.beginGroup("GUI"); s.beginGroup("GUI");
@ -505,12 +545,15 @@ QPixmap Settings::getSavedAvatar(const QString &ownerId)
QString filePath = dir.filePath("avatar_"+ownerId.left(64)); QString filePath = dir.filePath("avatar_"+ownerId.left(64));
if (!QFileInfo(filePath).exists()) // try without truncation, for old self avatars if (!QFileInfo(filePath).exists()) // try without truncation, for old self avatars
filePath = dir.filePath("avatar_"+ownerId); filePath = dir.filePath("avatar_"+ownerId);
pic.load(filePath); pic.load(filePath);
saveAvatar(pic, ownerId); saveAvatar(pic, ownerId);
QFile::remove(filePath); QFile::remove(filePath);
} }
else else
{
pic.load(filePath); pic.load(filePath);
}
return pic; return pic;
} }
@ -530,6 +573,7 @@ void Settings::saveAvatarHash(const QByteArray& hash, const QString& ownerId)
QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash")); QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash"));
if (!file.open(QIODevice::WriteOnly)) if (!file.open(QIODevice::WriteOnly))
return; return;
file.write(hash); file.write(hash);
file.close(); file.close();
} }
@ -541,6 +585,7 @@ QByteArray Settings::getAvatarHash(const QString& ownerId)
QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash")); QFile file(dir.filePath("avatars/"+ownerId.left(64)+".hash"));
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly))
return QByteArray(); return QByteArray();
QByteArray out = file.readAll(); QByteArray out = file.readAll();
file.close(); file.close();
return out; return out;
@ -840,6 +885,7 @@ void Settings::setAutoAwayTime(int newValue)
{ {
if (newValue < 0) if (newValue < 0)
newValue = 10; newValue = 10;
autoAwayTime = newValue; autoAwayTime = newValue;
} }
@ -849,9 +895,7 @@ QString Settings::getAutoAcceptDir(const ToxID& id) const
auto it = friendLst.find(key); auto it = friendLst.find(key);
if (it != friendLst.end()) if (it != friendLst.end())
{
return it->autoAcceptDir; return it->autoAcceptDir;
}
return QString(); return QString();
} }
@ -1110,9 +1154,7 @@ QString Settings::getFriendAdress(const QString &publicKey) const
QString key = ToxID::fromString(publicKey).publicKey; QString key = ToxID::fromString(publicKey).publicKey;
auto it = friendLst.find(key); auto it = friendLst.find(key);
if (it != friendLst.end()) if (it != friendLst.end())
{
return it->addr; return it->addr;
}
return QString(); return QString();
} }
@ -1124,7 +1166,9 @@ void Settings::updateFriendAdress(const QString &newAddr)
if (it != friendLst.end()) if (it != friendLst.end())
{ {
it->addr = newAddr; it->addr = newAddr;
} else { }
else
{
friendProp fp; friendProp fp;
fp.addr = newAddr; fp.addr = newAddr;
fp.alias = ""; fp.alias = "";
@ -1138,9 +1182,7 @@ QString Settings::getFriendAlias(const ToxID &id) const
QString key = id.publicKey; QString key = id.publicKey;
auto it = friendLst.find(key); auto it = friendLst.find(key);
if (it != friendLst.end()) if (it != friendLst.end())
{
return it->alias; return it->alias;
}
return QString(); return QString();
} }
@ -1152,7 +1194,9 @@ void Settings::setFriendAlias(const ToxID &id, const QString &alias)
if (it != friendLst.end()) if (it != friendLst.end())
{ {
it->alias = alias; it->alias = alias;
} else { }
else
{
friendProp fp; friendProp fp;
fp.addr = key; fp.addr = key;
fp.alias = alias; fp.alias = alias;

View File

@ -256,6 +256,9 @@ public:
void save(QString path, bool writePersonal = true); void save(QString path, bool writePersonal = true);
void load(); void load();
private:
static QString genRandomProfileName();
private: private:
static Settings* settings; static Settings* settings;

View File

@ -1,5 +1,5 @@
#include "nexus.h" #include "nexus.h"
#include "core.h" #include "src/core/core.h"
#include "misc/settings.h" #include "misc/settings.h"
#include "video/camera.h" #include "video/camera.h"
#include "widget/gui.h" #include "widget/gui.h"
@ -40,6 +40,7 @@ void Nexus::start()
{ {
if (started) if (started)
return; return;
qDebug() << "Nexus: Starting up"; qDebug() << "Nexus: Starting up";
// Setup the environment // Setup the environment
@ -47,6 +48,7 @@ void Nexus::start()
qRegisterMetaType<vpx_image>("vpx_image"); qRegisterMetaType<vpx_image>("vpx_image");
qRegisterMetaType<uint8_t>("uint8_t"); qRegisterMetaType<uint8_t>("uint8_t");
qRegisterMetaType<uint16_t>("uint16_t"); qRegisterMetaType<uint16_t>("uint16_t");
qRegisterMetaType<uint32_t>("uint32_t");
qRegisterMetaType<const int16_t*>("const int16_t*"); qRegisterMetaType<const int16_t*>("const int16_t*");
qRegisterMetaType<int32_t>("int32_t"); qRegisterMetaType<int32_t>("int32_t");
qRegisterMetaType<int64_t>("int64_t"); qRegisterMetaType<int64_t>("int64_t");
@ -77,6 +79,12 @@ void Nexus::start()
#endif #endif
GUI::getInstance(); GUI::getInstance();
// Zetok protection
// There are small instants on startup during which no
// profile is loaded but the GUI could still receive events,
// e.g. between two modal windows. Disable the GUI to prevent that.
GUI::setEnabled(false);
// Connections // Connections
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected); connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected);
@ -119,8 +127,8 @@ void Nexus::start()
connect(core, &Core::blockingClearContacts, widget, &Widget::clearContactsList, Qt::BlockingQueuedConnection); connect(core, &Core::blockingClearContacts, widget, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged); connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged);
connect(core, SIGNAL(messageSentResult(int,QString,int)), widget, SLOT(onMessageSendResult(int,QString,int))); connect(core, &Core::messageSentResult, widget, &Widget::onMessageSendResult);
connect(core, SIGNAL(groupSentResult(int,QString,int)), widget, SLOT(onGroupSendResult(int,QString,int))); connect(core, &Core::groupSentResult, widget, &Widget::onGroupSendResult);
connect(widget, &Widget::statusSet, core, &Core::setStatus); connect(widget, &Widget::statusSet, core, &Core::setStatus);
connect(widget, &Widget::friendRequested, core, &Core::requestFriendship); connect(widget, &Widget::friendRequested, core, &Core::requestFriendship);
@ -138,6 +146,7 @@ Nexus& Nexus::getInstance()
{ {
if (!nexus) if (!nexus)
nexus = new Nexus; nexus = new Nexus;
return *nexus; return *nexus;
} }
@ -167,10 +176,11 @@ QString Nexus::getSupportedImageFilter()
QString res; QString res;
for (auto type : QImageReader::supportedImageFormats()) for (auto type : QImageReader::supportedImageFormats())
res += QString("*.%1 ").arg(QString(type)); res += QString("*.%1 ").arg(QString(type));
return tr("Images (%1)", "filetype filter").arg(res.left(res.size()-1)); return tr("Images (%1)", "filetype filter").arg(res.left(res.size()-1));
} }
bool Nexus::isFilePathWritable(const QString& filepath) bool Nexus::tryRemoveFile(const QString& filepath)
{ {
QFile tmp(filepath); QFile tmp(filepath);
bool writable = tmp.open(QIODevice::WriteOnly); bool writable = tmp.open(QIODevice::WriteOnly);

View File

@ -23,7 +23,7 @@ public:
static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
static Widget* getDesktopGUI(); ///< Will return 0 if not started static Widget* getDesktopGUI(); ///< Will return 0 if not started
static QString getSupportedImageFilter(); static QString getSupportedImageFilter();
static bool isFilePathWritable(const QString& filepath); // WARNING: Tests by brute force, i.e. removes the file in question static bool tryRemoveFile(const QString& filepath); ///< Dangerous way to find out if a path is writable
private: private:
explicit Nexus(QObject *parent = 0); explicit Nexus(QObject *parent = 0);

View File

@ -18,7 +18,7 @@
#include "src/friend.h" #include "src/friend.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/core.h" #include "src/core/core.h"
#include <QMutexLocker> #include <QMutexLocker>
#include <QTimer> #include <QTimer>
@ -91,6 +91,7 @@ void OfflineMsgEngine::deliverOfflineMsgs()
rec = Core::getInstance()->sendAction(f->getFriendID(), messageText); rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
else else
rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText); rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
registerReceipt(rec, iter.key(), iter.value().msg); registerReceipt(rec, iter.key(), iter.value().msg);
} }
} }

View File

@ -24,7 +24,7 @@
#include <QMap> #include <QMap>
#include "src/chatlog/chatmessage.h" #include "src/chatlog/chatmessage.h"
struct Friend; class Friend;
class QTimer; class QTimer;
class OfflineMsgEngine : public QObject class OfflineMsgEngine : public QObject

99
src/profilelocker.cpp Normal file
View File

@ -0,0 +1,99 @@
#include "profilelocker.h"
#include "src/misc/settings.h"
#include <QDir>
#include <QDebug>
using namespace std;
unique_ptr<QLockFile> ProfileLocker::lockfile;
QString ProfileLocker::curLockName;
QString ProfileLocker::lockPathFromName(const QString& name)
{
return Settings::getInstance().getSettingsDirPath()+'/'+name+".lock";
}
bool ProfileLocker::isLockable(QString profile)
{
// If we already have the lock, it's definitely lockable
if (lockfile && curLockName == profile)
return true;
QLockFile newLock(lockPathFromName(profile));
return newLock.tryLock();
}
bool ProfileLocker::lock(QString profile)
{
if (lockfile && curLockName == profile)
return true;
QLockFile* newLock = new QLockFile(lockPathFromName(profile));
if (!newLock->tryLock())
{
delete newLock;
return false;
}
unlock();
lockfile.reset(newLock);
curLockName = profile;
return true;
}
void ProfileLocker::unlock()
{
if (!lockfile)
return;
lockfile->unlock();
delete lockfile.release();
lockfile = nullptr;
curLockName.clear();
}
void ProfileLocker::clearAllLocks()
{
qDebug() << "ProfileLocker::clearAllLocks: Wiping out all lock files";
if (lockfile)
unlock();
QDir dir(Settings::getInstance().getSettingsDirPath());
dir.setFilter(QDir::Files);
dir.setNameFilters({"*.lock"});
QFileInfoList files = dir.entryInfoList();
for (QFileInfo fileInfo : files)
{
QFile file(fileInfo.absoluteFilePath());
file.remove();
}
}
void ProfileLocker::assertLock()
{
if (!lockfile)
{
qCritical() << "ProfileLocker::assertLock: We don't seem to own any lock!";
deathByBrokenLock();
}
if (!QFile(lockPathFromName(curLockName)).exists())
{
QString tmp = curLockName;
unlock();
if (lock(tmp))
{
qCritical() << "ProfileLocker::assertLock: Lock file was lost, but could be restored";
}
else
{
qCritical() << "ProfileLocker::assertLock: Lock file was lost, and could *NOT* be restored";
deathByBrokenLock();
}
}
}
void ProfileLocker::deathByBrokenLock()
{
qCritical() << "ProfileLocker: Lock is *BROKEN*, exiting immediately";
abort();
}

46
src/profilelocker.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef PROFILELOCKER_H
#define PROFILELOCKER_H
#include <QLockFile>
#include <memory>
/// Locks a Tox profile so that multiple instances can not use the same profile.
/// Only one lock can be acquired at the same time, which means
/// that there is little need for manually unlocking.
/// The current lock will expire if you exit or acquire a new one.
class ProfileLocker
{
private:
ProfileLocker()=delete;
public:
/// Checks if a profile is currently locked by *another* instance
/// If we own the lock, we consider it lockable
/// There is no guarantee that the result will still be valid by the
/// time it is returned, this is provided on a best effort basis
static bool isLockable(QString profile);
/// Tries to acquire the lock on a profile, will not block
/// Returns true if we already own the lock
static bool lock(QString profile);
/// Releases the lock on the current profile
static void unlock();
/// Releases all locks on all profiles
/// DO NOT call unless all we're the only qTox instance
/// and we don't hold any lock yet.
static void clearAllLocks();
/// Check that we actually own the lock
/// In case the file was deleted on disk, restore it
/// If we can't get a lock, exit qTox immediately
/// If we never had a lock in the firt place, exit immediately
static void assertLock();
private:
static QString lockPathFromName(const QString& name);
static void deathByBrokenLock(); ///< Print an error then exit immediately
private:
static std::unique_ptr<QLockFile> lockfile;
static QString curLockName;
};
#endif // PROFILELOCKER_H

View File

@ -24,7 +24,7 @@
#include <tox/tox.h> #include <tox/tox.h>
#include <tox/toxdns.h> #include <tox/toxdns.h>
#define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE #define TOX_HEX_ID_LENGTH 2*TOX_ADDRESS_SIZE
const ToxDNS::tox3_server ToxDNS::pinnedServers[] const ToxDNS::tox3_server ToxDNS::pinnedServers[]
{ {
@ -58,35 +58,45 @@ QByteArray ToxDNS::fetchLastTextRecord(const QString& record, bool silent)
qApp->processEvents(); qApp->processEvents();
QThread::msleep(100); QThread::msleep(100);
} }
if (timeout >= 30) { if (timeout >= 30)
{
dns.abort(); dns.abort();
if (!silent) if (!silent)
showWarning(tr("The connection timed out","The DNS gives the Tox ID associated to toxme.se addresses")); showWarning(tr("The connection timed out","The DNS gives the Tox ID associated to toxme.se addresses"));
return result; return result;
} }
if (dns.error() == QDnsLookup::NotFoundError) { if (dns.error() == QDnsLookup::NotFoundError)
{
if (!silent) if (!silent)
showWarning(tr("This address does not exist","The DNS gives the Tox ID associated to toxme.se addresses")); showWarning(tr("This address does not exist","The DNS gives the Tox ID associated to toxme.se addresses"));
return result; return result;
} }
else if (dns.error() != QDnsLookup::NoError) { else if (dns.error() != QDnsLookup::NoError)
{
if (!silent) if (!silent)
showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses")); showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses"));
return result; return result;
} }
const QList<QDnsTextRecord> textRecords = dns.textRecords(); const QList<QDnsTextRecord> textRecords = dns.textRecords();
if (textRecords.isEmpty()) { if (textRecords.isEmpty())
{
if (!silent) if (!silent)
showWarning(tr("No text record found", "Error with the DNS")); showWarning(tr("No text record found", "Error with the DNS"));
return result; return result;
} }
const QList<QByteArray> textRecordValues = textRecords.last().values(); const QList<QByteArray> textRecordValues = textRecords.last().values();
if (textRecordValues.length() != 1) { if (textRecordValues.length() != 1)
{
if (!silent) if (!silent)
showWarning(tr("Unexpected number of values in text record", "Error with the DNS")); showWarning(tr("Unexpected number of values in text record", "Error with the DNS"));
return result; return result;
} }
@ -104,7 +114,8 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
// Check toxdns protocol version // Check toxdns protocol version
int verx = entry.indexOf("v="); int verx = entry.indexOf("v=");
if (verx) { if (verx)
{
verx += 2; verx += 2;
int verend = entry.indexOf(';', verx); int verend = entry.indexOf(';', verx);
if (verend) if (verend)
@ -114,6 +125,7 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
{ {
if (!silent) if (!silent)
showWarning(tr("The version of Tox DNS used by this server is not supported", "Error with the DNS")); showWarning(tr("The version of Tox DNS used by this server is not supported", "Error with the DNS"));
return toxId; return toxId;
} }
} }
@ -121,23 +133,29 @@ QString ToxDNS::queryTox1(const QString& record, bool silent)
// Get the tox id // Get the tox id
int idx = entry.indexOf("id="); int idx = entry.indexOf("id=");
if (idx < 0) { if (idx < 0)
{
if (!silent) if (!silent)
showWarning(tr("The DNS lookup does not contain any Tox ID", "Error with the DNS")); showWarning(tr("The DNS lookup does not contain any Tox ID", "Error with the DNS"));
return toxId; return toxId;
} }
idx += 3; idx += 3;
if (entry.length() < idx + static_cast<int>(TOX_ID_LENGTH)) { if (entry.length() < idx + static_cast<int>(TOX_HEX_ID_LENGTH))
{
if (!silent) if (!silent)
showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS")); showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS"));
return toxId; return toxId;
} }
toxId = entry.mid(idx, TOX_ID_LENGTH); toxId = entry.mid(idx, TOX_HEX_ID_LENGTH);
if (!ToxID::isToxId(toxId)) { if (!ToxID::isToxId(toxId))
{
if (!silent) if (!silent)
showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS")); showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS"));
return toxId; return toxId;
} }
@ -178,7 +196,8 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
// Check toxdns protocol version // Check toxdns protocol version
verx = entry.indexOf("v="); verx = entry.indexOf("v=");
if (verx!=-1) { if (verx!=-1)
{
verx += 2; verx += 2;
int verend = entry.indexOf(';', verx); int verend = entry.indexOf(';', verx);
if (verend!=-1) if (verend!=-1)
@ -194,14 +213,15 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
// Get and decrypt the tox id // Get and decrypt the tox id
idx = entry.indexOf("id="); idx = entry.indexOf("id=");
if (idx < 0) { if (idx < 0)
{
qWarning() << "queryTox3: Server "<<server.name<<" returned an empty id, using tox1 as a fallback"; qWarning() << "queryTox3: Server "<<server.name<<" returned an empty id, using tox1 as a fallback";
goto fallbackOnTox1; goto fallbackOnTox1;
} }
idx += 3; idx += 3;
id = entry.mid(idx).toUtf8(); id = entry.mid(idx).toUtf8();
uint8_t toxId[TOX_FRIEND_ADDRESS_SIZE]; uint8_t toxId[TOX_ADDRESS_SIZE];
toxIdSize = tox_decrypt_dns3_TXT(tox_dns3, toxId, (uint8_t*)id.data(), id.size(), request_id); toxIdSize = tox_decrypt_dns3_TXT(tox_dns3, toxId, (uint8_t*)id.data(), id.size(), request_id);
if (toxIdSize < 0) // We can always fallback on tox1 if toxdns3 fails if (toxIdSize < 0) // We can always fallback on tox1 if toxdns3 fails
{ {
@ -217,6 +237,7 @@ QString ToxDNS::queryTox3(const tox3_server& server, const QString &record, bool
fallbackOnTox1: fallbackOnTox1:
if (tox_dns3) if (tox_dns3)
tox_dns3_kill(tox_dns3); tox_dns3_kill(tox_dns3);
#if TOX1_SILENT_FALLBACK #if TOX1_SILENT_FALLBACK
toxIdStr = queryTox1(record, silent); toxIdStr = queryTox1(record, silent);
#elif TOX1_ASK_FALLBACK #elif TOX1_ASK_FALLBACK
@ -226,6 +247,7 @@ Should tox1 be used anyway?\n\
If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Yes|QMessageBox::No, QMessageBox::No); If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Yes) if (btn == QMessageBox::Yes)
queryTox1(record, silent); queryTox1(record, silent);
#endif #endif
return toxIdStr; return toxIdStr;
} }
@ -234,12 +256,17 @@ ToxID ToxDNS::resolveToxAddress(const QString &address, bool silent)
{ {
ToxID toxId; ToxID toxId;
if (address.isEmpty()) { if (address.isEmpty())
{
return toxId; return toxId;
} else if (ToxID::isToxId(address)) { }
else if (ToxID::isToxId(address))
{
toxId = ToxID::fromString(address); toxId = ToxID::fromString(address);
return toxId; return toxId;
} else { }
else
{
// If we're querying one of our pinned server, do a tox3 request directly // If we're querying one of our pinned server, do a tox3 request directly
QString servname = address.mid(address.indexOf('@')+1); QString servname = address.mid(address.indexOf('@')+1);
for (const ToxDNS::tox3_server& pin : ToxDNS::pinnedServers) for (const ToxDNS::tox3_server& pin : ToxDNS::pinnedServers)
@ -272,6 +299,7 @@ Should tox1 be used anyway?\n\
If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Ok|QMessageBox::No, QMessageBox::No); If unsure, press No, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
if (btn == QMessageBox::Ok) if (btn == QMessageBox::Ok)
toxId = ToxID::fromString(queryTox1(address, silent)); toxId = ToxID::fromString(queryTox1(address, silent));
#else #else
return toxId; return toxId;
#endif #endif

View File

@ -18,7 +18,7 @@
#ifndef QTOXDNS_H #ifndef QTOXDNS_H
#define QTOXDNS_H #define QTOXDNS_H
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include <QDnsLookup> #include <QDnsLookup>
#include <QObject> #include <QObject>

View File

@ -1,5 +1,5 @@
#include "toxme.h" #include "toxme.h"
#include "core.h" #include "src/core/core.h"
#include <QtDebug> #include <QtDebug>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkReply> #include <QNetworkReply>
@ -69,16 +69,19 @@ ToxID Toxme::lookup(QString address)
const int index = response.indexOf(pattern); const int index = response.indexOf(pattern);
if (index == -1) if (index == -1)
return id; return id;
response = response.mid(index+pattern.size()); response = response.mid(index+pattern.size());
const int idStart = response.indexOf('"'); const int idStart = response.indexOf('"');
if (idStart == -1) if (idStart == -1)
return id; return id;
response = response.mid(idStart+1); response = response.mid(idStart+1);
const int idEnd = response.indexOf('"'); const int idEnd = response.indexOf('"');
if (idEnd == -1) if (idEnd == -1)
return id; return id;
response.truncate(idEnd); response.truncate(idEnd);
id = ToxID::fromString(response); id = ToxID::fromString(response);
@ -93,17 +96,20 @@ int Toxme::extractError(QString json)
const int index = json.indexOf(pattern); const int index = json.indexOf(pattern);
if (index == -1) if (index == -1)
return INT_MIN; return INT_MIN;
json = json.mid(index+pattern.size()); json = json.mid(index+pattern.size());
const int end = json.indexOf('}'); const int end = json.indexOf('}');
if (end == -1) if (end == -1)
return INT_MIN; return INT_MIN;
json.truncate(end); json.truncate(end);
bool ok; bool ok;
int r = json.toInt(&ok); int r = json.toInt(&ok);
if (!ok) if (!ok)
return INT_MIN; return INT_MIN;
return r; return r;
} }

View File

@ -4,7 +4,7 @@
#include <QString> #include <QString>
#include <QMutex> #include <QMutex>
#include <memory> #include <memory>
#include "corestructs.h" #include "src/core/corestructs.h"
class QNetworkAccessManager; class QNetworkAccessManager;

View File

@ -37,12 +37,14 @@ CameraWorker::~CameraWorker()
void CameraWorker::onStart() void CameraWorker::onStart()
{ {
clock = new QTimer(this); if (!clock)
clock->setSingleShot(false); {
clock->setInterval(1000/60); clock = new QTimer(this);
clock->setSingleShot(false);
connect(clock, &QTimer::timeout, this, &CameraWorker::doWork); clock->setInterval(1000/60);
connect(clock, &QTimer::timeout, this, &CameraWorker::doWork);
}
emit started(); emit started();
} }

View File

@ -2,7 +2,7 @@
#include "ui_android.h" #include "ui_android.h"
#include "friendlistwidget.h" #include "friendlistwidget.h"
#include "maskablepixmapwidget.h" #include "maskablepixmapwidget.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/friend.h" #include "src/friend.h"
#include "src/friendlist.h" #include "src/friendlist.h"
#include "src/group.h" #include "src/group.h"

View File

@ -1,7 +1,7 @@
#ifndef ANDROIDGUI_H #ifndef ANDROIDGUI_H
#define ANDROIDGUI_H #define ANDROIDGUI_H
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include <QWidget> #include <QWidget>
class MaskablePixmapWidget; class MaskablePixmapWidget;

View File

@ -23,7 +23,7 @@
#include <tox/tox.h> #include <tox/tox.h>
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/cdata.h" #include "src/misc/cdata.h"
#include "src/toxdns.h" #include "src/toxdns.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
@ -87,16 +87,22 @@ void AddFriendForm::onSendTriggered()
{ {
QString id = toxId.text().trimmed(); QString id = toxId.text().trimmed();
if (id.isEmpty()) { if (id.isEmpty())
{
GUI::showWarning(tr("Couldn't add friend"), tr("Please fill in a valid Tox ID","Tox ID of the friend you're sending a friend request to")); GUI::showWarning(tr("Couldn't add friend"), tr("Please fill in a valid Tox ID","Tox ID of the friend you're sending a friend request to"));
} else if (ToxID::isToxId(id)) { }
else if (ToxID::isToxId(id))
{
if (id.toUpper() == Core::getInstance()->getSelfId().toString().toUpper()) if (id.toUpper() == Core::getInstance()->getSelfId().toString().toUpper())
GUI::showWarning(tr("Couldn't add friend"), tr("You can't add yourself as a friend!","When trying to add your own Tox ID as friend")); GUI::showWarning(tr("Couldn't add friend"), tr("You can't add yourself as a friend!","When trying to add your own Tox ID as friend"));
else else
emit friendRequested(id, getMessage()); emit friendRequested(id, getMessage());
this->toxId.clear(); this->toxId.clear();
this->message.clear(); this->message.clear();
} else { }
else
{
if (Settings::getInstance().getProxyType() != ProxyType::ptNone) if (Settings::getInstance().getProxyType() != ProxyType::ptNone)
{ {
QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\ QMessageBox::StandardButton btn = QMessageBox::warning(main, "qTox", tr("qTox needs to use the Tox DNS, but can't do it through a proxy.\n\
@ -123,7 +129,8 @@ void AddFriendForm::setIdFromClipboard()
{ {
QClipboard* clipboard = QApplication::clipboard(); QClipboard* clipboard = QApplication::clipboard();
QString id = clipboard->text().trimmed(); QString id = clipboard->text().trimmed();
if (Core::getInstance()->isReady() && !id.isEmpty() && ToxID::isToxId(id)) { if (Core::getInstance()->isReady() && !id.isEmpty() && ToxID::isToxId(id))
{
if (!ToxID::fromString(id).isMine()) if (!ToxID::fromString(id).isMine())
toxId.setText(id); toxId.setText(id);
} }

View File

@ -24,7 +24,7 @@
#include <QDragEnterEvent> #include <QDragEnterEvent>
#include <QBitmap> #include <QBitmap>
#include "chatform.h" #include "chatform.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/friend.h" #include "src/friend.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/misc/style.h" #include "src/misc/style.h"
@ -89,7 +89,10 @@ ChatForm::ChatForm(Friend* chatFriend)
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle())); connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed); connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
connect(this, SIGNAL(chatAreaCleared()), getOfflineMsgEngine(), SLOT(removeAllReciepts())); connect(this, SIGNAL(chatAreaCleared()), getOfflineMsgEngine(), SLOT(removeAllReciepts()));
connect(&typingTimer, &QTimer::timeout, this, [=]{Core::getInstance()->sendTyping(f->getFriendID(), false);}); connect(&typingTimer, &QTimer::timeout, this, [=]{
Core::getInstance()->sendTyping(f->getFriendID(), false);
isTyping = false;
} );
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) { connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) {
if (text != orig) emit aliasChanged(text); if (text != orig) emit aliasChanged(text);
} ); } );
@ -116,6 +119,8 @@ void ChatForm::onSendTriggered()
if (msg.isEmpty()) if (msg.isEmpty())
return; return;
msgEdit->setLastMessage(msg); //set last message only when sending it
bool isAction = msg.startsWith("/me "); bool isAction = msg.startsWith("/me ");
if (isAction) if (isAction)
msg = msg = msg.right(msg.length() - 4); msg = msg = msg.right(msg.length() - 4);
@ -123,8 +128,6 @@ void ChatForm::onSendTriggered()
QList<CString> splittedMsg = Core::splitMessage(msg, TOX_MAX_MESSAGE_LENGTH); QList<CString> splittedMsg = Core::splitMessage(msg, TOX_MAX_MESSAGE_LENGTH);
QDateTime timestamp = QDateTime::currentDateTime(); QDateTime timestamp = QDateTime::currentDateTime();
msgEdit->setLastMessage(msg); //set last message only when sending it
bool status = !Settings::getInstance().getFauxOfflineMessaging(); bool status = !Settings::getInstance().getFauxOfflineMessaging();
for (CString& c_msg : splittedMsg) for (CString& c_msg : splittedMsg)
@ -157,6 +160,7 @@ void ChatForm::onTextEditChanged()
{ {
if (isTyping) if (isTyping)
Core::getInstance()->sendTyping(f->getFriendID(), false); Core::getInstance()->sendTyping(f->getFriendID(), false);
isTyping = false; isTyping = false;
return; return;
} }
@ -178,6 +182,7 @@ void ChatForm::onAttachClicked()
QStringList paths = QFileDialog::getOpenFileNames(0,tr("Send a file")); QStringList paths = QFileDialog::getOpenFileNames(0,tr("Send a file"));
if (paths.isEmpty()) if (paths.isEmpty())
return; return;
for (QString path : paths) for (QString path : paths)
{ {
QFile file(path); QFile file(path);
@ -254,7 +259,7 @@ void ChatForm::onFileRecvRequest(ToxFile file)
} }
} }
void ChatForm::onAvInvite(int FriendId, int CallId, bool video) void ChatForm::onAvInvite(uint32_t FriendId, int CallId, bool video)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -269,6 +274,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
callConfirm = new CallConfirmWidget(videoButton); callConfirm = new CallConfirmWidget(videoButton);
if (isVisible()) if (isVisible())
callConfirm->show(); callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered); connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered); connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
@ -283,6 +289,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
callConfirm = new CallConfirmWidget(callButton); callConfirm = new CallConfirmWidget(callButton);
if (isVisible()) if (isVisible())
callConfirm->show(); callConfirm->show();
connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered); connect(callConfirm, &CallConfirmWidget::accepted, this, &ChatForm::onAnswerCallTriggered);
connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered); connect(callConfirm, &CallConfirmWidget::rejected, this, &ChatForm::onRejectCallTriggered);
@ -306,7 +313,7 @@ void ChatForm::onAvInvite(int FriendId, int CallId, bool video)
} }
} }
void ChatForm::onAvStart(int FriendId, int CallId, bool video) void ChatForm::onAvStart(uint32_t FriendId, int CallId, bool video)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -357,7 +364,7 @@ void ChatForm::onAvStart(int FriendId, int CallId, bool video)
startCounter(); startCounter();
} }
void ChatForm::onAvCancel(int FriendId, int) void ChatForm::onAvCancel(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -375,7 +382,7 @@ void ChatForm::onAvCancel(int FriendId, int)
addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime()); addSystemInfoMessage(tr("%1 stopped calling").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
} }
void ChatForm::onAvEnd(int FriendId, int) void ChatForm::onAvEnd(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -390,7 +397,7 @@ void ChatForm::onAvEnd(int FriendId, int)
netcam->hide(); netcam->hide();
} }
void ChatForm::onAvRinging(int FriendId, int CallId, bool video) void ChatForm::onAvRinging(uint32_t FriendId, int CallId, bool video)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -426,7 +433,7 @@ void ChatForm::onAvRinging(int FriendId, int CallId, bool video)
addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime()); addSystemInfoMessage(tr("Calling to %1").arg(f->getDisplayedName()), ChatMessage::INFO, QDateTime::currentDateTime());
} }
void ChatForm::onAvStarting(int FriendId, int CallId, bool video) void ChatForm::onAvStarting(uint32_t FriendId, int CallId, bool video)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -461,7 +468,7 @@ void ChatForm::onAvStarting(int FriendId, int CallId, bool video)
startCounter(); startCounter();
} }
void ChatForm::onAvEnding(int FriendId, int) void ChatForm::onAvEnding(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -477,7 +484,7 @@ void ChatForm::onAvEnding(int FriendId, int)
netcam->hide(); netcam->hide();
} }
void ChatForm::onAvRequestTimeout(int FriendId, int) void ChatForm::onAvRequestTimeout(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -493,7 +500,7 @@ void ChatForm::onAvRequestTimeout(int FriendId, int)
netcam->hide(); netcam->hide();
} }
void ChatForm::onAvPeerTimeout(int FriendId, int) void ChatForm::onAvPeerTimeout(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -509,7 +516,7 @@ void ChatForm::onAvPeerTimeout(int FriendId, int)
netcam->hide(); netcam->hide();
} }
void ChatForm::onAvRejected(int FriendId, int) void ChatForm::onAvRejected(uint32_t FriendId, int)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -526,7 +533,7 @@ void ChatForm::onAvRejected(int FriendId, int)
netcam->hide(); netcam->hide();
} }
void ChatForm::onAvMediaChange(int FriendId, int CallId, bool video) void ChatForm::onAvMediaChange(uint32_t FriendId, int CallId, bool video)
{ {
if (FriendId != f->getFriendID() || CallId != callId) if (FriendId != f->getFriendID() || CallId != callId)
return; return;
@ -534,13 +541,9 @@ void ChatForm::onAvMediaChange(int FriendId, int CallId, bool video)
qDebug() << "onAvMediaChange"; qDebug() << "onAvMediaChange";
if (video) if (video)
{
netcam->show(Core::getInstance()->getVideoSourceFromCall(CallId), f->getDisplayedName()); netcam->show(Core::getInstance()->getVideoSourceFromCall(CallId), f->getDisplayedName());
}
else else
{
netcam->hide(); netcam->hide();
}
} }
void ChatForm::onAnswerCallTriggered() void ChatForm::onAnswerCallTriggered()
@ -564,9 +567,7 @@ void ChatForm::onHangupCallTriggered()
//Fixes an OS X bug with ending a call while in full screen //Fixes an OS X bug with ending a call while in full screen
if(netcam->isFullScreen()) if(netcam->isFullScreen())
{
netcam->showNormal(); netcam->showNormal();
}
audioInputFlag = false; audioInputFlag = false;
audioOutputFlag = false; audioOutputFlag = false;
@ -590,7 +591,6 @@ void ChatForm::onRejectCallTriggered()
emit rejectCall(callId); emit rejectCall(callId);
enableCallButtons(); enableCallButtons();
} }
void ChatForm::onCallTriggered() void ChatForm::onCallTriggered()
@ -601,7 +601,7 @@ void ChatForm::onCallTriggered()
audioOutputFlag = true; audioOutputFlag = true;
callButton->disconnect(); callButton->disconnect();
videoButton->disconnect(); videoButton->disconnect();
emit startCall(f->getFriendID()); emit startCall(f->getFriendID(), false);
} }
void ChatForm::onVideoCallTriggered() void ChatForm::onVideoCallTriggered()
@ -612,10 +612,10 @@ void ChatForm::onVideoCallTriggered()
audioOutputFlag = true; audioOutputFlag = true;
callButton->disconnect(); callButton->disconnect();
videoButton->disconnect(); videoButton->disconnect();
emit startVideoCall(f->getFriendID(), true); emit startCall(f->getFriendID(), true);
} }
void ChatForm::onAvCallFailed(int FriendId) void ChatForm::onAvCallFailed(uint32_t FriendId)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -737,7 +737,7 @@ void ChatForm::onVolMuteToggle()
} }
} }
void ChatForm::onFileSendFailed(int FriendId, const QString &fname) void ChatForm::onFileSendFailed(uint32_t FriendId, const QString &fname)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -745,7 +745,7 @@ void ChatForm::onFileSendFailed(int FriendId, const QString &fname)
addSystemInfoMessage(tr("Failed to send file \"%1\"").arg(fname), ChatMessage::ERROR, QDateTime::currentDateTime()); addSystemInfoMessage(tr("Failed to send file \"%1\"").arg(fname), ChatMessage::ERROR, QDateTime::currentDateTime());
} }
void ChatForm::onAvatarChange(int FriendId, const QPixmap &pic) void ChatForm::onAvatarChange(uint32_t FriendId, const QPixmap &pic)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -787,7 +787,7 @@ void ChatForm::dropEvent(QDropEvent *ev)
} }
} }
void ChatForm::onAvatarRemoved(int FriendId) void ChatForm::onAvatarRemoved(uint32_t FriendId)
{ {
if (FriendId != f->getFriendID()) if (FriendId != f->getFriendID())
return; return;
@ -806,6 +806,7 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
{ {
if (earliestMessage < since) if (earliestMessage < since)
return; return;
if (earliestMessage < now) if (earliestMessage < now)
{ {
now = earliestMessage; now = earliestMessage;

View File

@ -18,14 +18,14 @@
#define CHATFORM_H #define CHATFORM_H
#include "genericchatform.h" #include "genericchatform.h"
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include <QSet> #include <QSet>
#include <QLabel> #include <QLabel>
#include <QTimer> #include <QTimer>
#include <QElapsedTimer> #include <QElapsedTimer>
struct Friend; class Friend;
class FileTransferInstance; class FileTransferInstance;
class NetCamView; class NetCamView;
class QPixmap; class QPixmap;
@ -50,12 +50,11 @@ public:
virtual void show(Ui::MainWindow &ui); virtual void show(Ui::MainWindow &ui);
signals: signals:
void sendFile(int32_t friendId, QString, QString, long long); void sendFile(uint32_t friendId, QString, QString, long long);
void startCall(int friendId); void startCall(uint32_t FriendId, bool video);
void startVideoCall(int friendId, bool video);
void answerCall(int callId); void answerCall(int callId);
void hangupCall(int callId); void hangupCall(int callId);
void cancelCall(int callId, int friendId); void cancelCall(int callId, uint32_t FriendId);
void rejectCall(int callId); void rejectCall(int callId);
void micMuteToggle(int callId); void micMuteToggle(int callId);
void volMuteToggle(int callId); void volMuteToggle(int callId);
@ -64,22 +63,22 @@ signals:
public slots: public slots:
void startFileSend(ToxFile file); void startFileSend(ToxFile file);
void onFileRecvRequest(ToxFile file); void onFileRecvRequest(ToxFile file);
void onAvInvite(int FriendId, int CallId, bool video); void onAvInvite(uint32_t FriendId, int CallId, bool video);
void onAvStart(int FriendId, int CallId, bool video); void onAvStart(uint32_t FriendId, int CallId, bool video);
void onAvCancel(int FriendId, int CallId); void onAvCancel(uint32_t FriendId, int CallId);
void onAvEnd(int FriendId, int CallId); void onAvEnd(uint32_t FriendId, int CallId);
void onAvRinging(int FriendId, int CallId, bool video); void onAvRinging(uint32_t FriendId, int CallId, bool video);
void onAvStarting(int FriendId, int CallId, bool video); void onAvStarting(uint32_t FriendId, int CallId, bool video);
void onAvEnding(int FriendId, int CallId); void onAvEnding(uint32_t FriendId, int CallId);
void onAvRequestTimeout(int FriendId, int CallId); void onAvRequestTimeout(uint32_t FriendId, int CallId);
void onAvPeerTimeout(int FriendId, int CallId); void onAvPeerTimeout(uint32_t FriendId, int CallId);
void onAvMediaChange(int FriendId, int CallId, bool video); void onAvMediaChange(uint32_t FriendId, int CallId, bool video);
void onAvCallFailed(int FriendId); void onAvCallFailed(uint32_t FriendId);
void onAvRejected(int FriendId, int CallId); void onAvRejected(uint32_t FriendId, int CallId);
void onMicMuteToggle(); void onMicMuteToggle();
void onVolMuteToggle(); void onVolMuteToggle();
void onAvatarChange(int FriendId, const QPixmap& pic); void onAvatarChange(uint32_t FriendId, const QPixmap& pic);
void onAvatarRemoved(int FriendId); void onAvatarRemoved(uint32_t FriendId);
private slots: private slots:
void onSendTriggered(); void onSendTriggered();
@ -91,7 +90,7 @@ private slots:
void onHangupCallTriggered(); void onHangupCallTriggered();
void onCancelCallTriggered(); void onCancelCallTriggered();
void onRejectCallTriggered(); void onRejectCallTriggered();
void onFileSendFailed(int FriendId, const QString &fname); void onFileSendFailed(uint32_t FriendId, const QString &fname);
void onLoadHistory(); void onLoadHistory();
void onUpdateTime(); void onUpdateTime();
void onEnableCallButtons(); void onEnableCallButtons();

View File

@ -29,7 +29,7 @@
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/widget/tool/chattextedit.h" #include "src/widget/tool/chattextedit.h"
#include "src/widget/maskablepixmapwidget.h" #include "src/widget/maskablepixmapwidget.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/grouplist.h" #include "src/grouplist.h"
#include "src/group.h" #include "src/group.h"
#include "src/friendlist.h" #include "src/friendlist.h"
@ -366,7 +366,9 @@ QString GenericChatForm::resolveToxID(const ToxID &id)
if (f) if (f)
{ {
return f->getDisplayedName(); return f->getDisplayedName();
} else { }
else
{
for (auto it : GroupList::getAllGroups()) for (auto it : GroupList::getAllGroups())
{ {
QString res = it->resolveToxID(id); QString res = it->resolveToxID(id);

View File

@ -21,7 +21,7 @@
#include <QPoint> #include <QPoint>
#include <QDateTime> #include <QDateTime>
#include <QMenu> #include <QMenu>
#include "src/corestructs.h" #include "src/core/corestructs.h"
#include "src/chatlog/chatmessage.h" #include "src/chatlog/chatmessage.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
@ -60,8 +60,8 @@ public:
ChatLog* getChatLog() const; ChatLog* getChatLog() const;
signals: signals:
void sendMessage(int, QString); void sendMessage(uint32_t, QString);
void sendAction(int, QString); void sendAction(uint32_t, QString);
void chatAreaCleared(); void chatAreaCleared();
public slots: public slots:

View File

@ -21,7 +21,7 @@
#include "src/widget/tool/chattextedit.h" #include "src/widget/tool/chattextedit.h"
#include "src/widget/croppinglabel.h" #include "src/widget/croppinglabel.h"
#include "src/widget/maskablepixmapwidget.h" #include "src/widget/maskablepixmapwidget.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include <QPushButton> #include <QPushButton>
#include <QMimeData> #include <QMimeData>
@ -64,11 +64,10 @@ GroupChatForm::GroupChatForm(Group* chatGroup)
namesListLayout = new FlowLayout(0,5,0); namesListLayout = new FlowLayout(0,5,0);
QStringList names(group->getPeerList()); QStringList names(group->getPeerList());
QLabel *l;
for (const QString& name : names) for (const QString& name : names)
{ {
l = new QLabel(name); QLabel *l = new QLabel(name);
l->setTextFormat(Qt::PlainText); l->setTextFormat(Qt::PlainText);
namesListLayout->addWidget(l); namesListLayout->addWidget(l);
} }
@ -110,10 +109,17 @@ void GroupChatForm::onSendTriggered()
emit sendAction(group->getGroupId(), msg); emit sendAction(group->getGroupId(), msg);
} }
else else
{
emit sendMessage(group->getGroupId(), msg); emit sendMessage(group->getGroupId(), msg);
}
} }
else else
addSelfMessage(msg, msg.startsWith("/me "), QDateTime::currentDateTime(), true); {
if (msg.startsWith("/me "))
addSelfMessage(msg.right(msg.length() - 4), true, QDateTime::currentDateTime(), true);
else
addSelfMessage(msg, false, QDateTime::currentDateTime(), true);
}
} }
void GroupChatForm::onUserListChanged() void GroupChatForm::onUserListChanged()
@ -150,6 +156,7 @@ void GroupChatForm::onUserListChanged()
QLabel* label = orderizer[names[i]]; QLabel* label = orderizer[names[i]];
if (i != nNames - 1) if (i != nNames - 1)
label->setText(label->text() + ", "); label->setText(label->text() + ", ");
namesListLayout->addWidget(label); namesListLayout->addWidget(label);
} }

View File

@ -14,7 +14,7 @@
See the COPYING file for more details. See the COPYING file for more details.
*/ */
#include "src/core.h" #include "src/core/core.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "ui_profileform.h" #include "ui_profileform.h"
#include "profileform.h" #include "profileform.h"
@ -27,6 +27,7 @@
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include "src/profilelocker.h"
#include <QLabel> #include <QLabel>
#include <QLineEdit> #include <QLineEdit>
#include <QGroupBox> #include <QGroupBox>
@ -41,6 +42,7 @@ void ProfileForm::refreshProfiles()
bodyUI->profiles->clear(); bodyUI->profiles->clear();
for (QString profile : Settings::getInstance().searchProfiles()) for (QString profile : Settings::getInstance().searchProfiles())
bodyUI->profiles->addItem(profile); bodyUI->profiles->addItem(profile);
QString current = Settings::getInstance().getCurrentProfile(); QString current = Settings::getInstance().getCurrentProfile();
if (current != "") if (current != "")
bodyUI->profiles->setCurrentText(current); bodyUI->profiles->setCurrentText(current);
@ -183,6 +185,7 @@ void ProfileForm::setToxId(const QString& id)
qr = new QRWidget(); qr = new QRWidget();
qr->setQRData("tox:"+id); qr->setQRData("tox:"+id);
bodyUI->qrCode->setPixmap(QPixmap::fromImage(qr->getImage()->scaledToWidth(150))); bodyUI->qrCode->setPixmap(QPixmap::fromImage(qr->getImage()->scaledToWidth(150)));
refreshProfiles();
} }
void ProfileForm::onAvatarClicked() void ProfileForm::onAvatarClicked()
@ -193,6 +196,7 @@ void ProfileForm::onAvatarClicked()
Nexus::getSupportedImageFilter()); Nexus::getSupportedImageFilter());
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
QFile file(filename); QFile file(filename);
file.open(QIODevice::ReadOnly); file.open(QIODevice::ReadOnly);
if (!file.isOpen()) if (!file.isOpen())
@ -214,22 +218,7 @@ void ProfileForm::onAvatarClicked()
pic.save(&buffer, "PNG"); pic.save(&buffer, "PNG");
buffer.close(); buffer.close();
if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH) Nexus::getCore()->setAvatar(bytes);
{
pic = pic.scaled(64,64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
bytes.clear();
buffer.open(QIODevice::WriteOnly);
pic.save(&buffer, "PNG");
buffer.close();
}
if (bytes.size() >= TOX_AVATAR_MAX_DATA_LENGTH)
{
GUI::showError(tr("Error"), tr("This image is too big"));
return;
}
Nexus::getCore()->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
} }
void ProfileForm::onLoadClicked() void ProfileForm::onLoadClicked()
@ -259,6 +248,13 @@ void ProfileForm::onRenameClicked()
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"), if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur))) tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
{ {
if (!ProfileLocker::lock(name))
{
GUI::showWarning(tr("Profile already exists", "rename failed title"),
tr("A profile named \"%1\" already exists and is in use.").arg(cur));
break;
}
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file); QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini")); QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name); bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
@ -268,6 +264,7 @@ void ProfileForm::onRenameClicked()
Settings::getInstance().setCurrentProfile(name); Settings::getInstance().setCurrentProfile(name);
if (resetAutorun) if (resetAutorun)
Settings::getInstance().setAutorun(true); // fixes -p flag in autostart command line Settings::getInstance().setAutorun(true); // fixes -p flag in autostart command line
break; break;
} }
} while (true); } while (true);
@ -281,7 +278,7 @@ void ProfileForm::onExportClicked()
tr("Tox save file (*.tox)", "save dialog filter")); tr("Tox save file (*.tox)", "save dialog filter"));
if (!path.isEmpty()) if (!path.isEmpty())
{ {
if (!Nexus::isFilePathWritable(path)) if (!Nexus::tryRemoveFile(path))
{ {
GUI::showWarning(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")); GUI::showWarning(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"));
return; return;
@ -389,7 +386,7 @@ void ProfileForm::on_saveQr_clicked()
tr("Save QrCode (*.png)", "save dialog filter")); tr("Save QrCode (*.png)", "save dialog filter"));
if (!path.isEmpty()) if (!path.isEmpty())
{ {
if (!Nexus::isFilePathWritable(path)) if (!Nexus::tryRemoveFile(path))
{ {
GUI::showWarning(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")); GUI::showWarning(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"));
return; return;

View File

@ -21,7 +21,7 @@
#include <QLabel> #include <QLabel>
#include <QTimer> #include <QTimer>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/qrwidget.h" #include "src/misc/qrwidget.h"
class CroppingLabel; class CroppingLabel;

View File

@ -20,7 +20,7 @@
#include "src/widget/widget.h" #include "src/widget/widget.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/misc/smileypack.h" #include "src/misc/smileypack.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include <QMessageBox> #include <QMessageBox>
#include <QStyleFactory> #include <QStyleFactory>
@ -52,6 +52,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6()); bodyUI->cbEnableIPv6->setChecked(Settings::getInstance().getEnableIPv6());
for (int i = 0; i < langs.size(); i++) for (int i = 0; i < langs.size(); i++)
bodyUI->transComboBox->insertItem(i, langs[i]); bodyUI->transComboBox->insertItem(i, langs[i]);
bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation())); bodyUI->transComboBox->setCurrentIndex(locales.indexOf(Settings::getInstance().getTranslation()));
bodyUI->cbAutorun->setChecked(Settings::getInstance().getAutorun()); bodyUI->cbAutorun->setChecked(Settings::getInstance().getAutorun());
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
@ -83,9 +84,8 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
bodyUI->cbGroupchatPosition->setChecked(Settings::getInstance().getGroupchatPosition()); bodyUI->cbGroupchatPosition->setChecked(Settings::getInstance().getGroupchatPosition());
for (auto entry : SmileyPack::listSmileyPacks()) for (auto entry : SmileyPack::listSmileyPacks())
{
bodyUI->smileyPackBrowser->addItem(entry.first, entry.second); bodyUI->smileyPackBrowser->addItem(entry.first, entry.second);
}
bodyUI->smileyPackBrowser->setCurrentIndex(bodyUI->smileyPackBrowser->findData(Settings::getInstance().getSmileyPack())); bodyUI->smileyPackBrowser->setCurrentIndex(bodyUI->smileyPackBrowser->findData(Settings::getInstance().getSmileyPack()));
reloadSmiles(); reloadSmiles();
bodyUI->smileyPackBrowser->setEnabled(bodyUI->useEmoticons->isChecked()); bodyUI->smileyPackBrowser->setEnabled(bodyUI->useEmoticons->isChecked());
@ -99,6 +99,7 @@ GeneralForm::GeneralForm(SettingsWidget *myParent) :
for (QString color : Style::themeColorNames) for (QString color : Style::themeColorNames)
bodyUI->themeColorCBox->addItem(color); bodyUI->themeColorCBox->addItem(color);
bodyUI->themeColorCBox->setCurrentIndex(Settings::getInstance().getThemeColor()); bodyUI->themeColorCBox->setCurrentIndex(Settings::getInstance().getThemeColor());
bodyUI->emoticonSize->setValue(Settings::getInstance().getEmojiFontPointSize()); bodyUI->emoticonSize->setValue(Settings::getInstance().getEmojiFontPointSize());
@ -331,11 +332,9 @@ void GeneralForm::onProxyAddrEdited()
void GeneralForm::onProxyPortEdited(int port) void GeneralForm::onProxyPortEdited(int port)
{ {
if (port > 0) if (port > 0)
{
Settings::getInstance().setProxyPort(port); Settings::getInstance().setProxyPort(port);
} else { else
Settings::getInstance().setProxyPort(-1); Settings::getInstance().setProxyPort(-1);
}
} }
void GeneralForm::onUseProxyUpdated() void GeneralForm::onUseProxyUpdated()

View File

@ -19,7 +19,7 @@
#include "src/widget/form/settingswidget.h" #include "src/widget/form/settingswidget.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/historykeeper.h" #include "src/historykeeper.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/widget/widget.h" #include "src/widget/widget.h"
#include "src/widget/gui.h" #include "src/widget/gui.h"
#include "src/widget/form/setpassworddialog.h" #include "src/widget/form/setpassworddialog.h"
@ -115,8 +115,10 @@ bool PrivacyForm::setChatLogsPassword()
else else
{ {
if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("There is currently an unused encrypted chat history, but the password you just entered doesn't match.\n\nIf you don't care about the old history, you may delete it and use the password you just entered.\nOtherwise, hit Cancel to try again.", "This happens when enabling encryption after previously \"Disabling History\""), tr("Delete"), tr("Cancel"))) if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("There is currently an unused encrypted chat history, but the password you just entered doesn't match.\n\nIf you don't care about the old history, you may delete it and use the password you just entered.\nOtherwise, hit Cancel to try again.", "This happens when enabling encryption after previously \"Disabling History\""), tr("Delete"), tr("Cancel")))
{
if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("Are you absolutely sure you want to lose the unused encrypted chat history?", "secondary popup"), tr("Delete"), tr("Cancel"))) if (GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("Are you absolutely sure you want to lose the unused encrypted chat history?", "secondary popup"), tr("Delete"), tr("Cancel")))
haveEncHist = false; // logically this is really just a `break`, but conceptually this is more accurate haveEncHist = false; // logically this is really just a `break`, but conceptually this is more accurate
}
} }
} while (haveEncHist); } while (haveEncHist);

View File

@ -19,7 +19,7 @@
was greatly simplified for use in qTox. */ was greatly simplified for use in qTox. */
#include "tabcompleter.h" #include "tabcompleter.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/group.h" #include "src/group.h"
#include "src/widget/tool/chattextedit.h" #include "src/widget/tool/chattextedit.h"
#include <QRegExp> #include <QRegExp>
@ -54,8 +54,10 @@ void TabCompleter::buildCompletionList()
QRegExp regex(QString("^[-_\\[\\]{}|`^.\\\\]*").append(QRegExp::escape(tabAbbrev)), Qt::CaseInsensitive); QRegExp regex(QString("^[-_\\[\\]{}|`^.\\\\]*").append(QRegExp::escape(tabAbbrev)), Qt::CaseInsensitive);
for (auto name : group->getPeerList()) for (auto name : group->getPeerList())
{
if (regex.indexIn(name) > -1) if (regex.indexIn(name) > -1)
completionMap[name.toLower()] = name; completionMap[name.toLower()] = name;
}
nextCompletion = completionMap.begin(); nextCompletion = completionMap.begin();
lastCompletionLength = tabAbbrev.length(); lastCompletionLength = tabAbbrev.length();
@ -64,12 +66,14 @@ void TabCompleter::buildCompletionList()
void TabCompleter::complete() void TabCompleter::complete()
{ {
if (!enabled) { if (!enabled)
{
buildCompletionList(); buildCompletionList();
enabled = true; enabled = true;
} }
if (nextCompletion != completionMap.end()) { if (nextCompletion != completionMap.end())
{
// clear previous completion // clear previous completion
auto cur = msgEdit->textCursor(); auto cur = msgEdit->textCursor();
cur.setPosition(cur.selectionEnd()); cur.setPosition(cur.selectionEnd());
@ -85,13 +89,16 @@ void TabCompleter::complete()
nextCompletion++; nextCompletion++;
// we're completing the first word of the line // we're completing the first word of the line
if (msgEdit->textCursor().position() == lastCompletionLength) { if (msgEdit->textCursor().position() == lastCompletionLength)
{
msgEdit->insertPlainText(nickSuffix); msgEdit->insertPlainText(nickSuffix);
lastCompletionLength += nickSuffix.length(); lastCompletionLength += nickSuffix.length();
} }
} }
else { // we're at the end of the list -> start over again else
if (!completionMap.isEmpty()) { { // we're at the end of the list -> start over again
if (!completionMap.isEmpty())
{
nextCompletion = completionMap.begin(); nextCompletion = completionMap.begin();
complete(); complete();
} }

View File

@ -87,7 +87,7 @@ void FriendListWidget::onGroupchatPositionChanged(bool top)
} }
} }
void FriendListWidget::moveWidget(QWidget *w, Status s, int hasNewEvents) void FriendListWidget::moveWidget(QWidget *w, Status s)
{ {
QVBoxLayout* l = getFriendLayout(s); QVBoxLayout* l = getFriendLayout(s);
l->removeWidget(w); l->removeWidget(w);

View File

@ -19,7 +19,7 @@
#include <QWidget> #include <QWidget>
#include <QHash> #include <QHash>
#include "src/corestructs.h" #include "src/core/corestructs.h"
class QVBoxLayout; class QVBoxLayout;
class QGridLayout; class QGridLayout;
@ -37,7 +37,7 @@ signals:
public slots: public slots:
void onGroupchatPositionChanged(bool top); void onGroupchatPositionChanged(bool top);
void moveWidget(QWidget *w, Status s, int hasNewEvents); void moveWidget(QWidget *w, Status s);
private: private:
QHash<int, QVBoxLayout*> layouts; QHash<int, QVBoxLayout*> layouts;

View File

@ -20,7 +20,7 @@
#include "groupwidget.h" #include "groupwidget.h"
#include "src/friendlist.h" #include "src/friendlist.h"
#include "src/friend.h" #include "src/friend.h"
#include "src/core.h" #include "src/core/core.h"
#include "form/chatform.h" #include "form/chatform.h"
#include "maskablepixmapwidget.h" #include "maskablepixmapwidget.h"
#include "croppinglabel.h" #include "croppinglabel.h"

View File

@ -21,7 +21,7 @@
#include "form/groupchatform.h" #include "form/groupchatform.h"
#include "maskablepixmapwidget.h" #include "maskablepixmapwidget.h"
#include "src/misc/style.h" #include "src/misc/style.h"
#include "src/core.h" #include "src/core/core.h"
#include <QPalette> #include <QPalette>
#include <QMenu> #include <QMenu>
#include <QContextMenuEvent> #include <QContextMenuEvent>
@ -60,7 +60,9 @@ void GroupWidget::contextMenuEvent(QContextMenuEvent * event)
if (selectedItem) if (selectedItem)
{ {
if (selectedItem == quitGroup) if (selectedItem == quitGroup)
{
emit removeGroup(groupId); emit removeGroup(groupId);
}
else if (selectedItem == setTitle) else if (selectedItem == setTitle)
{ {
bool ok; bool ok;

View File

@ -15,7 +15,7 @@
*/ */
#include "netcamview.h" #include "netcamview.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/widget/videosurface.h" #include "src/widget/videosurface.h"
#include <QLabel> #include <QLabel>
#include <QHBoxLayout> #include <QHBoxLayout>

View File

@ -16,7 +16,7 @@
#include "toxsave.h" #include "toxsave.h"
#include "gui.h" #include "gui.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
@ -42,9 +42,7 @@ bool handleToxSave(const QString& path)
} }
while (!core->isReady()) while (!core->isReady())
{
qApp->processEvents(); qApp->processEvents();
}
QFileInfo info(path); QFileInfo info(path);
if (!info.exists()) if (!info.exists())

View File

@ -19,7 +19,7 @@
#include "src/toxdns.h" #include "src/toxdns.h"
#include "src/widget/tool/friendrequestdialog.h" #include "src/widget/tool/friendrequestdialog.h"
#include "src/nexus.h" #include "src/nexus.h"
#include "src/core.h" #include "src/core/core.h"
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <QMessageBox> #include <QMessageBox>
@ -51,9 +51,7 @@ bool handleToxURI(const QString &toxURI)
} }
while (!core->isReady()) while (!core->isReady())
{
qApp->processEvents(); qApp->processEvents();
}
QString toxaddr; QString toxaddr;
if (toxURI.startsWith("tox://")) if (toxURI.startsWith("tox://"))

View File

@ -16,7 +16,7 @@
#include "widget.h" #include "widget.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "src/core.h" #include "src/core/core.h"
#include "src/misc/settings.h" #include "src/misc/settings.h"
#include "src/friend.h" #include "src/friend.h"
#include "src/friendlist.h" #include "src/friendlist.h"
@ -50,6 +50,7 @@
#include <QClipboard> #include <QClipboard>
#include <QThread> #include <QThread>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QShortcut>
#include <QTimer> #include <QTimer>
#include <QStyleFactory> #include <QStyleFactory>
#include <QTranslator> #include <QTranslator>
@ -72,6 +73,7 @@ bool toxActivateEventHandler(const QByteArray&)
{ {
if (!Widget::getInstance()->isActiveWindow()) if (!Widget::getInstance()->isActiveWindow())
Widget::getInstance()->forceShow(); Widget::getInstance()->forceShow();
return true; return true;
} }
@ -196,7 +198,11 @@ void Widget::init()
connect(timer, &QTimer::timeout, this, &Widget::onTryCreateTrayIcon); connect(timer, &QTimer::timeout, this, &Widget::onTryCreateTrayIcon);
connect(offlineMsgTimer, &QTimer::timeout, this, &Widget::processOfflineMsgs); connect(offlineMsgTimer, &QTimer::timeout, this, &Widget::processOfflineMsgs);
// keyboard shortcuts
new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(close()));
addFriendForm->show(*ui); addFriendForm->show(*ui);
setWindowTitle(tr("Add friend"));
connect(settingsWidget, &SettingsWidget::groupchatPositionToggled, contactListWidget, &FriendListWidget::onGroupchatPositionChanged); connect(settingsWidget, &SettingsWidget::groupchatPositionToggled, contactListWidget, &FriendListWidget::onGroupchatPositionChanged);
#if (AUTOUPDATE_ENABLED) #if (AUTOUPDATE_ENABLED)
@ -265,7 +271,9 @@ void Widget::updateIcons()
QString status; QString status;
if (eventIcon) if (eventIcon)
{
status = "event"; status = "event";
}
else else
{ {
status = ui->statusButton->property("status").toString(); status = ui->statusButton->property("status").toString();
@ -291,6 +299,7 @@ Widget::~Widget()
AutoUpdater::abortUpdates(); AutoUpdater::abortUpdates();
if (icon) if (icon)
icon->hide(); icon->hide();
hideMainForms(); hideMainForms();
delete profileForm; delete profileForm;
delete settingsWidget; delete settingsWidget;
@ -313,6 +322,7 @@ Widget* Widget::getInstance()
if (!instance) if (!instance)
instance = new Widget(); instance = new Widget();
return instance; return instance;
} }
@ -337,9 +347,7 @@ void Widget::changeEvent(QEvent *event)
if (event->type() == QEvent::WindowStateChange) if (event->type() == QEvent::WindowStateChange)
{ {
if (isMinimized() && Settings::getInstance().getMinimizeToTray()) if (isMinimized() && Settings::getInstance().getMinimizeToTray())
{
this->hide(); this->hide();
}
} }
} }
@ -531,13 +539,12 @@ void Widget::hideMainForms()
QLayoutItem* item; QLayoutItem* item;
while ((item = ui->mainHead->layout()->takeAt(0)) != 0) while ((item = ui->mainHead->layout()->takeAt(0)) != 0)
item->widget()->hide(); item->widget()->hide();
while ((item = ui->mainContent->layout()->takeAt(0)) != 0) while ((item = ui->mainContent->layout()->takeAt(0)) != 0)
item->widget()->hide(); item->widget()->hide();
if (activeChatroomWidget != nullptr) if (activeChatroomWidget != nullptr)
{
activeChatroomWidget->setAsInactiveChatroom(); activeChatroomWidget->setAsInactiveChatroom();
}
} }
void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername) void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
@ -580,7 +587,7 @@ void Widget::addFriend(int friendId, const QString &userId)
//qDebug() << "Widget: Adding friend with id" << userId; //qDebug() << "Widget: Adding friend with id" << userId;
ToxID userToxId = ToxID::fromString(userId); ToxID userToxId = ToxID::fromString(userId);
Friend* newfriend = FriendList::addFriend(friendId, userToxId); Friend* newfriend = FriendList::addFriend(friendId, userToxId);
contactListWidget->moveWidget(newfriend->getFriendWidget(),Status::Offline,0); contactListWidget->moveWidget(newfriend->getFriendWidget(),Status::Offline);
Core* core = Nexus::getCore(); Core* core = Nexus::getCore();
connect(newfriend, &Friend::displayedNameChanged, contactListWidget, &FriendListWidget::moveWidget); connect(newfriend, &Friend::displayedNameChanged, contactListWidget, &FriendListWidget::moveWidget);
@ -589,17 +596,16 @@ void Widget::addFriend(int friendId, const QString &userId)
connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int))); connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int))); connect(newfriend->getFriendWidget(), SIGNAL(copyFriendIdToClipboard(int)), this, SLOT(copyFriendIdToClipboard(int)));
connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newfriend->getChatForm(), SLOT(focusInput())); connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newfriend->getChatForm(), SLOT(focusInput()));
connect(newfriend->getChatForm(), SIGNAL(sendMessage(int,QString)), core, SLOT(sendMessage(int,QString))); connect(newfriend->getChatForm(), &GenericChatForm::sendMessage, core, &Core::sendMessage);
connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction); connect(newfriend->getChatForm(), &GenericChatForm::sendAction, core, &Core::sendAction);
connect(newfriend->getChatForm(), SIGNAL(sendFile(int32_t, QString, QString, long long)), core, SLOT(sendFile(int32_t, QString, QString, long long))); connect(newfriend->getChatForm(), &ChatForm::sendFile, core, &Core::sendFile);
connect(newfriend->getChatForm(), SIGNAL(answerCall(int)), core, SLOT(answerCall(int))); connect(newfriend->getChatForm(), &ChatForm::answerCall, core, &Core::answerCall);
connect(newfriend->getChatForm(), SIGNAL(hangupCall(int)), core, SLOT(hangupCall(int))); connect(newfriend->getChatForm(), &ChatForm::hangupCall, core, &Core::hangupCall);
connect(newfriend->getChatForm(), SIGNAL(rejectCall(int)), core, SLOT(rejectCall(int))); connect(newfriend->getChatForm(), &ChatForm::rejectCall, core, &Core::rejectCall);
connect(newfriend->getChatForm(), SIGNAL(startCall(int)), core, SLOT(startCall(int))); connect(newfriend->getChatForm(), &ChatForm::startCall, core, &Core::startCall);
connect(newfriend->getChatForm(), SIGNAL(startVideoCall(int,bool)), core, SLOT(startCall(int,bool))); connect(newfriend->getChatForm(), &ChatForm::cancelCall, core, &Core::cancelCall);
connect(newfriend->getChatForm(), SIGNAL(cancelCall(int,int)), core, SLOT(cancelCall(int,int))); connect(newfriend->getChatForm(), &ChatForm::micMuteToggle, core, &Core::micMuteToggle);
connect(newfriend->getChatForm(), SIGNAL(micMuteToggle(int)), core, SLOT(micMuteToggle(int))); connect(newfriend->getChatForm(), &ChatForm::volMuteToggle, core, &Core::volMuteToggle);
connect(newfriend->getChatForm(), SIGNAL(volMuteToggle(int)), core, SLOT(volMuteToggle(int)));
connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias); connect(newfriend->getChatForm(), &ChatForm::aliasChanged, newfriend->getFriendWidget(), &FriendWidget::setAlias);
connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest); connect(core, &Core::fileReceiveRequested, newfriend->getChatForm(), &ChatForm::onFileRecvRequest);
connect(core, &Core::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite); connect(core, &Core::avInvite, newfriend->getChatForm(), &ChatForm::onAvInvite);
@ -650,13 +656,9 @@ void Widget::onFriendStatusChanged(int friendId, Status status)
if (isActualChange) if (isActualChange)
{ {
if (f->getStatus() == Status::Offline) if (f->getStatus() == Status::Offline)
{ contactListWidget->moveWidget(f->getFriendWidget(), Status::Online);
contactListWidget->moveWidget(f->getFriendWidget(), Status::Online, f->getEventFlag());
}
else if (status == Status::Offline) else if (status == Status::Offline)
{ contactListWidget->moveWidget(f->getFriendWidget(), Status::Offline);
contactListWidget->moveWidget(f->getFriendWidget(), Status::Offline, f->getEventFlag());
}
} }
f->setStatus(status); f->setStatus(status);
@ -667,7 +669,8 @@ void Widget::onFriendStatusChanged(int friendId, Status status)
&& Settings::getInstance().getStatusChangeNotificationEnabled()) && Settings::getInstance().getStatusChangeNotificationEnabled())
{ {
QString fStatus = ""; QString fStatus = "";
switch(f->getStatus()){ switch(f->getStatus())
{
case Status::Away: case Status::Away:
fStatus = tr("away", "contact status"); break; fStatus = tr("away", "contact status"); break;
case Status::Busy: case Status::Busy:
@ -717,9 +720,8 @@ void Widget::onChatroomWidgetClicked(GenericChatroomWidget *widget)
hideMainForms(); hideMainForms();
widget->setChatForm(*ui); widget->setChatForm(*ui);
if (activeChatroomWidget != nullptr) if (activeChatroomWidget != nullptr)
{
activeChatroomWidget->setAsInactiveChatroom(); activeChatroomWidget->setAsInactiveChatroom();
}
activeChatroomWidget = widget; activeChatroomWidget = widget;
widget->setAsActiveChatroom(); widget->setAsActiveChatroom();
setWindowTitle(widget->getName()); setWindowTitle(widget->getName());
@ -736,7 +738,7 @@ void Widget::onFriendMessageReceived(int friendId, const QString& message, bool
QDateTime timestamp = QDateTime::currentDateTime(); QDateTime timestamp = QDateTime::currentDateTime();
f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true); f->getChatForm()->addMessage(f->getToxID(), message, isAction, timestamp, true);
HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + message : message, HistoryKeeper::getInstance()->addChatEntry(f->getToxID().publicKey, isAction ? "/me " + f->getDisplayedName() + " " + message : message,
f->getToxID().publicKey, timestamp, true); f->getToxID().publicKey, timestamp, true);
f->setEventFlag(f->getFriendWidget() != activeChatroomWidget); f->setEventFlag(f->getFriendWidget() != activeChatroomWidget);
@ -758,6 +760,7 @@ void Widget::newMessageAlert(GenericChatroomWidget* chat)
bool inactiveWindow = isMinimized() || !isActiveWindow(); bool inactiveWindow = isMinimized() || !isActiveWindow();
if (!inactiveWindow && activeChatroomWidget != nullptr && chat == activeChatroomWidget) if (!inactiveWindow && activeChatroomWidget != nullptr && chat == activeChatroomWidget)
return; return;
if (ui->statusButton->property("status").toString() == "busy") if (ui->statusButton->property("status").toString() == "busy")
return; return;
@ -793,6 +796,7 @@ void Widget::playRingtone()
{ {
if (ui->statusButton->property("status").toString() == "busy") if (ui->statusButton->property("status").toString() == "busy")
return; return;
QApplication::alert(this); QApplication::alert(this);
static QFile sndFile1(":audio/ToxicIncomingCall.pcm"); // for whatever reason this plays slower/downshifted from what any other program plays the file as... but whatever static QFile sndFile1(":audio/ToxicIncomingCall.pcm"); // for whatever reason this plays slower/downshifted from what any other program plays the file as... but whatever
@ -933,7 +937,9 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
// g->getChatForm()->addSystemInfoMessage(tr("%1 has left the chat").arg(name), "white", QDateTime::currentDateTime()); // g->getChatForm()->addSystemInfoMessage(tr("%1 has left the chat").arg(name), "white", QDateTime::currentDateTime());
} }
else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed... else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed...
{
g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber)); g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber));
}
} }
void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const QString& title) void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const QString& title)
@ -988,19 +994,20 @@ Group *Widget::createGroup(int groupId)
return g; return g;
} }
Core* core = Nexus::getCore();
QString groupName = QString("Groupchat #%1").arg(groupId); QString groupName = QString("Groupchat #%1").arg(groupId);
Group* newgroup = GroupList::addGroup(groupId, groupName, true); Group* newgroup = GroupList::addGroup(groupId, groupName, core->isGroupAvEnabled(groupId));
QLayout* layout = contactListWidget->getGroupLayout(); QLayout* layout = contactListWidget->getGroupLayout();
layout->addWidget(newgroup->getGroupWidget()); layout->addWidget(newgroup->getGroupWidget());
newgroup->getGroupWidget()->updateStatusLight(); newgroup->getGroupWidget()->updateStatusLight();
Core* core = Nexus::getCore();
connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::onCompactChanged); connect(settingsWidget, &SettingsWidget::compactToggled, newgroup->getGroupWidget(), &GenericChatroomWidget::onCompactChanged);
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*))); connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int))); connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int)));
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput())); connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput()));
connect(newgroup->getChatForm(), SIGNAL(sendMessage(int,QString)), core, SLOT(sendGroupMessage(int,QString))); connect(newgroup->getChatForm(), &GroupChatForm::sendMessage, core, &Core::sendGroupMessage);
connect(newgroup->getChatForm(), SIGNAL(sendAction(int,QString)), core, SLOT(sendGroupAction(int,QString))); connect(newgroup->getChatForm(), &GroupChatForm::sendAction, core, &Core::sendGroupAction);
connect(newgroup->getChatForm(), &GroupChatForm::groupTitleChanged, core, &Core::changeGroupTitle); connect(newgroup->getChatForm(), &GroupChatForm::groupTitleChanged, core, &Core::changeGroupTitle);
return newgroup; return newgroup;
} }
@ -1065,7 +1072,9 @@ void Widget::onUserAwayCheck()
} }
} }
else if (autoAwayActive) else if (autoAwayActive)
{
autoAwayActive = false; autoAwayActive = false;
}
#endif #endif
} }
@ -1108,10 +1117,14 @@ void Widget::onTryCreateTrayIcon()
setHidden(Settings::getInstance().getAutostartInTray()); setHidden(Settings::getInstance().getAutostartInTray());
} }
else else
{
show(); show();
}
} }
else if (!isVisible()) else if (!isVisible())
{
show(); show();
}
} }
else else
{ {
@ -1139,7 +1152,7 @@ void Widget::setStatusBusy()
Nexus::getCore()->setStatus(Status::Busy); Nexus::getCore()->setStatus(Status::Busy);
} }
void Widget::onMessageSendResult(int friendId, const QString& message, int messageId) void Widget::onMessageSendResult(uint32_t friendId, const QString& message, int messageId)
{ {
Q_UNUSED(message) Q_UNUSED(message)
Q_UNUSED(messageId) Q_UNUSED(messageId)
@ -1164,6 +1177,7 @@ void Widget::onFriendTypingChanged(int friendId, bool isTyping)
Friend* f = FriendList::findFriend(friendId); Friend* f = FriendList::findFriend(friendId);
if (!f) if (!f)
return; return;
f->getChatForm()->setFriendTyping(isTyping); f->getChatForm()->setFriendTyping(isTyping);
} }
@ -1197,9 +1211,7 @@ void Widget::processOfflineMsgs()
{ {
QList<Friend*> frnds = FriendList::getAllFriends(); QList<Friend*> frnds = FriendList::getAllFriends();
for (Friend *f : frnds) for (Friend *f : frnds)
{
f->getChatForm()->getOfflineMsgEngine()->deliverOfflineMsgs(); f->getChatForm()->getOfflineMsgEngine()->deliverOfflineMsgs();
}
OfflineMsgEngine::globalMutex.unlock(); OfflineMsgEngine::globalMutex.unlock();
} }
@ -1209,9 +1221,7 @@ void Widget::clearAllReceipts()
{ {
QList<Friend*> frnds = FriendList::getAllFriends(); QList<Friend*> frnds = FriendList::getAllFriends();
for (Friend *f : frnds) for (Friend *f : frnds)
{
f->getChatForm()->getOfflineMsgEngine()->removeAllReciepts(); f->getChatForm()->getOfflineMsgEngine()->removeAllReciepts();
}
} }
void Widget::reloadTheme() void Widget::reloadTheme()

View File

@ -25,7 +25,7 @@
#include "form/settingswidget.h" #include "form/settingswidget.h"
#include "form/profileform.h" #include "form/profileform.h"
#include "form/filesform.h" #include "form/filesform.h"
#include "src/corestructs.h" #include "src/core/corestructs.h"
#define PIXELS_TO_ACT 7 #define PIXELS_TO_ACT 7
@ -35,7 +35,7 @@ class MainWindow;
class GenericChatroomWidget; class GenericChatroomWidget;
class Group; class Group;
struct Friend; class Friend;
class QSplitter; class QSplitter;
class VideoSurface; class VideoSurface;
class QMenu; class QMenu;
@ -104,6 +104,7 @@ public slots:
void onFriendUsernameChanged(int friendId, const QString& username); void onFriendUsernameChanged(int friendId, const QString& username);
void onFriendMessageReceived(int friendId, const QString& message, bool isAction); void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
void onFriendRequestReceived(const QString& userId, const QString& message); void onFriendRequestReceived(const QString& userId, const QString& message);
void onMessageSendResult(uint32_t friendId, const QString& message, int messageId);
void onReceiptRecieved(int friendId, int receipt); void onReceiptRecieved(int friendId, int receipt);
void onEmptyGroupCreated(int groupId); void onEmptyGroupCreated(int groupId);
void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite); void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
@ -111,6 +112,7 @@ public slots:
void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change); void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title); void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
void onGroupPeerAudioPlaying(int groupnumber, int peernumber); void onGroupPeerAudioPlaying(int groupnumber, int peernumber);
void onGroupSendResult(int groupId, const QString& message, int result);
void playRingtone(); void playRingtone();
void onFriendTypingChanged(int friendId, bool isTyping); void onFriendTypingChanged(int friendId, bool isTyping);
void nextContact(); void nextContact();
@ -140,8 +142,6 @@ private slots:
void setStatusOnline(); void setStatusOnline();
void setStatusAway(); void setStatusAway();
void setStatusBusy(); void setStatusBusy();
void onMessageSendResult(int friendId, const QString& message, int messageId);
void onGroupSendResult(int groupId, const QString& message, int result);
void onIconClick(QSystemTrayIcon::ActivationReason); void onIconClick(QSystemTrayIcon::ActivationReason);
void onUserAwayCheck(); void onUserAwayCheck();
void onEventIconTick(); void onEventIconTick();

2166
translations/de.ts vendored

File diff suppressed because it is too large Load Diff

152
translations/it.ts vendored
View File

@ -116,62 +116,62 @@ qualità video elevate, questo può causare problemi con le chiamate video.</tra
<context> <context>
<name>AddFriendForm</name> <name>AddFriendForm</name>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="36"/> <location filename="../src/widget/form/addfriendform.cpp" line="37"/>
<source>Add Friends</source> <source>Add Friends</source>
<translation>Aggiungi Contatto</translation> <translation>Aggiungi Contatto</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="39"/> <location filename="../src/widget/form/addfriendform.cpp" line="40"/>
<source>Tox ID</source> <source>Tox ID</source>
<comment>Tox ID of the person you&apos;re sending a friend request to</comment> <comment>Tox ID of the person you&apos;re sending a friend request to</comment>
<translation>Tox ID</translation> <translation>Tox ID</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="40"/> <location filename="../src/widget/form/addfriendform.cpp" line="41"/>
<source>Message</source> <source>Message</source>
<comment>The message you send in friend requests</comment> <comment>The message you send in friend requests</comment>
<translation>Messaggio</translation> <translation>Messaggio</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="41"/> <location filename="../src/widget/form/addfriendform.cpp" line="42"/>
<source>Send friend request</source> <source>Send friend request</source>
<translation>Invia richiesta d&apos;amicizia</translation> <translation>Invia richiesta d&apos;amicizia</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="80"/> <location filename="../src/widget/form/addfriendform.cpp" line="83"/>
<source>%1 here! Tox me maybe?</source> <source>%1 here! Tox me maybe?</source>
<comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment> <comment>Default message in friend requests if the field is left blank. Write something appropriate!</comment>
<translation>Ciao, sono %1. <translation>Ciao, sono %1.
Permettimi di aggiungerti alla mia lista contatti.</translation> Permettimi di aggiungerti alla mia lista contatti.</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="88"/> <location filename="../src/widget/form/addfriendform.cpp" line="91"/>
<source>Please fill in a valid Tox ID</source> <source>Please fill in a valid Tox ID</source>
<comment>Tox ID of the friend you&apos;re sending a friend request to</comment> <comment>Tox ID of the friend you&apos;re sending a friend request to</comment>
<translation>Inserisci un Tox ID valido</translation> <translation>Inserisci un Tox ID valido</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="88"/>
<location filename="../src/widget/form/addfriendform.cpp" line="91"/> <location filename="../src/widget/form/addfriendform.cpp" line="91"/>
<location filename="../src/widget/form/addfriendform.cpp" line="109"/> <location filename="../src/widget/form/addfriendform.cpp" line="94"/>
<location filename="../src/widget/form/addfriendform.cpp" line="112"/>
<source>Couldn&apos;t add friend</source> <source>Couldn&apos;t add friend</source>
<translation>Impossibile aggiungere il contatto</translation> <translation>Impossibile aggiungere il contatto</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="91"/> <location filename="../src/widget/form/addfriendform.cpp" line="94"/>
<source>You can&apos;t add yourself as a friend!</source> <source>You can&apos;t add yourself as a friend!</source>
<comment>When trying to add your own Tox ID as friend</comment> <comment>When trying to add your own Tox ID as friend</comment>
<translation>Non puoi aggiungere te stesso come contatto!</translation> <translation>Non puoi aggiungere te stesso come contatto!</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="99"/> <location filename="../src/widget/form/addfriendform.cpp" line="102"/>
<source>qTox needs to use the Tox DNS, but can&apos;t do it through a proxy. <source>qTox needs to use the Tox DNS, but can&apos;t do it through a proxy.
Ignore the proxy and connect to the Internet directly?</source> Ignore the proxy and connect to the Internet directly?</source>
<translation>qTox deve usare Tox DNS, ma non può farlo attraverso un proxy. <translation>qTox deve usare Tox DNS, ma non può farlo attraverso un proxy.
Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?</translation> Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/addfriendform.cpp" line="109"/> <location filename="../src/widget/form/addfriendform.cpp" line="112"/>
<source>This Tox ID does not exist</source> <source>This Tox ID does not exist</source>
<comment>DNS error</comment> <comment>DNS error</comment>
<translation>Questo Tox ID non esiste</translation> <translation>Questo Tox ID non esiste</translation>
@ -413,12 +413,12 @@ Ignorare le impostazioni del proxy e connettersi direttamente alla rete Tox?</tr
<translation>Invio del file &quot;%1&quot; fallito</translation> <translation>Invio del file &quot;%1&quot; fallito</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/chatform.cpp" line="909"/> <location filename="../src/widget/form/chatform.cpp" line="910"/>
<source>Call with %1 ended. %2</source> <source>Call with %1 ended. %2</source>
<translation>Chiamata con %1 terminata. %2</translation> <translation>Chiamata con %1 terminata. %2</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/chatform.cpp" line="928"/> <location filename="../src/widget/form/chatform.cpp" line="929"/>
<source>Call duration: </source> <source>Call duration: </source>
<translation>Durata chiamata: </translation> <translation>Durata chiamata: </translation>
</message> </message>
@ -760,30 +760,30 @@ Soprannome:</translation>
<context> <context>
<name>GeneralForm</name> <name>GeneralForm</name>
<message> <message>
<location filename="../src/widget/form/settings/generalform.cpp" line="39"/> <location filename="../src/widget/form/settings/generalform.cpp" line="42"/>
<source>General</source> <source>General</source>
<translation>Generale</translation> <translation>Generale</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalform.cpp" line="89"/> <location filename="../src/widget/form/settings/generalform.cpp" line="93"/>
<location filename="../src/widget/form/settings/generalform.cpp" line="94"/> <location filename="../src/widget/form/settings/generalform.cpp" line="98"/>
<source>None</source> <source>None</source>
<translation>Nessuno</translation> <translation>Nessuno</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalform.cpp" line="270"/> <location filename="../src/widget/form/settings/generalform.cpp" line="295"/>
<source>Choose an auto accept directory</source> <source>Choose an auto accept directory</source>
<comment>popup title</comment> <comment>popup title</comment>
<translation>Scegli dove salvare i files accettati automaticamente</translation> <translation>Scegli dove salvare i files accettati automaticamente</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalform.cpp" line="328"/> <location filename="../src/widget/form/settings/generalform.cpp" line="353"/>
<source>Call active</source> <source>Call active</source>
<comment>popup title</comment> <comment>popup title</comment>
<translation>Chiamata in corso</translation> <translation>Chiamata in corso</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalform.cpp" line="329"/> <location filename="../src/widget/form/settings/generalform.cpp" line="354"/>
<source>You can&apos;t disconnect while a call is active!</source> <source>You can&apos;t disconnect while a call is active!</source>
<comment>popup text</comment> <comment>popup text</comment>
<translation>Non puoi disconnetterti mentre c&apos;è una chiamata in corso!</translation> <translation>Non puoi disconnetterti mentre c&apos;è una chiamata in corso!</translation>
@ -929,62 +929,55 @@ nella traybar invece che nella taskbar.</translation>
<source>Play sound</source> <source>Play sound</source>
<translation>Riproduci suono</translation> <translation>Riproduci suono</translation>
</message> </message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="376"/>
<source>Messages you are trying to send to your friends when they are not online
will be sent to them when they will appear online to you.</source>
<comment>toolTip for Faux offline messaging setting</comment>
<translation>I messaggi che invii ai contatti offline saranno inviati quando appaiono online.</translation>
</message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="390"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="390"/>
<source>Compact contact list</source> <source>Compact contact list</source>
<translation>Usa lista contatti compatta</translation> <translation>Usa lista contatti compatta</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="430"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="440"/>
<source>Smiley Pack:</source> <source>Smiley Pack:</source>
<extracomment>Text on smiley pack label</extracomment> <extracomment>Text on smiley pack label</extracomment>
<translation>Emoticons:</translation> <translation>Emoticons:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="504"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="514"/>
<source>Emoticon size:</source> <source>Emoticon size:</source>
<translation>Dimensione:</translation> <translation>Dimensione:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="536"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="546"/>
<source>Style:</source> <source>Style:</source>
<translation>Stile:</translation> <translation>Stile:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="553"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="563"/>
<source>Theme color:</source> <source>Theme color:</source>
<translation>Colore tema:</translation> <translation>Colore tema:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="570"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="580"/>
<source>Timestamp format:</source> <source>Timestamp format:</source>
<translation>Formato data/ora:</translation> <translation>Formato data/ora:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="592"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="612"/>
<source>Connection Settings</source> <source>Connection Settings</source>
<translation>Impostazioni Connessione</translation> <translation>Impostazioni Connessione</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="616"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="636"/>
<source>Enable IPv6 (recommended)</source> <source>Enable IPv6 (recommended)</source>
<extracomment>Text on a checkbox to enable IPv6</extracomment> <extracomment>Text on a checkbox to enable IPv6</extracomment>
<translation>Abilita IPv6 (consigliato)</translation> <translation>Abilita IPv6 (consigliato)</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="650"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="670"/>
<source>Proxy type:</source> <source>Proxy type:</source>
<translation>Proxy:</translation> <translation>Proxy:</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="657"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="677"/>
<source>Address:</source> <source>Address:</source>
<extracomment>Text on proxy addr label</extracomment> <extracomment>Text on proxy addr label</extracomment>
<translation>Indirizzo:</translation> <translation>Indirizzo:</translation>
@ -1046,6 +1039,13 @@ will be sent to them when they will appear online to you.</source>
<comment>toolTip for Focus window setting</comment> <comment>toolTip for Focus window setting</comment>
<translation>Dai il focus alla finestra di qTox quando arriva un nuovo messaggio.</translation> <translation>Dai il focus alla finestra di qTox quando arriva un nuovo messaggio.</translation>
</message> </message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="376"/>
<source>Messages you are trying to send to your friends when they are not online
will be sent to them when they appear online to you.</source>
<comment>toolTip for Faux offline messaging setting</comment>
<translation>I messaggi che invii ai contatti offline, saranno spediti quando appaiono online.</translation>
</message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="387"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="387"/>
<source>Your contact list will be shown in compact mode.</source> <source>Your contact list will be shown in compact mode.</source>
@ -1053,55 +1053,71 @@ will be sent to them when they will appear online to you.</source>
<translation>La lista contatti sarà visualizzata in modo compatto.</translation> <translation>La lista contatti sarà visualizzata in modo compatto.</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="406"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="397"/>
<source>If checked, groupchats will be placed at the top of the friends list, otherwise, they&apos;ll be placed below online friends.</source>
<comment>toolTip for groupchat positioning</comment>
<translation>Le chat di gruppo saranno posizionate all&apos;inizio della lista contatti, altrimenti saranno posizionate sotto ai contatti online.</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="400"/>
<source>Place groupchats at top of friend list</source>
<translation>Posiziona le chat di gruppo in cima alla lista contatti</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="416"/>
<source>Theme</source> <source>Theme</source>
<translation>Impostazioni Tema</translation> <translation>Impostazioni Tema</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="412"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="422"/>
<source>Use emoticons</source> <source>Use emoticons</source>
<translation>Usa emoticons</translation> <translation>Usa emoticons</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="520"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="530"/>
<source> px</source> <source> px</source>
<translation> px</translation> <translation> px</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="606"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="597"/>
<source>Date format:</source>
<translation>Formato data:</translation>
</message>
<message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="626"/>
<source>Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</source> <source>Disabling this allows, e.g., toxing over Tor. It adds load to the Tox network however, so uncheck only when necessary.</source>
<extracomment>force tcp checkbox tooltip</extracomment> <extracomment>force tcp checkbox tooltip</extracomment>
<translation>Disabilitando questo sarà possibile usare qTox con Tor. Tuttavia verrà aggiunto carico alla rete Tox, quindi disabilitare solo se necessario.</translation> <translation>Disabilitando questo sarà possibile usare qTox con Tor. Tuttavia verrà aggiunto carico alla rete Tox, quindi disabilitare solo se necessario.</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="609"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="629"/>
<source>Enable UDP (recommended)</source> <source>Enable UDP (recommended)</source>
<extracomment>Text on checkbox to disable UDP</extracomment> <extracomment>Text on checkbox to disable UDP</extracomment>
<translation>Abilita UDP (consigliato)</translation> <translation>Abilita UDP (consigliato)</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="681"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="701"/>
<source>None</source> <source>None</source>
<translation>Nessuno</translation> <translation>Nessuno</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="686"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="706"/>
<source>SOCKS5</source> <source>SOCKS5</source>
<translation>SOCKS 5</translation> <translation>SOCKS 5</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="691"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="711"/>
<source>HTTP</source> <source>HTTP</source>
<translation>HTTP</translation> <translation>HTTP</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="667"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="687"/>
<source>Port</source> <source>Port</source>
<extracomment>Text on proxy port label</extracomment> <extracomment>Text on proxy port label</extracomment>
<translation>Porta</translation> <translation>Porta</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/form/settings/generalsettings.ui" line="701"/> <location filename="../src/widget/form/settings/generalsettings.ui" line="721"/>
<source>Reconnect</source> <source>Reconnect</source>
<comment>reconnect button</comment> <comment>reconnect button</comment>
<translation>Riconnetti</translation> <translation>Riconnetti</translation>
@ -2084,131 +2100,131 @@ Se non sei sicuro, scegli &quot;No&quot;, così le informazioni inviate al serve
<context> <context>
<name>Widget</name> <name>Widget</name>
<message> <message>
<location filename="../src/widget/widget.cpp" line="1063"/> <location filename="../src/widget/widget.cpp" line="1092"/>
<source>&amp;Quit</source> <source>&amp;Quit</source>
<translation>&amp;Esci</translation> <translation>&amp;Esci</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="106"/> <location filename="../src/widget/widget.cpp" line="107"/>
<source>Online</source> <source>Online</source>
<comment>Button to set your status to &apos;Online&apos;</comment> <comment>Button to set your status to &apos;Online&apos;</comment>
<translation>Online</translation> <translation>Online</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="109"/> <location filename="../src/widget/widget.cpp" line="110"/>
<source>Away</source> <source>Away</source>
<comment>Button to set your status to &apos;Away&apos;</comment> <comment>Button to set your status to &apos;Away&apos;</comment>
<translation>Assente</translation> <translation>Assente</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="112"/> <location filename="../src/widget/widget.cpp" line="113"/>
<source>Busy</source> <source>Busy</source>
<comment>Button to set your status to &apos;Busy&apos;</comment> <comment>Button to set your status to &apos;Busy&apos;</comment>
<translation>Occupato</translation> <translation>Occupato</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="364"/> <location filename="../src/widget/widget.cpp" line="383"/>
<source>Toxcore failed to start, the application will terminate after you close this message.</source> <source>Toxcore failed to start, the application will terminate after you close this message.</source>
<translation>Impossibile avviare Toxcore.\nqTox terminerà dopo che avrai chiuso questo messaggio.</translation> <translation>Impossibile avviare Toxcore.\nqTox terminerà dopo che avrai chiuso questo messaggio.</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="373"/> <location filename="../src/widget/widget.cpp" line="392"/>
<source>toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart.</source> <source>toxcore failed to start with your proxy settings. qTox cannot run; please modify your settings and restart.</source>
<comment>popup text</comment> <comment>popup text</comment>
<translation>Impossibile avviare Toxcore con le tue impostazione proxy.\nqTox non può funzionare correttamente, per favore modifica le impostazioni e riavvia il programma.</translation> <translation>Impossibile avviare Toxcore con le tue impostazione proxy.\nqTox non può funzionare correttamente, per favore modifica le impostazioni e riavvia il programma.</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="405"/> <location filename="../src/widget/widget.cpp" line="424"/>
<source>Add friend</source> <source>Add friend</source>
<translation>Aggiungi contatto</translation> <translation>Aggiungi contatto</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="418"/> <location filename="../src/widget/widget.cpp" line="437"/>
<source>File transfers</source> <source>File transfers</source>
<translation>Files trasferiti</translation> <translation>Files trasferiti</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="428"/> <location filename="../src/widget/widget.cpp" line="447"/>
<source>Executable file</source> <source>Executable file</source>
<comment>popup title</comment> <comment>popup title</comment>
<translation>File eseguibile</translation> <translation>File eseguibile</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="428"/> <location filename="../src/widget/widget.cpp" line="447"/>
<source>You have asked qTox to open an executable file. Executable files can potentially damage your computer. Are you sure want to open this file?</source> <source>You have asked qTox to open an executable file. Executable files can potentially damage your computer. Are you sure want to open this file?</source>
<comment>popup text</comment> <comment>popup text</comment>
<translation>Hai chiesto a qTox di aprire un file eseguibile. I files eseguibili possono danneggiare il tuo computer. Sei sicuro di voler aprire questo file?</translation> <translation>Hai chiesto a qTox di aprire un file eseguibile. I files eseguibili possono danneggiare il tuo computer. Sei sicuro di voler aprire questo file?</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="498"/> <location filename="../src/widget/widget.cpp" line="517"/>
<source>Settings</source> <source>Settings</source>
<translation>Impostazioni</translation> <translation>Impostazioni</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="506"/> <location filename="../src/widget/widget.cpp" line="525"/>
<source>Profile</source> <source>Profile</source>
<translation>Profilo</translation> <translation>Profilo</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="615"/> <location filename="../src/widget/widget.cpp" line="634"/>
<source>Couldn&apos;t request friendship</source> <source>Couldn&apos;t request friendship</source>
<translation>Impossibile inviare la richiesta d&apos;amicizia</translation> <translation>Impossibile inviare la richiesta d&apos;amicizia</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="643"/> <location filename="../src/widget/widget.cpp" line="672"/>
<source>away</source> <source>away</source>
<comment>contact status</comment> <comment>contact status</comment>
<translation>assente</translation> <translation>assente</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="645"/> <location filename="../src/widget/widget.cpp" line="674"/>
<source>busy</source> <source>busy</source>
<comment>contact status</comment> <comment>contact status</comment>
<translation>occupato</translation> <translation>occupato</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="647"/> <location filename="../src/widget/widget.cpp" line="676"/>
<source>offline</source> <source>offline</source>
<comment>contact status</comment> <comment>contact status</comment>
<translation>offline</translation> <translation>offline</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="651"/> <location filename="../src/widget/widget.cpp" line="680"/>
<source>online</source> <source>online</source>
<comment>contact status</comment> <comment>contact status</comment>
<translation>online</translation> <translation>online</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="654"/> <location filename="../src/widget/widget.cpp" line="683"/>
<source>%1 is now %2</source> <source>%1 is now %2</source>
<comment>e.g. &quot;Dubslow is now online&quot;</comment> <comment>e.g. &quot;Dubslow is now online&quot;</comment>
<translation>%1 è ora %2</translation> <translation>%1 è ora %2</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="837"/> <location filename="../src/widget/widget.cpp" line="866"/>
<source>Group invite</source> <source>Group invite</source>
<comment>popup title</comment> <comment>popup title</comment>
<translation>Invito chat di gruppo</translation> <translation>Invito chat di gruppo</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="837"/> <location filename="../src/widget/widget.cpp" line="866"/>
<source>%1 has invited you to a groupchat. Would you like to join?</source> <source>%1 has invited you to a groupchat. Would you like to join?</source>
<comment>popup text</comment> <comment>popup text</comment>
<translation>%1 ti ha invitato in una chat di gruppo. Vuoi partecipare?</translation> <translation>%1 ti ha invitato in una chat di gruppo. Vuoi partecipare?</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="892"/> <location filename="../src/widget/widget.cpp" line="921"/>
<source>&lt;Unknown&gt;</source> <source>&lt;Unknown&gt;</source>
<comment>Placeholder when we don&apos;t know someone&apos;s name in a group chat</comment> <comment>Placeholder when we don&apos;t know someone&apos;s name in a group chat</comment>
<translation>&lt;Sconosciuto&gt;</translation> <translation>&lt;Sconosciuto&gt;</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="918"/> <location filename="../src/widget/widget.cpp" line="947"/>
<source>%1 has set the title to %2</source> <source>%1 has set the title to %2</source>
<translation>%1 ha impostato il titolo in %2</translation> <translation>%1 ha impostato il titolo in %2</translation>
</message> </message>
<message> <message>
<location filename="../src/widget/widget.cpp" line="1130"/> <location filename="../src/widget/widget.cpp" line="1159"/>
<source>Message failed to send</source> <source>Message failed to send</source>
<translation>Impossibile inviare il messaggio</translation> <translation>Impossibile inviare il messaggio</translation>
</message> </message>

View File

@ -84,108 +84,132 @@ QScrollArea
QScrollArea > QWidget > QWidget QScrollArea > QWidget > QWidget
{ {
background: transparent; background: transparent;
} }
QScrollBar:vertical { QScrollArea::corner
{
background: white;
border: none;
}
QScrollBar:vertical
{
background: transparent; background: transparent;
width: 12px; width: 12px;
margin-top: 2px; margin-top: 2px;
margin-bottom: 2px; margin-bottom: 2px;
} }
QScrollBar::handle:vertical { QScrollBar::handle:vertical
{
background: #d1d1d1; background: #d1d1d1;
min-height: 20px; min-height: 20px;
border-radius: 3px; border-radius: 3px;
margin-left: 2px; margin-left: 2px;
} }
QScrollBar::handle:vertical:hover { QScrollBar::handle:vertical:hover
{
background: #e3e3e3; background: #e3e3e3;
} }
QScrollBar::handle:vertical:pressed { QScrollBar::handle:vertical:pressed
{
background: #b1b1b1; background: #b1b1b1;
} }
QScrollBar::add-line:vertical { QScrollBar::add-line:vertical
background: url(":ui/chatArea/scrollBarDownArrow.svg") center; {
background: white;
height: 0px; height: 0px;
subcontrol-position: bottom; subcontrol-position: bottom;
subcontrol-origin: margin; subcontrol-origin: margin;
} }
QScrollBar::sub-line:vertical { QScrollBar::sub-line:vertical
background: url(":ui/chatArea/scrollBarUpArrow.svg") center; {
background: white;
height: 0px; height: 0px;
subcontrol-position: top; subcontrol-position: top;
subcontrol-origin: margin; subcontrol-origin: margin;
} }
QScrollBar:QScrollBar::down-arrow:vertical { QScrollBar:QScrollBar::down-arrow:vertical
{
width: 10; width: 10;
height: 10px; height: 10px;
background: white; background: white;
} }
QScrollBar:QScrollBar::up-arrow:vertical { QScrollBar:QScrollBar::up-arrow:vertical
{
width: 10px; width: 10px;
height: 10px; height: 10px;
background: white; background: white;
} }
QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
{
background: none; background: none;
} }
QScrollBar:horizontal { QScrollBar:horizontal
{
background: white; background: white;
height: 10px; height: 10px;
margin: 0 2px 0 2px; margin: 0 2px 0 2px;
} }
QScrollBar::handle:horizontal { QScrollBar::handle:horizontal
{
background: #d1d1d1; background: #d1d1d1;
min-width: 20px; min-width: 20px;
border-radius: 2px; border-radius: 2px;
} }
QScrollBar::handle:horizontal:hover { QScrollBar::handle:horizontal:hover
{
background: #e3e3e3; background: #e3e3e3;
} }
QScrollBar::handle:horizontal:pressed { QScrollBar::handle:horizontal:pressed
{
background: #b1b1b1; background: #b1b1b1;
} }
QScrollBar::add-line:horizontal { QScrollBar::add-line:horizontal
background: url(":ui/chatArea/scrollBarRightArrow.svg") center; {
background: white;
width: 0px; width: 0px;
subcontrol-position: right; subcontrol-position: right;
subcontrol-origin: margin; subcontrol-origin: margin;
} }
QScrollBar::sub-line:horizontal { QScrollBar::sub-line:horizontal
background: url(":ui/chatArea/scrollBarLeftArrow.svg") center; {
background: white;
width: 0px; width: 0px;
subcontrol-position: left; subcontrol-position: left;
subcontrol-origin: margin; subcontrol-origin: margin;
} }
QScrollBar:QScrollBar::down-arrow:horizontal { QScrollBar:QScrollBar::down-arrow:horizontal
{
width: 10; width: 10;
height: 10px; height: 10px;
background: white; background: white;
} }
QScrollBar:QScrollBar::up-arrow:horizontal { QScrollBar:QScrollBar::up-arrow:horizontal
{
width: 10px; width: 10px;
height: 10px; height: 10px;
background: white; background: white;
} }
QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal
{
background: none; background: none;
} }