1 /*
2 * This file is part of Licq, an instant messaging client for UNIX.
3 * Copyright (C) 2007-2014 Licq developers <licq-dev@googlegroups.com>
4 *
5 * Licq is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Licq 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 Licq; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include "contactuserdata.h"
21
22 // Standard
23 #include <climits>
24 #include <cstring>
25
26 // Qt
27 #include <QDateTime>
28 #include <QImage>
29
30 // Licq
31 #include <licq/contactlist/user.h>
32 #include <licq/icq/user.h>
33 #include <licq/plugin/pluginmanager.h>
34 #include <licq/pluginsignal.h>
35 #include <licq/socket.h>
36 #include <licq/userevents.h>
37
38 // Qt-gui
39 #include "config/contactlist.h"
40
41 #include "core/gui-defines.h"
42
43 #include "contactgroup.h"
44 #include "contactuser.h"
45
46 using namespace LicqQtGui;
47 /* TRANSLATOR LicqQtGui::ContactUserData */
48
49 using std::string;
50 using Licq::User;
51
52 #define FLASH_TIME 500
53
54 // Can't initialize timers here in static context so set to zero and let first object take care of initialization
55 QTimer* ContactUserData::myRefreshTimer = NULL;
56 QTimer* ContactUserData::myAnimateTimer = NULL;
57
58 int ContactUserData::myAnimatorCount = 0;
59
60
ContactUserData(const Licq::User * licqUser,QObject * parent)61 ContactUserData::ContactUserData(const Licq::User* licqUser, QObject* parent)
62 : myStatus(User::OfflineStatus),
63 myEvents(0),
64 myFlash(false),
65 mySubGroup(ContactListModel::OfflineSubGroup),
66 myVisibility(false),
67 myOnlCounter(0),
68 myCarCounter(0),
69 myAnimating(false),
70 myUserIcon(NULL)
71 {
72 myUserId = licqUser->id();
73
74 if (myRefreshTimer == NULL)
75 {
76 // Create the static timer used to update dynamic contents
77 myRefreshTimer = new QTimer(parent);
78 myRefreshTimer->start(60 * 1000);
79 }
80 connect(myRefreshTimer, SIGNAL(timeout()), SLOT(refresh()));
81
82 // Create the static timer used for animations
83 if (myAnimateTimer == NULL)
84 {
85 myAnimateTimer = new QTimer(parent);
86 myAnimateTimer->setInterval(FLASH_TIME);
87 }
88
89 update(licqUser, 0);
90 }
91
~ContactUserData()92 ContactUserData::~ContactUserData()
93 {
94 // Free up animation timer resource if we were using it
95 if (myFlash || myOnlCounter > 0 || myCarCounter > 0)
96 stopAnimation();
97
98 // Remove this user from all groups
99 while (!myUserInstances.isEmpty())
100 delete myUserInstances.takeFirst();
101
102 if (myUserIcon != NULL)
103 delete myUserIcon;
104 }
105
update(unsigned long subSignal,int argument)106 void ContactUserData::update(unsigned long subSignal, int argument)
107 {
108 if (subSignal == Licq::PluginSignal::UserEvents && argument == 0)
109 {
110 // User fetched our auto response message
111 myCarCounter = ((5*1000/FLASH_TIME)+1)&(-2);
112 startAnimation();
113 return;
114 }
115
116 if (subSignal == Licq::PluginSignal::UserStatus && argument == 1)
117 {
118 // User came online
119 myOnlCounter = 5*1000/FLASH_TIME; // run about 5 seconds
120 startAnimation();
121 // Fall trough to actually update status
122 }
123
124 Licq::UserReadGuard u(myUserId);
125 if (!u.isLocked())
126 return;
127
128 update(*u, subSignal);
129 }
130
update(const Licq::User * u,unsigned long subSignal)131 void ContactUserData::update(const Licq::User* u, unsigned long subSignal)
132 {
133 // Save some old values so we know if we got changes to signal
134 ContactListModel::SubGroupType oldSubGroup = mySubGroup;
135 bool oldVisibility = myVisibility;
136
137 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserStatus)
138 {
139 myStatus = u->status();
140 myStatusInvisible = u->isInvisible();
141 myTouched = u->Touched();
142 }
143
144 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserTyping)
145 myStatusTyping = u->isTyping();
146
147
148 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserInfo)
149 {
150 myBirthday = (u->Birthday() == 0);
151 myPhone = !u->getUserInfoString("PhoneNumber").empty();
152 myCellular = !u->getCellularNumber().empty();
153 }
154
155 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserSecurity)
156 {
157 mySecure = u->Secure();
158 myGPGKey = !u->gpgKey().empty();
159 myGPGKeyEnabled = u->UseGPG();
160 }
161
162 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserSettings)
163 {
164 myCustomAR = !u->customAutoResponse().empty();
165 myNotInList = u->NotInList();
166 myNewUser = u->NewUser();
167 myAwaitingAuth = u->GetAwaitingAuth();
168 myInIgnoreList = u->IgnoreList();
169 myInOnlineNotify = u->OnlineNotify();
170 myInInvisibleList = u->InvisibleList();
171 myInVisibleList = u->VisibleList();
172 }
173
174 if (myUserId.protocolId() == ICQ_PPID)
175 {
176 const Licq::IcqUser* icquser = dynamic_cast<const Licq::IcqUser*>(u);
177
178 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserPluginStatus)
179 {
180 myPhoneFollowMeStatus = icquser->phoneFollowMeStatus();
181 myIcqPhoneStatus = icquser->icqPhoneStatus();
182 mySharedFilesStatus = icquser->sharedFilesStatus();
183 }
184 }
185 else
186 {
187 myPhoneFollowMeStatus = Licq::IcqPluginInactive;
188 myIcqPhoneStatus = Licq::IcqPluginInactive;
189 mySharedFilesStatus = Licq::IcqPluginInactive;
190 }
191
192 updateExtendedStatus();
193
194 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserEvents)
195 updateEvents(u);
196
197 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserPicture)
198 updatePicture(u);
199
200 if (subSignal != Licq::PluginSignal::UserGroups &&
201 subSignal != Licq::PluginSignal::UserPicture &&
202 subSignal != Licq::PluginSignal::UserTyping &&
203 subSignal != Licq::PluginSignal::UserSecurity)
204 {
205 if (myNotInList)
206 mySubGroup = ContactListModel::NotInListSubGroup;
207 else if (myStatus == User::OfflineStatus)
208 mySubGroup = ContactListModel::OfflineSubGroup;
209 else
210 mySubGroup = ContactListModel::OnlineSubGroup;
211
212 updateText(u);
213 updateSorting();
214 updateVisibility();
215 }
216
217 // Note: When we get called from constructor, noone is connected to our signals
218 // and myUserInstances is empty so below code won't trigger anything strange
219
220 // Signal our own data changes before starting to touch groups and bars
221 if (subSignal != Licq::PluginSignal::UserGroups)
222 emit dataChanged(this);
223
224 // If status has changed update the sub groups of all groups
225 if (mySubGroup != oldSubGroup)
226 foreach (ContactUser* user, myUserInstances)
227 user->group()->updateSubGroup(oldSubGroup, mySubGroup, myEvents);
228
229 // Update group visibility
230 if (myVisibility != oldVisibility)
231 foreach (ContactUser* user, myUserInstances)
232 user->group()->updateVisibility(myVisibility, mySubGroup);
233
234 // Add/remove us to/from groups
235 if (subSignal == 0 || subSignal == Licq::PluginSignal::UserSettings || subSignal == Licq::PluginSignal::UserGroups)
236 // Group membership is handled by ContactList so send it a signal to update
237 emit updateUserGroups(this, u);
238 }
239
updatePicture(const Licq::User * u)240 void ContactUserData::updatePicture(const Licq::User* u)
241 {
242 if (myUserIcon != NULL)
243 {
244 delete myUserIcon;
245 myUserIcon = NULL;
246 }
247
248 if (u->GetPicturePresent())
249 {
250 myUserIcon = new QImage(QString::fromLocal8Bit(u->pictureFileName().c_str()));
251 if (myUserIcon->isNull())
252 {
253 delete myUserIcon;
254 myUserIcon = NULL;
255 }
256 }
257 }
258
updateEvents(const Licq::User * u)259 void ContactUserData::updateEvents(const Licq::User* u)
260 {
261 myUrgent = false;
262 myNewMessages = u->NewMessages();
263 if (myEvents != myNewMessages)
264 {
265 foreach (ContactUser* user, myUserInstances)
266 user->group()->updateNumEvents(myNewMessages - myEvents, mySubGroup);
267
268 myEvents = myNewMessages;
269 }
270
271 myEventType = 0;
272
273 if (myNewMessages > 0)
274 {
275 for (unsigned short i = 0; i < myNewMessages; i++)
276 {
277 switch (u->EventPeek(i)->eventType())
278 {
279 case Licq::UserEvent::TypeFile:
280 myEventType = Licq::UserEvent::TypeFile;
281 break;
282 case Licq::UserEvent::TypeChat:
283 if (myEventType != Licq::UserEvent::TypeFile)
284 myEventType = Licq::UserEvent::TypeChat;
285 break;
286 case Licq::UserEvent::TypeUrl:
287 if (myEventType != Licq::UserEvent::TypeFile &&
288 myEventType != Licq::UserEvent::TypeChat)
289 myEventType = Licq::UserEvent::TypeUrl;
290 break;
291 case Licq::UserEvent::TypeContactList:
292 if(myEventType != Licq::UserEvent::TypeFile &&
293 myEventType != Licq::UserEvent::TypeChat &&
294 myEventType != Licq::UserEvent::TypeUrl)
295 myEventType = Licq::UserEvent::TypeContactList;
296 case Licq::UserEvent::TypeMessage:
297 default:
298 if (myEventType == 0)
299 myEventType = Licq::UserEvent::TypeMessage;
300 break;
301 }
302 if (u->EventPeek(i)->IsUrgent())
303 myUrgent = true;
304 }
305 }
306 Config::ContactList::FlashMode flash = Config::ContactList::instance()->flash();
307 bool shouldFlash = ((myNewMessages > 0 && flash == Config::ContactList::FlashAll) ||
308 (myUrgent && flash == Config::ContactList::FlashUrgent));
309
310 if (shouldFlash != myFlash)
311 {
312 myFlash = shouldFlash;
313
314 if (myFlash)
315 {
316 myFlashCounter = false;
317 startAnimation();
318 }
319 }
320 }
321
updateExtendedStatus()322 void ContactUserData::updateExtendedStatus()
323 {
324 // Make a bitmask of everything the delegate needs to display extended icons
325 myExtendedStatus = 0;
326
327 if (myStatusInvisible)
328 myExtendedStatus |= ContactListModel::InvisibleStatus;
329
330 if (myStatusTyping)
331 myExtendedStatus |= ContactListModel::TypingStatus;
332
333 if (myPhoneFollowMeStatus == Licq::IcqPluginActive)
334 myExtendedStatus |= ContactListModel::PhoneFollowMeActiveStatus;
335 else if (myPhoneFollowMeStatus == Licq::IcqPluginBusy)
336 myExtendedStatus |= ContactListModel::PhoneFollowMeBusyStatus;
337
338 if (myIcqPhoneStatus == Licq::IcqPluginActive)
339 myExtendedStatus |= ContactListModel::IcqPhoneActiveStatus;
340 else if (myIcqPhoneStatus == Licq::IcqPluginBusy)
341 myExtendedStatus |= ContactListModel::IcqPhoneBusyStatus;
342
343 if (mySharedFilesStatus == Licq::IcqPluginActive)
344 myExtendedStatus |= ContactListModel::SharedFilesStatus;
345
346 if (myCustomAR)
347 myExtendedStatus |= ContactListModel::CustomArStatus;
348
349 if (mySecure)
350 myExtendedStatus |= ContactListModel::SecureStatus;
351
352 if (myBirthday)
353 myExtendedStatus |= ContactListModel::BirthdayStatus;
354
355 if (myPhone)
356 myExtendedStatus |= ContactListModel::PhoneStatus;
357
358 if (myCellular)
359 myExtendedStatus |= ContactListModel::CellularStatus;
360
361 if (myGPGKey)
362 myExtendedStatus |= ContactListModel::GpgKeyStatus;
363
364 if (myGPGKeyEnabled)
365 myExtendedStatus |= ContactListModel::GpgKeyEnabledStatus;
366
367 if (myInIgnoreList)
368 myExtendedStatus |= ContactListModel::IgnoreStatus;
369
370 if (myInOnlineNotify)
371 myExtendedStatus |= ContactListModel::OnlineNotifyStatus;
372
373 if (myNotInList)
374 myExtendedStatus |= ContactListModel::NotInListStatus;
375
376 if (myInInvisibleList)
377 myExtendedStatus |= ContactListModel::InvisibleListStatus;
378
379 if (myInVisibleList)
380 myExtendedStatus |= ContactListModel::VisibleListStatus;
381
382 if (myNewUser)
383 myExtendedStatus |= ContactListModel::NewUserStatus;
384
385 if (myAwaitingAuth)
386 myExtendedStatus |= ContactListModel::AwaitingAuthStatus;
387 }
388
updateSorting()389 void ContactUserData::updateSorting()
390 {
391 // Set status sort order
392 int sort = 9;
393 if (myStatus & User::OccupiedStatus)
394 sort = 1;
395 else if (myStatus & User::DoNotDisturbStatus)
396 sort = 2;
397 else if (myStatus & User::AwayStatus)
398 sort = 3;
399 else if (myStatus & User::NotAvailableStatus)
400 sort = 4;
401 else if (myStatus == User::OfflineStatus)
402 sort = 5;
403 else
404 sort = 0;
405
406 // Set sorting
407 mySortKey = "";
408 switch (Config::ContactList::instance()->sortByStatus())
409 {
410 case 0: // no sorting
411 break;
412 case 1: // sort by status
413 mySortKey.sprintf("%1x", sort);
414 break;
415 case 2: // sort by status and last event
416 mySortKey.sprintf("%1x%016lx", sort, ULONG_MAX - myTouched);
417 break;
418 case 3: // sort by status and number of new messages
419 mySortKey.sprintf("%1x%016lx", sort, ULONG_MAX - myNewMessages);
420 break;
421 }
422 mySortKey += myText[0];
423 }
424
updateText(const Licq::User * licqUser)425 bool ContactUserData::updateText(const Licq::User* licqUser)
426 {
427 bool hasChanged = false;
428
429 myAlias = QString::fromUtf8(licqUser->getAlias().c_str());
430
431 for (int i = 0; i < Config::ContactList::instance()->columnCount(); i++)
432 {
433 QString format = Config::ContactList::instance()->columnFormat(i);
434 format.replace("%a", "@_USER_ALIAS_@");
435 QString newStr = QString::fromLocal8Bit(licqUser->usprintf(format.toLocal8Bit().constData()).c_str());
436 newStr.replace("@_USER_ALIAS_@", myAlias);
437
438 if (newStr != myText[i])
439 {
440 myText[i] = newStr;
441 hasChanged = true;
442 }
443 }
444 return hasChanged;
445 }
446
configUpdated()447 void ContactUserData::configUpdated()
448 {
449 bool oldVisibility = myVisibility;
450
451 {
452 Licq::UserReadGuard u(myUserId);
453 if (!u.isLocked())
454 return;
455
456 updateText(*u);
457 updateSorting();
458 updateVisibility();
459 }
460
461 emit dataChanged(this);
462
463 // Update groups
464 if (myVisibility != oldVisibility)
465 foreach (ContactUser* user, myUserInstances)
466 user->group()->updateVisibility(myVisibility, mySubGroup);
467 }
468
updateVisibility()469 void ContactUserData::updateVisibility()
470 {
471 myVisibility = false;
472
473 // Only hide contacts who are offline
474 if (myStatus != User::OfflineStatus)
475 myVisibility = true;
476
477 // Don't hide contacts with unread events
478 if (myEvents > 0)
479 myVisibility = true;
480
481 // ... or the contact is in online notify list and option "Always show online notify users" is active
482 if (Config::ContactList::instance()->alwaysShowONU() &&
483 ((myExtendedStatus & ContactListModel::OnlineNotifyStatus) != 0))
484 myVisibility = true;
485
486 // ... or the contact is not added to the list
487 if ((myExtendedStatus & ContactListModel::NotInListStatus) != 0)
488 myVisibility = true;
489 }
490
setData(const QVariant & value,int role)491 bool ContactUserData::setData(const QVariant& value, int role)
492 {
493 if (role != ContactListModel::NameRole || !value.isValid())
494 return false;
495
496 if (value.toString() == myAlias)
497 return true;
498
499 {
500 Licq::UserWriteGuard u(myUserId);
501 if (!u.isLocked())
502 return false;
503
504 myAlias = value.toString();
505 u->SetKeepAliasOnUpdate(true);
506 u->setAlias(myAlias.toUtf8().data());
507
508 Licq::gPluginManager.pushPluginSignal(new Licq::PluginSignal(
509 Licq::PluginSignal::SignalUser, Licq::PluginSignal::UserBasic, myUserId));
510 }
511
512 return true;
513 }
514
refresh()515 void ContactUserData::refresh()
516 {
517 // Here we update any content that may be dynamic, for example timestamps
518
519 bool birthday;
520 bool hasChanged;
521 {
522 Licq::UserReadGuard u(myUserId);
523 if (!u.isLocked())
524 return;
525
526 // Check if birthday icon should be updated
527 birthday = (u->Birthday() == 0);
528 hasChanged = updateText(*u);
529 }
530
531 if (birthday != myBirthday)
532 {
533 myBirthday = birthday;
534 hasChanged = true;
535 if (myBirthday)
536 myExtendedStatus |= ContactListModel::BirthdayStatus;
537 else
538 myExtendedStatus &= ~ContactListModel::BirthdayStatus;
539 }
540
541 // To reduce performance impact on refreshes, keep track on
542 // whether anything has changed so we don't force unnecessary updates
543 if (hasChanged)
544 {
545 updateSorting();
546 emit dataChanged(this);
547 }
548 }
549
startAnimation()550 void ContactUserData::startAnimation()
551 {
552 // Start common timer if not already running
553 if (!myAnimateTimer->isActive())
554 myAnimateTimer->start();
555
556 // Attach to signal if we are not already animating something else
557 if (!myAnimating)
558 {
559 myAnimatorCount++;
560 connect(myAnimateTimer, SIGNAL(timeout()), SLOT(animate()));
561 myAnimating = true;
562 }
563 }
564
stopAnimation()565 void ContactUserData::stopAnimation()
566 {
567 // Disconnect from timer and keep track of usage
568 disconnect(myAnimateTimer, SIGNAL(timeout()), this, SLOT(animate()));
569 myAnimatorCount--;
570
571 // Stop animation timer if noone is using it anymore
572 if (myAnimatorCount == 0)
573 myAnimateTimer->stop();
574
575 myAnimating = false;
576 }
577
animate()578 void ContactUserData::animate()
579 {
580 // Animation for incoming event
581 if (myFlash)
582 myFlashCounter = !myFlashCounter;
583
584 // Animation for going online
585 if (myOnlCounter > 0)
586 myOnlCounter--;
587
588 // Animation for auto response read
589 if (myCarCounter > 0)
590 myCarCounter--;
591
592 // Release timer if this was last animation
593 if (!myFlash && myOnlCounter == 0 && myCarCounter == 0)
594 stopAnimation();
595
596 // data() will check the counter value to determine which icon to show so nothing to do here except triggering an update
597 emit dataChanged(this);
598 }
599
addGroup(ContactUser * user)600 void ContactUserData::addGroup(ContactUser* user)
601 {
602 myUserInstances.append(user);
603 }
604
removeGroup(ContactUser * user)605 void ContactUserData::removeGroup(ContactUser* user)
606 {
607 myUserInstances.removeAll(user);
608 }
609
data(int column,int role) const610 QVariant ContactUserData::data(int column, int role) const
611 {
612 switch (role)
613 {
614 case Qt::DisplayRole:
615 if (column >= 0 && column < MAX_COLUMNCOUNT)
616 return myText[column];
617 break;
618
619 case ContactListModel::NameRole:
620 return myAlias;
621
622 case Qt::ToolTipRole:
623 return tooltip();
624
625 case ContactListModel::UserIdRole:
626 return QVariant::fromValue(myUserId);
627
628 case ContactListModel::AccountIdRole:
629 return myUserId.accountId().c_str();
630
631 case ContactListModel::PpidRole:
632 return static_cast<unsigned int>(myUserId.protocolId());
633
634 case ContactListModel::ItemTypeRole:
635 return ContactListModel::UserItem;
636
637 case ContactListModel::SortPrefixRole:
638 // Primary sorting by sub group, make room for the seprator bars between each sub group
639 return 2 * mySubGroup + 1;
640
641 case ContactListModel::SortRole:
642 return mySortKey;
643
644 case ContactListModel::UnreadEventsRole:
645 return myEvents;
646
647 case ContactListModel::SubGroupRole:
648 return mySubGroup;
649
650 case ContactListModel::StatusRole:
651 return myStatus;
652
653 case ContactListModel::ExtendedStatusRole:
654 return myExtendedStatus;
655
656 case ContactListModel::UserIconRole:
657 if (myUserIcon != NULL)
658 return *myUserIcon;
659 break;
660
661 case ContactListModel::EventTypeRole:
662 return myEventType;
663
664 case ContactListModel::CarAnimationRole:
665 if (myCarCounter > 0)
666 return myCarCounter & 1;
667 break;
668
669 case ContactListModel::OnlineAnimationRole:
670 if (myOnlCounter > 0)
671 return myOnlCounter & 1;
672 break;
673
674 case ContactListModel::EventAnimationRole:
675 if (myFlash)
676 return myFlashCounter;
677 else if (myNewMessages > 0)
678 // No flashing but we have unread events so show a static event icon
679 return 1;
680 break;
681
682 case ContactListModel::VisibilityRole:
683 return myVisibility;
684 }
685
686 return QVariant();
687 }
688
tooltip() const689 QString ContactUserData::tooltip() const
690 {
691 Licq::UserReadGuard u(myUserId);
692 if (!u.isLocked())
693 return "";
694
695 Config::ContactList* config = Config::ContactList::instance();
696
697 QString s = "<nobr>";
698 if (config->popupPicture() && u->GetPicturePresent())
699 {
700 QString file = QString::fromLocal8Bit(u->pictureFileName().c_str());
701 QImage picture = QImage(file);
702 if (!picture.isNull())
703 s += QString("<center><img src=\"%1\"></center>").arg(file);
704 }
705
706 s += User::statusToString(myStatus).c_str();
707
708 if (config->popupAlias() && !u->getAlias().empty())
709 s += "<br>" + QString::fromUtf8(u->getAlias().c_str());
710
711 if (config->popupName())
712 {
713 string fullName = u->getFullName();
714 if (!fullName.empty())
715 s += "<br>" + QString::fromUtf8(fullName.c_str());
716 }
717
718 if (myBirthday)
719 s += "<br><b>" + tr("Birthday Today!") + "</b>";
720
721 if (myStatus != User::OfflineStatus)
722 {
723 if (myStatusTyping)
724 s += "<br>" + tr("Typing a message");
725 if (myPhoneFollowMeStatus == Licq::IcqPluginActive)
726 s += "<br>" + tr("Phone "Follow Me": Available");
727 else if (myPhoneFollowMeStatus == Licq::IcqPluginBusy)
728 s += "<br>" + tr("Phone "Follow Me": Busy");
729
730 if (myIcqPhoneStatus == Licq::IcqPluginActive)
731 s += "<br>" + tr("ICQphone: Available");
732 else if (myIcqPhoneStatus == Licq::IcqPluginBusy)
733 s += "<br>" + tr("ICQphone: Busy");
734
735 if (mySharedFilesStatus == Licq::IcqPluginActive)
736 s += "<br>" + tr("File Server: Enabled");
737 }
738
739 if (mySecure)
740 s += "<br>" + tr("Secure connection");
741
742 if (myCustomAR)
743 s += "<br>" + tr("Custom Auto Response");
744
745 if (config->popupAuth() && u->GetAwaitingAuth())
746 s += "<br>" + tr("Awaiting authorization");
747
748 if (u->isOnline() && !u->clientInfo().empty())
749 s += "<br>" + QString::fromUtf8(u->clientInfo().c_str());
750
751 if (!u->autoResponse().empty() && myStatus & User::MessageStatuses)
752 s += "<br><u>" + tr("Auto Response:") + "</u><br> " +
753 QString::fromUtf8(u->autoResponse().c_str()).trimmed()
754 .replace("\n", "<br> ");
755
756 if (config->popupEmail())
757 {
758 string email = u->getEmail();
759 if (!email.empty())
760 s += "<br>" + tr("E: ") + QString::fromUtf8(email.c_str());
761 }
762
763 if (config->popupPhone() && myPhone)
764 s += "<br>" + tr("P: ") + QString::fromUtf8(u->getUserInfoString("PhoneNumber").c_str());
765
766 if (config->popupCellular() && myCellular)
767 s += "<br>" + tr("C: ") + QString::fromUtf8(u->getCellularNumber().c_str());
768
769 if (config->popupFax())
770 {
771 string faxNumber = u->getUserInfoString("FaxNumber");
772 if (!faxNumber.empty())
773 s += "<br>" + tr("F: ") + QString::fromUtf8(faxNumber.c_str());
774 }
775
776 if (config->popupIP() && (u->Ip() || u->IntIp()))
777 {
778 char buf[32];
779 Licq::ip_ntoa(u->Ip(), buf);
780 s += "<br>" + tr("Ip: ") + QString::fromLatin1(buf);
781 if (u->IntIp() != 0 && u->IntIp() != u->Ip())
782 {
783 Licq::ip_ntoa(u->IntIp(), buf);
784 s += " / " + QString::fromLatin1(buf);
785 }
786 }
787
788 if (config->popupLastOnline() && u->LastOnline() > 0)
789 {
790 QDateTime t;
791 t.setTime_t(u->LastOnline());
792 s += "<br>" + tr("O: ") + t.toString();
793 }
794
795 if (config->popupOnlineSince() && u->isOnline() && u->OnlineSince() > 0 && u->OnlineSince() <= time(0))
796 s += "<br>" + tr("Logged In: ") + User::RelativeStrTime(u->OnlineSince()).c_str();
797
798 if (config->popupAwayTime() && (myStatus & User::AwayStatuses) && u->awaySince())
799 s += "<br>" + tr("Away: ") + User::RelativeStrTime(u->awaySince()).c_str();
800
801 if (config->popupIdleTime() && u->IdleSince())
802 s += "<br>" + tr("Idle: ") + User::RelativeStrTime(u->IdleSince()).c_str();
803
804 if (config->popupLocalTime())
805 s += "<br>" + tr("Local time: ") + u->usprintf("%F").c_str();
806
807 if (config->popupID())
808 s += "<br>" + tr("ID: ") + u->usprintf("%u").c_str();
809
810 s += "</nobr>";
811
812 return s;
813 }
814