Merge branch 'fileUpload' of blue/squawk into master

This commit is contained in:
Blue 2019-11-16 18:03:47 +00:00 committed by Gitea
commit 09a946c204
24 changed files with 1045 additions and 329 deletions

View File

@ -14,6 +14,15 @@ include_directories(.)
find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5Widgets CONFIG REQUIRED)
find_package(Qt5LinguistTools) find_package(Qt5LinguistTools)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
message("Build type: ${CMAKE_BUILD_TYPE}")
set(squawk_SRC set(squawk_SRC
main.cpp main.cpp
global.cpp global.cpp

View File

@ -8,22 +8,52 @@ A compact XMPP desktop messenger
- uuid _(usually included in some other package, for example it's ***libutil-linux*** in archlinux)_ - uuid _(usually included in some other package, for example it's ***libutil-linux*** in archlinux)_
- lmdb - lmdb
- CMake 3.0 or higher - CMake 3.0 or higher
- qxmpp 1.1.0 or higher
### Getting
The easiest way to get the Squawk is to install it from AUR (if you use Archlinux like distribution)
Here is the [link](https://aur.archlinux.org/packages/squawk/) for the AUR package
You can also install it from console if you use some AUR wrapper. Here what it's going to look like with *pacaur*
```
$ pacaur -S squawk
```
### Building ### Building
You can also clone the repo and build it from source
Squawk requires Qt with SSL enabled. It uses CMake as build system. Squawk requires Qt with SSL enabled. It uses CMake as build system.
Squawk uses upstream version of QXmpp library so first we need to pull it There are two ways to build, it depends whether you have qxmpp installed in your system
```
git submodule update --init --recursive #### Building with system qxmpp
```
Then create a folder for the build, go there and build the project using CMake Here is what you do
``` ```
mkdir build $ git clone https://git.macaw.me/blue/squawk
cd build $ cd squawk
cmake .. $ mkdir build
cmake --build . $ cd build
$ cmake ..
$ cmake --build .
```
#### Building with bundled qxmpp
Here is what you do
```
$ git clone --recurse-submodules https://git.macaw.me/blue/squawk
$ cd squawk
$ mkdir build
$ cd build
$ cmake .. -D SYSTEM_QXMPP=False
$ cmake --build .
``` ```
## License ## License

View File

@ -23,7 +23,7 @@
using namespace Core; using namespace Core;
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent): Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent):
QObject(parent), QObject(parent),
name(p_name), name(p_name),
achiveQueries(), achiveQueries(),
@ -38,15 +38,20 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
bm(new QXmppBookmarkManager()), bm(new QXmppBookmarkManager()),
rm(client.findExtension<QXmppRosterManager>()), rm(client.findExtension<QXmppRosterManager>()),
vm(client.findExtension<QXmppVCardManager>()), vm(client.findExtension<QXmppVCardManager>()),
um(new QXmppUploadRequestManager()),
dm(client.findExtension<QXmppDiscoveryManager>()),
contacts(), contacts(),
conferences(), conferences(),
maxReconnectTimes(0), maxReconnectTimes(0),
reconnectTimes(0), reconnectTimes(0),
queuedContacts(), queuedContacts(),
outOfRosterContacts(), outOfRosterContacts(),
pendingMessages(),
uploadingSlotsQueue(),
avatarHash(), avatarHash(),
avatarType(), avatarType(),
ownVCardRequestInProgress(false) ownVCardRequestInProgress(false),
network(p_net)
{ {
config.setUser(p_login); config.setUser(p_login);
config.setDomain(p_server); config.setDomain(p_server);
@ -85,6 +90,16 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived);
//QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler
client.addExtension(um);
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, this, &Account::onUploadSlotReceived);
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed);
QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived);
QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived);
QObject::connect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded);
QObject::connect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name; path += "/" + name;
QDir dir(path); QDir dir(path);
@ -133,6 +148,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
Account::~Account() Account::~Account()
{ {
QObject::disconnect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded);
QObject::disconnect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError);
for (std::map<QString, Contact*>::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { for (std::map<QString, Contact*>::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) {
delete itr->second; delete itr->second;
} }
@ -141,6 +159,7 @@ Account::~Account()
delete itr->second; delete itr->second;
} }
delete um;
delete bm; delete bm;
delete mm; delete mm;
delete am; delete am;
@ -181,6 +200,7 @@ void Core::Account::onClientConnected()
reconnectTimes = maxReconnectTimes; reconnectTimes = maxReconnectTimes;
state = Shared::connected; state = Shared::connected;
cm->setCarbonsEnabled(true); cm->setCarbonsEnabled(true);
dm->requestItems(getServer());
emit connectionStateChanged(state); emit connectionStateChanged(state);
} else { } else {
qDebug() << "Something weird had happened - xmpp client reported about successful connection but account wasn't in" << state << "state"; qDebug() << "Something weird had happened - xmpp client reported about successful connection but account wasn't in" << state << "state";
@ -598,6 +618,7 @@ void Core::Account::sendMessage(const Shared::Message& data)
QXmppMessage msg(data.getFrom(), data.getTo(), data.getBody(), data.getThread()); QXmppMessage msg(data.getFrom(), data.getTo(), data.getBody(), data.getThread());
msg.setId(data.getId()); msg.setId(data.getId());
msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible msg.setType(static_cast<QXmppMessage::Type>(data.getType())); //it is safe here, my type is compatible
msg.setOutOfBandUrl(data.getOutOfBandUrl());
RosterItem* ri = 0; RosterItem* ri = 0;
std::map<QString, Contact*>::const_iterator itr = contacts.find(data.getPenPalJid()); std::map<QString, Contact*>::const_iterator itr = contacts.find(data.getPenPalJid());
@ -623,6 +644,50 @@ void Core::Account::sendMessage(const Shared::Message& data)
} }
} }
void Core::Account::sendMessage(const Shared::Message& data, const QString& path)
{
if (state == Shared::connected) {
QString url = network->getFileRemoteUrl(path);
if (url.size() != 0) {
sendMessageWithLocalUploadedFile(data, url);
} else {
if (network->isUploading(path, data.getId())) {
pendingMessages.emplace(data.getId(), data);
} else {
if (um->serviceFound()) {
QFileInfo file(path);
if (file.exists() && file.isReadable()) {
uploadingSlotsQueue.emplace_back(path, data);
if (uploadingSlotsQueue.size() == 1) {
um->requestUploadSlot(file);
}
} else {
emit onFileUploadError(data.getId(), "Uploading file dissapeared or your system user has no permission to read it");
qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable";
}
} else {
emit onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account");
qDebug() << "Requested upload slot in account" << name << "for file" << path << "but upload manager didn't discover any upload services";
}
}
}
} else {
emit onFileUploadError(data.getId(), "Account is offline or reconnecting");
qDebug() << "An attempt to send message with not connected account " << name << ", skipping";
}
}
void Core::Account::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url)
{
msg.setOutOfBandUrl(url);
if (msg.getBody().size() == 0) {
msg.setBody(url);
}
sendMessage(msg);
//TODO removal/progress update
}
void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg) void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg)
{ {
handleChatMessage(msg, false, true); handleChatMessage(msg, false, true);
@ -1551,3 +1616,66 @@ void Core::Account::uploadVCard(const Shared::VCard& card)
vm->setClientVCard(iq); vm->setClientVCard(iq);
onOwnVCardReceived(iq); onOwnVCardReceived(iq);
} }
void Core::Account::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot)
{
if (uploadingSlotsQueue.size() == 0) {
qDebug() << "HTTP Upload manager of account" << name << "reports about success requesting upload slot, but none was requested";
} else {
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
const QString& mId = pair.second.getId();
network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders());
pendingMessages.emplace(mId, pair.second);
uploadingSlotsQueue.pop_front();
if (uploadingSlotsQueue.size() > 0) {
um->requestUploadSlot(uploadingSlotsQueue.front().first);
}
}
}
void Core::Account::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request)
{
if (uploadingSlotsQueue.size() == 0) {
qDebug() << "HTTP Upload manager of account" << name << "reports about an error requesting upload slot, but none was requested";
qDebug() << request.error().text();
} else {
const std::pair<QString, Shared::Message>& pair = uploadingSlotsQueue.front();
qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << name << ":" << request.error().text();
emit uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text());
if (uploadingSlotsQueue.size() > 0) {
um->requestUploadSlot(uploadingSlotsQueue.front().first);
}
uploadingSlotsQueue.pop_front();
}
}
void Core::Account::onFileUploaded(const QString& messageId, const QString& url)
{
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
if (itr != pendingMessages.end()) {
sendMessageWithLocalUploadedFile(itr->second, url);
pendingMessages.erase(itr);
}
}
void Core::Account::onFileUploadError(const QString& messageId, const QString& errMsg)
{
std::map<QString, Shared::Message>::const_iterator itr = pendingMessages.find(messageId);
if (itr != pendingMessages.end()) {
pendingMessages.erase(itr);
}
}
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
{
for (QXmppDiscoveryIq::Item item : items.items()) {
dm->requestInfo(item.jid());
}
}
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
{
}

View File

@ -31,16 +31,20 @@
#include <QXmppRosterManager.h> #include <QXmppRosterManager.h>
#include <QXmppCarbonManager.h> #include <QXmppCarbonManager.h>
#include <QXmppDiscoveryManager.h>
#include <QXmppMamManager.h> #include <QXmppMamManager.h>
#include <QXmppMucManager.h> #include <QXmppMucManager.h>
#include <QXmppClient.h> #include <QXmppClient.h>
#include <QXmppBookmarkManager.h> #include <QXmppBookmarkManager.h>
#include <QXmppBookmarkSet.h> #include <QXmppBookmarkSet.h>
#include <QXmppUploadRequestManager.h>
#include <QXmppHttpUploadIq.h>
#include <QXmppVCardIq.h> #include <QXmppVCardIq.h>
#include <QXmppVCardManager.h> #include <QXmppVCardManager.h>
#include "../global.h" #include "global.h"
#include "contact.h" #include "contact.h"
#include "conference.h" #include "conference.h"
#include "networkaccess.h"
namespace Core namespace Core
{ {
@ -49,7 +53,7 @@ class Account : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent = 0); Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0);
~Account(); ~Account();
void connect(); void connect();
@ -73,6 +77,7 @@ public:
void setAvailability(Shared::Availability avail); void setAvailability(Shared::Availability avail);
QString getFullJid() const; QString getFullJid() const;
void sendMessage(const Shared::Message& data); void sendMessage(const Shared::Message& data);
void sendMessage(const Shared::Message& data, const QString& path);
void requestArchive(const QString& jid, int count, const QString& before); void requestArchive(const QString& jid, int count, const QString& before);
void setReconnectTimes(unsigned int times); void setReconnectTimes(unsigned int times);
void subscribeToContact(const QString& jid, const QString& reason); void subscribeToContact(const QString& jid, const QString& reason);
@ -112,6 +117,8 @@ signals:
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data); void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
void removeRoomParticipant(const QString& jid, const QString& nickName); void removeRoomParticipant(const QString& jid, const QString& nickName);
void receivedVCard(const QString& jid, const Shared::VCard& card); void receivedVCard(const QString& jid, const Shared::VCard& card);
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
void uploadFileError(const QString& messageId, const QString& error);
private: private:
QString name; QString name;
@ -127,6 +134,8 @@ private:
QXmppBookmarkManager* bm; QXmppBookmarkManager* bm;
QXmppRosterManager* rm; QXmppRosterManager* rm;
QXmppVCardManager* vm; QXmppVCardManager* vm;
QXmppUploadRequestManager* um;
QXmppDiscoveryManager* dm;
std::map<QString, Contact*> contacts; std::map<QString, Contact*> contacts;
std::map<QString, Conference*> conferences; std::map<QString, Conference*> conferences;
unsigned int maxReconnectTimes; unsigned int maxReconnectTimes;
@ -135,10 +144,13 @@ private:
std::map<QString, QString> queuedContacts; std::map<QString, QString> queuedContacts;
std::set<QString> outOfRosterContacts; std::set<QString> outOfRosterContacts;
std::set<QString> pendingVCardRequests; std::set<QString> pendingVCardRequests;
std::map<QString, Shared::Message> pendingMessages;
std::deque<std::pair<QString, Shared::Message>> uploadingSlotsQueue;
QString avatarHash; QString avatarHash;
QString avatarType; QString avatarType;
bool ownVCardRequestInProgress; bool ownVCardRequestInProgress;
NetworkAccess* network;
private slots: private slots:
void onClientConnected(); void onClientConnected();
@ -184,6 +196,13 @@ private slots:
void onVCardReceived(const QXmppVCardIq& card); void onVCardReceived(const QXmppVCardIq& card);
void onOwnVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card);
void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot);
void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request);
void onFileUploaded(const QString& messageId, const QString& url);
void onFileUploadError(const QString& messageId, const QString& errMsg);
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
private: private:
void addedAccount(const QString &bareJid); void addedAccount(const QString &bareJid);
void handleNewContact(Contact* contact); void handleNewContact(Contact* contact);
@ -199,7 +218,7 @@ private:
void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: ");
void storeConferences(); void storeConferences();
void clearConferences(); void clearConferences();
void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url);
}; };
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);

