1 /***************************************************************************
2 * *
3 * This program is free software; you can redistribute it and/or modify *
4 * it under the terms of the GNU General Public License as published by *
5 * the Free Software Foundation; either version 3 of the License, or *
6 * (at your option) any later version. *
7 * *
8 ***************************************************************************/
9
10 #include "ArenaWidgetFactory.h"
11 #include "HubFrame.h"
12 #include "PMWindow.h"
13 #include "WulforUtil.h"
14 #include "Antispam.h"
15 #include "HubManager.h"
16 #include "Notification.h"
17 #include "ShellCommandRunner.h"
18 #include "EmoticonDialog.h"
19 #include "WulforSettings.h"
20 #include "FlowLayout.h"
21 #include "SearchFrame.h"
22 #ifdef USE_ASPELL
23 #include "SpellCheck.h"
24 #endif
25 #include "ArenaWidgetManager.h"
26 #include "MainWindow.h"
27 #include "GlobalTimer.h"
28
29 #include "UserListModel.h"
30 #include "EmoticonFactory.h"
31
32 #include "dcpp/LogManager.h"
33 #include "dcpp/User.h"
34 #include "dcpp/UserCommand.h"
35 #include "dcpp/CID.h"
36 #include "dcpp/HashManager.h"
37 #include "dcpp/Util.h"
38 #include "dcpp/ChatMessage.h"
39
40 #if HAVE_MALLOC_TRIM
41 #include <malloc.h>
42 #endif
43
44 #include <QMouseEvent>
45 #include <QTextCodec>
46 #include <QItemSelectionModel>
47 #include <QMenu>
48 #include <QClipboard>
49 #include <QInputDialog>
50 #include <QTextCursor>
51 #include <QTextBlock>
52 #include <QTextLayout>
53 #include <QTextDocument>
54 #include <QUrl>
55 #include <QCloseEvent>
56 #include <QThread>
57 #include <QRegExp>
58 #include <QScrollBar>
59 #include <QShortcut>
60 #include <QHeaderView>
61
62 #if QT_VERSION >= 0x050000
63 #include <QUrlQuery>
64 #endif
65
66 #include <QtDebug>
67
68 #include <exception>
69
70 class HubFramePrivate {
71 typedef QMap<QString, PMWindow*> PMMap;
72 typedef QMap<QString, QVariant> VarMap;
73 typedef QList<ShellCommandRunner*> ShellList;
74 public:
75 QMenu *arenaMenu;
76
77 Client *client;
78
79 // Work data
80 QTextCodec *codec;
81
82 quint64 total_shared;
83 QString hub_title;
84
85 bool chatDisabled;
86 bool hasMessages;
87 bool hasHighlightMessages;
88 bool drawLine;
89
90 QStringList status_msg_history;
91 QStringList out_messages;
92 int out_messages_index;
93 bool out_messages_unsent;
94
95 PMMap pm;
96 ShellList shell_list;
97
98 // Userlist data and some helpful functions
99 UserListModel *model;
100 UserListProxyModel *proxy;
101
102 QCompleter * completer;
103 };
104
clearLayout(QLayout * l)105 static inline void clearLayout(QLayout *l){
106 if (!l)
107 return;
108
109 QLayoutItem *item = NULL;
110 while ((item = l->takeAt(0))){
111 l->removeWidget(item->widget());
112 item->widget()->deleteLater();
113
114 delete item;
115 }
116
117 l->invalidate();
118 }
119
120 //replace [tag]some_text[/tag] with <txt>some_text</txt>
parseBasicBBCode(const QString & tag,const QString & txt,QString & input,QString & output)121 static bool parseBasicBBCode(const QString &tag, const QString &txt, QString &input, QString &output){
122 if (tag.isEmpty())
123 return false;
124
125 const QString &bbCode1 = QString("[%1]").arg(tag);
126 const QString &bbCode2 = QString("[/%1]").arg(tag);
127
128 if (input.startsWith(bbCode1) && input.indexOf(bbCode2) >= bbCode1.length()){
129 input.remove(0, bbCode1.length());
130 int c_len = input.indexOf(bbCode2);
131
132 const QString &chunk = HubFrame::LinkParser::parseForLinks(input.left(c_len), false);
133
134 output += QString("<%1>").arg(txt) + chunk + QString("</%1>").arg(txt);
135 input.remove(0, c_len+bbCode2.length());
136
137 return true;
138 }
139
140 return false;
141 }
142
143 HubFrame::Menu *HubFrame::Menu::instance = NULL;
144 unsigned HubFrame::Menu::counter = 0;
145
newInstance()146 void HubFrame::Menu::newInstance(){
147 counter++;
148
149 if (instance)
150 return;
151
152 instance = new Menu();
153 }
154
deleteInstance()155 void HubFrame::Menu::deleteInstance(){
156 counter--;
157
158 if (counter)
159 return;
160
161 delete instance;
162
163 instance = NULL;
164 }
165
getInstance()166 HubFrame::Menu *HubFrame::Menu::getInstance(){
167 return instance;
168 }
169
Menu()170 HubFrame::Menu::Menu(){
171 menu = new QMenu();
172 WulforUtil *WU = WulforUtil::getInstance();
173
174 last_user_cmd = "";
175
176 // Userlist actions
177 QAction *copy_text = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy"), NULL);
178 QAction *search_text = new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Search text"), NULL);
179 QAction *copy_nick = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy nick"), NULL);
180 QAction *find = new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Show in list"), NULL);
181 QAction *browse = new QAction(WU->getPixmap(WulforUtil::eiFOLDER_BLUE), tr("Browse files"), NULL);
182 QAction *match_queue = new QAction(WU->getPixmap(WulforUtil::eiDOWN), tr("Match Queue"), NULL);
183 QAction *private_msg = new QAction(WU->getPixmap(WulforUtil::eiMESSAGE), tr("Private Message"), NULL);
184 QAction *fav_add = new QAction(WU->getPixmap(WulforUtil::eiFAVADD), tr("Add to Favorites"), NULL);
185 QAction *fav_del = new QAction(WU->getPixmap(WulforUtil::eiFAVREM), tr("Remove from Favorites"), NULL);
186 QAction *grant_slot = new QAction(WU->getPixmap(WulforUtil::eiEDITADD), tr("Grant slot"), NULL);
187 QAction *rem_queue = new QAction(WU->getPixmap(WulforUtil::eiEDITDELETE), tr("Remove from Queue"), NULL);
188
189 // Chat actions
190 QAction *sep1 = new QAction(NULL);
191 QAction *clear_chat = new QAction(WU->getPixmap(WulforUtil::eiCLEAR), tr("Clear chat"), NULL);
192 QAction *find_in_chat= new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Find in chat"), NULL);
193 QAction *dis_chat = new QAction(WU->getPixmap(WulforUtil::eiFILECLOSE), tr("Disable/Enable chat"), NULL);
194 QAction *sep2 = new QAction(NULL);
195 QAction *select_all = new QAction(tr("Select all"), NULL);
196 QAction *sep3 = new QAction(NULL);
197 QAction *zoom_in = new QAction(WU->getPixmap(WulforUtil::eiZOOM_IN), tr("Zoom In"), NULL);
198 QAction *zoom_out = new QAction(WU->getPixmap(WulforUtil::eiZOOM_OUT), tr("Zoom Out"), NULL);
199
200 // submenu copy_data for user list
201 QAction *copy_data_nick = new QAction(tr("Nick"), NULL);
202 QAction *copy_data_ip = new QAction(tr("IP"), NULL);
203 QAction *copy_data_share = new QAction(tr("Share"), NULL);
204 QAction *copy_data_tag = new QAction(tr("Tag"), NULL);
205 QAction *sep4 = new QAction(NULL);
206 QAction *copy_data_all = new QAction(tr("All"), NULL);
207
208 QMenu *menuCopyData = new QMenu(NULL);
209 menuCopyData->addActions(QList<QAction*>() << copy_data_nick << copy_data_ip << copy_data_share << copy_data_tag << sep4 << copy_data_all);
210
211 QAction *copy_data = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy data"), NULL);
212 copy_data->setMenu(menuCopyData);
213 // end submenu
214
215 sep1->setSeparator(true), sep2->setSeparator(true), sep3->setSeparator(true), sep4->setSeparator(true);
216
217 actions << copy_text
218 << search_text
219 << copy_nick
220 << find
221 << browse
222 << match_queue
223 << private_msg
224 << fav_add
225 << fav_del
226 << grant_slot
227 << rem_queue;
228
229 pm_actions << copy_text
230 << copy_nick
231 << browse
232 << match_queue
233 << private_msg
234 << fav_add
235 << fav_del
236 << grant_slot
237 << rem_queue;
238
239 ul_actions << browse
240 << private_msg
241 << fav_add
242 << fav_del
243 << grant_slot
244 << copy_data
245 << match_queue
246 << rem_queue;
247
248 chat_actions << sep1
249 << clear_chat
250 << find_in_chat
251 << dis_chat
252 << sep2
253 << select_all
254 << sep3
255 << zoom_in
256 << zoom_out;
257
258 pm_chat_actions << sep1
259 << clear_chat
260 << sep2
261 << select_all
262 << sep3
263 << zoom_in
264 << zoom_out;
265
266 chat_actions_map.insert(copy_text, CopyText);
267 chat_actions_map.insert(search_text, SearchText);
268 chat_actions_map.insert(copy_nick, CopyNick);
269 chat_actions_map.insert(clear_chat, ClearChat);
270 chat_actions_map.insert(find_in_chat, FindInChat);
271 chat_actions_map.insert(dis_chat, DisableChat);
272 chat_actions_map.insert(select_all, SelectAllChat);
273 chat_actions_map.insert(zoom_in, ZoomInChat);
274 chat_actions_map.insert(zoom_out, ZoomOutChat);
275
276 chat_actions_map.insert(copy_data_nick, CopyNick);
277 chat_actions_map.insert(copy_data_ip, CopyIP);
278 chat_actions_map.insert(copy_data_share, CopyShare);
279 chat_actions_map.insert(copy_data_tag, CopyTag);
280 chat_actions_map.insert(copy_data_all, CopyText);
281 }
282
283
~Menu()284 HubFrame::Menu::~Menu(){
285 delete menu;
286
287 qDeleteAll(chat_actions);
288 qDeleteAll(actions);
289 }
290
execUserMenu(Client * client,const QString & cid=QString ())291 HubFrame::Menu::Action HubFrame::Menu::execUserMenu(Client *client, const QString &cid = QString()){
292 if (!client)
293 return None;
294
295 menu->clear();
296 menu->setProperty("iconVisibleInMenu", true);
297
298 menu->setTitle(WulforUtil::getInstance()->getNickViaOnlineUser(cid, _q(client->getHubUrl())));
299
300 if (menu->title().isEmpty())
301 menu->setTitle(tr("[User went offline]"));
302
303 menu->addActions(ul_actions);
304
305 QMenu *user_menu = NULL;
306
307 if (!cid.isEmpty()){
308 user_menu = WulforUtil::getInstance()->buildUserCmdMenu(client->getHubUrl(), UserCommand::CONTEXT_USER);
309
310 if (user_menu && !user_menu->actions().empty())
311 menu->addMenu(user_menu);
312 }
313
314 QMenu *antispam_menu = NULL;
315
316 if (AntiSpam::getInstance()){
317 antispam_menu = new QMenu(NULL);
318 antispam_menu->setTitle(tr("AntiSpam"));
319 antispam_menu->menuAction()->setIcon(WICON(WulforUtil::eiSPAM));
320 antispam_menu->setProperty("iconVisibleInMenu", true);
321
322 antispam_menu->addAction(tr("Add to Black"))->setData(static_cast<int>(AntiSpamBlack));
323 antispam_menu->addAction(tr("Add to White"))->setData(static_cast<int>(AntiSpamWhite));
324
325 menu->addMenu(antispam_menu);
326 }
327
328 QAction *res = menu->exec(QCursor::pos());
329
330 if (user_menu)
331 user_menu->deleteLater();
332 if (antispam_menu)
333 antispam_menu->deleteLater();
334
335 if (actions.contains(res))
336 return static_cast<Action>(actions.indexOf(res));
337 else if (chat_actions_map.contains(res))
338 return chat_actions_map[res];
339 else if (antispam_menu && antispam_menu->actions().contains(res))
340 return static_cast<HubFrame::Menu::Action>(res->data().toInt());
341 else if (res && res->data().canConvert(QVariant::Int)){//User command{
342 int id = res->data().toInt();
343
344 UserCommand uc;
345 if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
346 return None;
347
348 StringMap params;
349
350 if (WulforUtil::getInstance()->getUserCommandParams(uc, params)) {
351 UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
352
353 if (user)
354 ClientManager::getInstance()->userCommand(HintedUser(user, client->getHubUrl()), uc, params, true);
355
356 return UserCommands;
357 }
358 else
359 return None;
360 }
361 else
362 return None;
363 }
364
getLastUserCmd() const365 QString HubFrame::Menu::getLastUserCmd() const{
366 return last_user_cmd;
367 }
368
execChatMenu(Client * client,const QString & cid=QString (),bool pmw=false)369 HubFrame::Menu::Action HubFrame::Menu::execChatMenu(Client *client, const QString &cid = QString(), bool pmw = false){
370 if (!client)
371 return None;
372
373 menu->clear();
374 QAction *title = new QAction(WulforUtil::getInstance()->getNickViaOnlineUser(cid, _q(client->getHubUrl())), menu);
375 QFont f;
376 f.setBold(true);
377 title->setFont(f);
378 title->setEnabled(false);
379
380 if (title->text().isEmpty())
381 title->setText(tr("[User went offline]"));
382
383 menu->addAction(title);
384
385 if(pmw){
386 menu->addActions(pm_actions);
387 menu->addActions(pm_chat_actions);
388 }
389 else{
390 menu->addActions(actions);
391 menu->addActions(chat_actions);
392 }
393
394 QMenu *user_menu = NULL;
395
396 if (!cid.isEmpty() && !pmw){
397 user_menu = WulforUtil::getInstance()->buildUserCmdMenu(client->getHubUrl(), UserCommand::CONTEXT_HUB);
398
399 if (user_menu && !user_menu->actions().isEmpty())
400 menu->addMenu(user_menu);
401 }
402
403 QMenu *antispam_menu = NULL;
404
405 if (AntiSpam::getInstance()){
406 antispam_menu = new QMenu(NULL);
407 antispam_menu->setTitle(tr("AntiSpam"));
408
409 antispam_menu->addAction(tr("Add to Black"))->setData(static_cast<int>(AntiSpamBlack));
410 antispam_menu->addAction(tr("Add to White"))->setData(static_cast<int>(AntiSpamWhite));
411
412 menu->addMenu(antispam_menu);
413 }
414
415 QAction *res = menu->exec(QCursor::pos());
416
417 if (user_menu)
418 user_menu->deleteLater();
419
420 title->deleteLater();
421
422 if (actions.contains(res))
423 return static_cast<Action>(actions.indexOf(res));
424 else if (chat_actions_map.contains(res))
425 return chat_actions_map[res];
426 else if (antispam_menu && antispam_menu->actions().contains(res))
427 return static_cast<HubFrame::Menu::Action>(res->data().toInt());
428 else if (res && res->data().canConvert(QVariant::Int)){//User command
429 int id = res->data().toInt();
430
431 UserCommand uc;
432 if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
433 return None;
434
435 StringMap params;
436
437 if (WulforUtil::getInstance()->getUserCommandParams(uc, params)){
438 UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
439
440 if (user)
441 ClientManager::getInstance()->userCommand(HintedUser(user, client->getHubUrl()), uc, params, true);
442
443 return UserCommands;
444 }
445 else
446 return None;
447 }
448 else
449 return None;
450 }
451
parseForLinks(QString input,bool use_emot)452 QString HubFrame::LinkParser::parseForLinks(QString input, bool use_emot){
453 if (input.isEmpty())
454 return input;
455
456 static QList<QChar> unwise_chars = QList<QChar>() << '{' << '}' << '|' << '\\' << '^' << '[' << ']' << '`';
457 static QStringList link_types = QStringList() << "http://" << "https://" << "ftp://" << "www."
458 << "dchub://" << "adc://" << "adcs://" << "magnet:";
459
460 QString output = "";
461
462 EmoticonMap emoticons;
463
464 if (use_emot && WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
465 emoticons = EmoticonFactory::getInstance()->getEmoticons();
466
467 const QString &emo_theme = WSGET(WS_APP_EMOTICON_THEME);
468 bool force_emot = WBGET(WB_APP_FORCE_EMOTICONS);
469
470 while (!input.isEmpty()){
471 for (int j = 0; j < link_types.size(); j++){
472 const QString &linktype = link_types.at(j);
473
474 if (input.startsWith(linktype)){
475 int l_pos = linktype.length();
476
477 while (l_pos < input.size()){
478 QChar ch = input.at(l_pos);
479
480 if (ch.isSpace() || ch == '\n' ||
481 ch == '>' || ch == '<' || unwise_chars.contains(ch)){
482 break;
483 }
484 else
485 l_pos++;
486 }
487
488 QString link = input.left(l_pos);
489 QString toshow = link;
490
491 if (linktype == "http://" || linktype == "https://" || linktype == "ftp://"){
492 while (!QUrl(link).isValid() && !link.isEmpty()){
493 input.prepend(link.at(link.length()-1));
494 link.remove(link.length()-1, 1);
495 }
496
497 toshow = QUrl::fromEncoded(link.toUtf8()).toString();
498 }
499
500 if (linktype == "magnet:"){
501 QString magnet = link;
502
503 #if QT_VERSION >= 0x050000
504 QUrlQuery u;
505 #else
506 QUrl u;
507 #endif
508
509 if (!magnet.contains("+")) {
510 #if QT_VERSION >= 0x050000
511 u.setQuery(magnet.toUtf8());
512 #else
513 u.setEncodedUrl(magnet.toUtf8());
514 #endif
515 } else {
516 QString _l = magnet;
517
518 _l.replace("+", "%20");
519 #if QT_VERSION >= 0x050000
520 u.setQuery(_l.toUtf8());
521 #else
522 u.setEncodedUrl(_l.toUtf8());
523 #endif
524 }
525 if (u.hasQueryItem("kt")) {
526 QString keywords = u.queryItemValue("kt");
527 QString hub = u.hasQueryItem("xs")? u.queryItemValue("xs") : "";
528 if (!(hub.startsWith("dchub://", Qt::CaseInsensitive) ||
529 hub.startsWith("adc://", Qt::CaseInsensitive) ||
530 hub.startsWith("adcs://", Qt::CaseInsensitive)) && !hub.isEmpty())
531 hub.prepend("dchub://");
532
533 if (keywords.isEmpty())
534 keywords = tr("Invalid keywords");
535
536 if (!hub.isEmpty())
537 #if QT_VERSION >= 0x050000
538 toshow = keywords.toHtmlEscaped() + " (" + hub.toHtmlEscaped() + ")";
539 else
540 toshow = keywords.toHtmlEscaped();
541 #else
542 toshow = Qt::escape(keywords) + " (" + Qt::escape(hub) + ")";
543 else
544 toshow = Qt::escape(keywords);
545 #endif
546 }
547 else {
548 QString name, tth;
549 int64_t size;
550
551 WulforUtil::splitMagnet(link, size, tth, name);
552 toshow = QString("%1 (%2)").arg(name).arg(WulforUtil::formatBytes(size));
553 }
554 }
555
556 if (linktype == "www.")
557 toshow.prepend("http://");
558
559 QString html_link = "";
560
561 if (linktype != "magnet:")
562 html_link = QString("<a href=\"%1\" title=\"%1\" style=\"cursor: hand\">%1</a>").arg(toshow);
563 else{
564 html_link = "<a href=\"" + link + "\" title=\"" + toshow + "\" style=\"cursor: hand\">" + toshow + "</a>";
565 }
566
567 output += html_link;
568 input.remove(0, l_pos);
569 }
570
571 if (input.isEmpty())
572 break;
573 }
574
575 if (input.isEmpty())
576 break;
577
578 auto it = emoticons.begin();
579 auto end_it = emoticons.end();
580 bool smile_found = false;
581
582 for (; it != end_it; ++it){//Let's try to parse smiles
583 const QString &emo_text = it.key();
584 EmoticonObject *obj = it.value();
585
586 if (input.startsWith(emo_text) && obj){
587 if (force_emot || input == emo_text){
588 QString img = QString("<img alt=\"%1\" title=\"%1\" align=\"center\" source=\"%2/emoticon%3\" />")
589 .arg(emo_text)
590 .arg(emo_theme)
591 .arg(obj->id);
592
593 output += img;
594 input.remove(0, emo_text.length());
595
596 smile_found = true;
597
598 break;
599 }
600 else if (output.endsWith(' ') || output.endsWith('\t') || output.isEmpty()){
601 int emo_text_len = emo_text.length();
602 int input_length = input.length();
603
604 bool nextCharisSpace = false;
605
606 if (emo_text_len == input_length)
607 nextCharisSpace = true;
608 else if (input_length > emo_text_len){
609 char c = input.at(emo_text_len).unicode();
610
611 nextCharisSpace = (c == ' ' || c == '\t');
612 }
613
614 if (!nextCharisSpace)
615 continue;
616
617 QString img = QString("<img alt=\"%1\" title=\"%1\" align=\"center\" source=\"%2/emoticon%3\" />")
618 .arg(emo_text)
619 .arg(emo_theme)
620 .arg(obj->id);
621
622 output += img;
623 input.remove(0, emo_text_len);
624
625 smile_found = true;
626
627 break;
628 }
629 }
630 }
631
632 if(smile_found)
633 continue;
634
635 if (WBGET("hubframe/use-bb-code", false)){
636 if (parseBasicBBCode("b", "b", input, output))
637 continue;
638 else if (parseBasicBBCode("u", "u", input, output))
639 continue;
640 else if (parseBasicBBCode("i", "i", input, output))
641 continue;
642 else if (parseBasicBBCode("s", "s", input, output))
643 continue;
644
645 if (input.startsWith("[color=") && input.indexOf("[/color]") > 8){
646 QRegExp exp("\\[color=(\\w+|#.{6,6})\\]((.*))\\[/color\\].*");
647 QString chunk = input.left(input.indexOf("[/color]")+8);
648
649 if (exp.exactMatch(chunk)){
650 if (exp.captureCount() == 3){
651 output += "<font color=\"" + exp.cap(1) + "\">" + parseForLinks(exp.cap(2), false) + "</font>";
652
653 input.remove(0, chunk.length());
654 }
655 }
656 }
657 else if (input.startsWith(("[url")) && input.indexOf("[/url]") > 0){
658 QRegExp exp("\\[url=*((.+[^\\]\\[]))*\\]((.+))\\[/url\\]");
659 QString chunk = input.left(input.indexOf("[/url]")+6);
660
661 if (exp.exactMatch(chunk) && exp.captureCount() == 4){
662 QString link = exp.cap(2);
663 QString title = exp.cap(3);
664
665 link = link.isEmpty()? title : link;
666
667 if (link.startsWith("="))
668 link.remove(0, 1);
669
670 if (!title.isEmpty()){
671 #if QT_VERSION >= 0x050000
672 output += "<a href=\"" + link + "\" title=\"" + title.toHtmlEscaped() + "\">" + title.toHtmlEscaped() + "</a>";
673 #else
674 output += "<a href=\"" + link + "\" title=\"" + Qt::escape(title) + "\">" + Qt::escape(title) + "</a>";
675 #endif
676
677 input.remove(0, chunk.length());
678 }
679 }
680 }
681 else if (input.startsWith(("[code]")) && input.indexOf("[/code]") > 0){
682 input.remove(0, 6);
683 int c_len = input.indexOf("[/code]");
684
685 QString chunk = input.left(c_len);
686
687 output += "<table border=1 width=100%><tr><td align=\"left\">Code:</td></tr><tr><td align=\"left\"><pre style=\"white-space: pre;\"><tt>" + chunk + "</tt></pre></td></tr></table>";
688 input.remove(0, c_len+7);
689
690 continue;
691 }
692 }
693
694 if (input.startsWith("<")){
695 output += "<";
696 input.remove(0, 1);
697
698 continue;
699 }
700 else if (input.startsWith(">")){
701 output += ">";
702 input.remove(0, 1);
703
704 continue;
705 }
706 else if (input.startsWith("&")){
707 input.remove(0, 1);
708 output += "&";
709
710 continue;
711 }
712 else if (input.startsWith("[magnet=\"") && input.indexOf("[/magnet]") > 9){//9 - length of [magnet="
713 QString chunk = input.left(input.indexOf("[/magnet]"));
714
715 do{
716 if (chunk.isEmpty())
717 break;
718
719 chunk.remove(0, 9);
720
721 if (chunk.isEmpty() || chunk.indexOf("\"") <= 0)
722 break;
723
724 QString magnet = chunk.left(chunk.indexOf("\""));
725
726 magnet = magnet.trimmed();
727
728 if (magnet.isEmpty())
729 break;
730
731 QString name, tth;
732 int64_t size;
733
734 WulforUtil::splitMagnet(magnet, size, tth, name);
735
736 chunk.remove(0, magnet.length());
737
738 if (chunk.indexOf("]") < 1)
739 break;
740
741 chunk.remove(0, chunk.indexOf("]") + 1);
742
743 if (chunk.isEmpty())
744 break;
745
746 QString toshow = tr("%1 (%2)").arg(chunk).arg(WulforUtil::formatBytes(size));
747 QString html_link = "<a href=\"" + magnet + "\" title=\"" + toshow + "\" style=\"cursor: hand\">" + toshow + "</a>";
748
749 output += html_link;
750 input.remove(0, input.indexOf("[/magnet]")+1+9);
751 }
752 while (0);
753 }
754
755 if (input.isEmpty())
756 break;
757
758 output += input.at(0);
759
760 input.remove(0, 1);
761 }
762
763 return output;
764 }
765
parseForMagnetAlias(QString & output)766 void HubFrame::LinkParser::parseForMagnetAlias(QString &output){
767 int pos = 0;
768 QRegExp rx("(<magnet(?:\\s+show=([^>]+))?>(.+)</magnet>)");
769 rx.setMinimal(true);
770 while ((pos = output.indexOf(rx, pos)) >= 0) {
771 QFileInfo fi(rx.cap(3));
772 if (fi.isDir() || !fi.exists()) {
773 pos++;
774 continue;
775 }
776 QString name = fi.fileName();
777 if (!rx.cap(2).isEmpty())
778 name = rx.cap(2);
779
780 const TTHValue *tth = HashManager::getInstance()->getFileTTHif(_tq(fi.absoluteFilePath()));
781 if (tth) {
782 QString urlStr = WulforUtil::getInstance()->makeMagnet(name, fi.size(), _q(tth->toBase32()));
783 output.replace(pos, rx.cap(1).length(), urlStr);
784 } else {
785 output.replace(pos, rx.cap(1).length(), tr("not shared"));
786 }
787 }
788 }
789
HubFrame(QWidget * parent,QString hub="",QString encoding="")790 HubFrame::HubFrame(QWidget *parent, QString hub="", QString encoding=""):
791 QWidget(parent),
792 d_ptr(new HubFramePrivate())
793 {
794 Q_D(HubFrame);
795
796 d->total_shared = 0;
797 d->arenaMenu = NULL;
798 d->codec = NULL;
799 d->chatDisabled = false;
800 d->hasMessages = false;
801 d->hasHighlightMessages = false;
802 d->client = NULL;
803
804 setupUi(this);
805
806 Menu::newInstance();
807
808 d->client = ClientManager::getInstance()->getClient(hub.toStdString());
809 d->client->addListener(this);
810
811 QString enc = WulforUtil::getInstance()->qtEnc2DcEnc(encoding);
812
813 if (enc.isEmpty())
814 enc = WulforUtil::getInstance()->qtEnc2DcEnc(WSGET(WS_DEFAULT_LOCALE));
815
816 if (enc.indexOf(" ") > 0){
817 enc = enc.left(enc.indexOf(" "));
818 enc.replace(" ", "");
819 }
820
821 d->client->setEncoding(enc.toStdString());
822
823 d->codec = WulforUtil::getInstance()->codecForEncoding(encoding);
824
825 init();
826
827 FavoriteHubEntry* entry = FavoriteManager::getInstance()->getFavoriteHubEntry(_tq(hub));
828
829 if (entry && entry->getDisableChat())
830 disableChat();
831
832 d->client->connect();
833
834 setAttribute(Qt::WA_DeleteOnClose);
835
836 d->out_messages_index = 0;
837 d->out_messages_unsent = false;
838
839 FavoriteManager::getInstance()->addListener(this);
840 }
841
842
~HubFrame()843 HubFrame::~HubFrame(){
844 Q_D(HubFrame);
845
846 Menu::deleteInstance();
847
848 treeView_USERS->setModel(NULL);
849
850 delete d->proxy;
851 delete d->model;
852
853 delete d;
854 }
855
eventFilter(QObject * obj,QEvent * e)856 bool HubFrame::eventFilter(QObject *obj, QEvent *e){
857 Q_D(HubFrame);
858
859 if (e->type() == QEvent::KeyRelease){
860 QKeyEvent *k_e = reinterpret_cast<QKeyEvent*>(e);
861
862 if ((static_cast<QTextEdit*>(obj) == plainTextEdit_INPUT) &&
863 (k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return) &&
864 (k_e->modifiers() != Qt::ShiftModifier))
865 {
866 return true;
867 }
868 else if (static_cast<QLineEdit*>(obj) == lineEdit_FIND){
869 bool ret = QWidget::eventFilter(obj, e);
870
871 if (k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return)
872 slotFindForward();
873
874 return ret;
875 }
876
877 }
878 else if (e->type() == QEvent::KeyPress){
879 QKeyEvent *k_e = reinterpret_cast<QKeyEvent*>(e);
880
881 if ((static_cast<QTextEdit*>(obj) == plainTextEdit_INPUT) &&
882 (!WBGET(WB_USE_CTRL_ENTER) || k_e->modifiers() == Qt::ControlModifier) &&
883 ((k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return) && k_e->modifiers() != Qt::ShiftModifier) ||
884 (k_e->key() == Qt::Key_Enter && k_e->modifiers() == Qt::KeypadModifier))
885 {
886 sendChat(plainTextEdit_INPUT->toPlainText(), false, false);
887
888 plainTextEdit_INPUT->setPlainText("");
889
890 return true;
891 }
892
893 if (qobject_cast<LineEdit*>(obj) == lineEdit_FIND && k_e->key() == Qt::Key_Escape){
894 lineEdit_FIND->clear();
895
896 requestFilter();
897
898 return true;
899 }
900
901 if (k_e->modifiers() == Qt::ControlModifier){
902 if (k_e->key() == Qt::Key_Equal || k_e->key() == Qt::Key_Plus){
903 textEdit_CHAT->zoomIn();
904
905 return true;
906 }
907 else if (k_e->key() == Qt::Key_Minus){
908 textEdit_CHAT->zoomOut();
909
910 return true;
911 }
912 }
913 }
914 else if (e->type() == QEvent::MouseButtonPress){
915 QMouseEvent *m_e = reinterpret_cast<QMouseEvent*>(e);
916
917 bool isChat = (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport());
918 bool isUserList = (static_cast<QWidget*>(obj) == treeView_USERS->viewport());
919
920 if (isChat)
921 textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
922
923 if (isChat && (m_e->button() == Qt::LeftButton)){
924 QString pressedParagraph = textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos()));
925
926 if (!WulforUtil::getInstance()->openUrl(pressedParagraph)){
927 /**
928 Do nothing
929 */
930 }
931 }
932 else if ((isChat || isUserList) && m_e->button() == Qt::MidButton)
933 {
934 QString nick = "";
935 QString cid = "";
936 bool cursoratnick = false;
937
938 if (isChat){
939 QTextCursor cursor = textEdit_CHAT->textCursor();
940 QString pressedParagraph = cursor.block().text();
941 int positionCursor = cursor.columnNumber();
942
943 int l = pressedParagraph.indexOf(" <");
944 int r = pressedParagraph.indexOf("> ");
945
946 if (l < r){
947 nick = pressedParagraph.mid(l+2, r-l-2);
948 cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
949 }
950 if ((positionCursor < r) && (positionCursor > l))
951 cursoratnick = true;
952 }
953 else if (isUserList){
954 QModelIndex index = treeView_USERS->indexAt(treeView_USERS->viewport()->mapFromGlobal(QCursor::pos()));
955
956 if (d->proxy && treeView_USERS->model() == d->proxy)
957 index = d->proxy->mapToSource(index);
958
959 if (index.isValid()){
960 UserListItem *i = reinterpret_cast<UserListItem*>(index.internalPointer());
961
962 nick = i->getNick();
963 cid = i->getCID();
964 }
965 }
966
967 if (!cid.isEmpty()){
968 if (WIGET(WI_CHAT_MDLCLICK_ACT) == 0){
969 if(!plainTextEdit_INPUT->textCursor().position())
970 plainTextEdit_INPUT->textCursor().insertText(nick + WSGET(WS_CHAT_SEPARATOR) + " ");
971 else
972 plainTextEdit_INPUT->textCursor().insertText(nick + " ");
973
974 plainTextEdit_INPUT->setFocus();
975 }
976 else if (WIGET(WI_CHAT_MDLCLICK_ACT) == 2 && (cursoratnick || isUserList))
977 addPM(cid, "", false);
978 else if (cursoratnick || isUserList)
979 browseUserFiles(cid, false);
980 }
981 }
982 }
983 else if (e->type() == QEvent::MouseButtonDblClick){
984 bool isChat = (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport());
985 bool isUserList = (static_cast<QWidget*>(obj) == treeView_USERS->viewport());
986
987 if (isChat || isUserList){
988 QString nick = "";
989 QString cid = "";
990 bool cursoratnick = false;
991 QTextCursor cursor = textEdit_CHAT->textCursor();
992
993 if (isChat){
994 QString nickstatus="",nickmessage="";
995 QString pressedParagraph = cursor.block().text();
996 //qDebug() << pressedParagraph;
997 int positionCursor = cursor.columnNumber();
998 int l = pressedParagraph.indexOf(" <");
999 int r = pressedParagraph.indexOf("> ");
1000 if (l < r)
1001 nickmessage = pressedParagraph.mid(l+2, r-l-2);
1002 else {
1003 int l1 = pressedParagraph.indexOf(" * ");
1004 //qDebug() << positionCursor << " " << l1 << " " << l << " " << r;
1005 if (l1 > -1 ) {
1006 QString pressedParagraphstatus = pressedParagraph.remove(0,l1+3).simplified();
1007 //qDebug() << pressedParagraphstatus;
1008 int r1 = pressedParagraphstatus.indexOf(" ");
1009 //qDebug() << r1;
1010 nickstatus = pressedParagraphstatus.mid(0, r1);
1011 //qDebug() << nickstatus;
1012 }
1013 }
1014 if (!nickmessage.isEmpty() || !nickstatus.isEmpty()) {
1015 //qDebug() << nickstatus;
1016 //qDebug() << nickmessage;
1017 nick = nickmessage + nickstatus;
1018 //qDebug() << nick;
1019 cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
1020 //qDebug() << cid;
1021 }
1022 if (((positionCursor < r) && (positionCursor > l))/* || positionCursor > l1*/)
1023 cursoratnick = true;
1024 }
1025 else if (isUserList){
1026 QModelIndex index = treeView_USERS->indexAt(treeView_USERS->viewport()->mapFromGlobal(QCursor::pos()));
1027
1028 if (d->proxy && treeView_USERS->model() == d->proxy)
1029 index = d->proxy->mapToSource(index);
1030
1031 if (index.isValid()){
1032 UserListItem *i = reinterpret_cast<UserListItem*>(index.internalPointer());
1033
1034 nick = i->getNick();
1035 cid = i->getCID();
1036 }
1037 }
1038
1039 if (!cid.isEmpty()){
1040 if (WIGET(WI_CHAT_DBLCLICK_ACT) == 1 && (cursoratnick || isUserList)){
1041 browseUserFiles(cid, false);
1042 }
1043 else if (WIGET(WI_CHAT_DBLCLICK_ACT) == 2 && (cursoratnick || isUserList)){
1044 addPM(cid, "", false);
1045 }
1046 else if (textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos())).startsWith("user://") || isUserList){//may be dbl click on user nick
1047 if(!plainTextEdit_INPUT->textCursor().position())
1048 plainTextEdit_INPUT->textCursor().insertText(nick + WSGET(WS_CHAT_SEPARATOR) + " ");
1049 else
1050 plainTextEdit_INPUT->textCursor().insertText(nick + " ");
1051
1052 plainTextEdit_INPUT->setFocus();
1053 }
1054 }
1055 }
1056 }
1057 else if (e->type() == QEvent::MouseMove && (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport())){
1058 if (!textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos())).isEmpty())
1059 textEdit_CHAT->viewport()->setCursor(Qt::PointingHandCursor);
1060 else
1061 textEdit_CHAT->viewport()->setCursor(Qt::IBeamCursor);
1062 }
1063
1064 return QWidget::eventFilter(obj, e);
1065 }
1066
closeEvent(QCloseEvent * e)1067 void HubFrame::closeEvent(QCloseEvent *e){
1068 Q_D(HubFrame);
1069
1070 blockSignals(true);
1071
1072 QObject::disconnect(this, NULL, this, NULL);
1073
1074 FavoriteManager::getInstance()->removeListener(this);
1075
1076 HubManager::getInstance()->unregisterHubUrl(_q(d->client->getHubUrl()));
1077
1078 d->client->removeListener(this);
1079 d->client->disconnect(true);
1080 ClientManager::getInstance()->putClient(d->client);
1081
1082 save();
1083
1084 for (const auto &it : d->pm){
1085 PMWindow *w = const_cast<PMWindow*>(it);
1086
1087 disconnect(w, SIGNAL(privateMessageClosed(QString)), this, SLOT(slotPMClosed(QString)));
1088
1089 ArenaWidgetManager::getInstance()->rem(w);
1090 }
1091
1092 d->pm.clear();
1093
1094 for (const auto &r : d->shell_list){
1095 r->cancel();
1096 r->exit(0);
1097
1098 r->wait(100);
1099
1100 if (r->isRunning())
1101 r->terminate();
1102
1103 delete r;
1104 }
1105
1106 if (isVisible())
1107 HubManager::getInstance()->setActiveHub(NULL);
1108
1109 setAttribute(Qt::WA_DeleteOnClose);
1110
1111 e->accept();
1112
1113 blockSignals(false);
1114 {
1115 emit closeRequest();
1116 }
1117 blockSignals(true);
1118 }
1119
showEvent(QShowEvent * e)1120 void HubFrame::showEvent(QShowEvent *e){
1121 Q_D(HubFrame);
1122
1123 e->accept();
1124
1125 d->drawLine = false;
1126
1127 HubManager::getInstance()->setActiveHub(this);
1128
1129 d->hasMessages = false;
1130 d->hasHighlightMessages = false;
1131
1132 MainWindow::getInstance()->redrawToolPanel();
1133 }
1134
hideEvent(QHideEvent * e)1135 void HubFrame::hideEvent(QHideEvent *e){
1136 Q_D(HubFrame);
1137
1138 e->accept();
1139
1140 d->drawLine = true;
1141
1142 if (!isVisible())
1143 HubManager::getInstance()->setActiveHub(NULL);
1144 }
1145
init()1146 void HubFrame::init(){
1147 Q_D(HubFrame);
1148
1149 d->model = new UserListModel(this);
1150 d->proxy = NULL;
1151
1152 treeView_USERS->setModel(d->model);
1153 treeView_USERS->setSortingEnabled(true);
1154 treeView_USERS->setItemsExpandable(false);
1155 treeView_USERS->setUniformRowHeights(true);
1156 treeView_USERS->setContextMenuPolicy(Qt::CustomContextMenu);
1157 treeView_USERS->header()->setContextMenuPolicy(Qt::CustomContextMenu);
1158 treeView_USERS->viewport()->installEventFilter(this);
1159
1160 installEventFilter(this);
1161 lineEdit_FILTER->installEventFilter(this);
1162 lineEdit_FIND->installEventFilter(this);
1163
1164 textEdit_CHAT->document()->setMaximumBlockCount(WIGET(WI_CHAT_MAXPARAGRAPHS));
1165 textEdit_CHAT->setContextMenuPolicy(Qt::CustomContextMenu);
1166 textEdit_CHAT->setReadOnly(true);
1167 textEdit_CHAT->setAutoFormatting(QTextEdit::AutoNone);
1168 textEdit_CHAT->viewport()->installEventFilter(this);//QTextEdit don't receive all mouse events
1169 textEdit_CHAT->setMouseTracking(true);
1170
1171 if (WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
1172 EmoticonFactory::getInstance()->addEmoticons(textEdit_CHAT->document());
1173
1174 frame->setVisible(false);
1175
1176 for (int i = 0; i < d->model->columnCount(); i++)
1177 comboBox_COLUMNS->addItem(d->model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
1178
1179 toolButton_SMILE->setVisible(WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance());
1180 toolButton_SMILE->setContextMenuPolicy(Qt::CustomContextMenu);
1181 toolButton_SMILE->setIcon(WICON(WulforUtil::eiEMOTICON));
1182
1183 toolButton_HIDE->setIcon(WICON(WulforUtil::eiEDITDELETE));
1184
1185 frame_SMILES->setLayout(new FlowLayout(frame_SMILES));
1186 frame_SMILES->setVisible(false);
1187
1188 QSize sz;
1189 Q_UNUSED(sz);
1190
1191 if (EmoticonFactory::getInstance())
1192 EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
1193
1194 for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
1195 connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
1196
1197 connect(this, SIGNAL(coreConnecting(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1198 connect(this, SIGNAL(coreConnected(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1199 connect(this, SIGNAL(coreUserUpdated(dcpp::UserPtr,dcpp::Identity)), this, SLOT(userUpdated(dcpp::UserPtr,dcpp::Identity)), Qt::QueuedConnection);
1200 connect(this, SIGNAL(coreUserRemoved(dcpp::UserPtr,dcpp::Identity)), this, SLOT(userRemoved(dcpp::UserPtr,dcpp::Identity)), Qt::QueuedConnection);
1201 connect(this, SIGNAL(coreStatusMsg(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1202 connect(this, SIGNAL(coreFollow(QString)), this, SLOT(follow(QString)), Qt::QueuedConnection);
1203 connect(this, SIGNAL(coreFailed()), this, SLOT(clearUsers()), Qt::QueuedConnection);
1204 connect(this, SIGNAL(corePassword()), this, SLOT(getPassword()), Qt::QueuedConnection);
1205 connect(this, SIGNAL(coreMessage(VarMap)), this, SLOT(newMsg(VarMap)), Qt::QueuedConnection);
1206 connect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)), Qt::QueuedConnection);
1207 connect(this, SIGNAL(coreHubUpdated()), MainWindow::getInstance(), SLOT(redrawToolPanel()), Qt::QueuedConnection);
1208 connect(this, SIGNAL(coreFavoriteUserAdded(QString)), this, SLOT(changeFavStatus(QString)), Qt::QueuedConnection);
1209 connect(this, SIGNAL(coreFavoriteUserRemoved(QString)), this, SLOT(changeFavStatus(QString)), Qt::QueuedConnection);
1210
1211 connect(label_LAST_STATUS, SIGNAL(linkActivated(QString)), this, SLOT(slotStatusLinkOpen(QString)));
1212 connect(treeView_USERS, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotUserListMenu(QPoint)));
1213 connect(treeView_USERS->header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu(QPoint)));
1214 connect(GlobalTimer::getInstance(), SIGNAL(second()), this, SLOT(slotUsersUpdated()));
1215 connect(textEdit_CHAT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotChatMenu(QPoint)));
1216 connect(toolButton_BACK, SIGNAL(clicked()), this, SLOT(slotFindBackward()));
1217 connect(toolButton_FORWARD, SIGNAL(clicked()), this, SLOT(slotFindForward()));
1218 connect(toolButton_HIDE, SIGNAL(clicked()), this, SLOT(slotHideFindFrame()));
1219 connect(lineEdit_FIND, SIGNAL(textEdited(QString)), this, SLOT(slotFindTextEdited(QString)));
1220 connect(lineEdit_FILTER, SIGNAL(textChanged(QString)), this, SLOT(slotFilterTextChanged()));
1221 connect(comboBox_COLUMNS, SIGNAL(activated(int)), this, SLOT(slotFilterTextChanged()));
1222 connect(toolButton_SMILE, SIGNAL(clicked()), this, SLOT(slotSmile()));
1223 connect(toolButton_SMILE, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotSmileContextMenu()));
1224 connect(toolButton_ALL, SIGNAL(clicked()), this, SLOT(slotFindAll()));
1225 connect(WulforSettings::getInstance(), SIGNAL(strValueChanged(QString, QString)), this, SLOT(slotSettingsChanged(QString,QString)));
1226 connect(WulforSettings::getInstance(), SIGNAL(intValueChanged(QString,int)), this, SLOT(slotBoolSettingsChanged(QString,int)));
1227
1228 #ifdef USE_ASPELL
1229 connect(plainTextEdit_INPUT, SIGNAL(textChanged()), this, SLOT(slotInputTextChanged()));
1230 connect(plainTextEdit_INPUT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotInputContextMenu()));
1231
1232 plainTextEdit_INPUT->setContextMenuPolicy(Qt::CustomContextMenu);
1233 #endif
1234
1235 plainTextEdit_INPUT->setWordWrapMode(QTextOption::NoWrap);
1236 plainTextEdit_INPUT->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1237 plainTextEdit_INPUT->installEventFilter(this);
1238 plainTextEdit_INPUT->setAcceptRichText(false);
1239
1240 textEdit_CHAT->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1241 textEdit_CHAT->setTabStopWidth(40);
1242 updateStyles();
1243
1244 load();
1245
1246 d->completer = new QCompleter(this);
1247 d->completer->setMaxVisibleItems(10); // This property was introduced in Qt 4.6.
1248 plainTextEdit_INPUT->setCompleter(d->completer, d->model);
1249
1250 slotSettingsChanged(WS_APP_EMOTICON_THEME, WSGET(WS_APP_EMOTICON_THEME));//toggle emoticon button
1251 }
1252
initMenu()1253 void HubFrame::initMenu(){
1254 Q_D(HubFrame);
1255 WulforUtil *WU = WulforUtil::getInstance();
1256
1257 delete d->arenaMenu;
1258
1259 d->arenaMenu = new QMenu(tr("Hub menu"), this);
1260
1261 QAction *reconnect = new QAction(WU->getPixmap(WulforUtil::eiRECONNECT), tr("Reconnect"), d->arenaMenu);
1262 QAction *show_wnd = new QAction(WU->getPixmap(WulforUtil::eiCHAT), tr("Show widget"), d->arenaMenu);
1263 QAction *addToFav = new QAction(WU->getPixmap(WulforUtil::eiFAVSERVER), tr("Add to Favorites"), d->arenaMenu);
1264 QMenu *copyInfo = new QMenu(tr("Copy"), d->arenaMenu);
1265 QAction *copyIP = copyInfo->addAction(tr("Hub IP"));
1266 QAction *copyURL = copyInfo->addAction(tr("Hub URL"));
1267 QAction *copyTitle = copyInfo->addAction(tr("Hub Title"));
1268
1269 QAction *sep = new QAction(d->arenaMenu);
1270 sep->setSeparator(true);
1271 QAction *close_wnd = new QAction(WU->getPixmap(WulforUtil::eiEXIT), tr("Close"), d->arenaMenu);
1272
1273 d->arenaMenu->addActions(QList<QAction*>() << reconnect
1274 << show_wnd
1275 << addToFav
1276 );
1277
1278 d->arenaMenu->addMenu(copyInfo);
1279
1280 if (d->client && d->client->isConnected()){
1281 QMenu *u_c = WulforUtil::getInstance()->buildUserCmdMenu(d->client->getHubUrl(), UserCommand::CONTEXT_HUB, d->arenaMenu);
1282
1283 if (u_c){
1284 if (!u_c->actions().isEmpty()){
1285 u_c->setTitle(tr("Hub Menu"));
1286
1287 d->arenaMenu->addMenu(u_c);
1288
1289 connect(u_c, SIGNAL(triggered(QAction*)), this, SLOT(slotHubMenu(QAction*)));
1290 }
1291 }
1292 }
1293
1294 d->arenaMenu->addActions(QList<QAction*>() << sep << close_wnd);
1295
1296 connect(reconnect, SIGNAL(triggered()), this, SLOT(slotReconnect()));
1297 connect(show_wnd, SIGNAL(triggered()), this, SLOT(slotShowWnd()));
1298 connect(addToFav, SIGNAL(triggered()), this, SLOT(addAsFavorite()));
1299 connect(copyIP, SIGNAL(triggered()), this, SLOT(slotCopyHubIP()));
1300 connect(copyTitle, SIGNAL(triggered()), this, SLOT(slotCopyHubTitle()));
1301 connect(copyURL, SIGNAL(triggered()), this, SLOT(slotCopyHubURL()));
1302 connect(close_wnd, SIGNAL(triggered()), this, SLOT(slotClose()));
1303 }
1304
1305
save()1306 void HubFrame::save(){
1307 Q_D(HubFrame);
1308
1309 WSSET(WS_CHAT_USERLIST_STATE, treeView_USERS->header()->saveState().toBase64());
1310 WISET(WI_CHAT_WIDTH, textEdit_CHAT->width());
1311 WISET(WI_CHAT_USERLIST_WIDTH, treeView_USERS->width());
1312 WISET(WI_CHAT_SORT_COLUMN, d->model->getSortColumn());
1313 WISET(WI_CHAT_SORT_ORDER, WulforUtil::getInstance()->sortOrderToInt(d->model->getSortOrder()));
1314 WSSET("hubframe/chat-background-color", textEdit_CHAT->palette().color(QPalette::Active, QPalette::Base).name());
1315 }
1316
load()1317 void HubFrame::load(){
1318 int w_chat = WIGET(WI_CHAT_WIDTH), w_ulist = WIGET(WI_CHAT_USERLIST_WIDTH);
1319
1320 QString ustate = WSGET(WS_CHAT_USERLIST_STATE);
1321
1322 if (!ustate.isEmpty())
1323 treeView_USERS->header()->restoreState(QByteArray::fromBase64(ustate.toUtf8()));
1324
1325 if (w_chat >= 0 && w_ulist >= 0){
1326 QList<int> frames;
1327
1328 frames << w_chat << w_ulist;
1329
1330 splitter_2->setSizes(frames);
1331 }
1332
1333 treeView_USERS->sortByColumn(WIGET(WI_CHAT_SORT_COLUMN), WulforUtil::getInstance()->intToSortOrder(WIGET(WI_CHAT_SORT_ORDER)));
1334
1335 reloadSomeSettings();
1336 }
1337
reloadSomeSettings()1338 void HubFrame::reloadSomeSettings(){
1339 label_USERSTATE->setVisible(WBGET(WB_USERS_STATISTICS));
1340
1341 label_LAST_STATUS->setVisible(WBGET(WB_LAST_STATUS));
1342
1343 if (!WSGET("hubframe/chat-background-color", "").isEmpty()){
1344 QPalette p = textEdit_CHAT->palette();
1345 QColor clr = p.color(QPalette::Active, QPalette::Base);
1346
1347 clr.setNamedColor(WSGET("hubframe/chat-background-color"));
1348
1349 if (clr.isValid()){
1350 p.setColor(QPalette::Base, clr);
1351
1352 textEdit_CHAT->setPalette(p);
1353 }
1354 }
1355 }
1356
getWidget()1357 QWidget *HubFrame::getWidget(){
1358 return this;
1359 }
1360
getArenaTitle()1361 QString HubFrame::getArenaTitle(){
1362 Q_D(HubFrame);
1363 QString ret = tr("Not connected");
1364
1365 if (d->client && d->client->isConnected()){
1366 ret = QString("%1 - %2 [%3]").arg(QString(d->client->getHubName().c_str()))
1367 .arg(QString(d->client->getHubDescription().c_str()))
1368 .arg(QString(d->client->getIp().c_str()));
1369 QString prefix = QString("[+%1] ").arg(d->client->isSecure()? ("S") : (d->client->isTrusted()? ("T"): ("")));
1370
1371 ret.prepend(prefix);
1372 }
1373 else if (d->client){
1374 ret = QString("[-] %1").arg(d->client->getHubUrl().c_str());
1375 }
1376
1377 return ret;
1378 }
1379
getCIDforNick(QString nick)1380 QString HubFrame::getCIDforNick(QString nick){
1381 Q_D(HubFrame);
1382
1383 return d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
1384 }
1385
getArenaShortTitle()1386 QString HubFrame::getArenaShortTitle(){
1387 Q_D(HubFrame);
1388 QString ret = tr("Not connected");
1389
1390 if (d->client && d->client->isConnected()){
1391 ret = QString("[+] %1").arg(QString(d->client->getHubName().c_str()));
1392 }
1393 else if (d->client){
1394 ret = QString("[-] %1").arg(d->client->getHubUrl().c_str());
1395 }
1396
1397 return ret;
1398 }
1399
getMenu()1400 QMenu *HubFrame::getMenu(){
1401 initMenu();
1402
1403 Q_D(HubFrame);
1404
1405 return d->arenaMenu;
1406 }
1407
getPixmap()1408 const QPixmap &HubFrame::getPixmap(){
1409 Q_D(HubFrame);
1410
1411 if (d->hasHighlightMessages)
1412 return WICON(WulforUtil::eiMESSAGE);
1413 else if (d->hasMessages)
1414 return WICON(WulforUtil::eiHUBMSG);
1415 else
1416 return WICON(WulforUtil::eiSERVER);
1417 }
1418
clearChat()1419 void HubFrame::clearChat(){
1420 textEdit_CHAT->setHtml("");
1421 addStatus(tr("Chat cleared."));
1422
1423 updateStyles();
1424
1425 if (WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
1426 EmoticonFactory::getInstance()->addEmoticons(textEdit_CHAT->document());
1427 }
1428
disableChat()1429 void HubFrame::disableChat(){
1430 Q_D(HubFrame);
1431
1432 if (!d->chatDisabled){
1433 addStatus(tr("Chat disabled."));
1434
1435 d->chatDisabled = true;
1436 }
1437 else{
1438 d->chatDisabled = false;
1439
1440 addStatus(tr("Chat enabled."));
1441 }
1442
1443 plainTextEdit_INPUT->setEnabled(!d->chatDisabled);
1444 frame_INPUT->setVisible(!d->chatDisabled);
1445 }
1446
getStatistic(quint64 & users,quint64 & share) const1447 void HubFrame::getStatistic(quint64 &users, quint64 &share) const{
1448 Q_D(const HubFrame);
1449
1450 if (d->model)
1451 users = d->model->rowCount();
1452
1453 share = d->total_shared;
1454 }
1455
isConnected() const1456 bool HubFrame::isConnected() const {
1457 Q_D(const HubFrame);
1458
1459 return (d->client? d->client->isConnected() : false);
1460 }
1461
getUserInfo(UserListItem * item)1462 QString HubFrame::getUserInfo(UserListItem *item){
1463 Q_D(HubFrame);
1464 QString ttip = "";
1465
1466 ttip += d->model->headerData(COLUMN_NICK, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getNick() + "\n";
1467 ttip += d->model->headerData(COLUMN_COMMENT, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getComment() + "\n";
1468 ttip += d->model->headerData(COLUMN_EMAIL, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getEmail() + "\n";
1469 ttip += d->model->headerData(COLUMN_IP, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getIP() + "\n";
1470 ttip += d->model->headerData(COLUMN_SHARE, Qt::Horizontal, Qt::DisplayRole).toString() + ": " +
1471 WulforUtil::formatBytes(item->getShare()) + "\n";
1472 ttip += d->model->headerData(COLUMN_TAG, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getTag() + "\n";
1473 ttip += d->model->headerData(COLUMN_CONN, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getConnection() + "\n";
1474
1475 if (item->isOP())
1476 ttip += tr("Hub role: Operator");
1477 else
1478 ttip += tr("Hub role: User");
1479
1480 if (item->isFav())
1481 ttip += tr("\nFavorite user");
1482
1483 return ttip;
1484 }
1485
sendMsg(const QString & msg)1486 void HubFrame::sendMsg(const QString &msg){
1487 sendChat(msg, false, false);
1488 }
1489
sendChat(QString msg,bool thirdPerson,bool stripNewLines)1490 void HubFrame::sendChat(QString msg, bool thirdPerson, bool stripNewLines){
1491 Q_D(HubFrame);
1492
1493 if (!d->client || !d->client->isConnected() || msg.isEmpty() || msg.isNull())
1494 return;
1495
1496 if (stripNewLines)
1497 msg.replace("\n", "");
1498
1499 if (msg.trimmed().isEmpty())
1500 return;
1501
1502 if (msg.endsWith("\n"))
1503 msg = msg.left(msg.lastIndexOf("\n"));
1504
1505 bool script_ret = false;
1506 #ifdef LUA_SCRIPT
1507 script_ret = ((ClientScriptInstance *) (d->client))->onHubFrameEnter(d->client, msg.toStdString());
1508 #endif
1509 if (!script_ret && !parseForCmd(msg, this))
1510 d->client->hubMessage(msg.toStdString(), thirdPerson);
1511
1512 //qDebug() << "cmd: " << cmd <<" sript_ret: " << script_ret;
1513 if (!thirdPerson){
1514 if (d->out_messages_unsent){
1515 d->out_messages.removeLast();
1516 d->out_messages_unsent = false;
1517 }
1518
1519 d->out_messages << msg;
1520
1521 if (d->out_messages.size() > WIGET(WI_OUT_IN_HIST))
1522 d->out_messages.removeFirst();
1523
1524 d->out_messages_index = d->out_messages.size()-1;
1525 }
1526 }
1527
parseForCmd(QString line,QWidget * wg)1528 bool HubFrame::parseForCmd(QString line, QWidget *wg){
1529 HubFrame *fr = qobject_cast<HubFrame *>(wg);
1530 PMWindow *pm = qobject_cast<PMWindow *>(wg);
1531 Q_D(HubFrame);
1532
1533 QStringList list = line.split(" ", QString::SkipEmptyParts);
1534
1535 if (list.isEmpty())
1536 return false;
1537
1538 if (!line.startsWith("/"))
1539 return false;
1540
1541 QString cmd = list.at(0);
1542 QString param = "";
1543 bool emptyParam = true;
1544
1545 if (list.size() > 1){
1546 param = list.at(1);
1547 emptyParam = false;
1548 }
1549
1550 if (cmd == "/away"){
1551 if (Util::getAway() && emptyParam){
1552 Util::setAway(false);
1553 Util::setManualAway(false);
1554
1555 if (fr == this)
1556 addStatus(tr("Away mode off"));
1557 else if (pm)
1558 pm->addStatus(tr("Away mode off"));
1559 }
1560 else {
1561 Util::setAway(true);
1562 Util::setManualAway(true);
1563
1564 if (!emptyParam){
1565 line.remove(0, 6);
1566 Util::setAwayMessage(line.toStdString());
1567 }
1568
1569 if (fr == this)
1570 addStatus(tr("Away mode on: ") + QString::fromStdString(Util::getAwayMessage()));
1571 else if (pm)
1572 pm->addStatus(tr("Away mode on: ") + QString::fromStdString(Util::getAwayMessage()));
1573 }
1574 }
1575 else if (cmd == "/alias" && !emptyParam){
1576 QStringList lex = line.split(" ", QString::SkipEmptyParts);
1577
1578 if (lex.size() >= 2){
1579 QString aliases = QByteArray::fromBase64(WSGET(WS_CHAT_CMD_ALIASES).toUtf8());
1580
1581 if (lex.at(1) == "list"){
1582 if (!aliases.isEmpty()){
1583 if (fr == this)
1584 addStatus("\n"+aliases);
1585 else if (pm)
1586 pm->addStatus("\n"+aliases);
1587 }
1588 else{
1589 if (fr == this)
1590 addStatus(tr("Aliases not found."));
1591 else if (pm)
1592 pm->addStatus(tr("Aliases not found."));
1593 }
1594 }
1595 else if (lex.at(1) == "purge" && lex.size() == 3){
1596 QString alias = lex.at(2);
1597 QStringList alias_list = aliases.split('\n', QString::SkipEmptyParts);
1598
1599 for (const auto &line : alias_list){
1600 QStringList cmds = line.split('\t', QString::SkipEmptyParts);
1601
1602 if (cmds.size() == 2 && alias == cmds.at(0)){
1603 alias_list.removeAt(alias_list.indexOf(line));
1604
1605 QString new_aliases;
1606 for (const auto &line : alias_list)
1607 new_aliases += line + "\n";
1608
1609 WSSET(WS_CHAT_CMD_ALIASES, new_aliases.toUtf8().toBase64());
1610
1611 if (fr == this)
1612 addStatus(tr("Alias removed."));
1613 else if (pm)
1614 pm->addStatus(tr("Alias removed."));
1615 }
1616 }
1617 }
1618 else if (lex.size() >= 2){
1619 QString raw = line;
1620
1621 raw.remove(0, raw.indexOf(" ")+1);
1622
1623 if (raw.indexOf("::") <= 0){
1624 if (fr == this)
1625 addStatus(tr("Invalid alias syntax."));
1626 else if (pm)
1627 pm->addStatus(tr("Invalid alias syntax."));
1628 }
1629 else {
1630 QStringList new_cmd = raw.split("::", QString::SkipEmptyParts);
1631
1632 if (new_cmd.size() < 2 || new_cmd.at(1).isEmpty()){
1633 if (fr == this)
1634 addStatus(tr("Invalid alias syntax."));
1635 else if (pm)
1636 pm->addStatus(tr("Invalid alias syntax."));
1637 }
1638 else if (!aliases.contains(new_cmd.at(0)+'\t')){
1639 aliases += new_cmd.at(0) + '\t' + new_cmd.at(1) + '\n';
1640
1641 WSSET(WS_CHAT_CMD_ALIASES, aliases.toUtf8().toBase64());
1642
1643 if (fr == this)
1644 addStatus(tr("Alias %1 => %2 has been added").arg(new_cmd.at(0)).arg(new_cmd.at(1)));
1645 else if (pm)
1646 pm->addStatus(tr("Alias %1 => %2 has been added").arg(new_cmd.at(0)).arg(new_cmd.at(1)));
1647 }
1648 }
1649 }
1650 }
1651 }
1652 else if (cmd == "/kword" && !emptyParam){
1653 if (list.size() < 2 || list.size() > 3)
1654 return false;
1655
1656 enum { List=0, Add, Remove };
1657
1658 int kw_action = List;
1659
1660 if (list.at(1) == QString("add"))
1661 kw_action = Add;
1662 else if (list.at(1) == QString("purge"))
1663 kw_action = Remove;
1664 else if (list.at(1) == QString("list"))
1665 kw_action = List;
1666 else {
1667 if (fr == this)
1668 addStatus(tr("Invalid command syntax."));
1669 else if (pm)
1670 pm->addStatus(tr("Invalid command syntax."));
1671
1672 return false;
1673 }
1674
1675 if (kw_action != List && list.size() != 3){
1676 if (fr == this)
1677 addStatus(tr("Invalid command syntax."));
1678 else if (pm)
1679 pm->addStatus(tr("Invalid command syntax."));
1680
1681 return false;
1682 }
1683
1684 QStringList kwords = WVGET("hubframe/chat-keywords", QStringList()).toStringList();
1685
1686 switch (kw_action){
1687 case List:
1688 {
1689 QString str = tr("List of keywords:\n");
1690
1691 for (const auto &s : kwords)
1692 str += "\t" + s + "\n";
1693
1694 if (fr == this)
1695 addStatus(str);
1696 else if (pm)
1697 pm->addStatus(str);;
1698
1699 break;
1700 }
1701 case Remove:
1702 {
1703 QString kword = list.last();
1704
1705 if (kwords.contains(kword))
1706 kwords.removeOne(kword);
1707
1708 break;
1709 }
1710 case Add:
1711 {
1712 QString kword = list.last();
1713
1714 if (!kwords.contains(kword))
1715 kwords.push_back(kword);
1716
1717 break;
1718 }
1719 default:
1720 break;
1721 }
1722
1723 WVSET("hubframe/chat-keywords", kwords);
1724 }
1725 else if (cmd == "/ratio"){
1726 double ratio;
1727 double up = static_cast<double>(SETTING(TOTAL_UPLOAD));
1728 double down = static_cast<double>(SETTING(TOTAL_DOWNLOAD));
1729
1730
1731 if (down > 0)
1732 ratio = up / down;
1733 else
1734 ratio = 0;
1735
1736 QString line = tr("ratio: %1 (uploads: %2, downloads: %3)")
1737 .arg(QString().setNum(ratio, 'f', 3))
1738 .arg(WulforUtil::formatBytes(up))
1739 .arg(WulforUtil::formatBytes(down));
1740
1741 if (param.trimmed() == "show"){
1742 if (fr == this)
1743 sendChat(line, true, false);
1744 else if (pm)
1745 pm->sendMessage(line, true, false);
1746 } else if (emptyParam){
1747 if (fr == this)
1748 addStatus(line);
1749 else if (pm)
1750 pm->addStatus(line);
1751 }
1752 }
1753 else if (cmd == "/rebuild") {
1754 HashManager::getInstance()->rebuild();
1755 }
1756 else if (cmd == "/refresh") {
1757 ShareManager::getInstance()->setDirty();
1758 ShareManager::getInstance()->refresh(true);
1759 }
1760 #ifdef USE_ASPELL
1761 else if (cmd == "/aspell" && !emptyParam){
1762 WBSET(WB_APP_ENABLE_ASPELL, param.trimmed() == "on");
1763
1764 if (WBGET(WB_APP_ENABLE_ASPELL) && !SpellCheck::getInstance())
1765 SpellCheck::newInstance();
1766 else if (SpellCheck::getInstance())
1767 SpellCheck::deleteInstance();
1768
1769 if (fr == this)
1770 addStatus(tr("Aspell switched %1").arg((WBGET(WB_APP_ENABLE_ASPELL)? tr("on") : tr("off"))) );
1771 else if (pm)
1772 pm->addStatus(tr("Aspell switched %1").arg((WBGET(WB_APP_ENABLE_ASPELL)? tr("on") : tr("off"))) );
1773 }
1774 #endif
1775 else if (cmd == "/back"){
1776 Util::setAway(false);
1777
1778 if (fr == this)
1779 addStatus(tr("Away mode off"));
1780 else if (pm)
1781 pm->addStatus(tr("Away mode off"));
1782 }
1783 else if (cmd == "/clear"){
1784 textEdit_CHAT->setHtml("");
1785
1786 if (fr == this)
1787 addStatus(tr("Chat has been cleared"));
1788 else if (pm)
1789 pm->addStatus(tr("Chat has been cleared"));
1790
1791 }
1792 else if (cmd == "/close"){
1793 this->close();
1794 }
1795 else if (cmd == "/fav"){
1796 addAsFavorite();
1797 }
1798 else if (cmd == "/browse" && !emptyParam){
1799 browseUserFiles(d->model->CIDforNick(param, _q(d->client->getHubUrl())), false);
1800 }
1801 else if (cmd == "/grant" && !emptyParam){
1802 grantSlot(d->model->CIDforNick(param, _q(d->client->getHubUrl())));
1803 }
1804 else if (cmd == "/magnet" && !emptyParam){
1805 WISET(WI_DEF_MAGNET_ACTION, param.toInt());
1806 }
1807 else if (cmd == "/info" && !emptyParam){
1808 UserListItem *item = d->model->itemForNick(param, _q(d->client->getHubUrl()));
1809
1810 if (item){
1811 QString ttip = "\n" + getUserInfo(item);
1812
1813 if (fr == this)
1814 addStatus(ttip);
1815 else if (pm)
1816 pm->addStatus(ttip);
1817 }
1818 }
1819 else if (cmd == "/me" && !emptyParam){
1820 if (line.endsWith("\n"))//remove extra \n char
1821 line = line.left(line.lastIndexOf("\n"));
1822
1823 // This is temporary. It is need to check ClientManager::privateMessage(...) function
1824 // in dcpp kernel with version > 0.75. And "if (fr == this)" will not be needed.
1825 if (fr == this)
1826 line.remove(0, 4);
1827
1828 if (fr == this)
1829 sendChat(line, true, false);
1830 else if (pm)
1831 pm->sendMessage(line, true, false);
1832 }
1833 else if (cmd == "/pm" && !emptyParam){
1834 addPM(d->model->CIDforNick(param, _q(d->client->getHubUrl())), "", false);
1835 }
1836 else if (cmd == "/help" || cmd == "/?" || cmd == "/h"){
1837 QString out = "\n";
1838 #ifdef USE_ASPELL
1839 out += tr("/aspell on/off - enable/disable spell checking\n");
1840 #endif
1841 out += tr("/alias <ALIAS_NAME>::<COMMAND> - make alias /ALIAS_NAME to /COMMAND\n");
1842 out += tr("/alias purge <ALIAS_NAME> - remove alias\n");
1843 out += tr("/alias list - list all aliases\n");
1844 out += tr("/away <message> - set away-mode on/off\n");
1845 out += tr("/back - set away-mode off\n");
1846 out += tr("/browse <nick> - browse user files\n");
1847 out += tr("/clear - clear chat window\n");
1848 out += tr("/kword add <keyword> - add user-defined keyword which will be highlighted in the chat\n");
1849 out += tr("/kword purge <keyword> - remove user-defined keyword\n");
1850 out += tr("/kword list - full list of keywords which will be highlighted in the chat\n");
1851 out += tr("/magnet - default action with magnet (0-ask, 1-search, 2-download)\n");
1852 out += tr("/close - close this hub\n");
1853 out += tr("/fav - add this hub to favorites\n");
1854 out += tr("/grant <nick> - grant extra slot to user\n");
1855 out += tr("/help, /?, /h - show this help\n");
1856 out += tr("/info <nick> - show info about user\n");
1857 out += tr("/ratio [show] - show ratio [send in chat]\n");
1858 out += tr("/rebuild - rebuild hash\n");
1859 out += tr("/refresh - update own file list\n");
1860 out += tr("/me - say a third person\n");
1861 out += tr("/pm <nick> - begin private chat with user\n");
1862 out += tr("/ws param value - set gui option param in value (without value return current value of option)\n");
1863 out += tr("/dcpps param value - set core option param in value (without value return current value of option)\n");
1864 #ifdef LUA_SCRIPT
1865 out += tr("/luafile <file> - load Lua file\n");
1866 out += tr("/lua <chunk> - execute Lua chunk\n");
1867 #endif
1868 out = out.trimmed();
1869
1870 if (fr == this)
1871 addStatus(out);
1872 else if (pm)
1873 pm->addStatus(out);
1874 }
1875 #ifdef LUA_SCRIPT
1876 else if (cmd == "/lua" && !emptyParam) {
1877 ScriptManager::getInstance()->EvaluateChunk(Text::fromT(_tq(param)));
1878 }
1879 else if( cmd == "/luafile" && !emptyParam) {
1880 ScriptManager::getInstance()->EvaluateFile(Text::fromT(_tq(param)));
1881 }
1882 #endif
1883 else if (cmd == "/sh" && !emptyParam){
1884 if (line.endsWith("\n"))//remove extra \n char
1885 line = line.left(line.lastIndexOf("\n"));
1886
1887 line = line.remove(0, 4);
1888
1889 ShellCommandRunner *sh = new ShellCommandRunner(line, wg);
1890 connect(sh, SIGNAL(finished(bool,QString)), this, SLOT(slotShellFinished(bool,QString)));
1891
1892 d->shell_list.append(sh);
1893
1894 sh->start();
1895 }
1896 else if (cmd == "/ws" && !emptyParam){
1897 line = line.remove(0, 4);
1898 line.replace("\n", "");
1899 QString res;
1900 WSCMD(line,res);
1901 if (fr == this)
1902 addStatus(res);
1903 else if (pm)
1904 pm->addStatus(res);
1905 }
1906 else if (cmd == "/dcpps" && !emptyParam) {
1907 line = line.remove(0,7);
1908 QString out = _q(SettingsManager::getInstance()->parseCoreCmd (_tq(line)));
1909 if (fr == this)
1910 addStatus(out);
1911 else if (pm)
1912 pm->addStatus(out);
1913 }
1914 else if (!WSGET(WS_CHAT_CMD_ALIASES).isEmpty()){
1915 QString aliases = QByteArray::fromBase64(WSGET(WS_CHAT_CMD_ALIASES).toUtf8());
1916 QStringList alias_list = aliases.split('\n', QString::SkipEmptyParts);
1917 bool ok = false;
1918
1919 for (const auto &line : alias_list){
1920 QStringList cmds = line.split('\t', QString::SkipEmptyParts);
1921
1922 if (cmds.size() == 2 && cmd == ("/" + cmds.at(0))){
1923 parseForCmd(cmds.at(1), wg);
1924
1925 ok = true;
1926 }
1927 }
1928
1929 if (!ok)
1930 return ok;
1931 }
1932 else
1933 return false;
1934
1935 return true;
1936 }
1937
getHubUrl()1938 QString HubFrame::getHubUrl() {
1939 Q_D(HubFrame);
1940
1941 if (d->client)
1942 return _q(d->client->getHubUrl());
1943
1944 return "";
1945 }
1946
getHubName()1947 QString HubFrame::getHubName() {
1948 Q_D(HubFrame);
1949
1950 if (d->client)
1951 return _q(d->client->getHubName());
1952
1953 return "";
1954 }
1955
getMyNick()1956 QString HubFrame::getMyNick() {
1957 Q_D(HubFrame);
1958
1959 if (d->client)
1960 return _q(d->client->getMyNick());
1961
1962 return "";
1963 }
1964
addStatus(QString msg)1965 void HubFrame::addStatus(QString msg){
1966 Q_D(HubFrame);
1967
1968 if (d->chatDisabled)
1969 return;
1970
1971 QString pure_msg;
1972 QString short_msg = "";
1973 QString status = "";
1974 QString nick = " * ";
1975
1976 QStringList lines = msg.split(QRegExp("[\\n\\r\\f]+"), QString::SkipEmptyParts);
1977 for (int i = 0; i < lines.size(); ++i) {
1978 if (lines.at(i).contains(QRegExp("\\w+"))) {
1979 short_msg = lines.at(i);
1980 break;
1981 }
1982 }
1983 if (short_msg.isEmpty() && !lines.isEmpty())
1984 short_msg = lines.first();
1985
1986 pure_msg = LinkParser::parseForLinks(msg.left(WIGET(WI_CHAT_STATUS_MSG_MAX_LEN)), false);
1987 short_msg = LinkParser::parseForLinks(short_msg, false);
1988 msg = LinkParser::parseForLinks(msg, true);
1989
1990 pure_msg = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + pure_msg + "</font>";
1991 short_msg = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + short_msg + "</font>";
1992 msg = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + msg + "</font>";
1993 QString time = "";
1994
1995 if (!WSGET(WS_CHAT_TIMESTAMP).isEmpty())
1996 time = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP)) + "]</font>";
1997
1998 status = time + "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\"><b>" + nick + "</b> </font>";
1999
2000 QRegExp rot_msg = QRegExp("is(\\s+)kicking(\\s+)(\\S+)*(\\s+)because:");
2001
2002 bool isRotating = (msg.indexOf("is kicking because:") >= 0) || (rot_msg.indexIn(msg) >= 0);
2003
2004 if (!(isRotating && WBGET(WB_CHAT_ROTATING_MSGS)))
2005 addOutput(status + msg);
2006
2007 label_LAST_STATUS->setText(status + short_msg);
2008
2009 status += pure_msg;
2010 WulforUtil::getInstance()->textToHtml(status, false);
2011
2012 d->status_msg_history.push_back(status);
2013
2014 if (WIGET(WI_CHAT_STATUS_HISTORY_SZ) > 0){
2015 while (d->status_msg_history.size() > WIGET(WI_CHAT_STATUS_HISTORY_SZ))
2016 d->status_msg_history.removeFirst();
2017 }
2018 else
2019 d->status_msg_history.clear();
2020
2021 label_LAST_STATUS->setToolTip(d->status_msg_history.join("<br/>"));
2022 }
2023
addOutput(QString msg)2024 void HubFrame::addOutput(QString msg){
2025 msg.replace("\r", "");
2026 msg = "<pre>" + msg + "</pre>";
2027 textEdit_CHAT->append(msg);
2028 }
2029
addPM(QString cid,QString output,bool keepfocus)2030 void HubFrame::addPM(QString cid, QString output, bool keepfocus){
2031 Q_D(HubFrame);
2032 bool redirectToMainChat = WBGET("hubframe/redirect-pm-to-main-chat", true);
2033
2034 if (!d->pm.contains(cid)){
2035 PMWindow *p = ArenaWidgetFactory().create<PMWindow, QString, QString>(cid, _q(d->client->getHubUrl()));
2036 p->textEdit_CHAT->setContextMenuPolicy(Qt::CustomContextMenu);
2037
2038 connect(p, SIGNAL(privateMessageClosed(QString)), this, SLOT(slotPMClosed(QString)));
2039 connect(p, SIGNAL(inputTextChanged()), this, SLOT(slotInputTextChanged()));
2040 connect(p, SIGNAL(inputTextMenu()), this, SLOT(slotInputContextMenu()));
2041 connect(p->textEdit_CHAT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotChatMenu(QPoint)));
2042
2043 p->setCompleter(d->completer, d->model);
2044 p->addOutput(output);
2045 p->setAttribute(Qt::WA_DeleteOnClose);
2046
2047 if (!(keepfocus && WBGET(WB_CHAT_KEEPFOCUS))){
2048 ArenaWidgetManager::getInstance()->activate(p);
2049
2050 p->requestFocus();
2051 }
2052
2053 d->pm.insert(cid, p);
2054
2055 if (!p->isVisible() && redirectToMainChat)
2056 addOutput("<b>PM: </b>" + output);
2057 }
2058 else{
2059 auto it = d->pm.find(cid);
2060
2061 if (output.indexOf(_q(d->client->getMyNick())) >= 0)
2062 it.value()->setHasHighlightMessages(true);
2063
2064 it.value()->addOutput(output);
2065
2066 if (!(keepfocus && WBGET(WB_CHAT_KEEPFOCUS))){
2067 ArenaWidgetManager::getInstance()->activate(it.value());
2068
2069 it.value()->requestFocus();
2070 }
2071
2072 if (! it.value()->isVisible() && redirectToMainChat)
2073 addOutput("<b>PM: </b>" + output);
2074 }
2075 }
2076
isOP(const QString & nick)2077 bool HubFrame::isOP(const QString& nick) {
2078 Q_D(HubFrame);
2079
2080 UserListItem *item = d->model->itemForNick(nick, _q(d->client->getHubUrl()));
2081
2082 return (item? item->isOP() : false);
2083 }
2084
2085
userUpdated(const UserPtr & user,const dcpp::Identity & id)2086 void HubFrame::userUpdated(const UserPtr &user, const dcpp::Identity &id){
2087 Q_D(HubFrame);
2088
2089 static WulforSettings *WS = WulforSettings::getInstance();
2090 static bool showFavJoinsOnly = WS->getBool(WB_CHAT_SHOW_JOINS_FAV);
2091 static bool showJoins = WS->getBool(WB_CHAT_SHOW_JOINS);
2092 const bool isFavorite = FavoriteManager::getInstance()->isFavoriteUser(user);
2093
2094 if (!d->model)
2095 return;
2096
2097 UserListItem *item = d->model->itemForPtr(user);
2098
2099 QString cid = _q(user->getCID().toBase32());
2100
2101 if (item){
2102 d->total_shared -= item->getShare();
2103
2104 d->model->updateUser(item, id, cid, isFavorite);
2105 }
2106 else{
2107 item = d->model->addUser(user, id, cid, isFavorite);
2108
2109 QString nick = item->getNick();
2110
2111 if (showJoins){
2112 do {
2113 if (showFavJoinsOnly && !isFavorite)
2114 break;
2115
2116 addStatus(nick + tr(" joins the chat"));
2117 } while (0);
2118 }
2119
2120 if (isFavorite)
2121 Notification::getInstance()->showMessage(Notification::FAVORITE, tr("Favorites"), tr("%1 is now online").arg(nick));
2122
2123 if (d->pm.contains(nick)){
2124 PMWindow *wnd = d->pm[nick];
2125
2126 wnd->cid = cid;
2127 wnd->plainTextEdit_INPUT->setEnabled(true);
2128 wnd->hubUrl = _q(d->client->getHubUrl());
2129
2130 d->pm.insert(cid, wnd);
2131
2132 d->pm.remove(nick);
2133
2134 pmUserEvent(cid, tr("User online."));
2135 }
2136 }
2137
2138 d->total_shared += qlonglong(id.getBytesShared());
2139 }
2140
userRemoved(const UserPtr & user,const dcpp::Identity & id)2141 void HubFrame::userRemoved(const UserPtr &user, const dcpp::Identity &id){
2142 Q_D(HubFrame);
2143
2144 UserListItem *item = d->model->itemForPtr(user);
2145
2146 if (!item)
2147 return;
2148
2149 d->total_shared -= item->getShare();
2150
2151 QString cid = item->getCID();
2152 QString nick = item->getNick();
2153
2154 if (d->pm.contains(cid)){
2155 pmUserOffline(cid);
2156
2157 PMWindow *pmw = d->pm[cid];
2158
2159 d->pm.insert(nick, pmw);
2160
2161 pmw->cid = nick;
2162 pmw->plainTextEdit_INPUT->setEnabled(false);//we need interface function
2163
2164 d->pm.remove(cid);
2165 }
2166
2167 if (WulforSettings::getInstance()->getBool(WB_CHAT_SHOW_JOINS)){
2168 do {
2169 if (WulforSettings::getInstance()->getBool(WB_CHAT_SHOW_JOINS_FAV) &&
2170 !FavoriteManager::getInstance()->isFavoriteUser(user))
2171 break;
2172
2173 addStatus(nick + tr(" left the chat"));
2174 } while (0);
2175 }
2176
2177 if (FavoriteManager::getInstance()->isFavoriteUser(user))
2178 Notification::getInstance()->showMessage(Notification::FAVORITE, tr("Favorites"), tr("%1 is now offline").arg(nick));
2179
2180 d->model->removeUser(user);
2181 }
2182
2183
getParams(HubFrame::VarMap & map,const Identity & id)2184 void HubFrame::getParams(HubFrame::VarMap &map, const Identity &id){
2185 map["NICK"] = _q(id.getNick());
2186 map["SHARE"] = qlonglong(id.getBytesShared());
2187 map["COMM"] = _q(id.getDescription());
2188 map["TAG"] = _q(id.getTag());
2189 map["CONN"] = _q(id.getConnection());
2190 map["IP"] = _q(id.getIp());
2191 map["EMAIL"] = _q(id.getEmail());
2192 map["ISOP"] = id.isOp();
2193 map["SPEED"] = QString::fromStdString(id.getConnection());
2194 map["AWAY"] = id.isAway();
2195 map["CID"] = _q(id.getUser()->getCID().toBase32());
2196 }
2197
browseUserFiles(const QString & id,bool match)2198 void HubFrame::browseUserFiles(const QString& id, bool match){
2199 string message;
2200 string cid = id.toStdString();
2201
2202 if (!cid.empty()){
2203 try{
2204 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2205
2206 if (user){
2207 Q_D(HubFrame);
2208
2209 if (user == ClientManager::getInstance()->getMe())
2210 MainWindow::getInstance()->browseOwnFiles();
2211 else if (match)
2212 QueueManager::getInstance()->addList(HintedUser(user, d->client->getHubUrl()), QueueItem::FLAG_MATCH_QUEUE, "");
2213 else
2214 QueueManager::getInstance()->addList(HintedUser(user, d->client->getHubUrl()), QueueItem::FLAG_CLIENT_VIEW, "");
2215 }
2216 else {
2217 message = QString(tr("User not found")).toStdString();
2218 }
2219 }
2220 catch (const Exception &e){
2221 message = e.getError();
2222
2223 LogManager::getInstance()->message(message);
2224 }
2225 }
2226 }
2227
grantSlot(const QString & id)2228 void HubFrame::grantSlot(const QString& id){
2229 Q_D(HubFrame);
2230
2231 QString message = tr("User not found");
2232
2233 if (!id.isEmpty()){
2234 UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2235
2236 if (user){
2237 UploadManager::getInstance()->reserveSlot(HintedUser(user, d->client->getHubUrl()));
2238 message = tr("Slot granted to ") + WulforUtil::getInstance()->getNicks(user->getCID(), _q(d->client->getHubUrl()));
2239 }
2240 }
2241
2242 MainWindow::getInstance()->setStatusMessage(message);
2243 }
2244
addUserToFav(const QString & id)2245 void HubFrame::addUserToFav(const QString& id){
2246 if (id.isEmpty())
2247 return;
2248
2249 string cid = id.toStdString();
2250
2251 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2252
2253 if (user){
2254 if (user != ClientManager::getInstance()->getMe() && !FavoriteManager::getInstance()->isFavoriteUser(user))
2255 FavoriteManager::getInstance()->addFavoriteUser(user);
2256 }
2257 }
2258
delUserFromFav(const QString & id)2259 void HubFrame::delUserFromFav(const QString& id){
2260 if (id.isEmpty())
2261 return;
2262
2263 string cid = id.toStdString();
2264
2265 UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2266
2267 if (user){
2268 if (user != ClientManager::getInstance()->getMe() && FavoriteManager::getInstance()->isFavoriteUser(user))
2269 FavoriteManager::getInstance()->removeFavoriteUser(user);
2270 }
2271 }
2272
changeFavStatus(const QString & id)2273 void HubFrame::changeFavStatus(const QString &id) {
2274 if (id.isEmpty())
2275 return;
2276
2277 UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2278
2279 if (user) {
2280 Q_D(HubFrame);
2281
2282 UserListItem *item = NULL;
2283
2284 if (d->model)
2285 item = d->model->itemForPtr(user);
2286
2287 bool bFav = FavoriteManager::getInstance()->isFavoriteUser(user);
2288
2289 if (item) {
2290 QModelIndex ixb = d->model->index(item->row(), COLUMN_NICK);
2291 QModelIndex ixe = d->model->index(item->row(), COLUMN_EMAIL);
2292
2293 d->model->repaintData(ixb, ixe);
2294 }
2295
2296 QString message = WulforUtil::getInstance()->getNicks(id, _q(d->client->getHubUrl())) +
2297 (bFav ? tr(" has been added to favorites.") : tr(" has been removed from favorites."));
2298
2299 MainWindow::getInstance()->setStatusMessage(message);
2300 }
2301 }
2302
delUserFromQueue(const QString & id)2303 void HubFrame::delUserFromQueue(const QString& id){
2304 if (!id.isEmpty()){
2305 UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2306
2307 if (user)
2308 QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
2309 }
2310 }
2311
addAsFavorite()2312 void HubFrame::addAsFavorite(){
2313 Q_D(HubFrame);
2314 FavoriteHubEntry *existingHub = FavoriteManager::getInstance()->getFavoriteHubEntry(d->client->getHubUrl());
2315
2316 if (!existingHub){
2317 FavoriteHubEntry aEntry;
2318
2319 aEntry.setServer(d->client->getHubUrl());
2320 aEntry.setName(d->client->getHubName());
2321 aEntry.setDescription(d->client->getHubDescription());
2322 aEntry.setConnect(false);
2323 aEntry.setNick(d->client->getMyNick());
2324 aEntry.setEncoding(d->client->getEncoding());
2325
2326 FavoriteManager::getInstance()->addFavorite(aEntry);
2327 FavoriteManager::getInstance()->save();
2328
2329 addStatus(tr("Favorite hub added."));
2330 }
2331 else{
2332 addStatus(tr("Favorite hub already exists."));
2333 }
2334 }
2335
disablePrivateMessages(bool disable)2336 void HubFrame::disablePrivateMessages(bool disable) {
2337 if (disable)
2338 disconnect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)));
2339 else
2340 connect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)), Qt::QueuedConnection);
2341 }
2342
2343
newMsg(const VarMap & map)2344 void HubFrame::newMsg(const VarMap &map){
2345 Q_D(HubFrame);
2346 QString output = "";
2347
2348 QString nick = map["NICK"].toString();
2349 QString message = map["MSG"].toString();
2350 QString time = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + map["TIME"].toString() + "]</font>";;
2351 QString color = map["CLR"].toString();
2352 QString msg_color = WS_CHAT_MSG_COLOR;
2353 QString trigger = "";
2354
2355 const QStringList &kwords = WVGET("hubframe/chat-keywords", QStringList()).toStringList();
2356
2357 for (const auto &word : kwords){
2358 if (message.contains(word, Qt::CaseInsensitive)){
2359 msg_color = WS_CHAT_SAY_NICK;
2360 trigger = word;
2361
2362 break;
2363 }
2364 }
2365
2366 if (message.indexOf(_q(d->client->getMyNick())) >= 0){
2367 msg_color = WS_CHAT_SAY_NICK;
2368 trigger = _q(d->client->getMyNick());
2369
2370 Notification::getInstance()->showMessage(Notification::NICKSAY, getArenaTitle().left(20), nick + ": " + message);
2371 }
2372
2373 emit new_msg(map);
2374
2375 if (msg_color == WS_CHAT_SAY_NICK){
2376 VarMap tmap = map;
2377 tmap["TRIGGER"] = trigger;
2378
2379 emit highlighted(tmap);
2380 }
2381
2382 bool third = map["3RD"].toBool();
2383
2384 QString nicktoout = third? ("* " + nick + " ") : ("<" + nick + "> ");
2385
2386 message = LinkParser::parseForLinks(message, true);
2387
2388 WulforUtil::getInstance()->textToHtml(nicktoout, true);
2389
2390 message = "<font color=\"" + WSGET(msg_color) + "\">" + message + "</font>";
2391
2392 output += time;
2393 string info= Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
2394
2395 if (!info.empty())
2396 output += " <font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">" + _q(info) + "</font>";
2397
2398 output += QString(" <a style=\"text-decoration:none\" href=\"user://%1\"><font color=\"%2\"><b>%3</b></font></a>")
2399 .arg(nicktoout).arg(WSGET(color)).arg(nicktoout.replace("\"", """));
2400 output += message;
2401
2402 if (!isVisible()){
2403 if (msg_color == WS_CHAT_SAY_NICK)
2404 d->hasHighlightMessages = true;
2405
2406 d->hasMessages = true;
2407
2408 MainWindow::getInstance()->redrawToolPanel();
2409 }
2410
2411 QTextDocument *chatDoc = textEdit_CHAT->document();
2412
2413 if (d->drawLine && WBGET("hubframe/unreaden-draw-line", true)){
2414 QString hr = "<hr />";
2415
2416 int scrollbarValue = textEdit_CHAT->verticalScrollBar()->value();
2417
2418 for (QTextBlock it = chatDoc->begin(); it != chatDoc->end(); it = it.next()){
2419 if (it.userState() == 1){
2420 if (it.text().isEmpty()){ // additional check that it is not message
2421 QTextCursor c(it);
2422 c.select(QTextCursor::BlockUnderCursor);
2423 c.deleteChar(); // delete string with horizontal line
2424
2425 if (scrollbarValue > textEdit_CHAT->verticalScrollBar()->maximum())
2426 scrollbarValue = textEdit_CHAT->verticalScrollBar()->maximum();
2427
2428 textEdit_CHAT->verticalScrollBar()->setValue(scrollbarValue);
2429
2430 break;
2431 }
2432 }
2433 }
2434
2435 d->drawLine = false;
2436
2437 chatDoc->lastBlock().setUserState(0); // add label for the last of the old messages
2438
2439 output.prepend(hr);
2440
2441 addOutput(output);
2442
2443 for (QTextBlock itu = chatDoc->lastBlock(); itu != chatDoc->begin(); itu = itu.previous()){
2444 if (!itu.userData())
2445 itu.setUserData(new UserListUserData(nick));
2446 else
2447 break;
2448 }
2449
2450 for (QTextBlock it = chatDoc->begin(); it != chatDoc->end(); it = it.next()){
2451 if(!it.userState()){
2452 it.setUserState(-1); // delete label for the last of the old messages
2453
2454 if (it.blockNumber() < chatDoc->blockCount()-3){
2455 it = it.next().next();
2456 it.setUserState(1); // add label for string with horizontal line
2457
2458 it = it.previous();
2459 if (it.text().isEmpty()){ // additional check that it is not message
2460 QTextCursor c(it);
2461 c.select(QTextCursor::BlockUnderCursor);
2462 c.deleteChar(); // delete empty string above horizontal line
2463 }
2464 }
2465
2466 break;
2467 }
2468 }
2469
2470 return;
2471 }
2472
2473 addOutput(output);
2474
2475 for (QTextBlock itu = chatDoc->lastBlock(); itu != chatDoc->begin(); itu = itu.previous()){
2476 if (!itu.userData())
2477 itu.setUserData(new UserListUserData(nick));
2478 else
2479 break;
2480 }
2481
2482 }
2483
newPm(const VarMap & map)2484 void HubFrame::newPm(const VarMap &map){
2485 Q_D(HubFrame);
2486 QString nick = map["NICK"].toString();
2487 QString message = map["MSG"].toString();
2488 QString time = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + map["TIME"].toString() + "]</font>";
2489 QString color = map["CLR"].toString();
2490 QString full_message = "";
2491
2492 if (nick != _q(d->client->getMyNick())){
2493 bool show_msg = false;
2494
2495 if (!d->pm.contains(map["CID"].toString()))
2496 show_msg = true;
2497 else
2498 show_msg = (!d->pm[map["CID"].toString()]->isVisible() || WBGET("notification/play-sound-with-active-pm", true));
2499
2500 if (show_msg)
2501 Notification::getInstance()->showMessage(Notification::PM, nick, message);
2502 }
2503
2504 bool third = map["3RD"].toBool();
2505
2506 if (message.startsWith("/me ")){
2507 message.remove(0, 4);
2508 third = true;
2509 }
2510
2511 nick = third? ("* " + nick + " ") : ("<" + nick + "> ");
2512
2513 message = LinkParser::parseForLinks(message, true);
2514
2515 WulforUtil::getInstance()->textToHtml(nick, true);
2516
2517 message = "<font color=\"" + WSGET(WS_CHAT_MSG_COLOR) + "\">" + message + "</font>";
2518 full_message += time;
2519 string info= Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
2520
2521 if (!info.empty())
2522 full_message += " <font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">" + _q(info) + "</font>";
2523
2524 full_message += QString(" <a style=\"text-decoration:none\" href=\"user://%1\"><font color=\"%2\"><b>%3</b></font></a>")
2525 .arg(nick).arg(WSGET(color)).arg(nick.replace("\"", """));
2526 full_message += message;
2527
2528 WulforUtil::getInstance()->textToHtml(full_message, false);
2529
2530 addPM(map["CID"].toString(), full_message);
2531 }
2532
createPMWindow(const QString & nick)2533 void HubFrame::createPMWindow(const QString &nick){
2534 Q_D(HubFrame);
2535 createPMWindow(CID(_tq(d->model->CIDforNick(nick, _q(d->client->getHubUrl())))));
2536 }
2537
createPMWindow(const dcpp::CID & cid)2538 void HubFrame::createPMWindow(const dcpp::CID &cid){
2539 addPM(_q(cid.toBase32()), "");
2540 }
2541
hasCID(const dcpp::CID & cid,const QString & nick)2542 bool HubFrame::hasCID(const dcpp::CID &cid, const QString &nick){
2543 Q_D(HubFrame);
2544 return (d->model->CIDforNick(nick, _q(d->client->getHubUrl())) == _q(cid.toBase32()));
2545 }
2546
clearUsers()2547 void HubFrame::clearUsers(){
2548 Q_D(HubFrame);
2549
2550 if (d->model){
2551 d->model->blockSignals(true);
2552 d->model->clear();
2553 d-> model->blockSignals(false);
2554 treeView_USERS->setModel(d->model);
2555 }
2556
2557 d->total_shared = 0;
2558
2559 treeView_USERS->repaint();
2560
2561 slotUsersUpdated();
2562
2563 d->model->repaint();
2564 }
2565
pmUserOffline(QString cid)2566 void HubFrame::pmUserOffline(QString cid){
2567 pmUserEvent(cid, tr("User offline."));
2568 }
2569
pmUserEvent(QString cid,QString e)2570 void HubFrame::pmUserEvent(QString cid, QString e){
2571 Q_D(HubFrame);
2572
2573 if (!d->pm.contains(cid))
2574 return;
2575
2576 QString output = "";
2577 QString nick = " * ";
2578
2579 QString msg = "<font color=\"" + WSGET(WS_CHAT_MSG_COLOR) + "\">" + e + "</font>";
2580 QString time = "";
2581
2582 if (!WSGET(WS_CHAT_TIMESTAMP).isEmpty())
2583 time = "<font color=\""+WSGET(WS_CHAT_TIME_COLOR)+">["+QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP))+"]</font>";
2584
2585 output = time + "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\"><b>" + nick + "</b> </font>";
2586 output += msg;
2587
2588 WulforUtil::getInstance()->textToHtml(output, false);
2589
2590 d->pm[cid]->addOutput(output);
2591 }
2592
getPassword()2593 void HubFrame::getPassword(){
2594 Q_D(HubFrame);
2595
2596 MainWindow *MW = MainWindow::getInstance();
2597
2598 if (!MW->isVisible() && d->client->getPassword().empty()){
2599 MW->show();
2600 MW->raise();
2601
2602 }
2603
2604 if(d->client && !d->client->getPassword().empty()) {
2605 d->client->password(d->client->getPassword());
2606 addStatus(tr("Stored password sent..."));
2607 }
2608 else if (d->client && d->client->isConnected()){
2609 QString pass = QInputDialog::getText(this, _q(d->client->getHubUrl()), tr("Password"), QLineEdit::Password);
2610
2611 if (!pass.isEmpty()){
2612 d->client->setPassword(pass.toStdString());
2613 d->client->password(pass.toStdString());
2614 }
2615 else
2616 d->client->disconnect(true);
2617 }
2618 }
2619
follow(QString redirect)2620 void HubFrame::follow(QString redirect){
2621 if(!redirect.isEmpty()) {
2622 if(ClientManager::getInstance()->isConnected(_tq(redirect))) {
2623 addStatus(tr("Redirect request received to a hub that's already connected"));
2624 return;
2625 }
2626
2627 string url = _tq(redirect);
2628
2629 Q_D(HubFrame);
2630 // the client is dead, long live the client!
2631 d->client->removeListener(this);
2632 HubManager::getInstance()->unregisterHubUrl(_q(d->client->getHubUrl()));
2633 ClientManager::getInstance()->putClient(d->client);
2634 clearUsers();
2635 d->client = ClientManager::getInstance()->getClient(url);
2636
2637 d->client->addListener(this);
2638 d->client->connect();
2639 }
2640 }
2641
findText(QTextDocument::FindFlags flag)2642 void HubFrame::findText(QTextDocument::FindFlags flag){
2643 textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
2644
2645 if (lineEdit_FIND->text().isEmpty())
2646 return;
2647
2648 QTextCursor c = textEdit_CHAT->textCursor();
2649
2650 bool ok = textEdit_CHAT->find(lineEdit_FIND->text(), flag);
2651
2652 if (flag == QTextDocument::FindBackward && !ok)
2653 c.movePosition(QTextCursor::End,QTextCursor::MoveAnchor,1);
2654 else if (!flag && !ok)
2655 c.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor,1);
2656
2657 c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, flag);
2658 if (!c.isNull()) {
2659 textEdit_CHAT->setTextCursor(c);
2660 slotFindAll();
2661 }
2662 }
2663
updateStyles()2664 void HubFrame::updateStyles(){
2665 QString custom_font_desc = WSGET(WS_CHAT_FONT);
2666 QFont custom_font;
2667
2668 if (!custom_font_desc.isEmpty() && custom_font.fromString(custom_font_desc)){
2669 textEdit_CHAT->document()->setDefaultStyleSheet(
2670 QString("pre { margin:0px; white-space:pre-wrap; font-family:'%1'; font-size: %2pt; }")
2671 .arg(custom_font.family()).arg(custom_font.pointSize())
2672 );
2673 }
2674 else {
2675 textEdit_CHAT->document()->setDefaultStyleSheet(
2676 QString("pre { margin:0px; white-space:pre-wrap; font-family:'%1' }")
2677 .arg(QApplication::font().family())
2678 );
2679 }
2680
2681 custom_font_desc = WSGET(WS_CHAT_ULIST_FONT);
2682
2683 if (!custom_font_desc.isEmpty() && custom_font.fromString(custom_font_desc))
2684 treeView_USERS->setFont(custom_font);
2685 }
2686
slotActivate()2687 void HubFrame::slotActivate(){
2688 plainTextEdit_INPUT->setFocus();
2689 }
2690
slotUsersUpdated()2691 void HubFrame::slotUsersUpdated(){
2692 Q_D(HubFrame);
2693
2694 if (d->proxy && treeView_USERS->model() == d->proxy){
2695 label_USERSTATE->setText(QString(tr("Users count: %3/%1 | Total share: %2"))
2696 .arg(d->model->rowCount())
2697 .arg(WulforUtil::formatBytes(d->total_shared))
2698 .arg(d->proxy->rowCount()));
2699 }
2700 else {
2701 label_USERSTATE->setText(QString(tr("Users count: %1 | Total share: %2"))
2702 .arg(d->model->rowCount())
2703 .arg(WulforUtil::formatBytes(d->total_shared)));
2704 }
2705
2706 label_LAST_STATUS->setMaximumHeight(label_USERSTATE->height());
2707 }
2708
slotReconnect()2709 void HubFrame::slotReconnect(){
2710 clearUsers();
2711
2712 Q_D(HubFrame);
2713
2714 if (d->client)
2715 d->client->reconnect();
2716 }
2717
slotMapOnArena()2718 void HubFrame::slotMapOnArena(){
2719 ArenaWidgetManager::getInstance()->activate(this);
2720 }
2721
slotClose()2722 void HubFrame::slotClose(){
2723 ArenaWidgetManager::getInstance()->rem(this);
2724 }
2725
slotPMClosed(QString cid)2726 void HubFrame::slotPMClosed(QString cid){
2727 Q_D(HubFrame);
2728
2729 auto it = d->pm.find(cid);
2730
2731 if (it != d->pm.end())
2732 d->pm.erase(it);
2733 }
2734
2735 template < QString (UserListItem::*func)() const >
copyTagToClipboard(QModelIndexList & list)2736 static void copyTagToClipboard(QModelIndexList &list){
2737 QString ret = "";
2738 UserListItem *item = NULL;
2739
2740 for (const auto &i : list) {
2741 item = reinterpret_cast<UserListItem*> ( i.internalPointer() );
2742
2743 if ( !ret.isEmpty() )
2744 ret += "\n";
2745
2746 if ( item )
2747 ret += (item->*func)();
2748 }
2749
2750 qApp->clipboard()->setText ( ret, QClipboard::Clipboard );
2751 }
2752
2753 template < qulonglong (UserListItem::*func)() const >
copyTagToClipboard(QModelIndexList & list)2754 static void copyTagToClipboard(QModelIndexList &list){
2755 QString ret = "";
2756 UserListItem *item = NULL;
2757
2758 for (const auto &i : list) {
2759 item = reinterpret_cast<UserListItem*> ( i.internalPointer() );
2760
2761 if ( !ret.isEmpty() )
2762 ret += "\n";
2763
2764 if ( item )
2765 ret += WulforUtil::formatBytes((item->*func)());
2766 }
2767
2768 qApp->clipboard()->setText ( ret, QClipboard::Clipboard );
2769 }
2770
slotUserListMenu(const QPoint &)2771 void HubFrame::slotUserListMenu(const QPoint&){
2772 QItemSelectionModel *selection_model = treeView_USERS->selectionModel();
2773 QModelIndexList proxy_list = selection_model->selectedRows(0);
2774
2775 if (proxy_list.size() < 1)
2776 return;
2777
2778 QString cid = "";
2779
2780 Q_D(HubFrame);
2781
2782 if (d->proxy && treeView_USERS->model() == d->proxy){
2783 QModelIndex i = d->proxy->mapToSource(proxy_list.at(0));
2784 cid = reinterpret_cast<UserListItem*>(i.internalPointer())->getCID();
2785 }
2786 else{
2787 QModelIndex i = proxy_list.at(0);
2788 cid = reinterpret_cast<UserListItem*>(i.internalPointer())->getCID();
2789 }
2790
2791 Menu::Action action = Menu::getInstance()->execUserMenu(d->client, cid);
2792 UserListItem *item = NULL;
2793
2794 proxy_list = selection_model->selectedRows(0);
2795
2796 if (proxy_list.size() < 1)
2797 return;
2798
2799 QModelIndexList list;
2800
2801 if (d->proxy && treeView_USERS->model() == d->proxy){
2802 for (const auto &i : proxy_list)
2803 list.push_back(d->proxy->mapToSource(i));
2804 }
2805 else
2806 list = proxy_list;
2807
2808 switch (action){
2809 case Menu::None:
2810 {
2811 return;
2812 }
2813 case Menu::BrowseFilelist:
2814 {
2815 for (const auto &i : list){
2816 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2817
2818 if (item)
2819 browseUserFiles(item->getCID());
2820 }
2821
2822 break;
2823 }
2824 case Menu::PrivateMessage:
2825 {
2826 for (const auto &i : list){
2827 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2828
2829 if (item)
2830 addPM(item->getCID(), "", false);
2831 }
2832
2833 break;
2834 }
2835 case Menu::CopyText:
2836 {
2837 QString ttip = "";
2838
2839 for (const auto &i : list){
2840 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2841
2842 if (item)
2843 ttip += getUserInfo(item) + "\n";
2844
2845 ttip += "\n";
2846 }
2847
2848 if (!ttip.isEmpty())
2849 qApp->clipboard()->setText(ttip, QClipboard::Clipboard);
2850
2851 break;
2852 }
2853 case Menu::CopyNick:
2854 {
2855 copyTagToClipboard<&UserListItem::getNick> (list);
2856
2857 break;
2858 }
2859 case Menu::CopyIP:
2860 {
2861 copyTagToClipboard<&UserListItem::getIP> (list);
2862
2863 break;
2864 }
2865 case Menu::CopyShare:
2866 {
2867 copyTagToClipboard<&UserListItem::getShare> (list);
2868
2869 break;
2870 }
2871 case Menu::CopyTag:
2872 {
2873 copyTagToClipboard<&UserListItem::getTag> (list);
2874
2875 break;
2876 }
2877 case Menu::MatchQueue:
2878 {
2879 for (const auto &i : list){
2880 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2881
2882 if (item)
2883 browseUserFiles(item->getCID(), true);
2884 }
2885
2886 break;
2887 }
2888 case Menu::FavoriteAdd:
2889 {
2890 for (const auto &i : list){
2891 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2892
2893 if (item)
2894 addUserToFav(item->getCID());
2895 }
2896
2897 break;
2898 }
2899 case Menu::FavoriteRem:
2900 {
2901 for (const auto &i : list){
2902 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2903
2904 if (item)
2905 delUserFromFav(item->getCID());
2906 }
2907
2908 break;
2909 }
2910 case Menu::GrantSlot:
2911 {
2912 for (const auto &i : list){
2913 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2914
2915 if (item)
2916 grantSlot(item->getCID());
2917 }
2918
2919 break;
2920 }
2921 case Menu::RemoveQueue:
2922 {
2923 for (const auto &i : list){
2924 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2925
2926 if (item)
2927 delUserFromQueue(item->getCID());
2928 }
2929
2930 break;
2931 }
2932 case Menu::AntiSpamWhite:
2933 {
2934
2935 if (AntiSpam::getInstance()){
2936 for (const auto &i : list){
2937 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2938
2939 (*AntiSpam::getInstance()) << eIN_WHITE << item->getNick();
2940 }
2941 }
2942
2943 break;
2944 }
2945 case Menu::AntiSpamBlack:
2946 {
2947 if (AntiSpam::getInstance()){
2948 for (const auto &i : list){
2949 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2950
2951 (*AntiSpam::getInstance()) << eIN_BLACK << item->getNick();
2952 }
2953 }
2954
2955 break;
2956 }
2957 default:
2958 {
2959 break;
2960 }
2961 }
2962 }
2963
slotChatMenu(const QPoint &)2964 void HubFrame::slotChatMenu(const QPoint &){
2965 QTextEdit *editor = qobject_cast<QTextEdit*>(sender());
2966
2967 if (!editor)
2968 return;
2969 QTextCursor cursor = editor->cursorForPosition(editor->mapFromGlobal(QCursor::pos()));
2970 QString nick = "";
2971 if(cursor.block().userData())
2972 nick = static_cast<UserListUserData*>(cursor.block().userData())->data;
2973
2974 Q_D(HubFrame);
2975
2976 QString cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
2977
2978 if (cid.isEmpty()){
2979 QMenu *m = editor->createStandardContextMenu(QCursor::pos());
2980 m->exec(QCursor::pos());
2981
2982 delete m;
2983
2984 return;
2985 }
2986
2987 QPoint p = QCursor::pos();
2988
2989 bool pmw = (editor != this->textEdit_CHAT);
2990
2991 Menu::Action action = Menu::getInstance()->execChatMenu(d->client, cid, pmw);
2992
2993 switch (action){
2994 case Menu::CopyText:
2995 {
2996 QString ret = editor->textCursor().selectedText();
2997
2998 if (ret.isEmpty())
2999 ret = editor->anchorAt(textEdit_CHAT->mapFromGlobal(p));
3000
3001 if (ret.startsWith("user://")){
3002 ret.remove(0, 7);
3003
3004 ret = ret.trimmed();
3005
3006 if (ret.startsWith("<") && ret.endsWith(">")){
3007 ret.remove(0, 1);//remove <
3008 ret = ret.left(ret.lastIndexOf(">"));//remove >
3009 }
3010 }
3011
3012 if (ret.isEmpty())
3013 ret = editor->textCursor().block().text();
3014
3015 qApp->clipboard()->setText(ret, QClipboard::Clipboard);
3016
3017 break;
3018 }
3019 case Menu::SearchText:
3020 {
3021 QString ret = editor->textCursor().selectedText();
3022
3023 if (ret.isEmpty())
3024 ret = editor->anchorAt(textEdit_CHAT->mapFromGlobal(p));
3025
3026 if (ret.startsWith("user://")){
3027 ret.remove(0, 7);
3028
3029 ret = ret.trimmed();
3030
3031 if (ret.startsWith("<") && ret.endsWith(">")){
3032 ret.remove(0, 1);//remove <
3033 ret = ret.left(ret.lastIndexOf(">"));//remove >
3034 }
3035 }
3036
3037 if (ret.isEmpty())
3038 break;
3039
3040 SearchFrame *sf = ArenaWidgetFactory().create<SearchFrame, QWidget*>(this);
3041 sf->fastSearch(ret, false);
3042
3043 break;
3044 }
3045 case Menu::CopyNick:
3046 {
3047 qApp->clipboard()->setText(nick, QClipboard::Clipboard);
3048
3049 break;
3050 }
3051 case Menu::FindInList:
3052 {
3053 UserListItem *item = d->model->itemForNick(nick, _q(d->client->getHubUrl()));
3054
3055 if (item){
3056 QModelIndex index = d->model->index(item->row(), 0, QModelIndex());
3057
3058 treeView_USERS->clearSelection();
3059 treeView_USERS->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
3060 treeView_USERS->scrollTo(index, QAbstractItemView::PositionAtCenter);
3061 }
3062
3063 break;
3064 }
3065 case Menu::BrowseFilelist:
3066 {
3067 browseUserFiles(cid, false);
3068
3069 break;
3070 }
3071 case Menu::MatchQueue:
3072 {
3073 browseUserFiles(cid, true);
3074
3075 break;
3076 }
3077 case Menu::PrivateMessage:
3078 {
3079 addPM(cid, "", false);
3080
3081 if (d->pm.contains(cid))
3082 ArenaWidgetManager::getInstance()->activate(d->pm[cid]);
3083
3084 break;
3085 }
3086 case Menu::FavoriteAdd:
3087 {
3088 addUserToFav(cid);
3089
3090 break;
3091 }
3092 case Menu::FavoriteRem:
3093 {
3094 delUserFromFav(cid);
3095
3096 break;
3097 }
3098 case Menu::GrantSlot:
3099 {
3100 grantSlot(cid);
3101
3102 break;
3103 }
3104 case Menu::RemoveQueue:
3105 {
3106 delUserFromQueue(cid);
3107
3108 break;
3109 }
3110 case Menu::UserCommands:
3111 {
3112 //All work already done in Menu::exec*
3113 break;
3114 }
3115 case Menu::ClearChat:
3116 {
3117 if (pmw)
3118 MainWindow::getInstance()->slotChatClear(); // some hack
3119 else
3120 clearChat();
3121
3122 break;
3123 }
3124 case Menu::FindInChat:
3125 {
3126 slotHideFindFrame();
3127
3128 break;
3129 }
3130 case Menu::DisableChat:
3131 {
3132 disableChat();
3133
3134 break;
3135 }
3136 case Menu::SelectAllChat:
3137 {
3138 editor->selectAll();
3139
3140 break;
3141 }
3142 case Menu::ZoomInChat:
3143 {
3144 editor->zoomIn();
3145
3146 break;
3147 }
3148 case Menu::ZoomOutChat:
3149 {
3150 editor->zoomOut();
3151
3152 break;
3153 }
3154 case Menu::AntiSpamWhite:
3155 {
3156 if (AntiSpam::getInstance())
3157 (*AntiSpam::getInstance()) << eIN_WHITE << nick;
3158
3159 break;
3160 }
3161 case Menu::AntiSpamBlack:
3162 {
3163 if (AntiSpam::getInstance())
3164 (*AntiSpam::getInstance()) << eIN_BLACK << nick;
3165
3166 break;
3167 }
3168 case Menu::None:
3169 {
3170 return;
3171 }
3172 default:
3173 {
3174 return;
3175 }
3176 }
3177 }
3178
slotHeaderMenu(const QPoint &)3179 void HubFrame::slotHeaderMenu(const QPoint&){
3180 WulforUtil::headerMenu(treeView_USERS);
3181 }
3182
slotShowWnd()3183 void HubFrame::slotShowWnd(){
3184 if (isVisible())
3185 return;
3186
3187 ArenaWidgetManager::getInstance()->activate(this);
3188 }
3189
slotShellFinished(bool ok,QString output)3190 void HubFrame::slotShellFinished(bool ok, QString output){
3191 if (ok){
3192 HubFrame *fr = qobject_cast<HubFrame *>(sender()->parent());
3193 PMWindow *pm = qobject_cast<PMWindow *>(sender()->parent());
3194
3195 LinkParser::parseForMagnetAlias(output);
3196
3197 if (fr == this)
3198 sendChat(output, false, false);
3199 else if (pm)
3200 pm->sendMessage(output, false, false);
3201 }
3202
3203 ShellCommandRunner *runner = reinterpret_cast<ShellCommandRunner*>(sender());
3204
3205 runner->cancel();
3206 runner->exit(0);
3207 runner->wait(100);
3208
3209 if (runner->isRunning())
3210 runner->terminate();
3211
3212 Q_D(HubFrame);
3213
3214 if (d->shell_list.indexOf(runner) >= 0)
3215 d->shell_list.removeAt(d->shell_list.indexOf(runner));
3216
3217 delete runner;
3218 }
3219
nextMsg()3220 void HubFrame::nextMsg(){
3221 if (!plainTextEdit_INPUT->hasFocus())
3222 return;
3223
3224 Q_D(HubFrame);
3225
3226 if (d->out_messages_index < 0 ||
3227 d->out_messages_index+1 > d->out_messages.size()-1 ||
3228 d->out_messages.isEmpty())
3229 return;
3230
3231 if (d->out_messages.at(d->out_messages_index) != plainTextEdit_INPUT->toPlainText())
3232 d->out_messages[d->out_messages_index] = plainTextEdit_INPUT->toPlainText();
3233
3234 if (d->out_messages_index+1 <= d->out_messages.size()-1)
3235 d->out_messages_index++;
3236
3237 plainTextEdit_INPUT->setPlainText(d->out_messages.at(d->out_messages_index));
3238
3239 if (d->out_messages_unsent && d->out_messages_index == d->out_messages.size()-1){
3240 d->out_messages.removeLast();
3241 d->out_messages_unsent = false;
3242 d->out_messages_index = d->out_messages.size()-1;
3243 }
3244 }
3245
prevMsg()3246 void HubFrame::prevMsg(){
3247 if (!plainTextEdit_INPUT->hasFocus())
3248 return;
3249
3250 Q_D(HubFrame);
3251
3252 if (d->out_messages_index < 1 ||
3253 d->out_messages_index-1 > d->out_messages.size()-1 ||
3254 d->out_messages.isEmpty())
3255 return;
3256
3257 if (!d->out_messages_unsent && d->out_messages_index == d->out_messages.size()-1){
3258 d->out_messages << plainTextEdit_INPUT->toPlainText();
3259 d->out_messages_unsent = true;
3260 d->out_messages_index++;
3261 }
3262
3263 if (d->out_messages.at(d->out_messages_index) != plainTextEdit_INPUT->toPlainText())
3264 d->out_messages[d->out_messages_index] = plainTextEdit_INPUT->toPlainText();
3265
3266 if (d->out_messages_index >= 1)
3267 d->out_messages_index--;
3268
3269 plainTextEdit_INPUT->setPlainText(d->out_messages.at(d->out_messages_index));
3270 }
3271
slotHideFindFrame()3272 void HubFrame::slotHideFindFrame(){
3273 frame->setVisible(!frame->isVisible());
3274
3275 if (frame->isVisible()){
3276 QString stext = textEdit_CHAT->textCursor().selectedText();
3277
3278 if (!stext.isEmpty()){
3279 lineEdit_FIND->setText(stext);
3280 lineEdit_FIND->selectAll();
3281 }
3282
3283 lineEdit_FIND->setFocus();
3284 }
3285 else{
3286 QTextCursor c = textEdit_CHAT->textCursor();
3287
3288 c.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor,1);
3289
3290 textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3291
3292 textEdit_CHAT->setTextCursor(c);
3293 }
3294 }
3295
slotFilterTextChanged()3296 void HubFrame::slotFilterTextChanged(){
3297 QString text = lineEdit_FILTER->text();
3298
3299 Q_D(HubFrame);
3300
3301 if (!text.isEmpty()){
3302 if (!d->proxy){
3303 d->proxy = new UserListProxyModel();
3304 d->proxy->setDynamicSortFilter(true);
3305 d->proxy->setSourceModel(d->model);
3306 }
3307
3308 bool isRegExp = false;
3309
3310 if (text.startsWith("##")){
3311 isRegExp = true;
3312 text.remove(0, 2);
3313 }
3314
3315 if (!isRegExp){
3316 d->proxy->setFilterFixedString(text);
3317 d->proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
3318 }
3319 else{
3320 d->proxy->setFilterRegExp(text);
3321 d->proxy->setFilterCaseSensitivity(Qt::CaseSensitive);
3322 }
3323
3324 d->proxy->setFilterKeyColumn(comboBox_COLUMNS->currentIndex());
3325
3326 if (treeView_USERS->model() != d->proxy)
3327 treeView_USERS->setModel(d->proxy);
3328 }
3329 else if (treeView_USERS->model() != d->model)
3330 treeView_USERS->setModel(d->model);
3331
3332 if (comboBox_COLUMNS->hasFocus())
3333 lineEdit_FILTER->setFocus();
3334 }
3335
slotFindTextEdited(const QString & text)3336 void HubFrame::slotFindTextEdited(const QString & text){
3337 if (text.isEmpty()){
3338 textEdit_CHAT->verticalScrollBar()->setValue(textEdit_CHAT->verticalScrollBar()->maximum());
3339 textEdit_CHAT->textCursor().movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
3340
3341 return;
3342 }
3343
3344 QTextCursor c = textEdit_CHAT->textCursor();
3345
3346 c.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor,1);
3347 c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, 0);
3348 if (!c.isNull()) {
3349 textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3350 textEdit_CHAT->setTextCursor(c);
3351 slotFindAll();
3352 }
3353 }
3354
slotFindAll()3355 void HubFrame::slotFindAll(){
3356 if (!toolButton_ALL->isChecked()){
3357 textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3358
3359 return;
3360 }
3361
3362 QList<QTextEdit::ExtraSelection> extraSelections;
3363
3364 if (!lineEdit_FIND->text().isEmpty()) {
3365 QTextEdit::ExtraSelection selection;
3366
3367 QColor color;
3368 color.setNamedColor(WSGET(WS_CHAT_FIND_COLOR));
3369 color.setAlpha(WIGET(WI_CHAT_FIND_COLOR_ALPHA));
3370
3371 selection.format.setBackground(color);
3372
3373 QTextCursor c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), 0, 0);
3374
3375 while (!c.isNull()) {
3376 selection.cursor = c;
3377 extraSelections.append(selection);
3378
3379 c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, 0);
3380 }
3381 }
3382 textEdit_CHAT->setExtraSelections(extraSelections);
3383 }
3384
slotSmile()3385 void HubFrame::slotSmile(){
3386 if (!(WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance()))
3387 return;
3388
3389 if (WBGET(WB_CHAT_USE_SMILE_PANEL)){
3390 frame_SMILES->setVisible(!frame_SMILES->isVisible());
3391 }
3392 else {
3393 EmoticonDialog *dialog = new EmoticonDialog(this);
3394
3395 if (dialog->exec() == QDialog::Accepted) {
3396
3397 QString smiley = dialog->getEmoticonText();
3398
3399 if (!smiley.isEmpty()) {
3400
3401 smiley.replace("<", "<");
3402 smiley.replace(">", ">");
3403 smiley.replace("&", "&");
3404 smiley.replace("'", "\'");
3405 smiley.replace(""", "\"");
3406
3407 smiley += " ";
3408
3409 plainTextEdit_INPUT->textCursor().insertText(smiley);
3410 plainTextEdit_INPUT->setFocus();
3411 }
3412 }
3413
3414 delete dialog;
3415 }
3416 }
3417
slotSmileClicked()3418 void HubFrame::slotSmileClicked(){
3419 EmoticonLabel *lbl = qobject_cast<EmoticonLabel* >(sender());
3420
3421 if (!lbl)
3422 return;
3423
3424 QString smiley = lbl->toolTip();
3425
3426 if (!smiley.isEmpty()) {
3427
3428 smiley.replace("<", "<");
3429 smiley.replace(">", ">");
3430 smiley.replace("&", "&");
3431 smiley.replace("'", "\'");
3432 smiley.replace(""", "\"");
3433
3434 smiley += " ";
3435
3436 plainTextEdit_INPUT->textCursor().insertText(smiley);
3437 plainTextEdit_INPUT->setFocus();
3438 }
3439
3440 if (WBGET(WB_CHAT_HIDE_SMILE_PANEL))
3441 frame_SMILES->setVisible(false);
3442 }
3443
slotSmileContextMenu()3444 void HubFrame::slotSmileContextMenu(){
3445 #if !defined(Q_WS_WIN)
3446 QString emot = CLIENT_DATA_DIR "/emoticons/";
3447 #else
3448 QString emot = qApp->applicationDirPath()+QDir::separator()+CLIENT_DATA_DIR "/emoticons/";
3449 #endif
3450
3451 QMenu *m = new QMenu(this);
3452
3453 for (const auto &f : QDir(emot).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot)){
3454 if (!f.isEmpty()){
3455 QAction * act = m->addAction(f);
3456 act->setCheckable(true);
3457
3458 if (f == WSGET(WS_APP_EMOTICON_THEME)){
3459 act->setChecked(false);
3460 act->setChecked(true);
3461 }
3462 }
3463 }
3464
3465 QAction *a = m->exec(QCursor::pos());
3466
3467 if (a && a->isChecked())
3468 WSSET(WS_APP_EMOTICON_THEME, a->text());
3469
3470 m->deleteLater();
3471 }
3472
slotInputTextChanged()3473 void HubFrame::slotInputTextChanged(){
3474 #ifdef USE_ASPELL
3475 PMWindow *p = qobject_cast<PMWindow*>(sender());
3476 QTextEdit *plainTextEdit_INPUT = (p)? qobject_cast<QTextEdit*>(p->inputWidget()) : this->plainTextEdit_INPUT;
3477
3478 if (!plainTextEdit_INPUT)
3479 return;
3480 QString line = plainTextEdit_INPUT->toPlainText();
3481
3482 if (line.isEmpty() || !SpellCheck::getInstance())
3483 return;
3484
3485 SpellCheck *sp = SpellCheck::getInstance();
3486 QStringList words = line.split(QRegExp("\\W+"), QString::SkipEmptyParts);
3487
3488 if (words.isEmpty())
3489 return;
3490
3491 QList<QTextEdit::ExtraSelection> extraSelections;
3492
3493 QTextCursor c = plainTextEdit_INPUT->textCursor();
3494
3495 QTextEdit::ExtraSelection selection;
3496 selection.format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
3497 selection.format.setUnderlineColor(Qt::red);
3498
3499 bool ok = false;
3500 while (!words.empty()){
3501 QString s = words.takeLast();
3502
3503 if ((s.toLongLong(&ok) && ok) || !QUrl(s).scheme().isEmpty())
3504 continue;
3505
3506 if (plainTextEdit_INPUT->find(s, QTextDocument::FindBackward) && !sp->ok(s)){
3507 selection.cursor = plainTextEdit_INPUT->textCursor();
3508 extraSelections.append(selection);
3509 }
3510 }
3511
3512 plainTextEdit_INPUT->setTextCursor(c);
3513 plainTextEdit_INPUT->setExtraSelections(extraSelections);
3514 #endif
3515 }
3516
slotInputContextMenu()3517 void HubFrame::slotInputContextMenu(){
3518 PMWindow *p = qobject_cast<PMWindow*>(sender());
3519 QTextEdit *plainTextEdit_INPUT = (p)? qobject_cast<QTextEdit*>(p->inputWidget()) : this->plainTextEdit_INPUT;
3520
3521 if (!plainTextEdit_INPUT)
3522 return;
3523
3524 QMenu *m = plainTextEdit_INPUT->createStandardContextMenu();
3525
3526 #ifndef USE_ASPELL
3527 m->exec(QCursor::pos());
3528 m->deleteLater();
3529 #else
3530 if (SpellCheck::getInstance()) {
3531 SpellCheck *sp = SpellCheck::getInstance();
3532 QTextCursor c = plainTextEdit_INPUT->cursorForPosition(plainTextEdit_INPUT->mapFromGlobal(QCursor::pos()));
3533 c.select(QTextCursor::WordUnderCursor);
3534
3535 QString word = c.selectedText();
3536
3537 if (sp->ok(word) || word.isEmpty()){
3538 c.clearSelection();
3539 m->exec(QCursor::pos());
3540 m->deleteLater();
3541 }
3542 else {
3543 QStringList list;
3544 sp->suggestions(word, list);
3545
3546 m->addSeparator();
3547 QAction *add_to_dict = new QAction(tr("Add to dictionary"), m);
3548
3549 m->addAction(add_to_dict);
3550
3551 QMenu *ss = NULL;
3552 if (!list.isEmpty()) {
3553 ss = new QMenu(tr("Suggestions"), this);
3554
3555
3556 for (const auto &s : list)
3557 ss->addAction(s);
3558
3559 m->addMenu(ss);
3560 }
3561
3562 QAction *ret = m->exec(QCursor::pos());
3563
3564 if (ret == add_to_dict)
3565 sp->addToDict(word);
3566 else if (ss && ret && ret->parent() == ss){
3567 c.removeSelectedText();
3568
3569 c.insertText(ret->text());
3570 }
3571
3572 m->deleteLater();
3573
3574 if (ss)
3575 ss->deleteLater();
3576
3577 slotInputTextChanged();
3578 }
3579 }
3580 else {
3581 m->exec(QCursor::pos());
3582 m->deleteLater();
3583 }
3584 #endif
3585 }
3586
slotStatusLinkOpen(const QString & url)3587 void HubFrame::slotStatusLinkOpen(const QString &url){
3588 WulforUtil::getInstance()->openUrl(url);
3589 }
3590
slotHubMenu(QAction * res)3591 void HubFrame::slotHubMenu(QAction *res) {
3592 if (res && res->data().canConvert(QVariant::Int)) {//User command
3593 int id = res->data().toInt();
3594
3595 UserCommand uc;
3596 if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
3597 return;
3598
3599 StringMap params;
3600 Q_D(HubFrame);
3601
3602 if (WulforUtil::getInstance()->getUserCommandParams(uc, params)) {
3603 d->client->getMyIdentity().getParams(params, "my", true);
3604 d->client->getHubIdentity().getParams(params, "hub", false);
3605
3606 d->client->escapeParams(params);
3607 d->client->sendUserCmd(uc, params);
3608 }
3609 }
3610 }
3611
slotSettingsChanged(const QString & key,const QString & value)3612 void HubFrame::slotSettingsChanged(const QString &key, const QString &value){
3613 if (key == WS_CHAT_FONT || key == WS_CHAT_ULIST_FONT)
3614 updateStyles();
3615 else if (key == WS_APP_EMOTICON_THEME){
3616 if (EmoticonFactory::getInstance()){
3617 EmoticonFactory::getInstance()->load();
3618
3619 frame_SMILES->setVisible(false);
3620
3621 clearLayout(frame_SMILES->layout());
3622
3623 QSize sz;
3624 Q_UNUSED(sz);
3625
3626 EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
3627
3628 for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
3629 connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
3630 }
3631 }
3632 else if (key == "hubframe/chat-background-color"){
3633 QPalette p = textEdit_CHAT->palette();
3634 QColor clr = p.color(QPalette::Active, QPalette::Base);
3635
3636 clr.setNamedColor(value);
3637
3638 if (clr.isValid()){
3639 p.setColor(QPalette::Base, clr);
3640
3641 textEdit_CHAT->setPalette(p);
3642 }
3643 }
3644 else if (key == WS_TRANSLATION_FILE){
3645 retranslateUi(this);
3646 }
3647 }
3648
slotBoolSettingsChanged(const QString & key,int value)3649 void HubFrame::slotBoolSettingsChanged(const QString &key, int value){
3650 if (key == WB_APP_ENABLE_EMOTICON){
3651 bool enable = static_cast<bool>(value);
3652
3653 if (enable){
3654 EmoticonFactory::newInstance();
3655 EmoticonFactory::getInstance()->load();
3656
3657 frame_SMILES->setVisible(false);
3658
3659 clearLayout(frame_SMILES->layout());
3660
3661 QSize sz;
3662 Q_UNUSED(sz);
3663
3664 EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
3665
3666 for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
3667 connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
3668
3669 }
3670 else{
3671 if (EmoticonFactory::getInstance())
3672 EmoticonFactory::deleteInstance();
3673
3674 frame_SMILES->setVisible(false);
3675
3676 clearLayout(frame_SMILES->layout());
3677 }
3678
3679 toolButton_SMILE->setVisible(enable);
3680 }
3681 }
3682
slotCopyHubIP()3683 void HubFrame::slotCopyHubIP(){
3684 Q_D(HubFrame);
3685
3686 if (d->client && d->client->isConnected()){
3687 qApp->clipboard()->setText(_q(d->client->getIp()), QClipboard::Clipboard);
3688 }
3689 }
3690
slotCopyHubTitle()3691 void HubFrame::slotCopyHubTitle(){
3692 Q_D(HubFrame);
3693
3694 if (d->client && d->client->isConnected()){
3695 qApp->clipboard()->setText(QString("%1 - %2").arg(_q(d->client->getHubName())).arg(_q(d->client->getHubDescription())), QClipboard::Clipboard);
3696 }
3697 }
3698
slotCopyHubURL()3699 void HubFrame::slotCopyHubURL(){
3700 Q_D(HubFrame);
3701
3702 if (d->client && d->client->isConnected()){
3703 qApp->clipboard()->setText(_q(d->client->getHubUrl()), QClipboard::Clipboard);
3704 }
3705 }
3706
on(FavoriteManagerListener::UserAdded,const FavoriteUser & aUser)3707 void HubFrame::on(FavoriteManagerListener::UserAdded, const FavoriteUser& aUser) noexcept {
3708 emit coreFavoriteUserAdded(_q(aUser.getUser()->getCID().toBase32()));
3709 }
3710
on(FavoriteManagerListener::UserRemoved,const FavoriteUser & aUser)3711 void HubFrame::on(FavoriteManagerListener::UserRemoved, const FavoriteUser& aUser) noexcept {
3712 emit coreFavoriteUserRemoved(_q(aUser.getUser()->getCID().toBase32()));
3713 }
3714
on(ClientListener::Connecting,Client * c)3715 void HubFrame::on(ClientListener::Connecting, Client *c) noexcept{
3716 Q_D(HubFrame);
3717
3718 QString status = tr("Connecting to %1").arg(QString::fromStdString(d->client->getHubUrl()));
3719
3720 emit coreConnecting(status);
3721 }
3722
on(ClientListener::Connected,Client *)3723 void HubFrame::on(ClientListener::Connected, Client*) noexcept{
3724 Q_D(HubFrame);
3725
3726 QString status = tr("Connected to %1").arg(QString::fromStdString(d->client->getHubUrl()));
3727
3728 emit coreConnected(status);
3729
3730 HubManager::getInstance()->registerHubUrl(_q(d->client->getHubUrl()), this);
3731 }
3732
on(ClientListener::UserUpdated,Client *,const OnlineUser & user)3733 void HubFrame::on(ClientListener::UserUpdated, Client*, const OnlineUser &user) noexcept{
3734 if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3735 return;
3736
3737 emit coreUserUpdated(user.getUser(), user.getIdentity());
3738 }
3739
on(ClientListener::UsersUpdated x,Client *,const OnlineUserList & list)3740 void HubFrame::on(ClientListener::UsersUpdated x, Client*, const OnlineUserList &list) noexcept{
3741 for (const auto &it : list){
3742 const OnlineUser &user = *it;
3743 if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3744 continue;
3745
3746 emit coreUserUpdated(user.getUser(), user.getIdentity());
3747 }
3748 }
3749
on(ClientListener::UserRemoved,Client *,const OnlineUser & user)3750 void HubFrame::on(ClientListener::UserRemoved, Client*, const OnlineUser &user) noexcept{
3751 if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3752 return;
3753
3754 emit coreUserRemoved(user.getUser(), user.getIdentity());
3755 }
3756
on(ClientListener::Redirect,Client *,const string & link)3757 void HubFrame::on(ClientListener::Redirect, Client*, const string &link) noexcept{
3758 if(ClientManager::getInstance()->isConnected(link)) {
3759 emit coreStatusMsg(tr("Redirect request received to a hub that's already connected"));
3760
3761 return;
3762 }
3763
3764 if(BOOLSETTING(AUTO_FOLLOW))
3765 emit coreFollow(_q(link));
3766 }
3767
on(ClientListener::Failed,Client *,const string & msg)3768 void HubFrame::on(ClientListener::Failed, Client*, const string &msg) noexcept{
3769 QString status = tr("Fail: %1...").arg(_q(msg));
3770
3771 emit coreStatusMsg(status);
3772 emit coreFailed();
3773 emit coreHubUpdated();
3774 }
3775
on(GetPassword,Client *)3776 void HubFrame::on(GetPassword, Client*) noexcept{
3777 emit corePassword();
3778 }
3779
on(ClientListener::HubUpdated,Client *)3780 void HubFrame::on(ClientListener::HubUpdated, Client*) noexcept{
3781 emit coreHubUpdated();
3782 }
3783
on(ClientListener::Message,Client *,const ChatMessage & message)3784 void HubFrame::on(ClientListener::Message, Client*, const ChatMessage &message) noexcept{
3785 if (message.text.empty())
3786 return;
3787
3788 VarMap map;
3789 QString msg = _q(message.text);
3790 bool third = false;
3791
3792 if (msg.startsWith("/me ")){
3793 msg.remove(0, 4);
3794
3795 third = true;
3796 }
3797 else
3798 third = message.thirdPerson;
3799
3800 Q_D(HubFrame);
3801
3802 map["HUBURL"] = _q(d->client->getHubUrl());
3803
3804 if(message.to && message.replyTo)
3805 {
3806 //private message
3807 const OnlineUser *user = (message.replyTo->getUser() == ClientManager::getInstance()->getMe())?
3808 message.to : message.replyTo;
3809
3810 bool isBot = user->getIdentity().isBot() || user->getUser()->isSet(User::BOT);
3811 bool isHub = user->getIdentity().isHub();
3812 bool isOp = user->getIdentity().isOp();
3813
3814 if (isHub && BOOLSETTING(IGNORE_HUB_PMS))
3815 return;
3816 else if (isBot && BOOLSETTING(IGNORE_BOT_PMS))
3817 return;
3818
3819 CID id = user->getUser()->getCID();
3820 QString nick = _q(message.from->getIdentity().getNick());
3821 bool isInSandBox = false;
3822 bool isEcho = (message.from->getUser() == ClientManager::getInstance()->getMe());
3823 bool hasPMWindow = d->pm.contains(_q(id.toBase32()));//PMWindow is created
3824
3825 if (AntiSpam::getInstance())
3826 isInSandBox = AntiSpam::getInstance()->isInSandBox(_q(id.toBase32()));
3827
3828 if (AntiSpam::getInstance() && !isEcho){
3829 do {
3830 if (hasPMWindow)
3831 break;
3832
3833 if (isOp && !WBGET(WB_ANTISPAM_FILTER_OPS) && !isBot)
3834 break;
3835
3836 if (AntiSpam::getInstance()->isInBlack(nick))
3837 return;
3838 else if (!(AntiSpam::getInstance()->isInWhite(nick) || AntiSpam::getInstance()->isInGray(nick))){
3839 AntiSpam::getInstance()->checkUser(_q(id.toBase32()), msg, _q(d->client->getHubUrl()));
3840
3841 return;
3842 }
3843 } while (0);
3844 }
3845 else if (isEcho && isInSandBox && !hasPMWindow)
3846 return;
3847
3848 map["NICK"] = nick;
3849 map["MSG"] = msg;
3850 map["TIME"] = QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP));
3851 map["ECHO"] = isEcho;
3852
3853 QString color = WS_CHAT_PRIV_USER_COLOR;
3854
3855 if (nick == _q(d->client->getMyNick()))
3856 color = WS_CHAT_PRIV_LOCAL_COLOR;
3857 else if (isOp)
3858 color = WS_CHAT_OP_COLOR;
3859 else if (isBot)
3860 color = WS_CHAT_BOT_COLOR;
3861 else if (isHub)
3862 color = WS_CHAT_STAT_COLOR;
3863
3864 map["CLR"] = color;
3865 map["3RD"] = third;
3866 map["CID"] = _q(id.toBase32());
3867 map["I4"] = _q(message.from->getIdentity().getIp());
3868
3869 if (WBGET(WB_CHAT_REDIRECT_BOT_PMS) && isBot)
3870 emit coreMessage(map);
3871 else
3872 emit corePrivateMsg(map);
3873
3874 if (!(isBot || isHub) && (message.from->getUser() != ClientManager::getInstance()->getMe()) && Util::getAway() && !hasPMWindow)
3875 ClientManager::getInstance()->privateMessage(HintedUser(user->getUser(), d->client->getHubUrl()), Util::getAwayMessage(), false);
3876
3877 if (BOOLSETTING(LOG_PRIVATE_CHAT)){
3878 string info = Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
3879 QString qinfo = !info.empty() ? _q(info) : "";
3880
3881 StringMap params;
3882 params["message"] = _tq(qinfo + "<" + nick + "> " + msg);
3883 params["hubNI"] = _tq(WulforUtil::getInstance()->getHubNames(id));
3884 params["hubURL"] = d->client->getHubUrl();
3885 params["userCID"] = id.toBase32();
3886 params["userNI"] = user->getIdentity().getNick();
3887 params["myCID"] = ClientManager::getInstance()->getMe()->getCID().toBase32();
3888 params["userI4"] = message.from->getIdentity().getIp();
3889 LOG(LogManager::PM, params);
3890 }
3891 }
3892 else
3893 {
3894 // chat message
3895 const OnlineUser *user = message.from;
3896
3897 if (d->chatDisabled)
3898 return;
3899
3900 if (AntiSpam::getInstance() && AntiSpam::getInstance()->isInBlack(_q(user->getIdentity().getNick())))
3901 return;
3902
3903 map["NICK"] = _q(user->getIdentity().getNick());
3904 map["MSG"] = msg;
3905 map["TIME"] = QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP));
3906
3907 QString color = WS_CHAT_USER_COLOR;
3908
3909 if (user->getIdentity().isHub())
3910 color = WS_CHAT_STAT_COLOR;
3911 else if (user->getUser() == d->client->getMyIdentity().getUser())
3912 color = WS_CHAT_LOCAL_COLOR;
3913 else if (user->getIdentity().isOp())
3914 color = WS_CHAT_OP_COLOR;
3915 else if (user->getIdentity().isBot())
3916 color = WS_CHAT_BOT_COLOR;
3917
3918 if (FavoriteManager::getInstance()->isFavoriteUser(user->getUser()))
3919 color = WS_CHAT_FAVUSER_COLOR;
3920
3921 map["CLR"] = color;
3922 map["3RD"] = third;
3923 map["I4"] = _q(user->getIdentity().getIp());
3924
3925 emit coreMessage(map);
3926
3927 if (BOOLSETTING(LOG_MAIN_CHAT)){
3928 string info = Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
3929 QString qinfo = !info.empty() ? _q(info) : "";
3930 QString nick = _q(user->getIdentity().getNick());
3931
3932 StringMap params;
3933 params["message"] = _tq(qinfo + "<" + nick + "> " + msg);
3934 d->client->getHubIdentity().getParams(params, "hub", false);
3935 params["hubURL"] = d->client->getHubUrl();
3936 params["userNI"] = _tq(nick);
3937 params["userI4"] = user->getIdentity().getIp();
3938 d->client->getMyIdentity().getParams(params, "my", true);
3939 LOG(LogManager::CHAT, params);
3940 }
3941 }
3942 }
3943
on(ClientListener::StatusMessage,Client *,const string & msg,int)3944 void HubFrame::on(ClientListener::StatusMessage, Client*, const string &msg, int) noexcept{
3945 QString status = QString("%1 ").arg(_q(msg));
3946
3947 emit coreStatusMsg(status);
3948
3949 Q_D(HubFrame);
3950
3951 if (BOOLSETTING(LOG_STATUS_MESSAGES)){
3952 StringMap params;
3953 d->client->getHubIdentity().getParams(params, "hub", false);
3954 params["hubURL"] = d->client->getHubUrl();
3955 d->client->getMyIdentity().getParams(params, "my", true);
3956 params["message"] = msg;
3957 LOG(LogManager::STATUS, params);
3958 }
3959 }
3960
on(ClientListener::NickTaken,Client *)3961 void HubFrame::on(ClientListener::NickTaken, Client*) noexcept{
3962 Q_D(HubFrame);
3963 QString status = tr("Sorry, but nick \"%1\" is already taken by another user.").arg(d->client->getCurrentNick().c_str());
3964
3965 emit coreStatusMsg(status);
3966 }
3967
on(ClientListener::SearchFlood,Client *,const string & str)3968 void HubFrame::on(ClientListener::SearchFlood, Client*, const string &str) noexcept{
3969 emit coreStatusMsg(tr("Search flood detected: %1").arg(_q(str)));
3970 }
3971