forked from blue/squawk
657 lines
25 KiB
C++
657 lines
25 KiB
C++
// 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(),
|
|
trayIcon(nullptr),
|
|
actionQuit(Shared::icon("application-exit"), tr("Quit")),
|
|
actionToggle(QApplication::windowIcon(), tr("Minimize to tray")),
|
|
expandedPaths()
|
|
{
|
|
connect(&actionQuit, &QAction::triggered, this, &Application::quit);
|
|
connect(&actionToggle, &QAction::triggered, this, &Application::toggleSquawk);
|
|
|
|
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
|
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
|
connect(&roster, &Models::Roster::addedElement, this, &Application::onAddedElement);
|
|
|
|
|
|
//connecting myself to the backend
|
|
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, &roster, &Models::Roster::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 (trayIcon != nullptr) {
|
|
trayIcon->deleteLater();
|
|
trayIcon = nullptr;
|
|
}
|
|
|
|
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::changeTray, this, &Application::onChangeTray);
|
|
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
|
|
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
|
|
connect(squawk, &Squawk::quit, this, &Application::quit);
|
|
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::requestInfo, core, &Core::Squawk::requestInfo);
|
|
connect(squawk, &Squawk::updateInfo, core, &Core::Squawk::updateInfo);
|
|
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
|
|
|
connect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo);
|
|
|
|
dialogueQueue.setParentWidnow(squawk);
|
|
squawk->stateChanged(availability);
|
|
squawk->raise();
|
|
squawk->show();
|
|
squawk->activateWindow();
|
|
|
|
for (const std::list<QString>& entry : expandedPaths) {
|
|
QModelIndex ind = roster.getIndexByPath(entry);
|
|
if (ind.isValid())
|
|
squawk->expand(ind);
|
|
}
|
|
|
|
connect(squawk, &Squawk::itemExpanded, this, &Application::onItemExpanded);
|
|
connect(squawk, &Squawk::itemCollapsed, this, &Application::onItemCollapsed);
|
|
}
|
|
}
|
|
|
|
void Application::onSquawkClosing() {
|
|
dialogueQueue.setParentWidnow(nullptr);
|
|
|
|
if (!nowQuitting) {
|
|
disconnect(core, &Core::Squawk::responseInfo, squawk, &Squawk::responseInfo);
|
|
}
|
|
|
|
destroyingSquawk = true;
|
|
squawk->deleteLater();
|
|
squawk = nullptr;
|
|
|
|
QSettings settings;
|
|
if (!nowQuitting && QSystemTrayIcon::isSystemTrayAvailable() && settings.value("tray", false).toBool()) {
|
|
if (settings.value("hideTray", false).toBool()) {
|
|
createTrayIcon();
|
|
}
|
|
actionToggle.setText(tr("Show Squawk"));
|
|
} else {
|
|
quit();
|
|
}
|
|
}
|
|
|
|
void Application::onSquawkDestroyed() {
|
|
destroyingSquawk = false;
|
|
if (nowQuitting)
|
|
checkForTheLastWindow();
|
|
}
|
|
|
|
void Application::onChangeTray(bool enabled, bool hide) {
|
|
if (enabled) {
|
|
if (trayIcon == nullptr) {
|
|
if (!hide || squawk == nullptr)
|
|
createTrayIcon();
|
|
} else {
|
|
if (hide && squawk != nullptr) {
|
|
trayIcon->deleteLater();
|
|
trayIcon = nullptr;
|
|
}
|
|
}
|
|
} else if (trayIcon == nullptr) {
|
|
trayIcon->deleteLater();
|
|
trayIcon = nullptr;
|
|
}
|
|
}
|
|
|
|
void Application::createTrayIcon() {
|
|
trayIcon = new QSystemTrayIcon();
|
|
|
|
QMenu* trayIconMenu = new QMenu();
|
|
trayIconMenu->addAction(&actionToggle);
|
|
trayIconMenu->addAction(&actionQuit);
|
|
|
|
trayIcon->setContextMenu(trayIconMenu);
|
|
trayIcon->setIcon(QApplication::windowIcon().pixmap(32, 32));
|
|
trayIcon->setToolTip(QApplication::applicationDisplayName());
|
|
|
|
connect(trayIcon, &QSystemTrayIcon::activated, this, &Application::trayClicked);
|
|
connect(trayIcon, &QSystemTrayIcon::destroyed, trayIconMenu, &QMenu::deleteLater);
|
|
|
|
trayIcon->show();
|
|
}
|
|
|
|
void Application::trayClicked(QSystemTrayIcon::ActivationReason reason) {
|
|
switch (reason) {
|
|
case QSystemTrayIcon::Trigger:
|
|
case QSystemTrayIcon::DoubleClick:
|
|
case QSystemTrayIcon::MiddleClick:
|
|
toggleSquawk();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Application::toggleSquawk() {
|
|
QSettings settings;
|
|
if (squawk == nullptr) {
|
|
createMainWindow();
|
|
if (settings.value("hideTray", false).toBool()) {
|
|
trayIcon->deleteLater();
|
|
trayIcon = nullptr;
|
|
}
|
|
|
|
actionToggle.setText(tr("Minimize to tray"));
|
|
} else {
|
|
squawk->close();
|
|
}
|
|
}
|
|
|
|
void Application::onItemCollapsed(const QModelIndex& index) {
|
|
std::list<QString> address = roster.getItemPath(index);
|
|
if (address.size() > 0)
|
|
expandedPaths.erase(address);
|
|
}
|
|
|
|
void Application::onItemExpanded(const QModelIndex& index) {
|
|
std::list<QString> address = roster.getItemPath(index);
|
|
if (address.size() > 0)
|
|
expandedPaths.insert(address);
|
|
}
|
|
|
|
void Application::onAddedElement(const std::list<QString>& path) {
|
|
if (squawk != nullptr && expandedPaths.count(path) > 0) {
|
|
QModelIndex index = roster.getIndexByPath(path);
|
|
if (index.isValid())
|
|
squawk->expand(index);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
SHARED_UNUSED(messageId);
|
|
//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.beginGroup("roster");
|
|
QStringList entries = settings.allKeys();
|
|
for (const QString& entry : entries) {
|
|
QStringList p = entry.split("/");
|
|
if (p.last() == "expanded" && settings.value(entry, false).toBool()) {
|
|
p.pop_back();
|
|
expandedPaths.emplace(p.begin(), p.end());
|
|
}
|
|
}
|
|
|
|
settings.endGroup();
|
|
settings.endGroup();
|
|
|
|
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
|
createMainWindow();
|
|
|
|
if (settings.value("tray", false).toBool() && !settings.value("hideTray", false).toBool())
|
|
createTrayIcon();
|
|
}
|
|
|
|
void Application::writeSettings() {
|
|
QSettings settings;
|
|
settings.beginGroup("ui");
|
|
settings.setValue("availability", static_cast<int>(availability));
|
|
|
|
settings.remove("roster");
|
|
settings.beginGroup("roster");
|
|
for (const std::list<QString>& address : expandedPaths) {
|
|
QString path = "";
|
|
for (const QString& hop : address)
|
|
path += hop + "/";
|
|
|
|
path += "expanded";
|
|
settings.setValue(path, true);
|
|
}
|
|
|
|
settings.endGroup();
|
|
settings.endGroup();
|
|
}
|
|
|
|
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::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);}
|