2
* contactlistgroup.cpp - flat contact list group class
3
* Copyright (C) 2008-2010 Yandex LLC (Michail Pishchagin)
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "contactlistgroup.h"
25
#include "contactlistitemproxy.h"
26
#include "contactlistgroupmenu.h"
27
#include "contactlistmodel.h"
28
#include "psicontact.h"
29
#include "contactlistgroupstate.h"
30
#include "contactlistgroupcache.h"
32
#include "fakegroupcontact.h"
35
static QString GROUP_DELIMITER = "::";
40
ContactListGroup::ContactListGroup(ContactListModel* model, ContactListGroup* parent)
44
, updateOnlineContactsTimer_(0)
45
, haveOnlineContacts_(false)
46
, onlineContactsCount_(0)
47
, totalContactsCount_(0)
49
updateOnlineContactsTimer_ = new QTimer(this);
50
connect(updateOnlineContactsTimer_, SIGNAL(timeout()), SLOT(updateOnlineContactsFlag()));
51
updateOnlineContactsTimer_->setSingleShot(true);
52
updateOnlineContactsTimer_->setInterval(100);
55
ContactListGroup::~ContactListGroup()
60
void ContactListGroup::clearGroup()
62
foreach(PsiContact* contact, contacts_)
63
removeContact(contact);
64
qDeleteAll(contacts_);
69
* Used only in fullName().
71
QString ContactListGroup::internalGroupName() const
76
QString ContactListGroup::fullName() const
79
const ContactListGroup* group = this;
81
if (!group->internalGroupName().isEmpty())
82
name.prepend(group->internalGroupName());
83
group = group->parent();
85
return name.join(groupDelimiter());
88
const QString& ContactListGroup::groupDelimiter()
90
return GROUP_DELIMITER;
93
void ContactListGroup::setGroupDelimiter(const QString& str)
95
GROUP_DELIMITER = str;
98
QString ContactListGroup::sanitizeGroupName(const QString& name) const
100
return name.split(groupDelimiter(), QString::SkipEmptyParts).join(groupDelimiter());
103
QStringList ContactListGroup::sanitizeGroupNames(const QStringList& names) const
105
QStringList sanitized;
106
foreach(QString name, names) {
107
sanitized.append(sanitizeGroupName(name));
112
ContactListModel::Type ContactListGroup::type() const
114
return ContactListModel::GroupType;
117
const QString& ContactListGroup::name() const
122
void ContactListGroup::setName(const QString& name)
124
model()->renameGroup(this, name);
128
* Renames the group without telling the model.
130
void ContactListGroup::quietSetName(const QString& name)
133
model_->groupCache()->removeGroup(this);
138
model_->groupCache()->addGroup(this);
141
bool ContactListGroup::isExpandable() const
146
bool ContactListGroup::expanded() const
148
return model()->groupState()->groupExpanded(this);
151
void ContactListGroup::setExpanded(bool expanded)
153
model()->groupState()->setGroupExpanded(this, expanded);
156
bool ContactListGroup::isEditable() const
159
foreach(ContactListItemProxy* proxy, items_) {
161
if (proxy->item()->isEditable()) {
168
return result && model()->contactList()->haveAvailableAccounts();
171
bool ContactListGroup::isRemovable() const
174
foreach(ContactListItemProxy* proxy, items_) {
176
if (proxy->item()->isRemovable()) {
183
return result && isEditable();
186
ContactListItemMenu* ContactListGroup::contextMenu(ContactListModel* model)
188
return new ContactListGroupMenu(this, model);
191
void ContactListGroup::addItem(ContactListItemProxy* item)
193
Q_ASSERT(!items_.contains(item));
194
int index = items_.count();
195
model_->itemAboutToBeInserted(this, index);
197
model_->insertedItem(this, index);
198
updateOnlineContactsTimer_->start();
201
void ContactListGroup::removeItem(ContactListItemProxy* item)
203
int index = items_.indexOf(item);
204
Q_ASSERT(index != -1);
205
model_->itemAboutToBeRemoved(this, index);
206
items_.remove(index);
208
model_->removedItem(this, index);
209
updateOnlineContactsTimer_->start();
213
* contactGroups handling rules:
214
* 1. List is empty: we must not add this contact to self;
215
* 2. List contains null QString() element: we must add contact to General group
216
* 3. List contains non-null QString() elements: those contain group names (with group separators)
218
void ContactListGroup::addContact(PsiContact* contact, QStringList contactGroups)
220
if (contactGroups.isEmpty())
223
if (findContact(contact))
225
Q_ASSERT(!contacts_.contains(contact));
226
CL_DEBUG("ContactListGroup(%x)::addContact: %s (items = %d, contacts = %d)", this, qPrintable(contact->jid().full()), items_.count(), contacts_.count());
227
contacts_.append(contact);
228
addItem(new ContactListItemProxy(this, contact));
230
model_->groupCache()->addContact(this, contact);
233
void ContactListGroup::removeContact(PsiContact* contact)
235
int index = contacts_.indexOf(contact);
236
Q_ASSERT(index != -1);
237
CL_DEBUG("ContactListGroup(%x)::removeContact: %s (items = %d, contacts = %d)", this, qPrintable(contact->jid().full()), items_.count(), contacts_.count());
238
removeItem(findContact(contact));
239
contacts_.remove(index);
241
model_->groupCache()->removeContact(this, contact);
244
// Some room for future optimizations here
245
ContactListItemProxy* ContactListGroup::findContact(PsiContact* contact) const
247
foreach(ContactListItemProxy* item, items_)
248
if (item->item() == contact)
253
ContactListItemProxy* ContactListGroup::findGroup(ContactListGroup* group) const
255
foreach(ContactListItemProxy* item, items_)
256
if (item->item() == group)
261
// ContactListItemProxy* ContactListGroup::findAccount(ContactListAccountGroup* account) const
263
// foreach(ContactListItemProxy* item, items_)
264
// if (item->item() == account)
269
void ContactListGroup::contactUpdated(PsiContact* contact)
271
ContactListItemProxy* item = findContact(contact);
274
updateOnlineContactsTimer_->start();
275
model_->updatedItem(item);
278
void ContactListGroup::contactGroupsChanged(PsiContact* contact, QStringList contactGroups)
280
if (contactGroups.isEmpty()) {
281
if (contacts_.contains(contact))
282
removeContact(contact);
284
else if (!findContact(contact)) {
285
addContact(contact, contactGroups);
288
updateOnlineContactsTimer_->start();
291
ContactListItemProxy* ContactListGroup::item(int index) const
293
Q_ASSERT(index >= 0);
294
Q_ASSERT(index < items_.count());
295
return items_.at(index);
298
int ContactListGroup::itemsCount() const
300
return items_.count();
303
// Some room for optimizations here
304
int ContactListGroup::indexOf(const ContactListItem* item) const
306
for (int i = 0; i < items_.count(); ++i)
307
if (items_.at(i)->item() == item)
313
ContactListGroup* ContactListGroup::parent() const
318
QModelIndex ContactListGroup::toModelIndex() const
321
return QModelIndex();
323
int index = parent()->indexOf(this);
324
return model()->itemProxyToModelIndex(parent()->item(index), index);
327
const QVector<ContactListItemProxy*>& ContactListGroup::items() const
332
bool ContactListGroup::haveOnlineContacts() const
334
return haveOnlineContacts_;
337
int ContactListGroup::onlineContactsCount() const
339
return onlineContactsCount_;
342
int ContactListGroup::totalContactsCount() const
344
return totalContactsCount_;
347
int ContactListGroup::contactsCount() const
349
return contacts_.count();
352
void ContactListGroup::updateOnlineContactsFlag()
354
updateOnlineContactsTimer_->stop();
358
bool haveOnlineContacts = false;
359
int onlineContactsCount = 0;
360
int totalContactsCount = 0;
361
foreach(ContactListItemProxy* item, items_) {
362
PsiContact* contact = 0;
363
ContactListGroup* group = 0;
364
if ((contact = dynamic_cast<PsiContact*>(item->item()))) {
365
++totalContactsCount;
366
if (contact->isOnline()) {
367
haveOnlineContacts = true;
368
++onlineContactsCount;
372
else if ((group = dynamic_cast<ContactListGroup*>(item->item()))) {
373
totalContactsCount += group->totalContactsCount();
374
if (group->haveOnlineContacts()) {
375
haveOnlineContacts = true;
376
onlineContactsCount += group->onlineContactsCount();
382
if (haveOnlineContacts_ != haveOnlineContacts) {
383
haveOnlineContacts_ = haveOnlineContacts;
385
parent()->updateOnlineContactsFlag();
386
model_->updatedGroupVisibility(this);
390
if (onlineContactsCount != onlineContactsCount_ ||
391
totalContactsCount != totalContactsCount_)
393
onlineContactsCount_ = onlineContactsCount;
394
totalContactsCount_ = totalContactsCount;
396
model()->updatedItem(parent()->findGroup(this));
401
bool ContactListGroup::isFake() const
403
if (items_.count() != contacts_.count())
406
foreach(PsiContact* contact, contacts_) {
407
if (!contact->isFake())
413
return name().startsWith(FakeGroupContact::defaultGroupName());
419
bool ContactListGroup::compare(const ContactListItem* other) const
421
const ContactListGroup* group = dynamic_cast<const ContactListGroup*>(other);
423
if (group->isSpecial()) {
424
return !group->compare(this);
427
int order = model()->groupState()->groupOrder(group) - model()->groupState()->groupOrder(this);
433
return ContactListItem::compare(other);
436
QList<PsiContact*> ContactListGroup::contacts() const
438
QList<PsiContact*> result;
439
contactsHelper(&result);
443
void ContactListGroup::contactsHelper(QList<PsiContact*>* contacts) const
445
foreach(PsiContact* contact, contacts_) {
446
if (!contacts->contains(contact))
447
contacts->append(contact);
450
foreach(ContactListItemProxy* item, items_) {
451
ContactListGroup* group = dynamic_cast<ContactListGroup*>(item->item());
453
contactsHelper(contacts);
458
void ContactListGroup::dumpTree(int indent) const
461
foreach(ContactListItemProxy* item, items_) {
462
ContactListGroup* group = dynamic_cast<ContactListGroup*>(item->item());
464
dumpTree(indent + 1);
466
dumpInfo(item->item(), indent + 1);
470
void ContactListGroup::dumpInfo(const ContactListItem* item, int indent) const
472
qWarning("%sname = '%s', type = %s", qPrintable(QString(indent, ' ')),
473
qPrintable(item->name()),
474
dynamic_cast<const ContactListGroup*>(item) ? "group" : "contact");
477
void ContactListGroup::dumpTree() const
483
bool ContactListGroup::isSpecial() const
488
ContactListGroup::SpecialType ContactListGroup::specialGroupType() const
490
return SpecialType_None;