1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2000-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 "userviewevent.h"
21 
22 #include "config.h"
23 
24 #include <QAction>
25 #include <QApplication>
26 #include <QCheckBox>
27 #include <QDesktopWidget>
28 #include <QFile>
29 #include <QGroupBox>
30 #include <QPushButton>
31 #include <QShortcut>
32 #include <QSplitter>
33 #include <QTreeWidgetItem>
34 #include <QVBoxLayout>
35 
36 #ifdef USE_KDE
37 #include <KDE/KToolInvocation>
38 #endif
39 
40 #include <licq/contactlist/usermanager.h>
41 #include <licq/daemon.h>
42 #include <licq/event.h>
43 #include <licq/icq/icq.h>
44 #include <licq/plugin/pluginmanager.h>
45 #include <licq/pluginsignal.h>
46 #include <licq/protocolmanager.h>
47 #include <licq/userevents.h>
48 
49 #include "config/chat.h"
50 #include "config/iconmanager.h"
51 
52 #include "core/gui-defines.h"
53 #include "core/licqgui.h"
54 #include "core/messagebox.h"
55 #include "core/signalmanager.h"
56 
57 #include "dialogs/adduserdlg.h"
58 #include "dialogs/authdlg.h"
59 #include "dialogs/chatdlg.h"
60 #include "dialogs/filedlg.h"
61 #include "dialogs/forwarddlg.h"
62 #include "dialogs/joinchatdlg.h"
63 #include "dialogs/refusedlg.h"
64 #include "userdlg/userdlg.h"
65 
66 #include "widgets/messagelist.h"
67 #include "widgets/mlview.h"
68 #include "widgets/skinnablebutton.h"
69 
70 #include "usersendevent.h"
71 
72 using Licq::gProtocolManager;
73 using namespace LicqQtGui;
74 /* TRANSLATOR LicqQtGui::UserViewEvent */
75 
UserViewEvent(const Licq::UserId & userId,QWidget * parent)76 UserViewEvent::UserViewEvent(const Licq::UserId& userId, QWidget* parent)
77   : UserEventCommon(userId, parent, "UserViewEvent")
78 {
79   myReadSplitter = new QSplitter(Qt::Vertical);
80   myReadSplitter->setOpaqueResize();
81   myMainWidget->addWidget(myReadSplitter);
82 
83   QShortcut* shortcutEscape = new QShortcut(Qt::Key_Escape, this);
84 
85   myMessageList = new MessageList();
86   myReadSplitter->addWidget(myMessageList);
87 
88   myMessageView = new MLView();
89   myMessageView->setSizeHintLines(8);
90   myReadSplitter->addWidget(myMessageView);
91 
92   myReadSplitter->setStretchFactor(0, 0);
93   myReadSplitter->setStretchFactor(1, 1);
94 
95   myActionsBox = new QGroupBox();
96   myMainWidget->addSpacing(10);
97   myMainWidget->addWidget(myActionsBox);
98 
99   QHBoxLayout* h_action_lay = new QHBoxLayout(myActionsBox);
100 
101   myRead1Button = new QPushButton();
102   myRead2Button = new QPushButton();
103   myRead3Button = new QPushButton();
104   myRead4Button = new QPushButton();
105 
106   myRead1Button->setEnabled(false);
107   myRead2Button->setEnabled(false);
108   myRead3Button->setEnabled(false);
109   myRead4Button->setEnabled(false);
110 
111   h_action_lay->addWidget(myRead1Button);
112   h_action_lay->addWidget(myRead2Button);
113   h_action_lay->addWidget(myRead3Button);
114   h_action_lay->addWidget(myRead4Button);
115 
116   QHBoxLayout* h_lay = new QHBoxLayout();
117   myTopLayout->addLayout(h_lay);
118 
119   if (!myIsOwner)
120   {
121     myAutoCloseCheck = new QCheckBox(tr("Aut&o Close"));
122     myAutoCloseCheck->setChecked(Config::Chat::instance()->autoClose());
123     h_lay->addWidget(myAutoCloseCheck);
124   }
125 
126   h_lay->addStretch(1);
127 
128   myReadNextButton = new QPushButton(tr("Nex&t"));
129   myReadNextButton->setEnabled(false);
130   h_lay->addWidget(myReadNextButton);
131   setTabOrder(myRead4Button, myReadNextButton);
132 
133   myCloseButton = new SkinnableButton(tr("&Close"));
134   myCloseButton->setToolTip(tr("Normal Click - Close Window\n<CTRL>+Click - also delete User"));
135   h_lay->addWidget(myCloseButton);
136   setTabOrder(myReadNextButton, myCloseButton);
137 
138   Licq::UserReadGuard u(myUsers.front());
139   if (u.isLocked() && u->NewMessages() > 0)
140   {
141     unsigned short i = 0;
142     // Create an item for the message we're currently viewing.
143     if (Config::Chat::instance()->msgChatView())
144     {
145       for (i = 0; i < u->NewMessages(); i++)
146         if (u->EventPeek(i)->eventType() != Licq::UserEvent::TypeMessage &&
147             u->EventPeek(i)->eventType() != Licq::UserEvent::TypeUrl)
148           break;
149       if (i == u->NewMessages())
150         i = 0;
151     }
152 
153     MessageListItem* e = new MessageListItem(u->EventPeek(i), myMessageList);
154     myHighestEventId = u->EventPeek(i)->Id();
155 
156     /* Create items for all the messages which already await
157      * in the queue. We cannot rely on getting CICQSignals for them
158      * since they might've arrived before the dialog appeared,
159      * possibly being undisplayed messages from previous licq session.
160      */
161     for (i++; i < u->NewMessages(); i++)
162     {
163       const Licq::UserEvent* event = u->EventPeek(i);
164       if (!Config::Chat::instance()->msgChatView() ||
165           (event->eventType() != Licq::UserEvent::TypeMessage &&
166           event->eventType() != Licq::UserEvent::TypeUrl))
167       {
168         new MessageListItem(event, myMessageList);
169         // Make sure we don't add this message again,
170         // even if we receive an userUpdated signal for it.
171         if (myHighestEventId < event->Id())
172           myHighestEventId = event->Id();
173       }
174     }
175 
176     u.unlock();
177     for (int i = 0; i < myMessageList->columnCount(); i++)
178       myMessageList->resizeColumnToContents(i);
179     myMessageList->setCurrentItem(e, 0);
180     myMessageList->scrollToItem(e);
181     printMessage(e);
182   }
183   u.unlock();
184 
185   QSize dialogSize = Config::Chat::instance()->viewDialogSize();
186   if (dialogSize.isValid())
187     resize(dialogSize);
188 
189   connect(gLicqGui, SIGNAL(eventSent(const Licq::Event*)),
190       SLOT(sentEvent(const Licq::Event*)));
191   connect(myMessageList, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
192       SLOT(printMessage(QTreeWidgetItem*)));
193   connect(myRead1Button, SIGNAL(clicked()), SLOT(read1()));
194   connect(myRead2Button, SIGNAL(clicked()), SLOT(read2()));
195   connect(myRead3Button, SIGNAL(clicked()), SLOT(read3()));
196   connect(myRead4Button, SIGNAL(clicked()), SLOT(read4()));
197   connect(myReadNextButton, SIGNAL(clicked()), SLOT(readNext()));
198   connect(myCloseButton, SIGNAL(clicked()), SLOT(closeDialog()));
199   connect(shortcutEscape, SIGNAL(activated()), SLOT(closeDialog()));
200   connect(this, SIGNAL(encodingChanged()), SLOT(setEncoding()));
201 }
202 
~UserViewEvent()203 UserViewEvent::~UserViewEvent()
204 {
205   // Empty
206 }
207 
generateReply()208 void UserViewEvent::generateReply()
209 {
210   QString s = QString("> ");
211 
212   if (!myMessageView->markedText().trimmed().isEmpty())
213     s += myMessageView->markedText().trimmed();
214   else
215     if (!myMessageView->toPlainText().trimmed().isEmpty())
216       s += myMessageView->toPlainText().trimmed();
217     else
218       s = QString::null;
219 
220   s.replace("\n", "\n> ");
221   s = s.trimmed();
222   if (!s.isEmpty())
223     s += "\n\n";
224 
225   sendMsg(s);
226 }
227 
sendMsg(QString text)228 void UserViewEvent::sendMsg(QString text)
229 {
230   UserSendEvent* e = new UserSendEvent(MessageEvent, myUsers.front());
231 
232   e->setText(text);
233 
234   // Find a good position for the new window
235   if (Config::Chat::instance()->autoPosReplyWin())
236   {
237     int yp = myRead1Button->parentWidget()->mapToGlobal(QPoint(0, 0)).y();
238     if (yp + e->height() + 8 > QApplication::desktop()->height())
239       yp = QApplication::desktop()->height() - e->height() - 8;
240     e->move(x(), yp);
241   }
242 
243   QTimer::singleShot(10, e, SLOT(show()));
244 
245   connect(e, SIGNAL(autoCloseNotify()), SLOT(autoClose()));
246 }
247 
updateNextButton()248 void UserViewEvent::updateNextButton()
249 {
250   int num = myMessageList->getNumUnread();
251   MessageListItem* e = myMessageList->getNextUnread();
252 
253   myReadNextButton->setEnabled(num > 0);
254 
255   if (num > 1)
256     myReadNextButton->setText(tr("Nex&t (%1)").arg(num));
257   else if (num == 1)
258     myReadNextButton->setText(tr("Nex&t"));
259 
260   if (e != NULL && e->msg() != NULL)
261     myReadNextButton->setIcon(IconManager::instance()->iconForEvent(e->msg()->eventType()));
262   else
263     myReadNextButton->setIcon(QIcon());
264 }
265 
userUpdated(const Licq::UserId & userId,unsigned long subSignal,int argument,unsigned long)266 void UserViewEvent::userUpdated(const Licq::UserId& userId, unsigned long subSignal, int argument, unsigned long /* cid */)
267 {
268   Licq::UserReadGuard u(userId);
269 
270   if (!u.isLocked())
271     return;
272 
273   if (subSignal == Licq::PluginSignal::UserEvents)
274   {
275     if (argument > 0)
276     {
277       int eventId = argument;
278       const Licq::UserEvent* e = u->EventPeekId(eventId);
279       // Making sure we didn't handle this message already.
280       if (e != NULL && myHighestEventId < eventId &&
281           (!Config::Chat::instance()->msgChatView() ||
282           (e->eventType() != Licq::UserEvent::TypeMessage &&
283           e->eventType() != Licq::UserEvent::TypeUrl)))
284       {
285          myHighestEventId = eventId;
286          MessageListItem* m = new MessageListItem(e, myMessageList);
287          myMessageList->scrollToItem(m);
288       }
289     }
290 
291     if (argument != 0)
292       updateNextButton();
293   }
294 }
295 
autoClose()296 void UserViewEvent::autoClose()
297 {
298   if (!myAutoCloseCheck->isChecked())
299     return;
300 
301   bool doclose = false;
302 
303   {
304     Licq::UserReadGuard u(myUsers.front());
305     if (u.isLocked())
306       doclose = (u->NewMessages() == 0);
307   }
308 
309   if (doclose)
310     closeDialog();
311 }
312 
read1()313 void UserViewEvent::read1()
314 {
315   if (myCurrentEvent == 0)
316     return;
317 
318   switch (myCurrentEvent->eventType())
319   {
320     case Licq::UserEvent::TypeMessage:  // reply/quote
321     case Licq::UserEvent::TypeUrl:
322     case Licq::UserEvent::TypeChat:
323     case Licq::UserEvent::TypeFile:
324       sendMsg("");
325       break;
326 
327     case Licq::UserEvent::TypeAuthRequest:
328     {
329       Licq::EventAuthRequest* p = dynamic_cast<Licq::EventAuthRequest*>(myCurrentEvent);
330       new AuthDlg(AuthDlg::GrantAuth, p->userId());
331       break;
332     }
333 
334     case Licq::UserEvent::TypeAuthGranted:
335     {
336       Licq::EventAuthGranted* p = dynamic_cast<Licq::EventAuthGranted*>(myCurrentEvent);
337       new AddUserDlg(p->userId(), this);
338       break;
339     }
340 
341     case Licq::UserEvent::TypeAdded:
342     {
343       Licq::EventAdded* p = dynamic_cast<Licq::EventAdded*>(myCurrentEvent);
344       new AddUserDlg(p->userId(), this);
345       break;
346     }
347 
348     case Licq::UserEvent::TypeContactList:
349     {
350       const Licq::EventContactList::ContactList& cl = dynamic_cast<Licq::EventContactList*>(myCurrentEvent)->Contacts();
351       Licq::EventContactList::ContactList::const_iterator it;
352 
353       for (it = cl.begin(); it != cl.end(); ++it)
354       {
355         new AddUserDlg((*it)->userId(), this);
356       }
357 
358       myRead1Button->setEnabled(false);
359       break;
360     }
361 
362     case Licq::UserEvent::TypeEmailAlert:
363     {
364       // FIXME: For now assume MSN protocol, will need to be fixed soon.
365       Licq::EventEmailAlert* p = dynamic_cast<Licq::EventEmailAlert*>(myCurrentEvent);
366 
367       // Create the HTML
368       QString url = Licq::gDaemon.baseDir().c_str();
369       url += ".msn_email.html";
370 
371       QString strUser = QString::fromUtf8(p->to().c_str());
372       QString strHTML = QString(
373           "<html><head><noscript><meta http-equiv=Refresh content=\"0; url=http://www.hotmail.com\">"
374           "</noscript></head><body onload=\"document.pform.submit(); \"><form name=\"pform\" action=\""
375           "%1\" method=\"POST\"><input type=\"hidden\" name=\"mode\" value=\"ttl\">"
376           "<input type=\"hidden\" name=\"login\" value=\"%2\"><input type=\"hidden\" name=\"username\""
377           "value=\"%3\"><input type=\"hidden\" name=\"sid\" value=\"%4\"><input type=\"hidden\" name=\"kv\" value=\""
378           "%5\"><input type=\"hidden\" name=\"id\" value=\"%6\"><input type=\"hidden\" name=\"sl\" value=\"9\"><input "
379           "type=\"hidden\" name=\"rru\" value=\"%7\"><input type=\"hidden\" name=\"auth\" value=\"%8\""
380           "><input type=\"hidden\" name=\"creds\" value=\"%9\"><input type=\"hidden\" name=\"svc\" value=\"mail\">"
381           "<input type=\"hidden\" name=\"js\"value=\"yes\"></form></body></html>")
382         .arg(QString::fromUtf8(p->postUrl().c_str()))
383         .arg(strUser.left(strUser.indexOf("@")))
384         .arg(strUser)
385         .arg(QString::fromUtf8(p->sid().c_str()))
386         .arg(QString::fromUtf8(p->kv().c_str()))
387         .arg(QString::fromUtf8(p->id().c_str()))
388         .arg(QString::fromUtf8(p->msgUrl().c_str()))
389         .arg(QString::fromUtf8(p->mspAuth().c_str()))
390         .arg(QString::fromUtf8(p->creds().c_str()));
391 
392       QFile fileHTML(url);
393       fileHTML.open(QIODevice::WriteOnly);
394       fileHTML.write(strHTML.toLatin1(), strHTML.length());
395       fileHTML.close();
396 
397       // Now we have to add the file:// after it was created,
398       // but before it is executed.
399       url.prepend("file://");
400 
401       gLicqGui->viewUrl(url);
402 
403       break;
404     }
405   } // switch
406 }
407 
read2()408 void UserViewEvent::read2()
409 {
410   if (myCurrentEvent == NULL)
411     return;
412 
413   const Licq::UserId& frontUserId(myUsers.front());
414 
415   switch (myCurrentEvent->eventType())
416   {
417     case Licq::UserEvent::TypeMessage:  // quote
418     case Licq::UserEvent::TypeUrl:
419       generateReply();
420       break;
421 
422     case Licq::UserEvent::TypeChat:  // accept a chat request
423     {
424       Licq::IcqProtocol::Ptr icq = plugin_internal_cast<Licq::IcqProtocol>(
425           Licq::gPluginManager.getProtocolInstance(frontUserId.ownerId()));
426       if (!icq)
427         return;
428 
429       myCurrentEvent->SetPending(false);
430       myRead2Button->setEnabled(false);
431       myRead3Button->setEnabled(false);
432       Licq::EventChat* c = dynamic_cast<Licq::EventChat*>(myCurrentEvent);
433       ChatDlg* chatDlg = new ChatDlg(frontUserId);
434       if (c->Port() != 0)  // Joining a multiparty chat (we connect to them)
435       {
436         // FIXME: must have been done in CICQDaemon
437         if (chatDlg->StartAsClient(c->Port()))
438           icq->icqChatRequestAccept(frontUserId,
439               0, c->clients(), c->Sequence(),
440               c->MessageID()[0], c->MessageID()[1], c->IsDirect());
441       }
442       else  // single party (other side connects to us)
443       {
444         // FIXME: must have been done in CICQDaemon
445         if (chatDlg->StartAsServer())
446           icq->icqChatRequestAccept(frontUserId,
447               chatDlg->LocalPort(), c->clients(), c->Sequence(),
448               c->MessageID()[0], c->MessageID()[1], c->IsDirect());
449       }
450       break;
451     }
452 
453     case Licq::UserEvent::TypeFile:  // accept a file transfer
454     {
455       myCurrentEvent->SetPending(false);
456       myRead2Button->setEnabled(false);
457       myRead3Button->setEnabled(false);
458       Licq::EventFile* f = dynamic_cast<Licq::EventFile*>(myCurrentEvent);
459       FileDlg* fileDlg = new FileDlg(frontUserId);
460 
461       if (fileDlg->ReceiveFiles())
462         // FIXME: must have been done in CICQDaemon
463         gProtocolManager.fileTransferAccept(frontUserId,
464             fileDlg->LocalPort(), f->Sequence(), f->MessageID()[0], f->MessageID()[1],
465             f->fileDescription(), f->filename(), f->FileSize(), !f->IsDirect());
466       break;
467     }
468 
469     case Licq::UserEvent::TypeAuthRequest:
470     {
471       Licq::EventAuthRequest* p = dynamic_cast<Licq::EventAuthRequest*>(myCurrentEvent);
472       new AuthDlg(AuthDlg::RefuseAuth, p->userId());
473       break;
474     }
475   } // switch
476 }
477 
read3()478 void UserViewEvent::read3()
479 {
480   if (myCurrentEvent == NULL)
481     return;
482 
483   const Licq::UserId& frontUserId(myUsers.front());
484 
485   switch (myCurrentEvent->eventType())
486   {
487     case Licq::UserEvent::TypeMessage:  // Forward
488     case Licq::UserEvent::TypeUrl:
489     {
490       ForwardDlg* f = new ForwardDlg(myCurrentEvent, this);
491       f->show();
492       break;
493     }
494 
495     case Licq::UserEvent::TypeChat:  // refuse a chat request
496     {
497       RefuseDlg* r = new RefuseDlg(frontUserId, tr("Chat"), this);
498 
499       if (r->exec())
500       {
501         Licq::IcqProtocol::Ptr icq = plugin_internal_cast<Licq::IcqProtocol>(
502             Licq::gPluginManager.getProtocolInstance(frontUserId.ownerId()));
503         if (!icq)
504           return;
505 
506         myCurrentEvent->SetPending(false);
507         Licq::EventChat* c = dynamic_cast<Licq::EventChat*>(myCurrentEvent);
508         myRead2Button->setEnabled(false);
509         myRead3Button->setEnabled(false);
510 
511         // FIXME: must have been done in CICQDaemon
512         icq->icqChatRequestRefuse(frontUserId,
513             r->RefuseMessage().toUtf8().data(), myCurrentEvent->Sequence(),
514             c->MessageID()[0], c->MessageID()[1], c->IsDirect());
515       }
516       delete r;
517       break;
518     }
519 
520     case Licq::UserEvent::TypeFile:  // refuse a file transfer
521     {
522       RefuseDlg* r = new RefuseDlg(frontUserId, tr("File Transfer"), this);
523 
524       if (r->exec())
525       {
526         myCurrentEvent->SetPending(false);
527         Licq::EventFile* f = dynamic_cast<Licq::EventFile*>(myCurrentEvent);
528         myRead2Button->setEnabled(false);
529         myRead3Button->setEnabled(false);
530 
531         // FIXME: must have been done in CICQDaemon
532         gProtocolManager.fileTransferRefuse(frontUserId,
533             r->RefuseMessage().toUtf8().data(), myCurrentEvent->Sequence(),
534             f->MessageID()[0], f->MessageID()[1], !f->IsDirect());
535       }
536       delete r;
537       break;
538     }
539 
540     case Licq::UserEvent::TypeAuthRequest:
541     {
542       Licq::EventAuthRequest* p = dynamic_cast<Licq::EventAuthRequest*>(myCurrentEvent);
543       new AddUserDlg(p->userId(), this);
544       break;
545     }
546   } // switch
547 }
548 
read4()549 void UserViewEvent::read4()
550 {
551   if (myCurrentEvent == NULL)
552     return;
553 
554   const Licq::UserId& frontUserId(myUsers.front());
555 
556   switch (myCurrentEvent->eventType())
557   {
558     case Licq::UserEvent::TypeMessage:
559       gLicqGui->showEventDialog(ChatEvent, frontUserId);
560       break;
561 
562     case Licq::UserEvent::TypeChat:  // join to current chat
563     {
564       Licq::IcqProtocol::Ptr icq = plugin_internal_cast<Licq::IcqProtocol>(
565           Licq::gPluginManager.getProtocolInstance(frontUserId.ownerId()));
566       if (!icq)
567         return;
568 
569       Licq::EventChat* c = dynamic_cast<Licq::EventChat*>(myCurrentEvent);
570       if (c->Port() != 0)  // Joining a multiparty chat (we connect to them)
571       {
572         ChatDlg* chatDlg = new ChatDlg(frontUserId);
573         // FIXME: must have been done in CICQDaemon
574         if (chatDlg->StartAsClient(c->Port()))
575           icq->icqChatRequestAccept(frontUserId,
576               0, c->clients(), c->Sequence(), c->MessageID()[0], c->MessageID()[1], c->IsDirect());
577       }
578       else  // single party (other side connects to us)
579       {
580         ChatDlg* chatDlg = NULL;
581         JoinChatDlg* j = new JoinChatDlg(this);
582         // FIXME: must have been done in CICQDaemon
583         if (j->exec() && (chatDlg = j->JoinedChat()) != NULL)
584           icq->icqChatRequestAccept(frontUserId,
585               chatDlg->LocalPort(), c->clients(), c->Sequence(), c->MessageID()[0], c->MessageID()[1], c->IsDirect());
586         delete j;
587       }
588       break;
589     }
590 
591     case Licq::UserEvent::TypeUrl:   // view a url
592       gLicqGui->viewUrl(QString::fromUtf8(dynamic_cast<const Licq::EventUrl*>(myCurrentEvent)->url().c_str()));
593       break;
594 
595     case Licq::UserEvent::TypeAuthRequest:
596     case Licq::UserEvent::TypeAuthGranted:
597     case Licq::UserEvent::TypeAdded:
598     {
599       Licq::UserId userId;
600 #define GETINFO(sub, type) \
601       if (myCurrentEvent->eventType() == sub) \
602       { \
603         type* p = dynamic_cast<type*>(myCurrentEvent); \
604         userId = p->userId(); \
605       }
606 
607       GETINFO(Licq::UserEvent::TypeAuthRequest, Licq::EventAuthRequest);
608       GETINFO(Licq::UserEvent::TypeAuthGranted, Licq::EventAuthGranted);
609       GETINFO(Licq::UserEvent::TypeAdded, Licq::EventAdded);
610 #undef GETINFO
611 
612       {
613         Licq::UserReadGuard u(userId, true);
614       }
615 
616       UserDlg::showDialog(userId, UserDlg::GeneralPage, true);
617       break;
618     }
619   } // switch
620 }
621 
readNext()622 void UserViewEvent::readNext()
623 {
624   MessageListItem* e = myMessageList->getNextUnread();
625 
626   updateNextButton();
627 
628   if (e != NULL)
629   {
630     myMessageList->setCurrentItem(e, 0);
631     myMessageList->scrollToItem(e);
632     printMessage(e);
633   }
634 }
635 
clearEvent()636 void UserViewEvent::clearEvent()
637 {
638   Licq::UserWriteGuard u(myUsers.front());
639 
640   if (!u.isLocked())
641     return;
642 
643   u->EventClearId(myCurrentEvent->Id());
644 }
645 
closeDialog()646 void UserViewEvent::closeDialog()
647 {
648   myDeleteUser = myCloseButton->modifiersWhenPressed() & Qt::ControlModifier;
649   close();
650 }
651 
printMessage(QTreeWidgetItem * item)652 void UserViewEvent::printMessage(QTreeWidgetItem* item)
653 {
654   if (item == NULL)
655     return;
656 
657   MessageListItem* e = dynamic_cast<MessageListItem*>(item);
658 
659   myRead1Button->setText("");
660   myRead2Button->setText("");
661   myRead3Button->setText("");
662   myRead4Button->setText("");
663   myEncoding->setEnabled(true);
664 
665   Licq::UserEvent* m = e->msg();
666   myCurrentEvent = m;
667 
668   // Set the color for the message
669   myMessageView->setBackground(QColor(m->color()->backRed(), m->color()->backGreen(), m->color()->backBlue()));
670   myMessageView->setForeground(QColor(m->color()->foreRed(), m->color()->foreGreen(), m->color()->foreBlue()));
671 
672   // Set the text
673   myMessageText = QString::fromUtf8(m->text().c_str());
674 
675   QString colorAttr;
676   colorAttr.sprintf("#%02x%02x%02x", m->color()->foreRed(), m->color()->foreGreen(), m->color()->foreBlue());
677   myMessageView->setText("<font color=\"" + colorAttr + "\">" + MLView::toRichText(myMessageText, true) + "</font>");
678 
679   myMessageView->GotoHome();
680 
681   if (m->isReceiver())
682   {
683     switch (m->eventType())
684     {
685       case Licq::UserEvent::TypeChat:  // accept or refuse a chat request
686       case Licq::UserEvent::TypeFile:  // accept or refuse a file transfer
687         myRead1Button->setText(tr("&Reply"));
688         if (m->IsCancelled())
689           myMessageView->append(tr("\n--------------------\nRequest was cancelled."));
690         else
691         {
692           if (m->Pending())
693           {
694             myRead2Button->setText(tr("A&ccept"));
695             myRead3Button->setText(tr("&Refuse"));
696           }
697           // If this is a chat, and we already have chats going, and this is
698           // not a join request, then we can join
699           if (m->eventType() == Licq::UserEvent::TypeChat &&
700               ChatDlg::chatDlgs.size() > 0 &&
701               dynamic_cast<Licq::EventChat*>(m)->Port() == 0)
702             myRead4Button->setText(tr("&Join"));
703         }
704         break;
705 
706       case Licq::UserEvent::TypeMessage:
707         myRead1Button->setText(tr("&Reply"));
708         myRead2Button->setText(tr("&Quote"));
709         myRead3Button->setText(tr("&Forward"));
710         myRead4Button->setText(tr("Start Chat"));
711         break;
712 
713       case Licq::UserEvent::TypeSms:
714         myEncoding->setEnabled(false);
715         break;
716 
717       case Licq::UserEvent::TypeUrl:   // view a url
718         myRead1Button->setText(tr("&Reply"));
719         myRead2Button->setText(tr("&Quote"));
720         myRead3Button->setText(tr("&Forward"));
721         myRead4Button->setText(tr("&View"));
722         break;
723 
724       case Licq::UserEvent::TypeAuthRequest:
725       {
726         myRead1Button->setText(tr("A&uthorize"));
727         myRead2Button->setText(tr("&Refuse"));
728         Licq::EventAuthRequest* pAuthReq = dynamic_cast<Licq::EventAuthRequest*>(m);
729         if (!Licq::gUserManager.userExists(pAuthReq->userId()))
730           myRead3Button->setText(tr("A&dd User"));
731         myRead4Button->setText(tr("&View Info"));
732         break;
733       }
734 
735       case Licq::UserEvent::TypeAuthGranted:
736       {
737         Licq::EventAuthGranted* pAuth = dynamic_cast<Licq::EventAuthGranted*>(m);
738         if (!Licq::gUserManager.userExists(pAuth->userId()))
739           myRead1Button->setText(tr("A&dd User"));
740         myRead4Button->setText(tr("&View Info"));
741         break;
742       }
743 
744       case Licq::UserEvent::TypeAdded:
745       {
746         Licq::EventAdded* pAdd = dynamic_cast<Licq::EventAdded*>(m);
747         if (!Licq::gUserManager.userExists(pAdd->userId()))
748           myRead1Button->setText(tr("A&dd User"));
749         myRead4Button->setText(tr("&View Info"));
750         break;
751       }
752 
753       case Licq::UserEvent::TypeContactList:
754       {
755         int s = dynamic_cast<Licq::EventContactList*>(m)->Contacts().size();
756         if (s > 1)
757           myRead1Button->setText(tr("A&dd %1 Users").arg(s));
758         else
759           if (s == 1)
760             myRead1Button->setText(tr("A&dd User"));
761         break;
762       }
763 
764       case Licq::UserEvent::TypeEmailAlert:
765         myRead1Button->setText(tr("&View Email"));
766         break;
767     } // switch
768   }  // if
769 
770   myRead1Button->setEnabled(!myRead1Button->text().isEmpty());
771   myRead2Button->setEnabled(!myRead2Button->text().isEmpty());
772   myRead3Button->setEnabled(!myRead3Button->text().isEmpty());
773   myRead4Button->setEnabled(!myRead4Button->text().isEmpty());
774 
775   myActionsBox->setVisible(
776       myRead1Button->isEnabled() || myRead2Button->isEnabled() ||
777       myRead3Button->isEnabled() || myRead4Button->isEnabled());
778 
779   myRead1Button->setFocus();
780 
781   if (e->isUnread())
782   {
783     // clear the event only after all the slots have been invoked
784     QTimer::singleShot(20, this, SLOT(clearEvent()));
785     e->MarkRead();
786   }
787 }
788 
sentEvent(const Licq::Event * e)789 void UserViewEvent::sentEvent(const Licq::Event* e)
790 {
791   if (e->userId() != myUsers.front())
792     return;
793 
794   if (!Config::Chat::instance()->msgChatView())
795     new MessageListItem(e->userEvent(), myMessageList);
796 }
797 
setEncoding()798 void UserViewEvent::setEncoding()
799 {
800   // if we have an open view, just refresh it
801   if (myMessageList != NULL)
802     printMessage(myMessageList->currentItem());
803 }
804 
resizeEvent(QResizeEvent * event)805 void UserViewEvent::resizeEvent(QResizeEvent* event)
806 {
807   Config::Chat::instance()->setViewDialogSize(size());
808   UserEventCommon::resizeEvent(event);
809 }
810