File indexing completed on 2023-11-26 08:16:45
0001 /* 0002 * Copyright (C) 2013 David Edmundson <kde@davidedmundson.co.uk> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Lesser General Public 0006 * License as published by the Free Software Foundation; either 0007 * version 2.1 of the License, or (at your option) any later version. 0008 * 0009 * This library is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 * Lesser General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU Lesser General Public 0015 * License along with this library; if not, write to the Free Software 0016 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0017 */ 0018 0019 #include "text-channel-watcher-proxy-model.h" 0020 0021 #include <TelepathyQt/TextChannel> 0022 #include <TelepathyQt/ChannelClassSpecList> 0023 #include <TelepathyQt/MethodInvocationContext> 0024 #include <TelepathyQt/Types> 0025 #include <TelepathyQt/ReceivedMessage> 0026 0027 #include <KTp/types.h> 0028 #include <KTp/contact.h> 0029 #include <KTp/message.h> 0030 0031 0032 inline Tp::ChannelClassSpecList channelClasses() { 0033 return Tp::ChannelClassSpecList() << Tp::ChannelClassSpec::textChat(); 0034 } 0035 0036 class ChannelWatcher : public QObject, public Tp::RefCounted 0037 { 0038 Q_OBJECT 0039 public: 0040 ChannelWatcher(const QPersistentModelIndex &index, const Tp::TextChannelPtr &channel, QObject *parent=nullptr); 0041 int unreadMessageCount() const; 0042 QString lastMessage() const; 0043 KTp::Message::MessageDirection lastMessageDirection() const; 0044 QPersistentModelIndex modelIndex() const; 0045 Q_SIGNALS: 0046 void messagesChanged(); 0047 void invalidated(); 0048 private Q_SLOTS: 0049 void onMessageReceived(const Tp::ReceivedMessage &message); 0050 void onMessageSent(const Tp::Message &message); 0051 private: 0052 QPersistentModelIndex m_index; 0053 Tp::TextChannelPtr m_channel; 0054 QString m_lastMessage; 0055 KTp::Message::MessageDirection m_lastMessageDirection; 0056 }; 0057 0058 typedef Tp::SharedPtr<ChannelWatcher> ChannelWatcherPtr; 0059 0060 ChannelWatcher::ChannelWatcher(const QPersistentModelIndex &index, const Tp::TextChannelPtr &channel, QObject *parent): 0061 QObject(parent), 0062 m_index(index), 0063 m_channel(channel), 0064 m_lastMessageDirection(KTp::Message::LocalToRemote) 0065 { 0066 connect(channel.data(), SIGNAL(pendingMessageRemoved(Tp::ReceivedMessage)), SIGNAL(messagesChanged())); 0067 connect(channel.data(), SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)), SIGNAL(invalidated())); 0068 0069 connect(channel.data(), SIGNAL(messageReceived(Tp::ReceivedMessage)), SLOT(onMessageReceived(Tp::ReceivedMessage))); 0070 connect(channel.data(), SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString)), SLOT(onMessageSent(Tp::Message))); 0071 0072 //trigger an update to the contact straight away 0073 QTimer::singleShot(0, this, SIGNAL(messagesChanged())); 0074 } 0075 0076 QPersistentModelIndex ChannelWatcher::modelIndex() const 0077 { 0078 return m_index; 0079 } 0080 0081 int ChannelWatcher::unreadMessageCount() const 0082 { 0083 return m_channel->messageQueue().size(); 0084 } 0085 0086 QString ChannelWatcher::lastMessage() const 0087 { 0088 return m_lastMessage; 0089 } 0090 0091 KTp::Message::MessageDirection ChannelWatcher::lastMessageDirection() const 0092 { 0093 return m_lastMessageDirection; 0094 } 0095 0096 void ChannelWatcher::onMessageReceived(const Tp::ReceivedMessage &message) 0097 { 0098 if (!message.isDeliveryReport()) { 0099 m_lastMessage = message.text(); 0100 m_lastMessageDirection = KTp::Message::RemoteToLocal; 0101 Q_EMIT messagesChanged(); 0102 } 0103 } 0104 0105 void ChannelWatcher::onMessageSent(const Tp::Message &message) 0106 { 0107 m_lastMessage = message.text(); 0108 m_lastMessageDirection = KTp::Message::LocalToRemote; 0109 Q_EMIT messagesChanged(); 0110 } 0111 0112 namespace KTp { 0113 0114 class TextChannelWatcherProxyModel::Private { 0115 public: 0116 QHash<KTp::ContactPtr, ChannelWatcherPtr> currentChannels; 0117 }; 0118 0119 } //namespace 0120 0121 0122 0123 0124 KTp::TextChannelWatcherProxyModel::TextChannelWatcherProxyModel(QObject *parent) : 0125 QIdentityProxyModel(parent), 0126 Tp::AbstractClientObserver(channelClasses(), true), 0127 d(new TextChannelWatcherProxyModel::Private) 0128 { 0129 } 0130 0131 KTp::TextChannelWatcherProxyModel::~TextChannelWatcherProxyModel() 0132 { 0133 delete d; 0134 } 0135 0136 void KTp::TextChannelWatcherProxyModel::observeChannels(const Tp::MethodInvocationContextPtr<> &context, const Tp::AccountPtr &account, const Tp::ConnectionPtr &connection, const QList<Tp::ChannelPtr> &channels, const Tp::ChannelDispatchOperationPtr &dispatchOperation, const QList<Tp::ChannelRequestPtr> &requestsSatisfied, const Tp::AbstractClientObserver::ObserverInfo &observerInfo) 0137 { 0138 Q_UNUSED(context) 0139 Q_UNUSED(account) 0140 Q_UNUSED(connection) 0141 Q_UNUSED(dispatchOperation) 0142 Q_UNUSED(requestsSatisfied) 0143 Q_UNUSED(observerInfo) 0144 0145 if (!sourceModel()) { 0146 return; 0147 } 0148 0149 Q_FOREACH(const Tp::ChannelPtr & channel, channels) { 0150 Tp::TextChannelPtr textChannel = Tp::TextChannelPtr::dynamicCast(channel); 0151 if (textChannel) { 0152 KTp::ContactPtr targetContact = KTp::ContactPtr::qObjectCast(textChannel->targetContact()); 0153 0154 //skip group chats and situations where we don't have a single contact to mark messages for 0155 if (targetContact.isNull()) { 0156 continue; 0157 } 0158 0159 //if it's not in our source model, ignore the channel 0160 QModelIndexList matchedContacts = sourceModel()->match(QModelIndex(sourceModel()->index(0,0)), KTp::IdRole, QVariant::fromValue(targetContact->id())); 0161 if (matchedContacts.size() !=1) { 0162 continue; 0163 } 0164 0165 QPersistentModelIndex contactIndex(matchedContacts[0]); 0166 0167 ChannelWatcherPtr watcher = ChannelWatcherPtr(new ChannelWatcher(contactIndex, textChannel)); 0168 d->currentChannels[targetContact] = watcher; 0169 0170 connect(watcher.data(), SIGNAL(messagesChanged()), SLOT(onChannelMessagesChanged())); 0171 } 0172 } 0173 } 0174 0175 QVariant KTp::TextChannelWatcherProxyModel::data(const QModelIndex &proxyIndex, int role) const 0176 { 0177 // if we're processing a person and either of those two roles, 0178 // we propagate the data from sub contacts 0179 if (proxyIndex.model()->rowCount(proxyIndex) > 0 0180 && (role == KTp::ContactHasTextChannelRole || role == KTp::ContactUnreadMessageCountRole)) { 0181 QVariant personData; 0182 0183 for (int i = 0; i < proxyIndex.model()->rowCount(proxyIndex); i++) { 0184 QVariant data = proxyIndex.child(i, 0).data(role); 0185 if (!data.isNull()) { 0186 personData = data; 0187 break; 0188 } 0189 } 0190 0191 return personData; 0192 } 0193 0194 const QModelIndex sourceIndex = mapToSource(proxyIndex); 0195 0196 if (role == KTp::ContactHasTextChannelRole) { 0197 KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>(); 0198 if (contact) { 0199 if (d->currentChannels.contains(contact)) { 0200 return true; 0201 } 0202 } 0203 return QVariant(); 0204 } 0205 0206 if (role == KTp::ContactUnreadMessageCountRole) { 0207 KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>(); 0208 if (contact) { 0209 if (d->currentChannels.contains(contact)) { 0210 return d->currentChannels[contact]->unreadMessageCount(); 0211 } 0212 } 0213 return QVariant(); 0214 } 0215 if (role == KTp::ContactLastMessageRole) { 0216 KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>(); 0217 if (contact) { 0218 if (d->currentChannels.contains(contact)) { 0219 return d->currentChannels[contact]->lastMessage(); 0220 } 0221 } 0222 return QString(); 0223 } 0224 if (role == KTp::ContactLastMessageDirectionRole) { 0225 KTp::ContactPtr contact = sourceIndex.data(KTp::ContactRole).value<KTp::ContactPtr>(); 0226 if (contact) { 0227 if (d->currentChannels.contains(contact)) { 0228 return d->currentChannels[contact]->lastMessageDirection(); 0229 } 0230 } 0231 return KTp::Message::LocalToRemote; 0232 } 0233 0234 return sourceIndex.data(role); 0235 } 0236 0237 void KTp::TextChannelWatcherProxyModel::onChannelMessagesChanged() 0238 { 0239 ChannelWatcher* watcher = qobject_cast<ChannelWatcher*>(sender()); 0240 Q_ASSERT(watcher); 0241 const QModelIndex &index = mapFromSource(watcher->modelIndex()); 0242 dataChanged(index, index); 0243 } 0244 0245 void KTp::TextChannelWatcherProxyModel::onChannelInvalidated() 0246 { 0247 ChannelWatcher* watcher = qobject_cast<ChannelWatcher*>(sender()); 0248 Q_ASSERT(watcher); 0249 const QModelIndex &index = mapFromSource(watcher->modelIndex()); 0250 const KTp::ContactPtr &contact = index.data(KTp::ContactRole).value<KTp::ContactPtr>(); 0251 0252 d->currentChannels.remove(contact); 0253 dataChanged(index, index); 0254 } 0255 0256 #include "text-channel-watcher-proxy-model.moc" 0257 #include "moc_text-channel-watcher-proxy-model.cpp"