View File

@ -23,7 +23,8 @@ Core::NetworkAccess::NetworkAccess(QObject* parent):
running(false), running(false),
manager(0), manager(0),
files("files"), files("files"),
downloads() downloads(),
uploads()
{ {
} }
@ -34,9 +35,9 @@ Core::NetworkAccess::~NetworkAccess()
void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url) void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url)
{ {
std::map<QString, Download*>::iterator itr = downloads.find(url); std::map<QString, Transfer*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) { if (itr != downloads.end()) {
Download* dwn = itr->second; Transfer* dwn = itr->second;
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId); std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
if (mItr == dwn->messages.end()) { if (mItr == dwn->messages.end()) {
dwn->messages.insert(messageId); dwn->messages.insert(messageId);
@ -63,9 +64,9 @@ void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const Q
void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url) void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url)
{ {
std::map<QString, Download*>::iterator itr = downloads.find(url); std::map<QString, Transfer*>::iterator itr = downloads.find(url);
if (itr != downloads.end()) { if (itr != downloads.end()) {
Download* dwn = itr->second; Transfer* dwn = itr->second;
std::set<QString>::const_iterator mItr = dwn->messages.find(messageId); std::set<QString>::const_iterator mItr = dwn->messages.find(messageId);
if (mItr == dwn->messages.end()) { if (mItr == dwn->messages.end()) {
dwn->messages.insert(messageId); dwn->messages.insert(messageId);
@ -85,7 +86,7 @@ void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QSt
startDownload(messageId, url); startDownload(messageId, url);
} catch (Archive::Unknown e) { } catch (Archive::Unknown e) {
qDebug() << "Error requesting file path:" << e.what(); qDebug() << "Error requesting file path:" << e.what();
startDownload(messageId, url); emit downloadFileError(messageId, QString("Database error: ") + e.what());
} }
} }
} }
@ -107,7 +108,7 @@ void Core::NetworkAccess::stop()
manager = 0; manager = 0;
running = false; running = false;
for (std::map<QString, Download*>::const_iterator itr = downloads.begin(), end = downloads.end(); itr != end; ++itr) { for (std::map<QString, Transfer*>::const_iterator itr = downloads.begin(), end = downloads.end(); itr != end; ++itr) {
itr->second->success = false; itr->second->success = false;
itr->second->reply->abort(); //assuming it's gonna call onRequestFinished slot itr->second->reply->abort(); //assuming it's gonna call onRequestFinished slot
} }
@ -118,11 +119,11 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
{ {
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url); std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) { if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
} else { } else {
Download* dwn = itr->second; Transfer* dwn = itr->second;
qreal received = bytesReceived; qreal received = bytesReceived;
qreal total = bytesTotal; qreal total = bytesTotal;
qreal progress = received/total; qreal progress = received/total;
@ -133,132 +134,18 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT
} }
} }
void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code) void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code)
{ {
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url); std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) { if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping";
} else { } else {
QString errorText; QString errorText = getErrorText(code);
switch (code) {
case QNetworkReply::NoError:
//this never is supposed to happen
break;
// network layer errors [relating to the destination server] (1-99):
case QNetworkReply::ConnectionRefusedError:
errorText = "Connection refused";
break;
case QNetworkReply::RemoteHostClosedError:
errorText = "Remote server closed the connection";
break;
case QNetworkReply::HostNotFoundError:
errorText = "Remote host is not found";
break;
case QNetworkReply::TimeoutError:
errorText = "Connection was closed because it timed out";
break;
case QNetworkReply::OperationCanceledError:
//this means I closed it myself by abort() or close(), don't think I need to notify here
break;
case QNetworkReply::SslHandshakeFailedError:
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
break;
case QNetworkReply::TemporaryNetworkFailureError:
//this means the connection is lost by opened route, but it's going to be resumed, not sure I need to notify
break;
case QNetworkReply::NetworkSessionFailedError:
errorText = "Outgoing connection problem";
break;
case QNetworkReply::BackgroundRequestNotAllowedError:
errorText = "Background request is not allowed";
break;
case QNetworkReply::TooManyRedirectsError:
errorText = "The request was redirected too many times";
break;
case QNetworkReply::InsecureRedirectError:
errorText = "The request was redirected to insecure connection";
break;
case QNetworkReply::UnknownNetworkError:
errorText = "Unknown network error";
break;
// proxy errors (101-199):
case QNetworkReply::ProxyConnectionRefusedError:
errorText = "The connection to the proxy server was refused";
break;
case QNetworkReply::ProxyConnectionClosedError:
errorText = "Proxy server closed the connection";
break;
case QNetworkReply::ProxyNotFoundError:
errorText = "Proxy host was not found";
break;
case QNetworkReply::ProxyTimeoutError:
errorText = "Connection to the proxy server was closed because it timed out";
break;
case QNetworkReply::ProxyAuthenticationRequiredError:
errorText = "Couldn't connect to proxy server, authentication is required";
break;
case QNetworkReply::UnknownProxyError:
errorText = "Unknown proxy error";
break;
// content errors (201-299):
case QNetworkReply::ContentAccessDenied:
errorText = "The access to file is denied";
break;
case QNetworkReply::ContentOperationNotPermittedError:
errorText = "The operation over requesting file is not permitted";
break;
case QNetworkReply::ContentNotFoundError:
errorText = "The file was not found";
break;
case QNetworkReply::AuthenticationRequiredError:
errorText = "Couldn't access the file, authentication is required";
break;
case QNetworkReply::ContentReSendError:
errorText = "Sending error, one more attempt will probably solve this problem";
break;
case QNetworkReply::ContentConflictError:
errorText = "The request could not be completed due to a conflict with the current state of the resource";
break;
case QNetworkReply::ContentGoneError:
errorText = "The requested resource is no longer available at the server";
break;
case QNetworkReply::UnknownContentError:
errorText = "Unknown content error";
break;
// protocol errors
case QNetworkReply::ProtocolUnknownError:
errorText = "Unknown protocol error";
break;
case QNetworkReply::ProtocolInvalidOperationError:
errorText = "Requested operation is not permitted in this protocol";
break;
case QNetworkReply::ProtocolFailure:
errorText = "Low level protocol error";
break;
// Server side errors (401-499)
case QNetworkReply::InternalServerError:
errorText = "Internal server error";
break;
case QNetworkReply::OperationNotImplementedError:
errorText = "Server doesn't support requested operation";
break;
case QNetworkReply::ServiceUnavailableError:
errorText = "The server is not available for this operation right now";
break;
case QNetworkReply::UnknownServerError:
errorText = "Unknown server error";
break;
}
if (errorText.size() > 0) { if (errorText.size() > 0) {
itr->second->success = false; itr->second->success = false;
Download* dwn = itr->second; Transfer* dwn = itr->second;
for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) { for (std::set<QString>::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) {
emit downloadFileError(*mItr, errorText); emit downloadFileError(*mItr, errorText);
} }
@ -266,16 +153,137 @@ void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code)
} }
} }
void Core::NetworkAccess::onRequestFinished() QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code)
{
QString errorText("");
switch (code) {
case QNetworkReply::NoError:
//this never is supposed to happen
break;
// network layer errors [relating to the destination server] (1-99):
case QNetworkReply::ConnectionRefusedError:
errorText = "Connection refused";
break;
case QNetworkReply::RemoteHostClosedError:
errorText = "Remote server closed the connection";
break;
case QNetworkReply::HostNotFoundError:
errorText = "Remote host is not found";
break;
case QNetworkReply::TimeoutError:
errorText = "Connection was closed because it timed out";
break;
case QNetworkReply::OperationCanceledError:
//this means I closed it myself by abort() or close(), don't think I need to notify here
break;
case QNetworkReply::SslHandshakeFailedError:
errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here
break;
case QNetworkReply::TemporaryNetworkFailureError:
//this means the connection is lost by opened route, but it's going to be resumed, not sure I need to notify
break;
case QNetworkReply::NetworkSessionFailedError:
errorText = "Outgoing connection problem";
break;
case QNetworkReply::BackgroundRequestNotAllowedError:
errorText = "Background request is not allowed";
break;
case QNetworkReply::TooManyRedirectsError:
errorText = "The request was redirected too many times";
break;
case QNetworkReply::InsecureRedirectError:
errorText = "The request was redirected to insecure connection";
break;
case QNetworkReply::UnknownNetworkError:
errorText = "Unknown network error";
break;
// proxy errors (101-199):
case QNetworkReply::ProxyConnectionRefusedError:
errorText = "The connection to the proxy server was refused";
break;
case QNetworkReply::ProxyConnectionClosedError:
errorText = "Proxy server closed the connection";
break;
case QNetworkReply::ProxyNotFoundError:
errorText = "Proxy host was not found";
break;
case QNetworkReply::ProxyTimeoutError:
errorText = "Connection to the proxy server was closed because it timed out";
break;
case QNetworkReply::ProxyAuthenticationRequiredError:
errorText = "Couldn't connect to proxy server, authentication is required";
break;
case QNetworkReply::UnknownProxyError:
errorText = "Unknown proxy error";
break;
// content errors (201-299):
case QNetworkReply::ContentAccessDenied:
errorText = "The access to file is denied";
break;
case QNetworkReply::ContentOperationNotPermittedError:
errorText = "The operation over requesting file is not permitted";
break;
case QNetworkReply::ContentNotFoundError:
errorText = "The file was not found";
break;
case QNetworkReply::AuthenticationRequiredError:
errorText = "Couldn't access the file, authentication is required";
break;
case QNetworkReply::ContentReSendError:
errorText = "Sending error, one more attempt will probably solve this problem";
break;
case QNetworkReply::ContentConflictError:
errorText = "The request could not be completed due to a conflict with the current state of the resource";
break;
case QNetworkReply::ContentGoneError:
errorText = "The requested resource is no longer available at the server";
break;
case QNetworkReply::UnknownContentError:
errorText = "Unknown content error";
break;
// protocol errors
case QNetworkReply::ProtocolUnknownError:
errorText = "Unknown protocol error";
break;
case QNetworkReply::ProtocolInvalidOperationError:
errorText = "Requested operation is not permitted in this protocol";
break;
case QNetworkReply::ProtocolFailure:
errorText = "Low level protocol error";
break;
// Server side errors (401-499)
case QNetworkReply::InternalServerError:
errorText = "Internal server error";
break;
case QNetworkReply::OperationNotImplementedError:
errorText = "Server doesn't support requested operation";
break;
case QNetworkReply::ServiceUnavailableError:
errorText = "The server is not available for this operation right now";
break;
case QNetworkReply::UnknownServerError:
errorText = "Unknown server error";
break;
}
return errorText;
}
void Core::NetworkAccess::onDownloadFinished()
{ {
QString path(""); QString path("");
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender()); QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString(); QString url = rpl->url().toString();
std::map<QString, Download*>::const_iterator itr = downloads.find(url); std::map<QString, Transfer*>::const_iterator itr = downloads.find(url);
if (itr == downloads.end()) { if (itr == downloads.end()) {
qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping"; qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping";
} else { } else {
Download* dwn = itr->second; Transfer* dwn = itr->second;
if (dwn->success) { if (dwn->success) {
qDebug() << "download success for" << url; qDebug() << "download success for" << url;
QStringList hops = url.split("/"); QStringList hops = url.split("/");
@ -320,13 +328,173 @@ void Core::NetworkAccess::onRequestFinished()
void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url) void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url)
{ {
Download* dwn = new Download({{messageId}, 0, 0, true}); Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", url, 0});
QNetworkRequest req(url); QNetworkRequest req(url);
dwn->reply = manager->get(req); dwn->reply = manager->get(req);
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress); connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onRequestError); connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onDownloadError);
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onRequestFinished); connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
downloads.insert(std::make_pair(url, dwn)); downloads.insert(std::make_pair(url, dwn));
emit downloadFileProgress(messageId, 0); emit downloadFileProgress(messageId, 0);
} }
void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
if (itr == uploads.end()) {
qDebug() << "an error uploading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping";
} else {
QString errorText = getErrorText(code);
if (errorText.size() > 0) {
itr->second->success = false;
Transfer* upl = itr->second;
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
emit uploadFileError(*mItr, errorText);
}
}
}
}
void Core::NetworkAccess::onUploadFinished()
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
if (itr == downloads.end()) {
qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping";
} else {
Transfer* upl = itr->second;
if (upl->success) {
qDebug() << "upload success for" << url;
files.addRecord(upl->url, upl->path);
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
emit fileLocalPathResponse(*mItr, upl->path);
emit uploadFileComplete(*mItr, upl->url);
}
}
upl->reply->deleteLater();
upl->file->close();
upl->file->deleteLater();
delete upl;
uploads.erase(itr);
}
}
void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
QNetworkReply* rpl = static_cast<QNetworkReply*>(sender());
QString url = rpl->url().toString();
std::map<QString, Transfer*>::const_iterator itr = uploads.find(url);
if (itr == uploads.end()) {
qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping";
} else {
Transfer* upl = itr->second;
qreal received = bytesReceived;
qreal total = bytesTotal;
qreal progress = received/total;
upl->progress = progress;
for (std::set<QString>::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) {
emit uploadFileProgress(*mItr, progress);
}
}
}
void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path)
{
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0});
QNetworkRequest req(url);
QFile* file = new QFile(path);
if (file->open(QIODevice::ReadOnly)) {
upl->reply = manager->put(req, file);
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
uploads.insert(std::make_pair(url, upl));
emit downloadFileProgress(messageId, 0);
} else {
qDebug() << "couldn't upload file" << path;
emit uploadFileError(messageId, "Error opening file");
delete file;
}
}
void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QString& url, const QString& path)
{
std::map<QString, Transfer*>::iterator itr = uploads.find(url);
if (itr != uploads.end()) {
Transfer* upl = itr->second;
std::set<QString>::const_iterator mItr = upl->messages.find(messageId);
if (mItr == upl->messages.end()) {
upl->messages.insert(messageId);
}
emit uploadFileProgress(messageId, upl->progress);
} else {
try {
QString ePath = files.getRecord(url);
if (ePath == path) {
QFileInfo info(path);
if (info.exists() && info.isFile()) {
emit fileLocalPathResponse(messageId, path);
} else {
files.removeRecord(url);
startUpload(messageId, url, path);
}
} else {
QFileInfo info(path);
if (info.exists() && info.isFile()) {
files.changeRecord(url, path);
emit fileLocalPathResponse(messageId, path);
} else {
files.removeRecord(url);
startUpload(messageId, url, path);
}
}
} catch (Archive::NotFound e) {
startUpload(messageId, url, path);
} catch (Archive::Unknown e) {
qDebug() << "Error requesting file path on upload:" << e.what();
emit uploadFileError(messageId, QString("Database error: ") + e.what());
}
}
}
QString Core::NetworkAccess::getFileRemoteUrl(const QString& path)
{
return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url
}
bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId)
{
return false; //TODO this is a way to avoid parallel uploading of the same files by different chats
// message is is supposed to be added to the uploading messageids list
// the result should be true if there was an uploading file with this path
// message id can be empty, then it's just to check and not to add
}
void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers)
{
QFile* file = new QFile(path);
Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), file});
QNetworkRequest req(put);
for (QMap<QString, QString>::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) {
req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8());
}
if (file->open(QIODevice::ReadOnly)) {
upl->reply = manager->put(req, file);
connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress);
connect(upl->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onUploadError);
connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished);
uploads.insert(std::make_pair(put.toString(), upl));
emit downloadFileProgress(messageId, 0);
} else {
qDebug() << "couldn't upload file" << path;
emit uploadFileError(messageId, "Error opening file");
delete file;
}
}

