refactoring: UI squawk now belongs to a new class, it enables me doing trayed mode, when main window is destroyed

This commit is contained in:
Blue 2022-04-22 18:26:18 +03:00
parent 83cb220175
commit 721d3a1a89
Signed by: blue
GPG Key ID: 9B203B252A63EE38
16 changed files with 908 additions and 769 deletions

View File

@ -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)

View File

@ -12,7 +12,6 @@ target_sources(squawk PRIVATE
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

View File

@ -1,201 +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);
QApplication::setApplicationName("squawk");
QApplication::setOrganizationName("macaw.me");
QApplication::setApplicationDisplayName("Squawk");
QApplication::setApplicationVersion("0.2.2");
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 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;
}

7
main/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
target_sources(squawk PRIVATE
main.cpp
application.cpp
application.h
dialogqueue.cpp
dialogqueue.h
)

476
main/application.cpp Normal file
View File

@ -0,0 +1,476 @@
// 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", QDBusConnection::sessionBus()),
roster(),
conversations(),
dialogueQueue(roster),
nowQuitting(false),
destroyingSquawk(false)
{
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
//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);
}
Application::~Application() {}
void Application::quit()
{
if (!nowQuitting) {
nowQuitting = true;
emit quitting();
writeSettings();
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);
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 name = QString(roster.getContactName(account, msg.getPenPalJid()));
QString path = QString(roster.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource()));
QVariantList args;
args << QString();
args << qHash(msg.getId());
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() + " from " + name;
} else {
args << name;
}
QString body(msg.getBody());
QString oob(msg.getOutOfBandUrl());
if (body == oob) {
body = tr("Attached file");
}
args << body;
args << QStringList();
args << QVariantMap({
{"desktop-entry", QString(QCoreApplication::applicationName())},
{"category", QString("message")},
// {"sound-file", "/path/to/macaw/squawk"},
{"sound-name", QString("message-new-instant")}
});
args << -1;
notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
if (squawk != nullptr) {
QApplication::alert(squawk);
}
}
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 != NULL) {
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 != NULL && 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);}

111
main/application.h Normal file
View File

@ -0,0 +1,111 @@
// 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 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();
private:
void createMainWindow();
void subscribeConversation(Conversation* conv);
void checkForTheLastWindow();
private:
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
Shared::Availability availability;
Core::Squawk* core;
Squawk* squawk;
QDBusInterface notifications;
Models::Roster roster;
Conversations conversations;
DialogQueue dialogueQueue;
bool nowQuitting;
bool destroyingSquawk;
};
#endif // APPLICATION_H

View File

