source: psi/trunk/src/contactview.cpp@ 13

Last change on this file since 13 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 74.8 KB
Line 
1/*
2 * contactview.cpp - contact list widget
3 * Copyright (C) 2001, 2002 Justin Karneges
4 *
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.
9 *
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.
14 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"contactview.h"
22
23#include<qapplication.h>
24#include<qptrlist.h>
25#include<qheader.h>
26#include<qtimer.h>
27#include<qpainter.h>
28#include<qpopupmenu.h>
29#include<qmessagebox.h>
30#include<qinputdialog.h>
31#include<qiconset.h>
32#include<qdragobject.h>
33#include<qfiledialog.h>
34#include<qlayout.h>
35#include<stdlib.h>
36#include"im.h"
37#include"common.h"
38#include"userlist.h"
39#include"psiaccount.h"
40#include"psicon.h"
41#include"iconaction.h"
42#include"alerticon.h"
43#include"avatars.h"
44
45//----------------------------------------------------------------------------
46// ContactProfile
47//----------------------------------------------------------------------------
48class ContactProfile::Entry
49{
50public:
51 Entry()
52 {
53 alerting = false;
54 cvi.setAutoDelete(true);
55 }
56 ~Entry()
57 {
58 }
59
60 UserListItem u;
61 QPtrList<ContactViewItem> cvi;
62 bool alerting;
63 Icon anim;
64};
65
66class ContactProfile::Private : public QObject
67{
68 Q_OBJECT
69public:
70 Private() {}
71
72 QString name;
73 ContactView *cv;
74 ContactViewItem *cvi;
75 ContactViewItem *self;
76 UserListItem su;
77 QPtrList<Entry> roster;
78 QPtrList<ContactViewItem> groups;
79 int oldstate;
80 QTimer *t;
81 PsiAccount *pa;
82 bool v_enabled;
83
84public slots:
85 /*
86 * \brief This slot is toggled when number of active accounts is changed
87 *
88 * At the moment, it tries to recalculate the roster size.
89 */
90 void numAccountsChanged()
91 {
92 cv->recalculateSize();
93 }
94};
95
96ContactProfile::ContactProfile(PsiAccount *pa, const QString &name, ContactView *cv, bool unique)
97{
98 d = new Private;
99 d->pa = pa;
100 d->v_enabled = d->pa->enabled();
101 d->name = name;
102 d->cv = cv;
103 d->cv->link(this);
104 d->t = new QTimer;
105 connect(d->t, SIGNAL(timeout()), SLOT(updateGroups()));
106 connect(pa->psi(), SIGNAL(accountCountChanged()), d, SLOT(numAccountsChanged()));
107
108 d->roster.setAutoDelete(true);
109
110 d->self = 0;
111
112 if(!unique)
113 d->cvi = new ContactViewItem(name, this, d->cv);
114 else
115 d->cvi = 0;
116
117 d->oldstate = -2;
118
119 deferredUpdateGroups();
120}
121
122ContactProfile::~ContactProfile()
123{
124 // delete the roster
125 clear();
126
127 // clean up
128 delete d->self;
129 delete d->cvi;
130
131 delete d->t;
132 d->cv->unlink(this);
133
134 delete d;
135}
136
137void ContactProfile::setEnabled(bool e)
138{
139 d->v_enabled = e;
140 if(d->v_enabled){
141 if(!d->cvi)
142 d->cvi = new ContactViewItem(d->name, this, d->cv);
143 addAllNeededContactItems();
144 }
145 else{
146 if(d->self)
147 removeSelf();
148
149 removeAllUnneededContactItems();
150 if(d->cvi)
151 delete d->cvi;
152 d->cvi = 0;
153 d->self = 0;
154 }
155}
156
157ContactView *ContactProfile::contactView() const
158{
159 return d->cv;
160}
161
162ContactViewItem *ContactProfile::self() const
163{
164 return d->self;
165}
166
167PsiAccount *ContactProfile::psiAccount() const
168{
169 return d->pa;
170}
171
172const QString & ContactProfile::name() const
173{
174 return d->name;
175}
176
177void ContactProfile::setName(const QString &name)
178{
179 d->name = name;
180 if(d->cvi)
181 d->cvi->setProfileName(name);
182}
183
184void ContactProfile::setName(const char *s)
185{
186 QObject::setName(s);
187}
188
189void ContactProfile::setState(int state)
190{
191 if(state == d->oldstate)
192 return;
193 d->oldstate = state;
194
195 if(d->cvi) {
196 d->cv->resetAnim();
197 d->cvi->setProfileState(state);
198 }
199}
200
201void ContactProfile::setUsingSSL(bool on)
202{
203 if(d->cvi)
204 d->cvi->setProfileSSL(on);
205}
206
207ContactViewItem *ContactProfile::addGroup(int type)
208{
209 ContactViewItem *item;
210
211 QString gname;
212 if(type == ContactViewItem::gGeneral)
213 gname = tr("General");
214 else if(type == ContactViewItem::gNotInList)
215 gname = tr("Not in list");
216 else if(type == ContactViewItem::gAgents)
217 gname = tr("Agents/Transports");
218 else if(type == ContactViewItem::gPrivate)
219 gname = tr("Private Messages");
220
221 if(d->cvi)
222 item = new ContactViewItem(gname, type, this, d->cvi);
223 else
224 item = new ContactViewItem(gname, type, this, d->cv);
225
226 if(type == ContactViewItem::gAgents && !d->cv->isShowAgents())
227 item->setVisible(false);
228
229 d->groups.append(item);
230
231 return item;
232}
233
234ContactViewItem *ContactProfile::addGroup(const QString &name)
235{
236 ContactViewItem *item;
237 if(d->cvi)
238 item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cvi);
239 else
240 item = new ContactViewItem(name, ContactViewItem::gUser, this, d->cv);
241
242 d->groups.append(item);
243
244 return item;
245}
246
247// check for special group
248ContactViewItem *ContactProfile::checkGroup(int type)
249{
250 ContactViewItem *item;
251 if(d->cvi)
252 item = (ContactViewItem *)d->cvi->firstChild();
253 else
254 item = (ContactViewItem *)d->cv->firstChild();
255
256 for(; item; item = (ContactViewItem *)item->nextSibling()) {
257 if(item->type() == ContactViewItem::Group && item->groupType() == type)
258 return item;
259 }
260
261 return 0;
262}
263
264// make a tooltip with account information
265QString ContactProfile::makeTip(bool trim, bool doLinkify) const
266{
267 if (d->cvi)
268 return "<qt> <center> <b>" + d->cvi->text(0) + " " + d->cvi->groupInfo() + "</b> </center>" + d->su.makeBareTip(trim,doLinkify) + "</qt>";
269 else
270 return d->su.makeTip(trim,doLinkify);
271}
272
273// check for user group
274ContactViewItem *ContactProfile::checkGroup(const QString &name)
275{
276 ContactViewItem *item;
277 if(d->cvi)
278 item = (ContactViewItem *)d->cvi->firstChild();
279 else
280 item = (ContactViewItem *)d->cv->firstChild();
281
282 for(; item; item = (ContactViewItem *)item->nextSibling()) {
283 if(item->type() == ContactViewItem::Group && item->groupType() == ContactViewItem::gUser && item->groupName() == name)
284 return item;
285 }
286
287 return 0;
288}
289
290ContactViewItem *ContactProfile::ensureGroup(int type)
291{
292 ContactViewItem *group_item = checkGroup(type);
293 if(!group_item)
294 group_item = addGroup(type);
295
296 return group_item;
297}
298
299ContactViewItem *ContactProfile::ensureGroup(const QString &name)
300{
301 ContactViewItem *group_item = checkGroup(name);
302 if(!group_item)
303 group_item = addGroup(name);
304
305 return group_item;
306}
307
308void ContactProfile::checkDestroyGroup(const QString &group)
309{
310 ContactViewItem *group_item = checkGroup(group);
311 if(group_item)
312 checkDestroyGroup(group_item);
313}
314
315void ContactProfile::checkDestroyGroup(ContactViewItem *group)
316{
317 if(group->childCount() == 0) {
318 d->groups.remove(group);
319 delete group;
320 }
321}
322
323void ContactProfile::updateEntry(const UserListItem &u)
324{
325 if (u.isSelf()) {
326 // Update the self item
327 d->su = u;
328
329 // Show and/or update item if necessary
330 if (d->cv->isShowSelf() || d->su.userResourceList().count() > 1) {
331 if (d->self) {
332 updateSelf();
333 }
334 else {
335 addSelf();
336 }
337 }
338 else {
339 removeSelf();
340 }
341 }
342 else {
343 Entry *e = findEntry(u.jid());
344 if(!e) {
345 e = new Entry;
346 d->roster.append(e);
347 e->u = u;
348 }
349 else {
350 e->u = u;
351 removeUnneededContactItems(e);
352
353 // update remaining items
354 QPtrListIterator<ContactViewItem> it(e->cvi);
355 for(ContactViewItem *i; (i = it.current()); ++it) {
356 i->setContact(&e->u);
357 if(!u.isAvailable())
358 i->stopAnimateNick();
359 }
360 }
361
362 deferredUpdateGroups();
363 addNeededContactItems(e);
364 }
365}
366
367void ContactProfile::updateSelf()
368{
369 if (d->self) {
370 d->self->setContact(&d->su);
371 if(!d->su.isAvailable())
372 d->self->stopAnimateNick();
373 }
374}
375
376void ContactProfile::addSelf()
377{
378 if(!d->self) {
379 if(!d->cvi)
380 return;
381 d->self = new ContactViewItem(&d->su, this, d->cvi);
382 }
383}
384
385void ContactProfile::removeSelf()
386{
387 if (d->self) {
388 delete d->self;
389 d->self = 0;
390 }
391}
392
393ContactViewItem *ContactProfile::addContactItem(Entry *e, ContactViewItem *group_item)
394{
395 ContactViewItem *i = new ContactViewItem(&e->u, this, group_item);
396 e->cvi.append(i);
397 if(e->alerting)
398 i->setAlert(&e->anim);
399 deferredUpdateGroups();
400 //printf("ContactProfile: adding [%s] to group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1());
401 return i;
402}
403
404/*
405 * \brief Ensures that specified Entry is present in contactlist
406 *
407 * \param e - Entry with the necessary data about item
408 * \param group_item - ContactViewItem that will be the group for this item
409 */
410ContactViewItem *ContactProfile::ensureContactItem(Entry *e, ContactViewItem *group_item)
411{
412 d->cv->recalculateSize();
413
414 QPtrListIterator<ContactViewItem> it(e->cvi);
415 for(ContactViewItem *i; (i = it.current()); ++it) {
416 ContactViewItem *g = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
417 if(g == group_item)
418 return i;
419 }
420 return addContactItem(e, group_item);
421}
422
423/*
424 * \brief Removes specified item from ContactView
425 *
426 * \param e - Entry with item's data
427 * \param i - ContactViewItem corresponding to the e
428 */
429void ContactProfile::removeContactItem(Entry *e, ContactViewItem *i)
430{
431 d->cv->recalculateSize();
432
433 ContactViewItem *group_item = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
434 //printf("ContactProfile: removing [%s] from group [%s]\n", e->u.jid().full().latin1(), group_item->groupName().latin1());
435 e->cvi.removeRef(i);
436 deferredUpdateGroups();
437 checkDestroyGroup(group_item);
438}
439
440void ContactProfile::addNeededContactItems(Entry *e)
441{
442 if(!d->v_enabled)
443 return;
444
445 const UserListItem &u = e->u;
446
447 if(u.inList()) {
448 // don't add if we're not supposed to see it
449 if(u.isTransport()) {
450 if(!d->cv->isShowAgents())
451 return;
452 }
453 else {
454 if(!e->alerting) {
455 if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden()))
456 return;
457 }
458 }
459 }
460
461 if(u.isPrivate())
462 ensureContactItem(e, ensureGroup(ContactViewItem::gPrivate));
463 else if(!u.inList())
464 ensureContactItem(e, ensureGroup(ContactViewItem::gNotInList));
465 else if(u.isTransport())
466 ensureContactItem(e, ensureGroup(ContactViewItem::gAgents));
467 else if(u.groups().isEmpty())
468 ensureContactItem(e, ensureGroup(ContactViewItem::gGeneral));
469 else {
470 const QStringList &groups = u.groups();
471 for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git)
472 ensureContactItem(e, ensureGroup(*git));
473 }
474}
475
476void ContactProfile::removeUnneededContactItems(Entry *e)
477{
478 const UserListItem &u = e->u;
479
480 if(u.inList()) {
481 bool delAll = !d->v_enabled;
482 if(u.isTransport()) {
483 if(!d->cv->isShowAgents())
484 delAll = true;
485 }
486 else {
487 if(!e->alerting) {
488 if((!d->cv->isShowOffline() && !u.isAvailable()) || (!d->cv->isShowAway() && u.isAway()) || (!d->cv->isShowHidden() && u.isHidden()))
489 delAll = true;
490 }
491 }
492 if(delAll) {
493 clearContactItems(e);
494 return;
495 }
496 }
497
498 QPtrListIterator<ContactViewItem> it(e->cvi);
499 for(ContactViewItem *i; (i = it.current());) {
500 bool del = false;
501 ContactViewItem *g = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
502
503 if(g->groupType() == ContactViewItem::gNotInList && u.inList())
504 del = true;
505 else if(g->groupType() != ContactViewItem::gNotInList && g->groupType() != ContactViewItem::gPrivate && !u.inList())
506 del = true;
507 else if(g->groupType() == ContactViewItem::gAgents && !u.isTransport())
508 del = true;
509 else if(g->groupType() != ContactViewItem::gAgents && u.isTransport())
510 del = true;
511 else if(g->groupType() == ContactViewItem::gGeneral && !u.groups().isEmpty())
512 del = true;
513 else if(g->groupType() != ContactViewItem::gPrivate && g->groupType() != ContactViewItem::gGeneral && u.groups().isEmpty() && !u.isTransport() && u.inList())
514 del = true;
515 else if(g->groupType() == ContactViewItem::gUser) {
516 const QStringList &groups = u.groups();
517 if(!groups.isEmpty()) {
518 bool found = false;
519 for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
520 if(g->groupName() == *git) {
521 found = true;
522 break;
523 }
524 }
525 if(!found)
526 del = true;
527 }
528 }
529
530 if(del)
531 removeContactItem(e, i);
532 else
533 ++it;
534 }
535}
536
537void ContactProfile::clearContactItems(Entry *e)
538{
539 QPtrListIterator<ContactViewItem> it(e->cvi);
540 for(ContactViewItem *i; (i = it.current());)
541 removeContactItem(e, i);
542}
543
544void ContactProfile::addAllNeededContactItems()
545{
546 QPtrListIterator<Entry> it(d->roster);
547 for(Entry *e; (e = it.current()); ++it)
548 addNeededContactItems(e);
549}
550
551void ContactProfile::removeAllUnneededContactItems()
552{
553 QPtrListIterator<Entry> it(d->roster);
554 for(Entry *e; (e = it.current()); ++it)
555 removeUnneededContactItems(e);
556}
557
558void ContactProfile::removeEntry(const Jid &j)
559{
560 Entry *e = findEntry(j);
561 if(e)
562 removeEntry(e);
563}
564
565void ContactProfile::removeEntry(Entry *e)
566{
567 e->alerting = false;
568 clearContactItems(e);
569 d->roster.remove(e);
570}
571
572void ContactProfile::setAlert(const Jid &j, const Icon *anim)
573{
574 if(d->su.jid().compare(j)) {
575 if(d->self)
576 d->self->setAlert(anim);
577 }
578 else {
579 Entry *e = findEntry(j);
580 if(!e)
581 return;
582
583 if(!d->cv->isShowAgents() && e->u.isTransport() && e->u.inList())
584 d->cv->setShowAgents(true);
585
586 e->alerting = true;
587 e->anim = *anim;
588 addNeededContactItems(e);
589 QPtrListIterator<ContactViewItem> it(e->cvi);
590 for(ContactViewItem *i; (i = it.current()); ++it)
591 i->setAlert(anim);
592
593 if(option.scrollTo)
594 ensureVisible(e);
595 }
596}
597
598void ContactProfile::clearAlert(const Jid &j)
599{
600 if(d->su.jid().compare(j)) {
601 if(d->self)
602 d->self->clearAlert();
603 }
604 else {
605 Entry *e = findEntry(j);
606 if(!e)
607 return;
608
609 e->alerting = false;
610 QPtrListIterator<ContactViewItem> it(e->cvi);
611 for(ContactViewItem *i; (i = it.current()); ++it)
612 i->clearAlert();
613 if(e->u.inList())
614 removeUnneededContactItems(e);
615 }
616}
617
618void ContactProfile::clear()
619{
620 QPtrListIterator<Entry> it(d->roster);
621 for(Entry *e; (e = it.current());)
622 removeEntry(e);
623}
624
625ContactProfile::Entry *ContactProfile::findEntry(const Jid &jid) const
626{
627 QPtrListIterator<Entry> it(d->roster);
628 for(Entry *e; (e = it.current()); ++it) {
629 if(e->u.jid().compare(jid))
630 return e;
631 }
632 return 0;
633}
634
635ContactProfile::Entry *ContactProfile::findEntry(ContactViewItem *i) const
636{
637 QPtrListIterator<Entry> it(d->roster);
638 for(Entry *e; (e = it.current()); ++it) {
639 QPtrListIterator<ContactViewItem> ci(e->cvi);
640 for(ContactViewItem *cvi; (cvi = ci.current()); ++ci) {
641 if(cvi == i)
642 return e;
643 }
644 }
645 return 0;
646}
647
648// return a list of contacts from a CVI group
649JidList ContactProfile::contactListFromCVGroup(ContactViewItem *group) const
650{
651 JidList list;
652
653 for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
654 if(item->type() != ContactViewItem::Contact)
655 continue;
656
657 list.append(item->u()->jid());
658 }
659
660 return list;
661}
662
663// return the number of contacts from a CVI group
664int ContactProfile::contactSizeFromCVGroup(ContactViewItem *group) const
665{
666 int total = 0;
667
668 for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
669 if(item->type() != ContactViewItem::Contact)
670 continue;
671
672 ++total;
673 }
674
675 return total;
676}
677
678// return the number of contacts from a CVI group
679int ContactProfile::contactsOnlineFromCVGroup(ContactViewItem *group) const
680{
681 int total = 0;
682
683 for(ContactViewItem *item = (ContactViewItem *)group->firstChild(); item ; item = (ContactViewItem *)item->nextSibling()) {
684 if(item->type() == ContactViewItem::Contact && item->u()->isAvailable())
685 ++total;
686 }
687
688 return total;
689}
690
691// return a list of contacts associated with "groupName"
692JidList ContactProfile::contactListFromGroup(const QString &groupName) const
693{
694 JidList list;
695
696 QPtrListIterator<Entry> it(d->roster);
697 for(Entry *e; (e = it.current()); ++it) {
698 const UserListItem &u = e->u;
699 if(u.isTransport())
700 continue;
701 const QStringList &g = u.groups();
702 if(g.isEmpty()) {
703 if(groupName.isEmpty())
704 list.append(u.jid());
705 }
706 else {
707 for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) {
708 if(*git == groupName) {
709 list.append(u.jid());
710 break;
711 }
712 }
713 }
714 }
715
716 return list;
717}
718
719// return the number of contacts associated with "groupName"
720int ContactProfile::contactSizeFromGroup(const QString &groupName) const
721{
722 int total = 0;
723
724 QPtrListIterator<Entry> it(d->roster);
725 for(Entry *e; (e = it.current()); ++it) {
726 const UserListItem &u = e->u;
727 if(u.isTransport())
728 continue;
729 const QStringList &g = u.groups();
730 if(g.isEmpty()) {
731 if(groupName.isEmpty())
732 ++total;
733 }
734 else {
735 for(QStringList::ConstIterator git = g.begin(); git != g.end(); ++git) {
736 if(*git == groupName) {
737 ++total;
738 break;
739 }
740 }
741 }
742 }
743
744 return total;
745}
746
747void ContactProfile::updateGroupInfo(ContactViewItem *group)
748{
749 int type = group->groupType();
750 if(type == ContactViewItem::gGeneral || type == ContactViewItem::gAgents || type == ContactViewItem::gPrivate || type == ContactViewItem::gUser) {
751 int online = contactsOnlineFromCVGroup(group);
752 int total;
753 if(type == ContactViewItem::gGeneral || type == ContactViewItem::gUser) {
754 QString gname;
755 if(type == ContactViewItem::gUser)
756 gname = group->groupName();
757 else
758 gname = "";
759 total = contactSizeFromGroup(gname);
760 }
761 else {
762 total = group->childCount();
763 }
764 if (option.showGroupCounts)
765 group->setGroupInfo(QString("(%1/%2)").arg(online).arg(total));
766 }
767 else if (option.showGroupCounts) {
768 int inGroup = contactSizeFromCVGroup(group);
769 group->setGroupInfo(QString("(%1)").arg(inGroup));
770 }
771}
772
773QStringList ContactProfile::groupList() const
774{
775 QStringList groupList;
776
777 QPtrListIterator<Entry> it(d->roster);
778 for(Entry *e; (e = it.current()); ++it) {
779 const QStringList &groups = e->u.groups();
780 for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
781 if(qstringlistmatch(groupList, *git) == -1)
782 groupList.append(*git);
783 }
784 }
785
786 groupList.sort();
787 return groupList;
788}
789
790void ContactProfile::animateNick(const Jid &j)
791{
792 if(d->su.jid().compare(j)) {
793 if(d->self)
794 d->self->setAnimateNick();
795 }
796
797 Entry *e = findEntry(j);
798 if(!e)
799 return;
800 QPtrListIterator<ContactViewItem> it(e->cvi);
801 for(ContactViewItem *i; (i = it.current()); ++it)
802 i->setAnimateNick();
803}
804
805void ContactProfile::deferredUpdateGroups()
806{
807 d->t->start(250, true);
808}
809
810void ContactProfile::updateGroups()
811{
812 int totalOnline = 0;
813 {
814 QPtrListIterator<Entry> it(d->roster);
815 for(Entry *e; (e = it.current()); ++it) {
816 if(e->u.isAvailable())
817 ++totalOnline;
818 }
819 if(d->cvi && option.showGroupCounts)
820 d->cvi->setGroupInfo(QString("(%1/%2)").arg(totalOnline).arg(d->roster.count()));
821 }
822
823 {
824 QPtrListIterator<ContactViewItem> it(d->groups);
825 for(ContactViewItem *g; (g = it.current()); ++it)
826 updateGroupInfo(g);
827 }
828}
829
830void ContactProfile::ensureVisible(const Jid &j)
831{
832 Entry *e = findEntry(j);
833 if(!e)
834 return;
835 ensureVisible(e);
836}
837
838void ContactProfile::ensureVisible(Entry *e)
839{
840 if(!e->alerting) {
841 if(!d->cv->isShowAgents() && e->u.isTransport())
842 d->cv->setShowAgents(true);
843 if(!d->cv->isShowOffline() && !e->u.isAvailable())
844 d->cv->setShowOffline(true);
845 if(!d->cv->isShowAway() && e->u.isAway())
846 d->cv->setShowAway(true);
847 if(!d->cv->isShowHidden() && e->u.isHidden())
848 d->cv->setShowHidden(true);
849 }
850
851 ContactViewItem *i = e->cvi.first();
852 if(!i)
853 return;
854 d->cv->ensureItemVisible(i);
855}
856
857void ContactProfile::doContextMenu(ContactViewItem *i, const QPoint &pos)
858{
859 bool online = d->pa->loggedIn();
860
861 if(i->type() == ContactViewItem::Profile) {
862 QPopupMenu pm;
863
864 QPopupMenu *am = new QPopupMenu(&pm);
865 am->insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Online Users"), 5);
866 am->insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send server message"), 1);
867 am->insertSeparator();
868 am->insertItem(/*IconsetFactory::iconPixmap("psi/edit"),*/ tr("Set MOTD"), 2);
869 am->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("Update MOTD"), 3);
870 am->insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Delete MOTD"), 4);
871
872 const int status_start = 16;
873 QPopupMenu *sm = new QPopupMenu(&pm);
874 sm->insertItem(is->status(STATUS_ONLINE), status2txt(STATUS_ONLINE), STATUS_ONLINE + status_start);
875 sm->insertItem(is->status(STATUS_CHAT), status2txt(STATUS_CHAT), STATUS_CHAT + status_start);
876 sm->insertSeparator();
877 sm->insertItem(is->status(STATUS_AWAY), status2txt(STATUS_AWAY), STATUS_AWAY + status_start);
878 sm->insertItem(is->status(STATUS_XA), status2txt(STATUS_XA), STATUS_XA + status_start);
879 sm->insertItem(is->status(STATUS_DND), status2txt(STATUS_DND), STATUS_DND + status_start);
880 sm->insertSeparator();
881 sm->insertItem(is->status(STATUS_INVISIBLE), status2txt(STATUS_INVISIBLE), STATUS_INVISIBLE + status_start);
882 sm->insertSeparator();
883 sm->insertItem(is->status(STATUS_OFFLINE), status2txt(STATUS_OFFLINE), STATUS_OFFLINE + status_start);
884 pm.insertItem(tr("&Status"), sm);
885
886 pm.insertSeparator();
887 pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("&Add a contact"), 7);
888 pm.insertItem(IconsetFactory::iconPixmap("psi/disco"), tr("Service &Discovery"), 9);
889 pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("New &blank message"), 6);
890 pm.insertSeparator();
891 pm.insertItem(IconsetFactory::iconPixmap("psi/xml"), tr("&XML Console"), 10);
892 pm.insertSeparator();
893 pm.insertItem(IconsetFactory::iconPixmap("psi/account"), tr("&Modify Account..."), 0);
894 pm.insertSeparator();
895 pm.insertItem(tr("&Admin"), am);
896
897 int x = pm.exec(pos);
898
899 if(x == -1)
900 return;
901
902 if(x == 0)
903 d->pa->modify();
904 else if(x == 1) {
905 Jid j = d->pa->jid().host() + '/' + "announce/online";
906 actionSendMessage(j);
907 }
908 else if(x == 2) {
909 Jid j = d->pa->jid().host() + '/' + "announce/motd";
910 actionSendMessage(j);
911 }
912 else if(x == 3) {
913 Jid j = d->pa->jid().host() + '/' + "announce/motd/update";
914 actionSendMessage(j);
915 }
916 else if(x == 4) {
917 Jid j = d->pa->jid().host() + '/' + "announce/motd/delete";
918 Message m;
919 m.setTo(j);
920 d->pa->dj_sendMessage(m, false);
921 }
922 else if(x == 5) {
923 // FIXME: will it still work on XMPP servers?
924 Jid j = d->pa->jid().host() + '/' + "admin";
925 actionDisco(j, "");
926 }
927 else if(x == 6) {
928 actionSendMessage("");
929 }
930 else if(x == 7) {
931 d->pa->openAddUserDlg();
932 }
933 else if(x == 9) {
934 Jid j = d->pa->jid().host();
935 actionDisco(j, "");
936 }
937 else if(x == 10) {
938 d->pa->showXmlConsole();
939 }
940 else if(x >= status_start) {
941 int status = x - status_start;
942 d->pa->changeStatus(status);
943 }
944 }
945 else if(i->type() == ContactViewItem::Group) {
946 QString gname = i->groupName();
947 QPopupMenu pm;
948
949 pm.insertItem(IconsetFactory::iconPixmap("psi/sendMessage"), tr("Send message to group"), 0);
950 if(!option.lockdown.roster) {
951 // disable if it's not a user group
952 if(!online || i->groupType() != ContactViewItem::gUser || gname == ContactView::tr("Hidden")) {
953 d->cv->qa_ren->setEnabled(false);
954 pm.setItemEnabled(2, false);
955 pm.setItemEnabled(3, false);
956 }
957 else
958 d->cv->qa_ren->setEnabled(true);
959
960 d->cv->qa_ren->addTo(&pm);
961 pm.insertSeparator();
962 pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group"), 2);
963 pm.insertItem(IconsetFactory::iconPixmap("psi/remove"), tr("Remove group and contacts"), 3);
964 }
965
966 if(i->groupType() == ContactViewItem::gAgents) {
967 pm.insertSeparator();
968 pm.insertItem(tr("Hide"), 4);
969 }
970
971 int x = pm.exec(pos);
972
973 // restore actions
974 if(option.lockdown.roster)
975 d->cv->qa_ren->setEnabled(false);
976 else
977 d->cv->qa_ren->setEnabled(true);
978
979 if(x == -1)
980 return;
981
982 if(x == 0) {
983 JidList list = contactListFromCVGroup(i);
984
985 // send multi
986 actionSendMessage(list);
987 }
988 else if(x == 2 && online) {
989 int n = QMessageBox::information(d->cv, tr("Remove Group"),tr(
990 "This will cause all contacts in this group to be disassociated with it.\n"
991 "\n"
992 "Proceed?"), tr("&Yes"), tr("&No"));
993
994 if(n == 0) {
995 JidList list = contactListFromGroup(i->groupName());
996 for(QValueList<Jid>::Iterator it = list.begin(); it != list.end(); ++it)
997 actionGroupRemove(*it, gname);
998 }
999 }
1000 else if(x == 3 && online) {
1001 int n = QMessageBox::information(d->cv, tr("Remove Group and Contacts"),tr(
1002 "WARNING! This will remove all contacts associated with this group!\n"
1003 "\n"
1004 "Proceed?"), tr("&Yes"), tr("&No"));
1005
1006 if(n == 0) {
1007 JidList list = contactListFromGroup(i->groupName());
1008 for(QValueList<Jid>::Iterator it = list.begin(); it != list.end(); ++it) {
1009 removeEntry(*it);
1010 actionRemove(*it);
1011 }
1012 }
1013 }
1014 else if(x == 4) {
1015 if(i->groupType() == ContactViewItem::gAgents)
1016 d->cv->setShowAgents(false);
1017 }
1018 }
1019 else if(i->type() == ContactViewItem::Contact) {
1020 bool self = false;
1021 UserListItem *u;
1022 Entry *e = 0;
1023 if(i == d->self) {
1024 self = true;
1025 u = &d->su;
1026 }
1027 else {
1028 e = findEntry(i);
1029 if(!e)
1030 return;
1031 u = &e->u;
1032 }
1033
1034 QStringList gl = groupList();
1035 qstringlistisort(gl); // caseless sort
1036
1037 bool inList = e ? e->u.inList() : false;
1038 bool isPrivate = e ? e->u.isPrivate(): false;
1039 bool isAgent = e ? e->u.isTransport() : false;
1040 bool avail = e ? e->u.isAvailable() : false;
1041 QString groupNameCache = ((ContactViewItem *)static_cast<QListViewItem *>(i)->parent())->groupName();
1042
1043 QPopupMenu pm;
1044
1045 if(!self && !inList && !isPrivate && !option.lockdown.roster) {
1046 pm.insertItem(IconsetFactory::iconPixmap("psi/addContact"), tr("Add/Authorize to contact list"), 10);
1047 if(!online)
1048 pm.setItemEnabled(10, false);
1049 pm.insertSeparator();
1050 }
1051
1052 if ( (self && i->isAlerting()) ||
1053 (!self && e->alerting) ) {
1054 d->cv->qa_recv->addTo(&pm);
1055 pm.insertSeparator();
1056 }
1057
1058 d->cv->qa_send->addTo(&pm);
1059
1060 //pm.insertItem(QIconSet(is->url), tr("Send &URL"), 2);
1061
1062 const UserResourceList &rl = u->userResourceList();
1063
1064 int base_sendto = 32;
1065 int at_sendto = 0;
1066 QPopupMenu *s2m = new QPopupMenu(&pm);
1067 QPopupMenu *c2m = new QPopupMenu(&pm);
1068
1069 /*if ( rl.isEmpty() ) {
1070 d->cv->qa_send->addTo(&pm);
1071 d->cv->qa_chat->addTo(&pm);
1072 }
1073 else {
1074 d->cv->qa_send->addTo(s2m);
1075 s2m->insertSeparator();
1076
1077 d->cv->qa_chat->addTo(c2m);
1078 c2m->insertSeparator();
1079
1080 pm.insertItem(is->send.pixmap(), tr("Send &message"), s2m, 17);
1081 pm.insertItem(is->chat.base().pixmap(), tr("Open &chat window"), c2m, 18);
1082 }*/
1083
1084 //s2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++);
1085 //c2m->insertItem(tr("Recipient Default"), base_sendto+at_sendto++);
1086 if(!rl.isEmpty()) {
1087 //s2m->insertSeparator();
1088 //c2m->insertSeparator();
1089 for(UserResourceList::ConstIterator it = rl.begin(); it != rl.end(); ++it) {
1090 const UserResource &r = *it;
1091 QString rname;
1092 if(r.name().isEmpty())
1093 rname = tr("[blank]");
1094 else
1095 rname = r.name();
1096 s2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++);
1097 c2m->insertItem(is->status(r.status()), rname, base_sendto+at_sendto++);
1098 }
1099 }
1100
1101 if(!isPrivate)
1102 pm.insertItem(tr("Send message to"), s2m, 17);
1103
1104 d->cv->qa_chat->setIconSet(IconsetFactory::iconPixmap("psi/start-chat"));
1105 d->cv->qa_chat->addTo(&pm);
1106
1107 if(!isPrivate)
1108 pm.insertItem(tr("Open chat to"), c2m, 18);
1109
1110 if(!isPrivate) {
1111 if(rl.isEmpty()) {
1112 pm.setItemEnabled(17, false);
1113 pm.setItemEnabled(18, false);
1114 }
1115 }
1116
1117 int base_hidden = base_sendto + at_sendto;
1118 int at_hidden = 0;
1119 QStringList hc;
1120 if(!isPrivate) {
1121 hc = d->pa->hiddenChats(u->jid());
1122 QPopupMenu *cm = new QPopupMenu(&pm);
1123 for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) {
1124 QString rname;
1125 if((*it).isEmpty())
1126 rname = tr("[blank]");
1127 else
1128 rname = *it;
1129
1130 // determine status
1131 int status;
1132 const UserResourceList &rl = u->userResourceList();
1133 UserResourceList::ConstIterator uit = rl.find(*it);
1134 if(uit != rl.end() || (uit = rl.priority()) != rl.end())
1135 status = makeSTATUS((*uit).status());
1136 else
1137 status = STATUS_OFFLINE;
1138 cm->insertItem(is->status(status), rname, base_hidden+at_hidden++);
1139 }
1140 pm.insertItem(tr("Active chats"), cm, 7);
1141 if(hc.isEmpty())
1142 pm.setItemEnabled(7, false);
1143 }
1144
1145 if(!isAgent) {
1146 pm.insertSeparator();
1147 pm.insertItem(IconsetFactory::iconPixmap("psi/upload"), tr("Send &file"), 23);
1148 if(!online)
1149 pm.setItemEnabled(23, false);
1150 }
1151
1152 // invites
1153 int base_gc = base_hidden + at_hidden;
1154 int at_gc = 0;
1155 QStringList groupchats;
1156 if(!isPrivate && !isAgent) {
1157 QPopupMenu *gm = new QPopupMenu(&pm);
1158 groupchats = d->pa->groupchats();
1159 for(QStringList::ConstIterator it = groupchats.begin(); it != groupchats.end(); ++it) {
1160 int id = gm->insertItem(*it, base_gc+at_gc++);
1161 if(!online)
1162 gm->setItemEnabled(id, false);
1163 }
1164 pm.insertItem(IconsetFactory::iconPixmap("psi/groupChat"), tr("Invite to"), gm, 14);
1165 if(groupchats.isEmpty())
1166 pm.setItemEnabled(14, false);
1167 }
1168
1169 // weird?
1170 if(inList || !isAgent)
1171 pm.insertSeparator();
1172
1173 int base_group = base_gc + at_gc;
1174
1175 if(!self) {
1176 if(inList) {
1177 if(!option.lockdown.roster) {
1178 d->cv->qa_ren->setEnabled(online);
1179 d->cv->qa_ren->addTo(&pm);
1180 }
1181 }
1182
1183 if(!isAgent) {
1184 if(inList && !option.lockdown.roster) {
1185 QPopupMenu *gm = new QPopupMenu(&pm);
1186
1187 gm->setCheckable(true);
1188 gm->insertItem(tr("&None"), 8);
1189 gm->insertSeparator();
1190
1191 QString g;
1192 if(e->u.groups().isEmpty())
1193 gm->setItemChecked(8, true);
1194 else
1195 g = groupNameCache;
1196
1197 int n = 0;
1198 gl.remove(ContactView::tr("Hidden"));
1199 for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) {
1200 QString str;
1201 if(n < 9)
1202 str = "&";
1203 str += QString("%1. %2").arg(n+1).arg(*it);
1204 gm->insertItem(str, n+base_group);
1205
1206 if(*it == g)
1207 gm->setItemChecked(n+base_group, true);
1208 ++n;
1209 }
1210 if(n > 0)
1211 gm->insertSeparator();
1212
1213 gm->insertItem(ContactView::tr("Hidden"),n+base_group);
1214 if(g == ContactView::tr("Hidden"))
1215 gm->setItemChecked(n+base_group, true);
1216 gm->insertSeparator();
1217 gm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/clear"),*/ tr("&Create new..."), 9);
1218 pm.insertItem(tr("&Group"), gm, 5);
1219
1220 if(!online)
1221 pm.setItemEnabled(5, false);
1222 }
1223 }
1224 else {
1225 pm.insertSeparator();
1226
1227 d->cv->qa_logon->setEnabled( !avail && online );
1228
1229 d->cv->qa_logon->setIconSet(is->status(e->u.jid(), STATUS_ONLINE));
1230 d->cv->qa_logon->addTo(&pm);
1231
1232 pm.insertItem(is->status(e->u.jid(), STATUS_OFFLINE), tr("Log off"), 16);
1233 if(!avail || !online)
1234 pm.setItemEnabled(16, false);
1235 pm.insertSeparator();
1236 }
1237 }
1238
1239 if(inList && !option.lockdown.roster) {
1240 QPopupMenu *authm = new QPopupMenu (&pm);
1241
1242 authm->insertItem(tr("Resend authorization to"), 6);
1243 authm->insertItem(tr("Rerequest authorization from"), 11);
1244 authm->insertItem(/*IconsetFactory::iconPixmap("psi/edit/delete"),*/ tr("Remove authorization from"), 15);
1245
1246 pm.insertItem (IconsetFactory::iconPixmap("psi/register"), tr("Authorization"), authm, 20);
1247 if(!online)
1248 pm.setItemEnabled(20, false);
1249 }
1250
1251 if(!self) {
1252 if(!option.lockdown.roster) {
1253 if(online || !inList)
1254 d->cv->qa_rem->setEnabled(true);
1255 else
1256 d->cv->qa_rem->setEnabled(false);
1257
1258 d->cv->qa_rem->addTo(&pm);
1259 }
1260 pm.insertSeparator();
1261 }
1262
1263 // Avatars
1264#ifdef AVATARS
1265 QPopupMenu *avpm = new QPopupMenu(&pm);
1266 d->cv->qa_assignAvatar->addTo(avpm);
1267 d->cv->qa_clearAvatar->setEnabled(d->pa->avatarFactory()->hasManualAvatar(u->jid()));
1268 d->cv->qa_clearAvatar->addTo(avpm);
1269 pm.insertItem(tr("&Avatar"), avpm);
1270 avpm->setEnabled(option.avatarsEnabled);
1271#endif
1272
1273 if(d->pa->psi()->pgp()) {
1274 if(u->publicKeyID().isEmpty())
1275 pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-yes"), tr("Assign Open&PGP key"), 21);
1276 else
1277 pm.insertItem(IconsetFactory::iconPixmap("psi/gpg-no"), tr("Unassign Open&PGP key"), 22);
1278 }
1279
1280 d->cv->qa_vcard->addTo( &pm );
1281
1282 if(!isPrivate) {
1283 d->cv->qa_hist->addTo(&pm);
1284 }
1285
1286 //if(link_test) {
1287 // pm.insertSeparator();
1288 // pm.insertItem(tr("Link Test"), 13);
1289 //}
1290
1291 QString name = u->jid().full();
1292 QString show = jidnick(u->jid().full(), u->name());
1293 if(name != show)
1294 name += QString(" (%1)").arg(u->name());
1295
1296 int x = pm.exec(pos);
1297
1298 // restore actions
1299 if(option.lockdown.roster) {
1300 d->cv->qa_ren->setEnabled(false);
1301 d->cv->qa_rem->setEnabled(false);
1302 }
1303 else {
1304 d->cv->qa_ren->setEnabled(true);
1305 d->cv->qa_rem->setEnabled(true);
1306 }
1307
1308 if(x == -1)
1309 return;
1310
1311 if(x == 0) {
1312 actionRecvEvent(u->jid());
1313 }
1314 else if(x == 1) {
1315 actionSendMessage(u->jid());
1316 }
1317 else if(x == 2) {
1318 actionSendUrl(u->jid());
1319 }
1320 else if(x == 6) {
1321 if(online) {
1322 actionAuth(u->jid());
1323 QMessageBox::information(d->cv, tr("Authorize"),
1324 tr("Sent authorization to <b>%1</b>.").arg(name));
1325 }
1326 }
1327 else if(x == 8) {
1328 if(online) {
1329 // if we have groups, but we are setting to 'none', then remove that particular group
1330 if(!u->groups().isEmpty()) {
1331 QString gname = groupNameCache;
1332 actionGroupRemove(u->jid(), gname);
1333 }
1334 }
1335 }
1336 else if(x == 9) {
1337 if(online) {
1338 while(1) {
1339 bool ok = false;
1340 QString newgroup = QInputDialog::getText(tr("Create New Group"), tr("Enter the new Group name:"), QLineEdit::Normal, QString::null, &ok, d->cv);
1341 if(!ok)
1342 break;
1343 if(newgroup.isEmpty())
1344 continue;
1345
1346 // make sure we don't have it already
1347 bool found = false;
1348 const QStringList &groups = u->groups();
1349 for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it) {
1350 if(*it == newgroup) {
1351 found = true;
1352 break;
1353 }
1354 }
1355 if(!found) {
1356 QString gname = groupNameCache;
1357 actionGroupRemove(u->jid(), gname);
1358 actionGroupAdd(u->jid(), newgroup);
1359 break;
1360 }
1361 }
1362 }
1363 }
1364 else if(x == 10) {
1365 if(online) {
1366 actionAdd(u->jid());
1367 actionAuth(u->jid());
1368
1369 QMessageBox::information(d->cv, tr("Add"),
1370 tr("Added/Authorized <b>%1</b> to the contact list.").arg(name));
1371 }
1372 }
1373 else if(x == 11) {
1374 if(online) {
1375 actionAuthRequest(u->jid());
1376 QMessageBox::information(d->cv, tr("Authorize"),
1377 tr("Rerequested authorization from <b>%1</b>.").arg(name));
1378 }
1379 }
1380 else if(x == 13) {
1381 actionTest(u->jid());
1382 }
1383 else if(x == 15) {
1384 if(online) {
1385 int n = QMessageBox::information(d->cv, tr("Remove"),
1386 tr("Are you sure you want to remove authorization from <b>%1</b>?").arg(name),
1387 tr("&Yes"), tr("&No"));
1388
1389 if(n == 0)
1390 actionAuthRemove(u->jid());
1391 }
1392 }
1393 else if(x == 16) {
1394 if(online) {
1395 Status s=makeStatus(STATUS_OFFLINE,"");
1396 actionAgentSetStatus(u->jid(), s);
1397 }
1398 }
1399 else if(x == 21) {
1400 actionAssignKey(u->jid());
1401 }
1402 else if(x == 22) {
1403 actionUnassignKey(u->jid());
1404 }
1405 else if(x == 23) {
1406 if(online)
1407 actionSendFile(u->jid());
1408 }
1409 else if(x >= base_sendto && x < base_hidden) {
1410 int n = x - base_sendto;
1411 int res = n / 2;
1412 int type = n % 2;
1413 QString rname = "";
1414 //if(res > 0) {
1415 const UserResource &r = rl[res];
1416 rname = r.name();
1417 //}
1418 QString s = u->jid().userHost();
1419 if(!rname.isEmpty()) {
1420 s += '/';
1421 s += rname;
1422 }
1423 Jid j(s);
1424
1425 if(type == 0)
1426 actionSendMessage(j);
1427 else if(type == 1)
1428 actionOpenChatSpecific(j);
1429 }
1430 else if(x >= base_hidden && x < base_gc) {
1431 int n = 0;
1432 int n2 = x - base_hidden;
1433
1434 QString rname;
1435 for(QStringList::ConstIterator it = hc.begin(); it != hc.end(); ++it) {
1436 if(n == n2) {
1437 rname = *it;
1438 break;
1439 }
1440 ++n;
1441 }
1442
1443 QString s = u->jid().userHost();
1444 if(!rname.isEmpty()) {
1445 s += '/';
1446 s += rname;
1447 }
1448 Jid j(s);
1449
1450 actionOpenChatSpecific(j);
1451 }
1452 else if(x >= base_gc && x < base_group) {
1453 if(online) {
1454 QString gc = groupchats[x - base_gc];
1455 actionInvite(u->jid(), gc);
1456
1457 QMessageBox::information(d->cv, tr("Invitation"),
1458 tr("Sent groupchat invitation to <b>%1</b>.").arg(name));
1459 }
1460 }
1461 else if(x >= base_group) {
1462 if(online) {
1463 int n = 0;
1464 int n2 = x - base_group;
1465
1466 QString newgroup;
1467 for(QStringList::Iterator it = gl.begin(); it != gl.end(); ++it) {
1468 if(n == n2) {
1469 newgroup = *it;
1470 break;
1471 }
1472 ++n;
1473 }
1474
1475 if(newgroup.isEmpty())
1476 newgroup = ContactView::tr("Hidden");
1477
1478 if(n == n2) {
1479 // remove the group of this cvi if there is one
1480 if(!u->groups().isEmpty()) {
1481 //QString gname = ((ContactViewItem *)static_cast<QListViewItem *>(i)->parent())->groupName();
1482 QString gname = groupNameCache;
1483 actionGroupRemove(u->jid(), gname);
1484 }
1485 // add the group
1486 actionGroupAdd(u->jid(), newgroup);
1487 }
1488 }
1489 }
1490 }
1491}
1492
1493void ContactProfile::scActionDefault(ContactViewItem *i)
1494{
1495 if(i->type() == ContactViewItem::Contact)
1496 actionDefault(i->u()->jid());
1497}
1498
1499void ContactProfile::scRecvEvent(ContactViewItem *i)
1500{
1501 if(i->type() == ContactViewItem::Contact)
1502 actionRecvEvent(i->u()->jid());
1503}
1504
1505void ContactProfile::scSendMessage(ContactViewItem *i)
1506{
1507 if(i->type() == ContactViewItem::Contact)
1508 actionSendMessage(i->u()->jid());
1509}
1510
1511void ContactProfile::scRename(ContactViewItem *i)
1512{
1513 if(!d->pa->loggedIn())
1514 return;
1515
1516 if((i->type() == ContactViewItem::Contact && i->u()->inList()) ||
1517 (i->type() == ContactViewItem::Group && i->groupType() == ContactViewItem::gUser && i->groupName() != ContactView::tr("Hidden"))) {
1518 i->setRenameEnabled(0, true);
1519 i->startRename(0);
1520 i->setRenameEnabled(0, false);
1521 }
1522}
1523
1524void ContactProfile::scVCard(ContactViewItem *i)
1525{
1526 if(i->type() == ContactViewItem::Contact)
1527 actionInfo(i->u()->jid());
1528}
1529
1530void ContactProfile::scHistory(ContactViewItem *i)
1531{
1532 if(i->type() == ContactViewItem::Contact)
1533 actionHistory(i->u()->jid());
1534}
1535
1536void ContactProfile::scOpenChat(ContactViewItem *i)
1537{
1538 if(i->type() == ContactViewItem::Contact)
1539 actionOpenChat(i->u()->jid());
1540}
1541
1542void ContactProfile::scAgentSetStatus(ContactViewItem *i, Status &s)
1543{
1544 if(i->type() != ContactViewItem::Contact)
1545 return;
1546 if(!i->isAgent())
1547 return;
1548
1549 if(i->u()->isAvailable() || !d->pa->loggedIn())
1550 return;
1551
1552 actionAgentSetStatus(i->u()->jid(), s);
1553}
1554
1555void ContactProfile::scRemove(ContactViewItem *i)
1556{
1557 if(i->type() != ContactViewItem::Contact)
1558 return;
1559
1560 Entry *e = findEntry(i);
1561 if(!e)
1562 return;
1563
1564 bool ok = true;
1565 if(!d->pa->loggedIn())
1566 ok = false;
1567 if(!i->u()->inList())
1568 ok = true;
1569
1570 if(ok) {
1571 QString name = e->u.jid().full();
1572 QString show = jidnick(e->u.jid().full(), e->u.name());
1573 if(name != show)
1574 name += QString(" (%1)").arg(e->u.name());
1575
1576 int n = 0;
1577 int gt = i->parentGroupType();
1578 if(gt != ContactViewItem::gNotInList && gt != ContactViewItem::gPrivate) {
1579 n = QMessageBox::information(d->cv, tr("Remove"),
1580 tr("Are you sure you want to remove <b>%1</b> from your contact list?").arg(name),
1581 tr("&Yes"), tr("&No"));
1582 }
1583 else
1584 n = 0;
1585
1586 if(n == 0) {
1587 Jid j = e->u.jid();
1588 removeEntry(e);
1589 actionRemove(j);
1590 }
1591 }
1592}
1593
1594void ContactProfile::doItemRenamed(ContactViewItem *i, const QString &text)
1595{
1596 if(i->type() == ContactViewItem::Contact) {
1597 Entry *e = findEntry(i);
1598 if(!e)
1599 return;
1600
1601 // no change?
1602 //if(text == i->text(0))
1603 // return;
1604 if(text.isEmpty()) {
1605 i->resetName();
1606 QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank name."));
1607 return;
1608 }
1609
1610 //e->u.setName(text);
1611 //i->setContact(&e->u);
1612 actionRename(e->u.jid(), text);
1613 }
1614 else {
1615 // no change?
1616 if(text == i->groupName()) {
1617 i->resetGroupName();
1618 return;
1619 }
1620 if(text.isEmpty()) {
1621 i->resetGroupName();
1622 QMessageBox::information(d->cv, tr("Error"), tr("You can't set a blank group name."));
1623 return;
1624 }
1625
1626 // make sure we don't have it already
1627 QStringList g = groupList();
1628 bool found = false;
1629 for(QStringList::ConstIterator it = g.begin(); it != g.end(); ++it) {
1630 if(*it == text) {
1631 found = true;
1632 break;
1633 }
1634 }
1635 if(found) {
1636 i->resetGroupName();
1637 QMessageBox::information(d->cv, tr("Error"), tr("You already have a group with that name."));
1638 return;
1639 }
1640
1641 QString oldName = i->groupName();
1642
1643 // set group name
1644 i->setGroupName(text);
1645
1646 // send signal
1647 actionGroupRename(oldName, text);
1648 }
1649}
1650
1651void ContactProfile::dragDrop(const QString &text, ContactViewItem *i)
1652{
1653 if(!d->pa->loggedIn())
1654 return;
1655
1656 // get group
1657 ContactViewItem *gr;
1658 if(i->type() == ContactViewItem::Group)
1659 gr = i;
1660 else
1661 gr = (ContactViewItem *)static_cast<QListViewItem *>(i)->parent();
1662
1663 Jid j(text);
1664 if(!j.isValid())
1665 return;
1666 Entry *e = findEntry(j);
1667 if(!e)
1668 return;
1669 const UserListItem &u = e->u;
1670 QStringList gl = u.groups();
1671
1672 // already in the general group
1673 if(gr->groupType() == ContactViewItem::gGeneral && gl.isEmpty())
1674 return;
1675 // already in this user group
1676 if(gr->groupType() == ContactViewItem::gUser && u.inGroup(gr->groupName()))
1677 return;
1678
1679 //printf("putting [%s] into group [%s]\n", u.jid().full().latin1(), gr->groupName().latin1());
1680
1681 // remove all other groups from this contact
1682 for(QStringList::ConstIterator it = gl.begin(); it != gl.end(); ++it) {
1683 actionGroupRemove(u.jid(), *it);
1684 }
1685 if(gr->groupType() == ContactViewItem::gUser) {
1686 // add the new group
1687 actionGroupAdd(u.jid(), gr->groupName());
1688 }
1689}
1690
1691void ContactProfile::dragDropFiles(const QStringList &files, ContactViewItem *i)
1692{
1693 if(files.isEmpty() || !d->pa->loggedIn() || i->type() != ContactViewItem::Contact)
1694 return;
1695
1696 Entry *e = findEntry(i);
1697 if(!e)
1698 return;
1699
1700 actionSendFiles(e->u.jid(),files);
1701}
1702
1703//----------------------------------------------------------------------------
1704// ContactView
1705//----------------------------------------------------------------------------
1706class ContactView::Private : public QObject
1707{
1708 Q_OBJECT
1709public:
1710 Private(ContactView *_cv)
1711 : QObject(_cv)
1712 {
1713 cv = _cv;
1714 autoRosterResizeInProgress = false;
1715
1716 // type ahead
1717 typeAheadTimer = new QTimer(cv);
1718 connect(typeAheadTimer, SIGNAL(timeout()), SLOT(resetTypeAhead()));
1719 }
1720
1721 ContactView *cv;
1722 QTimer *animTimer, *recalculateSizeTimer;
1723 QPtrList<ContactProfile> profiles;
1724 QString typeAhead;
1725 QTimer *typeAheadTimer;
1726 QSize lastSize;
1727 bool autoRosterResizeInProgress;
1728
1729 bool doTypeAhead(QKeyEvent *e)
1730 {
1731 typeAheadTimer->start(3000, TRUE); // reset type ahead in 3 seconds of inactivity
1732
1733 QString text = e->text().lower();
1734 if ( text.isEmpty() ) {
1735 // Shift key could be used for entering '@', '#' and other extended chars
1736 // and other modifier keys could be used for layout changing
1737
1738 //if ( e->key() != Qt::Key_Shift )
1739 // typeAhead = QString::null;
1740 return false;
1741 }
1742
1743 bool searchFromStart = true;
1744 if ( typeAhead.isEmpty() )
1745 searchFromStart = false;
1746
1747 bool found = false;
1748 typeAhead += text;
1749
1750 QListViewItemIterator it(cv);
1751 ContactViewItem *item;
1752
1753 if ( !searchFromStart && cv->currentItem() ) {
1754 for ( ; (item = (ContactViewItem *)it.current()); ++it) {
1755 if ( item == cv->currentItem() ) {
1756 ++it; // we want the search to start from next contact
1757 break;
1758 }
1759 }
1760 }
1761
1762 for ( ; (item = (ContactViewItem *)it.current()); ++it) {
1763 //qWarning("->%s | %s | %s", text.latin1(), item->text(0).lower().latin1(), typeAhead.latin1());
1764 if ( item->text(0).lower().startsWith(typeAhead) ) {
1765 found = true;
1766 cv->setSelected(item, true);
1767 cv->ensureItemVisible(item);
1768 item->optionsUpdate();
1769 item->repaint();
1770 break;
1771 }
1772 }
1773
1774 //qWarning("---------------\n");
1775 if ( !found ) {
1776 typeAhead = text;
1777 return false;
1778 }
1779
1780 return true;
1781 }
1782
1783public slots:
1784 /*
1785 * \brief Recalculates the size of ContactView and resizes it accordingly
1786 */
1787 void recalculateSize()
1788 {
1789 // save some CPU
1790 if ( !cv->allowResize() )
1791 return;
1792
1793 if ( !cv->isUpdatesEnabled() )
1794 return;
1795
1796 QSize oldSize = cv->size();
1797 QSize newSize = cv->sizeHint();
1798
1799 if ( newSize.height() != oldSize.height() ) {
1800 lastSize = newSize;
1801 QWidget *topParent = cv->topLevelWidget();
1802
1803 if ( cv->allowResize() ) {
1804 topParent->layout()->setEnabled( false ); // try to reduce some flicker
1805
1806 int dh = newSize.height() - oldSize.height();
1807 topParent->resize( topParent->width(),
1808 topParent->height() + dh );
1809
1810 autoRosterResizeInProgress = true;
1811
1812 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
1813 if ( option.autoRosterSizeGrowTop ) {
1814 int newy = topParent->y() - dh;
1815
1816 // check, if we need to move roster lower
1817 if ( dh > 0 && ( topParent->frameGeometry().top() <= desktop.top() ) ) {
1818 newy = desktop.top();
1819 }
1820
1821 topParent->move( topParent->x(), newy );
1822 }
1823
1824 // check, if we need to move roster upper
1825 if ( dh > 0 && ( topParent->frameGeometry().bottom() >= desktop.bottom() ) ) {
1826 int newy = desktop.bottom() - topParent->frameGeometry().height();
1827 topParent->move( topParent->x(), newy );
1828 }
1829
1830 QTimer::singleShot( 0, this, SLOT( resetAutoRosterResize() ) );
1831
1832 topParent->layout()->setEnabled( true );
1833 }
1834
1835 // issue a layout update
1836 topParent->layout()->activate();
1837 }
1838 }
1839
1840 /*
1841 * \brief Determine in which direction to grow Psi roster window when autoRosterSize is enabled
1842 */
1843 void determineAutoRosterSizeGrowSide()
1844 {
1845 if ( autoRosterResizeInProgress )
1846 return;
1847
1848 QWidget *topParent = cv->topLevelWidget();
1849 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
1850
1851 int top_offs = abs( desktop.top() - topParent->frameGeometry().top() );
1852 int bottom_offs = abs( desktop.bottom() - topParent->frameGeometry().bottom() );
1853
1854 option.autoRosterSizeGrowTop = bottom_offs < top_offs;
1855 //qWarning("growTop = %d", option.autoRosterSizeGrowTop);
1856 }
1857
1858private slots:
1859 void resetAutoRosterResize()
1860 {
1861 //qWarning("resetAutoRosterResize");
1862 autoRosterResizeInProgress = false;
1863 }
1864
1865 void resetTypeAhead()
1866 {
1867 typeAhead = QString::null;
1868 }
1869};
1870
1871ContactView::ContactView(QWidget *parent, const char *name)
1872:QListView(parent, name, QListView::WRepaintNoErase | QListView::WResizeNoErase), QToolTip(viewport())
1873{
1874 d = new Private(this);
1875
1876 // setup the QListView
1877 setAllColumnsShowFocus(true);
1878 setShowToolTips(false);
1879 setHScrollBarMode(AlwaysOff);
1880 setMinimumSize(96,32);
1881 setTreeStepSize(4);
1882 setSorting(0,true);
1883
1884 topLevelWidget()->installEventFilter( this );
1885
1886 // create the column and hide the header
1887 addColumn("");
1888 header()->hide();
1889
1890 setResizeMode(QListView::LastColumn);
1891 setDefaultRenameAction(QListView::Accept);
1892
1893 // catch signals
1894 lcto_active = false;
1895 connect(this, SIGNAL(itemRenamed(QListViewItem *, int, const QString &)), SLOT(qlv_itemRenamed(QListViewItem *, int, const QString &)));
1896 connect(this, SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)),SLOT(qlv_singleclick(int, QListViewItem *, const QPoint &, int)) );
1897 connect(this, SIGNAL(doubleClicked(QListViewItem *)),SLOT(qlv_doubleclick(QListViewItem *)) );
1898 connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(qlv_contextMenuRequested(QListViewItem *, const QPoint &, int)));
1899
1900 v_showOffline = true;
1901 v_showAway = true;
1902 v_showHidden = true;
1903 v_showAgents = true;
1904 v_showSelf = true;
1905
1906 d->lastSize = QSize( 0, 0 );
1907
1908 // animation timer
1909 d->animTimer = new QTimer(this);
1910 d->animTimer->start(120 * 5);
1911
1912 d->recalculateSizeTimer = new QTimer;
1913 connect(d->recalculateSizeTimer, SIGNAL(timeout()), d, SLOT(recalculateSize()));
1914
1915 // actions
1916 qa_send = new IconAction("", "psi/sendMessage", tr("Send &message"), QListView::CTRL+QListView::Key_M, this);
1917 connect(qa_send, SIGNAL(activated()), SLOT(doSendMessage()));
1918 qa_ren = new IconAction("", /*"psi/edit/clear",*/ tr("Re&name"), QListView::Key_F2, this);
1919 connect(qa_ren, SIGNAL(activated()), SLOT(doRename()));
1920#ifdef AVATARS
1921 qa_assignAvatar = new IconAction("", tr("&Assign Custom Avatar"),QKeySequence(), this);
1922 connect(qa_assignAvatar, SIGNAL(activated()), SLOT(doAssignAvatar()));
1923 qa_clearAvatar = new IconAction("", tr("&Clear Custom Avatar"), QKeySequence(), this);
1924 connect(qa_clearAvatar, SIGNAL(activated()), SLOT(doClearAvatar()));
1925#endif
1926 qa_chat = new IconAction("", "psi/start-chat", tr("Open &chat window"), QListView::CTRL+QListView::Key_C, this);
1927 connect(qa_chat, SIGNAL(activated()), SLOT(doOpenChat()));
1928 qa_hist = new IconAction("", "psi/history", tr("&History"), QListView::CTRL+QListView::Key_H, this);
1929 connect(qa_hist, SIGNAL(activated()), SLOT(doHistory()));
1930 qa_logon = new IconAction("", tr("&Log on"), QListView::CTRL+QListView::Key_L, this);
1931 connect(qa_logon, SIGNAL(activated()), SLOT(doLogon()));
1932 qa_recv = new IconAction("", tr("&Receive incoming event"), QListView::CTRL+QListView::Key_R, this);
1933 connect(qa_recv, SIGNAL(activated()), SLOT(doRecvEvent()));
1934 qa_rem = new IconAction("", "psi/remove", tr("Rem&ove"), QListView::Key_Delete, this);
1935 connect(qa_rem, SIGNAL(activated()), SLOT(doRemove()));
1936 qa_vcard = new IconAction("", "psi/vCard", tr("User &Info"), QListView::CTRL+QListView::Key_I, this);
1937 connect(qa_vcard, SIGNAL(activated()), SLOT(doVCard()));
1938
1939 if(option.lockdown.roster) {
1940 qa_ren->setEnabled(false);
1941 qa_rem->setEnabled(false);
1942 }
1943
1944 optionsUpdate();
1945 setAcceptDrops(true);
1946 viewport()->setAcceptDrops(true);
1947}
1948
1949ContactView::~ContactView()
1950{
1951 clear();
1952 delete d;
1953}
1954
1955QTimer *ContactView::animTimer() const
1956{
1957 return d->animTimer;
1958}
1959
1960void ContactView::clear()
1961{
1962 d->profiles.setAutoDelete(true);
1963 d->profiles.clear();
1964 d->profiles.setAutoDelete(false);
1965}
1966
1967void ContactView::link(ContactProfile *cp)
1968{
1969 d->profiles.append(cp);
1970}
1971
1972void ContactView::unlink(ContactProfile *cp)
1973{
1974 d->profiles.removeRef(cp);
1975}
1976
1977void ContactView::maybeTip(const QPoint &pos)
1978{
1979 ContactViewItem *i = (ContactViewItem *)itemAt(pos);
1980 if(!i)
1981 return;
1982 QRect r(itemRect(i));
1983 if(i->type() == ContactViewItem::Contact)
1984 tip(r, i->u()->makeTip(true, false));
1985 else if(i->type() == ContactViewItem::Profile) {
1986 tip(r, i->contactProfile()->makeTip(true, false));
1987 }
1988 else
1989 tip(r, i->text(0) + " " + i->groupInfo());
1990}
1991
1992void ContactView::keyPressEvent(QKeyEvent *e)
1993{
1994 int key = e->key();
1995 if(key == QListView::Key_Enter || key == QListView::Key_Return)
1996 doEnter();
1997 else if(key == QListView::Key_Space && d->typeAhead.isEmpty())
1998 doContext();
1999 else if (key == QListView::Key_Home || key == QListView::Key_End ||
2000 key == QListView::Key_PageUp || key == QListView::Key_PageDown ||
2001 key == QListView::Key_Up || key == QListView::Key_Down ||
2002 key == QListView::Key_Left || key == QListView::Key_Right) {
2003
2004 d->typeAhead = QString::null;
2005 QListView::keyPressEvent(e);
2006 }
2007 else {
2008 if (!d->doTypeAhead(e))
2009 QListView::keyPressEvent(e);
2010 }
2011}
2012
2013void ContactView::setShowOffline(bool x)
2014{
2015 bool oldstate = v_showOffline;
2016 v_showOffline = x;
2017
2018 if(v_showOffline != oldstate) {
2019 showOffline(v_showOffline);
2020
2021 QPtrListIterator<ContactProfile> it(d->profiles);
2022 for(ContactProfile *cp; (cp = it.current()); ++it) {
2023 if(!v_showOffline)
2024 cp->removeAllUnneededContactItems();
2025 else
2026 cp->addAllNeededContactItems();
2027 }
2028 }
2029}
2030
2031void ContactView::setShowAway(bool x)
2032{
2033 bool oldstate = v_showAway;
2034 v_showAway = x;
2035
2036 if(v_showAway != oldstate) {
2037 showAway(v_showAway);
2038
2039 QPtrListIterator<ContactProfile> it(d->profiles);
2040 for(ContactProfile *cp; (cp = it.current()); ++it) {
2041 if(!v_showAway)
2042 cp->removeAllUnneededContactItems();
2043 else
2044 cp->addAllNeededContactItems();
2045 }
2046 }
2047}
2048
2049void ContactView::setShowHidden(bool x)
2050{
2051 bool oldstate = v_showHidden;
2052 v_showHidden = x;
2053
2054 if(v_showHidden != oldstate) {
2055 showHidden(v_showHidden);
2056
2057 QPtrListIterator<ContactProfile> it(d->profiles);
2058 for(ContactProfile *cp; (cp = it.current()); ++it) {
2059 if(!v_showHidden)
2060 cp->removeAllUnneededContactItems();
2061 else
2062 cp->addAllNeededContactItems();
2063 }
2064 }
2065}
2066
2067/*
2068 * \brief Shows/hides the self contact in roster
2069 *
2070 * \param x - boolean variable specifies whether to show self-contact or not
2071 */
2072void ContactView::setShowSelf(bool x)
2073{
2074 if (v_showSelf != x) {
2075 v_showSelf = x;
2076 showSelf(v_showSelf);
2077
2078 QPtrListIterator<ContactProfile> it(d->profiles);
2079 for(ContactProfile *cp; (cp = it.current()); ++it) {
2080 if (v_showSelf && ! cp->self()) {
2081 cp->addSelf();
2082 }
2083 else if (!v_showSelf && cp->self() && cp->self()->u()->userResourceList().count() <= 1) {
2084 cp->removeSelf();
2085 }
2086 }
2087
2088 recalculateSize();
2089 }
2090}
2091
2092/*
2093 * \brief Event filter. Nuff said.
2094 */
2095bool ContactView::eventFilter( QObject *obj, QEvent *event )
2096{
2097 if ( event->type() == QEvent::Move )
2098 d->determineAutoRosterSizeGrowSide();
2099
2100 return QListView::eventFilter( obj, event );
2101}
2102
2103
2104void ContactView::setShowAgents(bool x)
2105{
2106 bool oldstate = v_showAgents;
2107 v_showAgents = x;
2108
2109 if(v_showAgents != oldstate) {
2110 showAgents(v_showAgents);
2111
2112 QPtrListIterator<ContactProfile> it(d->profiles);
2113 for(ContactProfile *cp; (cp = it.current()); ++it) {
2114 if(!v_showAgents)
2115 cp->removeAllUnneededContactItems();
2116 else
2117 cp->addAllNeededContactItems();
2118 }
2119 }
2120}
2121
2122// right-click context menu
2123void ContactView::qlv_contextMenuRequested(QListViewItem *lvi, const QPoint &pos, int c)
2124{
2125 if(option.useleft)
2126 return;
2127
2128 qlv_contextPopup(lvi, pos, c);
2129}
2130
2131void ContactView::qlv_contextPopup(QListViewItem *lvi, const QPoint &pos, int)
2132{
2133 ContactViewItem *i = (ContactViewItem *)lvi;
2134 if(!i)
2135 return;
2136
2137 i->contactProfile()->doContextMenu(i, pos);
2138}
2139
2140void ContactView::qlv_singleclick(int button, QListViewItem *i, const QPoint &pos, int c)
2141{
2142 lcto_active = false;
2143 QToolTip::hide();
2144
2145 if(!i)
2146 return;
2147
2148 ContactViewItem *item = (ContactViewItem *)i;
2149 setSelected(item, true);
2150
2151 if(button == QListView::MidButton) {
2152 if(item->type() == ContactViewItem::Contact)
2153 item->contactProfile()->scActionDefault(item);
2154 }
2155 else {
2156 const QPixmap * pix = item->pixmap(0);
2157 if (button == QListView::LeftButton && item->type() == ContactViewItem::Group && pix && viewport()->mapFromGlobal(pos).x() <= pix->width() + treeStepSize()) {
2158 setOpen(item, !item->isOpen());
2159 }
2160 else if(option.useleft) {
2161 if(button == QListView::LeftButton) {
2162 if(option.singleclick) {
2163 qlv_contextPopup(i, pos, c);
2164 }
2165 else {
2166 lcto_active = true;
2167 lcto_pos = pos;
2168 lcto_item = i;
2169 QTimer::singleShot(QApplication::doubleClickInterval()/2, this, SLOT(leftClickTimeOut()));
2170 }
2171 }
2172 else if(option.singleclick && button == QListView::RightButton) {
2173 if(item->type() == ContactViewItem::Contact)
2174 item->contactProfile()->scActionDefault(item);
2175 }
2176 }
2177 else {
2178 //if(button == QListView::RightButton) {
2179 // qlv_contextPopup(i, pos, c);
2180 //}
2181 /*else*/if(button == QListView::LeftButton && option.singleclick) {
2182 if(item->type() == ContactViewItem::Contact)
2183 item->contactProfile()->scActionDefault(item);
2184 }
2185 }
2186 }
2187
2188 d->typeAhead = "";
2189}
2190
2191void ContactView::qlv_doubleclick(QListViewItem *i)
2192{
2193 lcto_active = false;
2194
2195 if(!i)
2196 return;
2197
2198 if(option.singleclick)
2199 return;
2200
2201 ContactViewItem *item = (ContactViewItem *)i;
2202 item->contactProfile()->scActionDefault(item);
2203
2204 d->typeAhead = "";
2205}
2206
2207void ContactView::qlv_itemRenamed(QListViewItem *lvi, int, const QString &text)
2208{
2209 ContactViewItem *i = (ContactViewItem *)lvi;
2210 i->contactProfile()->doItemRenamed(i, text);
2211}
2212
2213void ContactView::leftClickTimeOut()
2214{
2215 if(lcto_active) {
2216 QToolTip::hide();
2217 qlv_contextPopup(lcto_item, lcto_pos, 0);
2218 lcto_active = false;
2219 }
2220}
2221
2222void ContactView::optionsUpdate()
2223{
2224 // set the font
2225 QFont f;
2226 f.fromString(option.font[fRoster]);
2227 QListView::setFont(f);
2228
2229 // set the text and background colors
2230 QPalette mypal = QListView::palette();
2231 mypal.setColor(QColorGroup::Text, option.color[cOnline]);
2232 mypal.setColor(QColorGroup::Base, option.color[cListBack]);
2233 QListView::setPalette(mypal);
2234
2235 // reload the icons
2236 QListViewItemIterator it(this);
2237 ContactViewItem *item;
2238 for(; it.current() ; ++it) {
2239 item = (ContactViewItem *)it.current();
2240 item->optionsUpdate();
2241 }
2242
2243 // resize if necessary
2244 if (option.autoRosterSize)
2245 recalculateSize();
2246
2247 update();
2248}
2249
2250void ContactView::resetAnim()
2251{
2252 for(QListViewItemIterator it(this); it.current() ; ++it) {
2253 ContactViewItem *item = (ContactViewItem *)it.current();
2254 if(item->isAlerting())
2255 item->resetAnim();
2256 }
2257}
2258
2259void ContactView::doRecvEvent()
2260{
2261 ContactViewItem *i = (ContactViewItem *)selectedItem();
2262 if(!i)
2263 return;
2264 i->contactProfile()->scRecvEvent(i);
2265}
2266
2267void ContactView::doRename()
2268{
2269 ContactViewItem *i = (ContactViewItem *)selectedItem();
2270 if(!i)
2271 return;
2272 i->contactProfile()->scRename(i);
2273}
2274
2275void ContactView::doAssignAvatar()
2276{
2277 // FIXME: Should check the supported filetypes dynamically
2278 QString file = QFileDialog::getOpenFileName("", tr("All files (*.png *.jpg *.gif)"), this, 0, tr("Choose an image"));
2279 if (!file.isNull()) {
2280 ContactViewItem *i = (ContactViewItem *)selectedItem();
2281 i->contactProfile()->psiAccount()->avatarFactory()->importManualAvatar(i->u()->jid(),file);
2282 }
2283}
2284
2285void ContactView::doClearAvatar()
2286{
2287 ContactViewItem *i = (ContactViewItem *)selectedItem();
2288 i->contactProfile()->psiAccount()->avatarFactory()->removeManualAvatar(i->u()->jid());
2289}
2290
2291void ContactView::doEnter()
2292{
2293 ContactViewItem *i = (ContactViewItem *)selectedItem();
2294 if(!i)
2295 return;
2296 i->contactProfile()->scActionDefault(i);
2297}
2298
2299void ContactView::doContext()
2300{
2301 ContactViewItem *i = (ContactViewItem *)selectedItem();
2302 if(!i)
2303 return;
2304 ensureItemVisible(i);
2305
2306 if(i->type() == ContactViewItem::Group)
2307 setOpen(i, !i->isOpen());
2308 else
2309 qlv_contextPopup(i, viewport()->mapToGlobal(QPoint(32, itemPos(i))), 0);
2310}
2311
2312void ContactView::doSendMessage()
2313{
2314 ContactViewItem *i = (ContactViewItem *)selectedItem();
2315 if(!i)
2316 return;
2317 i->contactProfile()->scSendMessage(i);
2318}
2319
2320void ContactView::doOpenChat()
2321{
2322 ContactViewItem *i = (ContactViewItem *)selectedItem();
2323 if(!i)
2324 return;
2325 i->contactProfile()->scOpenChat(i);
2326}
2327
2328void ContactView::doHistory()
2329{
2330 ContactViewItem *i = (ContactViewItem *)selectedItem();
2331 if(!i)
2332 return;
2333 i->contactProfile()->scHistory(i);
2334}
2335
2336void ContactView::doVCard()
2337{
2338 ContactViewItem *i = (ContactViewItem *)selectedItem();
2339 if(!i)
2340 return;
2341 i->contactProfile()->scVCard(i);
2342}
2343
2344void ContactView::doLogon()
2345{
2346 ContactViewItem *i = (ContactViewItem *)selectedItem();
2347 if(!i)
2348 return;
2349 Status s=i->contactProfile()->psiAccount()->status();
2350 i->contactProfile()->scAgentSetStatus(i, s);
2351}
2352
2353void ContactView::doRemove()
2354{
2355 ContactViewItem *i = (ContactViewItem *)selectedItem();
2356 if(!i)
2357 return;
2358 i->contactProfile()->scRemove(i);
2359}
2360
2361QDragObject *ContactView::dragObject()
2362{
2363 ContactViewItem *i = (ContactViewItem *)selectedItem();
2364 if(!i)
2365 return 0;
2366 if(i->type() != ContactViewItem::Contact)
2367 return 0;
2368
2369 QDragObject *d = new QTextDrag(i->u()->jid().full(), this);
2370 d->setPixmap(IconsetFactory::iconPixmap("status/online"), QPoint(8,8));
2371 return d;
2372}
2373
2374bool ContactView::allowResize() const
2375{
2376 if ( !option.autoRosterSize )
2377 return false;
2378
2379 if ( topLevelWidget()->isMaximized() )
2380 return false;
2381
2382 return true;
2383}
2384
2385QSize ContactView::minimumSizeHint() const
2386{
2387 return QSize( minimumWidth(), minimumHeight() );
2388}
2389
2390QSize ContactView::sizeHint() const
2391{
2392 // save some CPU
2393 if ( !allowResize() )
2394 return minimumSizeHint();
2395
2396 QSize s( QListView::sizeHint().width(), 0 );
2397 int border = 5;
2398 int h = border;
2399
2400 QListView *listView = (QListView *)this;
2401 QListViewItemIterator it( listView );
2402 while ( it.current() ) {
2403 if ( it.current()->isVisible() ) {
2404 // also we need to check whether the group is open or closed
2405 bool show = true;
2406 QListViewItem *item = it.current()->parent();
2407 while ( item ) {
2408 if ( !item->isOpen() ) {
2409 show = false;
2410 break;
2411 }
2412 item = item->parent();
2413 }
2414
2415 if ( show )
2416 h += it.current()->height();
2417 }
2418
2419 ++it;
2420 }
2421
2422 QWidget *topParent = topLevelWidget();
2423 QRect desktop = qApp->desktop()->availableGeometry( (QWidget *)topParent );
2424 int dh = h - d->lastSize.height();
2425
2426 // check that our dialog's height doesn't exceed the desktop's
2427 if ( allowResize() && dh > 0 && (topParent->frameGeometry().height() + dh) >= desktop.height() ) {
2428 h = desktop.height() - ( topParent->frameGeometry().height() - d->lastSize.height() );
2429 }
2430
2431 int minH = minimumSizeHint().height();
2432 if ( h < minH )
2433 h = minH + border;
2434 s.setHeight( h );
2435 return s;
2436}
2437
2438/*
2439 * \brief Adds the request to recalculate the ContactView size to the event queue
2440 */
2441void ContactView::recalculateSize()
2442{
2443 d->recalculateSizeTimer->start( 0, true );
2444}
2445
2446//----------------------------------------------------------------------------
2447// ContactViewItem
2448//----------------------------------------------------------------------------
2449class ContactViewItem::Private
2450{
2451private:
2452 ContactViewItem *cvi;
2453
2454public:
2455 Private(ContactViewItem *parent, ContactProfile *_cp) {
2456 cvi = parent;
2457 cp = _cp;
2458 u = 0;
2459
2460 icon = lastIcon = 0;
2461 }
2462
2463 ~Private() {
2464 }
2465
2466 void initGroupState() {
2467 UserAccount::GroupData gd = groupData();
2468 cvi->setOpen(gd.open);
2469 }
2470
2471 QString getGroupName() {
2472 QString group;
2473 if ( type == Profile )
2474 group = "/\\/" + profileName + "\\/\\";
2475 else
2476 group = groupName;
2477
2478 return group;
2479 }
2480
2481 QMap<QString, UserAccount::GroupData> *groupState() {
2482 return (QMap<QString, UserAccount::GroupData> *)&cp->psiAccount()->userAccount().groupState;
2483 }
2484
2485 UserAccount::GroupData groupData() {
2486 QMap<QString, UserAccount::GroupData> groupState = (QMap<QString, UserAccount::GroupData>)cp->psiAccount()->userAccount().groupState;
2487 QMap<QString, UserAccount::GroupData>::Iterator it = groupState.find(getGroupName());
2488
2489 UserAccount::GroupData gd;
2490 gd.open = true;
2491 gd.rank = 0;
2492
2493 if ( it != groupState.end() )
2494 gd = it.data();
2495
2496 return gd;
2497 }
2498
2499 int type;
2500 ContactProfile *cp;
2501 int status;
2502
2503 // profiles
2504 QString profileName;
2505 bool ssl;
2506
2507 // groups
2508 int groupType;
2509 QString groupName;
2510 QString groupInfo;
2511
2512 // contact
2513 UserListItem *u;
2514 bool isAgent;
2515 bool alerting;
2516 bool animatingNick;
2517
2518 Icon *icon, *lastIcon;
2519 int animateNickX, animateNickColor; // nick animation
2520};
2521
2522ContactViewItem::ContactViewItem(const QString &profileName, ContactProfile *cp, ContactView *parent)
2523:QListViewItem(parent)
2524{
2525 d = new Private(this, cp);
2526 d->type = Profile;
2527 d->profileName = profileName;
2528 d->alerting = false;
2529 d->ssl = false;
2530
2531 setProfileState(STATUS_OFFLINE);
2532 setText(0, profileName);
2533
2534 d->initGroupState();
2535}
2536
2537ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactView *parent)
2538:QListViewItem(parent)
2539{
2540 d = new Private(this, cp);
2541 d->type = Group;
2542 d->groupName = groupName;
2543 d->groupType = groupType;
2544 d->alerting = false;
2545
2546 drawGroupIcon();
2547 resetGroupName();
2548 setDropEnabled(true);
2549
2550 d->initGroupState();
2551}
2552
2553ContactViewItem::ContactViewItem(const QString &groupName, int groupType, ContactProfile *cp, ContactViewItem *parent)
2554:QListViewItem(parent)
2555{
2556 d = new Private(this, cp);
2557 d->type = Group;
2558 d->groupName = groupName;
2559 d->groupType = groupType;
2560 d->alerting = false;
2561
2562 drawGroupIcon();
2563 resetGroupName();
2564 setDropEnabled(true);
2565
2566 if(!parent->isVisible())
2567 setVisible(false);
2568
2569 d->initGroupState();
2570}
2571
2572ContactViewItem::ContactViewItem(UserListItem *u, ContactProfile *cp, ContactViewItem *parent)
2573:QListViewItem(parent)
2574{
2575 d = new Private(this, cp);
2576 d->cp = cp;
2577 d->type = Contact;
2578 d->u = u;
2579 d->alerting = false;
2580 d->animatingNick = false;
2581
2582 cacheValues();
2583
2584 resetStatus();
2585 resetName();
2586
2587 setDragEnabled(true);
2588 setDropEnabled(true);
2589
2590 if(!parent->isVisible())
2591 setVisible(false);
2592}
2593
2594ContactViewItem::~ContactViewItem()
2595{
2596 setIcon( 0 );
2597 delete d;
2598}
2599
2600void ContactViewItem::cacheValues()
2601{
2602 if ( d->u ) {
2603 if( !d->u->isAvailable() )
2604 d->status = STATUS_OFFLINE;
2605 else
2606 d->status = makeSTATUS((*d->u->priority()).status());
2607 d->isAgent = d->u->isTransport();
2608 }
2609}
2610
2611ContactProfile *ContactViewItem::contactProfile() const
2612{
2613 return d->cp;
2614}
2615
2616int ContactViewItem::type() const
2617{
2618 return d->type;
2619}
2620
2621const QString & ContactViewItem::groupName() const
2622{
2623 return d->groupName;
2624}
2625
2626const QString & ContactViewItem::groupInfo() const
2627{
2628 return d->groupInfo;
2629}
2630
2631int ContactViewItem::groupType() const
2632{
2633 return d->groupType;
2634}
2635
2636UserListItem *ContactViewItem::u() const
2637{
2638 return d->u;
2639}
2640
2641int ContactViewItem::status() const
2642{
2643 return d->status;
2644}
2645
2646bool ContactViewItem::isAgent() const
2647{
2648 return d->isAgent;
2649}
2650
2651bool ContactViewItem::isAlerting() const
2652{
2653 return d->alerting;
2654}
2655
2656bool ContactViewItem::isAnimatingNick() const
2657{
2658 return d->animatingNick;
2659}
2660
2661int ContactViewItem::parentGroupType() const
2662{
2663 ContactViewItem *item = (ContactViewItem *)QListViewItem::parent();
2664 return item->groupType();
2665}
2666
2667void ContactViewItem::drawGroupIcon()
2668{
2669 if ( d->type == Group ) {
2670 if ( childCount() == 0 )
2671 setIcon(IconsetFactory::iconPtr("psi/groupEmpty"));
2672 else if ( isOpen() )
2673 setIcon(IconsetFactory::iconPtr("psi/groupOpen"));
2674 else
2675 setIcon(IconsetFactory::iconPtr("psi/groupClosed"));
2676 }
2677 else if ( d->type == Profile ) {
2678 if ( !d->alerting )
2679 setProfileState(d->status);
2680 }
2681}
2682
2683void ContactViewItem::paintFocus(QPainter *, const QColorGroup &, const QRect &)
2684{
2685 // re-implimented to do nothing. selection is enough of a focus
2686}
2687
2688void ContactViewItem::paintBranches(QPainter *p, const QColorGroup &cg, int w, int, int h)
2689{
2690 // paint a square of nothing
2691 p->fillRect(0, 0, w, h, cg.base());
2692}
2693
2694void ContactViewItem::paintCell(QPainter *p, const QColorGroup & cg, int column, int width, int alignment)
2695{
2696 if ( d->type == Contact ) {
2697 QColorGroup xcg = cg;
2698
2699 if(d->status == STATUS_AWAY || d->status == STATUS_XA)
2700 xcg.setColor(QColorGroup::Text, option.color[cAway]);
2701 else if(d->status == STATUS_DND)
2702 xcg.setColor(QColorGroup::Text, option.color[cDND]);
2703 else if(d->status == STATUS_OFFLINE)
2704 xcg.setColor(QColorGroup::Text, option.color[cOffline]);
2705
2706 if(d->animatingNick) {
2707 xcg.setColor(QColorGroup::Text, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]);
2708 xcg.setColor(QColorGroup::HighlightedText, d->animateNickColor ? option.color[cAnimFront] : option.color[cAnimBack]);
2709 }
2710
2711 QListViewItem::paintCell(p, xcg, column, width, alignment);
2712
2713 QFontMetrics fm(p->font());
2714 const QPixmap *pix = pixmap(column);
2715 int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8;
2716
2717 if ( d->u ) {
2718 UserResourceList::ConstIterator it = d->u->priority();
2719 if(it != d->u->userResourceList().end()) {
2720 if(d->u->isSecure((*it).name())) {
2721 const QPixmap &pix = IconsetFactory::iconPixmap("psi/cryptoYes");
2722 int y = (height() - pix.height()) / 2;
2723 p->drawPixmap(x, y, pix);
2724 x += 24;
2725 }
2726 }
2727 }
2728 }
2729 else if ( d->type == Group || d->type == Profile ) {
2730 QColorGroup xcg = cg;
2731
2732 if(d->type == Profile) {
2733 xcg.setColor(QColorGroup::Text, option.color[cProfileFore]);
2734 xcg.setColor(QColorGroup::Base, option.color[cProfileBack]);
2735 }
2736 else if(d->type == Group) {
2737 QFont f = p->font();
2738 f.setPointSize(option.smallFontSize);
2739 p->setFont(f);
2740 xcg.setColor(QColorGroup::Text, option.color[cGroupFore]);
2741 if (!option.clNewHeadings) {
2742 xcg.setColor(QColorGroup::Base, option.color[cGroupBack]);
2743 }
2744 }
2745
2746 QListViewItem::paintCell(p, xcg, column, width, alignment);
2747
2748 QFontMetrics fm(p->font());
2749 const QPixmap *pix = pixmap(column);
2750 int x = fm.width(text(column)) + (pix ? pix->width() : 0) + 8;
2751
2752 if(d->type == Profile) {
2753 const QPixmap &pix = d->ssl ? IconsetFactory::iconPixmap("psi/cryptoYes") : IconsetFactory::iconPixmap("psi/cryptoNo");
2754 int y = (height() - pix.height()) / 2;
2755 p->drawPixmap(x, y, pix);
2756 x += 24;
2757 }
2758
2759 if(isSelected())
2760 p->setPen(xcg.highlightedText());
2761 else
2762 p->setPen(xcg.text());
2763
2764 QFont f_info = p->font();
2765 f_info.setPointSize(option.smallFontSize);
2766 p->setFont(f_info);
2767 QFontMetrics fm_info(p->font());
2768 //int info_x = width - fm_info.width(d->groupInfo) - 8;
2769 int info_x = x;
2770 int info_y = ((height() - fm_info.height()) / 2) + fm_info.ascent();
2771 p->drawText((info_x > x ? info_x : x), info_y, d->groupInfo);
2772
2773 if(d->type == Group && option.clNewHeadings && !isSelected()) {
2774 x += fm.width(d->groupInfo) + 8;
2775 if(x < width - 8) {
2776 int h = (height() / 2) - 1;
2777 p->setPen(QPen(option.color[cGroupBack]));
2778 p->drawLine(x, h, width - 8, h);
2779 h++;
2780 p->setPen(QPen(option.color[cGroupFore]));
2781 /*int h = height() / 2;
2782
2783 p->setPen(QPen(option.color[cGroupBack], 2));*/
2784 p->drawLine(x, h, width - 8, h);
2785 }
2786 }
2787 else {
2788 if (option.outlineHeadings) {
2789 p->setPen(QPen(option.color[cGroupFore]));
2790 p->drawRect(0, 0, width, height());
2791 }
2792 }
2793 }
2794}
2795
2796/*
2797 * \brief "Opens" or "closes the ContactViewItem
2798 *
2799 * When the item is in "open" state, all it's children items are visible.
2800 *
2801 * \param o - if true, the item will be "open"
2802 */
2803void ContactViewItem::setOpen(bool o)
2804{
2805 ((ContactView *)listView())->recalculateSize();
2806
2807 QListViewItem::setOpen(o);
2808 drawGroupIcon();
2809
2810 // save state
2811 UserAccount::GroupData gd = d->groupData();
2812 gd.open = o;
2813 d->groupState()->insert(d->getGroupName(), gd);
2814}
2815
2816void ContactViewItem::insertItem(QListViewItem *i)
2817{
2818 QListViewItem::insertItem(i);
2819 drawGroupIcon();
2820}
2821
2822void ContactViewItem::takeItem(QListViewItem *i)
2823{
2824 QListViewItem::takeItem(i);
2825 drawGroupIcon();
2826}
2827
2828int ContactViewItem::rankGroup(int groupType) const
2829{
2830 static int rankgroups[5] = {
2831 gGeneral,
2832 gUser,
2833 gPrivate,
2834 gAgents,
2835 gNotInList,
2836 };
2837
2838 int n;
2839 for(n = 0; n < (int)sizeof(rankgroups); ++n) {
2840 if(rankgroups[n] == groupType)
2841 break;
2842 }
2843 if(n == sizeof(rankgroups))
2844 return sizeof(rankgroups)-1;
2845
2846 return n;
2847}
2848
2849int ContactViewItem::rankStatus(int status) const
2850{
2851 static int rankstatuses[7] = {
2852 STATUS_CHAT,
2853 STATUS_ONLINE,
2854 STATUS_AWAY,
2855 STATUS_XA,
2856 STATUS_DND,
2857 STATUS_INVISIBLE,
2858 STATUS_OFFLINE,
2859 };
2860
2861 int n;
2862 for(n = 0; n < (int)sizeof(rankstatuses); ++n) {
2863 if(rankstatuses[n] == status)
2864 break;
2865 }
2866 if(n == sizeof(rankstatuses))
2867 return sizeof(rankstatuses)-1;
2868
2869 return n;
2870}
2871
2872int ContactViewItem::compare(QListViewItem *lvi, int, bool) const
2873{
2874 ContactViewItem *i = (ContactViewItem *)lvi;
2875 int ret = 0;
2876
2877 if(d->type == Group || d->type == Profile) {
2878 // contacts always go before groups
2879 if(i->type() == Contact)
2880 ret = 1;
2881 else if(i->type() == Group) {
2882 if ( option.rosterGroupSortStyle == Options::GroupSortStyle_Rank ) {
2883 int ourRank = d->groupData().rank;
2884 int theirRank = i->d->groupData().rank;
2885
2886 ret = ourRank - theirRank;
2887 }
2888 else { // GroupSortStyle_Alpha
2889 ret = rankGroup(d->groupType) - rankGroup(i->groupType());
2890 if(ret == 0)
2891 ret = text(0).lower().localeAwareCompare(i->text(0).lower());
2892 }
2893 }
2894 else if(i->type() == Profile) {
2895 if ( option.rosterAccountSortStyle == Options::AccountSortStyle_Rank ) {
2896 int ourRank = d->groupData().rank;
2897 int theirRank = i->d->groupData().rank;
2898
2899 ret = ourRank - theirRank;
2900 }
2901 else // AccountSortStyle_Alpha
2902 ret = text(0).lower().localeAwareCompare(i->text(0).lower());
2903 }
2904 }
2905 else if(d->type == Contact) {
2906 // contacts always go before groups
2907 if(i->type() == Group)
2908 ret = -1;
2909 else {
2910 if ( option.rosterContactSortStyle == Options::ContactSortStyle_Status ) {
2911 ret = rankStatus(d->status) - rankStatus(i->status());
2912 if(ret == 0)
2913 ret = text(0).lower().localeAwareCompare(i->text(0).lower());
2914 }
2915 else { // ContactSortStyle_Alpha
2916 ret = text(0).lower().localeAwareCompare(i->text(0).lower());
2917 }
2918 }
2919 }
2920
2921 return ret;
2922}
2923
2924void ContactViewItem::setProfileName(const QString &name)
2925{
2926 d->profileName = name;
2927 setText(0, d->profileName);
2928}
2929
2930void ContactViewItem::setProfileState(int status)
2931{
2932 if ( status == -1 ) {
2933 setAlert( IconsetFactory::iconPtr("psi/connect") );
2934 }
2935 else {
2936 d->status = status;
2937
2938 clearAlert();
2939 setIcon(is->statusPtr(status));
2940 }
2941}
2942
2943void ContactViewItem::setProfileSSL(bool on)
2944{
2945 d->ssl = on;
2946 repaint();
2947}
2948
2949void ContactViewItem::setGroupName(const QString &name)
2950{
2951 d->groupName = name;
2952 resetGroupName();
2953
2954 updatePosition();
2955}
2956
2957void ContactViewItem::setGroupInfo(const QString &info)
2958{
2959 d->groupInfo = info;
2960 repaint();
2961}
2962
2963void ContactViewItem::resetStatus()
2964{
2965 if ( !d->alerting && d->u ) {
2966 setIcon(is->statusPtr(d->u));
2967 }
2968}
2969
2970void ContactViewItem::resetName()
2971{
2972 resetStatus();
2973 if ( d->u ) {
2974 QString s = jidnick(d->u->jid().full(), d->u->name());
2975 if ( s != text(0) )
2976 setText(0, s);
2977 }
2978}
2979
2980void ContactViewItem::resetGroupName()
2981{
2982 if ( d->groupName != text(0) )
2983 setText(0, d->groupName);
2984}
2985
2986void ContactViewItem::resetAnim()
2987{
2988 if ( d->alerting ) {
2989 // TODO: think of how to reset animation frame
2990 }
2991}
2992
2993void ContactViewItem::setAlert(const Icon *icon)
2994{
2995 bool reset = false;
2996
2997 if ( !d->alerting ) {
2998 d->alerting = true;
2999 reset = true;
3000 }
3001 else {
3002 if ( d->lastIcon != icon )
3003 reset = true;
3004 }
3005
3006 if ( reset )
3007 setIcon(icon, true);
3008}
3009
3010void ContactViewItem::clearAlert()
3011{
3012 if ( d->alerting ) {
3013 d->alerting = false;
3014 //disconnect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animate()));
3015 resetStatus();
3016 }
3017}
3018
3019void ContactViewItem::setIcon(const Icon *icon, bool alert)
3020{
3021 if ( d->lastIcon == icon ) {
3022 return; // cause less flicker. but still have to run calltree valgring skin on psi while online (mblsha).
3023 }
3024 else
3025 d->lastIcon = (Icon *)icon;
3026
3027 if ( d->icon ) {
3028 disconnect(d->icon, 0, this, 0 );
3029 d->icon->stop();
3030
3031 delete d->icon;
3032 d->icon = 0;
3033 }
3034
3035 QPixmap pix;
3036 if ( icon ) {
3037 if ( !alert )
3038 d->icon = new Icon(*icon);
3039 else
3040 d->icon = new AlertIcon(icon);
3041
3042 connect(d->icon, SIGNAL(pixmapChanged(const QPixmap &)), SLOT(iconUpdated(const QPixmap &)));
3043 d->icon->activated();
3044
3045 pix = d->icon->pixmap();
3046 }
3047
3048 setPixmap(0, pix);
3049}
3050
3051void ContactViewItem::iconUpdated(const QPixmap &pix)
3052{
3053 setPixmap(0, pix);
3054}
3055
3056void ContactViewItem::animateNick()
3057{
3058 d->animateNickColor = !d->animateNickColor;
3059 repaint();
3060
3061 if(++d->animateNickX >= 16)
3062 stopAnimateNick();
3063}
3064
3065void ContactViewItem::stopAnimateNick()
3066{
3067 if ( !d->animatingNick )
3068 return;
3069
3070 disconnect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), this, SLOT(animateNick()));
3071
3072 d->animatingNick = false;
3073 repaint();
3074}
3075
3076void ContactViewItem::setAnimateNick()
3077{
3078 stopAnimateNick();
3079
3080 connect(static_cast<ContactView*>(QListViewItem::listView())->animTimer(), SIGNAL(timeout()), SLOT(animateNick()));
3081
3082 d->animatingNick = true;
3083 d->animateNickX = 0;
3084 animateNick();
3085}
3086
3087void ContactViewItem::updatePosition()
3088{
3089 ContactViewItem *par = (ContactViewItem *)QListViewItem::parent();
3090 if(!par)
3091 return;
3092
3093 ContactViewItem *after = 0;
3094 for(QListViewItem *i = par->firstChild(); i; i = i->nextSibling()) {
3095 ContactViewItem *item = (ContactViewItem *)i;
3096 // skip self
3097 if(item == this)
3098 continue;
3099 int x = compare(item, 0, true);
3100 if(x == 0)
3101 continue;
3102 if(x < 0)
3103 break;
3104 after = item;
3105 }
3106
3107 if(after)
3108 moveItem(after);
3109 else {
3110 QListViewItem *i = par->firstChild();
3111 moveItem(i);
3112 i->moveItem(this);
3113 }
3114}
3115
3116void ContactViewItem::optionsUpdate()
3117{
3118 if(d->type == Group || d->type == Profile) {
3119 drawGroupIcon();
3120 }
3121 else if(d->type == Contact) {
3122 if(!d->alerting)
3123 resetStatus();
3124 else
3125 resetAnim();
3126 }
3127}
3128
3129void ContactViewItem::setContact(UserListItem *u)
3130{
3131 //int oldStatus = d->status;
3132 QString oldName = text(0);
3133 //bool wasAgent = d->isAgent;
3134
3135 QString newName = jidnick(u->jid().full(), u->name());
3136
3137 d->u = u;
3138 cacheValues();
3139
3140 bool needUpdate = false;
3141 //if(d->status != oldStatus || d->isAgent != wasAgent || !u->presenceError().isEmpty()) {
3142 resetStatus();
3143 needUpdate = true;
3144 //}
3145 if(newName != oldName) {
3146 resetName();
3147 needUpdate = true;
3148 }
3149
3150 if(needUpdate)
3151 updatePosition();
3152
3153 repaint();
3154}
3155
3156bool ContactViewItem::acceptDrop(const QMimeSource *m) const
3157{
3158 if ( d->type == Profile )
3159 return false;
3160 else if ( d->type == Group ) {
3161 if(d->groupType != gGeneral && d->groupType != gUser)
3162 return false;
3163 }
3164 else if ( d->type == Contact ) {
3165 if ( d->u && d->u->isSelf() )
3166 return false;
3167 ContactViewItem *par = (ContactViewItem *)QListViewItem::parent();
3168 if(par->groupType() != gGeneral && par->groupType() != gUser)
3169 return false;
3170 }
3171
3172 // Files. Note that the QTextDrag test has to come after QUriDrag.
3173 if (d->type == Contact && QUriDrag::canDecode(m)) {
3174 QStringList l;
3175 if (QUriDrag::decodeLocalFiles(m,l) && !l.isEmpty())
3176 return true;
3177 }
3178
3179 if(!QTextDrag::canDecode(m))
3180 return false;
3181
3182 QString str;
3183 if(!QTextDrag::decode(m, str))
3184 return false;
3185
3186 return true;
3187}
3188
3189void ContactViewItem::dragEntered()
3190{
3191 //printf("entered\n");
3192}
3193
3194void ContactViewItem::dragLeft()
3195{
3196 //printf("left\n");
3197}
3198
3199void ContactViewItem::dropped(QDropEvent *i)
3200{
3201 if(!acceptDrop(i))
3202 return;
3203
3204 // Files
3205 if (QUriDrag::canDecode(i)) {
3206 QStringList l;
3207 if (QUriDrag::decodeLocalFiles(i,l) && !l.isEmpty()) {
3208 d->cp->dragDropFiles(l, this);
3209 return;
3210 }
3211 }
3212
3213 // Text
3214 if(QTextDrag::canDecode(i)) {
3215 QString text;
3216 if(QTextDrag::decode(i, text))
3217 d->cp->dragDrop(text, this);
3218 }
3219}
3220
3221int ContactViewItem::rtti() const
3222{
3223 return 5103;
3224}
3225
3226#include "contactview.moc"
Note: See TracBrowser for help on using the repository browser.