View File

@ -39,7 +39,7 @@ namespace Core {
class NetworkAccess : public QObject class NetworkAccess : public QObject
{ {
Q_OBJECT Q_OBJECT
struct Download; struct Transfer;
public: public:
NetworkAccess(QObject* parent = nullptr); NetworkAccess(QObject* parent = nullptr);
virtual ~NetworkAccess(); virtual ~NetworkAccess();
@ -47,34 +47,51 @@ public:
void start(); void start();
void stop(); void stop();
QString getFileRemoteUrl(const QString& path);
bool isUploading(const QString& path, const QString& messageId = "");
void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap<QString, QString> headers);
signals: signals:
void fileLocalPathResponse(const QString& messageId, const QString& path); void fileLocalPathResponse(const QString& messageId, const QString& path);
void downloadFileProgress(const QString& messageId, qreal value); void downloadFileProgress(const QString& messageId, qreal value);
void downloadFileError(const QString& messageId, const QString& path); void downloadFileError(const QString& messageId, const QString& path);
void uploadFileProgress(const QString& messageId, qreal value);
void uploadFileError(const QString& messageId, const QString& path);
void uploadFileComplete(const QString& messageId, const QString& url);
public slots: public slots:
void fileLocalPathRequest(const QString& messageId, const QString& url); void fileLocalPathRequest(const QString& messageId, const QString& url);
void downladFileRequest(const QString& messageId, const QString& url); void downladFileRequest(const QString& messageId, const QString& url);
void uploadFileRequest(const QString& messageId, const QString& url, const QString& path);
private: private:
void startDownload(const QString& messageId, const QString& url); void startDownload(const QString& messageId, const QString& url);
void startUpload(const QString& messageId, const QString& url, const QString& path);
QString getErrorText(QNetworkReply::NetworkError code);
private slots: private slots:
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onRequestError(QNetworkReply::NetworkError code); void onDownloadError(QNetworkReply::NetworkError code);
void onRequestFinished(); void onDownloadFinished();
void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal);
void onUploadError(QNetworkReply::NetworkError code);
void onUploadFinished();
private: private:
bool running; bool running;
QNetworkAccessManager* manager; QNetworkAccessManager* manager;
Storage files; Storage files;
std::map<QString, Download*> downloads; std::map<QString, Transfer*> downloads;
std::map<QString, Transfer*> uploads;
struct Download { struct Transfer {
std::set<QString> messages; std::set<QString> messages;
qreal progress; qreal progress;
QNetworkReply* reply; QNetworkReply* reply;
bool success; bool success;
QString path;
QString url;
QFile* file;
}; };
}; };

View File

@ -31,6 +31,8 @@ Core::Squawk::Squawk(QObject* parent):
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse); connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress); connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError); connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
} }
Core::Squawk::~Squawk() Core::Squawk::~Squawk()
@ -104,7 +106,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
QSettings settings; QSettings settings;
unsigned int reconnects = settings.value("reconnects", 2).toUInt(); unsigned int reconnects = settings.value("reconnects", 2).toUInt();
Account* acc = new Account(login, server, password, name); Account* acc = new Account(login, server, password, name, &network);
acc->setResource(resource); acc->setResource(resource);
acc->setReconnectTimes(reconnects); acc->setReconnectTimes(reconnects);
accounts.push_back(acc); accounts.push_back(acc);
@ -135,6 +137,8 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
connect(acc, &Account::uploadFileError, this, &Squawk::uploadFileError);
QMap<QString, QVariant> map = { QMap<QString, QVariant> map = {
{"login", login}, {"login", login},
{"server", server}, {"server", server},
@ -281,6 +285,17 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da
itr->second->sendMessage(data); itr->second->sendMessage(data);
} }
void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path)
{
AccountsMap::const_iterator itr = amap.find(account);
if (itr == amap.end()) {
qDebug("An attempt to send a message with non existing account, skipping");
return;
}
itr->second->sendMessage(data, path);
}
void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before)
{ {
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);
@ -373,7 +388,6 @@ void Core::Squawk::removeAccountRequest(const QString& name)
acc->deleteLater(); acc->deleteLater();
} }
void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason)
{ {
AccountsMap::const_iterator itr = amap.find(account); AccountsMap::const_iterator itr = amap.find(account);

View File

@ -66,6 +66,8 @@ signals:
void fileLocalPathResponse(const QString& messageId, const QString& path); void fileLocalPathResponse(const QString& messageId, const QString& path);
void downloadFileError(const QString& messageId, const QString& error); void downloadFileError(const QString& messageId, const QString& error);
void downloadFileProgress(const QString& messageId, qreal value); void downloadFileProgress(const QString& messageId, qreal value);
void uploadFileError(const QString& messageId, const QString& error);
void uploadFileProgress(const QString& messageId, qreal value);
void responseVCard(const QString& jid, const Shared::VCard& card); void responseVCard(const QString& jid, const Shared::VCard& card);
public slots: public slots:
@ -78,6 +80,7 @@ public slots:
void disconnectAccount(const QString& account); void disconnectAccount(const QString& account);
void changeState(int state); void changeState(int state);
void sendMessage(const QString& account, const Shared::Message& data); void sendMessage(const QString& account, const Shared::Message& data);
void sendMessage(const QString& account, const Shared::Message& data, const QString& path);
void requestArchive(const QString& account, const QString& jid, int count, const QString& before); void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
void subscribeContact(const QString& account, const QString& jid, const QString& reason); void subscribeContact(const QString& account, const QString& jid, const QString& reason);
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);

View File

@ -73,7 +73,7 @@ void Core::Storage::close()
void Core::Storage::addRecord(const QString& key, const QString& value) void Core::Storage::addRecord(const QString& key, const QString& value)
{ {
if (!opened) { if (!opened) {
throw Archive::Closed("addElement", name.toStdString()); throw Archive::Closed("addRecord", name.toStdString());
} }
const std::string& id = key.toStdString(); const std::string& id = key.toStdString();
const std::string& val = value.toStdString(); const std::string& val = value.toStdString();
@ -99,6 +99,33 @@ void Core::Storage::addRecord(const QString& key, const QString& value)
} }
} }
void Core::Storage::changeRecord(const QString& key, const QString& value)
{
if (!opened) {
throw Archive::Closed("changeRecord", name.toStdString());
}
const std::string& id = key.toStdString();
const std::string& val = value.toStdString();
MDB_val lmdbKey, lmdbData;
lmdbKey.mv_size = id.size();
lmdbKey.mv_data = (char*)id.c_str();
lmdbData.mv_size = val.size();
lmdbData.mv_data = (char*)val.c_str();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
int rc;
rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0);
if (rc != 0) {
mdb_txn_abort(txn);
if (rc) {
throw Archive::Unknown(name.toStdString(), mdb_strerror(rc));
}
} else {
mdb_txn_commit(txn);
}
}
QString Core::Storage::getRecord(const QString& key) const QString Core::Storage::getRecord(const QString& key) const
{ {
if (!opened) { if (!opened) {

View File

@ -39,6 +39,7 @@ public:
void close(); void close();
void addRecord(const QString& key, const QString& value); void addRecord(const QString& key, const QString& value);
void changeRecord(const QString& key, const QString& value);
void removeRecord(const QString& key); void removeRecord(const QString& key);
QString getRecord(const QString& key) const; QString getRecord(const QString& key) const;

2
external/qxmpp vendored

@ -1 +1 @@
Subproject commit b18a57daa33f0fefa5f4c63aa7f448b48d302e0d Subproject commit f8c546c5b701c53d708a38a951fcc734eaee7940

View File

@ -39,7 +39,7 @@ int main(int argc, char *argv[])
QApplication::setApplicationName("squawk"); QApplication::setApplicationName("squawk");
QApplication::setApplicationDisplayName("Squawk"); QApplication::setApplicationDisplayName("Squawk");
QApplication::setApplicationVersion("0.0.5"); QApplication::setApplicationVersion("0.1.1");
QTranslator qtTranslator; QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
@ -90,7 +90,10 @@ int main(int argc, char *argv[])
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount); QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount); QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState); QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
QObject::connect(&w, &Squawk::sendMessage, squawk, &Core::Squawk::sendMessage); QObject::connect(&w, qOverload<const QString&, const Shared::Message&>(&Squawk::sendMessage),
squawk, qOverload<const QString&, const Shared::Message&>(&Core::Squawk::sendMessage));
QObject::connect(&w, qOverload<const QString&, const Shared::Message&, const QString&>(&Squawk::sendMessage),
squawk, qOverload<const QString&, const Shared::Message&, const QString&>(&Core::Squawk::sendMessage));
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive); QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact); QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact); QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
@ -131,13 +134,12 @@ int main(int argc, char *argv[])
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant); QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant); QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse); QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse);
QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress); QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::fileProgress);
QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError); QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::fileError);
QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress);
QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError);
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
//qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
coreThread->start(); coreThread->start();
int result = app.exec(); int result = app.exec();

