1 /**
2  * This file is part of TelepathyQt
3  *
4  * @copyright Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
5  * @copyright Copyright (C) 2010 Nokia Corporation
6  * @license LGPL 2.1
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include <TelepathyQt/AccountSet>
24 #include "TelepathyQt/account-set-internal.h"
25 
26 #include "TelepathyQt/_gen/account-set.moc.hpp"
27 #include "TelepathyQt/_gen/account-set-internal.moc.hpp"
28 
29 #include "TelepathyQt/debug-internal.h"
30 
31 #include <TelepathyQt/Account>
32 #include <TelepathyQt/AccountFilter>
33 #include <TelepathyQt/AccountManager>
34 #include <TelepathyQt/ConnectionCapabilities>
35 #include <TelepathyQt/ConnectionManager>
36 
37 namespace Tp
38 {
39 
Private(AccountSet * parent,const AccountManagerPtr & accountManager,const AccountFilterConstPtr & filter)40 AccountSet::Private::Private(AccountSet *parent,
41         const AccountManagerPtr &accountManager,
42         const AccountFilterConstPtr &filter)
43     : parent(parent),
44       accountManager(accountManager),
45       filter(filter),
46       ready(false)
47 {
48     init();
49 }
50 
Private(AccountSet * parent,const AccountManagerPtr & accountManager,const QVariantMap & filterMap)51 AccountSet::Private::Private(AccountSet *parent,
52         const AccountManagerPtr &accountManager,
53         const QVariantMap &filterMap)
54     : parent(parent),
55       accountManager(accountManager),
56       ready(false)
57 {
58     AccountPropertyFilterPtr propertyFilter = AccountPropertyFilter::create();
59     for (QVariantMap::const_iterator i = filterMap.constBegin();
60             i != filterMap.constEnd(); ++i) {
61         propertyFilter->addProperty(i.key(), i.value());
62     }
63     filter = AccountFilterPtr::dynamicCast(propertyFilter);
64     init();
65 }
66 
init()67 void AccountSet::Private::init()
68 {
69     if (filter->isValid()) {
70         connectSignals();
71         insertAccounts();
72         ready = true;
73     }
74 }
75 
connectSignals()76 void AccountSet::Private::connectSignals()
77 {
78     parent->connect(accountManager.data(),
79             SIGNAL(newAccount(Tp::AccountPtr)),
80             SLOT(onNewAccount(Tp::AccountPtr)));
81 }
82 
insertAccounts()83 void AccountSet::Private::insertAccounts()
84 {
85     foreach (const Tp::AccountPtr &account, accountManager->allAccounts()) {
86         insertAccount(account);
87     }
88 }
89 
insertAccount(const Tp::AccountPtr & account)90 void AccountSet::Private::insertAccount(const Tp::AccountPtr &account)
91 {
92     QString accountPath = account->objectPath();
93     Q_ASSERT(!wrappers.contains(accountPath));
94     wrapAccount(account);
95     filterAccount(account);
96 }
97 
removeAccount(const Tp::AccountPtr & account)98 void AccountSet::Private::removeAccount(const Tp::AccountPtr &account)
99 {
100     QString accountPath = account->objectPath();
101     Q_ASSERT(wrappers.contains(accountPath));
102     accounts.remove(accountPath);
103 
104     AccountWrapper *wrapper = wrappers.take(accountPath);
105     Q_ASSERT(wrapper->disconnect(parent));
106     wrapper->deleteLater();
107 
108     emit parent->accountRemoved(account);
109 }
110 
wrapAccount(const AccountPtr & account)111 void AccountSet::Private::wrapAccount(const AccountPtr &account)
112 {
113     AccountWrapper *wrapper = new AccountWrapper(account, parent);
114     parent->connect(wrapper,
115             SIGNAL(accountRemoved(Tp::AccountPtr)),
116             SLOT(onAccountRemoved(Tp::AccountPtr)));
117     parent->connect(wrapper,
118             SIGNAL(accountPropertyChanged(Tp::AccountPtr,QString)),
119             SLOT(onAccountChanged(Tp::AccountPtr)));
120     parent->connect(wrapper,
121             SIGNAL(accountCapabilitiesChanged(Tp::AccountPtr,Tp::ConnectionCapabilities)),
122             SLOT(onAccountChanged(Tp::AccountPtr)));
123     wrappers.insert(account->objectPath(), wrapper);
124 }
125 
filterAccount(const AccountPtr & account)126 void AccountSet::Private::filterAccount(const AccountPtr &account)
127 {
128     QString accountPath = account->objectPath();
129     Q_ASSERT(wrappers.contains(accountPath));
130     AccountWrapper *wrapper = wrappers[accountPath];
131 
132     /* account changed, let's check if it matches filter */
133     if (accountMatchFilter(wrapper)) {
134         if (!accounts.contains(account->objectPath())) {
135             accounts.insert(account->objectPath(), account);
136             if (ready) {
137                 emit parent->accountAdded(account);
138             }
139         }
140     } else {
141         if (accounts.contains(account->objectPath())) {
142             accounts.remove(account->objectPath());
143             if (ready) {
144                 emit parent->accountRemoved(account);
145             }
146         }
147     }
148 }
149 
accountMatchFilter(AccountWrapper * wrapper)150 bool AccountSet::Private::accountMatchFilter(AccountWrapper *wrapper)
151 {
152     if (!filter) {
153         return true;
154     }
155 
156     return filter->matches(wrapper->account());
157 }
158 
AccountWrapper(const AccountPtr & account,QObject * parent)159 AccountSet::Private::AccountWrapper::AccountWrapper(
160         const AccountPtr &account, QObject *parent)
161     : QObject(parent),
162       mAccount(account)
163 {
164     connect(account.data(),
165             SIGNAL(removed()),
166             SLOT(onAccountRemoved()));
167     connect(account.data(),
168             SIGNAL(propertyChanged(QString)),
169             SLOT(onAccountPropertyChanged(QString)));
170     connect(account.data(),
171             SIGNAL(capabilitiesChanged(Tp::ConnectionCapabilities)),
172             SLOT(onAccountCapalitiesChanged(Tp::ConnectionCapabilities)));
173 }
174 
~AccountWrapper()175 AccountSet::Private::AccountWrapper::~AccountWrapper()
176 {
177 }
178 
onAccountRemoved()179 void AccountSet::Private::AccountWrapper::onAccountRemoved()
180 {
181     emit accountRemoved(mAccount);
182 }
183 
onAccountPropertyChanged(const QString & propertyName)184 void AccountSet::Private::AccountWrapper::onAccountPropertyChanged(
185         const QString &propertyName)
186 {
187     emit accountPropertyChanged(mAccount, propertyName);
188 }
189 
onAccountCapalitiesChanged(const ConnectionCapabilities & caps)190 void AccountSet::Private::AccountWrapper::onAccountCapalitiesChanged(
191         const ConnectionCapabilities &caps)
192 {
193     emit accountCapabilitiesChanged(mAccount, caps);
194 }
195 
196 /**
197  * \class AccountSet
198  * \ingroup clientaccount
199  * \headerfile TelepathyQt/account-set.h <TelepathyQt/AccountSet>
200  *
201  * \brief The AccountSet class represents a set of Telepathy accounts
202  * filtered by a given criteria.
203  *
204  * AccountSet is automatically updated whenever accounts that match the given
205  * criteria are added, removed or updated.
206  *
207  * \section account_set_usage_sec Usage
208  *
209  * \subsection account_set_create_sec Creating an AccountSet object
210  *
211  * The easiest way to create AccountSet objects is through AccountManager. One
212  * can just use the AccountManager convenience methods such as
213  * AccountManager::validAccounts() to get a set of account objects
214  * representing valid accounts.
215  *
216  * For example:
217  *
218  * \code
219  *
220  * class MyClass : public QObject
221  * {
222  *     QOBJECT
223  *
224  * public:
225  *     MyClass(QObject *parent = 0);
226  *     ~MyClass() { }
227  *
228  * private Q_SLOTS:
229  *     void onAccountManagerReady(Tp::PendingOperation *);
230  *     void onValidAccountAdded(const Tp::AccountPtr &);
231  *     void onValidAccountRemoved(const Tp::AccountPtr &);
232  *
233  * private:
234  *     AccountManagerPtr am;
235  *     AccountSetPtr validAccountsSet;
236  * };
237  *
238  * MyClass::MyClass(QObject *parent)
239  *     : QObject(parent)
240  *       am(AccountManager::create())
241  * {
242  *     connect(am->becomeReady(),
243  *             SIGNAL(finished(Tp::PendingOperation*)),
244  *             SLOT(onAccountManagerReady(Tp::PendingOperation*)));
245  * }
246  *
247  * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
248  * {
249  *     if (op->isError()) {
250  *         qWarning() << "Account manager cannot become ready:" <<
251  *             op->errorName() << "-" << op->errorMessage();
252  *         return;
253  *     }
254  *
255  *     validAccountsSet = am->validAccounts();
256  *     connect(validAccountsSet.data(),
257  *             SIGNAL(accountAdded(const Tp::AccountPtr &)),
258  *             SLOT(onValidAccountAdded(const Tp::AccountPtr &)));
259  *     connect(validAccountsSet.data(),
260  *             SIGNAL(accountRemoved(const Tp::AccountPtr &)),
261  *             SLOT(onValidAccountRemoved(const Tp::AccountPtr &)));
262  *
263  *     QList<AccountPtr> accounts = validAccountsSet->accounts();
264  *     // do something with accounts
265  * }
266  *
267  * void MyClass::onValidAccountAdded(const Tp::AccountPtr &account)
268  * {
269  *     // do something with account
270  * }
271  *
272  * void MyClass::onValidAccountRemoved(const Tp::AccountPtr &account)
273  * {
274  *     // do something with account
275  * }
276  *
277  * \endcode
278  *
279  * You can also define your own filter using AccountManager::filterAccounts:
280  *
281  * \code
282  *
283  * void MyClass::onAccountManagerReady(Tp::PendingOperation *op)
284  * {
285  *     ...
286  *
287  *     AccountPropertyFilterPtr filter = AccountPropertyFilter::create();
288  *     filter->addProperty(QLatin1String("protocolName"), QLatin1String("jabber"));
289  *     filter->addProperty(QLatin1String("enabled"), true);
290  *
291  *     AccountSetPtr filteredAccountSet = am->filterAccounts(filter);
292  *     // connect to AccountSet::accountAdded/accountRemoved signals
293  *     QList<AccountPtr> accounts = filteredAccountSet->accounts();
294  *     // do something with accounts
295  *
296  *     ....
297  * }
298  *
299  * \endcode
300  *
301  * Note that for AccountSet to property work with AccountCapabilityFilter
302  * objects, the feature Account::FeatureCapabilities need to be enabled in all
303  * accounts return by the AccountManager passed as param in the constructor.
304  * The easiest way to do this is to enable AccountManager feature
305  * AccountManager::FeatureFilterByCapabilities.
306  *
307  * AccountSet can also be instantiated directly, but when doing it,
308  * the AccountManager object passed as param in the constructor must be ready
309  * for AccountSet properly work.
310  */
311 
312 /**
313  * Construct a new AccountSet object.
314  *
315  * \param accountManager An account manager object used to filter accounts.
316  *                       The account manager object must be ready.
317  * \param filter The desired filter.
318  */
AccountSet(const AccountManagerPtr & accountManager,const AccountFilterConstPtr & filter)319 AccountSet::AccountSet(const AccountManagerPtr &accountManager,
320         const AccountFilterConstPtr &filter)
321     : Object(),
322       mPriv(new Private(this, accountManager, filter))
323 {
324 }
325 
326 /**
327  * Construct a new AccountSet object.
328  *
329  * The \a filter must contain Account property names and values as map items.
330  *
331  * \param accountManager An account manager object used to filter accounts.
332  *                       The account manager object must be ready.
333  * \param filter The desired filter.
334  */
AccountSet(const AccountManagerPtr & accountManager,const QVariantMap & filter)335 AccountSet::AccountSet(const AccountManagerPtr &accountManager,
336         const QVariantMap &filter)
337     : Object(),
338       mPriv(new Private(this, accountManager, filter))
339 {
340 }
341 
342 /**
343  * Class destructor.
344  */
~AccountSet()345 AccountSet::~AccountSet()
346 {
347     delete mPriv;
348 }
349 
350 /**
351  * Return the account manager object used to filter accounts.
352  *
353  * \return A pointer to the AccountManager object.
354  */
accountManager() const355 AccountManagerPtr AccountSet::accountManager() const
356 {
357     return mPriv->accountManager;
358 }
359 
360 /**
361  * Return the filter used to filter accounts.
362  *
363  * \return A read-only pointer the AccountFilter object.
364  */
filter() const365 AccountFilterConstPtr AccountSet::filter() const
366 {
367     return mPriv->filter;
368 }
369 
370 /**
371  * Return a list of account objects that match filter.
372  *
373  * Change notification is via the accountAdded() and accountRemoved() signals.
374  *
375  * \return A list of pointers to Account objects.
376  * \sa accountAdded(), accountRemoved()
377  */
accounts() const378 QList<AccountPtr> AccountSet::accounts() const
379 {
380     return mPriv->accounts.values();
381 }
382 
383 /**
384  * \fn void AccountSet::accountAdded(const Tp::AccountPtr &account)
385  *
386  * Emitted whenever an account that matches filter is added to
387  * this set.
388  *
389  * \param account The account that was added to this set.
390  * \sa accounts()
391  */
392 
393 /**
394  * \fn void AccountSet::accountRemoved(const Tp::AccountPtr &account)
395  *
396  * Emitted whenever an account that matches filter is removed
397  * from this set.
398  *
399  * \param account The account that was removed from this set.
400  * \sa accounts()
401  */
402 
onNewAccount(const AccountPtr & account)403 void AccountSet::onNewAccount(const AccountPtr &account)
404 {
405     mPriv->insertAccount(account);
406 }
407 
onAccountRemoved(const AccountPtr & account)408 void AccountSet::onAccountRemoved(const AccountPtr &account)
409 {
410     mPriv->removeAccount(account);
411 }
412 
onAccountChanged(const AccountPtr & account)413 void AccountSet::onAccountChanged(const AccountPtr &account)
414 {
415     mPriv->filterAccount(account);
416 }
417 
418 } // Tp
419