diff --git a/CMakeLists.txt b/CMakeLists.txt index 969b450..207cb99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,144 +1,145 @@ project(ktp-common-internals) cmake_minimum_required (VERSION 3.5) find_package(ECM 1.7.0 REQUIRED NO_MODULE) set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ) cmake_policy(SET CMP0002 OLD) # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "20") set (RELEASE_SERVICE_VERSION_MINOR "03") set (RELEASE_SERVICE_VERSION_MICRO "70") # Bump for every 0.x release, or whenever BC changes set (KTP_SONUMBER 9) # SO 9 is for 15.08 release set (KTP_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") set (KTP_MESSAGE_FILTER_FRAMEWORK_VERSION "5") find_package (Qt5 REQUIRED CONFIG COMPONENTS DBus Qml + Sql Test Xml) find_package (KF5 5.11 REQUIRED COMPONENTS CoreAddons Notifications KIO WidgetsAddons KCMUtils NotifyConfig TextEditor Wallet Config WindowSystem IconThemes People) set(REQUIRED_TPQT_VERSION 0.9.8) find_package (TelepathyQt5 ${REQUIRED_TPQT_VERSION} COMPONENTS Core REQUIRED) find_package (TelepathyQt5 ${REQUIRED_TPQT_VERSION} COMPONENTS Service) #used for the otr-proxy find_package (TelepathyLoggerQt) find_package (KAccounts) find_package (AccountsQt5 1.10 CONFIG) find_package (SignOnQt5 8.55 CONFIG) find_package (LibOTR 4.0.0) find_package (Libgcrypt) find_package (telepathy-accounts-signon) if (LIBOTR_FOUND AND LIBGCRYPT_FOUND AND TARGET TelepathyQt5::Service) set(OTR_LIBS_FOUND TRUE) endif () include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMMarkNonGuiExecutable) include(ECMPackageConfigHelpers) include(ECMInstallIcons) include(ECMSetupVersion) include(GenerateExportHeader) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckIncludeFiles) include(FeatureSummary) set_package_properties(KF5People PROPERTIES DESCRIPTION "Support for KDE Contact Aggregation" URL "https://commits.kde.org/kpeople" TYPE OPTIONAL ) set_package_properties(TelepathyLoggerQt PROPERTIES DESCRIPTION "Qt bindings for TelepathyLogger. This is needed to provide access to chat logs. HIGHLY recommended" URL "https://commits.kde.org/telepathy-logger-qt" TYPE OPTIONAL ) set_package_properties(LibOTR PROPERTIES DESCRIPTION "Required for OTR support" TYPE OPTIONAL) set_package_properties(Libgcrypt PROPERTIES DESCRIPTION "Required for OTR support" TYPE OPTIONAL) set_package_properties(telepathy-accounts-signon PROPERTIES PURPOSE "Runtime-only dependency on a Telepathy Mission Control plugin, currently residing at https://gitlab.com/accounts-sso/telepathy-accounts-signon" TYPE RUNTIME ) add_definitions(-DKTP_MESSAGE_FILTER_FRAMEWORK_VERSION=\"${KTP_MESSAGE_FILTER_FRAMEWORK_VERSION}\") add_definitions(-DTRANSLATION_DOMAIN=\"ktp-common-internals\") add_definitions ( -DQT_NO_CAST_FROM_ASCII -DQT_NO_KEYWORDS ) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) include_directories ( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) if (KF5People_FOUND) add_definitions(-DHAVE_KPEOPLE) add_subdirectory(kpeople) endif () #If we find KAccounts library, build the plugin for it if (KAccounts_FOUND AND AccountsQt5_FOUND AND SignOnQt5_FOUND) include_directories(${ACCOUNTSQT_INCLUDE_DIRS} ${SIGNONQT_INCLUDE_DIRS}) add_subdirectory(kaccounts) endif () ecm_setup_version(${KTP_VERSION} VARIABLE_PREFIX KTP VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/ktp_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KTpConfigVersion.cmake" SOVERSION ${KTP_SONUMBER}) add_subdirectory(KTp) add_subdirectory(tools) add_subdirectory(data) add_subdirectory(tests) if (OTR_LIBS_FOUND) include_directories (${LIBOTR_INCLUDE_DIR} ${LIBGCRYPT_INCLUDE_DIR} ) add_subdirectory(otr-proxy) endif (OTR_LIBS_FOUND) set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KTp") ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KTpConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KTpConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KTpConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KTpConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(EXPORT KTpTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KTpTargets.cmake NAMESPACE KTp:: COMPONENT Devel) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/KTp/Declarative/CMakeLists.txt b/KTp/Declarative/CMakeLists.txt index 2575573..788093b 100644 --- a/KTp/Declarative/CMakeLists.txt +++ b/KTp/Declarative/CMakeLists.txt @@ -1,32 +1,35 @@ include_directories (${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) set (ktp_qml_plugin_SRCS conversation.cpp conversations-model.cpp messages-model.cpp pinned-contacts-model.cpp filtered-pinned-contacts-proxy-model.cpp contact-pin.cpp telepathy-manager.cpp qml-plugins.cpp debug.cpp + mainlogmodel.cpp ) add_library (ktpqmlplugin SHARED ${ktp_qml_plugin_SRCS}) target_link_libraries (ktpqmlplugin Qt5::Qml - + Qt5::Sql + KF5::WindowSystem KF5::I18n + KF5::People KTp::CommonInternals KTp::Logger KTp::Models KTp::Widgets ) install (TARGETS ktpqmlplugin DESTINATION ${QML_INSTALL_DIR}/org/kde/telepathy) install (FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/telepathy) diff --git a/KTp/Declarative/mainlogmodel.cpp b/KTp/Declarative/mainlogmodel.cpp new file mode 100644 index 0000000..5460ec2 --- /dev/null +++ b/KTp/Declarative/mainlogmodel.cpp @@ -0,0 +1,436 @@ +/* + Copyright (C) 2016 Martin Klapetek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "mainlogmodel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "conversation.h" + +#include + +static inline Tp::ChannelClassSpecList channelClassList() +{ + return Tp::ChannelClassSpecList() << Tp::ChannelClassSpec::textChat(); +} + +ObserverProxy::ObserverProxy(MainLogModel *model) + : QObject(model), + Tp::AbstractClientObserver(channelClassList(), true), + m_model(model) +{ + +} + +void ObserverProxy::observeChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const Tp::ChannelDispatchOperationPtr &dispatchOperation, + const QList &requestsSatisfied, + const Tp::AbstractClientObserver::ObserverInfo &observerInfo) +{ + Q_UNUSED(context) + Q_UNUSED(connection) + Q_UNUSED(requestsSatisfied) + Q_UNUSED(observerInfo) + + Q_FOREACH(const Tp::ChannelPtr &channel, channels) { + Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); + if (textChannel) { + textChannel.data()->setProperty("dispatchOperation", QVariant::fromValue(dispatchOperation)); + m_model->handleChannel(account, textChannel); + } + } +} + +// ----------------------------------------------------------------------- + +MainLogModel::MainLogModel(QObject *parent) + : QAbstractListModel(parent), + Tp::AbstractClientHandler(channelClassList()), + m_observerProxy(new ObserverProxy(this)) +{ + const QString dbLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/ktp-mobile-logger/"); + + connect(qApp, &QCoreApplication::aboutToQuit, this, [=]() { + Q_FOREACH (Conversation *c, m_conversations.values()) { + if (!c->textChannel().isNull()) { + c->textChannel()->requestClose(); + } + } + }); + + m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("logger-db")); + m_db.setDatabaseName(dbLocation + QStringLiteral("history.db3")); + qDebug() << dbLocation << m_db.open(); + m_query = QSqlQuery(QStringLiteral("SELECT data.messageDateTime, data.message, " + "accountData.accountObjectPath, contactData.targetContact " + "FROM data LEFT JOIN contactData ON data.targetContactId = contactData.id " + "LEFT JOIN accountData ON data.accountId = accountData.id " + "GROUP BY data.targetContactId ORDER BY data.messageDateTime DESC"), + m_db); + + m_query.exec(); + + // The query results are processed as soon as AccountManager + // is passsed to MainLogModel +} + +MainLogModel::~MainLogModel() +{ + +} + +void MainLogModel::processQueryResults(QSqlQuery query) +{ + while (query.next()) { + LogItem item; + item.messageDateTime = query.value(QStringLiteral("messageDateTime")).toDateTime(); + item.message = query.value(QStringLiteral("message")).toString(); + item.accountObjectPath = query.value(QStringLiteral("accountObjectPath")).toString(); + item.targetContact = query.value(QStringLiteral("targetContact")).toString(); + + const QString accountObjectPath = item.accountObjectPath.mid(35); + item.conversation = new Conversation(item.targetContact, m_accountManager->accountForObjectPath(item.accountObjectPath), this); + + m_conversations.insert(accountObjectPath + item.targetContact, item.conversation); + setupSignals(item.conversation); + + //TODO: This might be more effective to insert at once? + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_logItems << item; + endInsertRows(); + } +} + +QVariant MainLogModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + const int row = index.row(); + + switch (role) { + case MainLogModel::ContactIdRole: + return m_logItems.at(row).targetContact; + case MainLogModel::AccountIdRole: + return m_logItems.at(row).accountObjectPath.mid(35); + case MainLogModel::LastMessageDateRole: + case MainLogModel::LastMessageTextRole: + case MainLogModel::ConversationRole: + case MainLogModel::HasUnreadMessagesRole: + case MainLogModel::ContactDisplayNameRole: + case MainLogModel::PersonUriRole: + { + if (role == MainLogModel::ConversationRole) { + return QVariant::fromValue(m_logItems.at(row).conversation); + } + + const Conversation *conversation = m_logItems.at(row).conversation; + + if (!conversation->personData()->isValid()) { + if (role == MainLogModel::PersonUriRole || role == MainLogModel::ContactDisplayNameRole) { + return QVariant(); + } + } else { + if (role == MainLogModel::PersonUriRole) { + return conversation->personData()->personUri(); + } else if (role == MainLogModel::ContactDisplayNameRole) { + return conversation->personData()->name(); + } + } + + if (!conversation->isValid()) { + if (role == MainLogModel::HasUnreadMessagesRole) { + return false; + } else if (role == MainLogModel::LastMessageDateRole) { + return m_logItems.at(row).messageDateTime; + } else if (role == MainLogModel::LastMessageTextRole) { + return m_logItems.at(row).message; + } + } else { + if (role == MainLogModel::HasUnreadMessagesRole) { + return conversation->hasUnreadMessages(); + } else if (role == MainLogModel::LastMessageDateRole) { + return conversation->messages()->lastMessageDateTime(); + } else if (role == MainLogModel::LastMessageTextRole) { + return conversation->messages()->lastMessage(); + } + } + } + } + + return QVariant(); +} + +QVariant MainLogModel::data(int index, QByteArray role) const +{ + return data(createIndex(index, 0), roleNames().key(role)); +} + +int MainLogModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_logItems.size(); +} + +QHash MainLogModel::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + + roles.insert(ContactDisplayNameRole, "contactDisplayName"); + roles.insert(ContactIdRole, "contactId"); + roles.insert(PersonUriRole, "personUri"); + roles.insert(AccountIdRole, "accountId"); + roles.insert(LastMessageDateRole, "lastMessageDate"); + roles.insert(LastMessageTextRole, "lastMessageText"); + roles.insert(ConversationRole, "conversation"); + roles.insert(HasUnreadMessagesRole, "hasUnreadMessages"); + + return roles; +} + +bool MainLogModel::canChat(const QString &accountId) const +{ + if (m_accountManager.isNull()) { + return false; + } + + const QString objectPath = TP_QT_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + accountId; + const Tp::AccountPtr account = m_accountManager->accountForObjectPath(objectPath); + + if (!account.isNull() && account->currentPresence().type() != Tp::ConnectionPresenceTypeOffline) { + return true; + } + + return false; +} + +void MainLogModel::startChat(const QString &accountId, const QString &contactId) +{ + const QString objectPath = TP_QT_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + accountId; + const Tp::AccountPtr account = m_accountManager->accountForObjectPath(objectPath); + + if (account.isNull()) { + qWarning() << "Cannot get account for" << accountId; + } + + if (m_conversations.contains(accountId + contactId)) { + Conversation *conversation = m_conversations.value(accountId + contactId); + if (conversation->isValid() && !conversation->textChannel().isNull()) { + Tp::ChannelDispatchOperationPtr dispatch = conversation->textChannel().data()->property("dispatchOperation").value(); + + if (!dispatch.isNull()) { + // Claim the channel that is being observed + dispatch->claim(); + } + // We already have a conversation, don't request new channel + return; + } + } + + Tp::PendingChannel *pendingChannel = account->ensureAndHandleTextChat(contactId); + connect(pendingChannel, &Tp::PendingChannel::finished, [=](Tp::PendingOperation *op) { + if (op->isError()) { + qWarning() << "Requesting text channel failed:" << op->errorName() << op->errorMessage(); + return; + } + + Tp::PendingChannel *pc = qobject_cast(op); + if (pc) { + Tp::TextChannel *channel = qobject_cast(pc->channel().data()); + handleChannel(account, Tp::TextChannelPtr(channel)); + } + }); +} + +void MainLogModel::setAccountManager(const Tp::AccountManagerPtr &accountManager) +{ + m_accountManager = accountManager; + + processQueryResults(m_query); +} + +QObject* MainLogModel::observerProxy() const +{ + return m_observerProxy; +} + +void MainLogModel::handleChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const QList &channelRequests, + const QDateTime &userActionTime, + const HandlerInfo &handlerInfo) +{ + Q_UNUSED(connection); + Q_UNUSED(channelRequests); + Q_UNUSED(userActionTime); + Q_UNUSED(handlerInfo); + + //check that the channel is of type text + Tp::TextChannelPtr textChannel; + Q_FOREACH (const Tp::ChannelPtr &channel, channels) { + textChannel = Tp::TextChannelPtr::dynamicCast(channel); + if (textChannel) { + break; + } + } + + Q_ASSERT(textChannel); + + const QString targetContact = textChannel->targetContact()->id(); + const QString accountObjectPath = account->objectPath(); + + bool existsInModel = false; + Q_FOREACH (const LogItem &item, m_logItems) { + if (item.targetContact == targetContact && item.accountObjectPath == accountObjectPath) { + existsInModel = true; + break; + } + } + + if (!existsInModel) { + QSqlQuery q(m_db); + q.prepare(QStringLiteral("SELECT data.messageDateTime, data.message, " + "accountData.accountObjectPath, contactData.targetContact " + "FROM data LEFT JOIN contactData ON data.targetContactId = contactData.id " + "LEFT JOIN accountData ON data.accountId = accountData.id " + "WHERE contactData.targetContact = :contactId AND accountData.accountObjectPath = :accountObjectPath " + "GROUP BY data.targetContactId ORDER BY data.messageDateTime DESC")); + q.bindValue(QStringLiteral(":contactId"), targetContact); + q.bindValue(QStringLiteral(":accountObjectPath"), accountObjectPath); + + q.exec(); + + if (q.lastError().isValid()) { + qWarning() << "Error selecting latest conversation from log database:" << q.lastError().text(); + } + + processQueryResults(q); + } + + handleChannel(account, textChannel); + context->setFinished(); +} + +bool MainLogModel::bypassApproval() const +{ + return true; +} + +void MainLogModel::handleChannel(const Tp::AccountPtr &account, const Tp::TextChannelPtr &channel) +{ + if (channel && account) { + const QString accountId = account->objectPath().mid(35); + const QString contactId = channel->targetContact()->id(); + qDebug() << accountId << contactId; + + int i = 0; + for (i = 0; i < m_logItems.size(); i++) { + LogItem &item = m_logItems[i]; + if (item.targetContact == contactId && item.accountObjectPath == account->objectPath()) { + // Don't set the same channel again + if (item.conversation->textChannel() == channel) { + return; + } + + item.conversation->setTextChannel(channel); + break; + } + } + + QModelIndex contactIndex = createIndex(i, 0); + + // No existing log item was found and it's a local request, + // do a model insert + if (i == m_logItems.size()) { + LogItem item; + item.targetContact = contactId; + item.accountObjectPath = account->objectPath(); + + Conversation *conversation = new Conversation(contactId, account, this); + item.conversation = conversation; + setupSignals(conversation); + m_conversations.insert(accountId + contactId, conversation); + + conversation->setTextChannel(channel); + + beginInsertRows(QModelIndex(), m_logItems.size(), m_logItems.size()); + m_logItems << item; + endInsertRows(); + } else { + if (contactIndex.isValid()) { + Q_EMIT dataChanged(contactIndex, contactIndex); + } + } + + if (channel->isRequested()) { + Q_EMIT newRequestedChannel(contactIndex); + } + } +} + +void MainLogModel::setupSignals(Conversation *conversation) const +{ + connect(conversation, &Conversation::unreadMessagesChanged, this, &MainLogModel::onConversationChanged); + connect(conversation, &Conversation::avatarChanged, this, &MainLogModel::onConversationChanged); + connect(conversation, &Conversation::presenceIconChanged, this, &MainLogModel::onConversationChanged); + connect(conversation, &Conversation::titleChanged, this, &MainLogModel::onConversationChanged); + connect(conversation, &Conversation::validityChanged, this, &MainLogModel::onConversationChanged); + connect(conversation, &Conversation::lastMessageChanged, this, &MainLogModel::onConversationChanged); +} + +void MainLogModel::onConversationChanged() +{ + Conversation *conversation = qobject_cast(sender()); + if (!conversation || !conversation->isValid()) { + return; + } + + int i = 0; + for (i = 0; i < m_logItems.size(); i++) { + if (m_logItems.at(i).conversation == conversation) { + break; + } + } + + const QModelIndex index = createIndex(i, 0); + + if (index.isValid()) { + Q_EMIT dataChanged(index, index); + } +} diff --git a/KTp/Declarative/mainlogmodel.h b/KTp/Declarative/mainlogmodel.h new file mode 100644 index 0000000..7323b19 --- /dev/null +++ b/KTp/Declarative/mainlogmodel.h @@ -0,0 +1,140 @@ +/* + Copyright (C) 2016 Martin Klapetek + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef MAINLOGMODEL_H +#define MAINLOGMODEL_H + +#include +#include +#include + +#include +#include +#include + +#include +#include + +class Conversation; +class MainLogModel; // Cause of ObserverProxy + +class LogItem { +public: + QDateTime messageDateTime; + QString message; + QString accountObjectPath; + QString targetContact; + Conversation *conversation; +}; + +/** + * The reason for this class is that an Observer and a Handler cannot + * be registered under the same client name if the Observer is not to + * be autostarted and only monitor things once the app is executed. + * + * So this is a tiny proxy class that gets registered as SpaceBarObserverProxy + * and forwards all observerChannels calls to the model which then merges + * them with the existing conversations + */ +class ObserverProxy : public QObject, public Tp::AbstractClientObserver +{ + Q_OBJECT + +public: + ObserverProxy(MainLogModel *model); + + void observeChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const Tp::ChannelDispatchOperationPtr &dispatchOperation, + const QList &requestsSatisfied, + const Tp::AbstractClientObserver::ObserverInfo &observerInfo); + +private: + MainLogModel *m_model; +}; + +//----------------------------------------------------------------------------- + +class MainLogModel : public QAbstractListModel, public Tp::AbstractClientHandler +{ + Q_OBJECT + +public: + enum Role { + ContactDisplayNameRole = Qt::DisplayRole, + ContactIdRole = Qt::UserRole, + PersonUriRole, + AccountIdRole, + LastMessageDateRole, + LastMessageTextRole, + ConversationRole, + HasUnreadMessagesRole, + + UserRole = Qt::UserRole + 0x1000 ///< in case it's needed to extend, use this one to start from + }; + Q_ENUMS(Role) + + MainLogModel(QObject *parent = 0); + ~MainLogModel(); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + virtual QHash roleNames() const Q_DECL_OVERRIDE; + + Q_INVOKABLE bool canChat(const QString &accountId) const; + Q_INVOKABLE void startChat(const QString &accountId, const QString &contactId); + Q_INVOKABLE void setAccountManager(const Tp::AccountManagerPtr &accountManager); + Q_INVOKABLE QVariant data(int index, QByteArray role) const; + Q_INVOKABLE QObject* observerProxy() const; + + void handleChannels(const Tp::MethodInvocationContextPtr<> &context, + const Tp::AccountPtr &account, + const Tp::ConnectionPtr &connection, + const QList &channels, + const QList &channelRequests, + const QDateTime &userActionTime, + const HandlerInfo &handlerInfo); + + bool bypassApproval() const; + +Q_SIGNALS: + void newRequestedChannel(const QModelIndex &index); + +private Q_SLOTS: + void handleChannel(const Tp::AccountPtr &account, const Tp::TextChannelPtr &channel); + void onConversationChanged(); + +private: + void setupSignals(Conversation *conversation) const; + void processQueryResults(QSqlQuery query); + + QHash m_conversations; // This is a hash with keys "accountId + contactId" + QList m_logItems; + QSqlQuery m_query; + QSqlDatabase m_db; + Tp::AccountManagerPtr m_accountManager; + ObserverProxy *m_observerProxy; + + friend class ObserverProxy; +}; + +Q_DECLARE_METATYPE(Tp::ChannelDispatchOperationPtr) + +#endif diff --git a/KTp/Declarative/qml-plugins.cpp b/KTp/Declarative/qml-plugins.cpp index 93972b0..a42818b 100644 --- a/KTp/Declarative/qml-plugins.cpp +++ b/KTp/Declarative/qml-plugins.cpp @@ -1,80 +1,82 @@ /* Copyright (C) 2011 Lasath Fernando Copyright (C) 2013 Dan Vrátil Copyright (C) 2013 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "qml-plugins.h" #include #include #include "conversation.h" #include "conversations-model.h" #include "messages-model.h" #include "pinned-contacts-model.h" #include "contact-pin.h" #include "filtered-pinned-contacts-proxy-model.h" #include "telepathy-manager.h" +#include "mainlogmodel.h" #include #include "KTp/types.h" #include "KTp/global-presence.h" #include "KTp/Models/presence-model.h" #include "KTp/Models/contacts-filter-model.h" #include "KTp/Models/contacts-model.h" #include "KTp/Models/accounts-list-model.h" #include void QmlPlugins::initializeEngine(QQmlEngine *engine, const char *uri) { Q_UNUSED(uri) engine->rootContext()->setContextProperty(QLatin1String("telepathyManager"), new TelepathyManager(engine)); } void QmlPlugins::registerTypes(const char *uri) { qmlRegisterType (uri, 0, 1, "ContactsModel"); qmlRegisterType (uri, 0, 1, "AccountsListModel"); qmlRegisterType (uri, 0, 1, "ConversationsModel"); qmlRegisterType(uri, 0, 1, "Conversation"); qmlRegisterType(uri, 0, 1, "PinnedContactsModel"); qmlRegisterType(uri, 0, 1, "ContactPin"); qmlRegisterType(uri, 0, 1, "FilteredPinnedContactsProxyModel"); qmlRegisterType (uri, 0, 1, "GlobalPresence"); qmlRegisterType (uri, 0, 1, "PresenceModel"); + qmlRegisterType (uri, 0, 1, "MainLogModel"); qmlRegisterUncreatableType (uri, 0, 1, "MessagesModel", QLatin1String("It will be created once the conversation is created")); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); QMetaType::registerComparators(); }