View File

@ -1,18 +1,18 @@
# Maintainer: Yury Gubich <blue@macaw.me> # Maintainer: Yury Gubich <blue@macaw.me>
pkgname=squawk pkgname=squawk
pkgver=0.0.5 pkgver=0.1.1
pkgrel=1 pkgrel=1
pkgdesc="An XMPP desktop messenger, written on qt" pkgdesc="An XMPP desktop messenger, written on qt"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
url="https://git.macaw.me/blue/squawk" url="https://git.macaw.me/blue/squawk"
license=('GPL3') license=('GPL3')
depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.0.0') depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0')
makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools')
source=("$pkgname-$pkgver.tar.gz") source=("$pkgname-$pkgver.tar.gz")
sha256sums=('12bfc517574387257a82143d8970ec0d8d434ccd32f7ac400355ed5fa18192ab') sha256sums=('d0448f2fdb321e31a40c08b77adc951bafe8d1c271f70d6ffc80fb17cef670cf')
build() { build() {
cd "$srcdir/squawk" cd "$srcdir/squawk"
cmake . -D CMAKE_INSTALL_PREFIX=/usr cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
cmake --build . -j $nproc cmake --build . -j $nproc
} }
package() { package() {

View File

@ -5,77 +5,92 @@
<name>Account</name> <name>Account</name>
<message> <message>
<location filename="../ui/widgets/account.ui" line="14"/> <location filename="../ui/widgets/account.ui" line="14"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="127"/>
<source>Account</source> <source>Account</source>
<translatorcomment>Заголовок окна</translatorcomment> <translatorcomment>Заголовок окна</translatorcomment>
<translation>Учетная запись</translation> <translation>Учетная запись</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="40"/> <location filename="../ui/widgets/account.ui" line="40"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="129"/>
<source>Your account login</source> <source>Your account login</source>
<translation>Имя пользователя Вашей учетной записи</translation> <translation>Имя пользователя Вашей учетной записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="43"/> <location filename="../ui/widgets/account.ui" line="43"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="131"/>
<source>john_smith1987</source> <source>john_smith1987</source>
<translation>ivan_ivanov1987</translation> <translation>ivan_ivanov1987</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="50"/> <location filename="../ui/widgets/account.ui" line="50"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="132"/>
<source>Server</source> <source>Server</source>
<translation>Сервер</translation> <translation>Сервер</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="57"/> <location filename="../ui/widgets/account.ui" line="57"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="134"/>
<source>A server address of your account. Like 404.city or macaw.me</source> <source>A server address of your account. Like 404.city or macaw.me</source>
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation> <translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="60"/> <location filename="../ui/widgets/account.ui" line="60"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="136"/>
<source>macaw.me</source> <source>macaw.me</source>
<translation>macaw.me</translation> <translation>macaw.me</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="67"/> <location filename="../ui/widgets/account.ui" line="67"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="137"/>
<source>Login</source> <source>Login</source>
<translation>Имя учетной записи</translation> <translation>Имя учетной записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="74"/> <location filename="../ui/widgets/account.ui" line="74"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="138"/>
<source>Password</source> <source>Password</source>
<translation>Пароль</translation> <translation>Пароль</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="81"/> <location filename="../ui/widgets/account.ui" line="81"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="140"/>
<source>Password of your account</source> <source>Password of your account</source>
<translation>Пароль вашей учетной записи</translation> <translation>Пароль вашей учетной записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="103"/> <location filename="../ui/widgets/account.ui" line="103"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="145"/>
<source>Name</source> <source>Name</source>
<translation>Имя</translation> <translation>Имя</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="110"/> <location filename="../ui/widgets/account.ui" line="110"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="147"/>
<source>Just a name how would you call this account, doesn&apos;t affect anything</source> <source>Just a name how would you call this account, doesn&apos;t affect anything</source>
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым</translation> <translation>Просто имя, то как Вы называете свою учетную запись, может быть любым</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="113"/> <location filename="../ui/widgets/account.ui" line="113"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="149"/>
<source>John</source> <source>John</source>
<translation>Иван</translation> <translation>Иван</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="120"/> <location filename="../ui/widgets/account.ui" line="120"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="150"/>
<source>Resource</source> <source>Resource</source>
<translation>Ресурс</translation> <translation>Ресурс</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="127"/> <location filename="../ui/widgets/account.ui" line="127"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="152"/>
<source>A resource name like &quot;Home&quot; or &quot;Work&quot;</source> <source>A resource name like &quot;Home&quot; or &quot;Work&quot;</source>
<translation>Имя этой программы для ваших контактов, может быть &quot;Home&quot; или &quot;Phone&quot;</translation> <translation>Имя этой программы для ваших контактов, может быть &quot;Home&quot; или &quot;Phone&quot;</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/account.ui" line="130"/> <location filename="../ui/widgets/account.ui" line="130"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_account.h" line="154"/>
<source>QXmpp</source> <source>QXmpp</source>
<translatorcomment>Ресурс по умолчанию</translatorcomment> <translatorcomment>Ресурс по умолчанию</translatorcomment>
<translation>QXmpp</translation> <translation>QXmpp</translation>
@ -85,31 +100,37 @@
<name>Accounts</name> <name>Accounts</name>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="14"/> <location filename="../ui/widgets/accounts.ui" line="14"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="108"/>
<source>Accounts</source> <source>Accounts</source>
<translation>Учетные записи</translation> <translation>Учетные записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="45"/> <location filename="../ui/widgets/accounts.ui" line="45"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="109"/>
<source>Delete</source> <source>Delete</source>
<translation>Удалить</translation> <translation>Удалить</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="86"/> <location filename="../ui/widgets/accounts.ui" line="86"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="110"/>
<source>Add</source> <source>Add</source>
<translation>Добавить</translation> <translation>Добавить</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="96"/> <location filename="../ui/widgets/accounts.ui" line="96"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="111"/>
<source>Edit</source> <source>Edit</source>
<translation>Редактировать</translation> <translation>Редактировать</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="106"/> <location filename="../ui/widgets/accounts.ui" line="106"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="112"/>
<source>Change password</source> <source>Change password</source>
<translation>Изменить пароль</translation> <translation>Изменить пароль</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/accounts.ui" line="129"/> <location filename="../ui/widgets/accounts.ui" line="129"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_accounts.h" line="113"/>
<location filename="../ui/widgets/accounts.cpp" line="125"/> <location filename="../ui/widgets/accounts.cpp" line="125"/>
<location filename="../ui/widgets/accounts.cpp" line="128"/> <location filename="../ui/widgets/accounts.cpp" line="128"/>
<source>Connect</source> <source>Connect</source>
@ -125,11 +146,12 @@
<name>Conversation</name> <name>Conversation</name>
<message> <message>
<location filename="../ui/widgets/conversation.ui" line="449"/> <location filename="../ui/widgets/conversation.ui" line="449"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_conversation.h" line="324"/>
<source>Type your message here...</source> <source>Type your message here...</source>
<translation>Введите сообщение...</translation> <translation>Введите сообщение...</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/conversation.cpp" line="260"/> <location filename="../ui/widgets/conversation.cpp" line="284"/>
<source>Chose a file to send</source> <source>Chose a file to send</source>
<translation>Выберите файл для отправки</translation> <translation>Выберите файл для отправки</translation>
</message> </message>
@ -281,52 +303,62 @@
<name>JoinConference</name> <name>JoinConference</name>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="14"/> <location filename="../ui/widgets/joinconference.ui" line="14"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="116"/>
<source>Join new conference</source> <source>Join new conference</source>
<translatorcomment>Заголовок окна</translatorcomment> <translatorcomment>Заголовок окна</translatorcomment>
<translation>Присоединиться к новой беседе</translation> <translation>Присоединиться к новой беседе</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="22"/> <location filename="../ui/widgets/joinconference.ui" line="22"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="117"/>
<source>JID</source> <source>JID</source>
<translation>JID</translation> <translation>JID</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="29"/> <location filename="../ui/widgets/joinconference.ui" line="29"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="119"/>
<source>Room JID</source> <source>Room JID</source>
<translation>Jabber-идентификатор беседы</translation> <translation>Jabber-идентификатор беседы</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="32"/> <location filename="../ui/widgets/joinconference.ui" line="32"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="121"/>
<source>identifier@conference.server.org</source> <source>identifier@conference.server.org</source>
<translation>identifier@conference.server.org</translation> <translation>identifier@conference.server.org</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="39"/> <location filename="../ui/widgets/joinconference.ui" line="39"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="122"/>
<source>Account</source> <source>Account</source>
<translation>Учетная запись</translation> <translation>Учетная запись</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="49"/> <location filename="../ui/widgets/joinconference.ui" line="49"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="123"/>
<source>Join on login</source> <source>Join on login</source>
<translation>Автовход</translation> <translation>Автовход</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="56"/> <location filename="../ui/widgets/joinconference.ui" line="56"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="125"/>
<source>If checked Squawk will try to join this conference on login</source> <source>If checked Squawk will try to join this conference on login</source>
<translation>Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении</translation> <translation>Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="66"/> <location filename="../ui/widgets/joinconference.ui" line="66"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="128"/>
<source>Nick name</source> <source>Nick name</source>
<translation>Псевдоним</translation> <translation>Псевдоним</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="73"/> <location filename="../ui/widgets/joinconference.ui" line="73"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="130"/>
<source>Your nick name for that conference. If you leave this field empty your account name will be used as a nick name</source> <source>Your nick name for that conference. If you leave this field empty your account name will be used as a nick name</source>
<translation>Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи</translation> <translation>Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/joinconference.ui" line="76"/> <location filename="../ui/widgets/joinconference.ui" line="76"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_joinconference.h" line="132"/>
<source>John</source> <source>John</source>
<translation>Ivan</translation> <translation>Ivan</translation>
</message> </message>
@ -334,26 +366,58 @@
<context> <context>
<name>Message</name> <name>Message</name>
<message> <message>
<location filename="../ui/utils/message.cpp" line="119"/>
<source>Download</source> <source>Download</source>
<translation>Скачать</translation> <translation>Скачать</translation>
</message> </message>
<message> <message>
<location filename="../ui/utils/message.cpp" line="123"/> <location filename="../ui/utils/message.cpp" line="172"/>
<source>Open</source>
<translation>Открыть</translation>
</message>
</context>
<context>
<name>MessageLine</name>
<message>
<location filename="../ui/utils/messageline.cpp" line="146"/>
<source>Downloading...</source>
<translation>Скачивается...</translation>
</message>
<message>
<location filename="../ui/utils/messageline.cpp" line="258"/>
<location filename="../ui/utils/messageline.cpp" line="324"/>
<source>Download</source>
<translation>Скачать</translation>
</message>
<message>
<location filename="../ui/utils/messageline.cpp" line="259"/>
<source>Push the button to daownload the file</source>
<translation>Нажмите на кнопку что бы загрузить файл</translation>
</message>
<message>
<location filename="../ui/utils/messageline.cpp" line="319"/>
<source>Error uploading file: %1
You can try again</source>
<translation>Ошибка загрузки файла на сервер:
%1
Для того, что бы попробовать снова нажмите на кнопку</translation>
</message>
<message>
<location filename="../ui/utils/messageline.cpp" line="320"/>
<source>Upload</source>
<translation>Загрузить</translation>
</message>
<message>
<location filename="../ui/utils/messageline.cpp" line="325"/>
<source>Error downloading file: %1 <source>Error downloading file: %1
You can try again</source> You can try again</source>
<translation>Ошибка загрузки файла: %1 <translation>Ошибка скачивания файла:
%1
Вы можете попробовать снова</translation> Вы можете попробовать снова</translation>
</message> </message>
<message> <message>
<location filename="../ui/utils/message.cpp" line="125"/> <location filename="../ui/utils/messageline.cpp" line="336"/>
<source>%1 is offering you to download a file</source> <source>Uploading...</source>
<translation>%1 предлагает Вам скачать файл</translation> <translation>Загружается...</translation>
</message>
<message>
<location filename="../ui/utils/message.cpp" line="190"/>
<source>Open</source>
<translation>Открыть</translation>
</message> </message>
</context> </context>
<context> <context>
@ -470,48 +534,57 @@ You can try again</source>
<name>NewContact</name> <name>NewContact</name>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="14"/> <location filename="../ui/widgets/newcontact.ui" line="14"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="103"/>
<source>Add new contact</source> <source>Add new contact</source>
<translatorcomment>Заголовок окна</translatorcomment> <translatorcomment>Заголовок окна</translatorcomment>
<translation>Добавление нового контакта</translation> <translation>Добавление нового контакта</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="22"/> <location filename="../ui/widgets/newcontact.ui" line="22"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="104"/>
<source>Account</source> <source>Account</source>
<translation>Учетная запись</translation> <translation>Учетная запись</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="29"/> <location filename="../ui/widgets/newcontact.ui" line="29"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="106"/>
<source>An account that is going to have new contact</source> <source>An account that is going to have new contact</source>
<translation>Учетная запись для которой будет добавлен контакт</translation> <translation>Учетная запись для которой будет добавлен контакт</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="36"/> <location filename="../ui/widgets/newcontact.ui" line="36"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="108"/>
<source>JID</source> <source>JID</source>
<translation>JID</translation> <translation>JID</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="43"/> <location filename="../ui/widgets/newcontact.ui" line="43"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="110"/>
<source>Jabber id of your new contact</source> <source>Jabber id of your new contact</source>
<translation>Jabber-идентификатор нового контакта</translation> <translation>Jabber-идентификатор нового контакта</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="46"/> <location filename="../ui/widgets/newcontact.ui" line="46"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="112"/>
<source>name@server.dmn</source> <source>name@server.dmn</source>
<translatorcomment>Placeholder поля ввода JID</translatorcomment> <translatorcomment>Placeholder поля ввода JID</translatorcomment>
<translation>name@server.dmn</translation> <translation>name@server.dmn</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="53"/> <location filename="../ui/widgets/newcontact.ui" line="53"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="113"/>
<source>Name</source> <source>Name</source>
<translation>Имя</translation> <translation>Имя</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="60"/> <location filename="../ui/widgets/newcontact.ui" line="60"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="115"/>
<source>The way this new contact will be labeled in your roster (optional)</source> <source>The way this new contact will be labeled in your roster (optional)</source>
<translation>То, как будет подписан контакт в вашем списке контактов (не обязательно)</translation> <translation>То, как будет подписан контакт в вашем списке контактов (не обязательно)</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/newcontact.ui" line="63"/> <location filename="../ui/widgets/newcontact.ui" line="63"/>
<location filename="../build/ui/widgets/squawkWidgets_autogen/include/ui_newcontact.h" line="117"/>
<source>John Smith</source> <source>John Smith</source>
<translation>Иван Иванов</translation> <translation>Иван Иванов</translation>
</message> </message>
@ -520,36 +593,43 @@ You can try again</source>
<name>Squawk</name> <name>Squawk</name>
<message> <message>
<location filename="../ui/squawk.ui" line="14"/> <location filename="../ui/squawk.ui" line="14"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="137"/>
<source>squawk</source> <source>squawk</source>
<translation>Squawk</translation> <translation>Squawk</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="78"/> <location filename="../ui/squawk.ui" line="78"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="143"/>
<source>Settings</source> <source>Settings</source>
<translation>Настройки</translation> <translation>Настройки</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="84"/> <location filename="../ui/squawk.ui" line="84"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="144"/>
<source>Squawk</source> <source>Squawk</source>
<translation>Squawk</translation> <translation>Squawk</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="99"/> <location filename="../ui/squawk.ui" line="99"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="138"/>
<source>Accounts</source> <source>Accounts</source>
<translation>Учетные записи</translation> <translation>Учетные записи</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="108"/> <location filename="../ui/squawk.ui" line="108"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="139"/>
<source>Quit</source> <source>Quit</source>
<translation>Выйти</translation> <translation>Выйти</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="120"/> <location filename="../ui/squawk.ui" line="120"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="140"/>
<source>Add contact</source> <source>Add contact</source>
<translation>Добавить контакт</translation> <translation>Добавить контакт</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.ui" line="132"/> <location filename="../ui/squawk.ui" line="132"/>
<location filename="../build/ui/squawkUI_autogen/include/ui_squawk.h" line="141"/>
<source>Add conference</source> <source>Add conference</source>
<translation>Присоединиться к беседе</translation> <translation>Присоединиться к беседе</translation>
</message> </message>
@ -559,52 +639,52 @@ You can try again</source>
<translation>Список контактов</translation> <translation>Список контактов</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="549"/> <location filename="../ui/squawk.cpp" line="558"/>
<source>Disconnect</source> <source>Disconnect</source>
<translation>Отключить</translation> <translation>Отключить</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="555"/> <location filename="../ui/squawk.cpp" line="564"/>
<source>Connect</source> <source>Connect</source>
<translation>Подключить</translation> <translation>Подключить</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="561"/> <location filename="../ui/squawk.cpp" line="570"/>
<location filename="../ui/squawk.cpp" line="659"/> <location filename="../ui/squawk.cpp" line="668"/>
<source>VCard</source> <source>VCard</source>
<translation>Карточка</translation> <translation>Карточка</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="565"/> <location filename="../ui/squawk.cpp" line="574"/>
<location filename="../ui/squawk.cpp" line="663"/> <location filename="../ui/squawk.cpp" line="672"/>
<location filename="../ui/squawk.cpp" line="703"/> <location filename="../ui/squawk.cpp" line="712"/>
<source>Remove</source> <source>Remove</source>
<translation>Удалить</translation> <translation>Удалить</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="577"/> <location filename="../ui/squawk.cpp" line="586"/>
<source>Open dialog</source> <source>Open dialog</source>
<translation>Открыть диалог</translation> <translation>Открыть диалог</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="587"/> <location filename="../ui/squawk.cpp" line="596"/>
<location filename="../ui/squawk.cpp" line="684"/> <location filename="../ui/squawk.cpp" line="693"/>
<source>Unsubscribe</source> <source>Unsubscribe</source>
<translation>Отписаться</translation> <translation>Отписаться</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="597"/> <location filename="../ui/squawk.cpp" line="606"/>
<location filename="../ui/squawk.cpp" line="693"/> <location filename="../ui/squawk.cpp" line="702"/>
<source>Subscribe</source> <source>Subscribe</source>
<translation>Подписаться</translation> <translation>Подписаться</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="608"/> <location filename="../ui/squawk.cpp" line="617"/>
<source>Rename</source> <source>Rename</source>
<translation>Переименовать</translation> <translation>Переименовать</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="621"/> <location filename="../ui/squawk.cpp" line="630"/>
<source>Input new name for %1 <source>Input new name for %1
or leave it empty for the contact or leave it empty for the contact
to be displayed as %1</source> to be displayed as %1</source>
@ -614,47 +694,47 @@ to be displayed as %1</source>
%1</translation> %1</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="622"/> <location filename="../ui/squawk.cpp" line="631"/>
<source>Renaming %1</source> <source>Renaming %1</source>
<translation>Назначение имени контакту %1</translation> <translation>Назначение имени контакту %1</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="628"/> <location filename="../ui/squawk.cpp" line="637"/>
<source>Groups</source> <source>Groups</source>
<translation>Группы</translation> <translation>Группы</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="643"/> <location filename="../ui/squawk.cpp" line="652"/>
<source>New group</source> <source>New group</source>
<translation>Создать новую группу</translation> <translation>Создать новую группу</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="653"/> <location filename="../ui/squawk.cpp" line="662"/>
<source>New group name</source> <source>New group name</source>
<translation>Имя группы</translation> <translation>Имя группы</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="654"/> <location filename="../ui/squawk.cpp" line="663"/>
<source>Add %1 to a new group</source> <source>Add %1 to a new group</source>
<translation>Добавление %1 в новую группу</translation> <translation>Добавление %1 в новую группу</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="675"/> <location filename="../ui/squawk.cpp" line="684"/>
<source>Open conversation</source> <source>Open conversation</source>
<translation>Открыть окно беседы</translation> <translation>Открыть окно беседы</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="780"/> <location filename="../ui/squawk.cpp" line="789"/>
<source>%1 account card</source> <source>%1 account card</source>
<translation>Карточка учетной записи %1</translation> <translation>Карточка учетной записи %1</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="782"/> <location filename="../ui/squawk.cpp" line="791"/>
<source>%1 contact card</source> <source>%1 contact card</source>
<translation>Карточка контакта %1</translation> <translation>Карточка контакта %1</translation>
</message> </message>
<message> <message>
<location filename="../ui/squawk.cpp" line="794"/> <location filename="../ui/squawk.cpp" line="803"/>
<source>Downloading vCard</source> <source>Downloading vCard</source>
<translation>Получение карточки</translation> <translation>Получение карточки</translation>
</message> </message>
@ -663,119 +743,145 @@ to be displayed as %1</source>
<name>VCard</name> <name>VCard</name>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="65"/> <location filename="../ui/widgets/vcard/vcard.ui" line="65"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="612"/>
<source>Received 12.07.2007 at 17.35</source> <source>Received 12.07.2007 at 17.35</source>
<translation>Не обновлялось</translation> <translation>Не обновлялось</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="100"/> <location filename="../ui/widgets/vcard/vcard.ui" line="100"/>
<location filename="../ui/widgets/vcard/vcard.ui" line="425"/> <location filename="../ui/widgets/vcard/vcard.ui" line="425"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="624"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="627"/>
<source>General</source> <source>General</source>
<translation>Общее</translation> <translation>Общее</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="130"/> <location filename="../ui/widgets/vcard/vcard.ui" line="130"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="613"/>
<source>Organization</source> <source>Organization</source>
<translation>Место работы</translation> <translation>Место работы</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="180"/> <location filename="../ui/widgets/vcard/vcard.ui" line="180"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="614"/>
<source>Middle name</source> <source>Middle name</source>
<translation>Среднее имя</translation> <translation>Среднее имя</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="190"/> <location filename="../ui/widgets/vcard/vcard.ui" line="190"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="615"/>
<source>First name</source> <source>First name</source>
<translation>Имя</translation> <translation>Имя</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="200"/> <location filename="../ui/widgets/vcard/vcard.ui" line="200"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="616"/>
<source>Last name</source> <source>Last name</source>
<translation>Фамилия</translation> <translation>Фамилия</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="226"/> <location filename="../ui/widgets/vcard/vcard.ui" line="226"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="617"/>
<source>Nick name</source> <source>Nick name</source>
<translation>Псевдоним</translation> <translation>Псевдоним</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="252"/> <location filename="../ui/widgets/vcard/vcard.ui" line="252"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="618"/>
<source>Birthday</source> <source>Birthday</source>
<translation>Дата рождения</translation> <translation>Дата рождения</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="272"/> <location filename="../ui/widgets/vcard/vcard.ui" line="272"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="619"/>
<source>Organization name</source> <source>Organization name</source>
<translation>Название организации</translation> <translation>Название организации</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="298"/> <location filename="../ui/widgets/vcard/vcard.ui" line="298"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="620"/>
<source>Unit / Department</source> <source>Unit / Department</source>
<translation>Отдел</translation> <translation>Отдел</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="324"/> <location filename="../ui/widgets/vcard/vcard.ui" line="324"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="621"/>
<source>Role / Profession</source> <source>Role / Profession</source>
<translation>Профессия</translation> <translation>Профессия</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="350"/> <location filename="../ui/widgets/vcard/vcard.ui" line="350"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="622"/>
<source>Job title</source> <source>Job title</source>
<translation>Наименование должности</translation> <translation>Наименование должности</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="390"/> <location filename="../ui/widgets/vcard/vcard.ui" line="390"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="623"/>
<source>Full name</source> <source>Full name</source>
<translation>Полное имя</translation> <translation>Полное имя</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="460"/> <location filename="../ui/widgets/vcard/vcard.ui" line="460"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="625"/>
<source>Personal information</source> <source>Personal information</source>
<translation>Личная информация</translation> <translation>Личная информация</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="653"/> <location filename="../ui/widgets/vcard/vcard.ui" line="653"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="629"/>
<source>Addresses</source> <source>Addresses</source>
<translation>Адреса</translation> <translation>Адреса</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="682"/> <location filename="../ui/widgets/vcard/vcard.ui" line="682"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="630"/>
<source>E-Mail addresses</source> <source>E-Mail addresses</source>
<translation>Адреса электронной почты</translation> <translation>Адреса электронной почты</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="767"/> <location filename="../ui/widgets/vcard/vcard.ui" line="767"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="633"/>
<source>Phone numbers</source> <source>Phone numbers</source>
<translation>Номера телефонов</translation> <translation>Номера телефонов</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="522"/> <location filename="../ui/widgets/vcard/vcard.ui" line="522"/>
<location filename="../ui/widgets/vcard/vcard.ui" line="546"/> <location filename="../ui/widgets/vcard/vcard.ui" line="546"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="628"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="634"/>
<source>Contact</source> <source>Contact</source>
<translation>Контактная информация</translation> <translation>Контактная информация</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="713"/> <location filename="../ui/widgets/vcard/vcard.ui" line="713"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="631"/>
<source>Jabber ID</source> <source>Jabber ID</source>
<translation>Jabber ID</translation> <translation>Jabber ID</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="739"/> <location filename="../ui/widgets/vcard/vcard.ui" line="739"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="632"/>
<source>Web site</source> <source>Web site</source>
<translation>Веб сайт</translation> <translation>Веб сайт</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="798"/> <location filename="../ui/widgets/vcard/vcard.ui" line="798"/>
<location filename="../ui/widgets/vcard/vcard.ui" line="822"/> <location filename="../ui/widgets/vcard/vcard.ui" line="822"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="635"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="636"/>
<source>Description</source> <source>Description</source>
<translation>Описание</translation> <translation>Описание</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="859"/> <location filename="../ui/widgets/vcard/vcard.ui" line="859"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="610"/>
<source>Set avatar</source> <source>Set avatar</source>
<translation>Установить иконку</translation> <translation>Установить иконку</translation>
</message> </message>
<message> <message>
<location filename="../ui/widgets/vcard/vcard.ui" line="868"/> <location filename="../ui/widgets/vcard/vcard.ui" line="868"/>
<location filename="../build/ui/widgets/vcard/vCardUI_autogen/include/ui_vcard.h" line="611"/>
<source>Clear avatar</source> <source>Clear avatar</source>
<translation>Убрать иконку</translation> <translation>Убрать иконку</translation>
</message> </message>

View File

@ -300,7 +300,9 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
conv->setAttribute(Qt::WA_DeleteOnClose); conv->setAttribute(Qt::WA_DeleteOnClose);
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage); connect(conv, qOverload<const Shared::Message&>(&Conversation::sendMessage), this, qOverload<const Shared::Message&>(&Squawk::onConversationMessage));
connect(conv, qOverload<const Shared::Message&, const QString&>(&Conversation::sendMessage),
this, qOverload<const Shared::Message&, const QString&>(&Squawk::onConversationMessage));
connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive); connect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile); connect(conv, &Conversation::requestLocalFile, this, &Squawk::onConversationRequestLocalFile);
connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile); connect(conv, &Conversation::downloadFile, this, &Squawk::onConversationDownloadFile);
@ -367,11 +369,11 @@ void Squawk::onConversationDownloadFile(const QString& messageId, const QString&
} }
} }
void Squawk::downloadFileProgress(const QString& messageId, qreal value) void Squawk::fileProgress(const QString& messageId, qreal value)
{ {
std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId); std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId);
if (itr == requestedFiles.end()) { if (itr == requestedFiles.end()) {
qDebug() << "downloadFileProgress in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; qDebug() << "fileProgress in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping";
return; return;
} else { } else {
const std::set<Models::Roster::ElId>& convs = itr->second; const std::set<Models::Roster::ElId>& convs = itr->second;
@ -379,17 +381,17 @@ void Squawk::downloadFileProgress(const QString& messageId, qreal value)
const Models::Roster::ElId& id = *cItr; const Models::Roster::ElId& id = *cItr;
Conversations::const_iterator c = conversations.find(id); Conversations::const_iterator c = conversations.find(id);
if (c != conversations.end()) { if (c != conversations.end()) {
c->second->responseDownloadProgress(messageId, value); c->second->responseFileProgress(messageId, value);
} }
} }
} }
} }
void Squawk::downloadFileError(const QString& messageId, const QString& error) void Squawk::fileError(const QString& messageId, const QString& error)
{ {
std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId); std::map<QString, std::set<Models::Roster::ElId>>::const_iterator itr = requestedFiles.find(messageId);
if (itr == requestedFiles.end()) { if (itr == requestedFiles.end()) {
qDebug() << "downloadFileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; qDebug() << "fileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping";
return; return;
} else { } else {
const std::set<Models::Roster::ElId>& convs = itr->second; const std::set<Models::Roster::ElId>& convs = itr->second;
@ -397,7 +399,7 @@ void Squawk::downloadFileError(const QString& messageId, const QString& error)
const Models::Roster::ElId& id = *cItr; const Models::Roster::ElId& id = *cItr;
Conversations::const_iterator c = conversations.find(id); Conversations::const_iterator c = conversations.find(id);
if (c != conversations.end()) { if (c != conversations.end()) {
c->second->downloadError(messageId, error); c->second->fileError(messageId, error);
} }
} }
requestedFiles.erase(itr); requestedFiles.erase(itr);
@ -489,10 +491,18 @@ void Squawk::notify(const QString& account, const Shared::Message& msg)
void Squawk::onConversationMessage(const Shared::Message& msg) void Squawk::onConversationMessage(const Shared::Message& msg)
{ {
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
emit sendMessage(conv->getAccount(), msg); emit sendMessage(conv->getAccount(), msg);
} }
void Squawk::onConversationMessage(const Shared::Message& msg, const QString& path)
{
Conversation* conv = static_cast<Conversation*>(sender());
std::map<QString, std::set<Models::Roster::ElId>>::iterator itr = requestedFiles.insert(std::make_pair(msg.getId(), std::set<Models::Roster::ElId>())).first;
itr->second.insert(Models::Roster::ElId(conv->getAccount(), conv->getJid()));
emit sendMessage(conv->getAccount(), msg, path);
}
void Squawk::onConversationRequestArchive(const QString& before) void Squawk::onConversationRequestArchive(const QString& before)
{ {
Conversation* conv = static_cast<Conversation*>(sender()); Conversation* conv = static_cast<Conversation*>(sender());
@ -518,7 +528,6 @@ void Squawk::removeAccount(const QString& account)
++itr; ++itr;
Conversation* conv = lItr->second; Conversation* conv = lItr->second;
disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed); disconnect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
disconnect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage);
disconnect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive); disconnect(conv, &Conversation::requestArchive, this, &Squawk::onConversationRequestArchive);
disconnect(conv, &Conversation::shown, this, &Squawk::onConversationShown); disconnect(conv, &Conversation::shown, this, &Squawk::onConversationShown);
conv->close(); conv->close();

