[Telepathy-commits] [telepathy-qt4/master] Make the Channel initial contacts and Group faking use the standard MCD handling mechanism
Olli Salli
olli.salli at collabora.co.uk
Thu Feb 12 08:28:18 PST 2009
---
TelepathyQt4/Client/channel.cpp | 275 ++++++++++++++++++++++-----------------
1 files changed, 158 insertions(+), 117 deletions(-)
diff --git a/TelepathyQt4/Client/channel.cpp b/TelepathyQt4/Client/channel.cpp
index b45fc06..1913a4e 100644
--- a/TelepathyQt4/Client/channel.cpp
+++ b/TelepathyQt4/Client/channel.cpp
@@ -82,6 +82,7 @@ struct Channel::Private
void extract0176GroupProps(const QVariantMap &props);
void nowHaveInterfaces();
+ void nowHaveInitialMembers();
bool setGroupFlags(uint groupFlags);
@@ -133,7 +134,6 @@ struct Channel::Private
// Group member introspection
bool groupHaveMembers;
- bool buildingInitialContacts;
bool buildingContacts;
// Queue of received MCD signals to process
@@ -148,6 +148,11 @@ struct Channel::Private
UIntList groupLocalPendingMembersToRemove;
UIntList groupRemotePendingMembersToRemove;
+ // Initial members
+ UIntList groupInitialMembers;
+ LocalPendingInfoList groupInitialLP;
+ UIntList groupInitialRP;
+
// Current members
QHash<uint, QSharedPointer<Contact> > groupContacts;
QHash<uint, QSharedPointer<Contact> > groupLocalPendingContacts;
@@ -226,7 +231,6 @@ Channel::Private::Private(Channel *parent, Connection *connection)
groupFlags(0),
usingMembersChangedDetailed(false),
groupHaveMembers(false),
- buildingInitialContacts(false),
buildingContacts(false),
currentGroupMembersChangedInfo(0),
groupAreHandleOwnersAvailable(false),
@@ -437,9 +441,10 @@ void Channel::Private::extract0177MainProps(const QVariantMap &props)
if (!fakeGroupInterfaceIfNeeded() &&
!interfaces.contains(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP) &&
initiatorHandle) {
- // there is no group interface, so lets try to build the contact
- // object for initiatorHandle now
- buildingInitialContacts = true;
+ // No group interface, so nobody will build the poor fellow for us. Will do it ourselves
+ // out of pity for him.
+ // TODO: needs testing. I would imagine some of the elaborate updateContacts logic
+ // tripping over with just this.
buildContacts();
}
@@ -469,33 +474,22 @@ void Channel::Private::extract0176GroupProps(const QVariantMap &props)
introspectQueue.enqueue(&Private::introspectGroupFallbackMembers);
introspectQueue.enqueue(&Private::introspectGroupFallbackLocalPendingWithInfo);
introspectQueue.enqueue(&Private::introspectGroupFallbackSelfHandle);
- }
- else {
+ } else {
debug() << " Found properties specified in 0.17.6";
- groupHaveMembers = true;
groupAreHandleOwnersAvailable = true;
groupIsSelfHandleTracked = true;
setGroupFlags(qdbus_cast<uint>(props["GroupFlags"]));
groupHandleOwners = qdbus_cast<HandleOwnerMap>(props["HandleOwners"]);
- pendingGroupMembers = QSet<uint>::fromList(qdbus_cast<UIntList>(props["Members"]));
- // FIXME: this will be ignored currently - fix by synthesizing MCD signals from here
- /*
- foreach (const LocalPendingInfo &info,
- qdbus_cast<LocalPendingInfoList>(props["LocalPendingMembers"])) {
- pendingGroupLocalPendingMembers.insert(info.toBeAdded);
- pendingGroupMembersChangeInfo[info.actor] = info;
- }
- */
- pendingGroupRemotePendingMembers =
- QSet<uint>::fromList(qdbus_cast<UIntList>(props["RemotePendingMembers"]));
+ groupInitialMembers = qdbus_cast<UIntList>(props["Members"]);
+ groupInitialLP = qdbus_cast<LocalPendingInfoList>(props["LocalPendingMembers"]);
+ groupInitialRP = qdbus_cast<UIntList>(props["RemotePendingMembers"]);
groupSelfHandle = qdbus_cast<uint>(props["SelfHandle"]);
- buildingInitialContacts = true;
- buildContacts();
+ nowHaveInitialMembers();
}
}
@@ -509,6 +503,56 @@ void Channel::Private::nowHaveInterfaces()
}
}
+void Channel::Private::nowHaveInitialMembers()
+{
+ // Must be called with no contacts anywhere in the first place
+ Q_ASSERT(!ready);
+ Q_ASSERT(!buildingContacts);
+
+ Q_ASSERT(pendingGroupMembers.isEmpty());
+ Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
+ Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
+
+ Q_ASSERT(groupContacts.isEmpty());
+ Q_ASSERT(groupLocalPendingContacts.isEmpty());
+ Q_ASSERT(groupRemotePendingContacts.isEmpty());
+
+ // Set groupHaveMembers so we start queueing fresh MCD signals
+ Q_ASSERT(!groupHaveMembers);
+ groupHaveMembers = true;
+
+ // Synthesize MCD for current + RP
+ groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(
+ groupInitialMembers, // Members
+ UIntList(), // Removed - obviously, none
+ UIntList(), // LP - will be handled separately, see below
+ groupInitialRP, // Remote pending
+ QVariantMap())); // No details for members + RP
+
+ // Synthesize one MCD for each initial LP member - they might have different details
+ foreach (const LocalPendingInfo &info, groupInitialLP) {
+ QVariantMap details;
+
+ if (info.actor != 0) {
+ details.insert("actor", info.actor);
+ }
+
+ if (info.reason != ChannelGroupChangeReasonNone) {
+ details.insert("change-reason", info.reason);
+ }
+
+ if (!info.message.isEmpty()) {
+ details.insert("message", info.message);
+ }
+
+ groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(UIntList(), UIntList(),
+ UIntList() << info.toBeAdded, UIntList(), details));
+ }
+
+ // At least our added MCD event to process
+ processMembersChanged();
+}
+
bool Channel::Private::setGroupFlags(uint newGroupFlags)
{
if (groupFlags == newGroupFlags) {
@@ -579,7 +623,9 @@ void Channel::Private::buildContacts()
toBuild.append(currentGroupMembersChangedInfo->actor);
}
- if (buildingInitialContacts && initiatorHandle) {
+ if (!initiatorContact && initiatorHandle) {
+ // No initiator contact, but Yes initiator handle - might do something about it with just
+ // that information
toBuild.append(initiatorHandle);
}
@@ -594,7 +640,9 @@ void Channel::Private::buildContacts()
if (toBuild.isEmpty()) {
if (!groupSelfHandle && groupSelfContact) {
groupSelfContact.clear();
- emit parent->groupSelfContactChanged();
+ if (ready) {
+ emit parent->groupSelfContactChanged();
+ }
}
return;
}
@@ -611,12 +659,39 @@ void Channel::Private::buildContacts()
void Channel::Private::processMembersChanged()
{
+ Q_ASSERT(!buildingContacts);
+
if (groupMembersChangedQueue.isEmpty()) {
if (pendingRetrieveGroupSelfContact) {
pendingRetrieveGroupSelfContact = false;
// nothing queued but selfContact changed
buildContacts();
+ return;
+ }
+
+ if (!ready) {
+ if (introspectQueue.isEmpty()) {
+ debug() << "Both the MCD and the introspect queue empty for the first time. Ready!";
+
+ if (initiatorHandle && !initiatorContact) {
+ warning() << " Unable to create contact object for initiator with handle" <<
+ initiatorHandle;
+ }
+
+ if (groupSelfHandle && !groupSelfContact) {
+ warning() << " Unable to create contact object for self handle" <<
+ groupSelfHandle;
+ }
+
+ setReady();
+
+ processMembersChanged();
+ return;
+ } else {
+ debug() << "Contact queue empty but introspect queue isn't. IS will set ready.";
+ }
}
+
return;
}
@@ -661,16 +736,8 @@ void Channel::Private::processMembersChanged()
groupMembersToRemove.append(handle);
}
- if (pendingGroupMembers.isEmpty() &&
- pendingGroupLocalPendingMembers.isEmpty() &&
- pendingGroupRemotePendingMembers.isEmpty())
- {
- // no member added, just remove the members to be removed and signal
- // membersChanged
- updateContacts();
- } else {
- buildContacts();
- }
+ // Always go through buildContacts - we might have a self/initiator/whatever handle to build
+ buildContacts();
}
void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &contacts)
@@ -681,6 +748,8 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
QSharedPointer<Contact> actorContact;
bool selfContactUpdated = false;
+ debug() << "Entering Chan::Priv::updateContacts() with" << contacts.size() << "contacts";
+
// FIXME: simplify. Some duplication of logic present.
foreach (QSharedPointer<Contact> contact, contacts) {
uint handle = contact->handle()[0];
@@ -702,7 +771,9 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
selfContactUpdated = true;
}
- if (buildingInitialContacts && initiatorHandle == handle) {
+ if (!initiatorContact && initiatorHandle == handle) {
+ // No initiator contact stored, but there's a contact for the initiator handle
+ // We can use that!
initiatorContact = contact;
}
@@ -717,8 +788,12 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
selfContactUpdated = true;
}
- // FIXME: This shouldn't be needed. Clearer would be to first scan for the actor being present in the contacts
- // supplied.
+ pendingGroupMembers.clear();
+ pendingGroupLocalPendingMembers.clear();
+ pendingGroupRemotePendingMembers.clear();
+
+ // FIXME: This shouldn't be needed. Clearer would be to first scan for the actor being present
+ // in the contacts supplied.
foreach (QSharedPointer<Contact> contact, contacts) {
uint handle = contact->handle()[0];
if (groupLocalPendingContactsChangeInfo.contains(handle)) {
@@ -727,32 +802,6 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
}
}
- pendingGroupMembers.clear();
- pendingGroupLocalPendingMembers.clear();
- pendingGroupRemotePendingMembers.clear();
-
- if (buildingInitialContacts) {
- buildingInitialContacts = false;
-
- if (initiatorHandle && !initiatorContact) {
- warning() << "Unable to create contact object for initiator with handle" <<
- initiatorHandle;
- }
-
- if (groupSelfHandle && !groupSelfContact) {
- warning() << "Unable to create contact object for self handle" <<
- groupSelfHandle;
- }
-
- if (introspectQueue.isEmpty()) {
- // if we were building the initial contacts from handles and the
- // introspect queue is empty it means we are ready now, so signal it
- setReady();
- }
- processMembersChanged();
- return;
- }
-
// FIXME: use QSet to make the code somewhat easier to comprehend and change
QList<QSharedPointer<Contact> > groupContactsRemoved;
@@ -779,6 +828,7 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
}
groupMembersToRemove.clear();
+ // FIXME: drop the LPToRemove and RPToRemove sets - they're redundant
foreach (uint handle, groupLocalPendingMembersToRemove) {
groupLocalPendingContacts.remove(handle);
}
@@ -796,17 +846,21 @@ void Channel::Private::updateContacts(const QList<QSharedPointer<Contact> > &con
GroupMemberChangeDetails details(
actorContact,
currentGroupMembersChangedInfo->details);
- emit parent->groupMembersChanged(
- groupContactsAdded,
- groupLocalPendingContactsAdded,
- groupRemotePendingContactsAdded,
- groupContactsRemoved,
- details);
+ if (ready) {
+ // Channel is ready, we can signal membership changes to the outside world without
+ // confusing anyone's fragile logic.
+ emit parent->groupMembersChanged(
+ groupContactsAdded,
+ groupLocalPendingContactsAdded,
+ groupRemotePendingContactsAdded,
+ groupContactsRemoved,
+ details);
+ }
}
delete currentGroupMembersChangedInfo;
currentGroupMembersChangedInfo = 0;
- if (selfContactUpdated) {
+ if (selfContactUpdated && ready) {
emit parent->groupSelfContactChanged();
}
@@ -817,35 +871,26 @@ bool Channel::Private::fakeGroupInterfaceIfNeeded()
{
if (interfaces.contains(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_GROUP)) {
return false;
+ } else if (targetHandleType != Telepathy::HandleTypeContact) {
+ return false;
}
- bool ret = false;
-
- if (targetHandleType == Telepathy::HandleTypeContact) {
- // fake group interface
-
- if (connection->selfContact() || !targetHandle) {
- // for groupSelfContact()
- groupSelfHandle = connection->selfContact()->handle()[0];
+ // fake group interface
+ if (connection->selfContact() && targetHandle) {
+ // Fake groupSelfHandle and initial members, let the MCD handling take care of the rest
+ groupSelfHandle = connection->selfContact()->handle()[0];
+ groupInitialMembers = UIntList() << groupSelfHandle << targetHandle;
- // for groupContacts()
- pendingGroupMembers.insert(groupSelfHandle);
- pendingGroupMembers.insert(targetHandle);
+ debug().nospace() << "Faking a group on channel with self handle=" <<
+ groupSelfHandle << " and other handle=" << targetHandle;
- debug().nospace() << "Faking a group on channel with self handle=" <<
- groupSelfHandle << " and other handle=" << targetHandle;
- ret = true;
-
- buildingInitialContacts = true;
- buildContacts();
- }
- else {
- warning() << "Connection::selfContact returned a null contact or targetHandle is 0, "
- "not faking a group on channel";
- }
+ nowHaveInitialMembers();
+ } else {
+ warning() << "Connection::selfContact returned a null contact or targetHandle is 0, "
+ "not faking a group on channel";
}
- return ret;
+ return true;
}
void Channel::Private::setReady()
@@ -1951,16 +1996,16 @@ void Channel::gotAllMembers(QDBusPendingCallWatcher *watcher)
if (reply.isError()) {
warning().nospace() << "Channel.Interface.Group::GetAllMembers() failed with " <<
reply.error().name() << ": " << reply.error().message();
- }
- else {
+ } else {
debug() << "Got reply to fallback Channel.Interface.Group::GetAllMembers()";
- mPriv->groupHaveMembers = true;
- mPriv->pendingGroupMembers = QSet<uint>::fromList(reply.argumentAt<0>());
- mPriv->pendingGroupLocalPendingMembers = QSet<uint>::fromList(reply.argumentAt<1>());
- mPriv->pendingGroupRemotePendingMembers = QSet<uint>::fromList(reply.argumentAt<2>());
+ mPriv->groupInitialMembers = reply.argumentAt<0>();
+ mPriv->groupInitialRP = reply.argumentAt<2>();
- // delay building contacts now until we process gotSelfHandle
+ foreach (uint handle, reply.argumentAt<1>()) {
+ LocalPendingInfo info = {handle, 0, ChannelGroupChangeReasonNone, ""};
+ mPriv->groupInitialLP.push_back(info);
+ }
}
continueIntrospection();
@@ -1978,8 +2023,8 @@ void Channel::gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher *watcher)
else {
debug() << "Got reply to fallback "
"Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
-
- // FIXME: queue synthesized MCD corresponding to the reply
+ // Overrides the previous vague list provided by gotAllMembers
+ mPriv->groupInitialLP = reply.value();
}
continueIntrospection();
@@ -1992,15 +2037,12 @@ void Channel::gotSelfHandle(QDBusPendingCallWatcher *watcher)
if (reply.isError()) {
warning().nospace() << "Channel.Interface.Group::GetSelfHandle() failed with " <<
reply.error().name() << ": " << reply.error().message();
- }
- else {
+ } else {
debug() << "Got reply to fallback Channel.Interface.Group::GetSelfHandle()";
mPriv->groupSelfHandle = reply.value();
}
- // build contacts even if the call failed
- mPriv->buildingInitialContacts = true;
- mPriv->buildContacts();
+ // FIXME: fix self contact building with no group
continueIntrospection();
}
@@ -2194,24 +2236,23 @@ void Channel::onSelfHandleChanged(uint newSelfHandle)
debug() << " Emitting groupSelfHandleChanged with new self handle" <<
newSelfHandle;
- if (!mPriv->buildingContacts) {
- mPriv->buildContacts();
- } else {
- // next call to processMembersChanged will build selfContact again
- mPriv->pendingRetrieveGroupSelfContact = true;
- }
+ // FIXME: fix self contact building with no group
+ mPriv->pendingRetrieveGroupSelfContact = true;
}
}
void Channel::continueIntrospection()
{
if (mPriv->introspectQueue.isEmpty()) {
- // we are still building initial contact objects so delay call to ready
- if (!mPriv->buildingInitialContacts) {
- mPriv->setReady();
+ if (!mPriv->ready) {
+ if (mPriv->groupMembersChangedQueue.isEmpty() && !mPriv->buildingContacts) {
+ debug() << "Both the IS and the MCD queue empty for the first time. Ready.";
+ mPriv->setReady();
+ } else {
+ debug() << "Introspection done before contacts done - contacts sets ready";
+ }
}
- }
- else {
+ } else {
(mPriv->*(mPriv->introspectQueue.dequeue()))();
}
}
--
1.5.6.5
More information about the telepathy-commits
mailing list