root/trunk/qt4-gui/src/userevents/usersendcommon.cpp

Revision 6466, 42.8 kB (checked in by flynd, 4 months ago)

Use const pointer for owner objects that are fetched only for read access.

Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of Licq, an instant messaging client for UNIX.
4 * Copyright (C) 2000-2006 Licq developers
5 *
6 * Licq is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * Licq is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Licq; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include "usersendcommon.h"
22
23#include "config.h"
24
25#include <assert.h>
26
27#include <QAction>
28#include <QApplication>
29#include <QColorDialog>
30#include <QDateTime>
31#include <QDesktopWidget>
32#include <QDialogButtonBox>
33#include <QGroupBox>
34#include <QKeyEvent>
35#include <QLabel>
36#include <QMenu>
37#include <QMovie>
38#include <QPushButton>
39#include <QShortcut>
40#include <QSplitter>
41#include <QTextCodec>
42#include <QToolBar>
43#include <QToolButton>
44#include <QVBoxLayout>
45
46#ifdef USE_KDE
47#include <KDE/KColorDialog>
48#endif
49
50#include <licq_icq.h>
51#include <licq_icqd.h>
52#include <licq_log.h>
53#include <licq_translate.h>
54
55#include "config/chat.h"
56#include "config/emoticons.h"
57#include "config/iconmanager.h"
58
59#include "core/gui-defines.h"
60#include "core/licqgui.h"
61#include "core/mainwin.h"
62#include "core/messagebox.h"
63#include "core/signalmanager.h"
64
65#include "dialogs/keyrequestdlg.h"
66
67#include "helpers/eventdesc.h"
68#include "helpers/licqstrings.h"
69#include "helpers/usercodec.h"
70
71#include "views/mmuserview.h"
72#include "views/userview.h"
73
74#include "widgets/historyview.h"
75#include "widgets/infofield.h"
76#include "widgets/mledit.h"
77
78#include "selectemoticon.h"
79#include "usereventtabdlg.h"
80#include "usersendchatevent.h"
81#include "usersendcontactevent.h"
82#include "usersendfileevent.h"
83#include "usersendmsgevent.h"
84#include "usersendsmsevent.h"
85#include "usersendurlevent.h"
86
87using namespace std;
88using namespace LicqQtGui;
89/* TRANSLATOR LicqQtGui::UserSendCommon */
90
91const size_t SHOW_RECENT_NUM = 5;
92
93typedef pair<const CUserEvent*, char*> messagePair;
94
95bool orderMessagePairs(const messagePair& mp1, const messagePair& mp2)
96{
97  return (mp1.first->Time() < mp2.first->Time());
98}
99
100UserSendCommon::UserSendCommon(int type, QString id, unsigned long ppid, QWidget* parent, const char* name)
101  : UserEventCommon(id, ppid, parent, name),
102    myType(type)
103{
104  myMassMessageBox = NULL;
105  myPictureLabel = NULL;
106  clearDelay = 250;
107
108  QShortcut* a = new QShortcut(Qt::Key_Escape, this);
109  connect(a, SIGNAL(activated()), SLOT(cancelSend()));
110
111  UserEventTabDlg* tabDlg = LicqGui::instance()->userEventTabDlg();
112  if (tabDlg != NULL && parent == tabDlg)
113  {
114    a = new QShortcut(Qt::ALT + Qt::Key_Left, this);
115    connect(a, SIGNAL(activated()), tabDlg, SLOT(moveLeft()));
116
117    a = new QShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab, this);
118    connect(a, SIGNAL(activated()), tabDlg, SLOT(moveLeft()));
119
120    a = new QShortcut(Qt::ALT + Qt::Key_Right, this);
121    connect(a, SIGNAL(activated()), tabDlg, SLOT(moveRight()));
122
123    a = new QShortcut(Qt::CTRL + Qt::Key_Tab, this);
124    connect(a, SIGNAL(activated()), tabDlg, SLOT(moveRight()));
125  }
126
127  myEventTypeGroup = new QActionGroup(this);
128  connect(myEventTypeGroup, SIGNAL(triggered(QAction*)), SLOT(changeEventType(QAction*)));
129
130  QAction* action;
131  int eventTypesCount = 0;
132
133#define ADD_SENDTYPE(eventFlag, eventType, caption) \
134    if (mySendFuncs & eventFlag) { \
135      action = new QAction(caption, myEventTypeGroup); \
136      action->setData(eventType); \
137      action->setCheckable(true); \
138      eventTypesCount++; \
139    }
140
141  // Populated menu for switching event type
142  ADD_SENDTYPE(PP_SEND_MSG, MessageEvent, tr("Message"));
143  ADD_SENDTYPE(PP_SEND_URL, UrlEvent, tr("URL"));
144  ADD_SENDTYPE(PP_SEND_CHAT, ChatEvent, tr("Chat Request"));
145  ADD_SENDTYPE(PP_SEND_FILE, FileEvent, tr("File Transfer"));
146  ADD_SENDTYPE(PP_SEND_CONTACT, ContactEvent, tr("Contact List"));
147  ADD_SENDTYPE(PP_SEND_SMS, SmsEvent, tr("SMS"));
148
149#undef ADD_SENDTYPE
150
151  QMenu* mnuSendType = new QMenu(this);
152  mnuSendType->addActions(myEventTypeGroup->actions());
153
154  myEventTypeMenu = myToolBar->addAction(tr("Message type"), this, SLOT(showSendTypeMenu()));
155  myEventTypeMenu->setShortcut(Qt::ALT + Qt::Key_P);
156  pushToolTip(myEventTypeMenu, tr("Select type of message to send"));
157  myEventTypeMenu->setMenu(mnuSendType);
158  if (eventTypesCount <= 1)
159    myEventTypeMenu->setEnabled(false);
160
161  mySendServerCheck = myToolBar->addAction(tr("Send through server"));
162  mySendServerCheck->setShortcut(Qt::ALT + Qt::Key_N);
163  pushToolTip(mySendServerCheck, tr("Send through server"));
164  mySendServerCheck->setCheckable(true);
165
166  bool canSendDirect = (mySendFuncs & PP_SEND_DIRECT);
167
168  const ICQUser* u = gUserManager.FetchUser(myUsers.front().c_str(), myPpid, LOCK_R);
169
170  if (u != NULL)
171  {
172    mySendServerCheck->setChecked(u->SendServer() ||
173        (u->StatusOffline() && u->SocketDesc(ICQ_CHNxNONE) == -1));
174
175    if (u->GetInGroup(GROUPS_SYSTEM, GROUP_INVISIBLE_LIST) ||
176        (u->Port() == 0 && u->SocketDesc(ICQ_CHNxNONE) == -1))
177      canSendDirect = false;
178
179    gUserManager.DropUser(u);
180  }
181  if (!canSendDirect)
182  {
183    mySendServerCheck->setChecked(true);
184    mySendServerCheck->setEnabled(false);
185  }
186
187  myUrgentCheck = myToolBar->addAction(tr("Urgent"));
188  myUrgentCheck->setShortcut(Qt::ALT + Qt::Key_R);
189  pushToolTip(myUrgentCheck, tr("Urgent"));
190  myUrgentCheck->setCheckable(true);
191
192  myMassMessageCheck = myToolBar->addAction(tr("Multiple Recipients"));
193  myMassMessageCheck->setShortcut(Qt::ALT + Qt::Key_U);
194  pushToolTip(myMassMessageCheck, tr("Multiple recipients"));
195  myMassMessageCheck->setCheckable(true);
196  connect(myMassMessageCheck, SIGNAL(toggled(bool)), SLOT(massMessageToggled(bool)));
197
198  myToolBar->addSeparator();
199
200  myEmoticon = myToolBar->addAction(tr("Smileys"), this, SLOT(showEmoticonsMenu()));
201  myEmoticon->setShortcut(Qt::ALT + Qt::Key_L);
202  pushToolTip(myEmoticon, tr("Insert smileys"));
203
204  myForeColor = myToolBar->addAction(tr("Text Color..."), this, SLOT(setForegroundICQColor()));
205  myForeColor->setShortcut(Qt::ALT + Qt::Key_T);
206  pushToolTip(myForeColor, tr("Change text color"));
207
208  myBackColor = myToolBar->addAction(tr("Background Color..."), this, SLOT(setBackgroundICQColor()));
209  myBackColor->setShortcut(Qt::ALT + Qt::Key_B);
210  pushToolTip(myBackColor, tr("Change background color"));
211
212  QDialogButtonBox* buttons = new QDialogButtonBox();
213  myTopLayout->addWidget(buttons);
214
215  mySendButton = buttons->addButton(tr("&Send"), QDialogButtonBox::ActionRole);
216  mySendButton->setDefault(true);
217  // add a wrapper around the send button that
218  // tries to establish a secure connection first.
219  connect(mySendButton, SIGNAL(clicked()), SLOT(sendTrySecure()));
220
221  myCloseButton = buttons->addButton(QDialogButtonBox::Close);
222  myCloseButton->setAutoDefault(true);
223  connect(myCloseButton, SIGNAL(clicked()), SLOT(closeDialog()));
224
225  buttons->setVisible(Config::Chat::instance()->showDlgButtons());
226
227  myViewSplitter = new QSplitter(Qt::Vertical);
228  myTopLayout->addWidget(myViewSplitter);
229
230  myHistoryView = 0;
231  if (Config::Chat::instance()->msgChatView())
232  {
233    myHistoryView = new HistoryView(false, myUsers.front().c_str(), myPpid, myViewSplitter);
234    connect(myHistoryView, SIGNAL(messageAdded()), SLOT(messageAdded()));
235
236    u = gUserManager.FetchUser(myUsers.front().c_str(), myPpid, LOCK_R);
237    if (u != NULL && Config::Chat::instance()->showHistory())
238    {
239      // Show the last SHOW_RECENT_NUM messages in the history
240      HistoryList lHistoryList;
241      if (u->GetHistory(lHistoryList))
242      {
243        // Rewind to the starting point. This will be the first message shown in the dialog.
244        // Make sure we don't show the new messages waiting.
245        unsigned short nNewMessages = u->NewMessages();
246        HistoryListIter lHistoryIter = lHistoryList.end();
247        for (size_t i = 0; i < (SHOW_RECENT_NUM + nNewMessages) && lHistoryIter != lHistoryList.begin(); i++)
248          lHistoryIter--;
249
250        bool bUseHTML = !isdigit((myUsers.front().c_str())[1]);
251        QTextCodec* myCodec = UserCodec::codecForICQUser(u);
252        QString contactName = QString::fromUtf8(u->GetAlias());
253        const ICQOwner* o = gUserManager.FetchOwner(myPpid, LOCK_R);
254        QString ownerName;
255        if (o)
256        {
257          ownerName = QString::fromUtf8(o->GetAlias());
258          gUserManager.DropOwner(o);
259        }
260        else
261          ownerName = QString(tr("Error! no owner set"));
262
263        // Iterate through each message to add
264        // Only show old messages as recent ones. Don't show duplicates.
265        int nMaxNumToShow;
266        if (lHistoryList.size() <= SHOW_RECENT_NUM)
267          nMaxNumToShow = lHistoryList.size() - nNewMessages;
268        else
269          nMaxNumToShow = SHOW_RECENT_NUM;
270
271        // Safety net
272        if (nMaxNumToShow < 0)
273          nMaxNumToShow = 0;
274
275        QDateTime date;
276
277        for (int i = 0; i < nMaxNumToShow && lHistoryIter != lHistoryList.end(); i++)
278        {
279          QString str;
280          date.setTime_t((*lHistoryIter)->Time());
281          QString messageText;
282          if ((*lHistoryIter)->SubCommand() == ICQ_CMDxSUB_SMS) // SMSs are always in UTF-8
283            messageText = QString::fromUtf8((*lHistoryIter)->Text());
284          else
285            messageText = myCodec->toUnicode((*lHistoryIter)->Text());
286
287          myHistoryView->addMsg(
288              (*lHistoryIter)->Direction(),
289              true,
290              (*lHistoryIter)->SubCommand() == ICQ_CMDxSUB_MSG ? "" : EventDescription(*lHistoryIter) + " ",
291              date,
292              (*lHistoryIter)->IsDirect(),
293              (*lHistoryIter)->IsMultiRec(),
294              (*lHistoryIter)->IsUrgent(),
295              (*lHistoryIter)->IsEncrypted(),
296              (*lHistoryIter)->Direction() == D_RECEIVER ? contactName : ownerName,
297              MLView::toRichText(messageText, true, bUseHTML));
298          lHistoryIter++;
299        }
300
301        myHistoryView->GotoEnd();
302
303        ICQUser::ClearHistory(lHistoryList);
304      }
305    }
306
307    // Collect all messages to put them in the correct time order
308    vector<messagePair> messages;
309
310    // add all unread messages.
311    if (u != 0 && u->NewMessages() > 0)
312    {
313      for (unsigned short i = 0; i < u->NewMessages(); i++)
314      {
315        const CUserEvent* e = u->EventPeek(i);
316        // Get the convo id now
317        unsigned long convoId = e->ConvoId();
318        if (myConvoId == 0)
319          myConvoId = convoId;
320
321        if (convoId == myConvoId)
322        {
323          if (e->Id() > myHighestEventId)
324            myHighestEventId = e->Id();
325
326          messages.push_back(make_pair(e, strdup(u->IdString())));
327        }
328      }
329      gUserManager.DropUser(u);
330
331      // Now add messages that are a part of this convo
332      if (myPpid != LICQ_PPID)
333      {
334        FOR_EACH_PROTO_USER_START(myPpid, LOCK_R)
335        {
336          if (pUser->NewMessages() && strcmp(myUsers.front().c_str(), pUser->IdString()) != 0)
337          {
338            for (unsigned short i = 0; i < pUser->NewMessages(); i++)
339            {
340              CUserEvent* e = pUser->EventPeek(i);
341
342              if (e->ConvoId() == myConvoId)
343              {
344                if (e->Id() > myHighestEventId)
345                  myHighestEventId = e->Id();
346
347                // add to the convo list (but what if they left by the time we open this?)
348                char* realId;
349                ICQUser::MakeRealId(pUser->IdString(), pUser->PPID(), realId);
350                myUsers.push_back(realId);
351                delete [] realId;
352                messages.push_back(make_pair(e, strdup(pUser->IdString())));
353              }
354            }
355          }
356        }
357        FOR_EACH_PROTO_USER_END
358      }
359
360      // Sort the messages by time
361      stable_sort(messages.begin(), messages.end(), orderMessagePairs);
362
363      // Now, finally add them
364      vector<messagePair>::iterator messageIter;
365      for (messageIter = messages.begin(); messageIter != messages.end(); messageIter++)
366      {
367        myHistoryView->addMsg((*messageIter).first, (*messageIter).second, myPpid);
368        free((*messageIter).second);
369      }
370      messages.clear();
371
372      // If the user closed the chat window, we have to make sure we aren't
373      // using the old nConvoId
374      if (gLicqDaemon->FindConversation(myConvoId) == 0)
375        myConvoId = 0;
376
377      // Fetch the user again since we dropped it above
378      u = gUserManager.FetchUser(myUsers.front().c_str(), myPpid, LOCK_R);
379    }
380
381    // Do we already have an open socket?
382    if (myConvoId == 0 && u != 0)
383    {
384//       bool bHasOpen = false;
385//       QPtrListIterator<UserSendCommon> it(licqUserSend);
386//       for (; it.current(); ++it)
387//       {
388//         if ((*it)->Id() == myId && (*it)->PPID() == myPpid)
389//         {
390//           bHasOpen = true;
391//           break;
392//         }
393//       }
394
395      if (u->SocketDesc(ICQ_CHNxNONE) != 1)
396      {
397        CConversation* pConv = gLicqDaemon->FindConversation(u->SocketDesc(ICQ_CHNxNONE));
398        if (pConv != 0)
399          myConvoId = pConv->CID();
400      }
401    }
402
403    gUserManager.DropUser(u);
404
405    connect(LicqGui::instance(), SIGNAL(eventSent(const ICQEvent*)),
406        myHistoryView, SLOT(addMsg(const ICQEvent*)));
407    //myViewSplitter->setResizeMode(myHistoryView, QSplitter::FollowSizeHint);
408  }
409
410  {
411    mySendTypingTimer = new QTimer(this);
412    connect(mySendTypingTimer, SIGNAL(timeout()), SLOT(textChangedTimeout()));
413  }
414
415  myPictureSplitter = new QSplitter(myViewSplitter);
416
417  myMessageEdit = new MLEdit(true, myPictureSplitter);
418  myMessageEdit->setSizeHintLines(3);
419  myMessageEdit->setCheckSpellingEnabled(Config::Chat::instance()->checkSpelling());
420  myMessageEdit->installEventFilter(this); // Enables send with enter
421
422  if (Config::Chat::instance()->msgChatView())
423  {
424    myViewSplitter->setStretchFactor(myViewSplitter->indexOf(myHistoryView), 1);
425    myViewSplitter->setStretchFactor(myViewSplitter->indexOf(myPictureSplitter), 0);
426
427    connect(myHistoryView, SIGNAL(quote(const QString&)),
428        myMessageEdit, SLOT(insertPlainText(const QString&)));
429  }
430
431  setFocusProxy(myMessageEdit);
432  if (Config::Chat::instance()->showDlgButtons())
433  {
434    setTabOrder(myMessageEdit, mySendButton);
435    setTabOrder(mySendButton, myCloseButton);
436  }
437
438  myIcqColor.SetToDefault();
439  myMessageEdit->setBackground(QColor(myIcqColor.BackRed(), myIcqColor.BackGreen(), myIcqColor.BackBlue()));
440  myMessageEdit->setForeground(QColor(myIcqColor.ForeRed(), myIcqColor.ForeGreen(), myIcqColor.ForeBlue()));
441
442  updateIcons();
443  updatePicture();
444
445  connect(myMessageEdit, SIGNAL(ctrlEnterPressed()), mySendButton, SIGNAL(clicked()));
446  connect(myMessageEdit, SIGNAL(textChanged()), SLOT(messageTextChanged()));
447  connect(mySendServerCheck, SIGNAL(triggered(bool)), SLOT(sendServerToggled(bool)));
448}
449
450UserSendCommon::~UserSendCommon()
451{
452  // Empty
453}
454
455bool UserSendCommon::eventFilter(QObject* watched, QEvent* e)
456{
457  if (watched == myMessageEdit)
458  {
459    // If we're in single line chat mode we send messages with Enter and
460    // insert new lines with Ctrl+Enter.
461    if (Config::Chat::instance()->singleLineChatMode() && e->type() == QEvent::KeyPress)
462    {
463      QKeyEvent* key = dynamic_cast<QKeyEvent*>(e);
464      const bool isEnter = (key->key() == Qt::Key_Enter || key->key() == Qt::Key_Return);
465      if (isEnter)
466      {
467        if (key->modifiers() & Qt::ControlModifier)
468          myMessageEdit->insertPlainText("\n");
469        else
470          mySendButton->animateClick();
471        return true; // filter the event out
472      }
473    }
474    return false;
475  }
476  else
477    return UserEventCommon::eventFilter(watched, e);
478}
479
480void UserSendCommon::updateIcons()
481{
482  UserEventCommon::updateIcons();
483
484  IconManager* iconman = IconManager::instance();
485
486  // Toolbar buttons
487  myEventTypeMenu->setIcon(iconForType(myType));
488  mySendServerCheck->setIcon(iconman->getIcon(IconManager::ThroughServerIcon));
489  myUrgentCheck->setIcon(iconman->getIcon(IconManager::UrgentIcon));
490  myMassMessageCheck->setIcon(iconman->getIcon(IconManager::MultipleRecIcon));
491  myEmoticon->setIcon(iconman->getIcon(IconManager::SmileIcon));
492  myForeColor->setIcon(iconman->getIcon(IconManager::TextColorIcon));
493  myBackColor->setIcon(iconman->getIcon(IconManager::BackColorIcon));
494
495  // Update message type icons in menu
496  foreach (QAction* a, myEventTypeGroup->actions())
497    a->setIcon(iconForType(a->data().toInt()));
498}
499
500void UserSendCommon::updatePicture(const ICQUser* u)
501{
502  bool fetched = false;
503
504  if (u == NULL)
505  {
506    u = gUserManager.FetchUser(myUsers.front().c_str(), myPpid, LOCK_R);
507    fetched = true;
508  }
509  if (u == NULL)
510    return;
511
512  if (myPictureLabel != NULL)
513  {
514    delete myPictureLabel;
515    myPictureLabel = NULL;
516  }
517
518  if (Config::Chat::instance()->showUserPic() &&
519      u->GetPicturePresent())
520  {
521    QString picPath = QString(BASE_DIR) + USER_DIR + "/" + u->IdString() + ".pic";
522    QMovie* picMovie = new QMovie(picPath);
523    if (picMovie->isValid())
524    {
525      myPictureLabel = new QLabel();
526      myPictureSplitter->insertWidget(1, myPictureLabel);
527      myPictureLabel->setMovie(picMovie);
528      picMovie->start();
529      myPictureLabel->setFixedWidth(myPictureLabel->sizeHint().width());
530      if (Config::Chat::instance()->showUserPicHidden())
531        myPictureSplitter->setSizes(QList<int>() << 1 << 0);
532    }
533    else
534      delete picMovie;
535  }
536
537  if (fetched)
538    gUserManager.DropUser(u);
539}
540
541const QPixmap& UserSendCommon::iconForType(int type) const
542{
543  switch (type)
544  {
545    case UrlEvent:
546      return IconManager::instance()->getIcon(IconManager::UrlMessageIcon);
547
548    case ChatEvent:
549      return IconManager::instance()->getIcon(IconManager::ChatMessageIcon);
550
551    case FileEvent:
552      return IconManager::instance()->getIcon(IconManager::FileMessageIcon);
553
554    case ContactEvent:
555      return IconManager::instance()->getIcon(IconManager::ContactMessageIcon);
556
557    case SmsEvent:
558      return IconManager::instance()->getIcon(IconManager::SmsMessageIcon);
559
560    case MessageEvent:
561    default:
562      return IconManager::instance()->getIcon(IconManager::StandardMessageIcon);
563  }
564}
565
566void UserSendCommon::setText(const QString& text)
567{
568  if (myMessageEdit == 0)
569    return;
570
571  myMessageEdit->setText(text);
572  myMessageEdit->GotoEnd();
573  myMessageEdit->document()->setModified(false);
574}
575
576void UserSendCommon::convoJoin(QString id, unsigned long convoId)
577{
578  if (id.isEmpty())
579    return;
580
581  if (Config::Chat::instance()->msgChatView())
582  {
583    const ICQUser* u = gUserManager.FetchUser(id.toLatin1(), myPpid, LOCK_R);
584    QString userName;
585    if (u != 0)
586    {
587      userName = QString::fromUtf8(u->GetAlias());
588      gUserManager.DropUser(u);
589    }
590    else
591      userName = id;
592
593    myHistoryView->addNotice(QDateTime::currentDateTime(),
594        tr("%1 has joined the conversation.").arg(userName));
595  }
596
597  if (!isUserInConvo(id))
598  {
599    char* realId;
600    ICQUser::MakeRealId(id.toLatin1(), myPpid, realId);
601    myUsers.push_back(realId);
602    delete [] realId;
603  }
604
605  myConvoId = convoId;
606
607  // Now update the tab label
608  UserEventTabDlg* tabDlg = LicqGui::instance()->userEventTabDlg();
609  if (tabDlg != NULL)
610    tabDlg->updateConvoLabel(this);
611}
612
613void UserSendCommon::convoLeave(QString id, unsigned long /* convoId */)
614{
615  if (id.isEmpty())
616    return;
617
618  if (Config::Chat::instance()->msgChatView())
619  {
620    ICQUser* u = gUserManager.FetchUser(id.toLatin1(), myPpid, LOCK_W);
621    QString userName;
622    if (u != 0)
623      userName = QString::fromUtf8(u->GetAlias());
624    else
625      userName = id;
626
627    myHistoryView->addNotice(QDateTime::currentDateTime(),
628        tr("%1 has left the conversation.").arg(userName));
629
630    // Remove the typing notification if active
631    if (u != 0)
632    {
633      if (u->GetTyping() == ICQ_TYPING_ACTIVE)
634      {
635        u->SetTyping(ICQ_TYPING_INACTIVEx0);
636        myTimezone->setPalette(QPalette());
637        UserEventTabDlg* tabDlg = LicqGui::instance()->userEventTabDlg();
638        if (Config::Chat::instance()->tabbedChatting() && tabDlg != NULL)
639          tabDlg->updateTabLabel(u);
640      }
641      gUserManager.DropUser(u);
642    }
643  }
644
645  if (myUsers.size() > 1)
646  {
647    list<string>::iterator it;
648    for (it = myUsers.begin(); it != myUsers.end(); it++)
649    {
650      if (id.compare(it->c_str(), Qt::CaseInsensitive) == 0)
651      {
652        myUsers.remove(*it);
653        break;
654      }
655    }
656    myHistoryView->setOwner(myUsers.front().c_str());
657  }
658  else
659    myConvoId = 0;
660
661  if (Config::Chat::instance()->msgChatView())
662  {
663    // Now update the tab label
664    UserEventTabDlg* tabDlg = LicqGui::instance()->userEventTabDlg();
665    if (tabDlg != NULL)
666      tabDlg->updateConvoLabel(this);
667  }
668}
669
670void UserSendCommon::windowActivationChange(bool oldActive)
671{
672  if (isActiveWindow())
673    QTimer::singleShot(clearDelay, this, SLOT(clearNewEvents()));
674  QWidget::windowActivationChange(oldActive);
675}
676
677void UserSendCommon::changeEventType(int type)
678{
679  if (myType == type)
680    return;
681
682  UserSendCommon* e = 0;
683  QWidget* parent = 0;
684
685  UserEventTabDlg* tabDlg = LicqGui::instance()->userEventTabDlg();
686  if (tabDlg != NULL && tabDlg->tabExists(this))
687    parent = tabDlg;
688
689  switch (type)
690  {
691    case MessageEvent:
692      if (mySendFuncs & PP_SEND_MSG)
693        e = new UserSendMsgEvent(myUsers.front().c_str(), myPpid, parent);
694      break;
695    case UrlEvent:
696      if (mySendFuncs & PP_SEND_URL)
697        e = new UserSendUrlEvent(myUsers.front().c_str(), myPpid, parent);
698      break;
699    case ChatEvent:
700      if (mySendFuncs & PP_SEND_CHAT)
701        e