View File

@ -58,6 +58,7 @@ signals:
void disconnectAccount(const QString&); void disconnectAccount(const QString&);
void changeState(int state); void changeState(int state);
void sendMessage(const QString& account, const Shared::Message& data); void sendMessage(const QString& account, const Shared::Message& data);
void sendMessage(const QString& account, const Shared::Message& data, const QString& path);
void requestArchive(const QString& account, const QString& jid, int count, const QString& before); void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
void subscribeContact(const QString& account, const QString& jid, const QString& reason); void subscribeContact(const QString& account, const QString& jid, const QString& reason);
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
@ -97,8 +98,8 @@ public slots:
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data); void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
void fileLocalPathResponse(const QString& messageId, const QString& path); void fileLocalPathResponse(const QString& messageId, const QString& path);
void downloadFileError(const QString& messageId, const QString& error); void fileError(const QString& messageId, const QString& error);
void downloadFileProgress(const QString& messageId, qreal value); void fileProgress(const QString& messageId, qreal value);
void responseVCard(const QString& jid, const Shared::VCard& card); void responseVCard(const QString& jid, const Shared::VCard& card);
private: private:
@ -132,6 +133,7 @@ private slots:
void onComboboxActivated(int index); void onComboboxActivated(int index);
void onRosterItemDoubleClicked(const QModelIndex& item); void onRosterItemDoubleClicked(const QModelIndex& item);
void onConversationMessage(const Shared::Message& msg); void onConversationMessage(const Shared::Message& msg);
void onConversationMessage(const Shared::Message& msg, const QString& path);
void onConversationRequestArchive(const QString& before); void onConversationRequestArchive(const QString& before);
void onRosterContextMenu(const QPoint& point); void onRosterContextMenu(const QPoint& point);
void onConversationShown(); void onConversationShown();

