merge conflicts, text copying from context menu in message line
This commit is contained in:
commit
c3a45ec58c
@ -4,6 +4,7 @@
|
|||||||
### Bug fixes
|
### Bug fixes
|
||||||
- now when you remove an account it actually gets removed
|
- now when you remove an account it actually gets removed
|
||||||
- segfault on unitialized Availability in some rare occesions
|
- segfault on unitialized Availability in some rare occesions
|
||||||
|
- fixed crash when you open a dialog with someone that has only error messages in archive
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- there is a way to disable an account and it wouldn't connect when you change availability
|
- there is a way to disable an account and it wouldn't connect when you change availability
|
||||||
@ -15,6 +16,8 @@
|
|||||||
### New features
|
### New features
|
||||||
- new "About" window with links, license, gratitudes
|
- new "About" window with links, license, gratitudes
|
||||||
- if the authentication failed Squawk will ask againg for your password and login
|
- if the authentication failed Squawk will ask againg for your password and login
|
||||||
|
- now there is an amount of unread messages showing on top of Squawk launcher icon
|
||||||
|
- notifications now have buttons to open a conversation or to mark that message as read
|
||||||
|
|
||||||
## Squawk 0.2.1 (Apr 02, 2022)
|
## Squawk 0.2.1 (Apr 02, 2022)
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
|
@ -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)
|
||||||
|
@ -8,13 +8,10 @@ target_sources(squawk PRIVATE
|
|||||||
account.h
|
account.h
|
||||||
adapterfunctions.cpp
|
adapterfunctions.cpp
|
||||||
adapterfunctions.h
|
adapterfunctions.h
|
||||||
archive.cpp
|
|
||||||
archive.h
|
|
||||||
conference.cpp
|
conference.cpp
|
||||||
conference.h
|
conference.h
|
||||||
contact.cpp
|
contact.cpp
|
||||||
contact.h
|
contact.h
|
||||||
main.cpp
|
|
||||||
networkaccess.cpp
|
networkaccess.cpp
|
||||||
networkaccess.h
|
networkaccess.h
|
||||||
rosteritem.cpp
|
rosteritem.cpp
|
||||||
@ -23,13 +20,10 @@ target_sources(squawk PRIVATE
|
|||||||
signalcatcher.h
|
signalcatcher.h
|
||||||
squawk.cpp
|
squawk.cpp
|
||||||
squawk.h
|
squawk.h
|
||||||
storage.cpp
|
|
||||||
storage.h
|
|
||||||
urlstorage.cpp
|
|
||||||
urlstorage.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_subdirectory(handlers)
|
add_subdirectory(handlers)
|
||||||
|
add_subdirectory(storage)
|
||||||
add_subdirectory(passwordStorageEngines)
|
add_subdirectory(passwordStorageEngines)
|
||||||
|
201
core/main.cpp
201
core/main.cpp
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "urlstorage.h"
|
#include "storage/urlstorage.h"
|
||||||
#include "shared/pathcheck.h"
|
#include "shared/pathcheck.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -124,6 +124,7 @@ void Core::RosterItem::nextRequest()
|
|||||||
if (requestedCount != -1) {
|
if (requestedCount != -1) {
|
||||||
bool last = false;
|
bool last = false;
|
||||||
if (archiveState == beginning || archiveState == complete) {
|
if (archiveState == beginning || archiveState == complete) {
|
||||||
|
try {
|
||||||
QString firstId = archive->oldestId();
|
QString firstId = archive->oldestId();
|
||||||
if (responseCache.size() == 0) {
|
if (responseCache.size() == 0) {
|
||||||
if (requestedBefore == firstId) {
|
if (requestedBefore == firstId) {
|
||||||
@ -134,6 +135,9 @@ void Core::RosterItem::nextRequest()
|
|||||||
last = true;
|
last = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (const Archive::Empty& e) {
|
||||||
|
last = true;
|
||||||
|
}
|
||||||
} else if (archiveState == empty && responseCache.size() == 0) {
|
} else if (archiveState == empty && responseCache.size() == 0) {
|
||||||
last = true;
|
last = true;
|
||||||
}
|
}
|
||||||
@ -171,8 +175,12 @@ void Core::RosterItem::performRequest(int count, const QString& before)
|
|||||||
requestCache.emplace_back(requestedCount, before);
|
requestCache.emplace_back(requestedCount, before);
|
||||||
requestedCount = -1;
|
requestedCount = -1;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
Shared::Message msg = archive->newest();
|
Shared::Message msg = archive->newest();
|
||||||
emit needHistory("", getId(msg), msg.getTime());
|
emit needHistory("", getId(msg), msg.getTime());
|
||||||
|
} catch (const Archive::Empty& e) { //this can happen when the only message in archive is not server stored (error, for example)
|
||||||
|
emit needHistory(before, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case end:
|
case end:
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
#include "shared/enums.h"
|
#include "shared/enums.h"
|
||||||
#include "shared/message.h"
|
#include "shared/message.h"
|
||||||
#include "shared/vcard.h"
|
#include "shared/vcard.h"
|
||||||
#include "archive.h"
|
#include "storage/archive.h"
|
||||||
#include "adapterfunctions.h"
|
#include "adapterfunctions.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
8
core/storage/CMakeLists.txt
Normal file
8
core/storage/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
archive.cpp
|
||||||
|
archive.h
|
||||||
|
storage.cpp
|
||||||
|
storage.h
|
||||||
|
urlstorage.cpp
|
||||||
|
urlstorage.h
|
||||||
|
)
|
7
main/CMakeLists.txt
Normal file
7
main/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
target_sources(squawk PRIVATE
|
||||||
|
main.cpp
|
||||||
|
application.cpp
|
||||||
|
application.h
|
||||||
|
dialogqueue.cpp
|
||||||
|
dialogqueue.h
|
||||||
|
)
|
564
main/application.cpp
Normal file
564
main/application.cpp
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
Application::Application(Core::Squawk* p_core):
|
||||||
|
QObject(),
|
||||||
|
availability(Shared::Availability::offline),
|
||||||
|
core(p_core),
|
||||||
|
squawk(nullptr),
|
||||||
|
notifications("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications"),
|
||||||
|
roster(),
|
||||||
|
conversations(),
|
||||||
|
dialogueQueue(roster),
|
||||||
|
nowQuitting(false),
|
||||||
|
destroyingSquawk(false),
|
||||||
|
storage()
|
||||||
|
{
|
||||||
|
connect(&roster, &Models::Roster::unnoticedMessage, this, &Application::notify);
|
||||||
|
connect(&roster, &Models::Roster::unreadMessagesCountChanged, this, &Application::unreadMessagesCountChanged);
|
||||||
|
|
||||||
|
|
||||||
|
//connecting myself to the backed
|
||||||
|
connect(this, &Application::changeState, core, &Core::Squawk::changeState);
|
||||||
|
connect(this, &Application::setRoomJoined, core, &Core::Squawk::setRoomJoined);
|
||||||
|
connect(this, &Application::setRoomAutoJoin, core, &Core::Squawk::setRoomAutoJoin);
|
||||||
|
connect(this, &Application::subscribeContact, core, &Core::Squawk::subscribeContact);
|
||||||
|
connect(this, &Application::unsubscribeContact, core, &Core::Squawk::unsubscribeContact);
|
||||||
|
connect(this, &Application::replaceMessage, core, &Core::Squawk::replaceMessage);
|
||||||
|
connect(this, &Application::sendMessage, core, &Core::Squawk::sendMessage);
|
||||||
|
connect(this, &Application::resendMessage, core, &Core::Squawk::resendMessage);
|
||||||
|
connect(&roster, &Models::Roster::requestArchive,
|
||||||
|
std::bind(&Core::Squawk::requestArchive, core, std::placeholders::_1, std::placeholders::_2, 20, std::placeholders::_3));
|
||||||
|
|
||||||
|
connect(&dialogueQueue, &DialogQueue::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::responsePassword, core, &Core::Squawk::responsePassword);
|
||||||
|
connect(&dialogueQueue, &DialogQueue::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(&roster, &Models::Roster::fileDownloadRequest, core, &Core::Squawk::fileDownloadRequest);
|
||||||
|
connect(&roster, &Models::Roster::localPathInvalid, core, &Core::Squawk::onLocalPathInvalid);
|
||||||
|
|
||||||
|
|
||||||
|
//coonecting backend to myself
|
||||||
|
connect(core, &Core::Squawk::stateChanged, this, &Application::stateChanged);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::accountMessage, &roster, &Models::Roster::addMessage);
|
||||||
|
connect(core, &Core::Squawk::responseArchive, &roster, &Models::Roster::responseArchive);
|
||||||
|
connect(core, &Core::Squawk::changeMessage, &roster, &Models::Roster::changeMessage);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::newAccount, &roster, &Models::Roster::addAccount);
|
||||||
|
connect(core, &Core::Squawk::changeAccount, this, &Application::changeAccount);
|
||||||
|
connect(core, &Core::Squawk::removeAccount, this, &Application::removeAccount);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addContact, this, &Application::addContact);
|
||||||
|
connect(core, &Core::Squawk::addGroup, this, &Application::addGroup);
|
||||||
|
connect(core, &Core::Squawk::removeGroup, &roster, &Models::Roster::removeGroup);
|
||||||
|
connect(core, qOverload<const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, qOverload<const QString&, const QString&, const QString&>(&Core::Squawk::removeContact),
|
||||||
|
&roster, qOverload<const QString&, const QString&, const QString&>(&Models::Roster::removeContact));
|
||||||
|
connect(core, &Core::Squawk::changeContact, &roster, &Models::Roster::changeContact);
|
||||||
|
connect(core, &Core::Squawk::addPresence, &roster, &Models::Roster::addPresence);
|
||||||
|
connect(core, &Core::Squawk::removePresence, &roster, &Models::Roster::removePresence);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::addRoom, &roster, &Models::Roster::addRoom);
|
||||||
|
connect(core, &Core::Squawk::changeRoom, &roster, &Models::Roster::changeRoom);
|
||||||
|
connect(core, &Core::Squawk::removeRoom, &roster, &Models::Roster::removeRoom);
|
||||||
|
connect(core, &Core::Squawk::addRoomParticipant, &roster, &Models::Roster::addRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::changeRoomParticipant, &roster, &Models::Roster::changeRoomParticipant);
|
||||||
|
connect(core, &Core::Squawk::removeRoomParticipant, &roster, &Models::Roster::removeRoomParticipant);
|
||||||
|
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::fileDownloadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, false));
|
||||||
|
connect(core, &Core::Squawk::fileUploadComplete, std::bind(&Models::Roster::fileComplete, &roster, std::placeholders::_1, true));
|
||||||
|
connect(core, &Core::Squawk::fileProgress, &roster, &Models::Roster::fileProgress);
|
||||||
|
connect(core, &Core::Squawk::fileError, &roster, &Models::Roster::fileError);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::requestPassword, this, &Application::requestPassword);
|
||||||
|
connect(core, &Core::Squawk::ready, this, &Application::readSettings);
|
||||||
|
|
||||||
|
QDBusConnection sys = QDBusConnection::sessionBus();
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"NotificationClosed",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationClosed(quint32, quint32))
|
||||||
|
);
|
||||||
|
sys.connect(
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"/org/freedesktop/Notifications",
|
||||||
|
"org.freedesktop.Notifications",
|
||||||
|
"ActionInvoked",
|
||||||
|
this,
|
||||||
|
SLOT(onNotificationInvoked(quint32, const QString&))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::~Application() {}
|
||||||
|
|
||||||
|
void Application::quit()
|
||||||
|
{
|
||||||
|
if (!nowQuitting) {
|
||||||
|
nowQuitting = true;
|
||||||
|
emit quitting();
|
||||||
|
|
||||||
|
writeSettings();
|
||||||
|
unreadMessagesCountChanged(0); //this notification persist in the desktop, for now I'll zero it on quit not to confuse people
|
||||||
|
for (Conversations::const_iterator itr = conversations.begin(), end = conversations.end(); itr != end; ++itr) {
|
||||||
|
disconnect(itr->second, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
itr->second->close();
|
||||||
|
}
|
||||||
|
conversations.clear();
|
||||||
|
dialogueQueue.quit();
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
squawk->close();
|
||||||
|
}
|
||||||
|
if (!destroyingSquawk) {
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::checkForTheLastWindow()
|
||||||
|
{
|
||||||
|
if (QApplication::topLevelWidgets().size() > 0) {
|
||||||
|
emit readyToQuit();
|
||||||
|
} else {
|
||||||
|
connect(qApp, &QApplication::lastWindowClosed, this, &Application::readyToQuit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::createMainWindow()
|
||||||
|
{
|
||||||
|
if (squawk == nullptr) {
|
||||||
|
squawk = new Squawk(roster);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::notify, this, &Application::notify);
|
||||||
|
connect(squawk, &Squawk::changeSubscription, this, &Application::changeSubscription);
|
||||||
|
connect(squawk, &Squawk::openedConversation, this, &Application::onSquawkOpenedConversation);
|
||||||
|
connect(squawk, &Squawk::openConversation, this, &Application::openConversation);
|
||||||
|
connect(squawk, &Squawk::changeState, this, &Application::setState);
|
||||||
|
connect(squawk, &Squawk::closing, this, &Application::onSquawkClosing);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::modifyAccountRequest, core, &Core::Squawk::modifyAccountRequest);
|
||||||
|
connect(squawk, &Squawk::newAccountRequest, core, &Core::Squawk::newAccountRequest);
|
||||||
|
connect(squawk, &Squawk::removeAccountRequest, core, &Core::Squawk::removeAccountRequest);
|
||||||
|
connect(squawk, &Squawk::connectAccount, core, &Core::Squawk::connectAccount);
|
||||||
|
connect(squawk, &Squawk::disconnectAccount, core, &Core::Squawk::disconnectAccount);
|
||||||
|
|
||||||
|
connect(squawk, &Squawk::addContactRequest, core, &Core::Squawk::addContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactRequest, core, &Core::Squawk::removeContactRequest);
|
||||||
|
connect(squawk, &Squawk::removeRoomRequest, core, &Core::Squawk::removeRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addRoomRequest, core, &Core::Squawk::addRoomRequest);
|
||||||
|
connect(squawk, &Squawk::addContactToGroupRequest, core, &Core::Squawk::addContactToGroupRequest);
|
||||||
|
connect(squawk, &Squawk::removeContactFromGroupRequest, core, &Core::Squawk::removeContactFromGroupRequest);
|
||||||
|
connect(squawk, &Squawk::renameContactRequest, core, &Core::Squawk::renameContactRequest);
|
||||||
|
connect(squawk, &Squawk::requestVCard, core, &Core::Squawk::requestVCard);
|
||||||
|
connect(squawk, &Squawk::uploadVCard, core, &Core::Squawk::uploadVCard);
|
||||||
|
connect(squawk, &Squawk::changeDownloadsPath, core, &Core::Squawk::changeDownloadsPath);
|
||||||
|
|
||||||
|
connect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
|
||||||
|
dialogueQueue.setParentWidnow(squawk);
|
||||||
|
squawk->stateChanged(availability);
|
||||||
|
squawk->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkClosing()
|
||||||
|
{
|
||||||
|
dialogueQueue.setParentWidnow(nullptr);
|
||||||
|
|
||||||
|
disconnect(core, &Core::Squawk::responseVCard, squawk, &Squawk::responseVCard);
|
||||||
|
|
||||||
|
destroyingSquawk = true;
|
||||||
|
squawk->deleteLater();
|
||||||
|
squawk = nullptr;
|
||||||
|
|
||||||
|
//for now
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkDestroyed() {
|
||||||
|
destroyingSquawk = false;
|
||||||
|
if (nowQuitting) {
|
||||||
|
checkForTheLastWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Application::notify(const QString& account, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
QString jid = msg.getPenPalJid();
|
||||||
|
QString name = QString(roster.getContactName(account, jid));
|
||||||
|
QString path = QString(roster.getContactIconPath(account, jid, msg.getPenPalResource()));
|
||||||
|
QVariantList args;
|
||||||
|
args << QString();
|
||||||
|
|
||||||
|
uint32_t notificationId = qHash(msg.getId());
|
||||||
|
args << notificationId;
|
||||||
|
if (path.size() > 0) {
|
||||||
|
args << path;
|
||||||
|
} else {
|
||||||
|
args << QString("mail-message"); //TODO should here better be unknown user icon?
|
||||||
|
}
|
||||||
|
if (msg.getType() == Shared::Message::groupChat) {
|
||||||
|
args << msg.getFromResource() + tr(" from ") + name;
|
||||||
|
} else {
|
||||||
|
args << name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString body(msg.getBody());
|
||||||
|
QString oob(msg.getOutOfBandUrl());
|
||||||
|
if (body == oob) {
|
||||||
|
body = tr("Attached file");
|
||||||
|
}
|
||||||
|
|
||||||
|
args << body;
|
||||||
|
args << QStringList({
|
||||||
|
"markAsRead", tr("Mark as Read"),
|
||||||
|
"openConversation", tr("Open conversation")
|
||||||
|
});
|
||||||
|
args << QVariantMap({
|
||||||
|
{"desktop-entry", qApp->desktopFileName()},
|
||||||
|
{"category", QString("message")},
|
||||||
|
{"urgency", 1},
|
||||||
|
// {"sound-file", "/path/to/macaw/squawk"},
|
||||||
|
{"sound-name", QString("message-new-instant")}
|
||||||
|
});
|
||||||
|
args << -1;
|
||||||
|
notifications.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
||||||
|
|
||||||
|
storage.insert(std::make_pair(notificationId, std::make_pair(Models::Roster::ElId(account, name), msg.getId())));
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QApplication::alert(squawk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationClosed(quint32 id, quint32 reason)
|
||||||
|
{
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (reason == 2) { //dissmissed by user (https://specifications.freedesktop.org/notification-spec/latest/ar01s09.html)
|
||||||
|
//TODO may ba also mark as read?
|
||||||
|
}
|
||||||
|
if (reason != 1) { //just expired, can be activated again from history, so no removing for now
|
||||||
|
storage.erase(id);
|
||||||
|
qDebug() << "Notification" << id << "was closed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onNotificationInvoked(quint32 id, const QString& action)
|
||||||
|
{
|
||||||
|
qDebug() << "Notification" << id << action << "request";
|
||||||
|
Notifications::const_iterator itr = storage.find(id);
|
||||||
|
if (itr != storage.end()) {
|
||||||
|
if (action == "markAsRead") {
|
||||||
|
roster.markMessageAsRead(itr->second.first, itr->second.second);
|
||||||
|
} else if (action == "openConversation") {
|
||||||
|
focusConversation(itr->second.first, "", itr->second.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::unreadMessagesCountChanged(int count)
|
||||||
|
{
|
||||||
|
QDBusMessage signal = QDBusMessage::createSignal("/", "com.canonical.Unity.LauncherEntry", "Update");
|
||||||
|
signal << qApp->desktopFileName() + QLatin1String(".desktop");
|
||||||
|
signal << QVariantMap ({
|
||||||
|
{"count-visible", count != 0},
|
||||||
|
{"count", count}
|
||||||
|
});
|
||||||
|
QDBusConnection::sessionBus().send(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::focusConversation(const Models::Roster::ElId& id, const QString& resource, const QString& messageId)
|
||||||
|
{
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
if (squawk->currentConversationId() != id) {
|
||||||
|
QModelIndex index = roster.getContactIndex(id.account, id.name, resource);
|
||||||
|
squawk->select(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk->isMinimized()) {
|
||||||
|
squawk->showNormal();
|
||||||
|
} else {
|
||||||
|
squawk->show();
|
||||||
|
}
|
||||||
|
squawk->raise();
|
||||||
|
squawk->activateWindow();
|
||||||
|
} else {
|
||||||
|
openConversation(id, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO focus messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::setState(Shared::Availability p_availability)
|
||||||
|
{
|
||||||
|
if (availability != p_availability) {
|
||||||
|
availability = p_availability;
|
||||||
|
emit changeState(availability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::stateChanged(Shared::Availability state)
|
||||||
|
{
|
||||||
|
availability = state;
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
squawk->stateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::readSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
int avail;
|
||||||
|
if (settings.contains("availability")) {
|
||||||
|
avail = settings.value("availability").toInt();
|
||||||
|
} else {
|
||||||
|
avail = static_cast<int>(Shared::Availability::online);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
|
||||||
|
setState(Shared::Global::fromInt<Shared::Availability>(avail));
|
||||||
|
createMainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::writeSettings()
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue("availability", static_cast<int>(availability));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::requestPassword(const QString& account, bool authenticationError) {
|
||||||
|
if (authenticationError) {
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askCredentials);
|
||||||
|
} else {
|
||||||
|
dialogueQueue.addAction(account, DialogQueue::askPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
void Application::onConversationClosed()
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
Models::Roster::ElId id(conv->getAccount(), conv->getJid());
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conversations.erase(itr);
|
||||||
|
}
|
||||||
|
if (conv->isMuc) {
|
||||||
|
Room* room = static_cast<Room*>(conv);
|
||||||
|
if (!room->autoJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeSubscription(const Models::Roster::ElId& id, bool subscribe)
|
||||||
|
{
|
||||||
|
Models::Item::Type type = roster.getContactType(id);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Models::Item::contact:
|
||||||
|
if (subscribe) {
|
||||||
|
emit subscribeContact(id.account, id.name, "");
|
||||||
|
} else {
|
||||||
|
emit unsubscribeContact(id.account, id.name, "");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Models::Item::room:
|
||||||
|
setRoomAutoJoin(id.account, id.name, subscribe);
|
||||||
|
if (!isConverstationOpened(id)) {
|
||||||
|
emit setRoomJoined(id.account, id.name, subscribe);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::subscribeConversation(Conversation* conv)
|
||||||
|
{
|
||||||
|
connect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
connect(conv, &Conversation::sendMessage, this, &Application::onConversationMessage);
|
||||||
|
connect(conv, &Conversation::replaceMessage, this, &Application::onConversationReplaceMessage);
|
||||||
|
connect(conv, &Conversation::resendMessage, this, &Application::onConversationResend);
|
||||||
|
connect(conv, &Conversation::notifyableMessage, this, &Application::notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::openConversation(const Models::Roster::ElId& id, const QString& resource)
|
||||||
|
{
|
||||||
|
Conversations::const_iterator itr = conversations.find(id);
|
||||||
|
Models::Account* acc = roster.getAccount(id.account);
|
||||||
|
Conversation* conv = nullptr;
|
||||||
|
bool created = false;
|
||||||
|
if (itr != conversations.end()) {
|
||||||
|
conv = itr->second;
|
||||||
|
} else {
|
||||||
|
Models::Element* el = roster.getElement(id);
|
||||||
|
if (el != nullptr) {
|
||||||
|
if (el->type == Models::Item::room) {
|
||||||
|
created = true;
|
||||||
|
Models::Room* room = static_cast<Models::Room*>(el);
|
||||||
|
conv = new Room(acc, room);
|
||||||
|
if (!room->getJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
} else if (el->type == Models::Item::contact) {
|
||||||
|
created = true;
|
||||||
|
conv = new Chat(acc, static_cast<Models::Contact*>(el));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conv != nullptr) {
|
||||||
|
if (created) {
|
||||||
|
conv->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
subscribeConversation(conv);
|
||||||
|
conversations.insert(std::make_pair(id, conv));
|
||||||
|
}
|
||||||
|
|
||||||
|
conv->show();
|
||||||
|
conv->raise();
|
||||||
|
conv->activateWindow();
|
||||||
|
|
||||||
|
if (resource.size() > 0) {
|
||||||
|
conv->setPalResource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationMessage(const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.addMessage(acc, msg);
|
||||||
|
emit sendMessage(acc, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
|
||||||
|
roster.changeMessage(acc, msg.getPenPalJid(), originalId, {
|
||||||
|
{"state", static_cast<uint>(Shared::Message::State::pending)}
|
||||||
|
});
|
||||||
|
emit replaceMessage(acc, originalId, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onConversationResend(const QString& id)
|
||||||
|
{
|
||||||
|
Conversation* conv = static_cast<Conversation*>(sender());
|
||||||
|
QString acc = conv->getAccount();
|
||||||
|
QString jid = conv->getJid();
|
||||||
|
|
||||||
|
emit resendMessage(acc, jid, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::onSquawkOpenedConversation() {
|
||||||
|
subscribeConversation(squawk->currentConversation);
|
||||||
|
Models::Roster::ElId id = squawk->currentConversationId();
|
||||||
|
|
||||||
|
const Models::Element* el = roster.getElementConst(id);
|
||||||
|
if (el != nullptr && el->isRoom() && !static_cast<const Models::Room*>(el)->getJoined()) {
|
||||||
|
emit setRoomJoined(id.account, id.name, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::removeAccount(const QString& account)
|
||||||
|
{
|
||||||
|
Conversations::const_iterator itr = conversations.begin();
|
||||||
|
while (itr != conversations.end()) {
|
||||||
|
if (itr->first.account == account) {
|
||||||
|
Conversations::const_iterator lItr = itr;
|
||||||
|
++itr;
|
||||||
|
Conversation* conv = lItr->second;
|
||||||
|
disconnect(conv, &Conversation::destroyed, this, &Application::onConversationClosed);
|
||||||
|
conv->close();
|
||||||
|
conversations.erase(lItr);
|
||||||
|
} else {
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (squawk != nullptr && squawk->currentConversationId().account == account) {
|
||||||
|
squawk->closeCurrentConversation();
|
||||||
|
}
|
||||||
|
|
||||||
|
roster.removeAccount(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::changeAccount(const QString& account, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
|
QString attr = itr.key();
|
||||||
|
roster.updateAccount(account, attr, *itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data)
|
||||||
|
{
|
||||||
|
roster.addContact(account, jid, group, data);
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = roster.getAccountIndex(account);
|
||||||
|
squawk->expand(ind);
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::addGroup(const QString& account, const QString& name)
|
||||||
|
{
|
||||||
|
roster.addGroup(account, name);
|
||||||
|
|
||||||
|
if (squawk != nullptr) {
|
||||||
|
QSettings settings;
|
||||||
|
settings.beginGroup("ui");
|
||||||
|
settings.beginGroup("roster");
|
||||||
|
settings.beginGroup(account);
|
||||||
|
if (settings.value("expanded", false).toBool()) {
|
||||||
|
QModelIndex ind = roster.getAccountIndex(account);
|
||||||
|
squawk->expand(ind);
|
||||||
|
if (settings.value(name + "/expanded", false).toBool()) {
|
||||||
|
squawk->expand(roster.getGroupIndex(account, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
settings.endGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::isConverstationOpened(const Models::Roster::ElId& id) const {
|
||||||
|
return (conversations.count(id) > 0) || (squawk != nullptr && squawk->currentConversationId() == id);}
|
118
main/application.h
Normal file
118
main/application.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// Squawk messenger.
|
||||||
|
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#ifndef APPLICATION_H
|
||||||
|
#define APPLICATION_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QDBusInterface>
|
||||||
|
|
||||||
|
#include "dialogqueue.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include "ui/squawk.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
#include "ui/widgets/conversation.h"
|
||||||
|
|
||||||
|
#include "shared/message.h"
|
||||||
|
#include "shared/enums.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo write docs
|
||||||
|
*/
|
||||||
|
class Application : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Application(Core::Squawk* core);
|
||||||
|
~Application();
|
||||||
|
|
||||||
|
bool isConverstationOpened(const Models::Roster::ElId& id) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sendMessage(const QString& account, const Shared::Message& data);
|
||||||
|
void replaceMessage(const QString& account, const QString& originalId, const Shared::Message& data);
|
||||||
|
void resendMessage(const QString& account, const QString& jid, const QString& id);
|
||||||
|
|
||||||
|
void changeState(Shared::Availability state);
|
||||||
|
|
||||||
|
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||||
|
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||||
|
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||||
|
|
||||||
|
void quitting();
|
||||||
|
void readyToQuit();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void readSettings();
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void notify(const QString& account, const Shared::Message& msg);
|
||||||
|
void unreadMessagesCountChanged(int count);
|
||||||
|
void setState(Shared::Availability availability);
|
||||||
|
|
||||||
|
void changeAccount(const QString& account, const QMap<QString, QVariant>& data);
|
||||||
|
void removeAccount(const QString& account);
|
||||||
|
void openConversation(const Models::Roster::ElId& id, const QString& resource = "");
|
||||||
|
|
||||||
|
void addGroup(const QString& account, const QString& name);
|
||||||
|
void addContact(const QString& account, const QString& jid, const QString& group, const QMap<QString, QVariant>& data);
|
||||||
|
|
||||||
|
void requestPassword(const QString& account, bool authenticationError);
|
||||||
|
|
||||||
|
void writeSettings();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConversationClosed();
|
||||||
|
void changeSubscription(const Models::Roster::ElId& id, bool subscribe);
|
||||||
|
void onSquawkOpenedConversation();
|
||||||
|
void onConversationMessage(const Shared::Message& msg);
|
||||||
|
void onConversationReplaceMessage(const QString& originalId, const Shared::Message& msg);
|
||||||
|
void onConversationResend(const QString& id);
|
||||||
|
void stateChanged(Shared::Availability state);
|
||||||
|
void onSquawkClosing();
|
||||||
|
void onSquawkDestroyed();
|
||||||
|
void onNotificationClosed(quint32 id, quint32 reason);
|
||||||
|
void onNotificationInvoked(quint32 id, const QString& action);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createMainWindow();
|
||||||
|
void subscribeConversation(Conversation* conv);
|
||||||
|
void checkForTheLastWindow();
|
||||||
|
void focusConversation(const Models::Roster::ElId& id, const QString& resource = "", const QString& messageId = "");
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::map<Models::Roster::ElId, Conversation*> Conversations;
|
||||||
|
typedef std::map<uint32_t, std::pair<Models::Roster::ElId, QString>> Notifications;
|
||||||
|
|
||||||
|
Shared::Availability availability;
|
||||||
|
Core::Squawk* core;
|
||||||
|
Squawk* squawk;
|
||||||
|
QDBusInterface notifications;
|
||||||
|
Models::Roster roster;
|
||||||
|
Conversations conversations;
|
||||||
|
DialogQueue dialogueQueue;
|
||||||
|
bool nowQuitting;
|
||||||
|
bool destroyingSquawk;
|
||||||
|
Notifications storage;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // APPLICATION_H
|
@ -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();
|
@ -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
|
140
main/main.cpp
Normal file
140
main/main.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Squawk messenger.
|
||||||
|
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shared/global.h"
|
||||||
|
#include "shared/messageinfo.h"
|
||||||
|
#include "shared/pathcheck.h"
|
||||||
|
#include "main/application.h"
|
||||||
|
#include "core/signalcatcher.h"
|
||||||
|
#include "core/squawk.h"
|
||||||
|
|
||||||
|
#include <QLibraryInfo>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtWidgets/QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Shared::Message>("Shared::Message");
|
||||||
|
qRegisterMetaType<Shared::MessageInfo>("Shared::MessageInfo");
|
||||||
|
qRegisterMetaType<Shared::VCard>("Shared::VCard");
|
||||||
|
qRegisterMetaType<std::list<Shared::Message>>("std::list<Shared::Message>");
|
||||||
|
qRegisterMetaType<std::list<Shared::MessageInfo>>("std::list<Shared::MessageInfo>");
|
||||||
|
qRegisterMetaType<QSet<QString>>("QSet<QString>");
|
||||||
|
qRegisterMetaType<Shared::ConnectionState>("Shared::ConnectionState");
|
||||||
|
qRegisterMetaType<Shared::Availability>("Shared::Availability");
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
SignalCatcher sc(&app);
|
||||||
|
|
||||||
|
QApplication::setApplicationName("squawk");
|
||||||
|
QApplication::setOrganizationName("macaw.me");
|
||||||
|
QApplication::setApplicationDisplayName("Squawk");
|
||||||
|
QApplication::setApplicationVersion("0.2.2");
|
||||||
|
app.setDesktopFileName("squawk");
|
||||||
|
|
||||||
|
QTranslator qtTranslator;
|
||||||
|
qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||||
|
app.installTranslator(&qtTranslator);
|
||||||
|
|
||||||
|
QTranslator myappTranslator;
|
||||||
|
QStringList shares = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
||||||
|
bool found = false;
|
||||||
|
for (QString share : shares) {
|
||||||
|
found = myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", share + "/l10n");
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
myappTranslator.load(QLocale(), QLatin1String("squawk"), ".", QCoreApplication::applicationDirPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.installTranslator(&myappTranslator);
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
icon.addFile(":images/logo.svg", QSize(16, 16));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(24, 24));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(32, 32));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(48, 48));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(64, 64));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(96, 96));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(128, 128));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(256, 256));
|
||||||
|
icon.addFile(":images/logo.svg", QSize(512, 512));
|
||||||
|
QApplication::setWindowIcon(icon);
|
||||||
|
|
||||||
|
new Shared::Global(); //translates enums
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
QVariant vs = settings.value("style");
|
||||||
|
if (vs.isValid()) {
|
||||||
|
QString style = vs.toString().toLower();
|
||||||
|
if (style != "system") {
|
||||||
|
Shared::Global::setStyle(style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Shared::Global::supported("colorSchemeTools")) {
|
||||||
|
QVariant vt = settings.value("theme");
|
||||||
|
if (vt.isValid()) {
|
||||||
|
QString theme = vt.toString();
|
||||||
|
if (theme.toLower() != "system") {
|
||||||
|
Shared::Global::setTheme(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QString path = Shared::downloadsPathCheck();
|
||||||
|
if (path.size() > 0) {
|
||||||
|
settings.setValue("downloadsPath", path);
|
||||||
|
} else {
|
||||||
|
qDebug() << "couldn't initialize directory for downloads, quitting";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Squawk* squawk = new Core::Squawk();
|
||||||
|
QThread* coreThread = new QThread();
|
||||||
|
squawk->moveToThread(coreThread);
|
||||||
|
|
||||||
|
Application application(squawk);
|
||||||
|
|
||||||
|
QObject::connect(&sc, &SignalCatcher::interrupt, &application, &Application::quit);
|
||||||
|
|
||||||
|
QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start);
|
||||||
|
QObject::connect(&application, &Application::quitting, squawk, &Core::Squawk::stop);
|
||||||
|
//QObject::connect(&app, &QApplication::aboutToQuit, &w, &QMainWindow::close);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::quit, squawk, &Core::Squawk::deleteLater);
|
||||||
|
QObject::connect(squawk, &Core::Squawk::destroyed, coreThread, &QThread::quit, Qt::QueuedConnection);
|
||||||
|
QObject::connect(coreThread, &QThread::finished, &app, &QApplication::quit, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
coreThread->start();
|
||||||
|
int result = app.exec();
|
||||||
|
|
||||||
|
if (coreThread->isRunning()) {
|
||||||
|
//coreThread->wait();
|
||||||
|
//todo if I uncomment that, the app will not quit if it has reconnected at least once
|
||||||
|
//it feels like a symptom of something badly desinged in the core thread
|
||||||
|
//need to investigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@
|
|||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
#include "enums.h"
|
#include "enums.h"
|
||||||
|
#include "ui/models/roster.h"
|
||||||
|
|
||||||
Shared::Global* Shared::Global::instance = 0;
|
Shared::Global* Shared::Global::instance = 0;
|
||||||
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
const std::set<QString> Shared::Global::supportedImagesExts = {"png", "jpg", "webp", "jpeg", "gif", "svg"};
|
||||||
|
@ -47,7 +47,6 @@ namespace Shared {
|
|||||||
|
|
||||||
class Global {
|
class Global {
|
||||||
Q_DECLARE_TR_FUNCTIONS(Global)
|
Q_DECLARE_TR_FUNCTIONS(Global)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct FileInfo {
|
struct FileInfo {
|
||||||
enum class Preview {
|
enum class Preview {
|
||||||
|
@ -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)
|
||||||
|
@ -155,6 +155,16 @@ void Models::Contact::removePresence(const QString& name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Presence * Models::Contact::getPresence(const QString& name)
|
||||||
|
{
|
||||||
|
QMap<QString, Presence*>::iterator itr = presences.find(name);
|
||||||
|
if (itr == presences.end()) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return itr.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Contact::refresh()
|
void Models::Contact::refresh()
|
||||||
{
|
{
|
||||||
QDateTime lastActivity;
|
QDateTime lastActivity;
|
||||||
|
@ -51,6 +51,7 @@ public:
|
|||||||
|
|
||||||
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
void addPresence(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removePresence(const QString& name);
|
void removePresence(const QString& name);
|
||||||
|
Presence* getPresence(const QString& name);
|
||||||
|
|
||||||
QString getContactName() const;
|
QString getContactName() const;
|
||||||
QString getStatus() const;
|
QString getStatus() const;
|
||||||
|
@ -134,6 +134,11 @@ unsigned int Models::Element::getMessagesCount() const
|
|||||||
return feed->unreadMessagesCount();
|
return feed->unreadMessagesCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::Element::markMessageAsRead(const QString& id) const
|
||||||
|
{
|
||||||
|
return feed->markMessageAsRead(id);
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Element::addMessage(const Shared::Message& data)
|
void Models::Element::addMessage(const Shared::Message& data)
|
||||||
{
|
{
|
||||||
feed->addMessage(data);
|
feed->addMessage(data);
|
||||||
@ -171,6 +176,7 @@ void Models::Element::fileError(const QString& messageId, const QString& error,
|
|||||||
|
|
||||||
void Models::Element::onFeedUnreadMessagesCountChanged()
|
void Models::Element::onFeedUnreadMessagesCountChanged()
|
||||||
{
|
{
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
if (type == contact) {
|
if (type == contact) {
|
||||||
changed(4);
|
changed(4);
|
||||||
} else if (type == room) {
|
} else if (type == room) {
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
void addMessage(const Shared::Message& data);
|
void addMessage(const Shared::Message& data);
|
||||||
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
void changeMessage(const QString& id, const QMap<QString, QVariant>& data);
|
||||||
unsigned int getMessagesCount() const;
|
unsigned int getMessagesCount() const;
|
||||||
|
bool markMessageAsRead(const QString& id) const;
|
||||||
void responseArchive(const std::list<Shared::Message> list, bool last);
|
void responseArchive(const std::list<Shared::Message> list, bool last);
|
||||||
bool isRoom() const;
|
bool isRoom() const;
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
@ -52,6 +53,7 @@ signals:
|
|||||||
void requestArchive(const QString& before);
|
void requestArchive(const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
|
void unreadMessagesCountChanged();
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -264,6 +264,16 @@ void Models::Room::removeParticipant(const QString& p_name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Participant * Models::Room::getParticipant(const QString& p_name)
|
||||||
|
{
|
||||||
|
std::map<QString, Participant*>::const_iterator itr = participants.find(p_name);
|
||||||
|
if (itr == participants.end()) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
void Models::Room::handleParticipantUpdate(std::map<QString, Participant*>::const_iterator itr, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Participant* part = itr->second;
|
Participant* part = itr->second;
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void addParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeParticipant(const QString& name);
|
void removeParticipant(const QString& name);
|
||||||
|
Participant* getParticipant(const QString& name);
|
||||||
|
|
||||||
void toOfflineState() override;
|
void toOfflineState() override;
|
||||||
QString getDisplayedName() const override;
|
QString getDisplayedName() const override;
|
||||||
|
@ -463,6 +463,7 @@ void Models::Roster::addContact(const QString& account, const QString& jid, cons
|
|||||||
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(contact, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(contact, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(contact, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
|
connect(contact, &Contact::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
||||||
contacts.insert(std::make_pair(id, contact));
|
contacts.insert(std::make_pair(id, contact));
|
||||||
} else {
|
} else {
|
||||||
contact = itr->second;
|
contact = itr->second;
|
||||||
@ -548,8 +549,8 @@ void Models::Roster::removeGroup(const QString& account, const QString& name)
|
|||||||
|
|
||||||
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeContact(const QString& account, const QString& jid, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, jid});
|
Element* el = getElement(ElId(account, jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
for (QMap<QString, QVariant>::const_iterator itr = data.begin(), end = data.end(); itr != end; ++itr) {
|
||||||
el->update(itr.key(), itr.value());
|
el->update(itr.key(), itr.value());
|
||||||
}
|
}
|
||||||
@ -558,8 +559,8 @@ void Models::Roster::changeContact(const QString& account, const QString& jid, c
|
|||||||
|
|
||||||
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
void Models::Roster::changeMessage(const QString& account, const QString& jid, const QString& id, const QMap<QString, QVariant>& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, jid});
|
Element* el = getElement(ElId(account, jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->changeMessage(id, data);
|
el->changeMessage(id, data);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found";
|
qDebug() << "A request to change a message of the contact " << jid << " in the account " << account << " but it wasn't found";
|
||||||
@ -706,8 +707,8 @@ void Models::Roster::removePresence(const QString& account, const QString& jid,
|
|||||||
|
|
||||||
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
void Models::Roster::addMessage(const QString& account, const Shared::Message& data)
|
||||||
{
|
{
|
||||||
Element* el = getElement({account, data.getPenPalJid()});
|
Element* el = getElement(ElId(account, data.getPenPalJid()));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->addMessage(data);
|
el->addMessage(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -763,7 +764,7 @@ void Models::Roster::removeAccount(const QString& account)
|
|||||||
acc->deleteLater();
|
acc->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactName(const QString& account, const QString& jid)
|
QString Models::Roster::getContactName(const QString& account, const QString& jid) const
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -801,10 +802,11 @@ void Models::Roster::addRoom(const QString& account, const QString jid, const QM
|
|||||||
}
|
}
|
||||||
|
|
||||||
Room* room = new Room(acc, jid, data);
|
Room* room = new Room(acc, jid, data);
|
||||||
connect(room, &Contact::requestArchive, this, &Roster::onElementRequestArchive);
|
connect(room, &Room::requestArchive, this, &Roster::onElementRequestArchive);
|
||||||
connect(room, &Contact::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
connect(room, &Room::fileDownloadRequest, this, &Roster::fileDownloadRequest);
|
||||||
connect(room, &Contact::unnoticedMessage, this, &Roster::unnoticedMessage);
|
connect(room, &Room::unnoticedMessage, this, &Roster::unnoticedMessage);
|
||||||
connect(room, &Contact::localPathInvalid, this, &Roster::localPathInvalid);
|
connect(room, &Room::localPathInvalid, this, &Roster::localPathInvalid);
|
||||||
|
connect(room, &Room::unreadMessagesCountChanged, this, &Roster::recalculateUnreadMessages);
|
||||||
rooms.insert(std::make_pair(id, room));
|
rooms.insert(std::make_pair(id, room));
|
||||||
acc->appendChild(room);
|
acc->appendChild(room);
|
||||||
}
|
}
|
||||||
@ -907,7 +909,7 @@ bool Models::Roster::groupHasContact(const QString& account, const QString& grou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource)
|
QString Models::Roster::getContactIconPath(const QString& account, const QString& jid, const QString& resource) const
|
||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
@ -927,9 +929,36 @@ QString Models::Roster::getContactIconPath(const QString& account, const QString
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Account * Models::Roster::getAccount(const QString& name)
|
Models::Account * Models::Roster::getAccount(const QString& name) {
|
||||||
|
return const_cast<Models::Account*>(getAccountConst(name));}
|
||||||
|
|
||||||
|
const Models::Account * Models::Roster::getAccountConst(const QString& name) const {
|
||||||
|
return accounts.at(name);}
|
||||||
|
|
||||||
|
const Models::Element * Models::Roster::getElementConst(const Models::Roster::ElId& id) const
|
||||||
{
|
{
|
||||||
return accounts.find(name)->second;
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(id);
|
||||||
|
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
return cItr->second;
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(id);
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
return rItr->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Models::Roster::markMessageAsRead(const Models::Roster::ElId& elementId, const QString& messageId)
|
||||||
|
{
|
||||||
|
const Element* el = getElementConst(elementId);
|
||||||
|
if (el != nullptr) {
|
||||||
|
return el->markMessageAsRead(messageId);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
QModelIndex Models::Roster::getAccountIndex(const QString& name)
|
||||||
@ -948,7 +977,7 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
if (itr == accounts.end()) {
|
if (itr == accounts.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
std::map<ElId, Group*>::const_iterator gItr = groups.find({account, name});
|
std::map<ElId, Group*>::const_iterator gItr = groups.find(ElId(account, name));
|
||||||
if (gItr == groups.end()) {
|
if (gItr == groups.end()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
} else {
|
} else {
|
||||||
@ -958,6 +987,48 @@ QModelIndex Models::Roster::getGroupIndex(const QString& account, const QString&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QModelIndex Models::Roster::getContactIndex(const QString& account, const QString& jid, const QString& resource)
|
||||||
|
{
|
||||||
|
std::map<QString, Account*>::const_iterator itr = accounts.find(account);
|
||||||
|
if (itr == accounts.end()) {
|
||||||
|
return QModelIndex();
|
||||||
|
} else {
|
||||||
|
Account* acc = itr->second;
|
||||||
|
QModelIndex accIndex = index(acc->row(), 0, QModelIndex());
|
||||||
|
std::map<ElId, Contact*>::const_iterator cItr = contacts.find(ElId(account, jid));
|
||||||
|
if (cItr != contacts.end()) {
|
||||||
|
QModelIndex contactIndex = index(acc->getContact(jid), 0, accIndex);
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
return contactIndex;
|
||||||
|
} else {
|
||||||
|
Presence* pres = cItr->second->getPresence(resource);
|
||||||
|
if (pres != nullptr) {
|
||||||
|
return index(pres->row(), 0, contactIndex);
|
||||||
|
} else {
|
||||||
|
return contactIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::map<ElId, Room*>::const_iterator rItr = rooms.find(ElId(account, jid));
|
||||||
|
if (rItr != rooms.end()) {
|
||||||
|
QModelIndex roomIndex = index(rItr->second->row(), 0, accIndex);
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
return roomIndex;
|
||||||
|
} else {
|
||||||
|
Participant* part = rItr->second->getParticipant(resource);
|
||||||
|
if (part != nullptr) {
|
||||||
|
return index(part->row(), 0, roomIndex);
|
||||||
|
} else {
|
||||||
|
return roomIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Models::Roster::onElementRequestArchive(const QString& before)
|
void Models::Roster::onElementRequestArchive(const QString& before)
|
||||||
{
|
{
|
||||||
Element* el = static_cast<Element*>(sender());
|
Element* el = static_cast<Element*>(sender());
|
||||||
@ -968,7 +1039,7 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
{
|
{
|
||||||
ElId id(account, jid);
|
ElId id(account, jid);
|
||||||
Element* el = getElement(id);
|
Element* el = getElement(id);
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->responseArchive(list, last);
|
el->responseArchive(list, last);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -976,8 +1047,8 @@ void Models::Roster::responseArchive(const QString& account, const QString& jid,
|
|||||||
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileProgress(info.messageId, value, up);
|
el->fileProgress(info.messageId, value, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -986,8 +1057,8 @@ void Models::Roster::fileProgress(const std::list<Shared::MessageInfo>& msgs, qr
|
|||||||
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileComplete(info.messageId, up);
|
el->fileComplete(info.messageId, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -996,8 +1067,8 @@ void Models::Roster::fileComplete(const std::list<Shared::MessageInfo>& msgs, bo
|
|||||||
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const QString& err, bool up)
|
||||||
{
|
{
|
||||||
for (const Shared::MessageInfo& info : msgs) {
|
for (const Shared::MessageInfo& info : msgs) {
|
||||||
Element* el = getElement({info.account, info.jid});
|
Element* el = getElement(ElId(info.account, info.jid));
|
||||||
if (el != NULL) {
|
if (el != nullptr) {
|
||||||
el->fileError(info.messageId, err, up);
|
el->fileError(info.messageId, err, up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1005,20 +1076,20 @@ void Models::Roster::fileError(const std::list<Shared::MessageInfo>& msgs, const
|
|||||||
|
|
||||||
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
Models::Element * Models::Roster::getElement(const Models::Roster::ElId& id)
|
||||||
{
|
{
|
||||||
std::map<ElId, Contact*>::iterator cItr = contacts.find(id);
|
return const_cast<Models::Element*>(getElementConst(id));
|
||||||
|
|
||||||
if (cItr != contacts.end()) {
|
|
||||||
return cItr->second;
|
|
||||||
} else {
|
|
||||||
std::map<ElId, Room*>::iterator rItr = rooms.find(id);
|
|
||||||
if (rItr != rooms.end()) {
|
|
||||||
return rItr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Models::Item::Type Models::Roster::getContactType(const Models::Roster::ElId& id) const
|
||||||
|
{
|
||||||
|
const Models::Element* el = getElementConst(id);
|
||||||
|
if (el == nullptr) {
|
||||||
|
return Item::root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return el->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Models::Roster::onAccountReconnected()
|
void Models::Roster::onAccountReconnected()
|
||||||
{
|
{
|
||||||
Account* acc = static_cast<Account*>(sender());
|
Account* acc = static_cast<Account*>(sender());
|
||||||
@ -1031,3 +1102,14 @@ void Models::Roster::onAccountReconnected()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Models::Roster::recalculateUnreadMessages()
|
||||||
|
{
|
||||||
|
int count(0);
|
||||||
|
for (const std::pair<const ElId, Contact*>& pair : contacts) {
|
||||||
|
count += pair.second->getMessagesCount();
|
||||||
|
}
|
||||||
|
for (const std::pair<const ElId, Room*>& pair : rooms) {
|
||||||
|
count += pair.second->getMessagesCount();
|
||||||
|
}
|
||||||
|
emit unreadMessagesCountChanged(count);
|
||||||
|
}
|
||||||
|
@ -46,6 +46,7 @@ public:
|
|||||||
Roster(QObject* parent = 0);
|
Roster(QObject* parent = 0);
|
||||||
~Roster();
|
~Roster();
|
||||||
|
|
||||||
|
public slots:
|
||||||
void addAccount(const QMap<QString, QVariant> &data);
|
void addAccount(const QMap<QString, QVariant> &data);
|
||||||
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
void updateAccount(const QString& account, const QString& field, const QVariant& value);
|
||||||
void removeAccount(const QString& account);
|
void removeAccount(const QString& account);
|
||||||
@ -65,7 +66,12 @@ public:
|
|||||||
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void addRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap<QString, QVariant>& data);
|
||||||
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
void removeRoomParticipant(const QString& account, const QString& jid, const QString& name);
|
||||||
QString getContactName(const QString& account, const QString& jid);
|
|
||||||
|
public:
|
||||||
|
QString getContactName(const QString& account, const QString& jid) const;
|
||||||
|
Item::Type getContactType(const Models::Roster::ElId& id) const;
|
||||||
|
const Element* getElementConst(const ElId& id) const;
|
||||||
|
Element* getElement(const ElId& id);
|
||||||
|
|
||||||
QVariant data ( const QModelIndex& index, int role ) const override;
|
QVariant data ( const QModelIndex& index, int role ) const override;
|
||||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||||
@ -77,10 +83,13 @@ public:
|
|||||||
|
|
||||||
std::deque<QString> groupList(const QString& account) const;
|
std::deque<QString> groupList(const QString& account) const;
|
||||||
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
bool groupHasContact(const QString& account, const QString& group, const QString& contactJID) const;
|
||||||
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource);
|
QString getContactIconPath(const QString& account, const QString& jid, const QString& resource) const;
|
||||||
Account* getAccount(const QString& name);
|
Account* getAccount(const QString& name);
|
||||||
|
const Account* getAccountConst(const QString& name) const;
|
||||||
QModelIndex getAccountIndex(const QString& name);
|
QModelIndex getAccountIndex(const QString& name);
|
||||||
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
QModelIndex getGroupIndex(const QString& account, const QString& name);
|
||||||
|
QModelIndex getContactIndex(const QString& account, const QString& jid, const QString& resource = "");
|
||||||
|
bool markMessageAsRead(const ElId& elementId, const QString& messageId);
|
||||||
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
void responseArchive(const QString& account, const QString& jid, const std::list<Shared::Message>& list, bool last);
|
||||||
|
|
||||||
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
void fileProgress(const std::list<Shared::MessageInfo>& msgs, qreal value, bool up);
|
||||||
@ -92,12 +101,10 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
void requestArchive(const QString& account, const QString& jid, const QString& before);
|
||||||
void fileDownloadRequest(const QString& url);
|
void fileDownloadRequest(const QString& url);
|
||||||
|
void unreadMessagesCountChanged(int count);
|
||||||
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
void unnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||||
void localPathInvalid(const QString& path);
|
void localPathInvalid(const QString& path);
|
||||||
|
|
||||||
private:
|
|
||||||
Element* getElement(const ElId& id);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
void onAccountDataChanged(const QModelIndex& tl, const QModelIndex& br, const QVector<int>& roles);
|
||||||
void onAccountReconnected();
|
void onAccountReconnected();
|
||||||
@ -109,6 +116,7 @@ private slots:
|
|||||||
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
void onChildIsAboutToBeMoved(Item* source, int first, int last, Item* destination, int newIndex);
|
||||||
void onChildMoved();
|
void onChildMoved();
|
||||||
void onElementRequestArchive(const QString& before);
|
void onElementRequestArchive(const QString& before);
|
||||||
|
void recalculateUnreadMessages();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Item* root;
|
Item* root;
|
||||||
|
487
ui/squawk.cpp
487
ui/squawk.cpp
@ -21,17 +21,14 @@
|
|||||||
#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(),
|
|
||||||
conversations(),
|
|
||||||
contextMenu(new QMenu()),
|
contextMenu(new QMenu()),
|
||||||
dbus("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus()),
|
|
||||||
vCards(),
|
vCards(),
|
||||||
currentConversation(nullptr),
|
currentConversation(nullptr),
|
||||||
restoreSelection(),
|
restoreSelection(),
|
||||||
@ -65,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);
|
||||||
@ -200,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)
|
||||||
{
|
{
|
||||||
@ -237,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)
|
||||||
{
|
{
|
||||||
@ -326,212 +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)
|
|
||||||
{
|
|
||||||
QString name = QString(rosterModel.getContactName(account, msg.getPenPalJid()));
|
|
||||||
QString path = QString(rosterModel.getContactIconPath(account, msg.getPenPalJid(), msg.getPenPalResource()));
|
|
||||||
QVariantList args;
|
|
||||||
args << QString(QCoreApplication::applicationName());
|
|
||||||
args << QVariant(QVariant::UInt); //TODO some normal id
|
|
||||||
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();
|
|
||||||
args << 3000;
|
|
||||||
dbus.callWithArgumentList(QDBus::AutoDetect, "Notify", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
||||||
@ -569,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) {
|
||||||
@ -583,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:
|
||||||
@ -593,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;
|
||||||
@ -680,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:
|
||||||
@ -717,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);
|
||||||
@ -804,22 +478,6 @@ 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;
|
||||||
@ -830,9 +488,6 @@ void Squawk::writeSettings()
|
|||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.setValue("splitter", m_ui->splitter->saveState());
|
settings.setValue("splitter", m_ui->splitter->saveState());
|
||||||
|
|
||||||
settings.setValue("availability", m_ui->comboBox->currentIndex());
|
|
||||||
|
|
||||||
settings.remove("roster");
|
settings.remove("roster");
|
||||||
settings.beginGroup("roster");
|
settings.beginGroup("roster");
|
||||||
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
int size = rosterModel.accountsModel->rowCount(QModelIndex());
|
||||||
@ -857,8 +512,6 @@ void Squawk::writeSettings()
|
|||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
|
||||||
settings.sync();
|
settings.sync();
|
||||||
|
|
||||||
qDebug() << "Saved settings";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Squawk::onItemCollepsed(const QModelIndex& index)
|
void Squawk::onItemCollepsed(const QModelIndex& index)
|
||||||
@ -880,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) {
|
||||||
@ -969,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);
|
||||||
@ -988,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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1024,7 +647,17 @@ 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Squawk::select(QModelIndex index)
|
||||||
|
{
|
||||||
|
m_ui->roster->scrollTo(index, QAbstractItemView::EnsureVisible);
|
||||||
|
m_ui->roster->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
|
||||||
}
|
}
|
||||||
|
80
ui/squawk.h
80
ui/squawk.h
@ -22,7 +22,6 @@
|
|||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QtDBus/QDBusInterface>
|
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
|
|
||||||
@ -40,94 +39,67 @@
|
|||||||
#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"
|
||||||
|
|
||||||
namespace Ui {
|
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 select(QModelIndex index);
|
||||||
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;
|
||||||
QDBusInterface dbus;
|
|
||||||
std::map<QString, VCard*> vCards;
|
std::map<QString, VCard*> vCards;
|
||||||
Conversation* currentConversation;
|
Conversation* currentConversation;
|
||||||
QModelIndex restoreSelection;
|
QModelIndex restoreSelection;
|
||||||
@ -135,9 +107,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();
|
||||||
@ -149,27 +119,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
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QAbstractTextDocumentLayout>
|
#include <QAbstractTextDocumentLayout>
|
||||||
#include <QCoreApplication>
|
#include <QApplication>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@ -499,6 +499,16 @@ void Conversation::onFeedContext(const QPoint& pos)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString body = item->getBody();
|
||||||
|
if (body.size() > 0) {
|
||||||
|
showMenu = true;
|
||||||
|
QAction* copy = contextMenu->addAction(Shared::icon("edit-copy"), tr("Copy message"));
|
||||||
|
connect(copy, &QAction::triggered, [body] () {
|
||||||
|
QClipboard* cb = QApplication::clipboard();
|
||||||
|
cb->setText(body);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QString path = Shared::resolvePath(item->getAttachPath());
|
QString path = Shared::resolvePath(item->getAttachPath());
|
||||||
if (path.size() > 0) {
|
if (path.size() > 0) {
|
||||||
showMenu = true;
|
showMenu = true;
|
||||||
|
@ -664,9 +664,32 @@ int MessageDelegate::paintBody(const Models::FeedItem& data, QPainter* painter,
|
|||||||
if (data.text.size() > 0) {
|
if (data.text.size() > 0) {
|
||||||
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
bodyRenderer->setHtml(Shared::processMessageBody(data.text));
|
||||||
bodyRenderer->setTextWidth(option.rect.size().width());
|
bodyRenderer->setTextWidth(option.rect.size().width());
|
||||||
|
painter->setBackgroundMode(Qt::BGMode::OpaqueMode);
|
||||||
painter->save();
|
painter->save();
|
||||||
|
// QTextCursor cursor(bodyRenderer);
|
||||||
|
// cursor.setPosition(2, QTextCursor::KeepAnchor);
|
||||||
painter->translate(option.rect.topLeft());
|
painter->translate(option.rect.topLeft());
|
||||||
|
// QTextFrameFormat format = bodyRenderer->rootFrame()->frameFormat();
|
||||||
|
// format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight));
|
||||||
|
// bodyRenderer->rootFrame()->setFrameFormat(format);
|
||||||
bodyRenderer->drawContents(painter);
|
bodyRenderer->drawContents(painter);
|
||||||
|
// QColor c = option.palette.color(QPalette::Active, QPalette::Highlight);
|
||||||
|
// QTextBlock b = bodyRenderer->begin();
|
||||||
|
// QTextBlockFormat format = b.blockFormat();
|
||||||
|
// format.setBackground(option.palette.brush(QPalette::Active, QPalette::Highlight));
|
||||||
|
// format.setProperty(QTextFormat::BackgroundBrush, option.palette.brush(QPalette::Active, QPalette::Highlight));
|
||||||
|
// QTextCursor cursor(bodyRenderer);
|
||||||
|
// cursor.setBlockFormat(format);
|
||||||
|
// b = bodyRenderer->begin();
|
||||||
|
// while (b.isValid() > 0) {
|
||||||
|
// QTextLayout* lay = b.layout();
|
||||||
|
// QTextLayout::FormatRange range;
|
||||||
|
// range.format = b.charFormat();
|
||||||
|
// range.start = 0;
|
||||||
|
// range.length = 2;
|
||||||
|
// lay->draw(painter, option.rect.topLeft(), {range});
|
||||||
|
// b = b.next();
|
||||||
|
// }
|
||||||
painter->restore();
|
painter->restore();
|
||||||
QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height()));
|
QSize bodySize(std::ceil(bodyRenderer->idealWidth()), std::ceil(bodyRenderer->size().height()));
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include "shared/icons.h"
|
#include "shared/icons.h"
|
||||||
#include "shared/global.h"
|
#include "shared/global.h"
|
||||||
|
@ -163,6 +163,12 @@ void Models::MessageFeed::changeMessage(const QString& id, const QMap<QString, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(index, index, cr);
|
emit dataChanged(index, index, cr);
|
||||||
|
|
||||||
|
if (observersAmount == 0 && !msg->getForwarded() && changeRoles.count(MessageRoles::Text) > 0) {
|
||||||
|
unreadMessages->insert(id);
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
|
emit unnoticedMessage(*msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,12 +318,7 @@ QVariant Models::MessageFeed::data(const QModelIndex& index, int role) const
|
|||||||
case Bulk: {
|
case Bulk: {
|
||||||
FeedItem item;
|
FeedItem item;
|
||||||
item.id = msg->getId();
|
item.id = msg->getId();
|
||||||
|
markMessageAsRead(item.id);
|
||||||
std::set<QString>::const_iterator umi = unreadMessages->find(item.id);
|
|
||||||
if (umi != unreadMessages->end()) {
|
|
||||||
unreadMessages->erase(umi);
|
|
||||||
emit unreadMessagesCountChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.sentByMe = sentByMe(*msg);
|
item.sentByMe = sentByMe(*msg);
|
||||||
item.date = msg->getTime();
|
item.date = msg->getTime();
|
||||||
@ -367,6 +368,17 @@ int Models::MessageFeed::rowCount(const QModelIndex& parent) const
|
|||||||
return storage.size();
|
return storage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Models::MessageFeed::markMessageAsRead(const QString& id) const
|
||||||
|
{
|
||||||
|
std::set<QString>::const_iterator umi = unreadMessages->find(id);
|
||||||
|
if (umi != unreadMessages->end()) {
|
||||||
|
unreadMessages->erase(umi);
|
||||||
|
emit unreadMessagesCountChanged();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
unsigned int Models::MessageFeed::unreadMessagesCount() const
|
||||||
{
|
{
|
||||||
return unreadMessages->size();
|
return unreadMessages->size();
|
||||||
|
@ -72,6 +72,7 @@ public:
|
|||||||
void reportLocalPathInvalid(const QString& messageId);
|
void reportLocalPathInvalid(const QString& messageId);
|
||||||
|
|
||||||
unsigned int unreadMessagesCount() const;
|
unsigned int unreadMessagesCount() const;
|
||||||
|
bool markMessageAsRead(const QString& id) const;
|
||||||
void fileProgress(const QString& messageId, qreal value, bool up);
|
void fileProgress(const QString& messageId, qreal value, bool up);
|
||||||
void fileError(const QString& messageId, const QString& error, bool up);
|
void fileError(const QString& messageId, const QString& error, bool up);
|
||||||
void fileComplete(const QString& messageId, bool up);
|
void fileComplete(const QString& messageId, bool up);
|
||||||
|
Loading…
Reference in New Issue
Block a user