@ -15,10 +15,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "dialogqueue.h" #include "dialogqueue.h"
#include "squawk.h"
#include <QDebug> #include <QDebug>
DialogQueue::DialogQueue(Squawk* p_squawk): DialogQueue::DialogQueue(const Models::Roster& p_roster):
QObject(), QObject(),
currentSource(), currentSource(),
currentAction(none), currentAction(none),
@ -26,7 +25,8 @@ DialogQueue::DialogQueue(Squawk* p_squawk):
collection(queue.get<0>()), collection(queue.get<0>()),
sequence(queue.get<1>()), sequence(queue.get<1>()),
prompt(nullptr), prompt(nullptr),
squawk(p_squawk) parent(nullptr),
roster(p_roster)
{ {
} }
@ -34,6 +34,19 @@ 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) bool DialogQueue::addAction(const QString& source, DialogQueue::Action action)
{ {
if (action == none) { if (action == none) {
@ -77,7 +90,7 @@ void DialogQueue::performNextAction()
actionDone(); actionDone();
break; break;
case askPassword: { case askPassword: {
QInputDialog* dialog = new QInputDialog(squawk); QInputDialog* dialog = new QInputDialog(parent);
prompt = dialog; prompt = dialog;
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
@ -90,11 +103,11 @@ void DialogQueue::performNextAction()
} }
break; break;
case askCredentials: { case askCredentials: {
CredentialsPrompt* dialog = new CredentialsPrompt(squawk); CredentialsPrompt* dialog = new CredentialsPrompt(parent);
prompt = dialog; prompt = dialog;
connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted); connect(dialog, &QDialog::accepted, this, &DialogQueue::onPropmtAccepted);
connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected); connect(dialog, &QDialog::rejected, this, &DialogQueue::onPropmtRejected);
Models::Account* acc = squawk->rosterModel.getAccount(currentSource); const Models::Account* acc = roster.getAccountConst(currentSource);
dialog->setAccount(currentSource); dialog->setAccount(currentSource);
dialog->setLogin(acc->getLogin()); dialog->setLogin(acc->getLogin());
dialog->setPassword(acc->getPassword()); dialog->setPassword(acc->getPassword());
@ -111,12 +124,12 @@ void DialogQueue::onPropmtAccepted()
break; break;
case askPassword: { case askPassword: {
QInputDialog* dialog = static_cast<QInputDialog*>(prompt); QInputDialog* dialog = static_cast<QInputDialog*>(prompt);
emit squawk->responsePassword(currentSource, dialog->textValue()); emit responsePassword(currentSource, dialog->textValue());
} }
break; break;
case askCredentials: { case askCredentials: {
CredentialsPrompt* dialog = static_cast<CredentialsPrompt*>(prompt); CredentialsPrompt* dialog = static_cast<CredentialsPrompt*>(prompt);
emit squawk->modifyAccountRequest(currentSource, { emit modifyAccountRequest(currentSource, {
{"login", dialog->getLogin()}, {"login", dialog->getLogin()},
{"password", dialog->getPassword()} {"password", dialog->getPassword()}
}); });
@ -133,7 +146,7 @@ void DialogQueue::onPropmtRejected()
break; break;
case askPassword: case askPassword:
case askCredentials: case askCredentials:
emit squawk->disconnectAccount(currentSource); emit disconnectAccount(currentSource);
break; break;
} }
actionDone(); actionDone();

View File

@ -19,14 +19,14 @@
#include <QObject> #include <QObject>
#include <QInputDialog> #include <QInputDialog>
#include <QMainWindow>
#include <boost/multi_index_container.hpp> #include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/sequenced_index.hpp>
#include <ui/widgets/accounts/credentialsprompt.h> #include <ui/widgets/accounts/credentialsprompt.h>
#include <ui/models/roster.h>
class Squawk;
class DialogQueue : public QObject class DialogQueue : public QObject
{ {
@ -38,12 +38,21 @@ public:
askCredentials askCredentials
}; };
DialogQueue(Squawk* squawk); DialogQueue(const Models::Roster& roster);
~DialogQueue(); ~DialogQueue();
bool addAction(const QString& source, Action action); bool addAction(const QString& source, Action action);
bool cancelAction(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: private:
void performNextAction(); void performNextAction();
void actionDone(); void actionDone();
@ -85,7 +94,8 @@ private:
Sequence& sequence; Sequence& sequence;
QDialog* prompt; QDialog* prompt;
Squawk* squawk; QMainWindow* parent;
const Models::Roster& roster;
}; };
#endif // DIALOGQUEUE_H #endif // DIALOGQUEUE_H

139
main/main.cpp Normal file
View File

@ -0,0 +1,139 @@
/*
* 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");
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;
}

View File

@ -95,8 +95,6 @@ Shared::Global::Global():
}), }),
defaultSystemStyle(QApplication::style()->objectName()), defaultSystemStyle(QApplication::style()->objectName()),
defaultSystemPalette(QApplication::palette()), defaultSystemPalette(QApplication::palette()),
rosterModel(new Models::Roster()),
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
pluginSupport({ pluginSupport({
{"KWallet", false}, {"KWallet", false},
{"openFileManagerWindowJob", false}, {"openFileManagerWindowJob", false},
@ -352,44 +350,6 @@ void Shared::Global::setStyle(const QString& style)
} }
} }
void Shared::Global::notify(const QString& account, const Shared::Message& msg)
{
QString name = QString(instance->rosterModel->getContactName(account, msg.getPenPalJid()));
QString path = QString(instance->rosterModel->getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource()));
QVariantList args;
args << QString();
args << qHash(msg.getId());
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() + " from " + name;
} else {
args << name;
}
QString body(msg.getBody());
QString oob(msg.getOutOfBandUrl());
if (body == oob) {
body = tr("Attached file");
}
args << body;
args << QStringList();
args << QVariantMap({
{"desktop-entry", QString(QCoreApplication::applicationName())},
{"category", QString("message")},
// {"sound-file", "/path/to/macaw/squawk"},
{"sound-name", QString("message-new-instant")}
});
args << -1;
instance->dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
}
#define FROM_INT_INPL(Enum) \ #define FROM_INT_INPL(Enum) \
template<> \ template<> \
Enum Shared::Global::fromInt(int src) \ Enum Shared::Global::fromInt(int src) \

View File

@ -42,18 +42,11 @@
#include <QProcess> #include <QProcess>
#include <QDesktopServices> #include <QDesktopServices>
#include <QRegularExpression> #include <QRegularExpression>
#include <QDBusInterface>
class Squawk;
namespace Models {
class Roster;
}
namespace Shared { namespace Shared {
class Global { class Global {
Q_DECLARE_TR_FUNCTIONS(Global) Q_DECLARE_TR_FUNCTIONS(Global)
friend class ::Squawk;
public: public:
struct FileInfo { struct FileInfo {
enum class Preview { enum class Preview {
@ -71,8 +64,6 @@ namespace Shared {
Global(); Global();
static void notify(const QString& account, const Shared::Message& msg);
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);
@ -130,8 +121,6 @@ namespace Shared {
private: private:
static Global* instance; static Global* instance;
Models::Roster* rosterModel;
QDBusInterface dbus;
std::map<QString, bool> pluginSupport; std::map<QString, bool> pluginSupport;
std::map<QString, FileInfo> fileCache; std::map<QString, FileInfo> fileCache;

View File

@ -2,8 +2,6 @@ target_sources(squawk PRIVATE
squawk.cpp squawk.cpp
squawk.h squawk.h
squawk.ui squawk.ui
dialogqueue.cpp
dialogqueue.h
) )
add_subdirectory(models) add_subdirectory(models)

View File

@ -927,11 +927,29 @@ 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 NULL;
} }
QModelIndex Models::Roster::getAccountIndex(const QString& name) QModelIndex Models::Roster::getAccountIndex(const QString& name)
{ {
std::map<QString, Account*>::const_iterator itr = accounts.find(name); std::map<QString, Account*>::const_iterator itr = accounts.find(name);
@ -1005,20 +1023,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 == NULL) {
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());

View File

@ -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);
public:
QString getContactName(const QString& account, const QString& jid) const; 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;
@ -79,6 +85,7 @@ public:
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) const; 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);
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);
@ -95,9 +102,6 @@ signals:
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();

View File

@ -21,15 +21,13 @@
#include <QDebug> #include <QDebug>
#include <QIcon> #include <QIcon>
Squawk::Squawk(QWidget *parent) : Squawk::Squawk(Models::Roster& p_rosterModel, QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
m_ui(new Ui::Squawk), m_ui(new Ui::Squawk),
accounts(nullptr), accounts(nullptr),
preferences(nullptr), preferences(nullptr),
about(nullptr), about(nullptr),
dialogueQueue(this), rosterModel(p_rosterModel),
rosterModel(*(Shared::Global::getInstance()->rosterModel)),
conversations(),
contextMenu(new QMenu()), contextMenu(new QMenu()),
vCards(), vCards(),
currentConversation(nullptr), currentConversation(nullptr),
@ -64,12 +62,8 @@ Squawk::Squawk(QWidget *parent) :
connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu); connect(m_ui->roster, &QTreeView::customContextMenuRequested, this, &Squawk::onRosterContextMenu);
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed); connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged); connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage);
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged); connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive);
connect(&rosterModel, &Models::Roster::fileDownloadRequest, this, &Squawk::fileDownloadRequest);
connect(&rosterModel, &Models::Roster::localPathInvalid, this, &Squawk::localPathInvalid);
connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide); connect(contextMenu, &QMenu::aboutToHide, this, &Squawk::onContextAboutToHide);
connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled); connect(m_ui->actionAboutSquawk, &QAction::triggered, this, &Squawk::onAboutSquawkCalled);
//m_ui->mainToolBar->addWidget(m_ui->comboBox); //m_ui->mainToolBar->addWidget(m_ui->comboBox);
@ -199,36 +193,25 @@ void Squawk::closeEvent(QCloseEvent* event)
about->close(); about->close();
} }
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
disconnect(itr->second, &Conversation::destroyed, this, &Squawk::onConversationClosed);
itr->second->close();
}
conversations.clear();
for (std::map<QString, VCard*>::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) { for (std::map<QString, VCard*>::const_iterator itr = vCards.begin(), end = vCards.end(); itr != end; ++itr) {
disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed); disconnect(itr->second, &VCard::destroyed, this, &Squawk::onVCardClosed);
itr->second->close(); itr->second->close();
} }
vCards.clear(); vCards.clear();
writeSettings();
emit closing();;
QMainWindow::closeEvent(event); QMainWindow::closeEvent(event);
} }
void Squawk::onAccountsClosed() {
accounts = nullptr;}
void Squawk::onAccountsClosed() void Squawk::onPreferencesClosed() {
{ preferences = nullptr;}
accounts = nullptr;
}
void Squawk::onPreferencesClosed() void Squawk::onAboutSquawkClosed() {
{ about = nullptr;}
preferences = nullptr;
}
void Squawk::newAccount(const QMap<QString, QVariant>& account)
{
rosterModel.addAccount(account);
}
void Squawk::onComboboxActivated(int index) void Squawk::onComboboxActivated(int index)
{ {
@ -236,85 +219,11 @@ void Squawk::onComboboxActivated(int index)
emit changeState(av); emit changeState(av);
} }
void Squawk::changeAccount(const QString& account, const QMap<QString, QVariant>& data) void Squawk::expand(const QModelIndex& index) {
{ m_ui->roster->expand(index);}
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
QString attr = itr.key();
rosterModel.updateAccount(account, attr, *itr);
}
}
void Squawk::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data) void Squawk::stateChanged(Shared::Availability state) {
{ m_ui->comboBox->setCurrentIndex(static_cast<int>(state));}
rosterModel.addContact(account, jid, group, data);
QSettings settings;
settings.beginGroup("ui");
settings.beginGroup("roster");
settings.beginGroup(account);
if (settings.value("expanded", false).toBool()) {
QModelIndex ind = rosterModel.getAccountIndex(account);
m_ui->roster->expand(ind);
}
settings.endGroup();
settings.endGroup();
settings.endGroup();
}
void Squawk::addGroup(const QString& account, const QString& name)
{
rosterModel.addGroup(account, name);
QSettings settings;
settings.beginGroup("ui");
settings.beginGroup("roster");
settings.beginGroup(account);
if (settings.value("expanded", false).toBool()) {
QModelIndex ind = rosterModel.getAccountIndex(account);
m_ui->roster->expand(ind);
if (settings.value(name + "/expanded", false).toBool()) {
m_ui->roster->expand(rosterModel.getGroupIndex(account, name));
}
}
settings.endGroup();
settings.endGroup();
settings.endGroup();
}
void Squawk::removeGroup(const QString& account, const QString& name)
{
rosterModel.removeGroup(account, name);
}
void Squawk::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
{
rosterModel.changeContact(account, jid, data);
}
void Squawk::removeContact(const QString& account, const QString& jid)
{
rosterModel.removeContact(account, jid);
}
void Squawk::removeContact(const QString& account, const QString& jid, const QString& group)
{
rosterModel.removeContact(account, jid, group);
}
void Squawk::addPresence(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
rosterModel.addPresence(account, jid, name, data);
}
void Squawk::removePresence(const QString& account, const QString& jid, const QString& name)
{
rosterModel.removePresence(account, jid, name);
}
void Squawk::stateChanged(Shared::Availability state)
{
m_ui->comboBox->setCurrentIndex(static_cast<int>(state));
}
void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
{ {
@ -325,186 +234,33 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item)
} }
Models::Contact* contact = nullptr; Models::Contact* contact = nullptr;
Models::Room* room = nullptr; Models::Room* room = nullptr;
QString res;
Models::Roster::ElId* id = nullptr;
switch (node->type) { switch (node->type) {
case Models::Item::contact: case Models::Item::contact:
contact = static_cast<Models::Contact*>(node); contact = static_cast<Models::Contact*>(node);
id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()));
break; break;
case Models::Item::presence: case Models::Item::presence:
contact = static_cast<Models::Contact*>(node->parentItem()); contact = static_cast<Models::Contact*>(node->parentItem());
id = new Models::Roster::ElId(contact->getAccountName(), contact->getJid()); emit openConversation(Models::Roster::ElId(contact->getAccountName(), contact->getJid()), node->getName());
res = node->getName();
break; break;
case Models::Item::room: case Models::Item::room:
room = static_cast<Models::Room*>(node); room = static_cast<Models::Room*>(node);
id = new Models::Roster::ElId(room->getAccountName(), room->getJid()); emit openConversation(Models::Roster::ElId(room->getAccountName(), room->getJid()));
break; break;
default: default:
m_ui->roster->expand(item); m_ui->roster->expand(item);
break; break;
} }
if (id != nullptr) {
Conversations::const_iterator itr = conversations.find(*id);
Models::Account* acc = rosterModel.getAccount(id->account);
Conversation* conv = nullptr;
bool created = false;
if (itr != conversations.end()) {
conv = itr->second;
} else if (contact != nullptr) {
created = true;
conv = new Chat(acc, contact);
} else if (room != nullptr) {
created = true;
conv = new Room(acc, room);
if (!room->getJoined()) {
emit setRoomJoined(id->account, id->name, true);
}
}
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 (res.size() > 0) {
conv->setPalResource(res);
}
}
delete id;
}
} }
} }
void Squawk::onConversationClosed(QObject* parent) void Squawk::closeCurrentConversation()
{ {
Conversation* conv = static_cast<Conversation*>(sender()); if (currentConversation != nullptr) {
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 Squawk::fileProgress(const std::list<Shared::MessageInfo> msgs, qreal value, bool up)
{
rosterModel.fileProgress(msgs, value, up);
}
void Squawk::fileDownloadComplete(const std::list<Shared::MessageInfo> msgs, const QString& path)
{
rosterModel.fileComplete(msgs, false);
}
void Squawk::fileError(const std::list<Shared::MessageInfo> msgs, const QString& error, bool up)
{
rosterModel.fileError(msgs, error, up);
}
void Squawk::fileUploadComplete(const std::list<Shared::MessageInfo> msgs, const QString& url, const QString& path)
{
rosterModel.fileComplete(msgs, true);
}
void Squawk::accountMessage(const QString& account, const Shared::Message& data)
{
rosterModel.addMessage(account, data);
}
void Squawk::onUnnoticedMessage(const QString& account, const Shared::Message& msg)
{
notify(account, msg); //Telegram does this way - notifies even if the app is visible
QApplication::alert(this);
}
void Squawk::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
{
rosterModel.changeMessage(account, jid, id, data);
}
void Squawk::notify(const QString& account, const Shared::Message& msg)
{
Shared::Global::notify(account, msg);
}
void Squawk::onConversationMessage(const Shared::Message& msg)
{
Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount();
rosterModel.addMessage(acc, msg);
emit sendMessage(acc, msg);
}
void Squawk::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg)
{
Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount();
rosterModel.changeMessage(acc, msg.getPenPalJid(), originalId, {
{"state", static_cast<uint>(Shared::Message::State::pending)}
});
emit replaceMessage(acc, originalId, msg);
}
void Squawk::onConversationResend(const QString& id)
{
Conversation* conv = static_cast<Conversation*>(sender());
QString acc = conv->getAccount();
QString jid = conv->getJid();
emit resendMessage(acc, jid, id);
}
void Squawk::onRequestArchive(const QString& account, const QString& jid, const QString& before)
{
emit requestArchive(account, jid, 20, before); //TODO amount as a settings value
}
void Squawk::responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last)
{
rosterModel.responseArchive(account, jid, list, last);
}
void Squawk::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, &Squawk::onConversationClosed);
conv->close();
conversations.erase(lItr);
} else {
++itr;
}
}
if (currentConversation != nullptr && currentConversation->getAccount() == account) {
currentConversation->deleteLater(); currentConversation->deleteLater();
currentConversation = nullptr; currentConversation = nullptr;
m_ui->filler->show(); m_ui->filler->show();
} }
rosterModel.removeAccount(account);
} }
void Squawk::onRosterContextMenu(const QPoint& point) void Squawk::onRosterContextMenu(const QPoint& point)
@ -542,13 +298,13 @@ void Squawk::onRosterContextMenu(const QPoint& point)
break; break;
case Models::Item::contact: { case Models::Item::contact: {
Models::Contact* cnt = static_cast<Models::Contact*>(item); Models::Contact* cnt = static_cast<Models::Contact*>(item);
Models::Roster::ElId id(cnt->getAccountName(), cnt->getJid());
QString cntName = cnt->getName();
hasMenu = true; hasMenu = true;
QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog")); QAction* dialog = contextMenu->addAction(Shared::icon("mail-message"), tr("Open dialog"));
dialog->setEnabled(active); dialog->setEnabled(active);
connect(dialog, &QAction::triggered, [this, index]() { connect(dialog, &QAction::triggered, std::bind(&Squawk::onRosterItemDoubleClicked, this, index));
onRosterItemDoubleClicked(index);
});
Shared::SubscriptionState state = cnt->getState(); Shared::SubscriptionState state = cnt->getState();
switch (state) { switch (state) {
@ -556,9 +312,7 @@ void Squawk::onRosterContextMenu(const QPoint& point)
case Shared::SubscriptionState::to: { case Shared::SubscriptionState::to: {
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
unsub->setEnabled(active); unsub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, cnt]() { connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false));
emit unsubscribeContact(cnt->getAccountName(), cnt->getJid(), "");
});
} }
break; break;
case Shared::SubscriptionState::from: case Shared::SubscriptionState::from:
@ -566,75 +320,68 @@ void Squawk::onRosterContextMenu(const QPoint& point)
case Shared::SubscriptionState::none: { case Shared::SubscriptionState::none: {
QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
sub->setEnabled(active); sub->setEnabled(active);
connect(sub, &QAction::triggered, [this, cnt]() { connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true));
emit subscribeContact(cnt->getAccountName(), cnt->getJid(), "");
});
} }
} }
QString accName = cnt->getAccountName();
QString cntJID = cnt->getJid();
QString cntName = cnt->getName();
QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename")); QAction* rename = contextMenu->addAction(Shared::icon("edit-rename"), tr("Rename"));
rename->setEnabled(active); rename->setEnabled(active);
connect(rename, &QAction::triggered, [this, cntName, accName, cntJID]() { connect(rename, &QAction::triggered, [this, cntName, id]() {
QInputDialog* dialog = new QInputDialog(this); QInputDialog* dialog = new QInputDialog(this);
connect(dialog, &QDialog::accepted, [this, dialog, cntName, accName, cntJID]() { connect(dialog, &QDialog::accepted, [this, dialog, cntName, id]() {
QString newName = dialog->textValue(); QString newName = dialog->textValue();
if (newName != cntName) { if (newName != cntName) {
emit renameContactRequest(accName, cntJID, newName); emit renameContactRequest(id.account, id.name, newName);
} }
dialog->deleteLater(); dialog->deleteLater();
}); });
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
dialog->setInputMode(QInputDialog::TextInput); dialog->setInputMode(QInputDialog::TextInput);
dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(cntJID)); dialog->setLabelText(tr("Input new name for %1\nor leave it empty for the contact \nto be displayed as %1").arg(id.name));
dialog->setWindowTitle(tr("Renaming %1").arg(cntJID)); dialog->setWindowTitle(tr("Renaming %1").arg(id.name));
dialog->setTextValue(cntName); dialog->setTextValue(cntName);
dialog->exec(); dialog->exec();
}); });
QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups")); QMenu* groupsMenu = contextMenu->addMenu(Shared::icon("group"), tr("Groups"));
std::deque<QString> groupList = rosterModel.groupList(accName); std::deque<QString> groupList = rosterModel.groupList(id.account);
for (QString groupName : groupList) { for (QString groupName : groupList) {
QAction* gr = groupsMenu->addAction(groupName); QAction* gr = groupsMenu->addAction(groupName);
gr->setCheckable(true); gr->setCheckable(true);
gr->setChecked(rosterModel.groupHasContact(accName, groupName, cntJID)); gr->setChecked(rosterModel.groupHasContact(id.account, groupName, id.name));
gr->setEnabled(active); gr->setEnabled(active);
connect(gr, &QAction::toggled, [this, accName, groupName, cntJID](bool checked) { connect(gr, &QAction::toggled, [this, groupName, id](bool checked) {
if (checked) { if (checked) {
emit addContactToGroupRequest(accName, cntJID, groupName); emit addContactToGroupRequest(id.account, id.name, groupName);
} else { } else {
emit removeContactFromGroupRequest(accName, cntJID, groupName); emit removeContactFromGroupRequest(id.account, id.name, groupName);
} }
}); });
} }
QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group")); QAction* newGroup = groupsMenu->addAction(Shared::icon("group-new"), tr("New group"));
newGroup->setEnabled(active); newGroup->setEnabled(active);
connect(newGroup, &QAction::triggered, [this, accName, cntJID]() { connect(newGroup, &QAction::triggered, [this, id]() {
QInputDialog* dialog = new QInputDialog(this); QInputDialog* dialog = new QInputDialog(this);
connect(dialog, &QDialog::accepted, [this, dialog, accName, cntJID]() { connect(dialog, &QDialog::accepted, [this, dialog, id]() {
emit addContactToGroupRequest(accName, cntJID, dialog->textValue()); emit addContactToGroupRequest(id.account, id.name, dialog->textValue());
dialog->deleteLater(); dialog->deleteLater();
}); });
connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater); connect(dialog, &QDialog::rejected, dialog, &QObject::deleteLater);
dialog->setInputMode(QInputDialog::TextInput); dialog->setInputMode(QInputDialog::TextInput);
dialog->setLabelText(tr("New group name")); dialog->setLabelText(tr("New group name"));
dialog->setWindowTitle(tr("Add %1 to a new group").arg(cntJID)); dialog->setWindowTitle(tr("Add %1 to a new group").arg(id.name));
dialog->exec(); dialog->exec();
}); });
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard")); QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
card->setEnabled(active); card->setEnabled(active);
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, accName, cnt->getJid(), false)); connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, id.account, id.name, false));
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
remove->setEnabled(active); remove->setEnabled(active);
connect(remove, &QAction::triggered, [this, cnt]() { connect(remove, &QAction::triggered, std::bind(&Squawk::removeContactRequest, this, id.account, id.name));
emit removeContactRequest(cnt->getAccountName(), cnt->getJid());
});
} }
break; break;
@ -653,32 +400,16 @@ void Squawk::onRosterContextMenu(const QPoint& point)
if (room->getAutoJoin()) { if (room->getAutoJoin()) {
QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe")); QAction* unsub = contextMenu->addAction(Shared::icon("news-unsubscribe"), tr("Unsubscribe"));
unsub->setEnabled(active); unsub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, id]() { connect(unsub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, false));
emit setRoomAutoJoin(id.account, id.name, false);
if (conversations.find(id) == conversations.end()
&& (currentConversation == nullptr || currentConversation->getId() != id)
) { //to leave the room if it's not opened in a conversation window
emit setRoomJoined(id.account, id.name, false);
}
});
} else { } else {
QAction* unsub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe")); QAction* sub = contextMenu->addAction(Shared::icon("news-subscribe"), tr("Subscribe"));
unsub->setEnabled(active); sub->setEnabled(active);
connect(unsub, &QAction::triggered, [this, id]() { connect(sub, &QAction::triggered, std::bind(&Squawk::changeSubscription, this, id, true));
emit setRoomAutoJoin(id.account, id.name, true);
if (conversations.find(id) == conversations.end()
&& (currentConversation == nullptr || currentConversation->getId() != id)
) { //to join the room if it's not already joined
emit setRoomJoined(id.account, id.name, true);
}
});
} }
QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove")); QAction* remove = contextMenu->addAction(Shared::icon("edit-delete"), tr("Remove"));
remove->setEnabled(active); remove->setEnabled(active);
connect(remove, &QAction::triggered, [this, id]() { connect(remove, &QAction::triggered, std::bind(&Squawk::removeRoomRequest, this, id.account, id.name));
emit removeRoomRequest(id.account, id.name);
});
} }
break; break;
default: default:
@ -690,36 +421,6 @@ void Squawk::onRosterContextMenu(const QPoint& point)
} }
} }
void Squawk::addRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data)
{
rosterModel.addRoom(account, jid, data);
}
void Squawk::changeRoom(const QString& account, const QString jid, const QMap<QString, QVariant>& data)
{
rosterModel.changeRoom(account, jid, data);
}
void Squawk::removeRoom(const QString& account, const QString jid)
{
rosterModel.removeRoom(account, jid);
}
void Squawk::addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
rosterModel.addRoomParticipant(account, jid, name, data);
}
void Squawk::changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data)
{
rosterModel.changeRoomParticipant(account, jid, name, data);
}
void Squawk::removeRoomParticipant(const QString& account, const QString& jid, const QString& name)
{
rosterModel.removeRoomParticipant(account, jid, name);
}
void Squawk::responseVCard(const QString& jid, const Shared::VCard& card) void Squawk::responseVCard(const QString& jid, const Shared::VCard& card)
{ {
std::map<QString, VCard*>::const_iterator itr = vCards.find(jid); std::map<QString, VCard*>::const_iterator itr = vCards.find(jid);
@ -777,61 +478,40 @@ void Squawk::onVCardSave(const Shared::VCard& card, const QString& account)
widget->deleteLater(); widget->deleteLater();
} }
void Squawk::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();
m_ui->comboBox->setCurrentIndex(avail);
emit changeState(Shared::Global::fromInt<Shared::Availability>(avail));
}
void Squawk::writeSettings() void Squawk::writeSettings()
{ {
QSettings settings; QSettings settings;
settings.beginGroup("ui"); settings.beginGroup("ui");
settings.beginGroup("window"); settings.beginGroup("window");
settings.setValue("geometry", saveGeometry()); settings.setValue("geometry", saveGeometry());
settings.setValue("state", saveState()); settings.setValue("state", saveState());
settings.endGroup(); settings.endGroup();
settings.setValue("splitter", m_ui->splitter->saveState()); settings.setValue("splitter", m_ui->splitter->saveState());
settings.remove("roster");
settings.setValue("availability", m_ui->comboBox->currentIndex()); settings.beginGroup("roster");
int size = rosterModel.accountsModel->rowCount(QModelIndex());
settings.remove("roster"); for (int i = 0; i < size; ++i) {
settings.beginGroup("roster"); QModelIndex acc = rosterModel.index(i, 0, QModelIndex());
int size = rosterModel.accountsModel->rowCount(QModelIndex()); Models::Account* account = rosterModel.accountsModel->getAccount(i);
for (int i = 0; i < size; ++i) { QString accName = account->getName();
QModelIndex acc = rosterModel.index(i, 0, QModelIndex()); settings.beginGroup(accName);
Models::Account* account = rosterModel.accountsModel->getAccount(i);
QString accName = account->getName(); settings.setValue("expanded", m_ui->roster->isExpanded(acc));
settings.beginGroup(accName); std::deque<QString> groups = rosterModel.groupList(accName);
for (const QString& groupName : groups) {
settings.setValue("expanded", m_ui->roster->isExpanded(acc)); settings.beginGroup(groupName);
std::deque<QString> groups = rosterModel.groupList(accName); QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName);
for (const QString& groupName : groups) { settings.setValue("expanded", m_ui->roster->isExpanded(gIndex));
settings.beginGroup(groupName); settings.endGroup();
QModelIndex gIndex = rosterModel.getGroupIndex(accName, groupName); }
settings.setValue("expanded", m_ui->roster->isExpanded(gIndex));
settings.endGroup(); settings.endGroup();
} }
settings.endGroup(); settings.endGroup();
}
settings.endGroup();
settings.endGroup(); settings.endGroup();
settings.sync(); settings.sync();
qDebug() << "Saved settings";
} }
void Squawk::onItemCollepsed(const QModelIndex& index) void Squawk::onItemCollepsed(const QModelIndex& index)
@ -853,24 +533,6 @@ void Squawk::onItemCollepsed(const QModelIndex& index)
} }
} }
void Squawk::requestPassword(const QString& account, bool authenticationError) {
if (authenticationError) {
dialogueQueue.addAction(account, DialogQueue::askCredentials);
} else {
dialogueQueue.addAction(account, DialogQueue::askPassword);
}
}
void Squawk::subscribeConversation(Conversation* conv)
{
connect(conv, &Conversation::destroyed, this, &Squawk::onConversationClosed);
connect(conv, &Conversation::sendMessage, this, &Squawk::onConversationMessage);
connect(conv, &Conversation::replaceMessage, this, &Squawk::onConversationReplaceMessage);
connect(conv, &Conversation::resendMessage, this, &Squawk::onConversationResend);
connect(conv, &Conversation::notifyableMessage, this, &Squawk::notify);
}
void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous) void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous)
{ {
if (restoreSelection.isValid() && restoreSelection == current) { if (restoreSelection.isValid() && restoreSelection == current) {
@ -942,16 +604,12 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
currentConversation = new Chat(acc, contact); currentConversation = new Chat(acc, contact);
} else if (room != nullptr) { } else if (room != nullptr) {
currentConversation = new Room(acc, room); currentConversation = new Room(acc, room);
if (!room->getJoined()) {
emit setRoomJoined(id->account, id->name, true);
}
} }
if (!testAttribute(Qt::WA_TranslucentBackground)) { if (!testAttribute(Qt::WA_TranslucentBackground)) {
currentConversation->setFeedFrames(true, false, true, true); currentConversation->setFeedFrames(true, false, true, true);
} }
subscribeConversation(currentConversation); emit openedConversation();
if (res.size() > 0) { if (res.size() > 0) {
currentConversation->setPalResource(res); currentConversation->setPalResource(res);
@ -961,18 +619,10 @@ void Squawk::onRosterSelectionChanged(const QModelIndex& current, const QModelIn
delete id; delete id;
} else { } else {
if (currentConversation != nullptr) { closeCurrentConversation();
currentConversation->deleteLater();
currentConversation = nullptr;
m_ui->filler->show();
}
} }
} else { } else {
if (currentConversation != nullptr) { closeCurrentConversation();
currentConversation->deleteLater();
currentConversation = nullptr;
m_ui->filler->show();
}
} }
} }
@ -997,7 +647,12 @@ void Squawk::onAboutSquawkCalled()
about->show(); about->show();
} }
void Squawk::onAboutSquawkClosed() Models::Roster::ElId Squawk::currentConversationId() const
{ {
about = nullptr; if (currentConversation == nullptr) {
return Models::Roster::ElId();
} else {
return Models::Roster::ElId(currentConversation->getAccount(), currentConversation->getJid());
}
} }

View File

@ -39,7 +39,6 @@
#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 "widgets/about.h"
#include "dialogqueue.h"
#include "shared/shared.h" #include "shared/shared.h"
#include "shared/global.h" #include "shared/global.h"
@ -48,84 +47,57 @@ namespace Ui {
class Squawk; class Squawk;
} }
class Application;
class Squawk : public QMainWindow class Squawk : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
friend class DialogQueue; 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();
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 requestPassword(const QString& account, bool authenticationError);
private: private:
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
QScopedPointer<Ui::Squawk> m_ui; QScopedPointer<Ui::Squawk> m_ui;
Accounts* accounts; Accounts* accounts;
Settings* preferences; Settings* preferences;
About* about; About* about;
DialogQueue dialogueQueue;
Models::Roster& rosterModel; Models::Roster& rosterModel;
Conversations conversations;
QMenu* contextMenu; QMenu* contextMenu;
std::map<QString, VCard*> vCards; std::map<QString, VCard*> vCards;
Conversation* currentConversation; Conversation* currentConversation;
@ -134,9 +106,7 @@ private:
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();
@ -148,27 +118,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 onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous); void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
void onContextAboutToHide(); void onContextAboutToHide();
void onAboutSquawkCalled(); void onAboutSquawkCalled();
void onAboutSquawkClosed(); void onAboutSquawkClosed();
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
private:
void subscribeConversation(Conversation* conv);
}; };
#endif // SQUAWK_H #endif // SQUAWK_H