feat/tray_pictogram #70
29
CHANGELOG.md
29
CHANGELOG.md
@ -1,6 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Squawk 0.2.1 (UNRELEASED)
|
## Squawk 0.2.2 (May 05, 2022)
|
||||||
|
### Bug fixes
|
||||||
|
- now when you remove an account it actually gets removed
|
||||||
|
- segfault on unitialized Availability in some rare occesions
|
||||||
|
- fixed crash when you open a dialog with someone that has only error messages in archive
|
||||||
|
- message height is now calculated correctly on Chinese and Japanese paragraphs
|
||||||
|
- the app doesn't crash on SIGINT anymore
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- there is a way to disable an account and it wouldn't connect when you change availability
|
||||||
|
- if you cancel password query an account becomes inactive and doesn't annoy you anymore
|
||||||
|
- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password
|
||||||
|
- if left the password field empty and chose KWallet as a storage Squawk will try to get that passord from KWallet before asking you to input it
|
||||||
|
- accounts now connect to the server asyncronously - if one is stopped on password prompt another is connecting
|
||||||
|
- actualized translations, added English localization file
|
||||||
|
|
||||||
|
### New features
|
||||||
|
- new "About" window with links, license, gratitudes
|
||||||
|
- if the authentication failed Squawk will ask againg for your password and login
|
||||||
|
- now there is an amount of unread messages showing on top of Squawk launcher icon
|
||||||
|
- notifications now have buttons to open a conversation or to mark that message as read
|
||||||
|
|
||||||
|
## Squawk 0.2.1 (Apr 02, 2022)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
- build in release mode now no longer spams warnings
|
- build in release mode now no longer spams warnings
|
||||||
- build now correctly installs all build plugin libs
|
- build now correctly installs all build plugin libs
|
||||||
@ -11,12 +33,13 @@
|
|||||||
### Improvements
|
### Improvements
|
||||||
- reduced amount of places where platform specific path separator is used
|
- reduced amount of places where platform specific path separator is used
|
||||||
- now message input is automatically focused when you open a dialog or a room
|
- now message input is automatically focused when you open a dialog or a room
|
||||||
|
- what() method on unhandled exception now actually tells what happened
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
- the settings are here! You con config different stuff from there
|
- the settings are here! You con config different stuff from there
|
||||||
- now it's possible to set up different qt styles from settings
|
- now it's possible to set up different qt styles from settings
|
||||||
- if you have KConfig nad KConfigWidgets packages installed - you can chose from global color schemes
|
- if you have KConfig nad KConfigWidgets packages installed - you can choose from global color schemes
|
||||||
- it's possible now to chose a folder where squawk is going to store downloaded files
|
- it's possible now to choose a folder where squawk is going to store downloaded files
|
||||||
- now you can correct your message
|
- now you can correct your message
|
||||||
|
|
||||||
## Squawk 0.2.0 (Jan 10, 2022)
|
## Squawk 0.2.0 (Jan 10, 2022)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required(VERSION 3.4)
|
cmake_minimum_required(VERSION 3.4)
|
||||||
project(squawk VERSION 0.2.1 LANGUAGES CXX)
|
project(squawk VERSION 0.2.2 LANGUAGES CXX)
|
||||||
|
|
||||||
cmake_policy(SET CMP0076 NEW)
|
cmake_policy(SET CMP0076 NEW)
|
||||||
cmake_policy(SET CMP0079 NEW)
|
cmake_policy(SET CMP0079 NEW)
|
||||||
@ -148,6 +148,7 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
|||||||
target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS})
|
target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS})
|
||||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||||
|
|
||||||
|
add_subdirectory(main)
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
add_subdirectory(external/simpleCrypt)
|
add_subdirectory(external/simpleCrypt)
|
||||||
add_subdirectory(packaging)
|
add_subdirectory(packaging)
|
||||||
@ -159,6 +160,8 @@ add_subdirectory(ui)
|
|||||||
|
|
||||||
# Install the executable
|
# Install the executable
|
||||||
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
|
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
|
||||||
|
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
|
@ -595,17 +595,17 @@ pointer to where the full notice is found.
|
|||||||
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
Copyright (C) <year> <name of author>
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
|
||||||
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
|
||||||
|
|
||||||
![Squawk screenshot](https://macaw.me/images/squawk/0.2.0.png)
|
![Squawk screenshot](https://macaw.me/images/squawk/0.2.2.png)
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
|
@ -6,14 +6,12 @@ endif(WIN32)
|
|||||||
target_sources(squawk PRIVATE
|
target_sources(squawk PRIVATE
|
||||||
account.cpp
|
account.cpp
|
||||||
account.h
|
account.h
|
||||||
adapterFuctions.cpp
|
adapterfunctions.cpp
|
||||||
archive.cpp
|
adapterfunctions.h
|
||||||
archive.h
|
|
||||||
conference.cpp
|
conference.cpp
|
||||||
conference.h
|
conference.h
|
||||||
contact.cpp
|
contact.cpp
|
||||||
contact.h
|
contact.h
|
||||||
main.cpp
|
|
||||||
networkaccess.cpp
|
networkaccess.cpp
|
||||||
networkaccess.h
|
networkaccess.h
|
||||||
rosteritem.cpp
|
rosteritem.cpp
|
||||||
@ -22,13 +20,10 @@ target_sources(squawk PRIVATE
|
|||||||
signalcatcher.h
|
signalcatcher.h
|
||||||
squawk.cpp
|
squawk.cpp
|
||||||
squawk.h
|
squawk.h
|
||||||
storage.cpp
|
|
||||||
storage.h
|
|
||||||
urlstorage.cpp
|
|
||||||
urlstorage.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_subdirectory(handlers)
|
add_subdirectory(handlers)
|
||||||
|
add_subdirectory(storage)
|
||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
582
core/account.cpp
582
core/account.cpp
@ -22,7 +22,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, NetworkAccess* p_net, QObject* parent):
|
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent):
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
name(p_name),
|
name(p_name),
|
||||||
archiveQueries(),
|
archiveQueries(),
|
||||||
@ -41,13 +41,15 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
rcpm(new QXmppMessageReceiptManager()),
|
rcpm(new QXmppMessageReceiptManager()),
|
||||||
reconnectScheduled(false),
|
reconnectScheduled(false),
|
||||||
reconnectTimer(new QTimer),
|
reconnectTimer(new QTimer),
|
||||||
avatarHash(),
|
|
||||||
avatarType(),
|
|
||||||
ownVCardRequestInProgress(false),
|
|
||||||
network(p_net),
|
network(p_net),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain),
|
||||||
|
lastError(Error::none),
|
||||||
|
pepSupport(false),
|
||||||
|
active(p_active),
|
||||||
|
notReadyPassword(false),
|
||||||
mh(new MessageHandler(this)),
|
mh(new MessageHandler(this)),
|
||||||
rh(new RosterHandler(this))
|
rh(new RosterHandler(this)),
|
||||||
|
vh(new VCardHandler(this))
|
||||||
{
|
{
|
||||||
config.setUser(p_login);
|
config.setUser(p_login);
|
||||||
config.setDomain(p_server);
|
config.setDomain(p_server);
|
||||||
@ -73,10 +75,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
|
|
||||||
client.addExtension(mm);
|
client.addExtension(mm);
|
||||||
client.addExtension(bm);
|
client.addExtension(bm);
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
client.addExtension(um);
|
client.addExtension(um);
|
||||||
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
|
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
|
||||||
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
|
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
|
||||||
@ -91,62 +89,18 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||||||
client.addExtension(rcpm);
|
client.addExtension(rcpm);
|
||||||
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
|
||||||
|
|
||||||
|
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
|
||||||
path += "/" + name;
|
|
||||||
QDir dir(path);
|
|
||||||
|
|
||||||
if (!dir.exists()) {
|
|
||||||
bool res = dir.mkpath(path);
|
|
||||||
if (!res) {
|
|
||||||
qDebug() << "Couldn't create a cache directory for account" << name;
|
|
||||||
throw 22;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile* avatar = new QFile(path + "/avatar.png");
|
|
||||||
QString type = "png";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.jpg");
|
|
||||||
type = "jpg";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.jpeg");
|
|
||||||
type = "jpeg";
|
|
||||||
if (!avatar->exists()) {
|
|
||||||
delete avatar;
|
|
||||||
avatar = new QFile(path + "/avatar.gif");
|
|
||||||
type = "gif";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avatar->exists()) {
|
|
||||||
if (avatar->open(QFile::ReadOnly)) {
|
|
||||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
|
||||||
sha1.addData(avatar);
|
|
||||||
avatarHash = sha1.result();
|
|
||||||
avatarType = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (avatarType.size() != 0) {
|
|
||||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
|
||||||
presence.setPhotoHash(avatarHash.toUtf8());
|
|
||||||
} else {
|
|
||||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
reconnectTimer->setSingleShot(true);
|
reconnectTimer->setSingleShot(true);
|
||||||
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
|
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
|
||||||
|
|
||||||
// QXmppLogger* logger = new QXmppLogger(this);
|
if (name == "Test") {
|
||||||
// logger->setLoggingType(QXmppLogger::SignalLogging);
|
QXmppLogger* logger = new QXmppLogger(this);
|
||||||
// client.setLogger(logger);
|
logger->setLoggingType(QXmppLogger::SignalLogging);
|
||||||
//
|
client.setLogger(logger);
|
||||||
// QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
|
|
||||||
// qDebug() << text;
|
QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
|
||||||
// });
|
qDebug() << text;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Account::~Account()
|
Account::~Account()
|
||||||
@ -160,6 +114,7 @@ Account::~Account()
|
|||||||
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
|
||||||
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
|
||||||
|
|
||||||
|
delete vh;
|
||||||
delete mh;
|
delete mh;
|
||||||
delete rh;
|
delete rh;
|
||||||
|
|
||||||
@ -185,7 +140,12 @@ void Core::Account::connect()
|
|||||||
reconnectTimer->stop();
|
reconnectTimer->stop();
|
||||||
}
|
}
|
||||||
if (state == Shared::ConnectionState::disconnected) {
|
if (state == Shared::ConnectionState::disconnected) {
|
||||||
client.connectToServer(config, presence);
|
if (notReadyPassword) {
|
||||||
|
emit needPassword();
|
||||||
|
} else {
|
||||||
|
client.connectToServer(config, presence);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qDebug("An attempt to connect an account which is already connected, skipping");
|
qDebug("An attempt to connect an account which is already connected, skipping");
|
||||||
}
|
}
|
||||||
@ -224,6 +184,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
|
|||||||
dm->requestItems(getServer());
|
dm->requestItems(getServer());
|
||||||
dm->requestInfo(getServer());
|
dm->requestInfo(getServer());
|
||||||
}
|
}
|
||||||
|
lastError = Error::none;
|
||||||
emit connectionStateChanged(state);
|
emit connectionStateChanged(state);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -255,45 +216,17 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
|
|||||||
|
|
||||||
void Core::Account::reconnect()
|
void Core::Account::reconnect()
|
||||||
{
|
{
|
||||||
if (state == Shared::ConnectionState::connected && !reconnectScheduled) {
|
if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting
|
||||||
reconnectScheduled = true;
|
if (state == Shared::ConnectionState::connected) {
|
||||||
reconnectTimer->start(500);
|
reconnectScheduled = true;
|
||||||
client.disconnectFromServer();
|
reconnectTimer->start(500);
|
||||||
} else {
|
client.disconnectFromServer();
|
||||||
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
|
} else {
|
||||||
|
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::Account::getName() const {
|
|
||||||
return name;}
|
|
||||||
|
|
||||||
QString Core::Account::getLogin() const {
|
|
||||||
return config.user();}
|
|
||||||
|
|
||||||
QString Core::Account::getPassword() const {
|
|
||||||
return config.password();}
|
|
||||||
|
|
||||||
QString Core::Account::getServer() const {
|
|
||||||
return config.domain();}
|
|
||||||
|
|
||||||
Shared::AccountPassword Core::Account::getPasswordType() const {
|
|
||||||
return passwordType;}
|
|
||||||
|
|
||||||
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
|
|
||||||
passwordType = pt; }
|
|
||||||
|
|
||||||
void Core::Account::setLogin(const QString& p_login) {
|
|
||||||
config.setUser(p_login);}
|
|
||||||
|
|
||||||
void Core::Account::setName(const QString& p_name) {
|
|
||||||
name = p_name;}
|
|
||||||
|
|
||||||
void Core::Account::setPassword(const QString& p_password) {
|
|
||||||
config.setPassword(p_password);}
|
|
||||||
|
|
||||||
void Core::Account::setServer(const QString& p_server) {
|
|
||||||
config.setDomain(p_server);}
|
|
||||||
|
|
||||||
Shared::Availability Core::Account::getAvailability() const
|
Shared::Availability Core::Account::getAvailability() const
|
||||||
{
|
{
|
||||||
if (state == Shared::ConnectionState::connected) {
|
if (state == Shared::ConnectionState::connected) {
|
||||||
@ -325,32 +258,11 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
|||||||
QString jid = comps.front().toLower();
|
QString jid = comps.front().toLower();
|
||||||
QString resource = comps.back();
|
QString resource = comps.back();
|
||||||
|
|
||||||
QString myJid = getLogin() + "@" + getServer();
|
if (jid == getBareJid()) {
|
||||||
|
|
||||||
if (jid == myJid) {
|
|
||||||
if (resource == getResource()) {
|
if (resource == getResource()) {
|
||||||
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
|
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
|
||||||
} else {
|
} else {
|
||||||
if (!ownVCardRequestInProgress) {
|
vh->handleOtherPresenceOfMyAccountChange(p_presence);
|
||||||
switch (p_presence.vCardUpdateType()) {
|
|
||||||
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
|
||||||
if (avatarHash != p_presence.photoHash()) {
|
|
||||||
vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RosterItem* item = rh->getRosterItem(jid);
|
RosterItem* item = rh->getRosterItem(jid);
|
||||||
@ -392,18 +304,6 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::Account::getResource() const {
|
|
||||||
return config.resource();}
|
|
||||||
|
|
||||||
void Core::Account::setResource(const QString& p_resource) {
|
|
||||||
config.setResource(p_resource);}
|
|
||||||
|
|
||||||
QString Core::Account::getFullJid() const {
|
|
||||||
return getLogin() + "@" + getServer() + "/" + getResource();}
|
|
||||||
|
|
||||||
void Core::Account::sendMessage(const Shared::Message& data) {
|
|
||||||
mh->sendMessage(data);}
|
|
||||||
|
|
||||||
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
|
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
|
||||||
{
|
{
|
||||||
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
|
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
|
||||||
@ -517,6 +417,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
qDebug() << "Error";
|
qDebug() << "Error";
|
||||||
QString errorText;
|
QString errorText;
|
||||||
QString errorType;
|
QString errorType;
|
||||||
|
lastError = Error::other;
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case QXmppClient::SocketError:
|
case QXmppClient::SocketError:
|
||||||
errorText = client.socketErrorString();
|
errorText = client.socketErrorString();
|
||||||
@ -558,6 +459,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||||||
break;
|
break;
|
||||||
case QXmppStanza::Error::NotAuthorized:
|
case QXmppStanza::Error::NotAuthorized:
|
||||||
errorText = "Authentication error";
|
errorText = "Authentication error";
|
||||||
|
lastError = Error::authentication;
|
||||||
break;
|
break;
|
||||||
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
|
||||||
case QXmppStanza::Error::PaymentRequired:
|
case QXmppStanza::Error::PaymentRequired:
|
||||||
@ -667,6 +569,166 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined)
|
|||||||
conf->setJoined(joined);
|
conf->setJoined(joined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
|
||||||
|
{
|
||||||
|
if (items.from() == getServer()) {
|
||||||
|
std::set<QString> needToRequest;
|
||||||
|
qDebug() << "Server items list received for account " << name << ":";
|
||||||
|
for (QXmppDiscoveryIq::Item item : items.items()) {
|
||||||
|
QString jid = item.jid();
|
||||||
|
if (jid != getServer()) {
|
||||||
|
qDebug() << " Node" << jid;
|
||||||
|
needToRequest.insert(jid);
|
||||||
|
} else {
|
||||||
|
qDebug() << " " << item.node().toStdString().c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const QString& jid : needToRequest) {
|
||||||
|
dm->requestInfo(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
|
||||||
|
{
|
||||||
|
if (info.from() == getServer()) {
|
||||||
|
bool enableCC = false;
|
||||||
|
qDebug() << "Server info received for account" << name;
|
||||||
|
QStringList features = info.features();
|
||||||
|
qDebug() << "List of supported features of the server " << getServer() << ":";
|
||||||
|
for (const QString& feature : features) {
|
||||||
|
qDebug() << " " << feature.toStdString().c_str();
|
||||||
|
if (feature == "urn:xmpp:carbons:2") {
|
||||||
|
enableCC = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableCC) {
|
||||||
|
qDebug() << "Enabling carbon copies for account" << name;
|
||||||
|
cm->setCarbonsEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Requesting account" << name << "capabilities";
|
||||||
|
dm->requestInfo(getBareJid());
|
||||||
|
} else if (info.from() == getBareJid()) {
|
||||||
|
qDebug() << "Received capabilities for account" << name << ":";
|
||||||
|
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
||||||
|
bool pepSupported = false;
|
||||||
|
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
||||||
|
QString type = identity.type();
|
||||||
|
qDebug() << " " << identity.category() << type;
|
||||||
|
if (type == "pep") {
|
||||||
|
pepSupported = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rh->setPepSupport(pepSupported);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received info for account" << name << "about" << info.from();
|
||||||
|
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
|
||||||
|
for (const QXmppDiscoveryIq::Identity& identity : identities) {
|
||||||
|
qDebug() << " " << identity.name() << identity.category() << identity.type();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::handleDisconnection()
|
||||||
|
{
|
||||||
|
cm->setCarbonsEnabled(false);
|
||||||
|
rh->handleOffline();
|
||||||
|
vh->handleOffline();
|
||||||
|
archiveQueries.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
|
||||||
|
{
|
||||||
|
RosterItem* contact = static_cast<RosterItem*>(sender());
|
||||||
|
|
||||||
|
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
||||||
|
if (last) {
|
||||||
|
qDebug() << "The response contains the first accounted message";
|
||||||
|
}
|
||||||
|
emit responseArchive(contact->jid, list, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Account::getActive() const {
|
||||||
|
return active;}
|
||||||
|
|
||||||
|
void Core::Account::setActive(bool p_active) {
|
||||||
|
if (active != p_active) {
|
||||||
|
active = p_active;
|
||||||
|
|
||||||
|
emit changed({
|
||||||
|
{"active", active}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::Account::getResource() const {
|
||||||
|
return config.resource();}
|
||||||
|
|
||||||
|
void Core::Account::setResource(const QString& p_resource) {
|
||||||
|
config.setResource(p_resource);}
|
||||||
|
|
||||||
|
QString Core::Account::getBareJid() const {
|
||||||
|
return getLogin() + "@" + getServer();}
|
||||||
|
|
||||||
|
QString Core::Account::getFullJid() const {
|
||||||
|
return getBareJid() + "/" + getResource();}
|
||||||
|
|
||||||
|
QString Core::Account::getName() const {
|
||||||
|
return name;}
|
||||||
|
|
||||||
|
QString Core::Account::getLogin() const {
|
||||||
|
return config.user();}
|
||||||
|
|
||||||
|
QString Core::Account::getPassword() const {
|
||||||
|
return config.password();}
|
||||||
|
|
||||||
|
QString Core::Account::getServer() const {
|
||||||
|
return config.domain();}
|
||||||
|
|
||||||
|
Shared::AccountPassword Core::Account::getPasswordType() const {
|
||||||
|
return passwordType;}
|
||||||
|
|
||||||
|
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
|
||||||
|
passwordType = pt; }
|
||||||
|
|
||||||
|
void Core::Account::setLogin(const QString& p_login) {
|
||||||
|
config.setUser(p_login);}
|
||||||
|
|
||||||
|
void Core::Account::setName(const QString& p_name) {
|
||||||
|
name = p_name;}
|
||||||
|
|
||||||
|
void Core::Account::setPassword(const QString& p_password) {
|
||||||
|
config.setPassword(p_password);
|
||||||
|
notReadyPassword = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Account::setServer(const QString& p_server) {
|
||||||
|
config.setDomain(p_server);}
|
||||||
|
|
||||||
|
void Core::Account::sendMessage(const Shared::Message& data) {
|
||||||
|
mh->sendMessage(data);}
|
||||||
|
|
||||||
|
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
|
||||||
|
mh->requestChangeMessage(jid, messageId, data);}
|
||||||
|
|
||||||
|
void Core::Account::resendMessage(const QString& jid, const QString& id) {
|
||||||
|
mh->resendMessage(jid, id);}
|
||||||
|
|
||||||
|
void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) {
|
||||||
|
mh->sendMessage(data, false, originalId);}
|
||||||
|
|
||||||
|
void Core::Account::requestVCard(const QString& jid) {
|
||||||
|
vh->requestVCard(jid);}
|
||||||
|
|
||||||
|
void Core::Account::uploadVCard(const Shared::VCard& card) {
|
||||||
|
vh->uploadVCard(card);}
|
||||||
|
|
||||||
|
QString Core::Account::getAvatarPath() const {
|
||||||
|
return vh->getAvatarPath();}
|
||||||
|
|
||||||
void Core::Account::removeRoomRequest(const QString& jid){
|
void Core::Account::removeRoomRequest(const QString& jid){
|
||||||
rh->removeRoomRequest(jid);}
|
rh->removeRoomRequest(jid);}
|
||||||
|
|
||||||
@ -689,253 +751,9 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
void Core::Account::invalidatePassword() {
|
||||||
{
|
notReadyPassword = true;}
|
||||||
QString id = card.from();
|
|
||||||
QStringList comps = id.split("/");
|
|
||||||
QString jid = comps.front().toLower();
|
|
||||||
QString resource("");
|
|
||||||
if (comps.size() > 1) {
|
|
||||||
resource = comps.back();
|
|
||||||
}
|
|
||||||
pendingVCardRequests.erase(id);
|
|
||||||
RosterItem* item = rh->getRosterItem(jid);
|
|
||||||
|
|
||||||
if (item == 0) {
|
|
||||||
if (jid == getLogin() + "@" + getServer()) {
|
|
||||||
onOwnVCardReceived(card);
|
|
||||||
} else {
|
|
||||||
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::VCard vCard = item->handleResponseVCard(card, resource);
|
|
||||||
|
|
||||||
emit receivedVCard(jid, vCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
|
Core::Account::Error Core::Account::getLastError() const {
|
||||||
{
|
return lastError;}
|
||||||
QByteArray ava = card.photo();
|
|
||||||
bool avaChanged = false;
|
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/";
|
|
||||||
if (ava.size() > 0) {
|
|
||||||
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
|
||||||
sha1.addData(ava);
|
|
||||||
QString newHash(sha1.result());
|
|
||||||
QMimeDatabase db;
|
|
||||||
QMimeType newType = db.mimeTypeForData(ava);
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
if (avatarHash != newHash) {
|
|
||||||
QString oldPath = path + "avatar." + avatarType;
|
|
||||||
QFile oldAvatar(oldPath);
|
|
||||||
bool oldToRemove = false;
|
|
||||||
if (oldAvatar.exists()) {
|
|
||||||
if (oldAvatar.rename(oldPath + ".bak")) {
|
|
||||||
oldToRemove = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
|
||||||
newAvatar.write(ava);
|
|
||||||
newAvatar.close();
|
|
||||||
avatarHash = newHash;
|
|
||||||
avatarType = newType.preferredSuffix();
|
|
||||||
avaChanged = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
|
||||||
if (oldToRemove) {
|
|
||||||
qDebug() << "rolling back to the old avatar";
|
|
||||||
if (!oldAvatar.rename(oldPath)) {
|
|
||||||
qDebug() << "Couldn't roll back to the old avatar in account" << name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
|
||||||
newAvatar.write(ava);
|
|
||||||
newAvatar.close();
|
|
||||||
avatarHash = newHash;
|
|
||||||
avatarType = newType.preferredSuffix();
|
|
||||||
avaChanged = true;
|
|
||||||
} else {
|
|
||||||
qDebug() << "Received new avatar for account" << name << "but can't save it";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oldAvatar(path + "avatar." + avatarType);
|
|
||||||
if (!oldAvatar.remove()) {
|
|
||||||
qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing";
|
|
||||||
} else {
|
|
||||||
avatarType = "";
|
|
||||||
avatarHash = "";
|
|
||||||
avaChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avaChanged) {
|
|
||||||
QMap<QString, QVariant> change;
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
presence.setPhotoHash(avatarHash.toUtf8());
|
|
||||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
|
||||||
change.insert("avatarPath", path + "avatar." + avatarType);
|
|
||||||
} else {
|
|
||||||
presence.setPhotoHash("");
|
|
||||||
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
|
||||||
change.insert("avatarPath", "");
|
|
||||||
}
|
|
||||||
client.setClientPresence(presence);
|
|
||||||
emit changed(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
ownVCardRequestInProgress = false;
|
|
||||||
|
|
||||||
Shared::VCard vCard;
|
|
||||||
initializeVCard(vCard, card);
|
|
||||||
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::valid);
|
|
||||||
vCard.setAvatarPath(path + "avatar." + avatarType);
|
|
||||||
} else {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit receivedVCard(getLogin() + "@" + getServer(), vCard);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Account::getAvatarPath() const
|
|
||||||
{
|
|
||||||
if (avatarType.size() == 0) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::requestVCard(const QString& jid)
|
|
||||||
{
|
|
||||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
|
||||||
qDebug() << "requesting vCard" << jid;
|
|
||||||
if (jid == getLogin() + "@" + getServer()) {
|
|
||||||
if (!ownVCardRequestInProgress) {
|
|
||||||
vm->requestClientVCard();
|
|
||||||
ownVCardRequestInProgress = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vm->requestVCard(jid);
|
|
||||||
pendingVCardRequests.insert(jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::uploadVCard(const Shared::VCard& card)
|
|
||||||
{
|
|
||||||
QXmppVCardIq iq;
|
|
||||||
initializeQXmppVCard(iq, card);
|
|
||||||
|
|
||||||
if (card.getAvatarType() != Shared::Avatar::empty) {
|
|
||||||
QString newPath = card.getAvatarPath();
|
|
||||||
QString oldPath = getAvatarPath();
|
|
||||||
QByteArray data;
|
|
||||||
QString type;
|
|
||||||
if (newPath != oldPath) {
|
|
||||||
QFile avatar(newPath);
|
|
||||||
if (!avatar.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "An attempt to upload new vCard to account" << name
|
|
||||||
<< "but it wasn't possible to read file" << newPath
|
|
||||||
<< "which was supposed to be new avatar, uploading old avatar";
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oA(oldPath);
|
|
||||||
if (!oA.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
|
||||||
} else {
|
|
||||||
data = oA.readAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data = avatar.readAll();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (avatarType.size() > 0) {
|
|
||||||
QFile oA(oldPath);
|
|
||||||
if (!oA.open(QFile::ReadOnly)) {
|
|
||||||
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
|
|
||||||
} else {
|
|
||||||
data = oA.readAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.size() > 0) {
|
|
||||||
QMimeDatabase db;
|
|
||||||
type = db.mimeTypeForData(data).name();
|
|
||||||
iq.setPhoto(data);
|
|
||||||
iq.setPhotoType(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm->setClientVCard(iq);
|
|
||||||
onOwnVCardReceived(iq);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
|
|
||||||
{
|
|
||||||
for (QXmppDiscoveryIq::Item item : items.items()) {
|
|
||||||
if (item.jid() != getServer()) {
|
|
||||||
dm->requestInfo(item.jid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
|
|
||||||
{
|
|
||||||
qDebug() << "Discovery info received for account" << name;
|
|
||||||
if (info.from() == getServer()) {
|
|
||||||
if (info.features().contains("urn:xmpp:carbons:2")) {
|
|
||||||
qDebug() << "Enabling carbon copies for account" << name;
|
|
||||||
cm->setCarbonsEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::handleDisconnection()
|
|
||||||
{
|
|
||||||
cm->setCarbonsEnabled(false);
|
|
||||||
rh->handleOffline();
|
|
||||||
archiveQueries.clear();
|
|
||||||
pendingVCardRequests.clear();
|
|
||||||
Shared::VCard vCard; //just to show, that there is now more pending request
|
|
||||||
for (const QString& jid : pendingVCardRequests) {
|
|
||||||
emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
|
||||||
}
|
|
||||||
pendingVCardRequests.clear();
|
|
||||||
ownVCardRequestInProgress = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
|
|
||||||
{
|
|
||||||
RosterItem* contact = static_cast<RosterItem*>(sender());
|
|
||||||
|
|
||||||
qDebug() << "Collected history for contact " << contact->jid << list.size() << "elements";
|
|
||||||
if (last) {
|
|
||||||
qDebug() << "The response contains the first accounted message";
|
|
||||||
}
|
|
||||||
emit responseArchive(contact->jid, list, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
|
|
||||||
mh->requestChangeMessage(jid, messageId, data);}
|
|
||||||
|
|
||||||
void Core::Account::resendMessage(const QString& jid, const QString& id) {
|
|
||||||
mh->resendMessage(jid, id);}
|
|
||||||
|
|
||||||
void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) {
|
|
||||||
mh->sendMessage(data, false, originalId);}
|
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#include <QXmppBookmarkManager.h>
|
#include <QXmppBookmarkManager.h>
|
||||||
#include <QXmppBookmarkSet.h>
|
#include <QXmppBookmarkSet.h>
|
||||||
#include <QXmppUploadRequestManager.h>
|
#include <QXmppUploadRequestManager.h>
|
||||||
#include <QXmppVCardIq.h>
|
|
||||||
#include <QXmppVCardManager.h>
|
#include <QXmppVCardManager.h>
|
||||||
#include <QXmppMessageReceiptManager.h>
|
#include <QXmppMessageReceiptManager.h>
|
||||||
|
|
||||||
@ -50,6 +49,7 @@
|
|||||||
|
|
||||||
#include "handlers/messagehandler.h"
|
#include "handlers/messagehandler.h"
|
||||||
#include "handlers/rosterhandler.h"
|
#include "handlers/rosterhandler.h"
|
||||||
|
#include "handlers/vcardhandler.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
@ -59,12 +59,20 @@ class Account : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
friend class MessageHandler;
|
friend class MessageHandler;
|
||||||
friend class RosterHandler;
|
friend class RosterHandler;
|
||||||
|
friend class VCardHandler;
|
||||||
public:
|
public:
|
||||||
|
enum class Error {
|
||||||
|
authentication,
|
||||||
|
other,
|
||||||
|
none
|
||||||
|
};
|
||||||
|
|
||||||
Account(
|
Account(
|
||||||
const QString& p_login,
|
const QString& p_login,
|
||||||
const QString& p_server,
|
const QString& p_server,
|
||||||
const QString& p_password,
|
const QString& p_password,
|
||||||
const QString& p_name,
|
const QString& p_name,
|
||||||
|
bool p_active,
|
||||||
NetworkAccess* p_net,
|
NetworkAccess* p_net,
|
||||||
QObject* parent = 0);
|
QObject* parent = 0);
|
||||||
~Account();
|
~Account();
|
||||||
@ -76,8 +84,12 @@ public:
|
|||||||
QString getPassword() const;
|
QString getPassword() const;
|
||||||
QString getResource() const;
|
QString getResource() const;
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath() const;
|
||||||
|
QString getBareJid() const;
|
||||||
|
QString getFullJid() const;
|
||||||
Shared::Availability getAvailability() const;
|
Shared::Availability getAvailability() const;
|
||||||
Shared::AccountPassword getPasswordType() const;
|
Shared::AccountPassword getPasswordType() const;
|
||||||
|
Error getLastError() const;
|
||||||
|
bool getActive() const;
|
||||||
|
|
||||||
void setName(const QString& p_name);
|
void setName(const QString& p_name);
|
||||||
void setLogin(const QString& p_login);
|
void setLogin(const QString& p_login);
|
||||||
@ -86,8 +98,8 @@ public:
|
|||||||
void setResource(const QString& p_resource);
|
void setResource(const QString& p_resource);
|
||||||
void setAvailability(Shared::Availability avail);
|
void setAvailability(Shared::Availability avail);
|
||||||
void setPasswordType(Shared::AccountPassword pt);
|
void setPasswordType(Shared::AccountPassword pt);
|
||||||
QString getFullJid() const;
|
|
||||||
void sendMessage(const Shared::Message& data);
|
void sendMessage(const Shared::Message& data);
|
||||||
|
void setActive(bool p_active);
|
||||||
void requestArchive(const QString& jid, int count, const QString& before);
|
void requestArchive(const QString& jid, int count, const QString& before);
|
||||||
void subscribeToContact(const QString& jid, const QString& reason);
|
void subscribeToContact(const QString& jid, const QString& reason);
|
||||||
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
||||||
@ -105,6 +117,7 @@ public:
|
|||||||
void uploadVCard(const Shared::VCard& card);
|
void uploadVCard(const Shared::VCard& card);
|
||||||
void resendMessage(const QString& jid, const QString& id);
|
void resendMessage(const QString& jid, const QString& id);
|
||||||
void replaceMessage(const QString& originalId, const Shared::Message& data);
|
void replaceMessage(const QString& originalId, const Shared::Message& data);
|
||||||
|
void invalidatePassword();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void connect();
|
void connect();
|
||||||
@ -137,6 +150,7 @@ signals:
|
|||||||
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 uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
|
||||||
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
|
||||||
|
void needPassword();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString name;
|
QString name;
|
||||||
@ -157,16 +171,16 @@ private:
|
|||||||
bool reconnectScheduled;
|
bool reconnectScheduled;
|
||||||
QTimer* reconnectTimer;
|
QTimer* reconnectTimer;
|
||||||
|
|
||||||
std::set<QString> pendingVCardRequests;
|
|
||||||
|
|
||||||
QString avatarHash;
|
|
||||||
QString avatarType;
|
|
||||||
bool ownVCardRequestInProgress;
|
|
||||||
NetworkAccess* network;
|
NetworkAccess* network;
|
||||||
Shared::AccountPassword passwordType;
|
Shared::AccountPassword passwordType;
|
||||||
|
Error lastError;
|
||||||
|
bool pepSupport;
|
||||||
|
bool active;
|
||||||
|
bool notReadyPassword;
|
||||||
|
|
||||||
MessageHandler* mh;
|
MessageHandler* mh;
|
||||||
RosterHandler* rh;
|
RosterHandler* rh;
|
||||||
|
VCardHandler* vh;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onClientStateChange(QXmppClient::State state);
|
void onClientStateChange(QXmppClient::State state);
|
||||||
@ -179,9 +193,6 @@ private slots:
|
|||||||
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
|
||||||
|
|
||||||
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
||||||
|
|
||||||
void onVCardReceived(const QXmppVCardIq& card);
|
|
||||||
void onOwnVCardReceived(const QXmppVCardIq& card);
|
|
||||||
|
|
||||||
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
|
||||||
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
|
||||||
@ -191,9 +202,6 @@ private:
|
|||||||
void handleDisconnection();
|
void handleDisconnection();
|
||||||
void onReconnectTimer();
|
void onReconnectTimer();
|
||||||
};
|
};
|
||||||
|
|
||||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
|
||||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Squawk messenger.
|
* Squawk messenger.
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
@ -15,10 +15,8 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
#define CORE_ADAPTER_FUNCTIONS_H
|
|
||||||
|
|
||||||
#include "account.h"
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
||||||
{
|
{
|
||||||
@ -271,5 +269,3 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
|
|||||||
iq.setEmails(emails);
|
iq.setEmails(emails);
|
||||||
iq.setPhones(phs);
|
iq.setPhones(phs);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // CORE_ADAPTER_FUNCTIONS_H
|
|
32
core/adapterfunctions.h
Normal file
32
core/adapterfunctions.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifndef CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
#define CORE_ADAPTER_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||||
|
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CORE_ADAPTER_FUNCTIONS_H
|
@ -3,4 +3,6 @@ target_sources(squawk PRIVATE
|
|||||||
messagehandler.h
|
messagehandler.h
|
||||||
rosterhandler.cpp
|
rosterhandler.cpp
|
||||||
rosterhandler.h
|
rosterhandler.h
|
||||||
|
vcardhandler.cpp
|
||||||
|
vcardhandler.h
|
||||||
)
|
)
|
||||||
|
@ -176,7 +176,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
|
|||||||
target.setForwarded(forwarded);
|
target.setForwarded(forwarded);
|
||||||
|
|
||||||
if (guessing) {
|
if (guessing) {
|
||||||
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
|
if (target.getFromJid() == acc->getBareJid()) {
|
||||||
outgoing = true;
|
outgoing = true;
|
||||||
} else {
|
} else {
|
||||||
outgoing = false;
|
outgoing = false;
|
||||||
|
@ -26,7 +26,8 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
|
|||||||
conferences(),
|
conferences(),
|
||||||
groups(),
|
groups(),
|
||||||
queuedContacts(),
|
queuedContacts(),
|
||||||
outOfRosterContacts()
|
outOfRosterContacts(),
|
||||||
|
pepSupport(false)
|
||||||
{
|
{
|
||||||
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
|
||||||
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
|
||||||
@ -51,8 +52,7 @@ Core::RosterHandler::~RosterHandler()
|
|||||||
|
|
||||||
void Core::RosterHandler::onRosterReceived()
|
void Core::RosterHandler::onRosterReceived()
|
||||||
{
|
{
|
||||||
acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
|
acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards
|
||||||
acc->ownVCardRequestInProgress = true;
|
|
||||||
|
|
||||||
QStringList bj = acc->rm->getRosterBareJids();
|
QStringList bj = acc->rm->getRosterBareJids();
|
||||||
for (int i = 0; i < bj.size(); ++i) {
|
for (int i = 0; i < bj.size(); ++i) {
|
||||||
@ -588,4 +588,13 @@ void Core::RosterHandler::handleOffline()
|
|||||||
pair.second->clearArchiveRequests();
|
pair.second->clearArchiveRequests();
|
||||||
pair.second->downgradeDatabaseState();
|
pair.second->downgradeDatabaseState();
|
||||||
}
|
}
|
||||||
|
setPepSupport(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Core::RosterHandler::setPepSupport(bool support)
|
||||||
|
{
|
||||||
|
if (pepSupport != support) {
|
||||||
|
pepSupport = support;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@ public:
|
|||||||
|
|
||||||
void storeConferences();
|
void storeConferences();
|
||||||
void clearConferences();
|
void clearConferences();
|
||||||
|
void setPepSupport(bool support);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onRosterReceived();
|
void onRosterReceived();
|
||||||
@ -107,6 +108,7 @@ private:
|
|||||||
std::map<QString, std::set<QString>> groups;
|
std::map<QString, std::set<QString>> groups;
|
||||||
std::map<QString, QString> queuedContacts;
|
std::map<QString, QString> queuedContacts;
|
||||||
std::set<QString> outOfRosterContacts;
|
std::set<QString> outOfRosterContacts;
|
||||||
|
bool pepSupport;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
312
core/handlers/vcardhandler.cpp
Normal file
312
core/handlers/vcardhandler.cpp
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "vcardhandler.h"
|
||||||
|
#include "core/account.h"
|
||||||
|
|
||||||
|
Core::VCardHandler::VCardHandler(Account* account):
|
||||||
|
QObject(),
|
||||||
|
acc(account),
|
||||||
|
ownVCardRequestInProgress(false),
|
||||||
|
pendingVCardRequests(),
|
||||||
|
avatarHash(),
|
||||||
|
avatarType()
|
||||||
|
{
|
||||||
|
connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived);
|
||||||
|
//for some reason it doesn't work, launching from common handler
|
||||||
|
//connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived);
|
||||||
|
|
||||||
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
|
path += "/" + acc->name;
|
||||||
|
QDir dir(path);
|
||||||
|
|
||||||
|
if (!dir.exists()) {
|
||||||
|
bool res = dir.mkpath(path);
|
||||||
|
if (!res) {
|
||||||
|
qDebug() << "Couldn't create a cache directory for account" << acc->name;
|
||||||
|
throw 22;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile* avatar = new QFile(path + "/avatar.png");
|
||||||
|
QString type = "png";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpg");
|
||||||
|
type = "jpg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.jpeg");
|
||||||
|
type = "jpeg";
|
||||||
|
if (!avatar->exists()) {
|
||||||
|
delete avatar;
|
||||||
|
avatar = new QFile(path + "/avatar.gif");
|
||||||
|
type = "gif";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatar->exists()) {
|
||||||
|
if (avatar->open(QFile::ReadOnly)) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(avatar);
|
||||||
|
avatarHash = sha1.result();
|
||||||
|
avatarType = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (avatarType.size() != 0) {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
} else {
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::VCardHandler::~VCardHandler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card)
|
||||||
|
{
|
||||||
|
QString id = card.from();
|
||||||
|
QStringList comps = id.split("/");
|
||||||
|
QString jid = comps.front().toLower();
|
||||||
|
QString resource("");
|
||||||
|
if (comps.size() > 1) {
|
||||||
|
resource = comps.back();
|
||||||
|
}
|
||||||
|
pendingVCardRequests.erase(id);
|
||||||
|
RosterItem* item = acc->rh->getRosterItem(jid);
|
||||||
|
|
||||||
|
if (item == 0) {
|
||||||
|
if (jid == acc->getBareJid()) {
|
||||||
|
onOwnVCardReceived(card);
|
||||||
|
} else {
|
||||||
|
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard vCard = item->handleResponseVCard(card, resource);
|
||||||
|
|
||||||
|
emit acc->receivedVCard(jid, vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card)
|
||||||
|
{
|
||||||
|
QByteArray ava = card.photo();
|
||||||
|
bool avaChanged = false;
|
||||||
|
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/";
|
||||||
|
if (ava.size() > 0) {
|
||||||
|
QCryptographicHash sha1(QCryptographicHash::Sha1);
|
||||||
|
sha1.addData(ava);
|
||||||
|
QString newHash(sha1.result());
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType newType = db.mimeTypeForData(ava);
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
if (avatarHash != newHash) {
|
||||||
|
QString oldPath = path + "avatar." + avatarType;
|
||||||
|
QFile oldAvatar(oldPath);
|
||||||
|
bool oldToRemove = false;
|
||||||
|
if (oldAvatar.exists()) {
|
||||||
|
if (oldAvatar.rename(oldPath + ".bak")) {
|
||||||
|
oldToRemove = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
||||||
|
if (oldToRemove) {
|
||||||
|
qDebug() << "rolling back to the old avatar";
|
||||||
|
if (!oldAvatar.rename(oldPath)) {
|
||||||
|
qDebug() << "Couldn't roll back to the old avatar in account" << acc->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
|
||||||
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
|
newAvatar.write(ava);
|
||||||
|
newAvatar.close();
|
||||||
|
avatarHash = newHash;
|
||||||
|
avatarType = newType.preferredSuffix();
|
||||||
|
avaChanged = true;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Received new avatar for account" << acc->name << "but can't save it";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oldAvatar(path + "avatar." + avatarType);
|
||||||
|
if (!oldAvatar.remove()) {
|
||||||
|
qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing";
|
||||||
|
} else {
|
||||||
|
avatarType = "";
|
||||||
|
avatarHash = "";
|
||||||
|
avaChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avaChanged) {
|
||||||
|
QMap<QString, QVariant> change;
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
||||||
|
change.insert("avatarPath", path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
acc->presence.setPhotoHash("");
|
||||||
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
|
||||||
|
change.insert("avatarPath", "");
|
||||||
|
}
|
||||||
|
acc->client.setClientPresence(acc->presence);
|
||||||
|
emit acc->changed(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
|
||||||
|
Shared::VCard vCard;
|
||||||
|
initializeVCard(vCard, card);
|
||||||
|
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::valid);
|
||||||
|
vCard.setAvatarPath(path + "avatar." + avatarType);
|
||||||
|
} else {
|
||||||
|
vCard.setAvatarType(Shared::Avatar::empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit acc->receivedVCard(acc->getBareJid(), vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::handleOffline()
|
||||||
|
{
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
Shared::VCard vCard; //just to show, that there is now more pending request
|
||||||
|
for (const QString& jid : pendingVCardRequests) {
|
||||||
|
emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
||||||
|
}
|
||||||
|
pendingVCardRequests.clear();
|
||||||
|
ownVCardRequestInProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::requestVCard(const QString& jid)
|
||||||
|
{
|
||||||
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||||
|
qDebug() << "requesting vCard" << jid;
|
||||||
|
if (jid == acc->getBareJid()) {
|
||||||
|
if (!ownVCardRequestInProgress) {
|
||||||
|
acc->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc->vm->requestVCard(jid);
|
||||||
|
pendingVCardRequests.insert(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence)
|
||||||
|
{
|
||||||
|
if (!ownVCardRequestInProgress) {
|
||||||
|
switch (p_presence.vCardUpdateType()) {
|
||||||
|
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
acc->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||||
|
if (avatarHash != p_presence.photoHash()) {
|
||||||
|
acc->vm->requestClientVCard();
|
||||||
|
ownVCardRequestInProgress = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::VCardHandler::uploadVCard(const Shared::VCard& card)
|
||||||
|
{
|
||||||
|
QXmppVCardIq iq;
|
||||||
|
initializeQXmppVCard(iq, card);
|
||||||
|
|
||||||
|
if (card.getAvatarType() != Shared::Avatar::empty) {
|
||||||
|
QString newPath = card.getAvatarPath();
|
||||||
|
QString oldPath = getAvatarPath();
|
||||||
|
QByteArray data;
|
||||||
|
QString type;
|
||||||
|
if (newPath != oldPath) {
|
||||||
|
QFile avatar(newPath);
|
||||||
|
if (!avatar.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "An attempt to upload new vCard to account" << acc->name
|
||||||
|
<< "but it wasn't possible to read file" << newPath
|
||||||
|
<< "which was supposed to be new avatar, uploading old avatar";
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
||||||
|
} else {
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = avatar.readAll();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (avatarType.size() > 0) {
|
||||||
|
QFile oA(oldPath);
|
||||||
|
if (!oA.open(QFile::ReadOnly)) {
|
||||||
|
qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar";
|
||||||
|
} else {
|
||||||
|
data = oA.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() > 0) {
|
||||||
|
QMimeDatabase db;
|
||||||
|
type = db.mimeTypeForData(data).name();
|
||||||
|
iq.setPhoto(data);
|
||||||
|
iq.setPhotoType(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
acc->vm->setClientVCard(iq);
|
||||||
|
onOwnVCardReceived(iq);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Core::VCardHandler::getAvatarPath() const
|
||||||
|
{
|
||||||
|
if (avatarType.size() == 0) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType;
|
||||||
|
}
|
||||||
|
}
|
65
core/handlers/vcardhandler.h
Normal file
65
core/handlers/vcardhandler.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CORE_VCARDHANDLER_H
|
||||||
|
#define CORE_VCARDHANDLER_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <QXmppPresence.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <shared/vcard.h>
|
||||||
|
#include <core/adapterfunctions.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
class Account;
|
||||||
|
|
||||||
|
class VCardHandler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
VCardHandler(Account* account);
|
||||||
|
~VCardHandler();
|
||||||
|
|
||||||
|
void handleOffline();
|
||||||
|
void requestVCard(const QString& jid);
|
||||||
|
void handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence);
|
||||||
|
void uploadVCard(const Shared::VCard& card);
|
||||||
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onVCardReceived(const QXmppVCardIq& card);
|
||||||
|
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Account* acc;
|
||||||
|
|
||||||
|
bool ownVCardRequestInProgress;
|
||||||
|
std::set<QString> pendingVCardRequests;
|
||||||
|
QString avatarHash;
|
||||||
|
QString avatarType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CORE_VCARDHANDLER_H
|
209
core/main.cpp
209
core/main.cpp
@ -1,209 +0,0 @@
|
|||||||
/*
|
|
||||||
* Squawk messenger.
|
|
||||||
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "../shared/global.h"
|
|
||||||
#include "../shared/messageinfo.h"
|
|
||||||
#include "../shared/pathcheck.h"
|
|
||||||
#include "../ui/squawk.h"
|
|
||||||
#include "signalcatcher.h"
|
|
||||||
#include "squawk.h"
|
|
||||||
|
|
||||||
#include <QLibraryInfo>
|
|
||||||
#include <QSettings>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QThread>
|
|
||||||
#include <QtWidgets/QApplication>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
qRegisterMetaType<Shared::Message>("Shared::Message");
|
|
||||||
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
|
||||||
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
|
||||||
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
|
||||||
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
|
||||||
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
|
||||||
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
|
||||||
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
|
||||||
SignalCatcher sc(&app);
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
// Windows need an organization name for QSettings to work
|
|
||||||
// https://doc.qt.io/qt-5/qsettings.html#basic-usage
|
|
||||||
{
|
|
||||||
const QString& orgName = QApplication::organizationName();
|
|
||||||
if (orgName.isNull() || orgName.isEmpty()) {
|
|
||||||
QApplication::setOrganizationName("squawk");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
QApplication::setApplicationName("squawk");
|
|
||||||
QApplication::setApplicationDisplayName("Squawk");
|
|
||||||
QApplication::setApplicationVersion("0.2.1");
|
|
||||||
|
|
||||||
QTranslator qtTranslator;
|
|
||||||
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
|
||||||
app.installTranslator(&qtTranslator);
|
|
||||||
|
|
||||||
QTranslator myappTranslator;
|
|
||||||
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
|
||||||
bool found = false;
|
|
||||||
for (QString share : shares) {
|
|
||||||
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
app.installTranslator(&myappTranslator);
|
|
||||||
|
|
||||||
QIcon icon;
|
|
||||||
icon.addFile(":images/logo.svg", QSize(16, 16));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(24, 24));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(32, 32));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(48, 48));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(64, 64));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(96, 96));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(128, 128));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(256, 256));
|
|
||||||
icon.addFile(":images/logo.svg", QSize(512, 512));
|
|
||||||
QApplication::setWindowIcon(icon);
|
|
||||||
|
|
||||||
new Shared::Global(); //translates enums
|
|
||||||
|
|
||||||
QSettings settings;
|
|
||||||
QVariant vs = settings.value("style");
|
|
||||||
if (vs.isValid()) {
|
|
||||||
QString style = vs.toString().toLower();
|
|
||||||
if (style != "system") {
|
|
||||||
Shared::Global::setStyle(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Shared::Global::supported("colorSchemeTools")) {
|
|
||||||
QVariant vt = settings.value("theme");
|
|
||||||
if (vt.isValid()) {
|
|
||||||
QString theme = vt.toString();
|
|
||||||
if (theme.toLower() != "system") {
|
|
||||||
Shared::Global::setTheme(theme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString path = Shared::downloadsPathCheck();
|
|
||||||
if (path.size() > 0) {
|
|
||||||
settings.setValue("downloadsPath", path);
|
|
||||||
} else {
|
|
||||||
qDebug() << "couldn't initialize directory for downloads, quitting";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Squawk w;
|
|
||||||
w.show();
|
|
||||||
|
|
||||||
Core::Squawk* squawk = new Core::Squawk();
|
|
||||||
QThread* coreThread = new QThread();
|
|
||||||
squawk->moveToThread(coreThread);
|
|
||||||
|
|
||||||
QObject::connect(&sc, &SignalCatcher::interrupt, &app, &QApplication::closeAllWindows);
|
|
||||||
|
|
||||||
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
|
||||||
QObject::connect(&app, &QApplication::lastWindowClosed, squawk, &Core::Squawk::stop);
|
|
||||||
QObject::connect(&app, &QApplication::lastWindowClosed, &w, &Squawk::writeSettings);
|
|
||||||
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit, Qt::QueuedConnection);
|
|
||||||
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
|
|
||||||
|
|
||||||
QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest);
|
|
||||||
QObject::connect(&w, &Squawk::connectAccount, squawk, &Core::Squawk::connectAccount);
|
|
||||||
QObject::connect(&w, &Squawk::disconnectAccount, squawk, &Core::Squawk::disconnectAccount);
|
|
||||||
QObject::connect(&w, &Squawk::changeState, squawk, &Core::Squawk::changeState);
|
|
||||||
QObject::connect(&w, &Squawk::sendMessage, squawk,&Core::Squawk::sendMessage);
|
|
||||||
QObject::connect(&w, &Squawk::replaceMessage, squawk,&Core::Squawk::replaceMessage);
|
|
||||||
QObject::connect(&w, &Squawk::resendMessage, squawk,&Core::Squawk::resendMessage);
|
|
||||||
QObject::connect(&w, &Squawk::requestArchive, squawk, &Core::Squawk::requestArchive);
|
|
||||||
QObject::connect(&w, &Squawk::subscribeContact, squawk, &Core::Squawk::subscribeContact);
|
|
||||||
QObject::connect(&w, &Squawk::unsubscribeContact, squawk, &Core::Squawk::unsubscribeContact);
|
|
||||||
QObject::connect(&w, &Squawk::addContactRequest, squawk, &Core::Squawk::addContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeContactRequest, squawk, &Core::Squawk::removeContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::setRoomJoined, squawk, &Core::Squawk::setRoomJoined);
|
|
||||||
QObject::connect(&w, &Squawk::setRoomAutoJoin, squawk, &Core::Squawk::setRoomAutoJoin);
|
|
||||||
QObject::connect(&w, &Squawk::removeRoomRequest, squawk, &Core::Squawk::removeRoomRequest);
|
|
||||||
QObject::connect(&w, &Squawk::addRoomRequest, squawk, &Core::Squawk::addRoomRequest);
|
|
||||||
QObject::connect(&w, &Squawk::fileDownloadRequest, squawk, &Core::Squawk::fileDownloadRequest);
|
|
||||||
QObject::connect(&w, &Squawk::addContactToGroupRequest, squawk, &Core::Squawk::addContactToGroupRequest);
|
|
||||||
QObject::connect(&w, &Squawk::removeContactFromGroupRequest, squawk, &Core::Squawk::removeContactFromGroupRequest);
|
|
||||||
QObject::connect(&w, &Squawk::renameContactRequest, squawk, &Core::Squawk::renameContactRequest);
|
|
||||||
QObject::connect(&w, &Squawk::requestVCard, squawk, &Core::Squawk::requestVCard);
|
|
||||||
QObject::connect(&w, &Squawk::uploadVCard, squawk, &Core::Squawk::uploadVCard);
|
|
||||||
QObject::connect(&w, &Squawk::responsePassword, squawk, &Core::Squawk::responsePassword);
|
|
||||||
QObject::connect(&w, &Squawk::localPathInvalid, squawk, &Core::Squawk::onLocalPathInvalid);
|
|
||||||
QObject::connect(&w, &Squawk::changeDownloadsPath, squawk, &Core::Squawk::changeDownloadsPath);
|
|
||||||
|
|
||||||
QObject::connect(squawk, &Core::Squawk::newAccount, &w, &Squawk::newAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addContact, &w, &Squawk::addContact);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeAccount, &w, &Squawk::changeAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeAccount, &w, &Squawk::removeAccount);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addGroup, &w, &Squawk::addGroup);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeGroup, &w, &Squawk::removeGroup);
|
|
||||||
QObject::connect(squawk, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&w, qOverload<const QString&, const QString&>(&Squawk::removeContact));
|
|
||||||
QObject::connect(squawk, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
|
||||||
&w, qOverload<const QString&, const QString&, const QString&>(&Squawk::removeContact));
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeContact, &w, &Squawk::changeContact);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addPresence, &w, &Squawk::addPresence);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removePresence, &w, &Squawk::removePresence);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::stateChanged, &w, &Squawk::stateChanged);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::accountMessage, &w, &Squawk::accountMessage);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeMessage, &w, &Squawk::changeMessage);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseArchive, &w, &Squawk::responseArchive);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addRoom, &w, &Squawk::addRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoom, &w, &Squawk::changeRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoom, &w, &Squawk::removeRoom);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::addRoomParticipant, &w, &Squawk::addRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileDownloadComplete, &w, &Squawk::fileDownloadComplete);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileUploadComplete, &w, &Squawk::fileUploadComplete);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileProgress, &w, &Squawk::fileProgress);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::fileError, &w, &Squawk::fileError);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::requestPassword, &w, &Squawk::requestPassword);
|
|
||||||
QObject::connect(squawk, &Core::Squawk::ready, &w, &Squawk::readSettings);
|
|
||||||
|
|
||||||
coreThread->start();
|
|
||||||
int result = app.exec();
|
|
||||||
|
|
||||||
if (coreThread->isRunning()) {
|
|
||||||
//coreThread->wait();
|
|
||||||
//todo if I uncomment that, the app will no quit if it has reconnected at least once
|
|
||||||
//it feels like a symptom of something badly desinged in the core coreThread
|
|
||||||
//need to investigate;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "urlstorage.h"
|
#include "storage/urlstorage.h"
|
||||||
#include "shared/pathcheck.h"
|
#include "shared/pathcheck.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -124,15 +124,19 @@ void Core::RosterItem::nextRequest()
|
|||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
bool last = false;
|
bool last = false;
|
||||||
if (archiveState == beginning || archiveState == complete) {
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
QString firstId = archive->oldestId();
|
try {
|
||||||
if (responseCache.size() == 0) {
|
QString firstId = archive->oldestId();
|
||||||
if (requestedBefore == firstId) {
|
if (responseCache.size() == 0) {
|
||||||
last = true;
|
if (requestedBefore == firstId) {
|
||||||
}
|
last = true;
|
||||||
} else {
|
}
|
||||||
if (responseCache.front().getId() == firstId) {
|
} else {
|
||||||
last = true;
|
if (responseCache.front().getId() == firstId) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (const Archive::Empty& e) {
|
||||||
|
last = true;
|
||||||
}
|
}
|
||||||
} else if (archiveState == empty && responseCache.size() == 0) {
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
last = true;
|
last = true;
|
||||||
@ -171,8 +175,12 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
}
|
}
|
||||||
Shared::Message msg = archive->newest();
|
try {
|
||||||
emit needHistory("", getId(msg), msg.getTime());
|
Shared::Message msg = archive->newest();
|
||||||
|
emit needHistory("", getId(msg), msg.getTime());
|
||||||
|
} catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example)
|
||||||
|
emit needHistory(before, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case end:
|
case end:
|
||||||
|
@ -34,7 +34,8 @@
|
|||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/vcard.h"
|
#include "shared/vcard.h"
|
||||||
#include "archive.h"
|
#include "storage/archive.h"
|
||||||
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
|
279
core/squawk.cpp
279
core/squawk.cpp
@ -26,8 +26,8 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
QObject(parent),
|
QObject(parent),
|
||||||
accounts(),
|
accounts(),
|
||||||
amap(),
|
amap(),
|
||||||
|
state(Shared::Availability::offline),
|
||||||
network(),
|
network(),
|
||||||
waitingForAccounts(0),
|
|
||||||
isInitialized(false)
|
isInitialized(false)
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
,kwallet()
|
,kwallet()
|
||||||
@ -42,7 +42,7 @@ Core::Squawk::Squawk(QObject* parent):
|
|||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
connect(&kwallet, &PSE::KWallet::opened, this, &Squawk::onWalletOpened);
|
||||||
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
connect(&kwallet, &PSE::KWallet::rejectPassword, this, &Squawk::onWalletRejectPassword);
|
||||||
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::onWalletResponsePassword);
|
connect(&kwallet, &PSE::KWallet::responsePassword, this, &Squawk::responsePassword);
|
||||||
|
|
||||||
Shared::Global::setSupported("KWallet", true);
|
Shared::Global::setSupported("KWallet", true);
|
||||||
}
|
}
|
||||||
@ -97,6 +97,7 @@ void Core::Squawk::stop()
|
|||||||
settings.setValue("password", password);
|
settings.setValue("password", password);
|
||||||
settings.setValue("resource", acc->getResource());
|
settings.setValue("resource", acc->getResource());
|
||||||
settings.setValue("passwordType", static_cast<int>(ap));
|
settings.setValue("passwordType", static_cast<int>(ap));
|
||||||
|
settings.setValue("active", acc->getActive());
|
||||||
}
|
}
|
||||||
settings.endArray();
|
settings.endArray();
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
@ -124,8 +125,9 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
|||||||
QString password = map.value("password").toString();
|
QString password = map.value("password").toString();
|
||||||
QString resource = map.value("resource").toString();
|
QString resource = map.value("resource").toString();
|
||||||
int passwordType = map.value("passwordType").toInt();
|
int passwordType = map.value("passwordType").toInt();
|
||||||
|
bool active = map.value("active").toBool();
|
||||||
|
|
||||||
addAccount(login, server, password, name, resource, Shared::Global::fromInt<Shared::AccountPassword>(passwordType));
|
addAccount(login, server, password, name, resource, active, Shared::Global::fromInt<Shared::AccountPassword>(passwordType));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::addAccount(
|
void Core::Squawk::addAccount(
|
||||||
@ -133,13 +135,15 @@ void Core::Squawk::addAccount(
|
|||||||
const QString& server,
|
const QString& server,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
Shared::AccountPassword passwordType
|
bool active,
|
||||||
)
|
Shared::AccountPassword passwordType)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
if (amap.count(name) > 0) {
|
||||||
|
qDebug() << "An attempt to add account" << name << "but an account with such name already exist, ignoring";
|
||||||
Account* acc = new Account(login, server, password, name, &network);
|
return;
|
||||||
|
}
|
||||||
|
Account* acc = new Account(login, server, password, name, active, &network);
|
||||||
acc->setResource(resource);
|
acc->setResource(resource);
|
||||||
acc->setPasswordType(passwordType);
|
acc->setPasswordType(passwordType);
|
||||||
accounts.push_back(acc);
|
accounts.push_back(acc);
|
||||||
@ -148,6 +152,8 @@ void Core::Squawk::addAccount(
|
|||||||
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
||||||
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
||||||
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
||||||
|
connect(acc, &Account::needPassword, this, &Squawk::onAccountNeedPassword);
|
||||||
|
|
||||||
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
||||||
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
||||||
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
||||||
@ -185,20 +191,44 @@ void Core::Squawk::addAccount(
|
|||||||
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
{"offline", QVariant::fromValue(Shared::Availability::offline)},
|
||||||
{"error", ""},
|
{"error", ""},
|
||||||
{"avatarPath", acc->getAvatarPath()},
|
{"avatarPath", acc->getAvatarPath()},
|
||||||
{"passwordType", QVariant::fromValue(passwordType)}
|
{"passwordType", QVariant::fromValue(passwordType)},
|
||||||
|
{"active", active}
|
||||||
};
|
};
|
||||||
|
|
||||||
emit newAccount(map);
|
emit newAccount(map);
|
||||||
|
|
||||||
|
switch (passwordType) {
|
||||||
|
case Shared::AccountPassword::alwaysAsk:
|
||||||
|
case Shared::AccountPassword::kwallet:
|
||||||
|
if (password == "") {
|
||||||
|
acc->invalidatePassword();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != Shared::Availability::offline) {
|
||||||
|
acc->setAvailability(state);
|
||||||
|
if (acc->getActive()) {
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::changeState(Shared::Availability p_state)
|
void Core::Squawk::changeState(Shared::Availability p_state)
|
||||||
{
|
{
|
||||||
if (state != p_state) {
|
if (state != p_state) {
|
||||||
|
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
||||||
|
Account* acc = *itr;
|
||||||
|
acc->setAvailability(p_state);
|
||||||
|
if (state == Shared::Availability::offline && acc->getActive()) {
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
state = p_state;
|
state = p_state;
|
||||||
}
|
|
||||||
|
emit stateChanged(p_state);
|
||||||
for (std::deque<Account*>::iterator itr = accounts.begin(), end = accounts.end(); itr != end; ++itr) {
|
|
||||||
(*itr)->setAvailability(state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,7 +239,10 @@ void Core::Squawk::connectAccount(const QString& account)
|
|||||||
qDebug("An attempt to connect non existing account, skipping");
|
qDebug("An attempt to connect non existing account, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->connect();
|
itr->second->setActive(true);
|
||||||
|
if (state != Shared::Availability::offline) {
|
||||||
|
itr->second->connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::disconnectAccount(const QString& account)
|
void Core::Squawk::disconnectAccount(const QString& account)
|
||||||
@ -220,14 +253,18 @@ void Core::Squawk::disconnectAccount(const QString& account)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr->second->setActive(false);
|
||||||
itr->second->disconnect();
|
itr->second->disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
|
void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_state)
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"state", QVariant::fromValue(p_state)}});
|
emit changeAccount(acc->getName(), {
|
||||||
|
{"state", QVariant::fromValue(p_state)},
|
||||||
|
{"error", ""}
|
||||||
|
});
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
if (p_state == Shared::ConnectionState::connected) {
|
if (p_state == Shared::ConnectionState::connected) {
|
||||||
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
|
if (acc->getPasswordType() == Shared::AccountPassword::kwallet && kwallet.supportState() == PSE::KWallet::success) {
|
||||||
@ -235,33 +272,6 @@ void Core::Squawk::onAccountConnectionStateChanged(Shared::ConnectionState p_sta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Accounts::const_iterator itr = accounts.begin();
|
|
||||||
bool es = true;
|
|
||||||
bool ea = true;
|
|
||||||
Shared::ConnectionState cs = (*itr)->getState();
|
|
||||||
Shared::Availability av = (*itr)->getAvailability();
|
|
||||||
itr++;
|
|
||||||
for (Accounts::const_iterator end = accounts.end(); itr != end; itr++) {
|
|
||||||
Account* item = *itr;
|
|
||||||
if (item->getState() != cs) {
|
|
||||||
es = false;
|
|
||||||
}
|
|
||||||
if (item->getAvailability() != av) {
|
|
||||||
ea = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (es) {
|
|
||||||
if (cs == Shared::ConnectionState::disconnected) {
|
|
||||||
state = Shared::Availability::offline;
|
|
||||||
emit stateChanged(state);
|
|
||||||
} else if (ea) {
|
|
||||||
state = av;
|
|
||||||
emit stateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
void Core::Squawk::onAccountAddContact(const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
@ -391,6 +401,7 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
Shared::ConnectionState st = acc->getState();
|
Shared::ConnectionState st = acc->getState();
|
||||||
QMap<QString, QVariant>::const_iterator mItr;
|
QMap<QString, QVariant>::const_iterator mItr;
|
||||||
bool needToReconnect = false;
|
bool needToReconnect = false;
|
||||||
|
bool wentReconnecting = false;
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
if (mItr != map.end()) {
|
if (mItr != map.end()) {
|
||||||
@ -416,8 +427,16 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
bool activeChanged = false;
|
||||||
acc->reconnect();
|
mItr = map.find("active");
|
||||||
|
if (mItr == map.end() || mItr->toBool() == acc->getActive()) {
|
||||||
|
if (needToReconnect && st != Shared::ConnectionState::disconnected) {
|
||||||
|
acc->reconnect();
|
||||||
|
wentReconnecting = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
acc->setActive(mItr->toBool());
|
||||||
|
activeChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mItr = map.find("login");
|
mItr = map.find("login");
|
||||||
@ -454,6 +473,14 @@ void Core::Squawk::modifyAccountRequest(const QString& name, const QMap<QString,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (state != Shared::Availability::offline) {
|
||||||
|
if (activeChanged && acc->getActive()) {
|
||||||
|
acc->connect();
|
||||||
|
} else if (!wentReconnecting && acc->getActive() && acc->getLastError() == Account::Error::authentication) {
|
||||||
|
acc->connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emit changeAccount(name, map);
|
emit changeAccount(name, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,6 +488,10 @@ void Core::Squawk::onAccountError(const QString& text)
|
|||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
emit changeAccount(acc->getName(), {{"error", text}});
|
emit changeAccount(acc->getName(), {{"error", text}});
|
||||||
|
|
||||||
|
if (acc->getLastError() == Account::Error::authentication) {
|
||||||
|
emit requestPassword(acc->getName(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::removeAccountRequest(const QString& name)
|
void Core::Squawk::removeAccountRequest(const QString& name)
|
||||||
@ -675,6 +706,70 @@ void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card
|
|||||||
itr->second->uploadVCard(card);
|
itr->second->uploadVCard(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("core");
|
||||||
|
int size = settings.beginReadArray("accounts");
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
settings.setArrayIndex(i);
|
||||||
|
Shared::AccountPassword passwordType =
|
||||||
|
Shared::Global::fromInt<Shared::AccountPassword>(
|
||||||
|
settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt()
|
||||||
|
);
|
||||||
|
|
||||||
|
QString password = settings.value("password", "").toString();
|
||||||
|
if (passwordType == Shared::AccountPassword::jammed) {
|
||||||
|
SimpleCrypt crypto(passwordHash);
|
||||||
|
password = crypto.decryptToString(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAccount(
|
||||||
|
settings.value("login").toString(),
|
||||||
|
settings.value("server").toString(),
|
||||||
|
password,
|
||||||
|
settings.value("name").toString(),
|
||||||
|
settings.value("resource").toString(),
|
||||||
|
settings.value("active").toBool(),
|
||||||
|
passwordType
|
||||||
|
);
|
||||||
|
}
|
||||||
|
settings.endArray();
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
qDebug() << "Squawk core is ready";
|
||||||
|
emit ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onAccountNeedPassword()
|
||||||
|
{
|
||||||
|
Account* acc = static_cast<Account*>(sender());
|
||||||
|
switch (acc->getPasswordType()) {
|
||||||
|
case Shared::AccountPassword::alwaysAsk:
|
||||||
|
emit requestPassword(acc->getName(), false);
|
||||||
|
break;
|
||||||
|
case Shared::AccountPassword::kwallet: {
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
if (kwallet.supportState() == PSE::KWallet::success) {
|
||||||
|
kwallet.requestReadPassword(acc->getName());
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
emit requestPassword(acc->getName(), false);
|
||||||
|
#ifdef WITH_KWALLET
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break; //should never happen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Squawk::onWalletRejectPassword(const QString& login)
|
||||||
|
{
|
||||||
|
emit requestPassword(login, false);
|
||||||
|
}
|
||||||
|
|
||||||
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
void Core::Squawk::responsePassword(const QString& account, const QString& password)
|
||||||
{
|
{
|
||||||
AccountsMap::const_iterator itr = amap.find(account);
|
AccountsMap::const_iterator itr = amap.find(account);
|
||||||
@ -682,96 +777,12 @@ void Core::Squawk::responsePassword(const QString& account, const QString& passw
|
|||||||
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
qDebug() << "An attempt to set password to non existing account" << account << ", skipping";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
itr->second->setPassword(password);
|
Account* acc = itr->second;
|
||||||
|
acc->setPassword(password);
|
||||||
emit changeAccount(account, {{"password", password}});
|
emit changeAccount(account, {{"password", password}});
|
||||||
accountReady();
|
if (state != Shared::Availability::offline && acc->getActive()) {
|
||||||
}
|
acc->connect();
|
||||||
|
|
||||||
void Core::Squawk::readSettings()
|
|
||||||
{
|
|
||||||
QSettings settings;
|
|
||||||
settings.beginGroup("core");
|
|
||||||
int size = settings.beginReadArray("accounts");
|
|
||||||
waitingForAccounts = size;
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
settings.setArrayIndex(i);
|
|
||||||
parseAccount(
|
|
||||||
settings.value("login").toString(),
|
|
||||||
settings.value("server").toString(),
|
|
||||||
settings.value("password", "").toString(),
|
|
||||||
settings.value("name").toString(),
|
|
||||||
settings.value("resource").toString(),
|
|
||||||
Shared::Global::fromInt<Shared::AccountPassword>(settings.value("passwordType", static_cast<int>(Shared::AccountPassword::plain)).toInt())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
settings.endArray();
|
|
||||||
settings.endGroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::accountReady()
|
|
||||||
{
|
|
||||||
--waitingForAccounts;
|
|
||||||
|
|
||||||
if (waitingForAccounts == 0) {
|
|
||||||
emit ready();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::parseAccount(
|
|
||||||
const QString& login,
|
|
||||||
const QString& server,
|
|
||||||
const QString& password,
|
|
||||||
const QString& name,
|
|
||||||
const QString& resource,
|
|
||||||
Shared::AccountPassword passwordType
|
|
||||||
)
|
|
||||||
{
|
|
||||||
switch (passwordType) {
|
|
||||||
case Shared::AccountPassword::plain:
|
|
||||||
addAccount(login, server, password, name, resource, passwordType);
|
|
||||||
accountReady();
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::jammed: {
|
|
||||||
SimpleCrypt crypto(passwordHash);
|
|
||||||
QString decrypted = crypto.decryptToString(password);
|
|
||||||
addAccount(login, server, decrypted, name, resource, passwordType);
|
|
||||||
accountReady();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::alwaysAsk:
|
|
||||||
addAccount(login, server, QString(), name, resource, passwordType);
|
|
||||||
emit requestPassword(name);
|
|
||||||
break;
|
|
||||||
case Shared::AccountPassword::kwallet: {
|
|
||||||
addAccount(login, server, QString(), name, resource, passwordType);
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
if (kwallet.supportState() == PSE::KWallet::success) {
|
|
||||||
kwallet.requestReadPassword(name);
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
emit requestPassword(name);
|
|
||||||
#ifdef WITH_KWALLET
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onWalletRejectPassword(const QString& login)
|
|
||||||
{
|
|
||||||
emit requestPassword(login);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::Squawk::onWalletResponsePassword(const QString& login, const QString& password)
|
|
||||||
{
|
|
||||||
AccountsMap::const_iterator itr = amap.find(login);
|
|
||||||
if (itr == amap.end()) {
|
|
||||||
qDebug() << "An attempt to set password to non existing account" << login << ", skipping";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
itr->second->setPassword(password);
|
|
||||||
emit changeAccount(login, {{"password", password}});
|
|
||||||
accountReady();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
void Core::Squawk::onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText)
|
||||||
|
@ -86,7 +86,7 @@ signals:
|
|||||||
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void requestPassword(const QString& account);
|
void requestPassword(const QString& account, bool authernticationError);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start();
|
void start();
|
||||||
@ -134,7 +134,6 @@ private:
|
|||||||
AccountsMap amap;
|
AccountsMap amap;
|
||||||
Shared::Availability state;
|
Shared::Availability state;
|
||||||
NetworkAccess network;
|
NetworkAccess network;
|
||||||
uint8_t waitingForAccounts;
|
|
||||||
bool isInitialized;
|
bool isInitialized;
|
||||||
|
|
||||||
#ifdef WITH_KWALLET
|
#ifdef WITH_KWALLET
|
||||||
@ -148,6 +147,7 @@ private slots:
|
|||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
Shared::AccountPassword passwordType
|
Shared::AccountPassword passwordType
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -172,22 +172,22 @@ private slots:
|
|||||||
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
void onAccountChangeRoomPresence(const QString& jid, const QString& nick, const QMap<QString, QVariant>& data);
|
||||||
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
void onAccountRemoveRoomPresence(const QString& jid, const QString& nick);
|
||||||
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void onAccountChangeMessage(const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
||||||
|
void onAccountNeedPassword();
|
||||||
|
|
||||||
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
void onAccountUploadFileError(const QString& jid, const QString id, const QString& errorText);
|
||||||
|
|
||||||
void onWalletOpened(bool success);
|
void onWalletOpened(bool success);
|
||||||
void onWalletResponsePassword(const QString& login, const QString& password);
|
|
||||||
void onWalletRejectPassword(const QString& login);
|
void onWalletRejectPassword(const QString& login);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSettings();
|
void readSettings();
|
||||||
void accountReady();
|
|
||||||
void parseAccount(
|
void parseAccount(
|
||||||
const QString& login,
|
const QString& login,
|
||||||
const QString& server,
|
const QString& server,
|
||||||
const QString& password,
|
const QString& password,
|
||||||
const QString& name,
|
const QString& name,
|
||||||
const QString& resource,
|
const QString& resource,
|
||||||
|
bool active,
|
||||||
Shared::AccountPassword passwordType
|
Shared::AccountPassword passwordType
|
||||||
);
|
);
|
||||||
|
|
||||||
|
8
core/storage/CMakeLists.txt
Normal file
8
core/storage/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
archive.cpp
|
||||||
|
archive.h
|
||||||
|
storage.cpp
|
||||||
|
storage.h
|
||||||
|
urlstorage.cpp
|
||||||
|
urlstorage.h
|
||||||
|
)
|
7
main/CMakeLists.txt
Normal file
7
main/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
main.cpp
|
||||||
|
application.cpp
|
||||||
|
application.h
|
||||||
|
dialogqueue.cpp
|
||||||
|
dialogqueue.h
|
||||||
|
)
|
566
main/application.cpp
Normal file
566
main/application.cpp
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
Application::Application(Core::Squawk* p_core):
|
||||||
|
QObject(),
|
||||||
|
availability(Shared::Availability::offline),
|
||||||
|
core(p_core),
|
||||||
|
squawk(nullptr),
|
||||||
|
notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"),
|
||||||
|
roster(),
|
||||||
|
conversations(),
|
||||||
|
dialogueQueue(roster),
|
||||||
|
nowQuitting(false),
|
||||||
|
destroyingSquawk(false),
|
||||||
|
storage()
|
||||||
|
{
|
||||||
|
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
||||||
|
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
||||||
|
|
||||||
|
|
||||||
|
//connecting myself to the backed
|
||||||
|
connect(this, &Application::changeState, core, &Core::Squawk::changeState);
|
||||||
|
connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined);
|
||||||
|
connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin);
|
||||||
|
connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact);
|
||||||
|
connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact);
|
||||||
|
connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage);
|
||||||
|
connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage);
|
||||||
|
connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage);
|
||||||
|
connect(&roster, &Models::Roster::requestArchive,
|
||||||
|
std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3));
|
||||||
|
|
||||||
|
connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest);
|
||||||
|
connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid);
|
||||||
|
|
||||||
|
|
||||||
|
//coonecting backend to myself
|
||||||
|
connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage);
|
||||||
|
connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive);
|
||||||
|
connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount);
|
||||||
|
connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount);
|
||||||
|
connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addContact, this, &Application::addContact);
|
||||||
|
connect(core, &Core::Squawk::addGroup, this, &Application::addGroup);
|
||||||
|
connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup);
|
||||||
|
connect(core, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact);
|
||||||
|
connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence);
|
||||||
|
connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom);
|
||||||
|
connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom);
|
||||||
|
connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom);
|
||||||
|
connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant);
|
||||||
|
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false));
|
||||||
|
connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true));
|
||||||
|
connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress);
|
||||||
|
connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword);
|
||||||
|
connect(core, &Core::Squawk::ready, this, &Application::readSettings);
|
||||||
|
|
||||||
|
QDBusConnection sys = QDBusConnection::sessionBus();
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"NotificationClosed",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationClosed(quint32, quint32))
|
||||||
|
);
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"ActionInvoked",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationInvoked(quint32, const QString&))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::~Application() {}
|
||||||
|
|
||||||
|
void Application::quit()
|
||||||
|
{
|
||||||
|
if (!nowQuitting) {
|
||||||
|
nowQuitting = true;
|
||||||
|
emit quitting();
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people
|
||||||
|
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
||||||
|
disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
itr->second->close();
|
||||||
|
}
|
||||||
|
conversations.clear();
|
||||||
|
dialogueQueue.quit();
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
squawk->close();
|
||||||
|
}
|
||||||
|
if (!destroyingSquawk) {
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::checkForTheLastWindow()
|
||||||
|
{
|
||||||
|
if (QApplication::topLevelWidgets().size() > 0) {
|
||||||
|
emit readyToQuit();
|
||||||
|
} else {
|
||||||
|
connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::createMainWindow()
|
||||||
|
{
|
||||||
|
if (squawk == nullptr) {
|
||||||
|
squawk = new Squawk(roster);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::notify, this, &Application::notify);
|
||||||
|
connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription);
|
||||||
|
connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation);
|
||||||
|
connect(squawk, &Squawk::openConversation, this, &Application::openConversation);
|
||||||
|
connect(squawk, &Squawk::changeState, this, &Application::setState);
|
||||||
|
connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest);
|
||||||
|
connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest);
|
||||||
|
connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount);
|
||||||
|
connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
|
connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest);
|
||||||
|
connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard);
|
||||||
|
connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard);
|
||||||
|
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
|
||||||
|
dialogueQueue.setParentWidnow(squawk);
|
||||||
|
squawk->stateChanged(availability);
|
||||||
|
squawk->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkClosing()
|
||||||
|
{
|
||||||
|
dialogueQueue.setParentWidnow(nullptr);
|
||||||
|
|
||||||
|
if (!nowQuitting) {
|
||||||
|
disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroyingSquawk = true;
|
||||||
|
squawk->deleteLater();
|
||||||
|
squawk = nullptr;
|
||||||
|
|
||||||
|
//for now
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkDestroyed() {
|
||||||
|
destroyingSquawk = false;
|
||||||
|
if (nowQuitting) {
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Application::notify(const QString& account, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
QString jid = msg.getPenPalJid();
|
||||||
|
QString name = QString(roster.getContactName(account, jid));
|
||||||
|
QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource()));
|
||||||
|
QVariantList args;
|
||||||
|
args << QString();
|
||||||
|
|
||||||
|
uint32_t notificationId = qHash(msg.getId());
|
||||||
|
args << notificationId;
|
||||||
|
if (path.size() > 0) {
|
||||||
|
args << path;
|
||||||
|
} else {
|
||||||
|
args << QString("mail-message"); //TODO should here better be unknown user icon?
|
||||||
|
}
|
||||||
|
if (msg.getType() == Shared::Message::groupChat) {
|
||||||
|
args << msg.getFromResource() + tr(" from ") + name;
|
||||||
|
} else {
|
||||||
|
args << name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString body(msg.getBody());
|
||||||
|
QString oob(msg.getOutOfBandUrl());
|
||||||
|
if (body == oob) {
|
||||||
|
body = tr("Attached file");
|
||||||
|
}
|
||||||
|
|
||||||
|
args << body;
|
||||||
|
args << QStringList({
|
||||||
|
"markAsRead", tr("Mark as Read"),
|
||||||
|
"openConversation", tr("Open conversation")
|
||||||
|
});
|
||||||
|
args << QVariantMap({
|
||||||
|
{"desktop-entry", qApp->desktopFileName()},
|
||||||
|
{"category", QString("message")},
|
||||||
|
{"urgency", 1},
|
||||||
|
// {"sound-file", "/path/to/macaw/squawk"},
|
||||||
|
{"sound-name", QString("message-new-instant")}
|
||||||
|
});
|
||||||
|
args << -1;
|
||||||
|
notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
||||||
|
|
||||||
|
storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId())));
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QApplication::alert(squawk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationClosed(quint32 id, quint32 reason)
|
||||||
|
{
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
|
||||||
|
//TODO may ba also mark as read?
|
||||||
|
}
|
||||||
|
if (reason != 1) { //just expired, can be activated again from history, so no removing for now
|
||||||
|
storage.erase(id);
|
||||||
|
qDebug() << "Notification" << id << "was closed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationInvoked(quint32 id, const QString& action)
|
||||||
|
{
|
||||||
|
qDebug() << "Notification" << id << action << "request";
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (action == "markAsRead") {
|
||||||
|
roster.markMessageAsRead(itr->second.first, itr->second.second);
|
||||||
|
} else if (action == "openConversation") {
|
||||||
|
focusConversation(itr->second.first, "", itr->second.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::unreadMessagesCountChanged(int count)
|
||||||
|
{
|
||||||
|
QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update");
|
||||||
|
signal << qApp->desktopFileName() + QLatin1String(".desktop");
|
||||||
|
signal << QVariantMap ({
|
||||||
|
{"count-visible", count != 0},
|
||||||
|
{"count", count}
|
||||||
|
});
|
||||||
|
QDBusConnection::sessionBus().send(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId)
|
||||||
|
{
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
if (squawk->currentConversationId() != id) {
|
||||||
|
QModelIndex index = roster.getContactIndex(id.account, id.name, resource);
|
||||||
|
squawk->select(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk->isMinimized()) {
|
||||||
|
squawk->showNormal();
|
||||||
|
} else {
|
||||||
|
squawk->show();
|
||||||
|
}
|
||||||
|
squawk->raise();
|
||||||
|
squawk->activateWindow();
|
||||||
|
} else {
|
||||||
|
openConversation(id, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO focus messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::setState(Shared::Availability p_availability)
|
||||||
|
{
|
||||||
|
if (availability != p_availability) {
|
||||||
|
availability = p_availability;
|
||||||
|
emit changeState(availability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::stateChanged(Shared::Availability state)
|
||||||
|
{
|
||||||
|
availability = state;
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
squawk->stateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
int avail;
|
||||||
|
if (settings.contains("availability")) {
|
||||||
|
avail = settings.value("availability").toInt();
|
||||||
|
} else {
|
||||||
|
avail = static_cast<int>(Shared::Availability::online);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
|
createMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::writeSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("availability", static_cast<int>(availability));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::requestPassword(const QString& account, bool authenticationError) {
|
||||||
|
if (authenticationError) {
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askCredentials);
|
||||||
|
} else {
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void Application::onConversationClosed()
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
Models::Roster::ElId id(conv->getAccount(), conv->getJid());
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conversations.erase(itr);
|
||||||
|
}
|
||||||
|
if (conv->isMuc) {
|
||||||
|
Room* room = static_cast<Room*>(conv);
|
||||||
|
if (!room->autoJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe)
|
||||||
|
{
|
||||||
|
Models::Item::Type type = roster.getContactType(id);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Models::Item::contact:
|
||||||
|
if (subscribe) {
|
||||||
|
emit subscribeContact(id.account, id.name, "");
|
||||||
|
} else {
|
||||||
|
emit unsubscribeContact(id.account, id.name, "");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Models::Item::room:
|
||||||
|
setRoomAutoJoin(id.account, id.name, subscribe);
|
||||||
|
if (!isConverstationOpened(id)) {
|
||||||
|
emit setRoomJoined(id.account, id.name, subscribe);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::subscribeConversation(Conversation* conv)
|
||||||
|
{
|
||||||
|
connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage);
|
||||||
|
connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage);
|
||||||
|
connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend);
|
||||||
|
connect(conv, &Conversation::notifyableMessage, this, &Application::notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::openConversation(const Models::Roster::ElId& id, const QString& resource)
|
||||||
|
{
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
Models::Account* acc = roster.getAccount(id.account);
|
||||||
|
Conversation* conv = nullptr;
|
||||||
|
bool created = false;
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conv = itr->second;
|
||||||
|
} else {
|
||||||
|
Models::Element* el = roster.getElement(id);
|
||||||
|
if (el != nullptr) {
|
||||||
|
if (el->type == Models::Item::room) {
|
||||||
|
created = true;
|
||||||
|
Models::Room* room = static_cast<Models::Room*>(el);
|
||||||
|
conv = new Room(acc, room);
|
||||||
|
if (!room->getJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
} else if (el->type == Models::Item::contact) {
|
||||||
|
created = true;
|
||||||
|
conv = new Chat(acc, static_cast<Models::Contact*>(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conv != nullptr) {
|
||||||
|
if (created) {
|
||||||
|
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
subscribeConversation(conv);
|
||||||
|
conversations.insert(std::make_pair(id, conv));
|
||||||
|
}
|
||||||
|
|
||||||
|
conv->show();
|
||||||
|
conv->raise();
|
||||||
|
conv->activateWindow();
|
||||||
|
|
||||||
|
if (resource.size() > 0) {
|
||||||
|
conv->setPalResource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationMessage(const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.addMessage(acc, msg);
|
||||||
|
emit sendMessage(acc, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.changeMessage(acc, msg.getPenPalJid(), originalId, {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::pending)}
|
||||||
|
});
|
||||||
|
emit replaceMessage(acc, originalId, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationResend(const QString& id)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
QString jid = conv->getJid();
|
||||||
|
|
||||||
|
emit resendMessage(acc, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkOpenedConversation() {
|
||||||
|
subscribeConversation(squawk->currentConversation);
|
||||||
|
Models::Roster::ElId id = squawk->currentConversationId();
|
||||||
|
|
||||||
|
const Models::Element* el = roster.getElementConst(id);
|
||||||
|
if (el != nullptr && el->isRoom() && !static_cast<const Models::Room*>(el)->getJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::removeAccount(const QString& account)
|
||||||
|
{
|
||||||
|
Conversations::const_iterator itr = conversations.begin();
|
||||||
|
while (itr != conversations.end()) {
|
||||||
|
if (itr->first.account == account) {
|
||||||
|
Conversations::const_iterator lItr = itr;
|
||||||
|
++itr;
|
||||||
|
Conversation* conv = lItr->second;
|
||||||
|
disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
conv->close();
|
||||||
|
conversations.erase(lItr);
|
||||||
|
} else {
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk != nullptr && squawk->currentConversationId().account == account) {
|
||||||
|
squawk->closeCurrentConversation();
|
||||||
|
}
|
||||||
|
|
||||||
|
roster.removeAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
|
QString attr = itr.key();
|
||||||
|
roster.updateAccount(account, attr, *itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
roster.addContact(account, jid, group, data);
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = roster.getAccountIndex(account);
|
||||||
|
squawk->expand(ind);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addGroup(const QString& account, const QString& name)
|
||||||
|
{
|
||||||
|
roster.addGroup(account, name);
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = roster.getAccountIndex(account);
|
||||||
|
squawk->expand(ind);
|
||||||
|
if (settings.value(name + "/expanded", false).toBool()) {
|
||||||
|
squawk->expand(roster.getGroupIndex(account, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::isConverstationOpened(const Models::Roster::ElId& id) const {
|
||||||
|
return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);}
|
118
main/application.h
Normal file
118
main/application.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef APPLICATION_H
|
||||||
|
#define APPLICATION_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
|
||||||
|
#include "dialogqueue.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include "ui/squawk.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
#include "ui/widgets/conversation.h"
|
||||||
|
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/enums.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class Application : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Application(Core::Squawk* core);
|
||||||
|
~Application();
|
||||||
|
|
||||||
|
bool isConverstationOpened(const Models::Roster::ElId& id) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
||||||
|
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
||||||
|
|
||||||
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
|
||||||
|
void quitting();
|
||||||
|
void readyToQuit();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void readSettings();
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
void unreadMessagesCountChanged(int count);
|
||||||
|
void setState(Shared::Availability availability);
|
||||||
|
|
||||||
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
|
void removeAccount(const QString& account);
|
||||||
|
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
||||||
|
|
||||||
|
void addGroup(const QString& account, const QString& name);
|
||||||
|
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
void requestPassword(const QString& account, bool authenticationError);
|
||||||
|
|
||||||
|
void writeSettings();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConversationClosed();
|
||||||
|
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
||||||
|
void onSquawkOpenedConversation();
|
||||||
|
void onConversationMessage(const Shared::Message& msg);
|
||||||
|
void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg);
|
||||||
|
void onConversationResend(const QString& id);
|
||||||
|
void stateChanged(Shared::Availability state);
|
||||||
|
void onSquawkClosing();
|
||||||
|
void onSquawkDestroyed();
|
||||||
|
void onNotificationClosed(quint32 id, quint32 reason);
|
||||||
|
void onNotificationInvoked(quint32 id, const QString& action);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createMainWindow();
|
||||||
|
void subscribeConversation(Conversation* conv);
|
||||||
|
void checkForTheLastWindow();
|
||||||
|
void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||||
|
typedef std::map<uint32_t, std::pair<Models::Roster::ElId, QString>> Notifications;
|
||||||
|
|
||||||
|
Shared::Availability availability;
|
||||||
|
Core::Squawk* core;
|
||||||
|
Squawk* squawk;
|
||||||
|
QDBusInterface notifications;
|
||||||
|
Models::Roster roster;
|
||||||
|
Conversations conversations;
|
||||||
|
DialogQueue dialogueQueue;
|
||||||
|
bool nowQuitting;
|
||||||
|
bool destroyingSquawk;
|
||||||
|
Notifications storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPLICATION_H
|
187
main/dialogqueue.cpp
Normal file
187
main/dialogqueue.cpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "dialogqueue.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
DialogQueue::DialogQueue(const Models::Roster& p_roster):
|
||||||
|
QObject(),
|
||||||
|
currentSource(),
|
||||||
|
currentAction(none),
|
||||||
|
queue(),
|
||||||
|
collection(queue.get<0>()),
|
||||||
|
sequence(queue.get<1>()),
|
||||||
|
prompt(nullptr),
|
||||||
|
parent(nullptr),
|
||||||
|
roster(p_roster)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::~DialogQueue()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::quit()
|
||||||
|
{
|
||||||
|
queue.clear();
|
||||||
|
if (currentAction != none) {
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::setParentWidnow(QMainWindow* p_parent)
|
||||||
|
{
|
||||||
|
parent = p_parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogQueue::addAction(const QString& source, DialogQueue::Action action)
|
||||||
|
{
|
||||||
|
if (action == none) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (currentAction == none) {
|
||||||
|
currentAction = action;
|
||||||
|
currentSource = source;
|
||||||
|
performNextAction();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (currentAction != action || currentSource != source) {
|
||||||
|
std::pair<Queue::iterator, bool> result = queue.emplace(source, action);
|
||||||
|
return result.second;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DialogQueue::cancelAction(const QString& source, DialogQueue::Action action)
|
||||||
|
{
|
||||||
|
if (source == currentSource && action == currentAction) {
|
||||||
|
actionDone();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Collection::iterator itr = collection.find(ActionId{source, action});
|
||||||
|
if (itr != collection.end()) {
|
||||||
|
collection.erase(itr);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::performNextAction()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
actionDone();
|
||||||
|
break;
|
||||||
|
case askPassword: {
|
||||||
|
QInputDialog* dialog = new QInputDialog(parent);
|
||||||
|
prompt = dialog;
|
||||||
|
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
||||||
|
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
||||||
|
dialog->setInputMode(QInputDialog::TextInput);
|
||||||
|
dialog->setTextEchoMode(QLineEdit::Password);
|
||||||
|
dialog->setLabelText(tr("Input the password for account %1").arg(currentSource));
|
||||||
|
dialog->setWindowTitle(tr("Password for account %1").arg(currentSource));
|
||||||
|
dialog->setTextValue("");
|
||||||
|
dialog->exec();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case askCredentials: {
|
||||||
|
CredentialsPrompt* dialog = new CredentialsPrompt(parent);
|
||||||
|
prompt = dialog;
|
||||||
|
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
|
||||||
|
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
|
||||||
|
const Models::Account* acc = roster.getAccountConst(currentSource);
|
||||||
|
dialog->setAccount(currentSource);
|
||||||
|
dialog->setLogin(acc->getLogin());
|
||||||
|
dialog->setPassword(acc->getPassword());
|
||||||
|
dialog->exec();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::onPropmtAccepted()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
break;
|
||||||
|
case askPassword: {
|
||||||
|
QInputDialog* dialog = static_cast<QInputDialog*>(prompt);
|
||||||
|
emit responsePassword(currentSource, dialog->textValue());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case askCredentials: {
|
||||||
|
CredentialsPrompt* dialog = static_cast<CredentialsPrompt*>(prompt);
|
||||||
|
emit modifyAccountRequest(currentSource, {
|
||||||
|
{"login", dialog->getLogin()},
|
||||||
|
{"password", dialog->getPassword()}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::onPropmtRejected()
|
||||||
|
{
|
||||||
|
switch (currentAction) {
|
||||||
|
case none:
|
||||||
|
break;
|
||||||
|
case askPassword:
|
||||||
|
case askCredentials:
|
||||||
|
emit disconnectAccount(currentSource);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
actionDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DialogQueue::actionDone()
|
||||||
|
{
|
||||||
|
prompt->deleteLater();
|
||||||
|
prompt = nullptr;
|
||||||
|
|
||||||
|
if (queue.empty()) {
|
||||||
|
currentAction = none;
|
||||||
|
currentSource = "";
|
||||||
|
} else {
|
||||||
|
Sequence::iterator itr = sequence.begin();
|
||||||
|
currentAction = itr->action;
|
||||||
|
currentSource = itr->source;
|
||||||
|
sequence.erase(itr);
|
||||||
|
performNextAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::ActionId::ActionId(const QString& p_source, DialogQueue::Action p_action):
|
||||||
|
source(p_source),
|
||||||
|
action(p_action) {}
|
||||||
|
|
||||||
|
bool DialogQueue::ActionId::operator < (const DialogQueue::ActionId& other) const
|
||||||
|
{
|
||||||
|
if (action == other.action) {
|
||||||
|
return source < other.source;
|
||||||
|
} else {
|
||||||
|
return action < other.action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogQueue::ActionId::ActionId(const DialogQueue::ActionId& other):
|
||||||
|
source(other.source),
|
||||||
|
action(other.action) {}
|
101
main/dialogqueue.h
Normal file
101
main/dialogqueue.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef DIALOGQUEUE_H
|
||||||
|
#define DIALOGQUEUE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include <boost/multi_index_container.hpp>
|
||||||
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
|
#include <boost/multi_index/sequenced_index.hpp>
|
||||||
|
|
||||||
|
#include <ui/widgets/accounts/credentialsprompt.h>
|
||||||
|
#include <ui/models/roster.h>
|
||||||
|
|
||||||
|
class DialogQueue : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Action {
|
||||||
|
none,
|
||||||
|
askPassword,
|
||||||
|
askCredentials
|
||||||
|
};
|
||||||
|
|
||||||
|
DialogQueue(const Models::Roster& roster);
|
||||||
|
~DialogQueue();
|
||||||
|
|
||||||
|
bool addAction(const QString& source, Action action);
|
||||||
|
bool cancelAction(const QString& source, Action action);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
||||||
|
void responsePassword(const QString& account, const QString& password);
|
||||||
|
void disconnectAccount(const QString&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setParentWidnow(QMainWindow* parent);
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void performNextAction();
|
||||||
|
void actionDone();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onPropmtAccepted();
|
||||||
|
void onPropmtRejected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString currentSource;
|
||||||
|
Action currentAction;
|
||||||
|
|
||||||
|
struct ActionId {
|
||||||
|
public:
|
||||||
|
ActionId(const QString& p_source, Action p_action);
|
||||||
|
ActionId(const ActionId& other);
|
||||||
|
|
||||||
|
const QString source;
|
||||||
|
const Action action;
|
||||||
|
|
||||||
|
bool operator < (const ActionId& other) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef boost::multi_index_container <
|
||||||
|
ActionId,
|
||||||
|
boost::multi_index::indexed_by <
|
||||||
|
boost::multi_index::ordered_unique <
|
||||||
|
boost::multi_index::identity <ActionId>
|
||||||
|
>,
|
||||||
|
boost::multi_index::sequenced<>
|
||||||
|
>
|
||||||
|
> Queue;
|
||||||
|
|
||||||
|
typedef Queue::nth_index<0>::type Collection;
|
||||||
|
typedef Queue::nth_index<1>::type Sequence;
|
||||||
|
|
||||||
|
Queue queue;
|
||||||
|
Collection& collection;
|
||||||
|
Sequence& sequence;
|
||||||
|
|
||||||
|
QDialog* prompt;
|
||||||
|
QMainWindow* parent;
|
||||||
|
const Models::Roster& roster;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DIALOGQUEUE_H
|
140
main/main.cpp
Normal file
140
main/main.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shared/global.h"
|
||||||
|
#include "shared/messageinfo.h"
|
||||||
|
#include "shared/pathcheck.h"
|
||||||
|
#include "main/application.h"
|
||||||
|
#include "core/signalcatcher.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
|
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
||||||
|
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||||
|
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||||
|
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
||||||
|
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||||
|
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
||||||
|
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
SignalCatcher sc(&app);
|
||||||
|
|
||||||
|
QApplication::setApplicationName("squawk");
|
||||||
|
QApplication::setOrganizationName("macaw.me");
|
||||||
|
QApplication::setApplicationDisplayName("Squawk");
|
||||||
|
QApplication::setApplicationVersion("0.2.2");
|
||||||
|
app.setDesktopFileName("squawk");
|
||||||
|
|
||||||
|
QTranslator qtTranslator;
|
||||||
|
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
|
app.installTranslator(&qtTranslator);
|
||||||
|
|
||||||
|
QTranslator myappTranslator;
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
bool found = false;
|
||||||
|
for (QString share : shares) {
|
||||||
|
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.installTranslator(&myappTranslator);
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":images/logo.svg", QSize(16, 16));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(24, 24));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(32, 32));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(48, 48));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(64, 64));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(96, 96));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(128, 128));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(256, 256));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(512, 512));
|
||||||
|
QApplication::setWindowIcon(icon);
|
||||||
|
|
||||||
|
new Shared::Global(); //translates enums
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QVariant vs = settings.value("style");
|
||||||
|
if (vs.isValid()) {
|
||||||
|
QString style = vs.toString().toLower();
|
||||||
|
if (style != "system") {
|
||||||
|
Shared::Global::setStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Shared::Global::supported("colorSchemeTools")) {
|
||||||
|
QVariant vt = settings.value("theme");
|
||||||
|
if (vt.isValid()) {
|
||||||
|
QString theme = vt.toString();
|
||||||
|
if (theme.toLower() != "system") {
|
||||||
|
Shared::Global::setTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString path = Shared::downloadsPathCheck();
|
||||||
|
if (path.size() > 0) {
|
||||||
|
settings.setValue("downloadsPath", path);
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't initialize directory for downloads, quitting";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Squawk* squawk = new Core::Squawk();
|
||||||
|
QThread* coreThread = new QThread();
|
||||||
|
squawk->moveToThread(coreThread);
|
||||||
|
|
||||||
|
Application application(squawk);
|
||||||
|
|
||||||
|
QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit);
|
||||||
|
|
||||||
|
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
||||||
|
QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop);
|
||||||
|
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
coreThread->start();
|
||||||
|
int result = app.exec();
|
||||||
|
|
||||||
|
if (coreThread->isRunning()) {
|
||||||
|
//coreThread->wait();
|
||||||
|
//todo if I uncomment that, the app will not quit if it has reconnected at least once
|
||||||
|
//it feels like a symptom of something badly desinged in the core thread
|
||||||
|
//need to investigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Yury Gubich <blue@macaw.me>
|
# Maintainer: Yury Gubich <blue@macaw.me>
|
||||||
pkgname=squawk
|
pkgname=squawk
|
||||||
pkgver=0.2.0
|
pkgver=0.2.2
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
pkgdesc="An XMPP desktop messenger, written on pure c++ (qt)"
|
||||||
arch=('i686' 'x86_64')
|
arch=('i686' 'x86_64')
|
||||||
@ -14,7 +14,7 @@ optdepends=('kwallet: secure password storage (requires rebuild)'
|
|||||||
'kio: better show in folder action (requires rebuild)')
|
'kio: better show in folder action (requires rebuild)')
|
||||||
|
|
||||||
source=("$pkgname-$pkgver.tar.gz")
|
source=("$pkgname-$pkgver.tar.gz")
|
||||||
sha256sums=('8e93d3dbe1fc35cfecb7783af409c6a264244d11609b2241d4fe77d43d068419')
|
sha256sums=('e4fa2174a3ba95159cc3b0bac3f00550c9e0ce971c55334e2662696a4543fc7e')
|
||||||
build() {
|
build() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release
|
||||||
@ -22,5 +22,5 @@ build() {
|
|||||||
}
|
}
|
||||||
package() {
|
package() {
|
||||||
cd "$srcdir/squawk"
|
cd "$srcdir/squawk"
|
||||||
DESTDIR="$pkgdir/" cmake --build . --target install
|
DESTDIR="$pkgdir/" cmake --build . --target install
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
#include "enums.h"
|
#include "enums.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
|
||||||
Shared::Global* Shared::Global::instance = 0;
|
Shared::Global* Shared::Global::instance = 0;
|
||||||
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
||||||
|
@ -47,7 +47,6 @@ namespace Shared {
|
|||||||
|
|
||||||
class Global {
|
class Global {
|
||||||
Q_DECLARE_TR_FUNCTIONS(Global)
|
Q_DECLARE_TR_FUNCTIONS(Global)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
enum class Preview {
|
enum class Preview {
|
||||||
@ -64,7 +63,7 @@ namespace Shared {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Global();
|
Global();
|
||||||
|
|
||||||
static Global* getInstance();
|
static Global* getInstance();
|
||||||
static QString getName(Availability av);
|
static QString getName(Availability av);
|
||||||
static QString getName(ConnectionState cs);
|
static QString getName(ConnectionState cs);
|
||||||
|
@ -40,5 +40,5 @@ QString Shared::processMessageBody(const QString& msg)
|
|||||||
{
|
{
|
||||||
QString processed = msg.toHtmlEscaped();
|
QString processed = msg.toHtmlEscaped();
|
||||||
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
|
processed.replace(urlReg, "<a href=\"\\1\">\\1</a>");
|
||||||
return "<p style=\"white-space: pre-wrap;\">" + processed + "</p>";
|
return "<p style=\"white-space: pre-wrap; line-height: 1em;\">" + processed + "</p>";
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,12 @@ static const std::vector<QColor> colorPalette = {
|
|||||||
QColor(17, 17, 80),
|
QColor(17, 17, 80),
|
||||||
QColor(54, 54, 94)
|
QColor(54, 54, 94)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Hover {
|
||||||
|
nothing,
|
||||||
|
text,
|
||||||
|
anchor
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SHARED_UTILS_H
|
#endif // SHARED_UTILS_H
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
find_package(Qt5LinguistTools)
|
find_package(Qt5LinguistTools)
|
||||||
|
|
||||||
set(TS_FILES
|
set(TS_FILES
|
||||||
|
squawk.en.ts
|
||||||
squawk.ru.ts
|
squawk.ru.ts
|
||||||
squawk.pt_BR.ts
|
squawk.pt_BR.ts
|
||||||
)
|
)
|
||||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||||
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
add_custom_target(translations ALL DEPENDS ${QM_FILES})
|
||||||
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/squawk/l10n)
|
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk/l10n)
|
||||||
|
|
||||||
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
add_dependencies(${CMAKE_PROJECT_NAME} translations)
|
||||||
|
1420
translations/squawk.en.ts
Normal file
1420
translations/squawk.en.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,108 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="pt_BR">
|
<TS version="2.1" language="pt_BR">
|
||||||
|
<context>
|
||||||
|
<name>About</name>
|
||||||
|
<message>
|
||||||
|
<source>About Squawk</source>
|
||||||
|
<translation>Sorbe Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Squawk</source>
|
||||||
|
<translation>Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About</source>
|
||||||
|
<translatorcomment>Tab title</translatorcomment>
|
||||||
|
<translation>Sobre</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>XMPP (jabber) messenger</source>
|
||||||
|
<translation>XMPP (jabber) mensageiro</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>(c) 2019 - 2022, Yury Gubich</source>
|
||||||
|
<translation>(c) 2019 - 2022, Yury Gubich</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk">Project site</a></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk">Site do projeto</a></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Licença: GNU General Public License versão 3</a></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Components</source>
|
||||||
|
<translation>Componentes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation>Versão</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>0.0.0</source>
|
||||||
|
<translation>0.0.0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Report Bugs</source>
|
||||||
|
<translation>Relatório de erros</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Please report any bug you find!
|
||||||
|
To report bugs you can use:</source>
|
||||||
|
<translation>Por favor reportar qualquer erro que você encontrar!
|
||||||
|
Para relatar bugs você pode usar:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk/issues">Rastreador de bugs do projeto</></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</source>
|
||||||
|
<translation>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</source>
|
||||||
|
<translation>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thanks To</source>
|
||||||
|
<translation>Graças ao</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Vae</source>
|
||||||
|
<translation>Vae</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Major refactoring, bug fixes, constructive criticism</source>
|
||||||
|
<translation>Refatoração importante, correção de erros, críticas construtivas</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shunf4</source>
|
||||||
|
<translation>Shunf4</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Major refactoring, bug fixes, build adaptations for Windows and MacOS</source>
|
||||||
|
<translation>Refatoração importante, correção de erros, adaptações de construção para Windows e MacOS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Bruno F. Fontes</source>
|
||||||
|
<translation>Bruno F. Fontes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Brazilian Portuguese translation</source>
|
||||||
|
<translation>Tradução para o português do Brasil</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>(built against %1)</source>
|
||||||
|
<translation>(Versão durante a compilação %1)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>License</source>
|
||||||
|
<translation>Licença</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Account</name>
|
<name>Account</name>
|
||||||
<message>
|
<message>
|
||||||
@ -10,10 +112,12 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Your account login</source>
|
<source>Your account login</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Suas informações de login</translation>
|
<translation>Suas informações de login</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>john_smith1987</source>
|
<source>john_smith1987</source>
|
||||||
|
<translatorcomment>Login placeholder</translatorcomment>
|
||||||
<translation>josé_silva1987</translation>
|
<translation>josé_silva1987</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -22,10 +126,12 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<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>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>O endereço do servidor da sua conta, como o 404.city ou o macaw.me</translation>
|
<translation>O endereço do servidor da sua conta, como o 404.city ou o macaw.me</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>macaw.me</source>
|
<source>macaw.me</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>macaw.me</translation>
|
<translation>macaw.me</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -38,6 +144,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password of your account</source>
|
<source>Password of your account</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Senha da sua conta</translation>
|
<translation>Senha da sua conta</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -46,10 +153,11 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Just a name how would you call this account, doesn't affect anything</source>
|
<source>Just a name how would you call this account, doesn't affect anything</source>
|
||||||
<translation>Apenas um nome para identificar esta conta. Não influencia em nada</translation>
|
<translation>Apenas um nome para identificar esta conta. Não influencia em nada (não pode ser mudado)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>John</source>
|
<source>John</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>José</translation>
|
<translation>José</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -58,6 +166,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>A resource name like "Home" or "Work"</source>
|
<source>A resource name like "Home" or "Work"</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Um nome de recurso como "Casa" ou "Trabalho"</translation>
|
<translation>Um nome de recurso como "Casa" ou "Trabalho"</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -69,6 +178,14 @@
|
|||||||
<source>Password storage</source>
|
<source>Password storage</source>
|
||||||
<translation>Armazenamento de senha</translation>
|
<translation>Armazenamento de senha</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Active</source>
|
||||||
|
<translation>Ativo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>enable</source>
|
||||||
|
<translation>habilitar</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Accounts</name>
|
<name>Accounts</name>
|
||||||
@ -98,30 +215,135 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation>Desconectar</translation>
|
<translation type="vanished">Desconectar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Deactivate</source>
|
||||||
|
<translation>Desativar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Activate</source>
|
||||||
|
<translation>Ativar</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Application</name>
|
||||||
|
<message>
|
||||||
|
<source> from </source>
|
||||||
|
<translation> de </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Attached file</source>
|
||||||
|
<translation>Arquivo anexado</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark as Read</source>
|
||||||
|
<translation>Marcar como lido</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open conversation</source>
|
||||||
|
<translation>Abrir conversa</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Conversation</name>
|
<name>Conversation</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Type your message here...</source>
|
<source>Type your message here...</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>Digite sua mensagem aqui...</translation>
|
<translation>Digite sua mensagem aqui...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Chose a file to send</source>
|
<source>Chose a file to send</source>
|
||||||
<translation>Escolha um arquivo para enviar</translation>
|
<translation>Escolha um arquivo para enviar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Drop files here to attach them to your message</source>
|
||||||
|
<translation>Arraste seus arquivos aqui para anexá-los a sua mensagem</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
||||||
<translation></translation>
|
<translation><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Drop files here to attach them to your message</source>
|
<source>Paste Image</source>
|
||||||
<translation>Arraste seus arquivos aqui para anexá-los a sua mensagem</translation>
|
<translation>Colar imagem</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Try sending again</source>
|
||||||
|
<translation>Tente enviar de novo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy selected</source>
|
||||||
|
<translation>Copiar selecionado</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy message</source>
|
||||||
|
<translation>Copiar mensagem</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Abrir</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show in folder</source>
|
||||||
|
<translation>Show in explorer</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit</source>
|
||||||
|
<translation>Editar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Editing message...</source>
|
||||||
|
<translation>Messae está sendo editado...</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CredentialsPrompt</name>
|
||||||
|
<message>
|
||||||
|
<source>Authentication error: %1</source>
|
||||||
|
<translatorcomment>Window title</translatorcomment>
|
||||||
|
<translation>Erro de autenticação: %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Couldn't authenticate account %1: login or password is icorrect.
|
||||||
|
Would you like to check them and try again?</source>
|
||||||
|
<translation>Não foi possível autenticar a conta %1: login ou senha incorretos.
|
||||||
|
Deseja verificá-los e tentar novamente?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Login</source>
|
||||||
|
<translation>Usuário</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Your account login (without @server.domain)</source>
|
||||||
|
<translation>Suas informações de login (sem @server.domain)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Password</source>
|
||||||
|
<translation>Senha</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Your password</source>
|
||||||
|
<translation>Senha da sua conta</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DialogQueue</name>
|
||||||
|
<message>
|
||||||
|
<source>Input the password for account %1</source>
|
||||||
|
<translation>Digite a senha para a conta %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Password for account %1</source>
|
||||||
|
<translation>Senha para a conta %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -370,14 +592,14 @@ p, li { white-space: pre-wrap; }
|
|||||||
<name>Message</name>
|
<name>Message</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Open</source>
|
<source>Open</source>
|
||||||
<translation>Abrir</translation>
|
<translation type="vanished">Abrir</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MessageLine</name>
|
<name>MessageLine</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Downloading...</source>
|
<source>Downloading...</source>
|
||||||
<translation>Baixando...</translation>
|
<translation type="vanished">Baixando...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download</source>
|
<source>Download</source>
|
||||||
@ -386,28 +608,28 @@ p, li { white-space: pre-wrap; }
|
|||||||
<message>
|
<message>
|
||||||
<source>Error uploading file: %1
|
<source>Error uploading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation>Error fazendo upload do arquivo:
|
<translation type="vanished">Error fazendo upload do arquivo:
|
||||||
%1
|
%1
|
||||||
Você pode tentar novamente</translation>
|
Você pode tentar novamente</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Upload</source>
|
<source>Upload</source>
|
||||||
<translation>Upload</translation>
|
<translation type="vanished">Upload</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error downloading file: %1
|
<source>Error downloading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation>Erro baixando arquivo:
|
<translation type="vanished">Erro baixando arquivo:
|
||||||
%1
|
%1
|
||||||
Você pode tentar novamente</translation>
|
Você pode tentar novamente</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Uploading...</source>
|
<source>Uploading...</source>
|
||||||
<translation>Fazendo upload...</translation>
|
<translation type="vanished">Fazendo upload...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Push the button to download the file</source>
|
<source>Push the button to download the file</source>
|
||||||
<translation>Pressione o botão para baixar o arquivo</translation>
|
<translation type="vanished">Pressione o botão para baixar o arquivo</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -481,7 +703,7 @@ Você pode tentar novamente</translation>
|
|||||||
<name>NewContact</name>
|
<name>NewContact</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Add new contact</source>
|
<source>Add new contact</source>
|
||||||
<translatorcomment>Заголовок окна</translatorcomment>
|
<translatorcomment>Window title</translatorcomment>
|
||||||
<translation>Adicionar novo contato</translation>
|
<translation>Adicionar novo contato</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -502,7 +724,7 @@ Você pode tentar novamente</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>name@server.dmn</source>
|
<source>name@server.dmn</source>
|
||||||
<translatorcomment>Placeholder поля ввода JID</translatorcomment>
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>nome@servidor.com.br</translation>
|
<translation>nome@servidor.com.br</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -518,6 +740,66 @@ Você pode tentar novamente</translation>
|
|||||||
<translation>José Silva</translation>
|
<translation>José Silva</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageAppearance</name>
|
||||||
|
<message>
|
||||||
|
<source>Theme</source>
|
||||||
|
<translation>Estilo</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Color scheme</source>
|
||||||
|
<translation>Esquema de cores</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>System</source>
|
||||||
|
<translation>Do sistema</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageGeneral</name>
|
||||||
|
<message>
|
||||||
|
<source>Downloads path</source>
|
||||||
|
<translation>Pasta de downloads</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Browse</source>
|
||||||
|
<translation>6 / 5,000
|
||||||
|
Translation results
|
||||||
|
Navegar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select where downloads folder is going to be</source>
|
||||||
|
<translation>Selecione onde a pasta de downloads ficará</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Settings</name>
|
||||||
|
<message>
|
||||||
|
<source>Preferences</source>
|
||||||
|
<translatorcomment>Window title</translatorcomment>
|
||||||
|
<translation>Preferências</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>General</source>
|
||||||
|
<translation>Geral</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Appearance</source>
|
||||||
|
<translation>Aparência</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Apply</source>
|
||||||
|
<translation>Aplicar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Cancelar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ok</source>
|
||||||
|
<translation>Feito</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Squawk</name>
|
<name>Squawk</name>
|
||||||
<message>
|
<message>
|
||||||
@ -530,6 +812,7 @@ Você pode tentar novamente</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Squawk</source>
|
<source>Squawk</source>
|
||||||
|
<translatorcomment>Menu bar entry</translatorcomment>
|
||||||
<translation>Squawk</translation>
|
<translation>Squawk</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -550,11 +833,11 @@ Você pode tentar novamente</translation>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation>Desconectar</translation>
|
<translation type="vanished">Desconectar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation>Conectar</translation>
|
<translation type="vanished">Conectar</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>VCard</source>
|
<source>VCard</source>
|
||||||
@ -627,26 +910,46 @@ ser exibido com
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Attached file</source>
|
<source>Attached file</source>
|
||||||
<translation>Arquivo anexado</translation>
|
<translation type="vanished">Arquivo anexado</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Input the password for account %1</source>
|
<source>Input the password for account %1</source>
|
||||||
<translation>Digite a senha para a conta %1</translation>
|
<translation type="vanished">Digite a senha para a conta %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password for account %1</source>
|
<source>Password for account %1</source>
|
||||||
<translation>Senha para a conta %1</translation>
|
<translation type="vanished">Senha para a conta %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Please select a contact to start chatting</source>
|
<source>Please select a contact to start chatting</source>
|
||||||
<translation>Por favor selecione um contato para começar a conversar</translation>
|
<translation>Por favor selecione um contato para começar a conversar</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Ajuda</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Preferences</source>
|
||||||
|
<translation>Preferências</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Squawk</source>
|
||||||
|
<translation>Sorbe Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Deactivate</source>
|
||||||
|
<translation>Desativar</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Activate</source>
|
||||||
|
<translation>Ativar</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VCard</name>
|
<name>VCard</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Received 12.07.2007 at 17.35</source>
|
<source>Received 12.07.2007 at 17.35</source>
|
||||||
<translation>Recebido 12/07/2007 às 17:35</translation>
|
<translation>Nunca atualizado</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>General</source>
|
<source>General</source>
|
||||||
@ -682,7 +985,7 @@ ser exibido com
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Unit / Department</source>
|
<source>Unit / Department</source>
|
||||||
<translation>Unidade/Departamento</translation>
|
<translation>Unidade / Departamento</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Role / Profession</source>
|
<source>Role / Profession</source>
|
||||||
|
@ -1,6 +1,108 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!DOCTYPE TS>
|
<!DOCTYPE TS>
|
||||||
<TS version="2.1" language="ru_RU">
|
<TS version="2.1" language="ru_RU">
|
||||||
|
<context>
|
||||||
|
<name>About</name>
|
||||||
|
<message>
|
||||||
|
<source>About Squawk</source>
|
||||||
|
<translation>О Программе Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Squawk</source>
|
||||||
|
<translation>Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About</source>
|
||||||
|
<translatorcomment>Tab title</translatorcomment>
|
||||||
|
<translation>Общее</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>XMPP (jabber) messenger</source>
|
||||||
|
<translation>XMPP (jabber) мессенджер</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>(c) 2019 - 2022, Yury Gubich</source>
|
||||||
|
<translation>(c) 2019 - 2022, Юрий Губич</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk">Project site</a></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk">Сайт проекта</a></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">Лицензия: GNU General Public License версия 3</a></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Components</source>
|
||||||
|
<translation>Компоненты</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation>Версия</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>0.0.0</source>
|
||||||
|
<translation>0.0.0</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Report Bugs</source>
|
||||||
|
<translation>Сообщать об ошибках</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Please report any bug you find!
|
||||||
|
To report bugs you can use:</source>
|
||||||
|
<translation>Пожалуйста, сообщайте о любых ошибках!
|
||||||
|
Способы сообщить об ошибках:</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source><a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</></source>
|
||||||
|
<translation><a href="https://git.macaw.me/blue/squawk/issues">Баг-трекер проекта</></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</source>
|
||||||
|
<translation>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</source>
|
||||||
|
<translation>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Thanks To</source>
|
||||||
|
<translation>Благодарности</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Vae</source>
|
||||||
|
<translation>Vae</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Major refactoring, bug fixes, constructive criticism</source>
|
||||||
|
<translation>Крупный рефакторинг, исправление ошибок, конструктивная критика</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Shunf4</source>
|
||||||
|
<translation>Shunf4</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Major refactoring, bug fixes, build adaptations for Windows and MacOS</source>
|
||||||
|
<translation>Крупный рефакторинг, исправление ошибок, адаптация сборки под Windows and MacOS</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Bruno F. Fontes</source>
|
||||||
|
<translation>Bruno F. Fontes</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Brazilian Portuguese translation</source>
|
||||||
|
<translation>Перевод на Португальский (Бразилия)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>(built against %1)</source>
|
||||||
|
<translation>(версия при сборке %1)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>License</source>
|
||||||
|
<translation>Лицензия</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Account</name>
|
<name>Account</name>
|
||||||
<message>
|
<message>
|
||||||
@ -10,10 +112,12 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Your account login</source>
|
<source>Your account login</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Имя пользователя Вашей учетной записи</translation>
|
<translation>Имя пользователя Вашей учетной записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>john_smith1987</source>
|
<source>john_smith1987</source>
|
||||||
|
<translatorcomment>Login placeholder</translatorcomment>
|
||||||
<translation>ivan_ivanov1987</translation>
|
<translation>ivan_ivanov1987</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -22,10 +126,12 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<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>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
|
<translation>Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>macaw.me</source>
|
<source>macaw.me</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>macaw.me</translation>
|
<translation>macaw.me</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -38,6 +144,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password of your account</source>
|
<source>Password of your account</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Пароль вашей учетной записи</translation>
|
<translation>Пароль вашей учетной записи</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -46,10 +153,11 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Just a name how would you call this account, doesn't affect anything</source>
|
<source>Just a name how would you call this account, doesn't affect anything</source>
|
||||||
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым</translation>
|
<translation>Просто имя, то как Вы называете свою учетную запись, может быть любым (нельзя поменять)</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>John</source>
|
<source>John</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>Иван</translation>
|
<translation>Иван</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -58,6 +166,7 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>A resource name like "Home" or "Work"</source>
|
<source>A resource name like "Home" or "Work"</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
<translation>Имя этой программы для ваших контактов, может быть "Home" или "Phone"</translation>
|
<translation>Имя этой программы для ваших контактов, может быть "Home" или "Phone"</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -69,6 +178,14 @@
|
|||||||
<source>Password storage</source>
|
<source>Password storage</source>
|
||||||
<translation>Хранение пароля</translation>
|
<translation>Хранение пароля</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Active</source>
|
||||||
|
<translation>Активен</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>enable</source>
|
||||||
|
<translation>включен</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Accounts</name>
|
<name>Accounts</name>
|
||||||
@ -98,30 +215,139 @@
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation>Отключить</translation>
|
<translation type="vanished">Отключить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Deactivate</source>
|
||||||
|
<translation>Деактивировать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Activate</source>
|
||||||
|
<translation>Активировать</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Application</name>
|
||||||
|
<message>
|
||||||
|
<source> from </source>
|
||||||
|
<translation> от </translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Attached file</source>
|
||||||
|
<translation>Прикрепленный файл</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Mark as Read</source>
|
||||||
|
<translation>Пометить прочитанным</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open conversation</source>
|
||||||
|
<translation>Открыть окно беседы</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Conversation</name>
|
<name>Conversation</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Type your message here...</source>
|
<source>Type your message here...</source>
|
||||||
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>Введите сообщение...</translation>
|
<translation>Введите сообщение...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Chose a file to send</source>
|
<source>Chose a file to send</source>
|
||||||
<translation>Выберите файл для отправки</translation>
|
<translation>Выберите файл для отправки</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Drop files here to attach them to your message</source>
|
||||||
|
<translation>Бросьте файлы сюда для того что бы прикрепить их к сообщению</translation>
|
||||||
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<source><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Liberation Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
||||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></source>
|
||||||
<translation></translation>
|
<translation><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||||
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
|
p, li { white-space: pre-wrap; }
|
||||||
|
</style></head><body style=" font-family:'Noto Sans'; font-size:8pt; font-weight:400; font-style:normal;">
|
||||||
|
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Drop files here to attach them to your message</source>
|
<source>Paste Image</source>
|
||||||
<translation>Бросьте файлы сюда для того что бы прикрепить их к сообщению</translation>
|
<translation>Вставить изображение</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Try sending again</source>
|
||||||
|
<translation>Отправить снова</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy selected</source>
|
||||||
|
<translation>Скопировать выделенное</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Copy message</source>
|
||||||
|
<translation>Скопировать сообщение</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Open</source>
|
||||||
|
<translation>Открыть</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Show in folder</source>
|
||||||
|
<translation>Показать в проводнике</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Edit</source>
|
||||||
|
<translation>Редактировать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Editing message...</source>
|
||||||
|
<translation>Сообщение редактируется...</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>CredentialsPrompt</name>
|
||||||
|
<message>
|
||||||
|
<source>Authentication error: %1</source>
|
||||||
|
<translatorcomment>Window title</translatorcomment>
|
||||||
|
<translation>Ошибка аутентификации: %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Couldn't authenticate account %1: login or password is icorrect.
|
||||||
|
Would you like to check them and try again?</source>
|
||||||
|
<translation>Не получилось аутентифицировать
|
||||||
|
учетную запись %1:
|
||||||
|
имя пользователя или пароль введены неверно.
|
||||||
|
Желаете ли проверить их и
|
||||||
|
попробовать аутентифицироваться еще раз?</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Login</source>
|
||||||
|
<translation>Имя учетной записи</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Your account login (without @server.domain)</source>
|
||||||
|
<translatorcomment>Tooltip</translatorcomment>
|
||||||
|
<translation>Имя вашей учтетной записи (без @server.domain)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Password</source>
|
||||||
|
<translation>Пароль</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Your password</source>
|
||||||
|
<translation>Ваш пароль</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>DialogQueue</name>
|
||||||
|
<message>
|
||||||
|
<source>Input the password for account %1</source>
|
||||||
|
<translation>Введите пароль для учетной записи %1</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Password for account %1</source>
|
||||||
|
<translation>Пароль для учетной записи %1</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -370,14 +596,14 @@ p, li { white-space: pre-wrap; }
|
|||||||
<name>Message</name>
|
<name>Message</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Open</source>
|
<source>Open</source>
|
||||||
<translation>Открыть</translation>
|
<translation type="vanished">Открыть</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>MessageLine</name>
|
<name>MessageLine</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Downloading...</source>
|
<source>Downloading...</source>
|
||||||
<translation>Скачивается...</translation>
|
<translation type="vanished">Скачивается...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Download</source>
|
<source>Download</source>
|
||||||
@ -386,28 +612,28 @@ p, li { white-space: pre-wrap; }
|
|||||||
<message>
|
<message>
|
||||||
<source>Error uploading file: %1
|
<source>Error uploading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation>Ошибка загрузки файла на сервер:
|
<translation type="vanished">Ошибка загрузки файла на сервер:
|
||||||
%1
|
%1
|
||||||
Для того, что бы попробовать снова нажмите на кнопку</translation>
|
Для того, что бы попробовать снова нажмите на кнопку</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Upload</source>
|
<source>Upload</source>
|
||||||
<translation>Загрузить</translation>
|
<translation type="vanished">Загрузить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Error downloading file: %1
|
<source>Error downloading file: %1
|
||||||
You can try again</source>
|
You can try again</source>
|
||||||
<translation>Ошибка скачивания файла:
|
<translation type="vanished">Ошибка скачивания файла:
|
||||||
%1
|
%1
|
||||||
Вы можете попробовать снова</translation>
|
Вы можете попробовать снова</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Uploading...</source>
|
<source>Uploading...</source>
|
||||||
<translation>Загружается...</translation>
|
<translation type="vanished">Загружается...</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Push the button to download the file</source>
|
<source>Push the button to download the file</source>
|
||||||
<translation>Нажмите на кнопку что бы загрузить файл</translation>
|
<translation type="vanished">Нажмите на кнопку что бы загрузить файл</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
@ -481,7 +707,7 @@ You can try again</source>
|
|||||||
<name>NewContact</name>
|
<name>NewContact</name>
|
||||||
<message>
|
<message>
|
||||||
<source>Add new contact</source>
|
<source>Add new contact</source>
|
||||||
<translatorcomment>Заголовок окна</translatorcomment>
|
<translatorcomment>Window title</translatorcomment>
|
||||||
<translation>Добавление нового контакта</translation>
|
<translation>Добавление нового контакта</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -502,7 +728,7 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>name@server.dmn</source>
|
<source>name@server.dmn</source>
|
||||||
<translatorcomment>Placeholder поля ввода JID</translatorcomment>
|
<translatorcomment>Placeholder</translatorcomment>
|
||||||
<translation>name@server.dmn</translation>
|
<translation>name@server.dmn</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -518,6 +744,64 @@ You can try again</source>
|
|||||||
<translation>Иван Иванов</translation>
|
<translation>Иван Иванов</translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageAppearance</name>
|
||||||
|
<message>
|
||||||
|
<source>Theme</source>
|
||||||
|
<translation>Оформление</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Color scheme</source>
|
||||||
|
<translation>Цветовая схема</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>System</source>
|
||||||
|
<translation>Системная</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>PageGeneral</name>
|
||||||
|
<message>
|
||||||
|
<source>Downloads path</source>
|
||||||
|
<translation>Папка для сохраненных файлов</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Browse</source>
|
||||||
|
<translation>Выбрать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Select where downloads folder is going to be</source>
|
||||||
|
<translation>Выберете папку, в которую будут сохраняться файлы</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>Settings</name>
|
||||||
|
<message>
|
||||||
|
<source>Preferences</source>
|
||||||
|
<translatorcomment>Window title</translatorcomment>
|
||||||
|
<translation>Настройки</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>General</source>
|
||||||
|
<translation>Общее</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Appearance</source>
|
||||||
|
<translation>Внешний вид</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Apply</source>
|
||||||
|
<translation>Применить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Cancel</source>
|
||||||
|
<translation>Отменить</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Ok</source>
|
||||||
|
<translation>Готово</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>Squawk</name>
|
<name>Squawk</name>
|
||||||
<message>
|
<message>
|
||||||
@ -530,6 +814,7 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Squawk</source>
|
<source>Squawk</source>
|
||||||
|
<translatorcomment>Menu bar entry</translatorcomment>
|
||||||
<translation>Squawk</translation>
|
<translation>Squawk</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
@ -550,11 +835,11 @@ You can try again</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Disconnect</source>
|
<source>Disconnect</source>
|
||||||
<translation>Отключить</translation>
|
<translation type="vanished">Отключить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Connect</source>
|
<source>Connect</source>
|
||||||
<translation>Подключить</translation>
|
<translation type="vanished">Подключить</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>VCard</source>
|
<source>VCard</source>
|
||||||
@ -627,20 +912,40 @@ to be displayed as %1</source>
|
|||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Attached file</source>
|
<source>Attached file</source>
|
||||||
<translation>Прикрепленный файл</translation>
|
<translation type="vanished">Прикрепленный файл</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Input the password for account %1</source>
|
<source>Input the password for account %1</source>
|
||||||
<translation>Введите пароль для учетной записи %1</translation>
|
<translation type="vanished">Введите пароль для учетной записи %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Password for account %1</source>
|
<source>Password for account %1</source>
|
||||||
<translation>Пароль для учетной записи %1</translation>
|
<translation type="vanished">Пароль для учетной записи %1</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Please select a contact to start chatting</source>
|
<source>Please select a contact to start chatting</source>
|
||||||
<translation>Выберите контакт или группу что бы начать переписку</translation>
|
<translation>Выберите контакт или группу что бы начать переписку</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Help</source>
|
||||||
|
<translation>Помощь</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Preferences</source>
|
||||||
|
<translation>Настройки</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>About Squawk</source>
|
||||||
|
<translation>О Программе Squawk</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Deactivate</source>
|
||||||
|
<translation>Деактивировать</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Activate</source>
|
||||||
|
<translation>Активировать</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VCard</name>
|
<name>VCard</name>
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
target_sources(squawk PRIVATE squawk.cpp squawk.h squawk.ui)
|
target_sources(squawk PRIVATE
|
||||||
|
squawk.cpp
|
||||||
|
squawk.h
|
||||||
|
squawk.ui
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(models)
|
add_subdirectory(models)
|
||||||
add_subdirectory(utils)
|
add_subdirectory(utils)
|
||||||
|
@ -32,7 +32,8 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
|||||||
state(Shared::ConnectionState::disconnected),
|
state(Shared::ConnectionState::disconnected),
|
||||||
availability(Shared::Availability::offline),
|
availability(Shared::Availability::offline),
|
||||||
passwordType(Shared::AccountPassword::plain),
|
passwordType(Shared::AccountPassword::plain),
|
||||||
wasEverConnected(false)
|
wasEverConnected(false),
|
||||||
|
active(false)
|
||||||
{
|
{
|
||||||
QMap<QString, QVariant>::const_iterator sItr = data.find("state");
|
QMap<QString, QVariant>::const_iterator sItr = data.find("state");
|
||||||
if (sItr != data.end()) {
|
if (sItr != data.end()) {
|
||||||
@ -46,6 +47,10 @@ Models::Account::Account(const QMap<QString, QVariant>& data, Models::Item* pare
|
|||||||
if (pItr != data.end()) {
|
if (pItr != data.end()) {
|
||||||
setPasswordType(pItr.value().toUInt());
|
setPasswordType(pItr.value().toUInt());
|
||||||
}
|
}
|
||||||
|
QMap<QString, QVariant>::const_iterator acItr = data.find("active");
|
||||||
|
if (acItr != data.end()) {
|
||||||
|
setActive(acItr.value().toBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Account::~Account()
|
Models::Account::~Account()
|
||||||
@ -176,6 +181,8 @@ QVariant Models::Account::data(int column) const
|
|||||||
return avatarPath;
|
return avatarPath;
|
||||||
case 9:
|
case 9:
|
||||||
return Shared::Global::getName(passwordType);
|
return Shared::Global::getName(passwordType);
|
||||||
|
case 10:
|
||||||
|
return active;
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
@ -183,7 +190,7 @@ QVariant Models::Account::data(int column) const
|
|||||||
|
|
||||||
int Models::Account::columnCount() const
|
int Models::Account::columnCount() const
|
||||||
{
|
{
|
||||||
return 10;
|
return 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Models::Account::update(const QString& field, const QVariant& value)
|
void Models::Account::update(const QString& field, const QVariant& value)
|
||||||
@ -208,6 +215,8 @@ void Models::Account::update(const QString& field, const QVariant& value)
|
|||||||
setAvatarPath(value.toString());
|
setAvatarPath(value.toString());
|
||||||
} else if (field == "passwordType") {
|
} else if (field == "passwordType") {
|
||||||
setPasswordType(value.toUInt());
|
setPasswordType(value.toUInt());
|
||||||
|
} else if (field == "active") {
|
||||||
|
setActive(value.toBool());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,3 +290,16 @@ void Models::Account::setPasswordType(unsigned int pt)
|
|||||||
{
|
{
|
||||||
setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(pt));
|
setPasswordType(Shared::Global::fromInt<Shared::AccountPassword>(pt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::Account::getActive() const
|
||||||
|
{
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Account::setActive(bool p_active)
|
||||||
|
{
|
||||||
|
if (active != p_active) {
|
||||||
|
active = p_active;
|
||||||
|
changed(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -58,6 +58,9 @@ namespace Models {
|
|||||||
|
|
||||||
void setAvatarPath(const QString& path);
|
void setAvatarPath(const QString& path);
|
||||||
QString getAvatarPath() const;
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
|
void setActive(bool active);
|
||||||
|
bool getActive() const;
|
||||||
|
|
||||||
void setAvailability(Shared::Availability p_avail);
|
void setAvailability(Shared::Availability p_avail);
|
||||||
void setAvailability(unsigned int p_avail);
|
void setAvailability(unsigned int p_avail);
|
||||||
@ -91,6 +94,7 @@ namespace Models {
|
|||||||
Shared::Availability availability;
|
Shared::Availability availability;
|
||||||
Shared::AccountPassword passwordType;
|
Shared::AccountPassword passwordType;
|
||||||
bool wasEverConnected;
|
bool wasEverConnected;
|
||||||
|
bool active;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
|
@ -48,6 +48,10 @@ QVariant Models::Accounts::data (const QModelIndex& index, int role) const
|
|||||||
answer = Shared::connectionStateIcon(accs[index.row()]->getState());
|
answer = Shared::connectionStateIcon(accs[index.row()]->getState());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Qt::ForegroundRole:
|
||||||
|
if (!accs[index.row()]->getActive()) {
|
||||||
|
answer = qApp->palette().brush(QPalette::Disabled, QPalette::Text);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,16 @@ void Models::Contact::removePresence(const QString& name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Presence * Models::Contact::getPresence(const QString& name)
|
||||||
|
{
|
||||||
|
QMap<QString, Presence*>::iterator itr = presences.find(name);
|
||||||
|
if (itr == presences.end()) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return itr.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Contact::refresh()
|
void Models::Contact::refresh()
|
||||||
{
|
{
|
||||||
QDateTime lastActivity;
|
QDateTime lastActivity;
|
||||||
|
@ -51,6 +51,7 @@ public:
|
|||||||
|
|
||||||
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removePresence(const QString& name);
|
void removePresence(const QString& name);
|
||||||
|
Presence* getPresence(const QString& name);
|
||||||
|
|
||||||
QString getContactName() const;
|
QString getContactName() const;
|
||||||
QString getStatus() const;
|
QString getStatus() const;
|
||||||
|
@ -134,6 +134,11 @@ unsigned int Models::Element::getMessagesCount() const
|
|||||||
return feed->unreadMessagesCount();
|
return feed->unreadMessagesCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::Element::markMessageAsRead(const QString& id) const
|
||||||
|
{
|
||||||
|
return feed->markMessageAsRead(id);
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Element::addMessage(const Shared::Message& data)
|
void Models::Element::addMessage(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
feed->addMessage(data);
|
feed->addMessage(data);
|
||||||
@ -171,6 +176,7 @@ void Models::Element::fileError(const QString& messageId, const QString& error,
|
|||||||
|
|
||||||
void Models::Element::onFeedUnreadMessagesCountChanged()
|
void Models::Element::onFeedUnreadMessagesCountChanged()
|
||||||
{
|
{
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
if (type == contact) {
|
if (type == contact) {
|
||||||
changed(4);
|
changed(4);
|
||||||
} else if (type == room) {
|
} else if (type == room) {
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
void addMessage(const Shared::Message& data);
|
void addMessage(const Shared::Message& data);
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
unsigned int getMessagesCount() const;
|
unsigned int getMessagesCount() const;
|
||||||
|
bool markMessageAsRead(const QString& id) const;
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
void responseArchive(const std::list<Shared::Message> list, bool last);
|
||||||
bool isRoom() const;
|
bool isRoom() const;
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
@ -52,6 +53,7 @@ signals:
|
|||||||
void requestArchive(const QString& before);
|
void requestArchive(const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
|
void unreadMessagesCountChanged();
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -264,6 +264,16 @@ void Models::Room::removeParticipant(const QString& p_name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Participant * Models::Room::getParticipant(const QString& p_name)
|
||||||
|
{
|
||||||
|
std::map<QString, Participant*>::const_iterator itr = participants.find(p_name);
|
||||||
|
if (itr == participants.end()) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Participant* part = itr->second;
|
Participant* part = itr->second;
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeParticipant(const QString& name);
|
void removeParticipant(const QString& name);
|
||||||
|
Participant* getParticipant(const QString& name);
|
||||||
|
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
@ -276,6 +276,18 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Qt::ForegroundRole:
|
||||||
|
switch (item->type) {
|
||||||
|
case Item::account: {
|
||||||
|
Account* acc = static_cast<Account*>(item);
|
||||||
|
if (!acc->getActive()) {
|
||||||
|
result = qApp->palette().brush(QPalette::Disabled, QPalette::Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -451,6 +463,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
|||||||
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
|
connect(contact, &Contact::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
||||||
contacts.insert(std::make_pair(id, contact));
|
contacts.insert(std::make_pair(id, contact));
|
||||||
} else {
|
} else {
|
||||||
contact = itr->second;
|
contact = itr->second;
|
||||||
@ -536,8 +549,8 @@ void Models::Roster::removeGroup(const QString& account, const QString& name)
|
|||||||
|
|
||||||
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, jid});
|
Element* el = getElement(ElId(account, jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
el->update(itr.key(), itr.value());
|
el->update(itr.key(), itr.value());
|
||||||
}
|
}
|
||||||
@ -546,8 +559,8 @@ void Models::Roster::changeContact(const QString& account, const QString& jid, c
|
|||||||
|
|
||||||
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, jid});
|
Element* el = getElement(ElId(account, jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->changeMessage(id, data);
|
el->changeMessage(id, data);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found";
|
qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found";
|
||||||
@ -694,8 +707,8 @@ void Models::Roster::removePresence(const QString& account, const QString& jid,
|
|||||||
|
|
||||||
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, data.getPenPalJid()});
|
Element* el = getElement(ElId(account, data.getPenPalJid()));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->addMessage(data);
|
el->addMessage(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -751,7 +764,7 @@ void Models::Roster::removeAccount(const QString& account)
|
|||||||
acc->deleteLater();
|
acc->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactName(const QString& account, const QString& jid)
|
QString Models::Roster::getContactName(const QString& account, const QString& jid) const
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -789,10 +802,11 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM
|
|||||||
}
|
}
|
||||||
|
|
||||||
Room* room = new Room(acc, jid, data);
|
Room* room = new Room(acc, jid, data);
|
||||||
connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive);
|
connect(room, &Room::requestArchive, this, &Roster::onElementRequestArchive);
|
||||||
connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
|
connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
||||||
rooms.insert(std::make_pair(id, room));
|
rooms.insert(std::make_pair(id, room));
|
||||||
acc->appendChild(room);
|
acc->appendChild(room);
|
||||||
}
|
}
|
||||||
@ -895,7 +909,7 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource)
|
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -915,9 +929,36 @@ QString Models::Roster::getContactIconPath(const QString& account, const QString
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Account * Models::Roster::getAccount(const QString& name)
|
Models::Account * Models::Roster::getAccount(const QString& name) {
|
||||||
|
return const_cast<Models::Account*>(getAccountConst(name));}
|
||||||
|
|
||||||
|
const Models::Account * Models::Roster::getAccountConst(const QString& name) const {
|
||||||
|
return accounts.at(name);}
|
||||||
|
|
||||||
|
const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const
|
||||||
{
|
{
|
||||||
return accounts.find(name)->second;
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
|
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
return cItr->second;
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
return rItr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId)
|
||||||
|
{
|
||||||
|
const Element* el = getElementConst(elementId);
|
||||||
|
if (el != nullptr) {
|
||||||
|
return el->markMessageAsRead(messageId);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
||||||
@ -936,7 +977,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
if (itr == accounts.end()) {
|
if (itr == accounts.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
std::map<ElId, Group*>::const_iterator gItr = groups.find({account, name});
|
std::map<ElId, Group*>::const_iterator gItr = groups.find(ElId(account, name));
|
||||||
if (gItr == groups.end()) {
|
if (gItr == groups.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
@ -946,6 +987,48 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource)
|
||||||
|
{
|
||||||
|
std::map<QString, Account*>::const_iterator itr = accounts.find(account);
|
||||||
|
if (itr == accounts.end()) {
|
||||||
|
return QModelIndex();
|
||||||
|
} else {
|
||||||
|
Account* acc = itr->second;
|
||||||
|
QModelIndex accIndex = index(acc->row(), 0, QModelIndex());
|
||||||
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(ElId(account, jid));
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
QModelIndex contactIndex = index(acc->getContact(jid), 0, accIndex);
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
return contactIndex;
|
||||||
|
} else {
|
||||||
|
Presence* pres = cItr->second->getPresence(resource);
|
||||||
|
if (pres != nullptr) {
|
||||||
|
return index(pres->row(), 0, contactIndex);
|
||||||
|
} else {
|
||||||
|
return contactIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(ElId(account, jid));
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
QModelIndex roomIndex = index(rItr->second->row(), 0, accIndex);
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
return roomIndex;
|
||||||
|
} else {
|
||||||
|
Participant* part = rItr->second->getParticipant(resource);
|
||||||
|
if (part != nullptr) {
|
||||||
|
return index(part->row(), 0, roomIndex);
|
||||||
|
} else {
|
||||||
|
return roomIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Roster::onElementRequestArchive(const QString& before)
|
void Models::Roster::onElementRequestArchive(const QString& before)
|
||||||
{
|
{
|
||||||
Element* el = static_cast<Element*>(sender());
|
Element* el = static_cast<Element*>(sender());
|
||||||
@ -956,7 +1039,7 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
Element* el = getElement(id);
|
Element* el = getElement(id);
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->responseArchive(list, last);
|
el->responseArchive(list, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -964,8 +1047,8 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileProgress(info.messageId, value, up);
|
el->fileProgress(info.messageId, value, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -974,8 +1057,8 @@ void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qr
|
|||||||
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileComplete(info.messageId, up);
|
el->fileComplete(info.messageId, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -984,8 +1067,8 @@ void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bo
|
|||||||
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileError(info.messageId, err, up);
|
el->fileError(info.messageId, err, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -993,20 +1076,20 @@ void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const
|
|||||||
|
|
||||||
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
||||||
{
|
{
|
||||||
std::map<ElId, Contact*>::iterator cItr = contacts.find(id);
|
return const_cast<Models::Element*>(getElementConst(id));
|
||||||
|
|
||||||
if (cItr != contacts.end()) {
|
|
||||||
return cItr->second;
|
|
||||||
} else {
|
|
||||||
std::map<ElId, Room*>::iterator rItr = rooms.find(id);
|
|
||||||
if (rItr != rooms.end()) {
|
|
||||||
return rItr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const
|
||||||
|
{
|
||||||
|
const Models::Element* el = getElementConst(id);
|
||||||
|
if (el == nullptr) {
|
||||||
|
return Item::root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return el->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Models::Roster::onAccountReconnected()
|
void Models::Roster::onAccountReconnected()
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
@ -1019,3 +1102,14 @@ void Models::Roster::onAccountReconnected()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Models::Roster::recalculateUnreadMessages()
|
||||||
|
{
|
||||||
|
int count(0);
|
||||||
|
for (const std::pair<const ElId, Contact*>& pair : contacts) {
|
||||||
|
count += pair.second->getMessagesCount();
|
||||||
|
}
|
||||||
|
for (const std::pair<const ElId, Room*>& pair : rooms) {
|
||||||
|
count += pair.second->getMessagesCount();
|
||||||
|
}
|
||||||
|
emit unreadMessagesCountChanged(count);
|
||||||
|
}
|
||||||
|
@ -46,6 +46,7 @@ public:
|
|||||||
Roster(QObject* parent = 0);
|
Roster(QObject* parent = 0);
|
||||||
~Roster();
|
~Roster();
|
||||||
|
|
||||||
|
public slots:
|
||||||
void addAccount(const QMap<QString, QVariant> &data);
|
void addAccount(const QMap<QString, QVariant> &data);
|
||||||
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
@ -65,7 +66,12 @@ public:
|
|||||||
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(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 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);
|
||||||
QString getContactName(const QString& account, const QString& jid);
|
|
||||||
|
public:
|
||||||
|
QString getContactName(const QString& account, const QString& jid) const;
|
||||||
|
Item::Type getContactType(const Models::Roster::ElId& id) const;
|
||||||
|
const Element* getElementConst(const ElId& id) const;
|
||||||
|
Element* getElement(const ElId& id);
|
||||||
|
|
||||||
QVariant data ( const QModelIndex& index, int role ) const override;
|
QVariant data ( const QModelIndex& index, int role ) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
@ -77,10 +83,13 @@ public:
|
|||||||
|
|
||||||
std::deque<QString> groupList(const QString& account) const;
|
std::deque<QString> groupList(const QString& account) const;
|
||||||
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
||||||
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource);
|
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const;
|
||||||
Account* getAccount(const QString& name);
|
Account* getAccount(const QString& name);
|
||||||
|
const Account* getAccountConst(const QString& name) const;
|
||||||
QModelIndex getAccountIndex(const QString& name);
|
QModelIndex getAccountIndex(const QString& name);
|
||||||
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
||||||
|
QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "");
|
||||||
|
bool markMessageAsRead(const ElId& elementId, const QString& messageId);
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
@ -92,12 +101,10 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
|
void unreadMessagesCountChanged(int count);
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
private:
|
|
||||||
Element* getElement(const ElId& id);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
||||||
void onAccountReconnected();
|
void onAccountReconnected();
|
||||||
@ -109,7 +116,8 @@ private slots:
|
|||||||
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
||||||
void onChildMoved();
|
void onChildMoved();
|
||||||
void onElementRequestArchive(const QString& before);
|
void onElementRequestArchive(const QString& before);
|
||||||
|
void recalculateUnreadMessages();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Item* root;
|
Item* root;
|
||||||
std::map<QString, Account*> accounts;
|
std::map<QString, Account*> accounts;
|
||||||
|
750
ui/squawk.cpp
750
ui/squawk.cpp
File diff suppressed because it is too large
Load Diff
94
ui/squawk.h
94
ui/squawk.h
@ -22,16 +22,16 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QtDBus/QDBusInterface>
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
#include "widgets/accounts.h"
|
#include "widgets/accounts/accounts.h"
|
||||||
#include "widgets/chat.h"
|
#include "widgets/chat.h"
|
||||||
#include "widgets/room.h"
|
#include "widgets/room.h"
|
||||||
#include "widgets/newcontact.h"
|
#include "widgets/newcontact.h"
|
||||||
@ -39,103 +39,79 @@
|
|||||||
#include "models/roster.h"
|
#include "models/roster.h"
|
||||||
#include "widgets/vcard/vcard.h"
|
#include "widgets/vcard/vcard.h"
|
||||||
#include "widgets/settings/settings.h"
|
#include "widgets/settings/settings.h"
|
||||||
|
#include "widgets/about.h"
|
||||||
|
|
||||||
#include "shared/shared.h"
|
#include "shared/shared.h"
|
||||||
|
#include "shared/global.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class Squawk;
|
class Squawk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Application;
|
||||||
|
|
||||||
class Squawk : public QMainWindow
|
class Squawk : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
friend class Application;
|
||||||
public:
|
public:
|
||||||
explicit Squawk(QWidget *parent = nullptr);
|
explicit Squawk(Models::Roster& rosterModel, QWidget *parent = nullptr);
|
||||||
~Squawk() override;
|
~Squawk() override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void closing();
|
||||||
void newAccountRequest(const QMap<QString, QVariant>&);
|
void newAccountRequest(const QMap<QString, QVariant>&);
|
||||||
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
|
||||||
void removeAccountRequest(const QString&);
|
void removeAccountRequest(const QString&);
|
||||||
void connectAccount(const QString&);
|
void connectAccount(const QString&);
|
||||||
void disconnectAccount(const QString&);
|
void disconnectAccount(const QString&);
|
||||||
void changeState(Shared::Availability state);
|
void changeState(Shared::Availability state);
|
||||||
void sendMessage(const QString& account, const Shared::Message& data);
|
|
||||||
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
|
||||||
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
|
||||||
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 unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
|
||||||
void removeContactRequest(const QString& account, const QString& jid);
|
void removeContactRequest(const QString& account, const QString& jid);
|
||||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
|
||||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
|
||||||
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& account, const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void removeRoomRequest(const QString& account, const QString& jid);
|
void removeRoomRequest(const QString& account, const QString& jid);
|
||||||
void fileDownloadRequest(const QString& url);
|
|
||||||
void requestVCard(const QString& account, const QString& jid);
|
void requestVCard(const QString& account, const QString& jid);
|
||||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||||
void responsePassword(const QString& account, const QString& password);
|
|
||||||
void localPathInvalid(const QString& path);
|
|
||||||
void changeDownloadsPath(const QString& path);
|
void changeDownloadsPath(const QString& path);
|
||||||
|
|
||||||
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
||||||
|
void openedConversation();
|
||||||
|
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
||||||
|
|
||||||
|
void modifyAccountRequest(const QString&, const QMap<QString, QVariant>&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Models::Roster::ElId currentConversationId() const;
|
||||||
|
void closeCurrentConversation();
|
||||||
|
static QSystemTrayIcon *trayIcon;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void writeSettings();
|
void writeSettings();
|
||||||
void readSettings();
|
|
||||||
void newAccount(const QMap<QString, QVariant>& account);
|
|
||||||
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
|
||||||
void removeAccount(const QString& account);
|
|
||||||
void addGroup(const QString& account, const QString& name);
|
|
||||||
void removeGroup(const QString& account, const QString& name);
|
|
||||||
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
|
||||||
void removeContact(const QString& account, const QString& jid, const QString& group);
|
|
||||||
void removeContact(const QString& account, const QString& jid);
|
|
||||||
void changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data);
|
|
||||||
void addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
|
||||||
void removePresence(const QString& account, const QString& jid, const QString& name);
|
|
||||||
void stateChanged(Shared::Availability state);
|
void stateChanged(Shared::Availability state);
|
||||||
void accountMessage(const QString& account, const Shared::Message& data);
|
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
|
||||||
void addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
|
||||||
void changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data);
|
|
||||||
void removeRoom(const QString& account, const QString jid);
|
|
||||||
void addRoomParticipant(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 fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up);
|
|
||||||
void fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up);
|
|
||||||
void fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path);
|
|
||||||
void fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path);
|
|
||||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||||
void changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data);
|
void select(QModelIndex index);
|
||||||
void requestPassword(const QString& account);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
void createTrayIcon();
|
||||||
|
|
||||||
QScopedPointer<Ui::Squawk> m_ui;
|
QScopedPointer<Ui::Squawk> m_ui;
|
||||||
|
|
||||||
Accounts* accounts;
|
Accounts* accounts;
|
||||||
Settings* preferences;
|
Settings* preferences;
|
||||||
Models::Roster rosterModel;
|
About* about;
|
||||||
Conversations conversations;
|
Models::Roster& rosterModel;
|
||||||
QMenu* contextMenu;
|
QMenu* contextMenu;
|
||||||
QDBusInterface dbus;
|
|
||||||
std::map<QString, VCard*> vCards;
|
std::map<QString, VCard*> vCards;
|
||||||
std::deque<QString> requestedAccountsForPasswords;
|
|
||||||
QInputDialog* prompt;
|
|
||||||
Conversation* currentConversation;
|
Conversation* currentConversation;
|
||||||
QModelIndex restoreSelection;
|
QModelIndex restoreSelection;
|
||||||
bool needToRestore;
|
bool needToRestore;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent * event) override;
|
void closeEvent(QCloseEvent * event) override;
|
||||||
|
void expand(const QModelIndex& index);
|
||||||
protected slots:
|
|
||||||
void notify(const QString& account, const Shared::Message& msg);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccounts();
|
void onAccounts();
|
||||||
@ -147,29 +123,17 @@ private slots:
|
|||||||
void onAccountsSizeChanged(unsigned int size);
|
void onAccountsSizeChanged(unsigned int size);
|
||||||
void onAccountsClosed();
|
void onAccountsClosed();
|
||||||
void onPreferencesClosed();
|
void onPreferencesClosed();
|
||||||
void onConversationClosed(QObject* parent = 0);
|
|
||||||
void onVCardClosed();
|
void onVCardClosed();
|
||||||
void onVCardSave(const Shared::VCard& card, const QString& account);
|
void onVCardSave(const Shared::VCard& card, const QString& account);
|
||||||
void onActivateVCard(const QString& account, const QString& jid, bool edition = false);
|
void onActivateVCard(const QString& account, const QString& jid, bool edition = false);
|
||||||
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 onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg);
|
|
||||||
void onConversationResend(const QString& id);
|
|
||||||
void onRequestArchive(const QString& account, const QString& jid, const QString& before);
|
|
||||||
void onRosterContextMenu(const QPoint& point);
|
void onRosterContextMenu(const QPoint& point);
|
||||||
void onItemCollepsed(const QModelIndex& index);
|
void onItemCollepsed(const QModelIndex& index);
|
||||||
void onPasswordPromptAccepted();
|
|
||||||
void onPasswordPromptRejected();
|
|
||||||
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||||
void onContextAboutToHide();
|
void onContextAboutToHide();
|
||||||
|
void onAboutSquawkCalled();
|
||||||
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
void onAboutSquawkClosed();
|
||||||
|
|
||||||
private:
|
|
||||||
void checkNextAccountForPassword();
|
|
||||||
void onPasswordPromptDone();
|
|
||||||
void subscribeConversation(Conversation* conv);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SQUAWK_H
|
#endif // SQUAWK_H
|
||||||
|
15
ui/squawk.ui
15
ui/squawk.ui
@ -201,8 +201,15 @@
|
|||||||
<addaction name="actionAddConference"/>
|
<addaction name="actionAddConference"/>
|
||||||
<addaction name="actionQuit"/>
|
<addaction name="actionQuit"/>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QMenu" name="menuHelp">
|
||||||
|
<property name="title">
|
||||||
|
<string>Help</string>
|
||||||
|
</property>
|
||||||
|
<addaction name="actionAboutSquawk"/>
|
||||||
|
</widget>
|
||||||
<addaction name="menuFile"/>
|
<addaction name="menuFile"/>
|
||||||
<addaction name="menuSettings"/>
|
<addaction name="menuSettings"/>
|
||||||
|
<addaction name="menuHelp"/>
|
||||||
</widget>
|
</widget>
|
||||||
<action name="actionAccounts">
|
<action name="actionAccounts">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
@ -248,12 +255,18 @@
|
|||||||
</action>
|
</action>
|
||||||
<action name="actionPreferences">
|
<action name="actionPreferences">
|
||||||
<property name="icon">
|
<property name="icon">
|
||||||
<iconset theme="settings-configure"/>
|
<iconset theme="settings-configure">
|
||||||
|
<normaloff>.</normaloff>.</iconset>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Preferences</string>
|
<string>Preferences</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionAboutSquawk">
|
||||||
|
<property name="text">
|
||||||
|
<string>About Squawk</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../resources/resources.qrc"/>
|
<include location="../resources/resources.qrc"/>
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
target_sources(squawk PRIVATE
|
target_sources(squawk PRIVATE
|
||||||
account.cpp
|
|
||||||
account.h
|
|
||||||
account.ui
|
|
||||||
accounts.cpp
|
|
||||||
accounts.h
|
|
||||||
accounts.ui
|
|
||||||
chat.cpp
|
chat.cpp
|
||||||
chat.h
|
chat.h
|
||||||
conversation.cpp
|
conversation.cpp
|
||||||
@ -18,8 +12,12 @@ target_sources(squawk PRIVATE
|
|||||||
newcontact.ui
|
newcontact.ui
|
||||||
room.cpp
|
room.cpp
|
||||||
room.h
|
room.h
|
||||||
|
about.cpp
|
||||||
|
about.h
|
||||||
|
about.ui
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(vcard)
|
add_subdirectory(vcard)
|
||||||
add_subdirectory(messageline)
|
add_subdirectory(messageline)
|
||||||
add_subdirectory(settings)
|
add_subdirectory(settings)
|
||||||
|
add_subdirectory(accounts)
|
||||||
|
108
ui/widgets/about.cpp
Normal file
108
ui/widgets/about.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "about.h"
|
||||||
|
#include "ui_about.h"
|
||||||
|
#include <QXmppGlobal.h>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
static const std::string QXMPP_VERSION_PATCH(std::to_string(QXMPP_VERSION & 0xff));
|
||||||
|
static const std::string QXMPP_VERSION_MINOR(std::to_string((QXMPP_VERSION & 0xff00) >> 8));
|
||||||
|
static const std::string QXMPP_VERSION_MAJOR(std::to_string(QXMPP_VERSION >> 16));
|
||||||
|
static const QString QXMPP_VERSION_STRING = QString::fromStdString(QXMPP_VERSION_MAJOR + "." + QXMPP_VERSION_MINOR + "." + QXMPP_VERSION_PATCH);
|
||||||
|
|
||||||
|
About::About(QWidget* parent):
|
||||||
|
QWidget(parent),
|
||||||
|
m_ui(new Ui::About),
|
||||||
|
license(nullptr)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
m_ui->versionValue->setText(QApplication::applicationVersion());
|
||||||
|
m_ui->qtVersionValue->setText(qVersion());
|
||||||
|
m_ui->qtBuiltAgainstVersion->setText(tr("(built against %1)").arg(QT_VERSION_STR));
|
||||||
|
|
||||||
|
m_ui->qxmppVersionValue->setText(QXmppVersion());
|
||||||
|
m_ui->qxmppBuiltAgainstVersion->setText(tr("(built against %1)").arg(QXMPP_VERSION_STRING));
|
||||||
|
|
||||||
|
setWindowFlag(Qt::Tool);
|
||||||
|
|
||||||
|
connect(m_ui->licenceLink, &QLabel::linkActivated, this, &About::onLicenseActivated);
|
||||||
|
}
|
||||||
|
|
||||||
|
About::~About() {
|
||||||
|
if (license != nullptr) {
|
||||||
|
license->deleteLater();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void About::onLicenseActivated()
|
||||||
|
{
|
||||||
|
if (license == nullptr) {
|
||||||
|
QFile file;
|
||||||
|
bool found = false;
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
for (const QString& path : shares) {
|
||||||
|
file.setFileName(path + "/LICENSE.md");
|
||||||
|
|
||||||
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
qDebug() << "couldn't read license file, bailing";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
license = new QWidget();
|
||||||
|
license->setWindowTitle(tr("License"));
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(license);
|
||||||
|
QLabel* text = new QLabel(license);
|
||||||
|
QScrollArea* area = new QScrollArea(license);
|
||||||
|
text->setTextFormat(Qt::MarkdownText);
|
||||||
|
text->setWordWrap(true);
|
||||||
|
text->setOpenExternalLinks(true);
|
||||||
|
text->setMargin(5);
|
||||||
|
area->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||||
|
|
||||||
|
layout->addWidget(area);
|
||||||
|
license->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
connect(license, &QWidget::destroyed, this, &About::onLicenseClosed);
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
QString line;
|
||||||
|
QString licenseText("");
|
||||||
|
while (!in.atEnd()) {
|
||||||
|
line = in.readLine();
|
||||||
|
licenseText.append(line + "\n");
|
||||||
|
}
|
||||||
|
text->setText(licenseText);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
area->setWidget(text);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
license->raise();
|
||||||
|
license->activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
license->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void About::onLicenseClosed()
|
||||||
|
{
|
||||||
|
license = nullptr;
|
||||||
|
}
|
51
ui/widgets/about.h
Normal file
51
ui/widgets/about.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef ABOUT_H
|
||||||
|
#define ABOUT_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class About;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class About : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
About(QWidget* parent = nullptr);
|
||||||
|
~About();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void onLicenseActivated();
|
||||||
|
void onLicenseClosed();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::About> m_ui;
|
||||||
|
QWidget* license;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ABOUT_H
|
680
ui/widgets/about.ui
Normal file
680
ui/widgets/about.ui
Normal file
@ -0,0 +1,680 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>About</class>
|
||||||
|
<widget class="QWidget" name="About">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>375</width>
|
||||||
|
<height>290</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>About Squawk</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="2" column="0" colspan="4">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1" colspan="2">
|
||||||
|
<widget class="QLabel" name="header">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Squawk</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" rowspan="2">
|
||||||
|
<widget class="QLabel" name="squawkIcon">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>50</width>
|
||||||
|
<height>50</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="../../resources/resources.qrc">:/images/logo.svg</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3" rowspan="2">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0" colspan="4">
|
||||||
|
<widget class="QTabWidget" name="tabs">
|
||||||
|
<property name="currentIndex">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="usesScrollButtons">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="aboutTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>About</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="description">
|
||||||
|
<property name="text">
|
||||||
|
<string>XMPP (jabber) messenger</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="copyright">
|
||||||
|
<property name="text">
|
||||||
|
<string>(c) 2019 - 2022, Yury Gubich</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="siteLink">
|
||||||
|
<property name="text">
|
||||||
|
<string><a href="https://git.macaw.me/blue/squawk">Project site</a></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="licenceLink">
|
||||||
|
<property name="text">
|
||||||
|
<string><a href="https://git.macaw.me/blue/squawk/src/branch/master/LICENSE.md">License: GNU General Public License version 3</a></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QScrollArea" name="componentsTab">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Components</string>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>355</width>
|
||||||
|
<height>181</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_1">
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Version</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="qtVersionValue">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0.0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel" name="qtBuiltAgainstVersion">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">(built against 0.0.0)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Qt</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3" rowspan="2">
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true"><a href="https://www.qt.io/">www.qt.io</a></string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget_2" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Version</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="qxmppVersionValue">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0.0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel" name="qxmppBuiltAgainstVersion">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">(built against 0.0.0)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0" colspan="3">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">QXmpp</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3" rowspan="2">
|
||||||
|
<spacer name="horizontalSpacer_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="4">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true"><a href="https://github.com/qxmpp-project/qxmpp">github.com/qxmpp-project/qxmpp</a></string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<widget class="QWidget" name="reportTab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Report Bugs</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_6">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_8">
|
||||||
|
<property name="text">
|
||||||
|
<string>Please report any bug you find!
|
||||||
|
To report bugs you can use:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string><a href="https://git.macaw.me/blue/squawk/issues">Project bug tracker</></string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_10">
|
||||||
|
<property name="text">
|
||||||
|
<string>XMPP (<a href="xmpp:blue@macaw.me">blue@macaw.me</a>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_9">
|
||||||
|
<property name="text">
|
||||||
|
<string>E-Mail (<a href="mailto:blue@macaw.me">blue@macaw.me</a>)</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QScrollArea" name="thanksTab">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::NoFrame</enum>
|
||||||
|
</property>
|
||||||
|
<property name="horizontalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||||
|
</property>
|
||||||
|
<property name="widgetResizable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="title">
|
||||||
|
<string>Thanks To</string>
|
||||||
|
</attribute>
|
||||||
|
<widget class="QWidget" name="thanksTabContent">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>355</width>
|
||||||
|
<height>181</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget_3" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_4">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_11">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Vae</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_12">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Major refactoring, bug fixes, constructive criticism</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget_4" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_5">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_13">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Shunf4</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_14">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Major refactoring, bug fixes, build adaptations for Windows and MacOS</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="widget_5" native="true">
|
||||||
|
<layout class="QGridLayout" name="gridLayout_6">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Bruno F. Fontes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_16">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<italic>true</italic>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Brazilian Portuguese translation</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_7">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="versionLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Version</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QLabel" name="versionValue">
|
||||||
|
<property name="text">
|
||||||
|
<string>0.0.0</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../resources/resources.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
11
ui/widgets/accounts/CMakeLists.txt
Normal file
11
ui/widgets/accounts/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
account.cpp
|
||||||
|
account.h
|
||||||
|
account.ui
|
||||||
|
accounts.cpp
|
||||||
|
accounts.h
|
||||||
|
accounts.ui
|
||||||
|
credentialsprompt.cpp
|
||||||
|
credentialsprompt.h
|
||||||
|
credentialsprompt.ui
|
||||||
|
)
|
@ -53,6 +53,7 @@ QMap<QString, QVariant> Account::value() const
|
|||||||
map["name"] = m_ui->name->text();
|
map["name"] = m_ui->name->text();
|
||||||
map["resource"] = m_ui->resource->text();
|
map["resource"] = m_ui->resource->text();
|
||||||
map["passwordType"] = m_ui->passwordType->currentIndex();
|
map["passwordType"] = m_ui->passwordType->currentIndex();
|
||||||
|
map["active"] = m_ui->active->isChecked();
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
@ -7,7 +7,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>438</width>
|
<width>438</width>
|
||||||
<height>342</height>
|
<height>345</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -34,7 +34,7 @@
|
|||||||
<property name="verticalSpacing">
|
<property name="verticalSpacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="1" column="1">
|
<item row="2" column="1">
|
||||||
<widget class="QLineEdit" name="login">
|
<widget class="QLineEdit" name="login">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Your account login</string>
|
<string>Your account login</string>
|
||||||
@ -44,14 +44,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Server</string>
|
<string>Server</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<widget class="QLineEdit" name="server">
|
<widget class="QLineEdit" name="server">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>A server address of your account. Like 404.city or macaw.me</string>
|
<string>A server address of your account. Like 404.city or macaw.me</string>
|
||||||
@ -61,21 +61,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Login</string>
|
<string>Login</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Password</string>
|
<string>Password</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="password">
|
<widget class="QLineEdit" name="password">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Password of your account</string>
|
<string>Password of your account</string>
|
||||||
@ -97,14 +97,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_4">
|
<widget class="QLabel" name="label_4">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Name</string>
|
<string>Name</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLineEdit" name="name">
|
<widget class="QLineEdit" name="name">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Just a name how would you call this account, doesn't affect anything</string>
|
<string>Just a name how would you call this account, doesn't affect anything</string>
|
||||||
@ -114,14 +114,14 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="7" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Resource</string>
|
<string>Resource</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="7" column="1">
|
||||||
<widget class="QLineEdit" name="resource">
|
<widget class="QLineEdit" name="resource">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>A resource name like "Home" or "Work"</string>
|
<string>A resource name like "Home" or "Work"</string>
|
||||||
@ -131,17 +131,17 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QLabel" name="label_6">
|
<widget class="QLabel" name="label_6">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Password storage</string>
|
<string>Password storage</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="5" column="1">
|
||||||
<widget class="QComboBox" name="passwordType"/>
|
<widget class="QComboBox" name="passwordType"/>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="6" column="1">
|
||||||
<widget class="QLabel" name="comment">
|
<widget class="QLabel" name="comment">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
@ -157,6 +157,23 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Active</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="active">
|
||||||
|
<property name="text">
|
||||||
|
<string>enable</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
@ -83,7 +83,8 @@ void Accounts::onEditButton()
|
|||||||
{"server", mAcc->getServer()},
|
{"server", mAcc->getServer()},
|
||||||
{"name", mAcc->getName()},
|
{"name", mAcc->getName()},
|
||||||
{"resource", mAcc->getResource()},
|
{"resource", mAcc->getResource()},
|
||||||
{"passwordType", QVariant::fromValue(mAcc->getPasswordType())}
|
{"passwordType", QVariant::fromValue(mAcc->getPasswordType())},
|
||||||
|
{"active", mAcc->getActive()}
|
||||||
});
|
});
|
||||||
acc->lockId();
|
acc->lockId();
|
||||||
connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted);
|
connect(acc, &Account::accepted, this, &Accounts::onAccountAccepted);
|
||||||
@ -118,17 +119,17 @@ void Accounts::updateConnectButton()
|
|||||||
bool allConnected = true;
|
bool allConnected = true;
|
||||||
for (int i = 0; i < selectionSize && allConnected; ++i) {
|
for (int i = 0; i < selectionSize && allConnected; ++i) {
|
||||||
const Models::Account* mAcc = model->getAccount(sm->selectedRows().at(i).row());
|
const Models::Account* mAcc = model->getAccount(sm->selectedRows().at(i).row());
|
||||||
allConnected = mAcc->getState() == Shared::ConnectionState::connected;
|
allConnected = allConnected && mAcc->getActive();
|
||||||
}
|
}
|
||||||
if (allConnected) {
|
if (allConnected) {
|
||||||
toDisconnect = true;
|
toDisconnect = true;
|
||||||
m_ui->connectButton->setText(tr("Disconnect"));
|
m_ui->connectButton->setText(tr("Deactivate"));
|
||||||
} else {
|
} else {
|
||||||
toDisconnect = false;
|
toDisconnect = false;
|
||||||
m_ui->connectButton->setText(tr("Connect"));
|
m_ui->connectButton->setText(tr("Activate"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_ui->connectButton->setText(tr("Connect"));
|
m_ui->connectButton->setText(tr("Activate"));
|
||||||
toDisconnect = false;
|
toDisconnect = false;
|
||||||
m_ui->connectButton->setEnabled(false);
|
m_ui->connectButton->setEnabled(false);
|
||||||
}
|
}
|
@ -24,7 +24,7 @@
|
|||||||
#include <QItemSelection>
|
#include <QItemSelection>
|
||||||
|
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "../models/accounts.h"
|
#include "ui/models/accounts.h"
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
60
ui/widgets/accounts/credentialsprompt.cpp
Normal file
60
ui/widgets/accounts/credentialsprompt.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "credentialsprompt.h"
|
||||||
|
#include "ui_credentialsprompt.h"
|
||||||
|
|
||||||
|
CredentialsPrompt::CredentialsPrompt(QWidget* parent):
|
||||||
|
QDialog(parent),
|
||||||
|
m_ui(new Ui::CredentialsPrompt),
|
||||||
|
title(),
|
||||||
|
message()
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
title = windowTitle();
|
||||||
|
message = m_ui->message->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialsPrompt::~CredentialsPrompt()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CredentialsPrompt::setAccount(const QString& account)
|
||||||
|
{
|
||||||
|
m_ui->message->setText(message.arg(account));
|
||||||
|
setWindowTitle(title.arg(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CredentialsPrompt::getLogin() const
|
||||||
|
{
|
||||||
|
return m_ui->login->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString CredentialsPrompt::getPassword() const
|
||||||
|
{
|
||||||
|
return m_ui->password->text();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CredentialsPrompt::setLogin(const QString& login)
|
||||||
|
{
|
||||||
|
m_ui->login->setText(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CredentialsPrompt::setPassword(const QString& password)
|
||||||
|
{
|
||||||
|
m_ui->password->setText(password);
|
||||||
|
}
|
52
ui/widgets/accounts/credentialsprompt.h
Normal file
52
ui/widgets/accounts/credentialsprompt.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef CREDENTIALSPROMPT_H
|
||||||
|
#define CREDENTIALSPROMPT_H
|
||||||
|
|
||||||
|
#include <qdialog.h>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class CredentialsPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class CredentialsPrompt : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CredentialsPrompt(QWidget* parent = nullptr);
|
||||||
|
~CredentialsPrompt();
|
||||||
|
|
||||||
|
void setAccount(const QString& account);
|
||||||
|
void setLogin(const QString& login);
|
||||||
|
void setPassword(const QString& password);
|
||||||
|
|
||||||
|
QString getLogin() const;
|
||||||
|
QString getPassword() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<Ui::CredentialsPrompt> m_ui;
|
||||||
|
QString title;
|
||||||
|
QString message;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CREDENTIALSPROMPT_H
|
144
ui/widgets/accounts/credentialsprompt.ui
Normal file
144
ui/widgets/accounts/credentialsprompt.ui
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>CredentialsPrompt</class>
|
||||||
|
<widget class="QDialog" name="CredentialsPrompt">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>318</width>
|
||||||
|
<height>229</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Authentication error: %1</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="message">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Couldn't authenticate account %1: login or password is icorrect.
|
||||||
|
Would you like to check them and try again?</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="loginLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Login</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLineEdit" name="login">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Your account login (without @server.domain)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="passwordLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="password">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Your password</string>
|
||||||
|
</property>
|
||||||
|
<property name="inputMethodHints">
|
||||||
|
<set>Qt::ImhHiddenText|Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhSensitiveData</set>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="echoMode">
|
||||||
|
<enum>QLineEdit::Password</enum>
|
||||||
|
</property>
|
||||||
|
<property name="clearButtonEnabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>CredentialsPrompt</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>CredentialsPrompt</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>20</x>
|
||||||
|
<y>20</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -26,7 +26,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QCoreApplication>
|
#include <QApplication>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@ -498,6 +498,26 @@ void Conversation::onFeedContext(const QPoint& pos)
|
|||||||
emit resendMessage(id);
|
emit resendMessage(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString selected = feed->getSelectedText();
|
||||||
|
if (selected.size() > 0) {
|
||||||
|
showMenu = true;
|
||||||
|
QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy selected"));
|
||||||
|
connect(copy, &QAction::triggered, [selected] () {
|
||||||
|
QClipboard* cb = QApplication::clipboard();
|
||||||
|
cb->setText(selected);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QString body = item->getBody();
|
||||||
|
if (body.size() > 0) {
|
||||||
|
showMenu = true;
|
||||||
|
QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy message"));
|
||||||
|
connect(copy, &QAction::triggered, [body] () {
|
||||||
|
QClipboard* cb = QApplication::clipboard();
|
||||||
|
cb->setText(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QString path = Shared::resolvePath(item->getAttachPath());
|
QString path = Shared::resolvePath(item->getAttachPath());
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0) {
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#include <QPaintEvent>
|
#include <QPaintEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include "messagedelegate.h"
|
#include "messagedelegate.h"
|
||||||
@ -50,7 +52,13 @@ FeedView::FeedView(QWidget* parent):
|
|||||||
modelState(Models::MessageFeed::complete),
|
modelState(Models::MessageFeed::complete),
|
||||||
progress(),
|
progress(),
|
||||||
dividerFont(),
|
dividerFont(),
|
||||||
dividerMetrics(dividerFont)
|
dividerMetrics(dividerFont),
|
||||||
|
mousePressed(false),
|
||||||
|
dragging(false),
|
||||||
|
hovered(Shared::Hover::nothing),
|
||||||
|
dragStartPoint(),
|
||||||
|
dragEndPoint(),
|
||||||
|
selectedText()
|
||||||
{
|
{
|
||||||
horizontalScrollBar()->setRange(0, 0);
|
horizontalScrollBar()->setRange(0, 0);
|
||||||
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
verticalScrollBar()->setSingleStep(approximateSingleMessageHeight);
|
||||||
@ -162,7 +170,7 @@ void FeedView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottom
|
|||||||
|
|
||||||
void FeedView::updateGeometries()
|
void FeedView::updateGeometries()
|
||||||
{
|
{
|
||||||
qDebug() << "updateGeometries";
|
//qDebug() << "updateGeometries";
|
||||||
QScrollBar* bar = verticalScrollBar();
|
QScrollBar* bar = verticalScrollBar();
|
||||||
|
|
||||||
const QStyle* st = style();
|
const QStyle* st = style();
|
||||||
@ -343,6 +351,7 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
|
|
||||||
QDateTime lastDate;
|
QDateTime lastDate;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
QRect viewportRect = vp->rect();
|
||||||
for (const QModelIndex& index : toRener) {
|
for (const QModelIndex& index : toRener) {
|
||||||
QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime();
|
QDateTime currentDate = index.data(Models::MessageFeed::Date).toDateTime();
|
||||||
option.rect = visualRect(index);
|
option.rect = visualRect(index);
|
||||||
@ -356,7 +365,10 @@ void FeedView::paintEvent(QPaintEvent* event)
|
|||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
bool mouseOver = option.rect.contains(cursor) && vp->rect().contains(cursor);
|
QRect stripe = option.rect;
|
||||||
|
stripe.setLeft(0);
|
||||||
|
stripe.setWidth(viewportRect.width());
|
||||||
|
bool mouseOver = stripe.contains(cursor) && viewportRect.contains(cursor);
|
||||||
option.state.setFlag(QStyle::State_MouseOver, mouseOver);
|
option.state.setFlag(QStyle::State_MouseOver, mouseOver);
|
||||||
itemDelegate(index)->paint(&painter, option, index);
|
itemDelegate(index)->paint(&painter, option, index);
|
||||||
|
|
||||||
@ -403,13 +415,151 @@ void FeedView::verticalScrollbarValueChanged(int value)
|
|||||||
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
QAbstractItemView::verticalScrollbarValueChanged(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FeedView::setAnchorHovered(Shared::Hover type)
|
||||||
|
{
|
||||||
|
if (hovered != type) {
|
||||||
|
hovered = type;
|
||||||
|
switch (hovered) {
|
||||||
|
case Shared::Hover::nothing:
|
||||||
|
setCursor(Qt::ArrowCursor);
|
||||||
|
break;
|
||||||
|
case Shared::Hover::text:
|
||||||
|
setCursor(Qt::IBeamCursor);
|
||||||
|
break;
|
||||||
|
case Shared::Hover::anchor:
|
||||||
|
setCursor(Qt::PointingHandCursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
void FeedView::mouseMoveEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
if (!isVisible()) {
|
if (!isVisible()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dragEndPoint = event->localPos().toPoint();
|
||||||
|
if (mousePressed) {
|
||||||
|
QPoint distance = dragStartPoint - dragEndPoint;
|
||||||
|
if (distance.manhattanLength() > 5) {
|
||||||
|
dragging = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QAbstractItemView::mouseMoveEvent(event);
|
QAbstractItemView::mouseMoveEvent(event);
|
||||||
|
|
||||||
|
if (specialDelegate) {
|
||||||
|
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
||||||
|
if (dragging) {
|
||||||
|
QModelIndex index = indexAt(dragStartPoint);
|
||||||
|
if (index.isValid()) {
|
||||||
|
QRect rect = visualRect(index);
|
||||||
|
if (rect.contains(dragStartPoint)) {
|
||||||
|
QString selected = del->mouseDrag(dragStartPoint, dragEndPoint, index, rect);
|
||||||
|
if (selectedText != selected) {
|
||||||
|
selectedText = selected;
|
||||||
|
setDirtyRegion(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QModelIndex index = indexAt(dragEndPoint);
|
||||||
|
if (index.isValid()) {
|
||||||
|
QRect rect = visualRect(index);
|
||||||
|
if (rect.contains(dragEndPoint)) {
|
||||||
|
setAnchorHovered(del->hoverType(dragEndPoint, index, rect));
|
||||||
|
} else {
|
||||||
|
setAnchorHovered(Shared::Hover::nothing);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAnchorHovered(Shared::Hover::nothing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedView::mousePressEvent(QMouseEvent* event)
|
||||||
|
{
|
||||||
|
QAbstractItemView::mousePressEvent(event);
|
||||||
|
|
||||||
|
mousePressed = event->button() == Qt::LeftButton;
|
||||||
|
if (mousePressed) {
|
||||||
|
dragStartPoint = event->localPos().toPoint();
|
||||||
|
if (specialDelegate && specialModel) {
|
||||||
|
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
||||||
|
QString lastSelectedId = del->clearSelection();
|
||||||
|
if (lastSelectedId.size()) {
|
||||||
|
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
||||||
|
QModelIndex index = feed->modelIndexById(lastSelectedId);
|
||||||
|
if (index.isValid()) {
|
||||||
|
setDirtyRegion(visualRect(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedView::mouseDoubleClickEvent(QMouseEvent* event)
|
||||||
|
{
|
||||||
|
QAbstractItemView::mouseDoubleClickEvent(event);
|
||||||
|
mousePressed = event->button() == Qt::LeftButton;
|
||||||
|
if (mousePressed) {
|
||||||
|
dragStartPoint = event->localPos().toPoint();
|
||||||
|
if (specialDelegate && specialModel) {
|
||||||
|
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
||||||
|
QString lastSelectedId = del->clearSelection();
|
||||||
|
selectedText = "";
|
||||||
|
if (lastSelectedId.size()) {
|
||||||
|
Models::MessageFeed* feed = static_cast<Models::MessageFeed*>(model());
|
||||||
|
QModelIndex index = feed->modelIndexById(lastSelectedId);
|
||||||
|
if (index.isValid()) {
|
||||||
|
setDirtyRegion(visualRect(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex index = indexAt(dragStartPoint);
|
||||||
|
QRect rect = visualRect(index);
|
||||||
|
if (rect.contains(dragStartPoint)) {
|
||||||
|
selectedText = del->leftDoubleClick(dragStartPoint, index, rect);
|
||||||
|
if (selectedText.size() > 0) {
|
||||||
|
setDirtyRegion(rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedView::mouseReleaseEvent(QMouseEvent* event)
|
||||||
|
{
|
||||||
|
QAbstractItemView::mouseReleaseEvent(event);
|
||||||
|
|
||||||
|
if (mousePressed) {
|
||||||
|
if (!dragging && specialDelegate) {
|
||||||
|
QPoint point = event->localPos().toPoint();
|
||||||
|
QModelIndex index = indexAt(point);
|
||||||
|
if (index.isValid()) {
|
||||||
|
QRect rect = visualRect(index);
|
||||||
|
MessageDelegate* del = static_cast<MessageDelegate*>(itemDelegate());
|
||||||
|
if (rect.contains(point)) {
|
||||||
|
del->leftClick(point, index, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dragging = false;
|
||||||
|
mousePressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FeedView::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
QKeyEvent *key_event = static_cast<QKeyEvent*>(event);
|
||||||
|
if (key_event->matches(QKeySequence::Copy)) {
|
||||||
|
if (selectedText.size() > 0) {
|
||||||
|
QClipboard* cb = QApplication::clipboard();
|
||||||
|
cb->setText(selectedText);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FeedView::resizeEvent(QResizeEvent* event)
|
void FeedView::resizeEvent(QResizeEvent* event)
|
||||||
@ -456,6 +606,7 @@ void FeedView::setItemDelegate(QAbstractItemDelegate* delegate)
|
|||||||
elementMargin = MessageDelegate::margin;
|
elementMargin = MessageDelegate::margin;
|
||||||
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
connect(del, &MessageDelegate::buttonPushed, this, &FeedView::onMessageButtonPushed);
|
||||||
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
connect(del, &MessageDelegate::invalidPath, this, &FeedView::onMessageInvalidPath);
|
||||||
|
connect(del, &MessageDelegate::openLink, &QDesktopServices::openUrl);
|
||||||
} else {
|
} else {
|
||||||
specialDelegate = false;
|
specialDelegate = false;
|
||||||
elementMargin = 0;
|
elementMargin = 0;
|
||||||
@ -520,3 +671,8 @@ void FeedView::onModelSyncStateChange(Models::MessageFeed::SyncState state)
|
|||||||
scheduleDelayedItemsLayout();
|
scheduleDelayedItemsLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FeedView::getSelectedText() const
|
||||||
|
{
|
||||||
|
return selectedText;
|
||||||
|
}
|
||||||
|
@ -20,12 +20,15 @@
|
|||||||
#define FEEDVIEW_H
|
#define FEEDVIEW_H
|
||||||
|
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include <ui/widgets/messageline/messagefeed.h>
|
#include <ui/widgets/messageline/messagefeed.h>
|
||||||
#include <ui/utils/progress.h>
|
#include <ui/utils/progress.h>
|
||||||
|
#include <shared/utils.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo write docs
|
* @todo write docs
|
||||||
@ -48,6 +51,7 @@ public:
|
|||||||
void setModel(QAbstractItemModel * model) override;
|
void setModel(QAbstractItemModel * model) override;
|
||||||
|
|
||||||
QFont getFont() const;
|
QFont getFont() const;
|
||||||
|
QString getSelectedText() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resized();
|
void resized();
|
||||||
@ -68,12 +72,17 @@ protected:
|
|||||||
void paintEvent(QPaintEvent * event) override;
|
void paintEvent(QPaintEvent * event) override;
|
||||||
void updateGeometries() override;
|
void updateGeometries() override;
|
||||||
void mouseMoveEvent(QMouseEvent * event) override;
|
void mouseMoveEvent(QMouseEvent * event) override;
|
||||||
|
void mousePressEvent(QMouseEvent * event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent * event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent * event) override;
|
||||||
|
void keyPressEvent(QKeyEvent * event) override;
|
||||||
void resizeEvent(QResizeEvent * event) override;
|
void resizeEvent(QResizeEvent * event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
bool tryToCalculateGeometriesWithNoScrollbars(const QStyleOptionViewItem& option, const QAbstractItemModel* model, uint32_t totalHeight);
|
||||||
void positionProgress();
|
void positionProgress();
|
||||||
void drawDateDevider(int top, const QDateTime& date, QPainter& painter);
|
void drawDateDevider(int top, const QDateTime& date, QPainter& painter);
|
||||||
|
void setAnchorHovered(Shared::Hover type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Hint {
|
struct Hint {
|
||||||
@ -93,6 +102,12 @@ private:
|
|||||||
Progress progress;
|
Progress progress;
|
||||||
QFont dividerFont;
|
QFont dividerFont;
|
||||||
QFontMetrics dividerMetrics;
|
QFontMetrics dividerMetrics;
|
||||||
|
bool mousePressed;
|
||||||
|
bool dragging;
|
||||||
|
Shared::Hover hovered;
|
||||||
|
QPoint dragStartPoint;
|
||||||
|
QPoint dragEndPoint;
|
||||||
|
QString selectedText;
|
||||||
|
|
||||||
static const std::set<int> geometryChangingRoles;
|
static const std::set<int> geometryChangingRoles;
|
||||||
|
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QAbstractItemView>
|
#include <QAbstractItemView>
|
||||||
|
#include <QAbstractTextDocumentLayout>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "messagedelegate.h"
|
#include "messagedelegate.h"
|
||||||
#include "messagefeed.h"
|
#include "messagefeed.h"
|
||||||
@ -41,7 +44,7 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
|||||||
bodyFont(),
|
bodyFont(),
|
||||||
nickFont(),
|
nickFont(),
|
||||||
dateFont(),
|
dateFont(),
|
||||||
bodyMetrics(bodyFont),
|
bodyRenderer(new QTextDocument()),
|
||||||
nickMetrics(nickFont),
|
nickMetrics(nickFont),
|
||||||
dateMetrics(dateFont),
|
dateMetrics(dateFont),
|
||||||
buttonHeight(0),
|
buttonHeight(0),
|
||||||
@ -51,11 +54,14 @@ MessageDelegate::MessageDelegate(QObject* parent):
|
|||||||
bars(new std::map<QString, QProgressBar*>()),
|
bars(new std::map<QString, QProgressBar*>()),
|
||||||
statusIcons(new std::map<QString, QLabel*>()),
|
statusIcons(new std::map<QString, QLabel*>()),
|
||||||
pencilIcons(new std::map<QString, QLabel*>()),
|
pencilIcons(new std::map<QString, QLabel*>()),
|
||||||
bodies(new std::map<QString, QLabel*>()),
|
|
||||||
previews(new std::map<QString, Preview*>()),
|
previews(new std::map<QString, Preview*>()),
|
||||||
idsToKeep(new std::set<QString>()),
|
idsToKeep(new std::set<QString>()),
|
||||||
clearingWidgets(false)
|
clearingWidgets(false),
|
||||||
|
currentId(""),
|
||||||
|
selection(0, 0)
|
||||||
{
|
{
|
||||||
|
bodyRenderer->setDocumentMargin(0);
|
||||||
|
|
||||||
QPushButton btn(QCoreApplication::translate("MessageLine", "Download"));
|
QPushButton btn(QCoreApplication::translate("MessageLine", "Download"));
|
||||||
buttonHeight = btn.sizeHint().height();
|
buttonHeight = btn.sizeHint().height();
|
||||||
buttonWidth = btn.sizeHint().width();
|
buttonWidth = btn.sizeHint().width();
|
||||||
@ -82,10 +88,6 @@ MessageDelegate::~MessageDelegate()
|
|||||||
delete pair.second;
|
delete pair.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::pair<const QString, QLabel*>& pair: *bodies){
|
|
||||||
delete pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::pair<const QString, Preview*>& pair: *previews){
|
for (const std::pair<const QString, Preview*>& pair: *previews){
|
||||||
delete pair.second;
|
delete pair.second;
|
||||||
}
|
}
|
||||||
@ -95,8 +97,8 @@ MessageDelegate::~MessageDelegate()
|
|||||||
delete idsToKeep;
|
delete idsToKeep;
|
||||||
delete buttons;
|
delete buttons;
|
||||||
delete bars;
|
delete bars;
|
||||||
delete bodies;
|
|
||||||
delete previews;
|
delete previews;
|
||||||
|
delete bodyRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||||
@ -123,11 +125,6 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
|||||||
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
opt.displayAlignment = Qt::AlignRight | Qt::AlignTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize bodySize(0, 0);
|
|
||||||
if (data.text.size() > 0) {
|
|
||||||
bodySize = bodyMetrics.boundingRect(opt.rect, Qt::TextWordWrap, data.text).size();
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect rect;
|
QRect rect;
|
||||||
if (ntds) {
|
if (ntds) {
|
||||||
painter->setFont(nickFont);
|
painter->setFont(nickFont);
|
||||||
@ -170,15 +167,7 @@ void MessageDelegate::paint(QPainter* painter, const QStyleOptionViewItem& optio
|
|||||||
painter->restore();
|
painter->restore();
|
||||||
|
|
||||||
QWidget* vp = static_cast<QWidget*>(painter->device());
|
QWidget* vp = static_cast<QWidget*>(painter->device());
|
||||||
if (data.text.size() > 0) {
|
paintBody(data, painter, opt);
|
||||||
QLabel* body = getBody(data);
|
|
||||||
body->setParent(vp);
|
|
||||||
body->setMinimumSize(bodySize);
|
|
||||||
body->setMaximumSize(bodySize);
|
|
||||||
body->move(opt.rect.left(), opt.rect.y());
|
|
||||||
body->show();
|
|
||||||
opt.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
|
||||||
}
|
|
||||||
painter->setFont(dateFont);
|
painter->setFont(dateFont);
|
||||||
QColor q = painter->pen().color();
|
QColor q = painter->pen().color();
|
||||||
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
QString dateString = data.date.toLocalTime().toString("hh:mm");
|
||||||
@ -306,7 +295,12 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
|||||||
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
QSize messageSize(0, 0);
|
QSize messageSize(0, 0);
|
||||||
if (data.text.size() > 0) {
|
if (data.text.size() > 0) {
|
||||||
messageSize = bodyMetrics.boundingRect(messageRect, Qt::TextWordWrap, data.text).size();
|
bodyRenderer->setPlainText(data.text);
|
||||||
|
bodyRenderer->setTextWidth(messageRect.size().width());
|
||||||
|
|
||||||
|
QSizeF size = bodyRenderer->size();
|
||||||
|
size.setWidth(bodyRenderer->idealWidth());
|
||||||
|
messageSize = QSize(std::ceil(size.width()), std::ceil(size.height()));
|
||||||
messageSize.rheight() += textMargin;
|
messageSize.rheight() += textMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,6 +361,183 @@ QSize MessageDelegate::sizeHint(const QStyleOptionViewItem& option, const QModel
|
|||||||
return messageSize;
|
return messageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect MessageDelegate::getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QRect localHint = sizeHint.adjusted(bubbleMargin, bubbleMargin + margin, -bubbleMargin, -bubbleMargin / 2);
|
||||||
|
if (needToDrawSender(index, data)) {
|
||||||
|
localHint.adjust(0, nickMetrics.lineSpacing() + textMargin, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int attachHeight = 0;
|
||||||
|
switch (data.attach.state) {
|
||||||
|
case Models::none:
|
||||||
|
break;
|
||||||
|
case Models::uploading:
|
||||||
|
attachHeight += Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint).height() + textMargin;
|
||||||
|
[[fallthrough]];
|
||||||
|
case Models::downloading:
|
||||||
|
attachHeight += barHeight + textMargin;
|
||||||
|
break;
|
||||||
|
case Models::remote:
|
||||||
|
attachHeight += buttonHeight + textMargin;
|
||||||
|
break;
|
||||||
|
case Models::ready:
|
||||||
|
case Models::local: {
|
||||||
|
QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint);
|
||||||
|
attachHeight += aSize.height() + textMargin;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Models::errorDownload: {
|
||||||
|
QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size();
|
||||||
|
attachHeight += commentSize.height() + buttonHeight + textMargin * 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Models::errorUpload: {
|
||||||
|
QSize aSize = Preview::calculateAttachSize(Shared::resolvePath(data.attach.localPath), localHint);
|
||||||
|
QSize commentSize = dateMetrics.boundingRect(localHint, Qt::TextWordWrap, data.attach.error).size();
|
||||||
|
attachHeight += aSize.height() + commentSize.height() + textMargin * 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bottomSize = std::max(dateMetrics.lineSpacing(), statusIconSize);
|
||||||
|
localHint.adjust(0, attachHeight, 0, -(bottomSize + textMargin));
|
||||||
|
|
||||||
|
return localHint;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageDelegate::getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
|
||||||
|
|
||||||
|
if (localHint.contains(point)) {
|
||||||
|
QPoint translated = point - localHint.topLeft();
|
||||||
|
|
||||||
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
|
bodyRenderer->setTextWidth(localHint.size().width());
|
||||||
|
|
||||||
|
return bodyRenderer->documentLayout()->anchorAt(translated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QString anchor = getAnchor(point, index, sizeHint);
|
||||||
|
if (anchor.size() > 0) {
|
||||||
|
emit openLink(anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageDelegate::leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint)
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
|
||||||
|
|
||||||
|
if (localHint.contains(point)) {
|
||||||
|
QPoint translated = point - localHint.topLeft();
|
||||||
|
|
||||||
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
|
bodyRenderer->setTextWidth(localHint.size().width());
|
||||||
|
|
||||||
|
QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout();
|
||||||
|
|
||||||
|
int position = lay->hitTest(translated, Qt::HitTestAccuracy::FuzzyHit);
|
||||||
|
QTextCursor cursor(bodyRenderer);
|
||||||
|
cursor.setPosition(position, QTextCursor::MoveAnchor);
|
||||||
|
cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
||||||
|
cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
|
selection.first = cursor.anchor();
|
||||||
|
selection.second = cursor.position();
|
||||||
|
currentId = data.id;
|
||||||
|
|
||||||
|
if (selection.first != selection.second) {
|
||||||
|
return cursor.selectedText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Hover MessageDelegate::hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
|
||||||
|
|
||||||
|
if (localHint.contains(point)) {
|
||||||
|
QPoint translated = point - localHint.topLeft();
|
||||||
|
|
||||||
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
|
bodyRenderer->setTextWidth(localHint.size().width());
|
||||||
|
|
||||||
|
QAbstractTextDocumentLayout* lay = bodyRenderer->documentLayout();
|
||||||
|
QString anchor = lay->anchorAt(translated);
|
||||||
|
|
||||||
|
if (anchor.size() > 0) {
|
||||||
|
return Shared::Hover::anchor;
|
||||||
|
} else {
|
||||||
|
int position = lay->hitTest(translated, Qt::HitTestAccuracy::ExactHit);
|
||||||
|
if (position != -1) {
|
||||||
|
return Shared::Hover::text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Shared::Hover::nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageDelegate::mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint)
|
||||||
|
{
|
||||||
|
QVariant vi = index.data(Models::MessageFeed::Bulk);
|
||||||
|
Models::FeedItem data = qvariant_cast<Models::FeedItem>(vi);
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
QRect localHint = getHoveredMessageBodyRect(index, data, sizeHint);
|
||||||
|
|
||||||
|
if (localHint.contains(start)) {
|
||||||
|
QPoint tl = localHint.topLeft();
|
||||||
|
QPoint first = start - tl;
|
||||||
|
QPoint last = end - tl;
|
||||||
|
last.setX(std::max(last.x(), 0));
|
||||||
|
last.setX(std::min(last.x(), localHint.width() - 1));
|
||||||
|
last.setY(std::max(last.y(), 0));
|
||||||
|
last.setY(std::min(last.y(), localHint.height()));
|
||||||
|
|
||||||
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
|
bodyRenderer->setTextWidth(localHint.size().width());
|
||||||
|
selection.first = bodyRenderer->documentLayout()->hitTest(first, Qt::HitTestAccuracy::FuzzyHit);
|
||||||
|
selection.second = bodyRenderer->documentLayout()->hitTest(last, Qt::HitTestAccuracy::FuzzyHit);
|
||||||
|
|
||||||
|
currentId = data.id;
|
||||||
|
|
||||||
|
if (selection.first != selection.second) {
|
||||||
|
QTextCursor cursor(bodyRenderer);
|
||||||
|
cursor.setPosition(selection.first, QTextCursor::MoveAnchor);
|
||||||
|
cursor.setPosition(selection.second, QTextCursor::KeepAnchor);
|
||||||
|
return cursor.selectedText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MessageDelegate::clearSelection()
|
||||||
|
{
|
||||||
|
QString lastSelectedId = currentId;
|
||||||
|
currentId = "";
|
||||||
|
selection = std::pair(0, 0);
|
||||||
|
return lastSelectedId;
|
||||||
|
}
|
||||||
|
|
||||||
void MessageDelegate::initializeFonts(const QFont& font)
|
void MessageDelegate::initializeFonts(const QFont& font)
|
||||||
{
|
{
|
||||||
bodyFont = font;
|
bodyFont = font;
|
||||||
@ -390,9 +561,11 @@ void MessageDelegate::initializeFonts(const QFont& font)
|
|||||||
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
|
dateFont.setPointSize(dateFont.pointSize() * dateFontMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyMetrics = QFontMetrics(bodyFont);
|
bodyFont.setKerning(false);
|
||||||
nickMetrics = QFontMetrics(nickFont);
|
nickMetrics = QFontMetrics(nickFont);
|
||||||
dateMetrics = QFontMetrics(dateFont);
|
dateMetrics = QFontMetrics(dateFont);
|
||||||
|
|
||||||
|
bodyRenderer->setDefaultFont(bodyFont);
|
||||||
|
|
||||||
Preview::initializeFont(bodyFont);
|
Preview::initializeFont(bodyFont);
|
||||||
}
|
}
|
||||||
@ -572,34 +745,6 @@ QLabel * MessageDelegate::getPencilIcon(const Models::FeedItem& data) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLabel * MessageDelegate::getBody(const Models::FeedItem& data) const
|
|
||||||
{
|
|
||||||
std::map<QString, QLabel*>::const_iterator itr = bodies->find(data.id);
|
|
||||||
QLabel* result = 0;
|
|
||||||
|
|
||||||
if (itr != bodies->end()) {
|
|
||||||
result = itr->second;
|
|
||||||
} else {
|
|
||||||
result = new QLabel();
|
|
||||||
result->setFont(bodyFont);
|
|
||||||
result->setContextMenuPolicy(Qt::NoContextMenu);
|
|
||||||
result->setWordWrap(true);
|
|
||||||
result->setOpenExternalLinks(true);
|
|
||||||
result->setTextInteractionFlags(result->textInteractionFlags() | Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse);
|
|
||||||
bodies->insert(std::make_pair(data.id, result));
|
|
||||||
}
|
|
||||||
|
|
||||||
result->setText(Shared::processMessageBody(data.text));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MessageDelegate::beginClearWidgets()
|
|
||||||
{
|
|
||||||
idsToKeep->clear();
|
|
||||||
clearingWidgets = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void removeElements(std::map<QString, T*>* elements, std::set<QString>* idsToKeep) {
|
void removeElements(std::map<QString, T*>* elements, std::set<QString>* idsToKeep) {
|
||||||
std::set<QString> toRemove;
|
std::set<QString> toRemove;
|
||||||
@ -614,6 +759,41 @@ void removeElements(std::map<QString, T*>* elements, std::set<QString>* idsToKee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const
|
||||||
|
{
|
||||||
|
if (data.text.size() > 0) {
|
||||||
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
|
bodyRenderer->setTextWidth(option.rect.size().width());
|
||||||
|
painter->save();
|
||||||
|
painter->translate(option.rect.topLeft());
|
||||||
|
|
||||||
|
if (data.id == currentId) {
|
||||||
|
QTextCursor cursor(bodyRenderer);
|
||||||
|
cursor.setPosition(selection.first, QTextCursor::MoveAnchor);
|
||||||
|
cursor.setPosition(selection.second, QTextCursor::KeepAnchor);
|
||||||
|
QTextCharFormat format = cursor.charFormat();
|
||||||
|
format.setBackground(option.palette.color(QPalette::Active, QPalette::Highlight));
|
||||||
|
format.setForeground(option.palette.color(QPalette::Active, QPalette::HighlightedText));
|
||||||
|
cursor.setCharFormat(format);
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyRenderer->drawContents(painter);
|
||||||
|
|
||||||
|
painter->restore();
|
||||||
|
QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height()));
|
||||||
|
|
||||||
|
option.rect.adjust(0, bodySize.height() + textMargin, 0, 0);
|
||||||
|
return bodySize.width();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageDelegate::beginClearWidgets()
|
||||||
|
{
|
||||||
|
idsToKeep->clear();
|
||||||
|
clearingWidgets = true;
|
||||||
|
}
|
||||||
|
|
||||||
void MessageDelegate::endClearWidgets()
|
void MessageDelegate::endClearWidgets()
|
||||||
{
|
{
|
||||||
if (clearingWidgets) {
|
if (clearingWidgets) {
|
||||||
@ -621,7 +801,6 @@ void MessageDelegate::endClearWidgets()
|
|||||||
removeElements(bars, idsToKeep);
|
removeElements(bars, idsToKeep);
|
||||||
removeElements(statusIcons, idsToKeep);
|
removeElements(statusIcons, idsToKeep);
|
||||||
removeElements(pencilIcons, idsToKeep);
|
removeElements(pencilIcons, idsToKeep);
|
||||||
removeElements(bodies, idsToKeep);
|
|
||||||
removeElements(previews, idsToKeep);
|
removeElements(previews, idsToKeep);
|
||||||
|
|
||||||
idsToKeep->clear();
|
idsToKeep->clear();
|
||||||
@ -649,8 +828,3 @@ void MessageDelegate::clearHelperWidget(const Models::FeedItem& data) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// void MessageDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
@ -56,6 +57,11 @@ public:
|
|||||||
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
bool editorEvent(QEvent * event, QAbstractItemModel * model, const QStyleOptionViewItem & option, const QModelIndex & index) override;
|
||||||
void endClearWidgets();
|
void endClearWidgets();
|
||||||
void beginClearWidgets();
|
void beginClearWidgets();
|
||||||
|
void leftClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
QString leftDoubleClick(const QPoint& point, const QModelIndex& index, const QRect& sizeHint);
|
||||||
|
Shared::Hover hoverType(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
QString mouseDrag(const QPoint& start, const QPoint& end, const QModelIndex& index, const QRect& sizeHint);
|
||||||
|
QString clearSelection();
|
||||||
|
|
||||||
static int avatarHeight;
|
static int avatarHeight;
|
||||||
static int margin;
|
static int margin;
|
||||||
@ -63,23 +69,28 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void buttonPushed(const QString& messageId) const;
|
void buttonPushed(const QString& messageId) const;
|
||||||
void invalidPath(const QString& messageId) const;
|
void invalidPath(const QString& messageId) const;
|
||||||
|
void openLink(const QString& href) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
int paintButton(QPushButton* btn, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||||
int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
int paintBar(QProgressBar* bar, QPainter* painter, bool sentByMe, QStyleOptionViewItem& option) const;
|
||||||
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
int paintPreview(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||||
int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
int paintComment(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||||
|
int paintBody(const Models::FeedItem& data, QPainter* painter, QStyleOptionViewItem& option) const;
|
||||||
void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const;
|
void paintAvatar(const Models::FeedItem& data, const QModelIndex& index, const QStyleOptionViewItem& option, QPainter* painter) const;
|
||||||
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const;
|
void paintBubble(const Models::FeedItem& data, QPainter* painter, const QStyleOptionViewItem& option) const;
|
||||||
|
|
||||||
QPushButton* getButton(const Models::FeedItem& data) const;
|
QPushButton* getButton(const Models::FeedItem& data) const;
|
||||||
QProgressBar* getBar(const Models::FeedItem& data) const;
|
QProgressBar* getBar(const Models::FeedItem& data) const;
|
||||||
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
QLabel* getStatusIcon(const Models::FeedItem& data) const;
|
||||||
QLabel* getPencilIcon(const Models::FeedItem& data) const;
|
QLabel* getPencilIcon(const Models::FeedItem& data) const;
|
||||||
QLabel* getBody(const Models::FeedItem& data) const;
|
|
||||||
void clearHelperWidget(const Models::FeedItem& data) const;
|
void clearHelperWidget(const Models::FeedItem& data) const;
|
||||||
|
|
||||||
bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const;
|
bool needToDrawAvatar(const QModelIndex& index, const Models::FeedItem& data, const QStyleOptionViewItem& option) const;
|
||||||
bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const;
|
bool needToDrawSender(const QModelIndex& index, const Models::FeedItem& data) const;
|
||||||
|
|
||||||
|
QRect getHoveredMessageBodyRect(const QModelIndex& index, const Models::FeedItem& data, const QRect& sizeHint) const;
|
||||||
|
QString getAnchor(const QPoint& point, const QModelIndex& index, const QRect& sizeHint) const;
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
void onButtonPushed() const;
|
void onButtonPushed() const;
|
||||||
@ -93,7 +104,7 @@ private:
|
|||||||
QFont bodyFont;
|
QFont bodyFont;
|
||||||
QFont nickFont;
|
QFont nickFont;
|
||||||
QFont dateFont;
|
QFont dateFont;
|
||||||
QFontMetrics bodyMetrics;
|
QTextDocument* bodyRenderer;
|
||||||
QFontMetrics nickMetrics;
|
QFontMetrics nickMetrics;
|
||||||
QFontMetrics dateMetrics;
|
QFontMetrics dateMetrics;
|
||||||
|
|
||||||
@ -105,11 +116,11 @@ private:
|
|||||||
std::map<QString, QProgressBar*>* bars;
|
std::map<QString, QProgressBar*>* bars;
|
||||||
std::map<QString, QLabel*>* statusIcons;
|
std::map<QString, QLabel*>* statusIcons;
|
||||||
std::map<QString, QLabel*>* pencilIcons;
|
std::map<QString, QLabel*>* pencilIcons;
|
||||||
std::map<QString, QLabel*>* bodies;
|
|
||||||
std::map<QString, Preview*>* previews;
|
std::map<QString, Preview*>* previews;
|
||||||
std::set<QString>* idsToKeep;
|
std::set<QString>* idsToKeep;
|
||||||
bool clearingWidgets;
|
bool clearingWidgets;
|
||||||
|
QString currentId;
|
||||||
|
std::pair<int, int> selection;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MESSAGEDELEGATE_H
|
#endif // MESSAGEDELEGATE_H
|
||||||
|
@ -163,6 +163,12 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap<QString, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(index, index, cr);
|
emit dataChanged(index, index, cr);
|
||||||
|
|
||||||
|
if (observersAmount == 0 && !msg->getForwarded() && changeRoles.count(MessageRoles::Text) > 0) {
|
||||||
|
unreadMessages->insert(id);
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
|
emit unnoticedMessage(*msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,12 +318,7 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
|||||||
case Bulk: {
|
case Bulk: {
|
||||||
FeedItem item;
|
FeedItem item;
|
||||||
item.id = msg->getId();
|
item.id = msg->getId();
|
||||||
|
markMessageAsRead(item.id);
|
||||||
std::set<QString>::const_iterator umi = unreadMessages->find(item.id);
|
|
||||||
if (umi != unreadMessages->end()) {
|
|
||||||
unreadMessages->erase(umi);
|
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.sentByMe = sentByMe(*msg);
|
item.sentByMe = sentByMe(*msg);
|
||||||
item.date = msg->getTime();
|
item.date = msg->getTime();
|
||||||
@ -367,6 +368,17 @@ int Models::MessageFeed::rowCount(const QModelIndex& parent) const
|
|||||||
return storage.size();
|
return storage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::MessageFeed::markMessageAsRead(const QString& id) const
|
||||||
|
{
|
||||||
|
std::set<QString>::const_iterator umi = unreadMessages->find(id);
|
||||||
|
if (umi != unreadMessages->end()) {
|
||||||
|
unreadMessages->erase(umi);
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
||||||
{
|
{
|
||||||
return unreadMessages->size();
|
return unreadMessages->size();
|
||||||
|
@ -57,6 +57,8 @@ public:
|
|||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
void removeMessage(const QString& id);
|
void removeMessage(const QString& id);
|
||||||
Shared::Message getMessage(const QString& id);
|
Shared::Message getMessage(const QString& id);
|
||||||
|
QModelIndex modelIndexById(const QString& id) const;
|
||||||
|
QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const;
|
||||||
|
|
||||||
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
@ -72,6 +74,7 @@ public:
|
|||||||
void reportLocalPathInvalid(const QString& messageId);
|
void reportLocalPathInvalid(const QString& messageId);
|
||||||
|
|
||||||
unsigned int unreadMessagesCount() const;
|
unsigned int unreadMessagesCount() const;
|
||||||
|
bool markMessageAsRead(const QString& id) const;
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
void fileError(const QString& messageId, const QString& error, bool up);
|
void fileError(const QString& messageId, const QString& error, bool up);
|
||||||
void fileComplete(const QString& messageId, bool up);
|
void fileComplete(const QString& messageId, bool up);
|
||||||
@ -125,8 +128,6 @@ protected:
|
|||||||
bool sentByMe(const Shared::Message& msg) const;
|
bool sentByMe(const Shared::Message& msg) const;
|
||||||
Attachment fillAttach(const Shared::Message& msg) const;
|
Attachment fillAttach(const Shared::Message& msg) const;
|
||||||
Edition fillCorrection(const Shared::Message& msg) const;
|
Edition fillCorrection(const Shared::Message& msg) const;
|
||||||
QModelIndex modelIndexById(const QString& id) const;
|
|
||||||
QModelIndex modelIndexByTime(const QString& id, const QDateTime& time) const;
|
|
||||||
std::set<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const;
|
std::set<MessageRoles> detectChanges(const Shared::Message& msg, const QMap<QString, QVariant>& data) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "pagegeneral.h"
|
#include "pagegeneral.h"
|
||||||
#include "ui_pagegeneral.h"
|
#include "ui_pagegeneral.h"
|
||||||
|
#include "ui/squawk.h"
|
||||||
|
|
||||||
PageGeneral::PageGeneral(QWidget* parent):
|
PageGeneral::PageGeneral(QWidget* parent):
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
@ -28,7 +29,10 @@ PageGeneral::PageGeneral(QWidget* parent):
|
|||||||
|
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString());
|
m_ui->downloadsPathInput->setText(settings.value("downloadsPath").toString());
|
||||||
|
m_ui->trayIconCheckbox->setChecked(settings.value("trayIconCheckbox").toBool());
|
||||||
|
|
||||||
connect(m_ui->downloadsPathButton, &QPushButton::clicked, this, &PageGeneral::onBrowseButtonClicked);
|
connect(m_ui->downloadsPathButton, &QPushButton::clicked, this, &PageGeneral::onBrowseButtonClicked);
|
||||||
|
connect(m_ui->trayIconCheckbox, &QCheckBox::stateChanged, this, &PageGeneral::onTrayIconCheckboxChecked);
|
||||||
}
|
}
|
||||||
|
|
||||||
PageGeneral::~PageGeneral()
|
PageGeneral::~PageGeneral()
|
||||||
@ -76,3 +80,9 @@ void PageGeneral::onDialogDestroyed()
|
|||||||
{
|
{
|
||||||
dialog = nullptr;
|
dialog = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PageGeneral::onTrayIconCheckboxChecked(){
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("trayIconCheckbox", m_ui->trayIconCheckbox->isChecked());
|
||||||
|
Squawk::trayIcon->setVisible(m_ui->trayIconCheckbox->isChecked());
|
||||||
|
}
|
||||||
|
@ -47,6 +47,7 @@ private slots:
|
|||||||
void onBrowseButtonClicked();
|
void onBrowseButtonClicked();
|
||||||
void onDialogAccepted();
|
void onDialogAccepted();
|
||||||
void onDialogDestroyed();
|
void onDialogDestroyed();
|
||||||
|
void onTrayIconCheckboxChecked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<Ui::PageGeneral> m_ui;
|
QScopedPointer<Ui::PageGeneral> m_ui;
|
||||||
|
@ -39,6 +39,19 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="trayIconCheckbox">
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::LeftToRight</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Hide Squawk to tray</string>
|
||||||
|
</property>
|
||||||
|
<property name="tristate">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
Loading…
Reference in New Issue
Block a user