/* * contactview.cpp - contact list widget * Copyright (C) 2001, 2002 Justin Karneges * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include"contactview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include"im.h" #include"common.h" #include"userlist.h" #include"psiaccount.h" #include"psicon.h" #include"iconaction.h" #include"alerticon.h" #include"avatars.h" //---------------------------------------------------------------------------- // ContactProfile //---------------------------------------------------------------------------- class ContactProfile::Entry { public: Entry() { alerting = false; cvi.setAutoDelete(true); } ~Entry() { } UserListItem u; QPtrList cvi; bool alerting; Icon anim; }; class ContactProfile::Private : public QObject { Q_OBJECT public: Private() {} QString name; ContactView *cv; ContactViewItem *cvi; ContactViewItem *self; UserListItem su; QPtrList roster; QPtrList groups; int oldstate; QTimer *t; PsiAccount *pa; bool v_enabled; public slots: /* * \brief This slot is toggled when number of active accounts is changed * * At the moment, it tries to recalculate the roster size. */ void numAccountsChanged() { cv->recalculateSize(); } }; ContactProfile::ContactProfile(PsiAccount *pa, const QString &name, ContactView *cv, bool unique) { d = new Private; d->pa = pa; d->v_enabled = d->pa->enabled(); d->name = name; d->cv = cv; d->cv->link(this); d->t = new QTimer; connect(d->t, SIGNAL(timeout()), SLOT(updateGroups())); connect(pa->psi(), SIGNAL(accountCountChanged()), d, SLOT(numAccountsChanged())); d->roster.setAutoDelete(true); d->self = 0; if(!unique) d->cvi = new ContactViewItem(name, this, d->cv); else d->cvi = 0; d->oldstate = -2; deferredUpdateGroups(); } ContactProfile::~ContactProfile() { // delete the roster clear(); // clean up delete d->self; delete d->cvi; delete d->t; d->cv->unlink(this); delete d; } void ContactProfile::setEnabled(bool e) { d->v_enabled = e; if(d->v_enabled){ if(!d->cvi) d->cvi = new ContactViewItem(d->name, this, d->cv); addAllNeededContactItems(); } else{ if(d->self) removeSelf(); removeAllUnneededContactItems(); if(d->cvi) delete d->cvi; d->cvi = 0; d->self = 0; } } ContactView *ContactProfile::contactView() const { return d->cv; } ContactViewItem *ContactProfile::self() const { return d->self; } PsiAccount *ContactProfile::psiAccount() const { return d->pa; } const QString & ContactProfile::name() const { return d->name; } void ContactProfile::setName(const QString &name) { d->name = name; if(d->cvi) d->cvi->setProfileName(name); } void ContactProfile::setName(const char *s) { QObject::setName(s); } void ContactProfile::setState(int state) { if(state == d->oldstate) return; d->oldstate = state; if(d->cvi) { d->cv->resetAnim(); d->cvi->setProfileState(state); } } void ContactProfile::setUsingSSL(bool on) { if(d->cvi) d->cvi->setProfileSSL(on); } ContactViewItem *ContactProfile::addGroup(int type) { ContactViewItem *item; QString gname; if(type == ContactViewItem::gGeneral) gname = tr("General"); else if(type == ContactViewItem::gNotInList) gname = tr("Not in list"); else if(type == ContactViewItem::gAgents) gname = tr("Agents/Transports"); else if(type == ContactViewItem::gPrivate) gname = tr("Private Messages"); if(d->cvi) item = new ContactViewItem(gname, type, this, d->cvi); else item = new ContactViewItem(gname, type, this, d->cv); if(type == ContactViewItem::gAgents && !d->cv->isShowAgents()) item->setVisible(false); d->groups.append(item); return item; } ContactViewItem *ContactProfile::addGroup(const QString &name) { ContactViewItem *item; if(d->cvi) item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cvi); else item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cv); d->groups.append(item); return item; } // check for special group ContactViewItem *ContactProfile::checkGroup(int type) { ContactViewItem *item; if(d->cvi) item = (ContactViewItem *)d->cvi->firstChild(); else item = (ContactViewItem *)d->cv->firstChild(); for(; item; item = (ContactViewItem *)item->nextSibling()) { if(item->type() == ContactViewItem::Group && item->groupType() == type) return item; } return 0; } // make a tooltip with account information QString ContactProfile::makeTip(bool trim, bool doLinkify) const { if (d->cvi) return "
" + d->cvi->text(0) + " " + d->cvi->groupInfo() + "
" + d->su.makeBareTip(trim,doLinkify) + "
"; else return d->su.makeTip(trim,doLinkify); } // check for user group ContactViewItem *ContactProfile::checkGroup(const QString &name) { ContactViewItem *item; if(d->cvi) item = (ContactViewItem *)d->cvi->firstChild(); else item = (ContactViewItem *)d->cv->firstChild(); for(; item; item = (ContactViewItem *)item->nextSibling()) { if(item->type() == ContactViewItem::Group && item->groupType() == ContactViewItem::gUser && item->groupName() == name) return item; } return 0; } ContactViewItem *ContactProfile::ensureGroup(int type) { ContactViewItem *group_item = checkGroup(type); if(!group_item) group_item = addGroup(type); return group_item; } ContactViewItem *ContactProfile::ensureGroup(const QString &name) { ContactViewItem *group_item = checkGroup(name); if(!group_item) group_item = addGroup(name); return group_item; } void ContactProfile::checkDestroyGroup(const QString &group) { ContactViewItem *group_item = checkGroup(group); if(group_item) checkDestroyGroup(group_item); } void ContactProfile::checkDestroyGroup(ContactViewItem *group) { if(group->childCount() == 0) { d->groups.remove(group); delete group; } } void ContactProfile::updateEntry(const UserListItem &u) { if (u.isSelf()) { // Update the self item d->su = u; // Show and/or update item if necessary if (d->cv->isShowSelf() || d->su.userResourceList().count() > 1) { if (d->self) { updateSelf(); } else { addSelf(); } } else { removeSelf(); } } else { Entry *e = findEntry(u.jid()); if(!e) { e = new Entry; d->roster.append(e); e->u = u; } else { e->u = u; removeUnneededContactItems(e); // update remaining items QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current()); ++it) { i->setContact(&e->u); if(!u.isAvailable()) i->stopAnimateNick(); } } deferredUpdateGroups(); addNeededContactItems(e); } } void ContactProfile::updateSelf() { if (d->self) { d->self->setContact(&d->su); if(!d->su.isAvailable()) d->self->stopAnimateNick(); } } void ContactProfile::addSelf() { if(!d->self) { if(!d->cvi) return; d->self = new ContactViewItem(&d->su, this, d->cvi); } } void ContactProfile::removeSelf() { if (d->self) { delete d->self; d->self = 0; } } ContactViewItem *ContactProfile::addContactItem(Entry *e, ContactViewItem *group_item) { ContactViewItem *i = new ContactViewItem(&e->u, this, group_item); e->cvi.append(i); if(e->alerting) i->setAlert(&e->anim); deferredUpdateGroups(); //printf("ContactProfile: adding [%s] to group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1()); return i; } /* * \brief Ensures that specified Entry is present in contactlist * * \param e - Entry with the necessary data about item * \param group_item - ContactViewItem that will be the group for this item */ ContactViewItem *ContactProfile::ensureContactItem(Entry *e, ContactViewItem *group_item) { d->cv->recalculateSize(); QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current()); ++it) { ContactViewItem *g = (ContactViewItem *)static_cast(i)->parent(); if(g == group_item) return i; } return addContactItem(e, group_item); } /* * \brief Removes specified item from ContactView * * \param e - Entry with item's data * \param i - ContactViewItem corresponding to the e */ void ContactProfile::removeContactItem(Entry *e, ContactViewItem *i) { d->cv->recalculateSize(); ContactViewItem *group_item = (ContactViewItem *)static_cast(i)->parent(); //printf("ContactProfile: removing [%s] from group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1()); e->cvi.removeRef(i); deferredUpdateGroups(); checkDestroyGroup(group_item); } void ContactProfile::addNeededContactItems(Entry *e) { if(!d->v_enabled) return; const UserListItem &u = e->u; if(u.inList()) { // don't add if we're not supposed to see it if(u.isTransport()) { if(!d->cv->isShowAgents()) return; } else { if(!e->alerting) { if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden())) return; } } } if(u.isPrivate()) ensureContactItem(e, ensureGroup(ContactViewItem::gPrivate)); else if(!u.inList()) ensureContactItem(e, ensureGroup(ContactViewItem::gNotInList)); else if(u.isTransport()) ensureContactItem(e, ensureGroup(ContactViewItem::gAgents)); else if(u.groups().isEmpty()) ensureContactItem(e, ensureGroup(ContactViewItem::gGeneral)); else { const QStringList &groups = u.groups(); for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) ensureContactItem(e, ensureGroup(*git)); } } void ContactProfile::removeUnneededContactItems(Entry *e) { const UserListItem &u = e->u; if(u.inList()) { bool delAll = !d->v_enabled; if(u.isTransport()) { if(!d->cv->isShowAgents()) delAll = true; } else { if(!e->alerting) { if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden())) delAll = true; } } if(delAll) { clearContactItems(e); return; } } QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current());) { bool del = false; ContactViewItem *g = (ContactViewItem *)static_cast(i)->parent(); if(g->groupType() == ContactViewItem::gNotInList && u.inList()) del = true; else if(g->groupType() != ContactViewItem::gNotInList && g->groupType() != ContactViewItem::gPrivate && !u.inList()) del = true; else if(g->groupType() == ContactViewItem::gAgents && !u.isTransport()) del = true; else if(g->groupType() != ContactViewItem::gAgents && u.isTransport()) del = true; else if(g->groupType() == ContactViewItem::gGeneral && !u.groups().isEmpty()) del = true; else if(g->groupType() != ContactViewItem::gPrivate && g->groupType() != ContactViewItem::gGeneral && u.groups().isEmpty() && !u.isTransport() && u.inList()) del = true; else if(g->groupType() == ContactViewItem::gUser) { const QStringList &groups = u.groups(); if(!groups.isEmpty()) { bool found = false; for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) { if(g->groupName() == *git) { found = true; break; } } if(!found) del = true; } } if(del) removeContactItem(e, i); else ++it; } } void ContactProfile::clearContactItems(Entry *e) { QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current());) removeContactItem(e, i); } void ContactProfile::addAllNeededContactItems() { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) addNeededContactItems(e); } void ContactProfile::removeAllUnneededContactItems() { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) removeUnneededContactItems(e); } void ContactProfile::removeEntry(const Jid &j) { Entry *e = findEntry(j); if(e) removeEntry(e); } void ContactProfile::removeEntry(Entry *e) { e->alerting = false; clearContactItems(e); d->roster.remove(e); } void ContactProfile::setAlert(const Jid &j, const Icon *anim) { if(d->su.jid().compare(j)) { if(d->self) d->self->setAlert(anim); } else { Entry *e = findEntry(j); if(!e) return; if(!d->cv->isShowAgents() && e->u.isTransport() && e->u.inList()) d->cv->setShowAgents(true); e->alerting = true; e->anim = *anim; addNeededContactItems(e); QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current()); ++it) i->setAlert(anim); if(option.scrollTo) ensureVisible(e); } } void ContactProfile::clearAlert(const Jid &j) { if(d->su.jid().compare(j)) { if(d->self) d->self->clearAlert(); } else { Entry *e = findEntry(j); if(!e) return; e->alerting = false; QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current()); ++it) i->clearAlert(); if(e->u.inList()) removeUnneededContactItems(e); } } void ContactProfile::clear() { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current());) removeEntry(e); } ContactProfile::Entry *ContactProfile::findEntry(const Jid &jid) const { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { if(e->u.jid().compare(jid)) return e; } return 0; } ContactProfile::Entry *ContactProfile::findEntry(ContactViewItem *i) const { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { QPtrListIterator ci(e->cvi); for(ContactViewItem *cvi; (cvi = ci.current()); ++ci) { if(cvi == i) return e; } } return 0; } // return a list of contacts from a CVI group JidList ContactProfile::contactListFromCVGroup(ContactViewItem *group) const { JidList list; for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) { if(item->type() != ContactViewItem::Contact) continue; list.append(item->u()->jid()); } return list; } // return the number of contacts from a CVI group int ContactProfile::contactSizeFromCVGroup(ContactViewItem *group) const { int total = 0; for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) { if(item->type() != ContactViewItem::Contact) continue; ++total; } return total; } // return the number of contacts from a CVI group int ContactProfile::contactsOnlineFromCVGroup(ContactViewItem *group) const { int total = 0; for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) { if(item->type() == ContactViewItem::Contact && item->u()->isAvailable()) ++total; } return total; } // return a list of contacts associated with "groupName" JidList ContactProfile::contactListFromGroup(const QString &groupName) const { JidList list; QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { const UserListItem &u = e->u; if(u.isTransport()) continue; const QStringList &g = u.groups(); if(g.isEmpty()) { if(groupName.isEmpty()) list.append(u.jid()); } else { for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) { if(*git == groupName) { list.append(u.jid()); break; } } } } return list; } // return the number of contacts associated with "groupName" int ContactProfile::contactSizeFromGroup(const QString &groupName) const { int total = 0; QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { const UserListItem &u = e->u; if(u.isTransport()) continue; const QStringList &g = u.groups(); if(g.isEmpty()) { if(groupName.isEmpty()) ++total; } else { for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) { if(*git == groupName) { ++total; break; } } } } return total; } void ContactProfile::updateGroupInfo(ContactViewItem *group) { int type = group->groupType(); if(type == ContactViewItem::gGeneral || type == ContactViewItem::gAgents || type == ContactViewItem::gPrivate || type == ContactViewItem::gUser) { int online = contactsOnlineFromCVGroup(group); int total; if(type == ContactViewItem::gGeneral || type == ContactViewItem::gUser) { QString gname; if(type == ContactViewItem::gUser) gname = group->groupName(); else gname = ""; total = contactSizeFromGroup(gname); } else { total = group->childCount(); } if (option.showGroupCounts) group->setGroupInfo(QString("(%1/%2)").arg(online).arg(total)); } else if (option.showGroupCounts) { int inGroup = contactSizeFromCVGroup(group); group->setGroupInfo(QString("(%1)").arg(inGroup)); } } QStringList ContactProfile::groupList() const { QStringList groupList; QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { const QStringList &groups = e->u.groups(); for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) { if(qstringlistmatch(groupList, *git) == -1) groupList.append(*git); } } groupList.sort(); return groupList; } void ContactProfile::animateNick(const Jid &j) { if(d->su.jid().compare(j)) { if(d->self) d->self->setAnimateNick(); } Entry *e = findEntry(j); if(!e) return; QPtrListIterator it(e->cvi); for(ContactViewItem *i; (i = it.current()); ++it) i->setAnimateNick(); } void ContactProfile::deferredUpdateGroups() { d->t->start(250, true); } void ContactProfile::updateGroups() { int totalOnline = 0; { QPtrListIterator it(d->roster); for(Entry *e; (e = it.current()); ++it) { if(e->u.isAvailable()) ++totalOnline; } if(d->cvi && option.showGroupCounts) d->cvi->setGroupInfo(QString("(%1/%2)").arg(totalOnline).arg(d->roster.count())); } { QPtrListIterator it(d->groups); for(ContactViewItem *g; (g = it.current()); ++it) updateGroupInfo(g); } } void ContactProfile::ensureVisible(const Jid &j) { Entry *e = findEntry(j); if(!e) return; ensureVisible(e); } void ContactProfile::ensureVisible(Entry *e) { if(!e->alerting) { if(!d->cv->isShowAgents() && e->u.isTransport()) d->cv->setShowAgents(true); if(!d->cv->isShowOffline() && !e->u.isAvailable()) d->cv->setShowOffline(true); if(!d->cv->isShowAway() && e->u.isAway()) d->cv->setShowAway(true); if(!d->cv->isShowHidden() && e->u.isHidden()) d->cv->setShowHidden(true); } ContactViewItem *i = e->cvi.first(); if(!i) return; d->cv->ensureItemVisible(i); } void ContactProfile::doContextMenu(ContactViewItem *i, const QPoint &pos) { bool online = d->pa->loggedIn(); if(i->type() == ContactViewItem::Profile) { QPopupMenu pm; QPopupMenu *am = new QPopupMenu(&pm); am->insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Online Users"), 5); am->insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send server message"), 1); am->insertSeparator(); am->insertItem(/*IconsetFactory::iconPixmap("psi/edit"),*/ tr("Set MOTD"), 2); am->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("Update MOTD"), 3); am->insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Delete MOTD"), 4); const int status_start = 16; QPopupMenu *sm = new QPopupMenu(&pm); sm->insertItem(is->status(STATUS_ONLINE), status2txt(STATUS_ONLINE), STATUS_ONLINE + status_start); sm->insertItem(is->status(STATUS_CHAT), status2txt(STATUS_CHAT), STATUS_CHAT + status_start); sm->insertSeparator(); sm->insertItem(is->status(STATUS_AWAY), status2txt(STATUS_AWAY), STATUS_AWAY + status_start); sm->insertItem(is->status(STATUS_XA), status2txt(STATUS_XA), STATUS_XA + status_start); sm->insertItem(is->status(STATUS_DND), status2txt(STATUS_DND), STATUS_DND + status_start); sm->insertSeparator(); sm->insertItem(is->status(STATUS_INVISIBLE), status2txt(STATUS_INVISIBLE), STATUS_INVISIBLE + status_start); sm->insertSeparator(); sm->insertItem(is->status(STATUS_OFFLINE), status2txt(STATUS_OFFLINE), STATUS_OFFLINE + status_start); pm.insertItem(tr("&Status"), sm); pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("&Add a contact"), 7); pm.insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Service &Discovery"), 9); pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("New &blank message"), 6); pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/xml"), tr("&XML Console"), 10); pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/account"), tr("&Modify Account..."), 0); pm.insertSeparator(); pm.insertItem(tr("&Admin"), am); int x = pm.exec(pos); if(x == -1) return; if(x == 0) d->pa->modify(); else if(x == 1) { Jid j = d->pa->jid().host() + '/' + "announce/online"; actionSendMessage(j); } else if(x == 2) { Jid j = d->pa->jid().host() + '/' + "announce/motd"; actionSendMessage(j); } else if(x == 3) { Jid j = d->pa->jid().host() + '/' + "announce/motd/update"; actionSendMessage(j); } else if(x == 4) { Jid j = d->pa->jid().host() + '/' + "announce/motd/delete"; Message m; m.setTo(j); d->pa->dj_sendMessage(m, false); } else if(x == 5) { // FIXME: will it still work on XMPP servers? Jid j = d->pa->jid().host() + '/' + "admin"; actionDisco(j, ""); } else if(x == 6) { actionSendMessage(""); } else if(x == 7) { d->pa->openAddUserDlg(); } else if(x == 9) { Jid j = d->pa->jid().host(); actionDisco(j, ""); } else if(x == 10) { d->pa->showXmlConsole(); } else if(x >= status_start) { int status = x - status_start; d->pa->changeStatus(status); } } else if(i->type() == ContactViewItem::Group) { QString gname = i->groupName(); QPopupMenu pm; pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send message to group"), 0); if(!option.lockdown.roster) { // disable if it's not a user group if(!online || i->groupType() != ContactViewItem::gUser || gname == ContactView::tr("Hidden")) { d->cv->qa_ren->setEnabled(false); pm.setItemEnabled(2, false); pm.setItemEnabled(3, false); } else d->cv->qa_ren->setEnabled(true); d->cv->qa_ren->addTo(&pm); pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group"), 2); pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group and contacts"), 3); } if(i->groupType() == ContactViewItem::gAgents) { pm.insertSeparator(); pm.insertItem(tr("Hide"), 4); } int x = pm.exec(pos); // restore actions if(option.lockdown.roster) d->cv->qa_ren->setEnabled(false); else d->cv->qa_ren->setEnabled(true); if(x == -1) return; if(x == 0) { JidList list = contactListFromCVGroup(i); // send multi actionSendMessage(list); } else if(x == 2 && online) { int n = QMessageBox::information(d->cv, tr("Remove Group"),tr( "This will cause all contacts in this group to be disassociated with it.\n" "\n" "Proceed?"), tr("&Yes"), tr("&No")); if(n == 0) { JidList list = contactListFromGroup(i->groupName()); for(QValueList::Iterator it = list.begin(); it != list.end(); ++it) actionGroupRemove(*it, gname); } } else if(x == 3 && online) { int n = QMessageBox::information(d->cv, tr("Remove Group and Contacts"),tr( "WARNING! This will remove all contacts associated with this group!\n" "\n" "Proceed?"), tr("&Yes"), tr("&No")); if(n == 0) { JidList list = contactListFromGroup(i->groupName()); for(QValueList::Iterator it = list.begin(); it != list.end(); ++it) { removeEntry(*it); actionRemove(*it); } } } else if(x == 4) { if(i->groupType() == ContactViewItem::gAgents) d->cv->setShowAgents(false); } } else if(i->type() == ContactViewItem::Contact) { bool self = false; UserListItem *u; Entry *e = 0; if(i == d->self) { self = true; u = &d->su; } else { e = findEntry(i); if(!e) return; u = &e->u; } QStringList gl = groupList(); qstringlistisort(gl); // caseless sort bool inList = e ? e->u.inList() : false; bool isPrivate = e ? e->u.isPrivate(): false; bool isAgent = e ? e->u.isTransport() : false; bool avail = e ? e->u.isAvailable() : false; QString groupNameCache = ((ContactViewItem *)static_cast(i)->parent())->groupName(); QPopupMenu pm; if(!self && !inList && !isPrivate && !option.lockdown.roster) { pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("Add/Authorize to contact list"), 10); if(!online) pm.setItemEnabled(10, false); pm.insertSeparator(); } if ( (self && i->isAlerting()) || (!self && e->alerting) ) { d->cv->qa_recv->addTo(&pm); pm.insertSeparator(); } d->cv->qa_send->addTo(&pm); //pm.insertItem(QIconSet(is->url), tr("Send &URL"), 2); const UserResourceList &rl = u->userResourceList(); int base_sendto = 32; int at_sendto = 0; QPopupMenu *s2m = new QPopupMenu(&pm); QPopupMenu *c2m = new QPopupMenu(&pm); /*if ( rl.isEmpty() ) { d->cv->qa_send->addTo(&pm); d->cv->qa_chat->addTo(&pm); } else { d->cv->qa_send->addTo(s2m); s2m->insertSeparator(); d->cv->qa_chat->addTo(c2m); c2m->insertSeparator(); pm.insertItem(is->send.pixmap(), tr("Send &message"), s2m, 17); pm.insertItem(is->chat.base().pixmap(), tr("Open &chat window"), c2m, 18); }*/ //s2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++); //c2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++); if(!rl.isEmpty()) { //s2m->insertSeparator(); //c2m->insertSeparator(); for(UserResourceList::ConstIterator it = rl.begin(); it != rl.end(); ++it) { const UserResource &r = *it; QString rname; if(r.name().isEmpty()) rname = tr("[blank]"); else rname = r.name(); s2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++); c2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++); } } if(!isPrivate) pm.insertItem(tr("Send message to"), s2m, 17); d->cv->qa_chat->setIconSet(IconsetFactory::iconPixmap("psi/start-chat")); d->cv->qa_chat->addTo(&pm); if(!isPrivate) pm.insertItem(tr("Open chat to"), c2m, 18); if(!isPrivate) { if(rl.isEmpty()) { pm.setItemEnabled(17, false); pm.setItemEnabled(18, false); } } int base_hidden = base_sendto + at_sendto; int at_hidden = 0; QStringList hc; if(!isPrivate) { hc = d->pa->hiddenChats(u->jid()); QPopupMenu *cm = new QPopupMenu(&pm); for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) { QString rname; if((*it).isEmpty()) rname = tr("[blank]"); else rname = *it; // determine status int status; const UserResourceList &rl = u->userResourceList(); UserResourceList::ConstIterator uit = rl.find(*it); if(uit != rl.end() || (uit = rl.priority()) != rl.end()) status = makeSTATUS((*uit).status()); else status = STATUS_OFFLINE; cm->insertItem(is->status(status), rname, base_hidden+at_hidden++); } pm.insertItem(tr("Active chats"), cm, 7); if(hc.isEmpty()) pm.setItemEnabled(7, false); } if(!isAgent) { pm.insertSeparator(); pm.insertItem(IconsetFactory::iconPixmap("psi/upload"), tr("Send &file"), 23); if(!online) pm.setItemEnabled(23, false); } // invites int base_gc = base_hidden + at_hidden; int at_gc = 0; QStringList groupchats; if(!isPrivate && !isAgent) { QPopupMenu *gm = new QPopupMenu(&pm); groupchats = d->pa->groupchats(); for(QStringList::ConstIterator it = groupchats.begin(); it != groupchats.end(); ++it) { int id = gm->insertItem(*it, base_gc+at_gc++); if(!online) gm->setItemEnabled(id, false); } pm.insertItem(IconsetFactory::iconPixmap("psi/groupChat"), tr("Invite to"), gm, 14); if(groupchats.isEmpty()) pm.setItemEnabled(14, false); } // weird? if(inList || !isAgent) pm.insertSeparator(); int base_group = base_gc + at_gc; if(!self) { if(inList) { if(!option.lockdown.roster) { d->cv->qa_ren->setEnabled(online); d->cv->qa_ren->addTo(&pm); } } if(!isAgent) { if(inList && !option.lockdown.roster) { QPopupMenu *gm = new QPopupMenu(&pm); gm->setCheckable(true); gm->insertItem(tr("&None"), 8); gm->insertSeparator(); QString g; if(e->u.groups().isEmpty()) gm->setItemChecked(8, true); else g = groupNameCache; int n = 0; gl.remove(ContactView::tr("Hidden")); for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) { QString str; if(n < 9) str = "&"; str += QString("%1. %2").arg(n+1).arg(*it); gm->insertItem(str, n+base_group); if(*it == g) gm->setItemChecked(n+base_group, true); ++n; } if(n > 0) gm->insertSeparator(); gm->insertItem(ContactView::tr("Hidden"),n+base_group); if(g == ContactView::tr("Hidden")) gm->setItemChecked(n+base_group, true); gm->insertSeparator(); gm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("&Create new..."), 9); pm.insertItem(tr("&Group"), gm, 5); if(!online) pm.setItemEnabled(5, false); } } else { pm.insertSeparator(); d->cv->qa_logon->setEnabled( !avail && online ); d->cv->qa_logon->setIconSet(is->status(e->u.jid(), STATUS_ONLINE)); d->cv->qa_logon->addTo(&pm); pm.insertItem(is->status(e->u.jid(), STATUS_OFFLINE), tr("Log off"), 16); if(!avail || !online) pm.setItemEnabled(16, false); pm.insertSeparator(); } } if(inList && !option.lockdown.roster) { QPopupMenu *authm = new QPopupMenu (&pm); authm->insertItem(tr("Resend authorization to"), 6); authm->insertItem(tr("Rerequest authorization from"), 11); authm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/delete"),*/ tr("Remove authorization from"), 15); pm.insertItem (IconsetFactory::iconPixmap("psi/register"), tr("Authorization"), authm, 20); if(!online) pm.setItemEnabled(20, false); } if(!self) { if(!option.lockdown.roster) { if(online || !inList) d->cv->qa_rem->setEnabled(true); else d->cv->qa_rem->setEnabled(false); d->cv->qa_rem->addTo(&pm); } pm.insertSeparator(); } // Avatars #ifdef AVATARS QPopupMenu *avpm = new QPopupMenu(&pm); d->cv->qa_assignAvatar->addTo(avpm); d->cv->qa_clearAvatar->setEnabled(d->pa->avatarFactory()->hasManualAvatar(u->jid())); d->cv->qa_clearAvatar->addTo(avpm); pm.insertItem(tr("&Avatar"), avpm); avpm->setEnabled(option.avatarsEnabled); #endif if(d->pa->psi()->pgp()) { if(u->publicKeyID().isEmpty()) pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-yes"), tr("Assign Open&PGP key"), 21); else pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-no"), tr("Unassign Open&PGP key"), 22); } d->cv->qa_vcard->addTo( &pm ); if(!isPrivate) { d->cv->qa_hist->addTo(&pm); } //if(link_test) { // pm.insertSeparator(); // pm.insertItem(tr("Link Test"), 13); //} QString name = u->jid().full(); QString show = jidnick(u->jid().full(), u->name()); if(name != show) name += QString(" (%1)").arg(u->name()); int x = pm.exec(pos); // restore actions if(option.lockdown.roster) { d->cv->qa_ren->setEnabled(false); d->cv->qa_rem->setEnabled(false); } else { d->cv->qa_ren->setEnabled(true); d->cv->qa_rem->setEnabled(true); } if(x == -1) return; if(x == 0) { actionRecvEvent(u->jid()); } else if(x == 1) { actionSendMessage(u->jid()); } else if(x == 2) { actionSendUrl(u->jid()); } else if(x == 6) { if(online) { actionAuth(u->jid()); QMessageBox::information(d->cv, tr("Authorize"), tr("Sent authorization to %1.").arg(name)); } } else if(x == 8) { if(online) { // if we have groups, but we are setting to 'none', then remove that particular group if(!u->groups().isEmpty()) { QString gname = groupNameCache; actionGroupRemove(u->jid(), gname); } } } else if(x == 9) { if(online) { while(1) { bool ok = false; QString newgroup = QInputDialog::getText(tr("Create New Group"), tr("Enter the new Group name:"), QLineEdit::Normal, QString::null, &ok, d->cv); if(!ok) break; if(newgroup.isEmpty()) continue; // make sure we don't have it already bool found = false; const QStringList &groups = u->groups(); for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it) { if(*it == newgroup) { found = true; break; } } if(!found) { QString gname = groupNameCache; actionGroupRemove(u->jid(), gname); actionGroupAdd(u->jid(), newgroup); break; } } } } else if(x == 10) { if(online) { actionAdd(u->jid()); actionAuth(u->jid()); QMessageBox::information(d->cv, tr("Add"), tr("Added/Authorized %1 to the contact list.").arg(name)); } } else if(x == 11) { if(online) { actionAuthRequest(u->jid()); QMessageBox::information(d->cv, tr("Authorize"), tr("Rerequested authorization from %1.").arg(name)); } } else if(x == 13) { actionTest(u->jid()); } else if(x == 15) { if(online) { int n = QMessageBox::information(d->cv, tr("Remove"), tr("Are you sure you want to remove authorization from %1?").arg(name), tr("&Yes"), tr("&No")); if(n == 0) actionAuthRemove(u->jid()); } } else if(x == 16) { if(online) { Status s=makeStatus(STATUS_OFFLINE,""); actionAgentSetStatus(u->jid(), s); } } else if(x == 21) { actionAssignKey(u->jid()); } else if(x == 22) { actionUnassignKey(u->jid()); } else if(x == 23) { if(online) actionSendFile(u->jid()); } else if(x >= base_sendto && x < base_hidden) { int n = x - base_sendto; int res = n / 2; int type = n % 2; QString rname = ""; //if(res > 0) { const UserResource &r = rl[res]; rname = r.name(); //} QString s = u->jid().userHost(); if(!rname.isEmpty()) { s += '/'; s += rname; } Jid j(s); if(type == 0) actionSendMessage(j); else if(type == 1) actionOpenChatSpecific(j); } else if(x >= base_hidden && x < base_gc) { int n = 0; int n2 = x - base_hidden; QString rname; for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) { if(n == n2) { rname = *it; break; } ++n; } QString s = u->jid().userHost(); if(!rname.isEmpty()) { s += '/'; s += rname; } Jid j(s); actionOpenChatSpecific(j); } else if(x >= base_gc && x < base_group) { if(online) { QString gc = groupchats[x - base_gc]; actionInvite(u->jid(), gc); QMessageBox::information(d->cv, tr("Invitation"), tr("Sent groupchat invitation to %1.").arg(name)); } } else if(x >= base_group) { if(online) { int n = 0; int n2 = x - base_group; QString newgroup; for(QStringList::Iterator it = gl.begin(); it != gl.end(); ++it) { if(n == n2) { newgroup = *it; break; } ++n; } if(newgroup.isEmpty()) newgroup = ContactView::tr("Hidden"); if(n == n2) { // remove the group of this cvi if there is one if(!u->groups().isEmpty()) { //QString gname = ((ContactViewItem *)static_cast(i)->parent())->groupName(); QString gname = groupNameCache; actionGroupRemove(u->jid(), gname); } // add the group actionGroupAdd(u->jid(), newgroup); } } } } } void ContactProfile::scActionDefault(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionDefault(i->u()->jid()); } void ContactProfile::scRecvEvent(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionRecvEvent(i->u()->jid()); } void ContactProfile::scSendMessage(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionSendMessage(i->u()->jid()); } void ContactProfile::scRename(ContactViewItem *i) { if(!d->pa->loggedIn()) return; if((i->type() == ContactViewItem::Contact && i->u()->inList()) || (i->type() == ContactViewItem::Group && i->groupType() == ContactViewItem::gUser && i->groupName() != ContactView::tr("Hidden"))) { i->setRenameEnabled(0, true); i->startRename(0); i->setRenameEnabled(0, false); } } void ContactProfile::scVCard(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionInfo(i->u()->jid()); } void ContactProfile::scHistory(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionHistory(i->u()->jid()); } void ContactProfile::scOpenChat(ContactViewItem *i) { if(i->type() == ContactViewItem::Contact) actionOpenChat(i->u()->jid()); } void ContactProfile::scAgentSetStatus(ContactViewItem *i, Status &s) { if(i->type() != ContactViewItem::Contact) return; if(!i->isAgent()) return; if(i->u()->isAvailable() || !d->pa->loggedIn()) return; actionAgentSetStatus(i->u()->jid(), s); } void ContactProfile::scRemove(ContactViewItem *i) { if(i->type() != ContactViewItem::Contact) return; Entry *e = findEntry(i); if(!e) return; bool ok = true; if(!d->pa->loggedIn()) ok = false; if(!i->u()->inList()) ok = true; if(ok) { QString name = e->u.jid().full(); QString show = jidnick(e->u.jid().full(), e->u.name()); if(name != show) name += QString(" (%1)").arg(e->u.name()); int n = 0; int gt = i->parentGroupType(); if(gt != ContactViewItem::gNotInList && gt != ContactViewItem::gPrivate) { n = QMessageBox::information(d->cv, tr("Remove"), tr("Are you sure you want to remove %1 from your contact list?").arg(name), tr("&Yes"), tr("&No")); } else n = 0; if(n == 0) { Jid j = e->u.jid(); removeEntry(e); actionRemove(j); } } } void ContactProfile::doItemRenamed(ContactViewItem *i, const QString &text) { if(i->type() == ContactViewItem::Contact) { Entry *e = findEntry(i); if(!e) return; // no change? //if(text == i->text(0)) // return; if(text.isEmpty()) { i->resetName(); QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank name.")); return; } //e->u.setName(text); //i->setContact(&e->u); actionRename(e->u.jid(), text); } else { // no change? if(text == i->groupName()) { i->resetGroupName(); return; } if(text.isEmpty()) { i->resetGroupName(); QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank group name.")); return; } // make sure we don't have it already QStringList g = groupList(); bool found = false; for(QStringList::ConstIterator it = g.begin(); it != g.end(); ++it) { if(*it == text) { found = true; break; } } if(found) { i->resetGroupName(); QMessageBox::information(d->cv, tr("Error"), tr("You already have a group with that name.")); return; } QString oldName = i->groupName(); // set group name i->setGroupName(text); // send signal actionGroupRename(oldName, text); } } void ContactProfile::dragDrop(const QString &text, ContactViewItem *i) { if(!d->pa->loggedIn()) return; // get group ContactViewItem *gr; if(i->type() == ContactViewItem::Group) gr = i; else gr = (ContactViewItem *)static_cast(i)->parent(); Jid j(text); if(!j.isValid()) return; Entry *e = findEntry(j); if(!e) return; const UserListItem &u = e->u; QStringList gl = u.groups(); // already in the general group if(gr->groupType() == ContactViewItem::gGeneral && gl.isEmpty()) return; // already in this user group if(gr->groupType() == ContactViewItem::gUser && u.inGroup(gr->groupName())) return; //printf("putting [%s] into group [%s]\n", u.jid().full().latin1(), gr->groupName().latin1()); // remove all other groups from this contact for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) { actionGroupRemove(u.jid(), *it); } if(gr->groupType() == ContactViewItem::gUser) { // add the new group actionGroupAdd(u.jid(), gr->groupName()); } } void ContactProfile::dragDropFiles(const QStringList &files, ContactViewItem *i) { if(files.isEmpty() || !d->pa->loggedIn() || i->type() != ContactViewItem::Contact) return; Entry *e = findEntry(i); if(!e) return; actionSendFiles(e->u.jid(),files); } //---------------------------------------------------------------------------- // ContactView //---------------------------------------------------------------------------- class ContactView::Private : public QObject { Q_OBJECT public: Private(ContactView *_cv) : QObject(_cv) { cv = _cv; autoRosterResizeInProgress = false; // type ahead typeAheadTimer = new QTimer(cv); connect(typeAheadTimer, SIGNAL(timeout()), SLOT(resetTypeAhead())); } ContactView *cv; QTimer *animTimer, *recalculateSizeTimer; QPtrList profiles; QString typeAhead; QTimer *typeAheadTimer; QSize lastSize; bool autoRosterResizeInProgress; bool doTypeAhead(QKeyEvent *e) { typeAheadTimer->start(3000, TRUE); // reset type ahead in 3 seconds of inactivity QString text = e->text().lower(); if ( text.isEmpty() ) { // Shift key could be used for entering '@', '#' and other extended chars // and other modifier keys could be used for layout changing //if ( e->key() != Qt::Key_Shift ) // typeAhead = QString::null; return false; } bool searchFromStart = true; if ( typeAhead.isEmpty() ) searchFromStart = false; bool found = false; typeAhead += text; QListViewItemIterator it(cv); ContactViewItem *item; if ( !searchFromStart && cv->currentItem() ) { for ( ; (item = (ContactViewItem *)it.current()); ++it) { if ( item == cv->currentItem() ) { ++it; // we want the search to start from next contact break; } } } for ( ; (item = (ContactViewItem *)it.current()); ++it) { //qWarning("->%s | %s | %s", text.latin1(), item->text(0).lower().latin1(), typeAhead.latin1()); if ( item->text(0).lower().startsWith(typeAhead) ) { found = true; cv->setSelected(item, true); cv->ensureItemVisible(item); item->optionsUpdate(); item->repaint(); break; } } //qWarning("---------------\n"); if ( !found ) { typeAhead = text; return false; } return true; } public slots: /* * \brief Recalculates the size of ContactView and resizes it accordingly */ void recalculateSize() { // save some CPU if ( !cv->allowResize() ) return; if ( !cv->isUpdatesEnabled() ) return; QSize oldSize = cv->size(); QSize newSize = cv->sizeHint(); if ( newSize.height() != oldSize.height() ) { lastSize = newSize; QWidget *topParent = cv->topLevelWidget(); if ( cv->allowResize() ) { topParent->layout()->setEnabled( false ); // try to reduce some flicker int dh = newSize.height() - oldSize.height(); topParent->resize( topParent->width(), topParent->height() + dh ); autoRosterResizeInProgress = true; QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent ); if ( option.autoRosterSizeGrowTop ) { int newy = topParent->y() - dh; // check, if we need to move roster lower if ( dh > 0 && ( topParent->frameGeometry().top() <= desktop.top() ) ) { newy = desktop.top(); } topParent->move( topParent->x(), newy ); } // check, if we need to move roster upper if ( dh > 0 && ( topParent->frameGeometry().bottom() >= desktop.bottom() ) ) { int newy = desktop.bottom() - topParent->frameGeometry().height(); topParent->move( topParent->x(), newy ); } QTimer::singleShot( 0, this, SLOT( resetAutoRosterResize() ) ); topParent->layout()->setEnabled( true ); } // issue a layout update topParent->layout()->activate(); } } /* * \brief Determine in which direction to grow Psi roster window when autoRosterSize is enabled */ void determineAutoRosterSizeGrowSide() { if ( autoRosterResizeInProgress ) return; QWidget *topParent = cv->topLevelWidget(); QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent ); int top_offs = abs( desktop.top() - topParent->frameGeometry().top() ); int bottom_offs = abs( desktop.bottom() - topParent->frameGeometry().bottom() ); option.autoRosterSizeGrowTop = bottom_offs < top_offs; //qWarning("growTop = %d", option.autoRosterSizeGrowTop); } private slots: void resetAutoRosterResize() { //qWarning("resetAutoRosterResize"); autoRosterResizeInProgress = false; } void resetTypeAhead() { typeAhead = QString::null; } }; ContactView::ContactView(QWidget *parent, const char *name) :QListView(parent, name, QListView::WRepaintNoErase | QListView::WResizeNoErase), QToolTip(viewport()) { d = new Private(this); // setup the QListView setAllColumnsShowFocus(true); setShowToolTips(false); setHScrollBarMode(AlwaysOff); setMinimumSize(96,32); setTreeStepSize(4); setSorting(0,true); topLevelWidget()->installEventFilter( this ); // create the column and hide the header addColumn(""); header()->hide(); setResizeMode(QListView::LastColumn); setDefaultRenameAction(QListView::Accept); // catch signals lcto_active = false; connect(this, SIGNAL(itemRenamed(QListViewItem *, int, const QString &)), SLOT(qlv_itemRenamed(QListViewItem *, int, const QString &))); connect(this, SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)),SLOT(qlv_singleclick(int, QListViewItem *, const QPoint &, int)) ); connect(this, SIGNAL(doubleClicked(QListViewItem *)),SLOT(qlv_doubleclick(QListViewItem *)) ); connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int))); v_showOffline = true; v_showAway = true; v_showHidden = true; v_showAgents = true; v_showSelf = true; d->lastSize = QSize( 0, 0 ); // animation timer d->animTimer = new QTimer(this); d->animTimer->start(120 * 5); d->recalculateSizeTimer = new QTimer; connect(d->recalculateSizeTimer, SIGNAL(timeout()), d, SLOT(recalculateSize())); // actions qa_send = new IconAction("", "psi/sendMessage", tr("Send &message"), QListView::CTRL+QListView::Key_M, this); connect(qa_send, SIGNAL(activated()), SLOT(doSendMessage())); qa_ren = new IconAction("", /*"psi/edit/clear",*/ tr("Re&name"), QListView::Key_F2, this); connect(qa_ren, SIGNAL(activated()), SLOT(doRename())); #ifdef AVATARS qa_assignAvatar = new IconAction("", tr("&Assign Custom Avatar"),QKeySequence(), this); connect(qa_assignAvatar, SIGNAL(activated()), SLOT(doAssignAvatar())); qa_clearAvatar = new IconAction("", tr("&Clear Custom Avatar"), QKeySequence(), this); connect(qa_clearAvatar, SIGNAL(activated()), SLOT(doClearAvatar())); #endif qa_chat = new IconAction("", "psi/start-chat", tr("Open &chat window"), QListView::CTRL+QListView::Key_C, this); connect(qa_chat, SIGNAL(activated()), SLOT(doOpenChat())); qa_hist = new IconAction("", "psi/history", tr("&History"), QListView::CTRL+QListView::Key_H, this); connect(qa_hist, SIGNAL(activated()), SLOT(doHistory())); qa_logon = new IconAction("", tr("&Log on"), QListView::CTRL+QListView::Key_L, this); connect(qa_logon, SIGNAL(activated()), SLOT(doLogon())); qa_recv = new IconAction("", tr("&Receive incoming event"), QListView::CTRL+QListView::Key_R, this); connect(qa_recv, SIGNAL(activated()), SLOT(doRecvEvent())); qa_rem = new IconAction("", "psi/remove", tr("Rem&ove"), QListView::Key_Delete, this); connect(qa_rem, SIGNAL(activated()), SLOT(doRemove())); qa_vcard = new IconAction("", "psi/vCard", tr("User &Info"), QListView::CTRL+QListView::Key_I, this); connect(qa_vcard, SIGNAL(activated()), SLOT(doVCard())); if(option.lockdown.roster) { qa_ren->setEnabled(false); qa_rem->setEnabled(false); } optionsUpdate(); setAcceptDrops(true); viewport()->setAcceptDrops(true); } ContactView::~ContactView() { clear(); delete d; } QTimer *ContactView::animTimer() const { return d->animTimer; } void ContactView::clear() { d->profiles.setAutoDelete(true); d->profiles.clear(); d->profiles.setAutoDelete(false); } void ContactView::link(ContactProfile *cp) { d->profiles.append(cp); } void ContactView::unlink(ContactProfile *cp) { d->profiles.removeRef(cp); } void ContactView::maybeTip(const QPoint &pos) { ContactViewItem *i = (ContactViewItem *)itemAt(pos); if(!i) return; QRect r(itemRect(i)); if(i->type() == ContactViewItem::Contact) tip(r, i->u()->makeTip(true, false)); else if(i->type() == ContactViewItem::Profile) { tip(r, i->contactProfile()->makeTip(true, false)); } else tip(r, i->text(0) + " " + i->groupInfo()); } void ContactView::keyPressEvent(QKeyEvent *e) { int key = e->key(); if(key == QListView::Key_Enter || key == QListView::Key_Return) doEnter(); else if(key == QListView::Key_Space && d->typeAhead.isEmpty()) doContext(); else if (key == QListView::Key_Home || key == QListView::Key_End || key == QListView::Key_PageUp || key == QListView::Key_PageDown || key == QListView::Key_Up || key == QListView::Key_Down || key == QListView::Key_Left || key == QListView::Key_Right) { d->typeAhead = QString::null; QListView::keyPressEvent(e); } else { if (!d->doTypeAhead(e)) QListView::keyPressEvent(e); } } void ContactView::setShowOffline(bool x) { bool oldstate = v_showOffline; v_showOffline = x; if(v_showOffline != oldstate) { showOffline(v_showOffline); QPtrListIterator it(d->profiles); for(ContactProfile *cp; (cp = it.current()); ++it) { if(!v_showOffline) cp->removeAllUnneededContactItems(); else cp->addAllNeededContactItems(); } } } void ContactView::setShowAway(bool x) { bool oldstate = v_showAway; v_showAway = x; if(v_showAway != oldstate) { showAway(v_showAway); QPtrListIterator it(d->profiles); for(ContactProfile *cp; (cp = it.current()); ++it) { if(!v_showAway) cp->removeAllUnneededContactItems(); else cp->addAllNeededContactItems(); } } } void ContactView::setShowHidden(bool x) { bool oldstate = v_showHidden; v_showHidden = x; if(v_showHidden != oldstate) { showHidden(v_showHidden); QPtrListIterator it(d->profiles); for(ContactProfile *cp; (cp = it.current()); ++it) { if(!v_showHidden) cp->removeAllUnneededContactItems(); else cp->addAllNeededContactItems(); } } } /* * \brief Shows/hides the self contact in roster * * \param x - boolean variable specifies whether to show self-contact or not */ void ContactView::setShowSelf(bool x) { if (v_showSelf != x) { v_showSelf = x; showSelf(v_showSelf); QPtrListIterator it(d->profiles); for(ContactProfile *cp; (cp = it.current()); ++it) { if (v_showSelf && ! cp->self()) { cp->addSelf(); } else if (!v_showSelf && cp->self() && cp->self()->u()->userResourceList().count() <= 1) { cp->removeSelf(); } } recalculateSize(); } } /* * \brief Event filter. Nuff said. */ bool ContactView::eventFilter( QObject *obj, QEvent *event ) { if ( event->type() == QEvent::Move ) d->determineAutoRosterSizeGrowSide(); return QListView::eventFilter( obj, event ); } void ContactView::setShowAgents(bool x) { bool oldstate = v_showAgents; v_showAgents = x; if(v_showAgents != oldstate) { showAgents(v_showAgents); QPtrListIterator it(d->profiles); for(ContactProfile *cp; (cp = it.current()); ++it) { if(!v_showAgents) cp->removeAllUnneededContactItems(); else cp->addAllNeededContactItems(); } } } // right-click context menu void ContactView::qlv_contextMenuRequested(QListViewItem *lvi, const QPoint &pos, int c) { if(option.useleft) return; qlv_contextPopup(lvi, pos, c); } void ContactView::qlv_contextPopup(QListViewItem *lvi, const QPoint &pos, int) { ContactViewItem *i = (ContactViewItem *)lvi; if(!i) return; i->contactProfile()->doContextMenu(i, pos); } void ContactView::qlv_singleclick(int button, QListViewItem *i, const QPoint &pos, int c) { lcto_active = false; QToolTip::hide(); if(!i) return; ContactViewItem *item = (ContactViewItem *)i; setSelected(item, true); if(button == QListView::MidButton) { if(item->type() == ContactViewItem::Contact) item->contactProfile()->scActionDefault(item); } else { const QPixmap * pix = item->pixmap(0); if (button == QListView::LeftButton && item->type() == ContactViewItem::Group && pix && viewport()->mapFromGlobal(pos).x() <= pix->width() + treeStepSize()) { setOpen(item, !item->isOpen()); } else if(option.useleft) { if(button == QListView::LeftButton) { if(option.singleclick) { qlv_contextPopup(i, pos, c); } else { lcto_active = true; lcto_pos = pos; lcto_item = i; QTimer::singleShot(QApplication::doubleClickInterval()/2, this, SLOT(leftClickTimeOut())); } } else if(option.singleclick && button == QListView::RightButton) { if(item->type() == ContactViewItem::Contact) item->contactProfile()->scActionDefault(item); } } else { //if(button == QListView::RightButton) { // qlv_contextPopup(i, pos, c); //} /*else*/if(button == QListView::LeftButton && option.singleclick) { if(item->type() == ContactViewItem::Contact) item->contactProfile()->scActionDefault(item); } } } d->typeAhead = ""; } void ContactView::qlv_doubleclick(QListViewItem *i) { lcto_active = false; if(!i) return; if(option.singleclick) return; ContactViewItem *item = (ContactViewItem *)i; item->contactProfile()->scActionDefault(item); d->typeAhead = ""; } void ContactView::qlv_itemRenamed(QListViewItem *lvi, int, const QString &text) { ContactViewItem *i = (ContactViewItem *)lvi; i->contactProfile()->doItemRenamed(i, text); } void ContactView::leftClickTimeOut() { if(lcto_active) { QToolTip::hide(); qlv_contextPopup(lcto_item, lcto_pos, 0); lcto_active = false; } } void ContactView::optionsUpdate() { // set the font QFont f; f.fromString(option.font[fRoster]); QListView::setFont(f); // set the text and background colors QPalette mypal = QListView::palette(); mypal.setColor(QColorGroup::Text, option.color[cOnline]); mypal.setColor(QColorGroup::Base, option.color[cListBack]); QListView::setPalette(mypal); // reload the icons QListViewItemIterator it(this); ContactViewItem *item; for(; it.current() ; ++it) { item = (ContactViewItem *)it.current(); item->optionsUpdate(); } // resize if necessary if (option.autoRosterSize) recalculateSize(); update(); } void ContactView::resetAnim() { for(QListViewItemIterator it(this); it.current() ; ++it) { ContactViewItem *item = (ContactViewItem *)it.current(); if(item->isAlerting()) item->resetAnim(); } } void ContactView::doRecvEvent() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scRecvEvent(i); } void ContactView::doRename() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scRename(i); } void ContactView::doAssignAvatar() { // FIXME: Should check the supported filetypes dynamically QString file = QFileDialog::getOpenFileName("", tr("All files (*.png *.jpg *.gif)"), this, 0, tr("Choose an image")); if (!file.isNull()) { ContactViewItem *i = (ContactViewItem *)selectedItem(); i->contactProfile()->psiAccount()->avatarFactory()->importManualAvatar(i->u()->jid(),file); } } void ContactView::doClearAvatar() { ContactViewItem *i = (ContactViewItem *)selectedItem(); i->contactProfile()->psiAccount()->avatarFactory()->removeManualAvatar(i->u()->jid()); } void ContactView::doEnter() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scActionDefault(i); } void ContactView::doContext() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; ensureItemVisible(i); if(i->type() == ContactViewItem::Group) setOpen(i, !i->isOpen()); else qlv_contextPopup(i, viewport()->mapToGlobal(QPoint(32, itemPos(i))), 0); } void ContactView::doSendMessage() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scSendMessage(i); } void ContactView::doOpenChat() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scOpenChat(i); } void ContactView::doHistory() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scHistory(i); } void ContactView::doVCard() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scVCard(i); } void ContactView::doLogon() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; Status s=i->contactProfile()->psiAccount()->status(); i->contactProfile()->scAgentSetStatus(i, s); } void ContactView::doRemove() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return; i->contactProfile()->scRemove(i); } QDragObject *ContactView::dragObject() { ContactViewItem *i = (ContactViewItem *)selectedItem(); if(!i) return 0; if(i->type() != ContactViewItem::Contact) return 0; QDragObject *d = new QTextDrag(i->u()->jid().full(), this); d->setPixmap(IconsetFactory::iconPixmap("status/online"), QPoint(8,8)); return d; } bool ContactView::allowResize() const { if ( !option.autoRosterSize ) return false; if ( topLevelWidget()->isMaximized() ) return false; return true; } QSize ContactView::minimumSizeHint() const { return QSize( minimumWidth(), minimumHeight() ); } QSize ContactView::sizeHint() const { // save some CPU if ( !allowResize() ) return minimumSizeHint(); QSize s( QListView::sizeHint().width(), 0 ); int border = 5; int h = border; QListView *listView = (QListView *)this; QListViewItemIterator it( listView ); while ( it.current() ) { if ( it.current()->isVisible() ) { // also we need to check whether the group is open or closed bool show = true; QListViewItem *item = it.current()->parent(); while ( item ) { if ( !item->isOpen() ) { show = false; break; } item = item->parent(); } if ( show ) h += it.current()->height(); } ++it; } QWidget *topParent = topLevelWidget(); QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent ); int dh = h - d->lastSize.height(); // check that our dialog's height doesn't exceed the desktop's if ( allowResize() && dh > 0 && (topParent->frameGeometry().height() + dh) >= desktop.height() ) { h = desktop.height() - ( topParent->frameGeometry().height() - d->lastSize.height() ); } int minH = minimumSizeHint().height(); if ( h < minH ) h = minH + border; s.setHeight( h ); return s; } /* * \brief Adds the request to recalculate the ContactView size to the event queue */ void ContactView::recalculateSize() { d->recalculateSizeTimer->start( 0, true ); } //---------------------------------------------------------------------------- // ContactViewItem //---------------------------------------------------------------------------- class ContactViewItem::Private { private: ContactViewItem *cvi; public: Private(ContactViewItem *parent, ContactProfile *_cp) { cvi = parent; cp = _cp; u = 0; icon = lastIcon = 0; } ~Private() { } void initGroupState() { UserAccount::GroupData gd = groupData(); cvi->setOpen(gd.open); } QString getGroupName() { QString group; if ( type == Profile ) group = "/\\/" + profileName + "\\/\\"; else group = groupName; return group; } QMap *groupState() { return (QMap *)&cp->psiAccount()->userAccount().groupState; } UserAccount::GroupData groupData() { QMap groupState = (QMap)cp->psiAccount()->userAccount().groupState; QMap::Iterator it = groupState.find(getGroupName()); UserAccount::GroupData gd; gd.open = true; gd.rank = 0; if ( it != groupState.end() ) gd = it.data(); return gd; } int type; ContactProfile *cp; int status; // profiles QString profileName; bool ssl; // groups int groupType; QString groupName; QString groupInfo; // contact UserListItem *u; bool isAgent; bool alerting; bool animatingNick; Icon *icon, *lastIcon; int animateNickX, animateNickColor; // nick animation }; ContactViewItem::ContactViewItem(const QString &profileName, ContactProfile *cp, ContactView *parent) :QListViewItem(parent) { d = new Private(this, cp); d->type = Profile; d->profileName = profileName; d->alerting = false; d->ssl = false; setProfileState(STATUS_OFFLINE); setText(0, profileName); d->initGroupState(); } ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactView *parent) :QListViewItem(parent) { d = new Private(this, cp); d->type = Group; d->groupName = groupName; d->groupType = groupType; d->alerting = false; drawGroupIcon(); resetGroupName(); setDropEnabled(true); d->initGroupState(); } ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactViewItem *parent) :QListViewItem(parent) { d = new Private(this, cp); d->type = Group; d->groupName = groupName; d->groupType = groupType; d->alerting = false; drawGroupIcon(); resetGroupName(); setDropEnabled(true); if(!parent->isVisible()) setVisible(false); d->initGroupState(); } ContactViewItem::ContactViewItem(UserListItem *u, ContactProfile *cp, ContactViewItem *parent) :QListViewItem(parent) { d = new Private(this, cp); d->cp = cp; d->type = Contact; d->u = u; d->alerting = false; d->animatingNick = false; cacheValues(); resetStatus(); resetName(); setDragEnabled(true); setDropEnabled(true); if(!parent->isVisible()) setVisible(false); } ContactViewItem::~ContactViewItem() { setIcon( 0 ); delete d; } void ContactViewItem::cacheValues() { if ( d->u ) { if( !d->u->isAvailable() ) d->status = STATUS_OFFLINE; else d->status = makeSTATUS((*d->u->priority()).status()); d->isAgent = d->u->isTransport(); } } ContactProfile *ContactViewItem::contactProfile() const { return d->cp; } int ContactViewItem::type() const { return d->type; } const QString & ContactViewItem::groupName() const { return d->groupName; } const QString & ContactViewItem::groupInfo() const { return d->groupInfo; } int ContactViewItem::groupType() const { return d->groupType; } UserListItem *ContactViewItem::u() const { return d->u; } int ContactViewItem::status() const { return d->status; } bool ContactViewItem::isAgent() const { return d->isAgent; } bool ContactViewItem::isAlerting() const { return d->alerting; } bool ContactViewItem::isAnimatingNick() const { return d->animatingNick; } int ContactViewItem::parentGroupType() const { ContactViewItem *item = (ContactViewItem *)QListViewItem::parent(); return item->groupType(); } void ContactViewItem::drawGroupIcon() { if ( d->type == Group ) { if ( childCount() == 0 ) setIcon(IconsetFactory::iconPtr("psi/groupEmpty")); else if ( isOpen() ) setIcon(IconsetFactory::iconPtr("psi/groupOpen")); else setIcon(IconsetFactory::iconPtr("psi/groupClosed")); } else if ( d->type == Profile ) { if ( !d->alerting ) setProfileState(d->status); } } void ContactViewItem::paintFocus(QPainter *, const QColorGroup &, const QRect &) { // re-implimented to do nothing. selection is enough of a focus } void ContactViewItem::paintBranches(QPainter *p, const QColorGroup &cg, int w, int, int h) { // paint a square of nothing p->fillRect(0, 0, w, h, cg.base()); } void ContactViewItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int alignment) { if ( d->type == Contact ) { QColorGroup xcg = cg; if(d->status == STATUS_AWAY || d->status == STATUS_XA) xcg.setColor(QColorGroup::Text, option.color[cAway]); else if(d->status == STATUS_DND) xcg.setColor(QColorGroup::Text, option.color[cDND]); else if(d->status == STATUS_OFFLINE) xcg.setColor(QColorGroup::Text, option.color[cOffline]); if(d->animatingNick) { xcg.setColor(QColorGroup::Text, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]); xcg.setColor(QColorGroup::HighlightedText, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]); } QListViewItem::paintCell(p, xcg, column, width, alignment); QFontMetrics fm(p->font()); const QPixmap *pix = pixmap(column); int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8; if ( d->u ) { UserResourceList::ConstIterator it = d->u->priority(); if(it != d->u->userResourceList().end()) { if(d->u->isSecure((*it).name())) { const QPixmap &pix = IconsetFactory::iconPixmap("psi/cryptoYes"); int y = (height() - pix.height()) / 2; p->drawPixmap(x, y, pix); x += 24; } } } } else if ( d->type == Group || d->type == Profile ) { QColorGroup xcg = cg; if(d->type == Profile) { xcg.setColor(QColorGroup::Text, option.color[cProfileFore]); xcg.setColor(QColorGroup::Base, option.color[cProfileBack]); } else if(d->type == Group) { QFont f = p->font(); f.setPointSize(option.smallFontSize); p->setFont(f); xcg.setColor(QColorGroup::Text, option.color[cGroupFore]); if (!option.clNewHeadings) { xcg.setColor(QColorGroup::Base, option.color[cGroupBack]); } } QListViewItem::paintCell(p, xcg, column, width, alignment); QFontMetrics fm(p->font()); const QPixmap *pix = pixmap(column); int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8; if(d->type == Profile) { const QPixmap &pix = d->ssl ? IconsetFactory::iconPixmap("psi/cryptoYes") : IconsetFactory::iconPixmap("psi/cryptoNo"); int y = (height() - pix.height()) / 2; p->drawPixmap(x, y, pix); x += 24; } if(isSelected()) p->setPen(xcg.highlightedText()); else p->setPen(xcg.text()); QFont f_info = p->font(); f_info.setPointSize(option.smallFontSize); p->setFont(f_info); QFontMetrics fm_info(p->font()); //int info_x = width - fm_info.width(d->groupInfo) - 8; int info_x = x; int info_y = ((height() - fm_info.height()) / 2) + fm_info.ascent(); p->drawText((info_x > x ? info_x : x), info_y, d->groupInfo); if(d->type == Group && option.clNewHeadings && !isSelected()) { x += fm.width(d->groupInfo) + 8; if(x < width - 8) { int h = (height() / 2) - 1; p->setPen(QPen(option.color[cGroupBack])); p->drawLine(x, h, width - 8, h); h++; p->setPen(QPen(option.color[cGroupFore])); /*int h = height() / 2; p->setPen(QPen(option.color[cGroupBack], 2));*/ p->drawLine(x, h, width - 8, h); } } else { if (option.outlineHeadings) { p->setPen(QPen(option.color[cGroupFore])); p->drawRect(0, 0, width, height()); } } } } /* * \brief "Opens" or "closes the ContactViewItem * * When the item is in "open" state, all it's children items are visible. * * \param o - if true, the item will be "open" */ void ContactViewItem::setOpen(bool o) { ((ContactView *)listView())->recalculateSize(); QListViewItem::setOpen(o); drawGroupIcon(); // save state UserAccount::GroupData gd = d->groupData(); gd.open = o; d->groupState()->insert(d->getGroupName(), gd); } void ContactViewItem::insertItem(QListViewItem *i) { QListViewItem::insertItem(i); drawGroupIcon(); } void ContactViewItem::takeItem(QListViewItem *i) { QListViewItem::takeItem(i); drawGroupIcon(); } int ContactViewItem::rankGroup(int groupType) const { static int rankgroups[5] = { gGeneral, gUser, gPrivate, gAgents, gNotInList, }; int n; for(n = 0; n < (int)sizeof(rankgroups); ++n) { if(rankgroups[n] == groupType) break; } if(n == sizeof(rankgroups)) return sizeof(rankgroups)-1; return n; } int ContactViewItem::rankStatus(int status) const { static int rankstatuses[7] = { STATUS_CHAT, STATUS_ONLINE, STATUS_AWAY, STATUS_XA, STATUS_DND, STATUS_INVISIBLE, STATUS_OFFLINE, }; int n; for(n = 0; n < (int)sizeof(rankstatuses); ++n) { if(rankstatuses[n] == status) break; } if(n == sizeof(rankstatuses)) return sizeof(rankstatuses)-1; return n; } int ContactViewItem::compare(QListViewItem *lvi, int, bool) const { ContactViewItem *i = (ContactViewItem *)lvi; int ret = 0; if(d->type == Group || d->type == Profile) { // contacts always go before groups if(i->type() == Contact) ret = 1; else if(i->type() == Group) { if ( option.rosterGroupSortStyle == Options::GroupSortStyle_Rank ) { int ourRank = d->groupData().rank; int theirRank = i->d->groupData().rank; ret = ourRank - theirRank; } else { // GroupSortStyle_Alpha ret = rankGroup(d->groupType) - rankGroup(i->groupType()); if(ret == 0) ret = text(0).lower().localeAwareCompare(i->text(0).lower()); } } else if(i->type() == Profile) { if ( option.rosterAccountSortStyle == Options::AccountSortStyle_Rank ) { int ourRank = d->groupData().rank; int theirRank = i->d->groupData().rank; ret = ourRank - theirRank; } else // AccountSortStyle_Alpha ret = text(0).lower().localeAwareCompare(i->text(0).lower()); } } else if(d->type == Contact) { // contacts always go before groups if(i->type() == Group) ret = -1; else { if ( option.rosterContactSortStyle == Options::ContactSortStyle_Status ) { ret = rankStatus(d->status) - rankStatus(i->status()); if(ret == 0) ret = text(0).lower().localeAwareCompare(i->text(0).lower()); } else { // ContactSortStyle_Alpha ret = text(0).lower().localeAwareCompare(i->text(0).lower()); } } } return ret; } void ContactViewItem::setProfileName(const QString &name) { d->profileName = name; setText(0, d->profileName); } void ContactViewItem::setProfileState(int status) { if ( status == -1 ) { setAlert( IconsetFactory::iconPtr("psi/connect") ); } else { d->status = status; clearAlert(); setIcon(is->statusPtr(status)); } } void ContactViewItem::setProfileSSL(bool on) { d->ssl = on; repaint(); } void ContactViewItem::setGroupName(const QString &name) { d->groupName = name; resetGroupName(); updatePosition(); } void ContactViewItem::setGroupInfo(const QString &info) { d->groupInfo = info; repaint(); } void ContactViewItem::resetStatus() { if ( !d->alerting && d->u ) { setIcon(is->statusPtr(d->u)); } } void ContactViewItem::resetName() { resetStatus(); if ( d->u ) { QString s = jidnick(d->u->jid().full(), d->u->name()); if ( s != text(0) ) setText(0, s); } } void ContactViewItem::resetGroupName() { if ( d->groupName != text(0) ) setText(0, d->groupName); } void ContactViewItem::resetAnim() { if ( d->alerting ) { // TODO: think of how to reset animation frame } } void ContactViewItem::setAlert(const Icon *icon) { bool reset = false; if ( !d->alerting ) { d->alerting = true; reset = true; } else { if ( d->lastIcon != icon ) reset = true; } if ( reset ) setIcon(icon, true); } void ContactViewItem::clearAlert() { if ( d->alerting ) { d->alerting = false; //disconnect(static_cast(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animate())); resetStatus(); } } void ContactViewItem::setIcon(const Icon *icon, bool alert) { if ( d->lastIcon == icon ) { return; // cause less flicker. but still have to run calltree valgring skin on psi while online (mblsha). } else d->lastIcon = (Icon *)icon; if ( d->icon ) { disconnect(d->icon, 0, this, 0 ); d->icon->stop(); delete d->icon; d->icon = 0; } QPixmap pix; if ( icon ) { if ( !alert ) d->icon = new Icon(*icon); else d->icon = new AlertIcon(icon); connect(d->icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &))); d->icon->activated(); pix = d->icon->pixmap(); } setPixmap(0, pix); } void ContactViewItem::iconUpdated(const QPixmap &pix) { setPixmap(0, pix); } void ContactViewItem::animateNick() { d->animateNickColor = !d->animateNickColor; repaint(); if(++d->animateNickX >= 16) stopAnimateNick(); } void ContactViewItem::stopAnimateNick() { if ( !d->animatingNick ) return; disconnect(static_cast(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animateNick())); d->animatingNick = false; repaint(); } void ContactViewItem::setAnimateNick() { stopAnimateNick(); connect(static_cast(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), SLOT(animateNick())); d->animatingNick = true; d->animateNickX = 0; animateNick(); } void ContactViewItem::updatePosition() { ContactViewItem *par = (ContactViewItem *)QListViewItem::parent(); if(!par) return; ContactViewItem *after = 0; for(QListViewItem *i = par->firstChild(); i; i = i->nextSibling()) { ContactViewItem *item = (ContactViewItem *)i; // skip self if(item == this) continue; int x = compare(item, 0, true); if(x == 0) continue; if(x < 0) break; after = item; } if(after) moveItem(after); else { QListViewItem *i = par->firstChild(); moveItem(i); i->moveItem(this); } } void ContactViewItem::optionsUpdate() { if(d->type == Group || d->type == Profile) { drawGroupIcon(); } else if(d->type == Contact) { if(!d->alerting) resetStatus(); else resetAnim(); } } void ContactViewItem::setContact(UserListItem *u) { //int oldStatus = d->status; QString oldName = text(0); //bool wasAgent = d->isAgent; QString newName = jidnick(u->jid().full(), u->name()); d->u = u; cacheValues(); bool needUpdate = false; //if(d->status != oldStatus || d->isAgent != wasAgent || !u->presenceError().isEmpty()) { resetStatus(); needUpdate = true; //} if(newName != oldName) { resetName(); needUpdate = true; } if(needUpdate) updatePosition(); repaint(); } bool ContactViewItem::acceptDrop(const QMimeSource *m) const { if ( d->type == Profile ) return false; else if ( d->type == Group ) { if(d->groupType != gGeneral && d->groupType != gUser) return false; } else if ( d->type == Contact ) { if ( d->u && d->u->isSelf() ) return false; ContactViewItem *par = (ContactViewItem *)QListViewItem::parent(); if(par->groupType() != gGeneral && par->groupType() != gUser) return false; } // Files. Note that the QTextDrag test has to come after QUriDrag. if (d->type == Contact && QUriDrag::canDecode(m)) { QStringList l; if (QUriDrag::decodeLocalFiles(m,l) && !l.isEmpty()) return true; } if(!QTextDrag::canDecode(m)) return false; QString str; if(!QTextDrag::decode(m, str)) return false; return true; } void ContactViewItem::dragEntered() { //printf("entered\n"); } void ContactViewItem::dragLeft() { //printf("left\n"); } void ContactViewItem::dropped(QDropEvent *i) { if(!acceptDrop(i)) return; // Files if (QUriDrag::canDecode(i)) { QStringList l; if (QUriDrag::decodeLocalFiles(i,l) && !l.isEmpty()) { d->cp->dragDropFiles(l, this); return; } } // Text if(QTextDrag::canDecode(i)) { QString text; if(QTextDrag::decode(i, text)) d->cp->dragDrop(text, this); } } int ContactViewItem::rtti() const { return 5103; } #include "contactview.moc"