View File

@ -34,16 +34,14 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_
sender(new QLabel(p_sender)), sender(new QLabel(p_sender)),
text(new QLabel()), text(new QLabel()),
shadow(new QGraphicsDropShadowEffect()), shadow(new QGraphicsDropShadowEffect()),
downloadButton(0), button(0),
file(0), file(0),
progress(0), progress(0),
fileComment(new QLabel()), fileComment(new QLabel()),
errorText(""), hasButton(false),
hasDownloadButton(false),
hasProgress(false), hasProgress(false),
hasFile(false), hasFile(false),
commentAdded(false), commentAdded(false)
errorDownloadingFile(false)
{ {
body->setBackgroundRole(QPalette::AlternateBase); body->setBackgroundRole(QPalette::AlternateBase);
body->setAutoFillBackground(true); body->setAutoFillBackground(true);
@ -79,13 +77,13 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_
body->setGraphicsEffect(shadow); body->setGraphicsEffect(shadow);
if (outgoing) { if (outgoing) {
addWidget(body);
addStretch();
} else {
sender->setAlignment(Qt::AlignRight); sender->setAlignment(Qt::AlignRight);
date->setAlignment(Qt::AlignRight); date->setAlignment(Qt::AlignRight);
addStretch(); addStretch();
addWidget(body); addWidget(body);
} else {
addWidget(body);
addStretch();
} }
} }
@ -94,6 +92,7 @@ Message::~Message()
if (!commentAdded) { if (!commentAdded) {
delete fileComment; delete fileComment;
} }
delete body;
} }
QString Message::getId() const QString Message::getId() const
@ -101,47 +100,38 @@ QString Message::getId() const
return msg.getId(); return msg.getId();
} }
QString Message::getFileUrl() const
{
return msg.getOutOfBandUrl();
}
void Message::setSender(const QString& p_sender) void Message::setSender(const QString& p_sender)
{ {
sender->setText(p_sender); sender->setText(p_sender);
} }
void Message::addDownloadDialog() void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip)
{ {
hideFile(); hideFile();
hideProgress(); hideProgress();
if (!hasDownloadButton) { if (!hasButton) {
hideComment(); hideComment();
if (msg.getBody() == msg.getOutOfBandUrl()) { if (msg.getBody() == msg.getOutOfBandUrl()) {
text->setText(""); text->setText("");
text->hide(); text->hide();
} }
downloadButton = new QPushButton(QIcon::fromTheme("download"), tr("Download")); button = new QPushButton(icon, buttonText);
downloadButton->setToolTip("<a href=\"" + msg.getOutOfBandUrl() + "\">" + msg.getOutOfBandUrl() + "</a>"); button->setToolTip(tooltip);
if (errorDownloadingFile) { connect(button, &QPushButton::clicked, this, &Message::buttonClicked);
fileComment->setWordWrap(true); bodyLayout->insertWidget(2, button);
fileComment->setText(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", errorText.toLatin1()))); hasButton = true;
} else {
fileComment->setText(tr("%1 is offering you to download a file").arg(sender->text()));
}
fileComment->show();
connect(downloadButton, &QPushButton::clicked, this, &Message::onDownload);
bodyLayout->insertWidget(2, fileComment);
bodyLayout->insertWidget(3, downloadButton);
hasDownloadButton = true;
commentAdded = true;
} }
} }
void Message::onDownload()
{
emit downloadFile(msg.getId(), msg.getOutOfBandUrl());
}
void Message::setProgress(qreal value) void Message::setProgress(qreal value)
{ {
hideFile(); hideFile();
hideDownload(); hideButton();
if (!hasProgress) { if (!hasProgress) {
hideComment(); hideComment();
if (msg.getBody() == msg.getOutOfBandUrl()) { if (msg.getBody() == msg.getOutOfBandUrl()) {
@ -150,19 +140,15 @@ void Message::setProgress(qreal value)
} }
progress = new QProgressBar(); progress = new QProgressBar();
progress->setRange(0, 100); progress->setRange(0, 100);
fileComment->setText("Downloading...");
fileComment->show();
bodyLayout->insertWidget(2, progress); bodyLayout->insertWidget(2, progress);
bodyLayout->insertWidget(3, fileComment);
hasProgress = true; hasProgress = true;
commentAdded = true;
} }
progress->setValue(value * 100); progress->setValue(value * 100);
} }
void Message::showFile(const QString& path) void Message::showFile(const QString& path)
{ {
hideDownload(); hideButton();
hideProgress(); hideProgress();
if (!hasFile) { if (!hasFile) {
hideComment(); hideComment();
@ -175,16 +161,13 @@ void Message::showFile(const QString& path)
QStringList parts = type.name().split("/"); QStringList parts = type.name().split("/");
QString big = parts.front(); QString big = parts.front();
QFileInfo info(path); QFileInfo info(path);
fileComment = new QLabel();
if (big == "image") { if (big == "image") {
file = new Image(path); file = new Image(path);
} else { } else {
file = new QLabel(); file = new QLabel();
file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50)); file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50));
file->setAlignment(Qt::AlignCenter); file->setAlignment(Qt::AlignCenter);
fileComment->setText(info.fileName()); showComment(info.fileName(), true);
fileComment->setWordWrap(true);
fileComment->show();
} }
file->setContextMenuPolicy(Qt::ActionsContextMenu); file->setContextMenuPolicy(Qt::ActionsContextMenu);
QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file); QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file);
@ -193,9 +176,7 @@ void Message::showFile(const QString& path)
}); });
file->addAction(openAction); file->addAction(openAction);
bodyLayout->insertWidget(2, file); bodyLayout->insertWidget(2, file);
bodyLayout->insertWidget(3, fileComment);
hasFile = true; hasFile = true;
commentAdded = true;
} }
} }
@ -208,13 +189,12 @@ void Message::hideComment()
} }
} }
void Message::hideDownload() void Message::hideButton()
{ {
if (hasDownloadButton) { if (hasButton) {
downloadButton->deleteLater(); button->deleteLater();
downloadButton = 0; button = 0;
hasDownloadButton = false; hasButton = false;
errorDownloadingFile = false;
} }
} }
@ -235,10 +215,29 @@ void Message::hideProgress()
hasProgress = false;; hasProgress = false;;
} }
} }
void Message::showComment(const QString& comment, bool wordWrap)
void Message::showError(const QString& error)
{ {
errorDownloadingFile = true; if (!commentAdded) {
errorText = error; int index = 2;
addDownloadDialog(); if (hasFile) {
index++;
}
if (hasButton) {
index++;
}
if (hasProgress) {
index++;
}
bodyLayout->insertWidget(index, fileComment);
fileComment->show();
commentAdded = true;
}
fileComment->setWordWrap(wordWrap);
fileComment->setText(comment);
} }
const Shared::Message & Message::getMessage() const
{
return msg;
}

