Added spawnCrystalItem
[ryzomcore.git] / ryzom / client / src / interface_v3 / chat_window.cpp
blob7e9dcf21cec8a8e5362a88c61edd5e817872a53f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 //
9 // This program is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU Affero General Public License as
11 // published by the Free Software Foundation, either version 3 of the
12 // License, or (at your option) any later version.
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU Affero General Public License for more details.
19 // You should have received a copy of the GNU Affero General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 // client
26 #include "stdpch.h"
28 #include "game_share/shard_names.h"
30 #include "../r2/editor.h"
32 #include "chat_window.h"
33 #include "chat_text_manager.h"
34 #include "../user_entity.h"
35 #include "people_interraction.h"
36 #include "../connection.h"
38 #include "nel/gui/group_container.h"
39 #include "nel/gui/group_editbox.h"
40 #include "nel/gui/group_tab.h"
41 #include "interface_manager.h"
42 #include "nel/gui/action_handler.h"
43 #include "../client_chat_manager.h"
45 #include "../session_browser_impl.h"
47 #include "../r2/editor.h"
48 #include "../r2/dmc/client_edition_module.h"
50 using namespace NLMISC;
51 using namespace std;
53 ////////////
54 // STATIC //
55 ////////////
57 CChatWindow *CChatWindow::_ChatWindowLaunchingCommand = NULL;
60 ////////////
61 // EXTERN //
62 ////////////
64 extern NLMISC::CLog g_log;
65 extern CClientChatManager ChatMngr;
67 /////////////////////
68 // CChatWindowDesc //
69 /////////////////////
71 CChatWindowDesc::CChatWindowDesc() : InsertPosition(-1),
72 ParentBlink(false),
73 Savable(false),
74 Localize(false),
75 Listener(NULL)
79 /////////////////
80 // CChatWindow //
81 /////////////////
83 //=================================================================================
84 CChatWindow::CChatWindow() : _Listener(NULL), _Chat(NULL), _EB(NULL), _ParentBlink(false)
88 //=================================================================================
89 bool CChatWindow::create(const CChatWindowDesc &desc, const std::string &chatId)
91 deleteContainer();
92 CInterfaceManager *im = CInterfaceManager::getInstance();
94 // get the father container
95 CGroupContainer *fatherContainer = NULL;
96 if (!desc.FatherContainer.empty())
98 if (desc.FatherContainer != "ui:interface" )
100 fatherContainer = dynamic_cast<CGroupContainer *>(CWidgetManager::getInstance()->getElementFromId(desc.FatherContainer));
101 if (!fatherContainer)
103 nlwarning("<CChatWindow::create> Can't get father group, or bad type");
104 return false;
109 // get the good template
110 std::string chatTemplate;
111 if (desc.ChatTemplate.empty())
113 // no chat template provided : use default
114 chatTemplate = "chat_id";
116 else
118 chatTemplate = desc.ChatTemplate;
121 // build params
122 CChatWindowDesc::TTemplateParams params;
123 params.push_back(make_pair(string("id"), chatId));
124 params.insert(params.end(), desc.ChatTemplateParams.begin(), desc.ChatTemplateParams.end());
126 // create a chat container from the template
127 CInterfaceGroup *chatGroup = CWidgetManager::getInstance()->getParser()->createGroupInstance(chatTemplate, "ui:interface", params);
128 if (chatGroup)
130 _Chat = dynamic_cast<CGroupContainer *>(chatGroup);
131 if (!_Chat)
133 nlwarning("<CChatWindow::create> Bad type for chat group");
134 delete chatGroup;
135 return false;
137 _Chat->setLocalize (desc.Localize);
138 _Chat->setTitle(desc.Title);
139 _Chat->setSavable(desc.Savable);
141 // groups like system info don't have edit box.
142 _EB = dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
143 if (_EB)
145 _EB->setAHOnEnter("chat_box_entry");
148 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
150 if (fatherContainer)
152 fatherContainer->attachContainer(_Chat, desc.InsertPosition);
155 // If root container
156 if (desc.FatherContainer == "ui:interface")
158 CWidgetManager::getInstance()->addWindowToMasterGroup("ui:interface", _Chat);
159 _Chat->setParent(pRoot);
160 _Chat->setMovable(true);
161 _Chat->setActive(false);
162 _Chat->setOpen(true);
165 _ParentBlink = desc.ParentBlink;
166 _Listener = desc.Listener;
168 pRoot->addGroup (_Chat);
169 return true;
171 else
173 return false;
178 //=================================================================================
179 bool CChatWindow::isVisible() const
181 if (!_Chat) return false;
182 if (_Chat->isOpen())
184 CInterfaceGroup *ig = _Chat;
187 if (ig->isGroupContainer())
189 if (!static_cast<CGroupContainer *>(ig)->isOpen()) break;
191 if (!ig->getActive()) break;
192 ig = ig->getParent();
194 while(ig);
195 return ig == NULL; // all parent windows must be open & visible
197 else
199 return false;
203 //=================================================================================
204 void CChatWindow::displayMessage(const string &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks /* = 0*/, bool *windowVisible /*= NULL*/)
206 if (!_Chat)
208 if (msg != "WRN: <CChatWindow::displayMessage> There's no global chat")
209 nlwarning("<CChatWindow::displayMessage> There's no global chat");
210 return;
212 CGroupList *gl;
214 CChatTextManager &ctm = getChatTextMngr();
216 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("cb:text_list"));
220 bool noTranslation = false;
221 CCDBNodeLeaf *nodeNoTranslation = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:TRANSLATION:" + toUpper(CChatGroup::groupTypeToString(gt)) + ":DISABLE", false);
222 if (nodeNoTranslation)
223 noTranslation = nodeNoTranslation->getValueBool();
225 string msgNoTranslate = msg;
227 if (noTranslation) {
228 string::size_type startTr = msg.find("{:");
229 string::size_type endOfOriginal = msg.find("}@{");
231 if (startTr != string::npos && endOfOriginal != string::npos) {
232 msgNoTranslate = msg.substr(0, startTr) + msg.substr(startTr+5, endOfOriginal-startTr-5);
236 CViewBase *child = ctm.createMsgText(msgNoTranslate, col);
237 if (child)
239 if (gl) gl->addChild(child);
241 // if the group is closed, make it blink
242 if (!_Chat->isOpen())
244 if (numBlinks) _Chat->enableBlink(numBlinks);
246 if (_ParentBlink)
248 CGroupContainer *father = dynamic_cast<CGroupContainer *>(_Chat->getParent());
249 if (father && !father->isOpen())
251 father->enableBlink(numBlinks);
254 if (windowVisible != NULL)
256 *windowVisible = isVisible();
258 /*for(std::vector<IObserver *>::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
260 (*it)->displayMessage(this, msg, col, numBlinks);
265 //=================================================================================
266 void CChatWindow::setMenu(const std::string &menuName)
268 if (!_Chat) return;
269 if (_Chat->getHeaderOpened())
271 _Chat->getHeaderOpened()->setRightClickHandler("active_menu");
272 _Chat->getHeaderOpened()->setRightClickHandlerParams("menu=" + menuName);
274 if (_Chat->getHeaderClosed())
276 _Chat->getHeaderClosed()->setRightClickHandler("active_menu");
277 _Chat->getHeaderClosed()->setRightClickHandlerParams("menu=" + menuName);
281 //=================================================================================
282 void CChatWindow::setPrompt(const string &prompt)
284 if (!_Chat) return;
285 CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
286 if (!eb) return;
287 eb->setPrompt(prompt);
290 void CChatWindow::setPromptColor(NLMISC::CRGBA col)
292 if (!_Chat)
293 return;
295 CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
296 if (!eb)
297 return;
299 eb->setColor(col);
302 //=================================================================================
303 void CChatWindow::deleteContainer()
305 if (!_Chat) return;
306 CGroupContainer *proprietaryContainer = _Chat->getProprietaryContainer();
307 if (proprietaryContainer)
309 if (_Chat->isPopuped())
311 _Chat->popin(-1, false); // popin & detach
313 else
315 proprietaryContainer->detachContainer(_Chat); // just detach
317 CInterfaceManager *im = CInterfaceManager::getInstance();
318 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
319 pRoot->delGroup (_Chat);
321 else
323 CWidgetManager::getInstance()->unMakeWindow(_Chat);
324 if (_Chat->getParent())
326 _Chat->getParent()->delGroup(_Chat);
329 // Removes from parent group
331 _Chat = NULL;
334 //=================================================================================
335 bool CChatWindow::rename(const string &newName, bool newNameLocalize)
337 return getChatWndMgr().rename(getTitle(), newName, newNameLocalize);
340 //=================================================================================
341 void CChatWindow::setKeyboardFocus()
343 if (!_EB || !_Chat) return;
344 CWidgetManager::getInstance()->setCaptureKeyboard(_EB);
345 if (!_Chat->isOpenable() || _Chat->isOpenWhenPopup())
347 if (_Chat->isPopable() && !_Chat->isPopuped())
349 _Chat->popup();
350 if (_Chat->getPopupW() != -1) // restore previous popup position if there's one
352 _Chat->setX(_Chat->getPopupX());
353 _Chat->setY(_Chat->getPopupY());
354 _Chat->setW(_Chat->getPopupW());
355 // must resize the children to get correct height
356 //_Chat->setChildrenH(_Chat->getPopupChildrenH());
362 //=================================================================================
363 void CChatWindow::enableBlink(uint numBlinks)
365 if (!_Chat) return;
366 _Chat->enableBlink(numBlinks);
370 //=================================================================================
371 void CChatWindow::setCommand(const std::string &command, bool execute)
373 if (!_EB) return;
374 _EB->setCommand(command, execute);
377 //=================================================================================
378 void CChatWindow::setEntry(const string &entry)
380 if (!_EB) return;
381 _EB->setInputString(entry);
384 //=================================================================================
385 string CChatWindow::getTitle() const
387 if (!_Chat)
389 return string();
391 else
393 return _Chat->getTitle();
397 //=================================================================================
398 void CChatWindow::addObserver(IObserver *obs)
400 if (!obs)
402 nlwarning("NULL observer is invalid");
403 return;
405 if (isObserver(obs))
407 nlwarning("Observer added twice");
408 return;
410 _Observers.push_back(obs);
413 //=================================================================================
414 void CChatWindow::removeObserver(IObserver *obs)
416 std::vector<IObserver *>::iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
417 if (it == _Observers.end())
419 nlwarning("Observer doesn't belong to this chatbox");
420 return;
422 _Observers.erase(it);
425 //=================================================================================
426 bool CChatWindow::isObserver(const IObserver *obs) const
428 std::vector<IObserver *>::const_iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
429 return it != _Observers.end();
432 //=================================================================================
433 CChatWindow::~CChatWindow()
435 for(std::vector<IObserver *>::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
437 (*it)->chatWindowRemoved(this);
439 if (this == _ChatWindowLaunchingCommand)
441 _ChatWindowLaunchingCommand = NULL;
445 //=================================================================================
446 void CChatWindow::setAHOnActive(const std::string &n)
448 if (_Chat) _Chat->setOnActiveHandler(n);
451 //=================================================================================
452 void CChatWindow::setAHOnActiveParams(const std::string &n)
454 if (_Chat) _Chat->setOnActiveParams(n);
457 //=================================================================================
458 void CChatWindow::setAHOnDeactive(const std::string &n)
460 if (_Chat) _Chat->setOnDeactiveHandler(n);
463 //=================================================================================
464 void CChatWindow::setAHOnDeactiveParams(const std::string &n)
466 if (_Chat) _Chat->setOnDeactiveParams(n);
469 //=================================================================================
470 void CChatWindow::setAHOnCloseButton(const std::string &n)
472 if (_Chat) _Chat->setOnCloseButtonHandler(n);
475 //=================================================================================
476 void CChatWindow::setAHOnCloseButtonParams(const std::string &n)
478 if (_Chat) _Chat->setOnCloseButtonParams(n);
481 //=================================================================================
482 void CChatWindow::setHeaderColor(const std::string &n)
484 if (_Chat) _Chat->setHeaderColor(n);
487 //=================================================================================
488 void CChatWindow::displayLocalPlayerTell(const string &receiver, const string &msg, uint numBlinks /*= 0*/)
490 string finalMsg;
491 CInterfaceProperty prop;
492 prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," ");
493 encodeColorTag(prop.getRGBA(), finalMsg, false);
495 string csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : "");
496 finalMsg += csr + CI18N::get("youTell") + ": ";
497 prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," ");
498 encodeColorTag(prop.getRGBA(), finalMsg, true);
499 finalMsg += msg;
501 string s = CI18N::get("youTellPlayer");
502 strFindReplace(s, "%name", receiver);
503 strFindReplace(finalMsg, CI18N::get("youTell"), s);
504 displayMessage(finalMsg, prop.getRGBA(), CChatGroup::tell, 0, numBlinks);
505 CInterfaceManager::getInstance()->log(finalMsg, CChatGroup::groupTypeToString(CChatGroup::tell));
508 void CChatWindow::encodeColorTag(const NLMISC::CRGBA &color, std::string &text, bool append)
510 // WARNING : The lookup table MUST contains 17 element (with the last doubled)
511 // because we add 7 to the 8 bit color before shifting to right in order to match color
512 // more accurately.
513 // Have 17 entry remove the need for a %16 for each color component.
514 // By the way, this comment is more longer to type than to add the %16...
516 static char ConvTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'F'};
517 string str;
518 if (append)
520 str.reserve(7 + str.size());
521 str = text;
523 else
524 str.reserve(7);
526 str += "@{";
527 str += ConvTable[(uint(color.R)+7)>>4];
528 str += ConvTable[(uint(color.G)+7)>>4];
529 str += ConvTable[(uint(color.B)+7)>>4];
530 str += ConvTable[(uint(color.A)+7)>>4];
531 str += '}';
533 text.swap(str);
537 //=================================================================================
538 void CChatWindow::clearMessages(CChatGroup::TGroupType /* gt */, uint32 /* dynamicChatDbIndex */)
540 // if not correctly init, abort
541 if(!_Chat)
542 return;
544 // get the group list
545 CGroupList *gl = dynamic_cast<CGroupList *>(_Chat->getGroup("cb:text_list"));
546 if (gl) gl->deleteAllChildren();
550 //////////////////////
551 // CChatGroupWindow //
552 //////////////////////
554 void CChatGroupWindow::displayMessage(const string &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks, bool *windowVisible)
556 if (!_Chat)
558 if (msg != "WRN: <CChatGroupWindow::displayMessage> There's no global chat")
559 nlwarning("<CChatGroupWindow::displayMessage> There's no global chat");
560 return;
563 CChatTextManager &ctm = getChatTextMngr();
565 if (_Chat->getHeaderOpened()==NULL)
566 return;
569 // *** Display the message in the correct tab window
570 // get the gl and tab according to filter
571 CGroupList *gl;
572 CCtrlTabButton *tab;
573 getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
575 // on a new message, change the Tab color
576 CInterfaceManager *pIM= CInterfaceManager::getInstance();
577 CRGBA newMsgColor= CRGBA::stringToRGBA(CWidgetManager::getInstance()->getParser()->getDefine("chat_group_tab_color_newmsg").c_str());
579 string newmsg = msg;
580 string prefix;
582 bool noTranslation = false;
583 CCDBNodeLeaf *nodeNoTranslation = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:TRANSLATION:" + toUpper(CChatGroup::groupTypeToString(gt)) + ":DISABLE", false);
584 if (nodeNoTranslation)
585 noTranslation = nodeNoTranslation->getValueBool();
587 if (noTranslation) {
588 string::size_type startTr = msg.find("{:");
589 string::size_type endOfOriginal = msg.find("}@{");
591 if (startTr != string::npos && endOfOriginal != string::npos) {
592 newmsg = newmsg.substr(0, startTr) + newmsg.substr(startTr+5, endOfOriginal-startTr-5);
596 CViewBase *child = NULL;
597 if (gl != NULL)
599 child = ctm.createMsgText(newmsg, col);
600 if (child)
602 gl->addChild(child);
603 if (!gl->getParent()->getActive())
604 if (tab != NULL)
605 tab->setTextColorNormal(newMsgColor);
609 // *** Display the message in the UserChat (special case)
611 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab5"));
612 gl = NULL;
613 CGroupList *gl2 = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:user:text_list"));
615 CChatWindow *cw = PeopleInterraction.TheUserChat.Window;
616 CChatStdInput &ci = PeopleInterraction.ChatInput;
618 switch(gt)
620 default:
621 case CChatGroup::arround:
622 case CChatGroup::say: if (ci.AroundMe.isListeningWindow(cw)) gl = gl2; break;
623 case CChatGroup::region: if (ci.Region.isListeningWindow(cw)) gl = gl2; break;
624 case CChatGroup::team: if (ci.Team.isListeningWindow(cw)) gl = gl2; break;
625 case CChatGroup::guild: if (ci.Guild.isListeningWindow(cw)) gl = gl2; break;
626 case CChatGroup::system: if (ci.SystemInfo.isListeningWindow(cw)) gl = gl2; break;
627 case CChatGroup::universe: if (ci.Universe.isListeningWindow(cw)) gl = gl2; break;
628 case CChatGroup::dyn_chat:
629 if (ci.DynamicChat[dynamicChatDbIndex].isListeningWindow(cw))
631 gl = gl2;
633 // Add dyn chan number before string
634 string prefix = "[" + NLMISC::toString(dynamicChatDbIndex) + "]";
635 // Find position to put the new string
636 // After timestamp?
637 size_t pos = newmsg.find("]");
638 size_t colonpos = newmsg.find(": @{");
639 // If no ] found or if found but after the colon (so part of the user chat)
640 if (pos == string::npos || (colonpos < pos))
642 // No timestamp, so put it right after the color and add a space
643 pos = newmsg.find("}");
644 prefix += " ";
647 if (pos == string::npos)
648 newmsg = prefix + newmsg;
649 else
650 newmsg = newmsg.substr(0, pos + 1) + prefix + newmsg.substr(pos + 1);
652 // Add dynchannel number and optionally name before text if user channel
653 CCDBNodeLeaf* node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB", false);
654 if (node && node->getValueBool())
656 uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(dynamicChatDbIndex);
657 string title;
658 STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
659 prefix = (title.empty() ? "" : " ") + title;
660 pos = newmsg.find("] ");
662 if (pos == string::npos)
663 newmsg = prefix + newmsg;
664 else
665 newmsg = newmsg.substr(0, pos) + prefix + newmsg.substr(pos);
668 break;
671 if (gl != NULL)
673 child = ctm.createMsgText(newmsg, col);
674 if (child)
676 gl->addChild(child);
677 if (!gl->getParent()->getActive())
678 if (tab != NULL)
679 tab->setTextColorNormal(newMsgColor);
684 if (child)
686 // *** Blink and visibility event
687 // if the group is closed, make it blink
688 if (!_Chat->isOpen())
690 if (numBlinks) _Chat->enableBlink(numBlinks);
692 if (_ParentBlink)
694 CGroupContainer *father = dynamic_cast<CGroupContainer *>(_Chat->getParent());
695 if (father && !father->isOpen())
697 father->enableBlink(numBlinks);
700 if (windowVisible != NULL)
702 *windowVisible = isVisible();
707 //=================================================================================
708 void CChatGroupWindow::displayTellMessage(const string &msg, NLMISC::CRGBA col, const string &sender)
710 // If we are here with a tell message this is because the teller doesn't belong to any people list
711 CGroupContainer *gcChat = createFreeTeller(sender);
712 if (gcChat == NULL)
714 nlwarning("<CChatGroupWindow::displayTellMessage> cannot open chat.");
715 return;
718 gcChat->requireAttention();
720 CWidgetManager::getInstance()->setTopWindow(gcChat);
722 // add the text to this window
723 CGroupList *gl = dynamic_cast<CGroupList *>(gcChat->getGroup("text_list"));
724 if (gl == NULL)
726 nlwarning("<CChatGroupWindow::displayTellMessage> can't get text_list.");
727 return;
729 CViewBase *child = getChatTextMngr().createMsgText(msg, col);
730 if (child)
731 gl->addChild(child);
734 //=================================================================================
735 sint32 CChatGroupWindow::getTabIndex()
737 CGroupTab *pTab = dynamic_cast<CGroupTab*>(_Chat->getGroup("header_opened:channel_select"));
738 if (pTab != NULL)
739 return pTab->getSelection();
740 else
741 return -1;
744 //=================================================================================
745 void CChatGroupWindow::setTabIndex(sint32 n)
747 CGroupTab *pTab = dynamic_cast<CGroupTab*>(_Chat->getGroup("header_opened:channel_select"));
748 if (pTab != NULL)
750 pTab->select(n);
751 // if the current button is hidden, select default not hid
752 pTab->selectDefaultIfCurrentHid();
756 //=================================================================================
757 const string CChatGroupWindow::getValidUiStringId(const string &stringId)
759 string validStringId;
761 for (uint32 i=0; i < stringId.length(); i++)
763 if ((stringId[i] < 'a') || (stringId[i] > 'z'))
764 validStringId += '_';
765 else
766 validStringId += stringId[i];
768 return validStringId;
771 //=================================================================================
772 CGroupContainer *CChatGroupWindow::createFreeTeller(const string &winNameIn, const string &winColor)
774 // must parse the entity name, and eventually make it Full with shard name (eg: 'ani.yoyo' becomes 'yoyo(Aniro)')
775 string winNameFull= CShardNames::getInstance().makeFullNameFromRelative(PlayerSelectedMainland, winNameIn);
777 // remove shard name if necessary
778 string winName= CEntityCL::removeShardFromName(winNameFull);
780 // get the color
781 string sWinColor = winColor;
782 if (sWinColor.empty())
783 sWinColor = "UI:SAVE:WIN:COLORS:COM";
785 // Look if the free teller do not already exists
786 uint32 i;
787 string sWinName = winName;
788 sWinName = toLower(sWinName);
789 for (i = 0; i < _FreeTellers.size(); ++i)
791 CGroupContainer *pGC = _FreeTellers[i];
792 if (toLower(pGC->getTitle()) == sWinName)
793 break;
795 // Create container if not present
796 if (i == _FreeTellers.size())
798 // Corresponding Chat not created -> create and open
799 vector<pair<string ,string> > properties;
800 properties.push_back(make_pair(string("posparent"), string("parent")));
801 properties.push_back(make_pair(string("id"), "free_chat_" + getValidUiStringId(sWinName)));
802 properties.push_back(make_pair(string("title"), std::string("")));
803 properties.push_back(make_pair(string("header_color"), sWinColor));
805 std::string templateName = "contact_chat_friend";
807 CInterfaceManager *pIM = CInterfaceManager::getInstance();
808 CInterfaceGroup *pIG = CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName, "ui:interface", properties);
809 if (!pIG) return NULL;
810 CGroupContainer *pGC = dynamic_cast<CGroupContainer *>(pIG);
811 if (!pGC)
813 delete pIG;
814 nlwarning("<CChatGroupWindow::createFreeTeller> group is not a container.(%s)", winName.c_str());
815 return NULL;
817 // set title from the name
818 pGC->setTitle(winName);
820 pGC->setSavable(true);
821 pGC->setEscapable(true);
823 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
824 pRoot->addGroup (pGC);
825 pGC->setParent(pRoot); // must be done before makeWindow
826 CWidgetManager::getInstance()->makeWindow(pGC);
827 pGC->open();
828 pGC->updateCoords();
829 pGC->center();
830 _FreeTellers.push_back(pGC);
832 CInterfaceElement *result = pRoot->findFromShortId(string("invite"));
833 if(result)
835 CCtrlBaseButton * inviteButton = dynamic_cast<CCtrlBaseButton*>(result);
836 if(inviteButton)
838 bool isDM = false;
839 R2::CEditor::TMode mode = R2::getEditor().getMode();
840 if (mode != R2::CEditor::NotInitialized )
842 isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
843 && uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
844 && uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
846 inviteButton->setActive(isDM);
850 // the group is only active on the current desktop
851 pGC->setActive(true);
854 if (!winColor.empty())
855 _FreeTellers[i]->setHeaderColor(winColor);
857 // updateFreeTellerHeader(*_FreeTellers[i]);
858 return _FreeTellers[i];
861 //=================================================================================
862 void CChatGroupWindow::updateAllFreeTellerHeaders()
864 for(uint k = 0; k < _FreeTellers.size(); ++k)
866 if (_FreeTellers[k])
868 updateFreeTellerHeader(*_FreeTellers[k]);
873 //=================================================================================
874 void CChatGroupWindow::updateFreeTellerHeader(CGroupContainer &ft)
876 string name = ft.getTitle();
877 CCtrlBaseButton *newFriendBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("new_friend"));
878 CCtrlBaseButton *ignoreBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("ignore"));
879 CCtrlBaseButton *inviteBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("invite"));
880 CCtrlBaseButton *inviteTeamBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("invite_team"));
881 if (newFriendBut)
883 newFriendBut->setFrozen(PeopleInterraction.isContactInList(name, 0));
885 if (ignoreBut)
887 ignoreBut->setFrozen(PeopleInterraction.isContactInList(name, 1));
890 if (inviteBut)
892 bool isDM = false;
893 R2::CEditor::TMode mode = R2::getEditor().getMode();
894 if (mode != R2::CEditor::NotInitialized )
896 isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
897 && uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
898 && uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
901 inviteBut->setActive(isDM);
902 if (isDM)
904 inviteBut->setFrozen(false); // TODO Boris : true if player is already invited in anim + do
905 // PeopleInterraction.updateAllFreeTellerHeaders() when list is updated
909 if (inviteTeamBut)
911 inviteTeamBut->setActive(PeopleInterraction.isContactOnline(name));
916 //=================================================================================
917 void CChatGroupWindow::setActiveFreeTeller(const string &winName, bool bActive)
919 CGroupContainer *pGC = createFreeTeller(winName);
920 if (pGC != NULL)
921 pGC->setActive(bActive);
924 //=================================================================================
925 string CChatGroupWindow::getFreeTellerName(const std::string &containerID)
927 uint32 i;
928 for (i = 0; i < _FreeTellers.size(); ++i)
930 CGroupContainer *pGC = _FreeTellers[i];
931 if (pGC->getId() == containerID)
932 break;
934 if (i == _FreeTellers.size())
935 return string();
936 return _FreeTellers[i]->getTitle();
939 //=================================================================================
940 bool CChatGroupWindow::removeFreeTeller(const std::string &containerID)
942 uint32 i;
943 for (i = 0; i < _FreeTellers.size(); ++i)
945 CGroupContainer *pGC = _FreeTellers[i];
946 if (pGC->getId() == containerID)
947 break;
949 if (i == _FreeTellers.size())
950 return false;
951 CInterfaceManager *pIM = CInterfaceManager::getInstance();
952 pIM->removeGroupContainerImageFromDesktops(_FreeTellers[i]->getId());
954 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
955 CWidgetManager::getInstance()->unMakeWindow(_FreeTellers[i]);
956 pRoot->delGroup (_FreeTellers[i]);
957 _FreeTellers[i] = NULL;
958 _FreeTellers.erase(_FreeTellers.begin()+i);
959 return true;
962 //=================================================================================
963 void CChatGroupWindow::removeAllFreeTellers()
965 while (!_FreeTellers.empty())
967 if (_FreeTellers[0])
969 removeFreeTeller(_FreeTellers[0]->getId());
971 else
973 _FreeTellers.erase(_FreeTellers.begin());
978 //=================================================================================
979 void CChatGroupWindow::saveFreeTeller(NLMISC::IStream &f)
981 f.serialVersion(3);
983 // Save the free teller only if it is present in the friend list to avoid the only-growing situation
984 // because free tellers are never deleted in game if we save/load all the free tellers, we just create more
985 // and more container.
987 uint32 i, nNbFreeTellerSaved = 0;
988 for (i = 0; i < _FreeTellers.size(); ++i)
989 if (PeopleInterraction.FriendList.getIndexFromName(_FreeTellers[i]->getTitle()) != -1)
990 nNbFreeTellerSaved++;
992 f.serial(nNbFreeTellerSaved);
994 for (i = 0; i < _FreeTellers.size(); ++i)
996 CGroupContainer *pGC = _FreeTellers[i];
998 if (PeopleInterraction.FriendList.getIndexFromName(pGC->getTitle()) != -1)
1000 string sTitle = pGC->getTitle();
1001 f.serial(sTitle);
1006 //=================================================================================
1007 void CChatGroupWindow::loadFreeTeller(NLMISC::IStream &f)
1009 sint ver = f.serialVersion(3);
1011 if (ver == 1)
1013 // Old serialized FreeTellIdCounter (deprecated with v2).
1014 uint32 tmp;
1015 f.serial(tmp);
1018 uint32 i, nNbFreeTellerSaved = 0;
1019 f.serial(nNbFreeTellerSaved);
1021 for (i = 0; i < nNbFreeTellerSaved; ++i)
1023 if (ver == 1)
1025 // Old serialized sID (deprecated with v2).
1026 string sID;
1027 f.serial(sID);
1029 string title;
1030 if (ver < 3)
1032 ucstring sTitle; // Old UTF-16 serial
1033 f.serial(sTitle);
1034 title = sTitle.toUtf8();
1036 else
1037 f.serial(title);
1039 CGroupContainer *pGC = createFreeTeller(title, "");
1041 // With version 1 all tells are active because windows information have "title based" ids and no "sID based".
1042 if ((ver == 1) && (pGC != NULL))
1043 pGC->setActive(false);
1047 //=================================================================================
1048 void CChatGroupWindow::clearMessages(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex)
1050 // if not correctly init, abort
1051 if(!_Chat)
1052 return;
1054 // get the gl and tab according to filter
1055 CGroupList *gl;
1056 CCtrlTabButton *tab;
1057 getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
1059 // delete all text lines
1060 if(gl!=NULL)
1062 gl->deleteAllChildren();
1066 //=================================================================================
1067 void CChatGroupWindow::getAssociatedSubWindow(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, CGroupList *&gl, CCtrlTabButton *&tab)
1069 nlassert(_Chat);
1070 gl= NULL;
1071 tab= NULL;
1073 switch(gt)
1075 default:
1076 case CChatGroup::say:
1077 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:around:text_list"));
1078 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab0"));
1079 break;
1080 case CChatGroup::team:
1081 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:team:text_list"));
1082 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab2"));
1083 break;
1084 case CChatGroup::guild:
1085 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:guild:text_list"));
1086 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab3"));
1087 break;
1088 case CChatGroup::system:
1089 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:sysinfo:text_list"));
1090 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab4"));
1091 break;
1092 case CChatGroup::region:
1093 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:region:text_list"));
1094 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab1"));
1095 break;
1096 case CChatGroup::dyn_chat:
1098 // use dynamicChatDbIndex to get the wanted tab button/group
1099 gl = dynamic_cast<CGroupList *>(_Chat->getGroup(toString("content:cb:dyn_chat%d:text_list", dynamicChatDbIndex)));
1100 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl(toString("header_opened:channel_select:tab_array0_%d",dynamicChatDbIndex)));
1102 break;
1103 case CChatGroup::universe:
1104 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:universe:text_list"));
1105 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab6"));
1106 break;
1111 ////////////////////////
1112 // CChatWindowManager //
1113 ////////////////////////
1115 //=================================================================================
1116 CChatWindowManager::CChatWindowManager() : _WindowID(0)
1120 //=================================================================================
1121 CChatWindowManager::~CChatWindowManager()
1123 for(TChatWindowMap::iterator it = _ChatWindowMap.begin(); it != _ChatWindowMap.end(); ++it)
1125 delete it->second;
1129 //=================================================================================
1130 CChatWindow *CChatWindowManager::createChatWindow(const CChatWindowDesc &desc)
1132 if (getChatWindow(desc.Title))
1134 return NULL; // duplicate name encountered
1136 CChatWindow *w;
1137 w = new CChatWindow;
1138 string zeId;
1139 if (desc.Id.empty())
1140 zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
1141 else
1142 zeId = desc.Id;
1144 if (w->create(desc, zeId))
1146 if (desc.Id.empty())
1147 _WindowID++;
1149 _ChatWindowMap[desc.Title] = w;
1151 w->setAHOnActive(desc.AHOnActive);
1152 w->setAHOnActiveParams(desc.AHOnActiveParams);
1153 w->setAHOnDeactive(desc.AHOnDeactive);
1154 w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
1155 w->setAHOnCloseButton(desc.AHOnCloseButton);
1156 w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
1157 if (!desc.HeaderColor.empty())
1158 w->setHeaderColor(desc.HeaderColor);
1160 return w;
1162 else
1164 return NULL;
1168 //=================================================================================
1169 CChatWindow *CChatWindowManager::createChatGroupWindow(const CChatWindowDesc &desc)
1171 if (getChatWindow(desc.Title))
1173 return NULL; // duplicate name encountered
1175 CChatGroupWindow *w;
1176 w = new CChatGroupWindow;
1177 string zeId;
1178 if (desc.Id.empty())
1179 zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
1180 else
1181 zeId = desc.Id;
1183 if (w->create(desc, zeId))
1185 if (desc.Id.empty())
1186 _WindowID++;
1188 _ChatWindowMap[desc.Title] = w;
1190 w->setAHOnActive(desc.AHOnActive);
1191 w->setAHOnActiveParams(desc.AHOnActiveParams);
1192 w->setAHOnDeactive(desc.AHOnDeactive);
1193 w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
1194 w->setAHOnCloseButton(desc.AHOnCloseButton);
1195 w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
1196 if (!desc.HeaderColor.empty())
1197 w->setHeaderColor(desc.HeaderColor);
1200 // because root group was created from template, element from scrollbar target attribute was not created yet
1201 CInterfaceGroup *pIG = w->getContainer()->getGroup("header_opened:channel_select");
1202 if (pIG)
1204 CCtrlScroll *sb = dynamic_cast<CCtrlScroll*>(w->getContainer()->getCtrl("channel_scroll"));
1205 if (sb) sb->setTarget(pIG);
1208 return w;
1210 else
1212 return NULL;
1216 //=================================================================================
1217 CChatWindow *CChatWindowManager::getChatWindow(const string &title)
1219 TChatWindowMap::iterator it = _ChatWindowMap.find(title);
1220 if (it == _ChatWindowMap.end())
1221 return NULL;
1222 else
1224 nlassert(it->second != NULL);
1225 return it->second;
1229 //=================================================================================
1230 void CChatWindowManager::removeChatWindow(const string &title)
1232 TChatWindowMap::iterator it = _ChatWindowMap.find(title);
1233 if (it == _ChatWindowMap.end())
1235 nlwarning("Unknown chat window '%s'", title.c_str());
1236 return;
1238 it->second->deleteContainer();
1239 delete it->second;
1240 _ChatWindowMap.erase(it);
1243 //=================================================================================
1244 CChatWindowManager &CChatWindowManager::getInstance()
1246 static CChatWindowManager instance;
1247 return instance;
1250 //=================================================================================
1251 CChatWindow *CChatWindowManager::getChatWindowFromCaller(CCtrlBase *caller)
1253 // retrieve pointer on the CChatWindow instance associated with the ui
1254 // find first enclosing group container
1255 CGroupContainer *father = NULL;
1256 while (caller)
1258 father = dynamic_cast<CGroupContainer *>(caller);
1259 if (father) break;
1260 caller = caller->getParent();
1262 if (!father) return NULL;
1264 return getChatWindow(father->getTitle());
1267 //=================================================================================
1268 bool CChatWindowManager::rename(const string &oldName, const string &newName, bool newNameLocalize)
1270 // if (oldName == newName) return true;
1271 CChatWindow *newWin = getChatWindow(newName);
1272 if (newWin != NULL) return false; // target window exists
1273 TChatWindowMap::iterator it = _ChatWindowMap.find(oldName);
1274 if (it == _ChatWindowMap.end()) return false;
1275 _ChatWindowMap[newName] = it->second;
1276 it->second->getContainer()->setLocalize(newNameLocalize);
1277 it->second->getContainer()->setTitle(newName);
1278 _ChatWindowMap.erase(it);
1279 return true;
1282 //=================================================================================
1283 CGroupEditBox *CChatWindow::getEditBox() const
1285 if (!_Chat) return NULL;
1286 return dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
1289 //=================================================================================
1290 void CChatWindowManager::removeChatWindow(CChatWindow *cw)
1292 if (!cw) return;
1293 removeChatWindow(cw->getTitle());
1296 //=================================================================================
1297 CChatWindow *CChatWindowManager::getChatWindowByIndex(uint index)
1299 TChatWindowMap::iterator it = _ChatWindowMap.begin();
1300 while (index--) { ++it; }
1301 return it->second;
1304 /////////////////////
1305 // ACTION HANDLERS //
1306 /////////////////////
1309 // ***************************************************************************************
1310 class CHandlerChatBoxEntry : public IActionHandler
1312 public:
1313 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1315 CGroupEditBox *pEB = dynamic_cast<CGroupEditBox*>(pCaller);
1316 if (pEB == NULL) return;
1317 string text = pEB->getInputString();
1318 // If the line is empty, do nothing
1319 if(text.empty())
1320 return;
1323 CChatWindow *chat = getChatWndMgr().getChatWindowFromCaller(pCaller);
1324 if (!chat)
1326 nlwarning("No chat box associated with %s", pEB->getId().c_str());
1327 return;
1330 // Parse any tokens in the text
1331 if ( ! CInterfaceManager::parseTokens(text))
1333 pEB->setInputString(std::string());
1334 return;
1337 // if, it s a command, execute it and don't send the command to the server
1338 if(text[0] == '/')
1340 CChatWindow::_ChatWindowLaunchingCommand = chat;
1341 string str = text;
1342 string cmdWithArgs = str.substr(1);
1344 // Get the command name from the string, can contain spaces
1345 string cmd = cmdWithArgs.substr(0, cmdWithArgs.find(' '));
1346 if (cmdWithArgs.find('"') == 0)
1348 string::size_type pos = cmdWithArgs.find('"', 1);
1349 if (string::npos != pos)
1351 cmd = cmdWithArgs.substr(1, pos - 1);
1355 if ( NLMISC::ICommand::exists( cmd ) )
1357 NLMISC::ICommand::execute( cmdWithArgs, g_log );
1359 else
1361 CInterfaceManager *im = CInterfaceManager::getInstance();
1362 im->displaySystemInfo (cmd + ": " + CI18N::get ("uiCommandNotExists"));
1365 else
1367 if (chat->getListener())
1369 chat->getListener()->msgEntered(text, chat);
1372 // Clear input string
1373 pEB->setInputString (std::string());
1374 CGroupContainer *gc = static_cast< CGroupContainer* >( pEB->getEnclosingContainer() );
1376 if (gc)
1378 // Restore position of enclosing container if it hasn't been moved/scaled/poped by the user
1379 if (!gc->getTouchFlag(true))
1381 gc->restorePosition();
1386 REGISTER_ACTION_HANDLER(CHandlerChatBoxEntry, "chat_box_entry");
1390 static string getFreeTellerName(CInterfaceElement *pCaller)
1392 if (!pCaller) return string();
1393 CChatGroupWindow *cgw = PeopleInterraction.getChatGroupWindow();
1394 if (!cgw) return string();
1395 CInterfaceGroup *freeTeller = pCaller->getParentContainer();
1396 if (!freeTeller) return string();
1397 return cgw->getFreeTellerName( freeTeller->getId() );
1400 // ***************************************************************************************
1401 class CHandlerAddTellerToFriendList : public IActionHandler
1403 public:
1404 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1406 string playerName = ::getFreeTellerName(pCaller);
1407 if (!playerName.empty())
1409 sint playerIndex = PeopleInterraction.IgnoreList.getIndexFromName(playerName);
1410 // if already in friend list, ask to move rather than add
1411 if (playerIndex != -1)
1413 PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.IgnoreList, &PeopleInterraction.FriendList);
1415 else
1417 PeopleInterraction.askAddContact(playerName, &PeopleInterraction.FriendList);
1422 REGISTER_ACTION_HANDLER(CHandlerAddTellerToFriendList, "add_teller_to_friend_list");
1425 // ***************************************************************************************
1426 class CHandlerAddTellerToIgnoreList : public IActionHandler
1428 public:
1429 void execute (CCtrlBase *pCaller, const std::string &sParams)
1431 CInterfaceManager *im = CInterfaceManager::getInstance();
1432 std::string callerId = getParam(sParams, "id");
1433 CInterfaceElement *prevCaller = CWidgetManager::getInstance()->getElementFromId(callerId);
1434 string playerName = ::getFreeTellerName(prevCaller);
1435 if (!playerName.empty())
1437 // if already in friend list, ask to move rather than add
1438 sint playerIndex = PeopleInterraction.FriendList.getIndexFromName(playerName);
1439 if (playerIndex != -1)
1441 PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.FriendList, &PeopleInterraction.IgnoreList);
1443 else
1445 PeopleInterraction.askAddContact(playerName, &PeopleInterraction.IgnoreList);
1447 if (pCaller)
1449 CInterfaceGroup *win = prevCaller->getParentContainer();
1450 if (win)
1452 static_cast< CGroupContainer* >( win )->setActive(false);
1458 REGISTER_ACTION_HANDLER(CHandlerAddTellerToIgnoreList, "add_teller_to_ignore_list");
1460 // ***************************************************************************************
1461 class CHandlerInviteToRingSession : public IActionHandler
1463 public:
1464 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1466 string playerName = ::getFreeTellerName(pCaller);
1467 if (!playerName.empty())
1469 // ask the SBS to invite the character in the session
1470 CSessionBrowserImpl::getInstance().inviteCharacterByName(CSessionBrowserImpl::getInstance().getCharId(), playerName);
1471 // additionaly, send a tell to signal the player he has been invited to a ring session
1472 ChatMngr.tell(playerName, CI18N::get("uiRingInviteNotification"));
1474 CInterfaceManager *im = CInterfaceManager::getInstance();
1475 im->displaySystemInfo("@{6F6F}" + playerName +" @{FFFF}" + CI18N::get("uiRingInvitationSent"), "BC");
1476 // force a refresh of the ui
1477 CLuaManager::getInstance().executeLuaScript("CharTracking:forceRefresh()");
1481 REGISTER_ACTION_HANDLER(CHandlerInviteToRingSession, "invite_to_ring_session");