View File

@ -30,9 +30,9 @@
#include <QDesktopServices> #include <QDesktopServices>
#include <QUrl> #include <QUrl>
#include "../../global.h" #include "global.h"
#include "../utils/resizer.h" #include "resizer.h"
#include "../utils/image.h" #include "image.h"
/** /**
* @todo write docs * @todo write docs
@ -46,14 +46,17 @@ public:
void setSender(const QString& sender); void setSender(const QString& sender);
QString getId() const; QString getId() const;
QString getFileUrl() const;
const Shared::Message& getMessage() const;
void addDownloadDialog(); void addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip = "");
void showComment(const QString& comment, bool wordWrap = false);
void hideComment();
void showFile(const QString& path); void showFile(const QString& path);
void showError(const QString& error);
void setProgress(qreal value); void setProgress(qreal value);
signals: signals:
void downloadFile(const QString& messageId, const QString& url); void buttonClicked();
private: private:
Shared::Message msg; Shared::Message msg;
@ -63,25 +66,19 @@ private:
QLabel* sender; QLabel* sender;
QLabel* text; QLabel* text;
QGraphicsDropShadowEffect* shadow; QGraphicsDropShadowEffect* shadow;
QPushButton* downloadButton; QPushButton* button;
QLabel* file; QLabel* file;
QProgressBar* progress; QProgressBar* progress;
QLabel* fileComment; QLabel* fileComment;
QString errorText; bool hasButton;
bool hasDownloadButton;
bool hasProgress; bool hasProgress;
bool hasFile; bool hasFile;
bool commentAdded; bool commentAdded;
bool errorDownloadingFile;
private slots:
void onDownload();
private: private:
void hideDownload(); void hideButton();
void hideProgress(); void hideProgress();
void hideFile(); void hideFile();
void hideComment();
}; };
#endif // MESSAGE_H #endif // MESSAGE_H

View File

@ -26,10 +26,12 @@ MessageLine::MessageLine(bool p_room, QWidget* parent):
messageOrder(), messageOrder(),
myMessages(), myMessages(),
palMessages(), palMessages(),
uploadPaths(),
layout(new QVBoxLayout(this)), layout(new QVBoxLayout(this)),
myName(), myName(),
palNames(), palNames(),
views(), uploading(),
downloading(),
room(p_room), room(p_room),
busyShown(false), busyShown(false),
progress() progress()
@ -45,7 +47,7 @@ MessageLine::~MessageLine()
} }
} }
MessageLine::Position MessageLine::message(const Shared::Message& msg) MessageLine::Position MessageLine::message(const Shared::Message& msg, bool forceOutgoing)
{ {
QString id = msg.getId(); QString id = msg.getId();
Index::iterator itr = messageIndex.find(id); Index::iterator itr = messageIndex.find(id);
@ -57,27 +59,32 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg)
QString sender; QString sender;
bool outgoing; bool outgoing;
if (room) { if (forceOutgoing) {
if (msg.getFromResource() == myName) { sender = myName;
sender = myName; outgoing = true;
outgoing = false;
} else {
sender = msg.getFromResource();
outgoing = true;
}
} else { } else {
if (msg.getOutgoing()) { if (room) {
sender = myName; if (msg.getFromResource() == myName) {
outgoing = false; sender = myName;
} else { outgoing = true;
QString jid = msg.getFromJid();
std::map<QString, QString>::iterator itr = palNames.find(jid);
if (itr != palNames.end()) {
sender = itr->second;
} else { } else {
sender = jid; sender = msg.getFromResource();
outgoing = false;
}
} else {
if (msg.getOutgoing()) {
sender = myName;
outgoing = true;
} else {
QString jid = msg.getFromJid();
std::map<QString, QString>::iterator itr = palNames.find(jid);
if (itr != palNames.end()) {
sender = itr->second;
} else {
sender = jid;
}
outgoing = false;
} }
outgoing = true;
} }
} }
@ -90,6 +97,8 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg)
return invalid; return invalid;
} }
if (outgoing) { if (outgoing) {
myMessages.insert(std::make_pair(id, message));
} else {
if (room) { if (room) {
} else { } else {
@ -100,8 +109,6 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg)
} }
pItr->second.insert(std::make_pair(id, message)); pItr->second.insert(std::make_pair(id, message));
} }
} else {
myMessages.insert(std::make_pair(id, message));
} }
messageIndex.insert(std::make_pair(id, message)); messageIndex.insert(std::make_pair(id, message));
int index = std::distance<Order::const_iterator>(messageOrder.begin(), result.first); //need to make with binary indexed tree int index = std::distance<Order::const_iterator>(messageOrder.begin(), result.first); //need to make with binary indexed tree
@ -125,14 +132,29 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg)
layout->insertLayout(index, message); layout->insertLayout(index, message);
} }
if (msg.hasOutOfBandUrl()) {\ if (msg.hasOutOfBandUrl()) {
emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl());
connect(message, &Message::downloadFile, this, &MessageLine::downloadFile); connect(message, &Message::buttonClicked, this, &MessageLine::onDownload);
} }
return res; return res;
} }
void MessageLine::onDownload()
{
Message* msg = static_cast<Message*>(sender());
QString messageId = msg->getId();
Index::const_iterator itr = downloading.find(messageId);
if (itr == downloading.end()) {
downloading.insert(std::make_pair(messageId, msg));
msg->setProgress(0);
msg->showComment(tr("Downloading..."));
emit downloadFile(messageId, msg->getFileUrl());
} else {
qDebug() << "An attempt to initiate download for already downloading file" << msg->getFileUrl() << ", skipping";
}
}
void MessageLine::setMyName(const QString& name) void MessageLine::setMyName(const QString& name)
{ {
myName = name; myName = name;
@ -192,11 +214,11 @@ void MessageLine::hideBusyIndicator()
} }
} }
void MessageLine::responseDownloadProgress(const QString& messageId, qreal progress) void MessageLine::fileProgress(const QString& messageId, qreal progress)
{ {
Index::const_iterator itr = messageIndex.find(messageId); Index::const_iterator itr = messageIndex.find(messageId);
if (itr == messageIndex.end()) { if (itr == messageIndex.end()) {
//TODO may be some logging, that's not normal
} else { } else {
itr->second->setProgress(progress); itr->second->setProgress(progress);
} }
@ -208,21 +230,121 @@ void MessageLine::responseLocalFile(const QString& messageId, const QString& pat
if (itr == messageIndex.end()) { if (itr == messageIndex.end()) {
} else { } else {
Index::const_iterator uItr = uploading.find(messageId);
if (path.size() > 0) { if (path.size() > 0) {
itr->second->showFile(path); Index::const_iterator dItr = downloading.find(messageId);
if (dItr != downloading.end()) {
downloading.erase(dItr);
itr->second->showFile(path);
} else {
if (uItr != uploading.end()) {
uploading.erase(uItr);
std::map<QString, QString>::const_iterator muItr = uploadPaths.find(messageId);
if (muItr != uploadPaths.end()) {
uploadPaths.erase(muItr);
}
if (room) {
removeMessage(messageId);
} else {
Shared::Message msg = itr->second->getMessage();
removeMessage(messageId);
msg.setCurrentTime();
message(msg);
itr = messageIndex.find(messageId);
itr->second->showFile(path);
}
} else {
itr->second->showFile(path); //then it is already cached file
}
}
} else { } else {
itr->second->addDownloadDialog(); if (uItr == uploading.end()) {
const Shared::Message& msg = itr->second->getMessage();
itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "<a href=\"" + msg.getOutOfBandUrl() + "\">" + msg.getOutOfBandUrl() + "</a>");
itr->second->showComment(tr("Push the button to daownload the file"));
} else {
qDebug() << "An unhandled state for file uploading - empty path";
}
} }
} }
} }
void MessageLine::downloadError(const QString& messageId, const QString& error) void MessageLine::removeMessage(const QString& messageId)
{ {
Index::const_iterator itr = messageIndex.find(messageId); Index::const_iterator itr = messageIndex.find(messageId);
if (itr == messageIndex.end()) { if (itr != messageIndex.end()) {
Message* ui = itr->second;
const Shared::Message& msg = ui->getMessage();
messageIndex.erase(itr);
Order::const_iterator oItr = messageOrder.find(msg.getTime());
if (oItr != messageOrder.end()) {
messageOrder.erase(oItr);
} else {
qDebug() << "An attempt to remove message from messageLine, but it wasn't found in order";
}
if (msg.getOutgoing()) {
Index::const_iterator mItr = myMessages.find(messageId);
if (mItr != myMessages.end()) {
myMessages.erase(mItr);
} else {
qDebug() << "Error removing message: it seems to be outgoing yet it wasn't found in outgoing messages";
}
} else {
if (room) {
} else {
QString jid = msg.getFromJid();
std::map<QString, Index>::iterator pItr = palMessages.find(jid);
if (pItr != palMessages.end()) {
Index& pMsgs = pItr->second;
Index::const_iterator pmitr = pMsgs.find(messageId);
if (pmitr != pMsgs.end()) {
pMsgs.erase(pmitr);
} else {
qDebug() << "Error removing message: it seems to be incoming yet it wasn't found among messages from that penpal";
}
}
}
}
ui->deleteLater();
qDebug() << "message" << messageId << "has been removed";
} else { } else {
itr->second->showError(error); qDebug() << "An attempt to remove non existing message from messageLine";
} }
} }
void MessageLine::fileError(const QString& messageId, const QString& error)
{
Index::const_iterator itr = downloading.find(messageId);
if (itr == downloading.end()) {
Index::const_iterator itr = uploading.find(messageId);
if (itr == uploading.end()) {
//TODO may be some logging, that's not normal
} else {
itr->second->showComment(tr("Error uploading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true);
itr->second->addButton(QIcon::fromTheme("upload"), tr("Upload"));
}
} else {
const Shared::Message& msg = itr->second->getMessage();
itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "<a href=\"" + msg.getOutOfBandUrl() + "\">" + msg.getOutOfBandUrl() + "</a>");
itr->second->showComment(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true);
}
}
void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path)
{
message(msg, true);
QString id = msg.getId();
Message* ui = messageIndex.find(id)->second;
connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry;
ui->setProgress(0);
ui->showComment(tr("Uploading..."));
uploading.insert(std::make_pair(id, ui));
uploadPaths.insert(std::make_pair(id, path));
emit uploadFile(msg, path);
}
void MessageLine::onUpload()
{
//TODO retry
}

View File

@ -43,24 +43,31 @@ public:
MessageLine(bool p_room, QWidget* parent = 0); MessageLine(bool p_room, QWidget* parent = 0);
~MessageLine(); ~MessageLine();
Position message(const Shared::Message& msg); Position message(const Shared::Message& msg, bool forceOutgoing = false);
void setMyName(const QString& name); void setMyName(const QString& name);
void setPalName(const QString& jid, const QString& name); void setPalName(const QString& jid, const QString& name);
QString firstMessageId() const; QString firstMessageId() const;
void showBusyIndicator(); void showBusyIndicator();
void hideBusyIndicator(); void hideBusyIndicator();
void responseLocalFile(const QString& messageId, const QString& path); void responseLocalFile(const QString& messageId, const QString& path);
void downloadError(const QString& messageId, const QString& error); void fileError(const QString& messageId, const QString& error);
void responseDownloadProgress(const QString& messageId, qreal progress); void fileProgress(const QString& messageId, qreal progress);
void appendMessageWithUpload(const Shared::Message& msg, const QString& path);
void removeMessage(const QString& messageId);
signals: signals:
void resize(int amount); void resize(int amount);
void downloadFile(const QString& messageId, const QString& url); void downloadFile(const QString& messageId, const QString& url);
void uploadFile(const Shared::Message& msg, const QString& path);
void requestLocalFile(const QString& messageId, const QString& url); void requestLocalFile(const QString& messageId, const QString& url);
protected: protected:
void resizeEvent(QResizeEvent * event) override; void resizeEvent(QResizeEvent * event) override;
protected:
void onDownload();
void onUpload();
private: private:
struct Comparator { struct Comparator {
bool operator()(const Shared::Message& a, const Shared::Message& b) const { bool operator()(const Shared::Message& a, const Shared::Message& b) const {
@ -76,11 +83,13 @@ private:
Order messageOrder; Order messageOrder;
Index myMessages; Index myMessages;
std::map<QString, Index> palMessages; std::map<QString, Index> palMessages;
std::map<QString, QString> uploadPaths;
QVBoxLayout* layout; QVBoxLayout* layout;
QString myName; QString myName;
std::map<QString, QString> palNames; std::map<QString, QString> palNames;
std::deque<QHBoxLayout*> views; Index uploading;
Index downloading;
bool room; bool room;
bool busyShown; bool busyShown;
Progress progress; Progress progress;

View File

@ -27,7 +27,8 @@ QObject(parent)
bool Resizer::eventFilter(QObject* obj, QEvent* event) bool Resizer::eventFilter(QObject* obj, QEvent* event)
{ {
if (event->type() == QEvent::Resize) { if (event->type() == QEvent::Resize) {
emit resized(); QResizeEvent* ev = static_cast<QResizeEvent*>(event);
emit resized(ev->oldSize(), ev->size());
} }
return false; return false;

View File

@ -22,6 +22,7 @@
#include <QObject> #include <QObject>
#include <QWidget> #include <QWidget>
#include <QEvent> #include <QEvent>
#include <QResizeEvent>
/** /**
* @todo write docs * @todo write docs
@ -35,7 +36,7 @@ protected:
bool eventFilter(QObject* obj, QEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override;
signals: signals:
void resized(); void resized(const QSize& oldSize, const QSize& newSize);
}; };
#endif // RESIZER_H #endif // RESIZER_H

View File

@ -36,7 +36,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co
line(new MessageLine(muc)), line(new MessageLine(muc)),
m_ui(new Ui::Conversation()), m_ui(new Ui::Conversation()),
ker(), ker(),
res(), scrollResizeCatcher(),
attachResizeCatcher(),
vis(), vis(),
thread(), thread(),
statusIcon(0), statusIcon(0),
@ -60,14 +61,17 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co
statusLabel = m_ui->statusLabel; statusLabel = m_ui->statusLabel;
connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed);
connect(&res, &Resizer::resized, this, &Conversation::onScrollResize); connect(&scrollResizeCatcher, &Resizer::resized, this, &Conversation::onScrollResize);
connect(&attachResizeCatcher, &Resizer::resized, this, &Conversation::onAttachResize);
connect(&vis, &VisibilityCatcher::shown, this, &Conversation::onScrollResize); connect(&vis, &VisibilityCatcher::shown, this, &Conversation::onScrollResize);
connect(&vis, &VisibilityCatcher::hidden, this, &Conversation::onScrollResize); connect(&vis, &VisibilityCatcher::hidden, this, &Conversation::onScrollResize);
connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed);
connect(line, &MessageLine::resize, this, &Conversation::onMessagesResize); connect(line, &MessageLine::resize, this, &Conversation::onMessagesResize);
connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile); connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile);
connect(line, &MessageLine::uploadFile, this, qOverload<const Shared::Message&, const QString&>(&Conversation::sendMessage));
connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile); connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile);
connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach);
connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton);
m_ui->messageEditor->installEventFilter(&ker); m_ui->messageEditor->installEventFilter(&ker);
@ -77,7 +81,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co
vs->setBackgroundRole(QPalette::Base); vs->setBackgroundRole(QPalette::Base);
vs->setAutoFillBackground(true); vs->setAutoFillBackground(true);
connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged); connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged);
m_ui->scrollArea->installEventFilter(&res); m_ui->scrollArea->installEventFilter(&scrollResizeCatcher);
m_ui->filesPanel->installEventFilter(&attachResizeCatcher);
applyVisualEffects(); applyVisualEffects();
} }
@ -184,6 +189,25 @@ void Conversation::onEnterPressed()
m_ui->messageEditor->clear(); m_ui->messageEditor->clear();
handleSendMessage(body); handleSendMessage(body);
} }
if (filesToAttach.size() > 0) {
for (Badge* badge : filesToAttach) {
Shared::Message msg;
if (isMuc) {
msg.setType(Shared::Message::groupChat);
} else {
msg.setType(Shared::Message::chat);
msg.setToResource(activePalResource);
}
msg.setFromJid(myJid);
msg.setFromResource(myResource);
msg.setToJid(palJid);
msg.setOutgoing(true);
msg.generateRandomId();
msg.setCurrentTime();
line->appendMessageWithUpload(msg, badge->id);
}
clearAttachedFiles();
}
} }
void Conversation::onMessagesResize(int amount) void Conversation::onMessagesResize(int amount)
@ -294,14 +318,14 @@ void Conversation::onScrollResize()
} }
} }
void Conversation::responseDownloadProgress(const QString& messageId, qreal progress) void Conversation::responseFileProgress(const QString& messageId, qreal progress)
{ {
line->responseDownloadProgress(messageId, progress); line->fileProgress(messageId, progress);
} }
void Conversation::downloadError(const QString& messageId, const QString& error) void Conversation::fileError(const QString& messageId, const QString& error)
{ {
line->downloadError(messageId, error); line->fileError(messageId, error);
} }
void Conversation::responseLocalFile(const QString& messageId, const QString& path) void Conversation::responseLocalFile(const QString& messageId, const QString& path)
@ -352,6 +376,30 @@ void Conversation::clearAttachedFiles()
filesLayout->setContentsMargins(0, 0, 0, 0); filesLayout->setContentsMargins(0, 0, 0, 0);
} }
void Conversation::onClearButton()
{
clearAttachedFiles();
m_ui->messageEditor->clear();
}
void Conversation::onAttachResize(const QSize& oldSize, const QSize& newSize)
{
int oh = oldSize.height();
int nh = newSize.height();
int d = oh - nh;
if (d != 0) {
QList<int> cs = m_ui->splitter->sizes();
cs.first() += d;
cs.last() -=d;
m_ui->splitter->setSizes(cs);
m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->maximum());
}
}
bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event) bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event)
{ {
if (event->type() == QEvent::Show) { if (event->type() == QEvent::Show) {

View File

@ -75,11 +75,12 @@ public:
void responseArchive(const std::list<Shared::Message> list); void responseArchive(const std::list<Shared::Message> list);
void showEvent(QShowEvent * event) override; void showEvent(QShowEvent * event) override;
void responseLocalFile(const QString& messageId, const QString& path); void responseLocalFile(const QString& messageId, const QString& path);
void downloadError(const QString& messageId, const QString& error); void fileError(const QString& messageId, const QString& error);
void responseDownloadProgress(const QString& messageId, qreal progress); void responseFileProgress(const QString& messageId, qreal progress);
signals: signals:
void sendMessage(const Shared::Message& message); void sendMessage(const Shared::Message& message);
void sendMessage(const Shared::Message& message, const QString& path);
void requestArchive(const QString& before); void requestArchive(const QString& before);
void shown(); void shown();
void requestLocalFile(const QString& messageId, const QString& url); void requestLocalFile(const QString& messageId, const QString& url);
@ -101,7 +102,9 @@ protected slots:
void onAttach(); void onAttach();
void onFileSelected(); void onFileSelected();
void onScrollResize(); void onScrollResize();
void onAttachResize(const QSize& oldSize, const QSize& newSize);
void onBadgeClose(); void onBadgeClose();
void onClearButton();
public: public:
const bool isMuc; const bool isMuc;
@ -120,7 +123,8 @@ protected:
MessageLine* line; MessageLine* line;
QScopedPointer<Ui::Conversation> m_ui; QScopedPointer<Ui::Conversation> m_ui;
KeyEnterReceiver ker; KeyEnterReceiver ker;
Resizer res; Resizer scrollResizeCatcher;
Resizer attachResizeCatcher;
VisibilityCatcher vis; VisibilityCatcher vis;
QString thread; QString thread;
QLabel* statusIcon; QLabel* statusIcon;