Fix css style order when using external css files
[ryzomcore.git] / ryzom / client / src / interface_v3 / chat_window.cpp
blobe1667839d4dd13ba46d62d74075b94b9630888d3
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 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"));
217 if (gl) gl->addChild(ctm.createMsgText(msg, col));
219 // if the group is closed, make it blink
220 if (!_Chat->isOpen())
222 if (numBlinks) _Chat->enableBlink(numBlinks);
224 if (_ParentBlink)
226 CGroupContainer *father = dynamic_cast<CGroupContainer *>(_Chat->getParent());
227 if (father && !father->isOpen())
229 father->enableBlink(numBlinks);
232 if (windowVisible != NULL)
234 *windowVisible = isVisible();
236 /*for(std::vector<IObserver *>::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
238 (*it)->displayMessage(this, msg, col, numBlinks);
242 //=================================================================================
243 void CChatWindow::setMenu(const std::string &menuName)
245 if (!_Chat) return;
246 if (_Chat->getHeaderOpened())
248 _Chat->getHeaderOpened()->setRightClickHandler("active_menu");
249 _Chat->getHeaderOpened()->setRightClickHandlerParams("menu=" + menuName);
251 if (_Chat->getHeaderClosed())
253 _Chat->getHeaderClosed()->setRightClickHandler("active_menu");
254 _Chat->getHeaderClosed()->setRightClickHandlerParams("menu=" + menuName);
258 //=================================================================================
259 void CChatWindow::setPrompt(const string &prompt)
261 if (!_Chat) return;
262 CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
263 if (!eb) return;
264 eb->setPrompt(prompt);
267 void CChatWindow::setPromptColor(NLMISC::CRGBA col)
269 if (!_Chat)
270 return;
272 CGroupEditBox *eb = dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
273 if (!eb)
274 return;
276 eb->setColor(col);
279 //=================================================================================
280 void CChatWindow::deleteContainer()
282 if (!_Chat) return;
283 CGroupContainer *proprietaryContainer = _Chat->getProprietaryContainer();
284 if (proprietaryContainer)
286 if (_Chat->isPopuped())
288 _Chat->popin(-1, false); // popin & detach
290 else
292 proprietaryContainer->detachContainer(_Chat); // just detach
294 CInterfaceManager *im = CInterfaceManager::getInstance();
295 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
296 pRoot->delGroup (_Chat);
298 else
300 CWidgetManager::getInstance()->unMakeWindow(_Chat);
301 if (_Chat->getParent())
303 _Chat->getParent()->delGroup(_Chat);
306 // Removes from parent group
308 _Chat = NULL;
311 //=================================================================================
312 bool CChatWindow::rename(const string &newName, bool newNameLocalize)
314 return getChatWndMgr().rename(getTitle(), newName, newNameLocalize);
317 //=================================================================================
318 void CChatWindow::setKeyboardFocus()
320 if (!_EB || !_Chat) return;
321 CWidgetManager::getInstance()->setCaptureKeyboard(_EB);
322 if (!_Chat->isOpenable() || _Chat->isOpenWhenPopup())
324 if (_Chat->isPopable() && !_Chat->isPopuped())
326 _Chat->popup();
327 if (_Chat->getPopupW() != -1) // restore previous popup position if there's one
329 _Chat->setX(_Chat->getPopupX());
330 _Chat->setY(_Chat->getPopupY());
331 _Chat->setW(_Chat->getPopupW());
332 // must resize the children to get correct height
333 //_Chat->setChildrenH(_Chat->getPopupChildrenH());
339 //=================================================================================
340 void CChatWindow::enableBlink(uint numBlinks)
342 if (!_Chat) return;
343 _Chat->enableBlink(numBlinks);
347 //=================================================================================
348 void CChatWindow::setCommand(const std::string &command, bool execute)
350 if (!_EB) return;
351 _EB->setCommand(command, execute);
354 //=================================================================================
355 void CChatWindow::setEntry(const string &entry)
357 if (!_EB) return;
358 _EB->setInputString(entry);
361 //=================================================================================
362 string CChatWindow::getTitle() const
364 if (!_Chat)
366 return string();
368 else
370 return _Chat->getTitle();
374 //=================================================================================
375 void CChatWindow::addObserver(IObserver *obs)
377 if (!obs)
379 nlwarning("NULL observer is invalid");
380 return;
382 if (isObserver(obs))
384 nlwarning("Observer added twice");
385 return;
387 _Observers.push_back(obs);
390 //=================================================================================
391 void CChatWindow::removeObserver(IObserver *obs)
393 std::vector<IObserver *>::iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
394 if (it == _Observers.end())
396 nlwarning("Observer doesn't belong to this chatbox");
397 return;
399 _Observers.erase(it);
402 //=================================================================================
403 bool CChatWindow::isObserver(const IObserver *obs) const
405 std::vector<IObserver *>::const_iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
406 return it != _Observers.end();
409 //=================================================================================
410 CChatWindow::~CChatWindow()
412 for(std::vector<IObserver *>::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
414 (*it)->chatWindowRemoved(this);
416 if (this == _ChatWindowLaunchingCommand)
418 _ChatWindowLaunchingCommand = NULL;
422 //=================================================================================
423 void CChatWindow::setAHOnActive(const std::string &n)
425 if (_Chat) _Chat->setOnActiveHandler(n);
428 //=================================================================================
429 void CChatWindow::setAHOnActiveParams(const std::string &n)
431 if (_Chat) _Chat->setOnActiveParams(n);
434 //=================================================================================
435 void CChatWindow::setAHOnDeactive(const std::string &n)
437 if (_Chat) _Chat->setOnDeactiveHandler(n);
440 //=================================================================================
441 void CChatWindow::setAHOnDeactiveParams(const std::string &n)
443 if (_Chat) _Chat->setOnDeactiveParams(n);
446 //=================================================================================
447 void CChatWindow::setAHOnCloseButton(const std::string &n)
449 if (_Chat) _Chat->setOnCloseButtonHandler(n);
452 //=================================================================================
453 void CChatWindow::setAHOnCloseButtonParams(const std::string &n)
455 if (_Chat) _Chat->setOnCloseButtonParams(n);
458 //=================================================================================
459 void CChatWindow::setHeaderColor(const std::string &n)
461 if (_Chat) _Chat->setHeaderColor(n);
464 //=================================================================================
465 void CChatWindow::displayLocalPlayerTell(const string &receiver, const string &msg, uint numBlinks /*= 0*/)
467 string finalMsg;
468 CInterfaceProperty prop;
469 prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," ");
470 encodeColorTag(prop.getRGBA(), finalMsg, false);
472 string csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : "");
473 finalMsg += csr + CI18N::get("youTell") + ": ";
474 prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," ");
475 encodeColorTag(prop.getRGBA(), finalMsg, true);
476 finalMsg += msg;
478 string s = CI18N::get("youTellPlayer");
479 strFindReplace(s, "%name", receiver);
480 strFindReplace(finalMsg, CI18N::get("youTell"), s);
481 displayMessage(finalMsg, prop.getRGBA(), CChatGroup::tell, 0, numBlinks);
482 CInterfaceManager::getInstance()->log(finalMsg, CChatGroup::groupTypeToString(CChatGroup::tell));
485 void CChatWindow::encodeColorTag(const NLMISC::CRGBA &color, std::string &text, bool append)
487 // WARNING : The lookup table MUST contains 17 element (with the last doubled)
488 // because we add 7 to the 8 bit color before shifting to right in order to match color
489 // more accurately.
490 // Have 17 entry remove the need for a %16 for each color component.
491 // By the way, this comment is more longer to type than to add the %16...
493 static char ConvTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'F'};
494 string str;
495 if (append)
497 str.reserve(7 + str.size());
498 str = text;
500 else
501 str.reserve(7);
503 str += "@{";
504 str += ConvTable[(uint(color.R)+7)>>4];
505 str += ConvTable[(uint(color.G)+7)>>4];
506 str += ConvTable[(uint(color.B)+7)>>4];
507 str += ConvTable[(uint(color.A)+7)>>4];
508 str += '}';
510 text.swap(str);
514 //=================================================================================
515 void CChatWindow::clearMessages(CChatGroup::TGroupType /* gt */, uint32 /* dynamicChatDbIndex */)
517 // if not correctly init, abort
518 if(!_Chat)
519 return;
521 // get the group list
522 CGroupList *gl = dynamic_cast<CGroupList *>(_Chat->getGroup("cb:text_list"));
523 if (gl) gl->deleteAllChildren();
527 //////////////////////
528 // CChatGroupWindow //
529 //////////////////////
531 void CChatGroupWindow::displayMessage(const string &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks, bool *windowVisible)
533 if (!_Chat)
535 if (msg != "WRN: <CChatGroupWindow::displayMessage> There's no global chat")
536 nlwarning("<CChatGroupWindow::displayMessage> There's no global chat");
537 return;
540 CChatTextManager &ctm = getChatTextMngr();
542 if (_Chat->getHeaderOpened()==NULL)
543 return;
546 // *** Display the message in the correct tab window
547 // get the gl and tab according to filter
548 CGroupList *gl;
549 CCtrlTabButton *tab;
550 getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
552 // on a new message, change the Tab color
553 CInterfaceManager *pIM= CInterfaceManager::getInstance();
554 CRGBA newMsgColor= CRGBA::stringToRGBA(CWidgetManager::getInstance()->getParser()->getDefine("chat_group_tab_color_newmsg").c_str());
556 string newmsg = msg;
557 string prefix;
559 if (gl != NULL)
561 gl->addChild(ctm.createMsgText(newmsg, col));
562 if (!gl->getParent()->getActive())
563 if (tab != NULL)
564 tab->setTextColorNormal(newMsgColor);
567 // *** Display the message in the UserChat (special case)
569 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab5"));
570 gl = NULL;
571 CGroupList *gl2 = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:user:text_list"));
573 CChatWindow *cw = PeopleInterraction.TheUserChat.Window;
574 CChatStdInput &ci = PeopleInterraction.ChatInput;
576 switch(gt)
578 default:
579 case CChatGroup::arround:
580 case CChatGroup::say: if (ci.AroundMe.isListeningWindow(cw)) gl = gl2; break;
581 case CChatGroup::region: if (ci.Region.isListeningWindow(cw)) gl = gl2; break;
582 case CChatGroup::team: if (ci.Team.isListeningWindow(cw)) gl = gl2; break;
583 case CChatGroup::guild: if (ci.Guild.isListeningWindow(cw)) gl = gl2; break;
584 case CChatGroup::system: if (ci.SystemInfo.isListeningWindow(cw)) gl = gl2; break;
585 case CChatGroup::universe: if (ci.Universe.isListeningWindow(cw)) gl = gl2; break;
586 case CChatGroup::dyn_chat:
587 if (ci.DynamicChat[dynamicChatDbIndex].isListeningWindow(cw))
589 gl = gl2;
591 // Add dyn chan number before string
592 string prefix = "[" + NLMISC::toString(dynamicChatDbIndex) + "]";
593 // Find position to put the new string
594 // After timestamp?
595 size_t pos = newmsg.find("]");
596 size_t colonpos = newmsg.find(": @{");
597 // If no ] found or if found but after the colon (so part of the user chat)
598 if (pos == string::npos || (colonpos < pos))
600 // No timestamp, so put it right after the color and add a space
601 pos = newmsg.find("}");
602 prefix += " ";
605 if (pos == string::npos)
606 newmsg = prefix + newmsg;
607 else
608 newmsg = newmsg.substr(0, pos + 1) + prefix + newmsg.substr(pos + 1);
610 // Add dynchannel number and optionally name before text if user channel
611 CCDBNodeLeaf* node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB", false);
612 if (node && node->getValueBool())
614 uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(dynamicChatDbIndex);
615 string title;
616 STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
617 prefix = (title.empty() ? "" : " ") + title;
618 pos = newmsg.find("] ");
620 if (pos == string::npos)
621 newmsg = prefix + newmsg;
622 else
623 newmsg = newmsg.substr(0, pos) + prefix + newmsg.substr(pos);
626 break;
629 if (gl != NULL)
631 gl->addChild(ctm.createMsgText(newmsg, col));
632 if (!gl->getParent()->getActive())
633 if (tab != NULL)
634 tab->setTextColorNormal(newMsgColor);
639 // *** Blink and visibility event
640 // if the group is closed, make it blink
641 if (!_Chat->isOpen())
643 if (numBlinks) _Chat->enableBlink(numBlinks);
645 if (_ParentBlink)
647 CGroupContainer *father = dynamic_cast<CGroupContainer *>(_Chat->getParent());
648 if (father && !father->isOpen())
650 father->enableBlink(numBlinks);
653 if (windowVisible != NULL)
655 *windowVisible = isVisible();
659 //=================================================================================
660 void CChatGroupWindow::displayTellMessage(const string &msg, NLMISC::CRGBA col, const string &sender)
662 // If we are here with a tell message this is because the teller doesn't belong to any people list
663 CGroupContainer *gcChat = createFreeTeller(sender);
664 if (gcChat == NULL)
666 nlwarning("<CChatGroupWindow::displayTellMessage> cannot open chat.");
667 return;
670 gcChat->requireAttention();
672 CWidgetManager::getInstance()->setTopWindow(gcChat);
674 // add the text to this window
675 CGroupList *gl = dynamic_cast<CGroupList *>(gcChat->getGroup("text_list"));
676 if (gl == NULL)
678 nlwarning("<CChatGroupWindow::displayTellMessage> can't get text_list.");
679 return;
682 gl->addChild(getChatTextMngr().createMsgText(msg, col));
685 //=================================================================================
686 sint32 CChatGroupWindow::getTabIndex()
688 CGroupTab *pTab = dynamic_cast<CGroupTab*>(_Chat->getGroup("header_opened:channel_select"));
689 if (pTab != NULL)
690 return pTab->getSelection();
691 else
692 return -1;
695 //=================================================================================
696 void CChatGroupWindow::setTabIndex(sint32 n)
698 CGroupTab *pTab = dynamic_cast<CGroupTab*>(_Chat->getGroup("header_opened:channel_select"));
699 if (pTab != NULL)
701 pTab->select(n);
702 // if the current button is hidden, select default not hid
703 pTab->selectDefaultIfCurrentHid();
707 //=================================================================================
708 const string CChatGroupWindow::getValidUiStringId(const string &stringId)
710 string validStringId;
712 for (uint32 i=0; i < stringId.length(); i++)
714 if ((stringId[i] < 'a') || (stringId[i] > 'z'))
715 validStringId += '_';
716 else
717 validStringId += stringId[i];
719 return validStringId;
722 //=================================================================================
723 CGroupContainer *CChatGroupWindow::createFreeTeller(const string &winNameIn, const string &winColor)
725 // must parse the entity name, and eventually make it Full with shard name (eg: 'ani.yoyo' becomes 'yoyo(Aniro)')
726 string winNameFull= CShardNames::getInstance().makeFullNameFromRelative(PlayerSelectedMainland, winNameIn);
728 // remove shard name if necessary
729 string winName= CEntityCL::removeShardFromName(winNameFull);
731 // get the color
732 string sWinColor = winColor;
733 if (sWinColor.empty())
734 sWinColor = "UI:SAVE:WIN:COLORS:COM";
736 // Look if the free teller do not already exists
737 uint32 i;
738 string sWinName = winName;
739 sWinName = toLower(sWinName);
740 for (i = 0; i < _FreeTellers.size(); ++i)
742 CGroupContainer *pGC = _FreeTellers[i];
743 if (toLower(pGC->getTitle()) == sWinName)
744 break;
746 // Create container if not present
747 if (i == _FreeTellers.size())
749 // Corresponding Chat not created -> create and open
750 vector<pair<string ,string> > properties;
751 properties.push_back(make_pair(string("posparent"), string("parent")));
752 properties.push_back(make_pair(string("id"), "free_chat_" + getValidUiStringId(sWinName)));
753 properties.push_back(make_pair(string("title"), std::string("")));
754 properties.push_back(make_pair(string("header_color"), sWinColor));
756 std::string templateName = "contact_chat_friend";
758 CInterfaceManager *pIM = CInterfaceManager::getInstance();
759 CInterfaceGroup *pIG = CWidgetManager::getInstance()->getParser()->createGroupInstance(templateName, "ui:interface", properties);
760 if (!pIG) return NULL;
761 CGroupContainer *pGC = dynamic_cast<CGroupContainer *>(pIG);
762 if (!pGC)
764 delete pIG;
765 nlwarning("<CChatGroupWindow::createFreeTeller> group is not a container.(%s)", winName.c_str());
766 return NULL;
768 // set title from the name
769 pGC->setTitle(winName);
771 pGC->setSavable(true);
772 pGC->setEscapable(true);
774 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
775 pRoot->addGroup (pGC);
776 pGC->setParent(pRoot); // must be done before makeWindow
777 CWidgetManager::getInstance()->makeWindow(pGC);
778 pGC->open();
779 pGC->updateCoords();
780 pGC->center();
781 _FreeTellers.push_back(pGC);
783 CInterfaceElement *result = pRoot->findFromShortId(string("invite"));
784 if(result)
786 CCtrlBaseButton * inviteButton = dynamic_cast<CCtrlBaseButton*>(result);
787 if(inviteButton)
789 bool isDM = false;
790 R2::CEditor::TMode mode = R2::getEditor().getMode();
791 if (mode != R2::CEditor::NotInitialized )
793 isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
794 && uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
795 && uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
797 inviteButton->setActive(isDM);
801 // the group is only active on the current desktop
802 pGC->setActive(true);
805 if (!winColor.empty())
806 _FreeTellers[i]->setHeaderColor(winColor);
808 // updateFreeTellerHeader(*_FreeTellers[i]);
809 return _FreeTellers[i];
812 //=================================================================================
813 void CChatGroupWindow::updateAllFreeTellerHeaders()
815 for(uint k = 0; k < _FreeTellers.size(); ++k)
817 if (_FreeTellers[k])
819 updateFreeTellerHeader(*_FreeTellers[k]);
824 //=================================================================================
825 void CChatGroupWindow::updateFreeTellerHeader(CGroupContainer &ft)
827 string name = ft.getTitle();
828 CCtrlBaseButton *newFriendBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("new_friend"));
829 CCtrlBaseButton *ignoreBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("ignore"));
830 CCtrlBaseButton *inviteBut = dynamic_cast<CCtrlBaseButton *>(ft.getCtrl("invite"));
831 if (newFriendBut)
833 newFriendBut->setFrozen(PeopleInterraction.isContactInList(name, 0));
835 if (ignoreBut)
837 ignoreBut->setFrozen(PeopleInterraction.isContactInList(name, 1));
840 if (inviteBut)
842 bool isDM = false;
843 R2::CEditor::TMode mode = R2::getEditor().getMode();
844 if (mode != R2::CEditor::NotInitialized )
846 isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
847 && uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
848 && uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
851 inviteBut->setActive(isDM);
852 if (isDM)
854 inviteBut->setFrozen(false); // TODO Boris : true if player is already invited in anim + do
855 // PeopleInterraction.updateAllFreeTellerHeaders() when list is updated
860 //=================================================================================
861 void CChatGroupWindow::setActiveFreeTeller(const string &winName, bool bActive)
863 CGroupContainer *pGC = createFreeTeller(winName);
864 if (pGC != NULL)
865 pGC->setActive(bActive);
868 //=================================================================================
869 string CChatGroupWindow::getFreeTellerName(const std::string &containerID)
871 uint32 i;
872 for (i = 0; i < _FreeTellers.size(); ++i)
874 CGroupContainer *pGC = _FreeTellers[i];
875 if (pGC->getId() == containerID)
876 break;
878 if (i == _FreeTellers.size())
879 return string();
880 return _FreeTellers[i]->getTitle();
883 //=================================================================================
884 bool CChatGroupWindow::removeFreeTeller(const std::string &containerID)
886 uint32 i;
887 for (i = 0; i < _FreeTellers.size(); ++i)
889 CGroupContainer *pGC = _FreeTellers[i];
890 if (pGC->getId() == containerID)
891 break;
893 if (i == _FreeTellers.size())
894 return false;
895 CInterfaceManager *pIM = CInterfaceManager::getInstance();
896 pIM->removeGroupContainerImageFromDesktops(_FreeTellers[i]->getId());
898 CInterfaceGroup *pRoot = dynamic_cast<CInterfaceGroup*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
899 CWidgetManager::getInstance()->unMakeWindow(_FreeTellers[i]);
900 pRoot->delGroup (_FreeTellers[i]);
901 _FreeTellers[i] = NULL;
902 _FreeTellers.erase(_FreeTellers.begin()+i);
903 return true;
906 //=================================================================================
907 void CChatGroupWindow::removeAllFreeTellers()
909 while (!_FreeTellers.empty())
911 if (_FreeTellers[0])
913 removeFreeTeller(_FreeTellers[0]->getId());
915 else
917 _FreeTellers.erase(_FreeTellers.begin());
922 //=================================================================================
923 void CChatGroupWindow::saveFreeTeller(NLMISC::IStream &f)
925 f.serialVersion(3);
927 // Save the free teller only if it is present in the friend list to avoid the only-growing situation
928 // because free tellers are never deleted in game if we save/load all the free tellers, we just create more
929 // and more container.
931 uint32 i, nNbFreeTellerSaved = 0;
932 for (i = 0; i < _FreeTellers.size(); ++i)
933 if (PeopleInterraction.FriendList.getIndexFromName(_FreeTellers[i]->getTitle()) != -1)
934 nNbFreeTellerSaved++;
936 f.serial(nNbFreeTellerSaved);
938 for (i = 0; i < _FreeTellers.size(); ++i)
940 CGroupContainer *pGC = _FreeTellers[i];
942 if (PeopleInterraction.FriendList.getIndexFromName(pGC->getTitle()) != -1)
944 string sTitle = pGC->getTitle();
945 f.serial(sTitle);
950 //=================================================================================
951 void CChatGroupWindow::loadFreeTeller(NLMISC::IStream &f)
953 sint ver = f.serialVersion(3);
955 if (ver == 1)
957 // Old serialized FreeTellIdCounter (deprecated with v2).
958 uint32 tmp;
959 f.serial(tmp);
962 uint32 i, nNbFreeTellerSaved = 0;
963 f.serial(nNbFreeTellerSaved);
965 for (i = 0; i < nNbFreeTellerSaved; ++i)
967 if (ver == 1)
969 // Old serialized sID (deprecated with v2).
970 string sID;
971 f.serial(sID);
973 string title;
974 if (ver < 3)
976 ucstring sTitle; // Old UTF-16 serial
977 f.serial(sTitle);
978 title = sTitle.toUtf8();
981 CGroupContainer *pGC = createFreeTeller(title, "");
983 // With version 1 all tells are active because windows information have "title based" ids and no "sID based".
984 if ((ver == 1) && (pGC != NULL))
985 pGC->setActive(false);
989 //=================================================================================
990 void CChatGroupWindow::clearMessages(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex)
992 // if not correctly init, abort
993 if(!_Chat)
994 return;
996 // get the gl and tab according to filter
997 CGroupList *gl;
998 CCtrlTabButton *tab;
999 getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
1001 // delete all text lines
1002 if(gl!=NULL)
1004 gl->deleteAllChildren();
1008 //=================================================================================
1009 void CChatGroupWindow::getAssociatedSubWindow(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, CGroupList *&gl, CCtrlTabButton *&tab)
1011 nlassert(_Chat);
1012 gl= NULL;
1013 tab= NULL;
1015 switch(gt)
1017 default:
1018 case CChatGroup::say:
1019 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:around:text_list"));
1020 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab0"));
1021 break;
1022 case CChatGroup::team:
1023 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:team:text_list"));
1024 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab2"));
1025 break;
1026 case CChatGroup::guild:
1027 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:guild:text_list"));
1028 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab3"));
1029 break;
1030 case CChatGroup::system:
1031 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:sysinfo:text_list"));
1032 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab4"));
1033 break;
1034 case CChatGroup::region:
1035 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:region:text_list"));
1036 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab1"));
1037 break;
1038 case CChatGroup::dyn_chat:
1040 // use dynamicChatDbIndex to get the wanted tab button/group
1041 gl = dynamic_cast<CGroupList *>(_Chat->getGroup(toString("content:cb:dyn_chat%d:text_list", dynamicChatDbIndex)));
1042 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl(toString("header_opened:channel_select:tab_array0_%d",dynamicChatDbIndex)));
1044 break;
1045 case CChatGroup::universe:
1046 gl = dynamic_cast<CGroupList *>(_Chat->getGroup("content:cb:universe:text_list"));
1047 tab = dynamic_cast<CCtrlTabButton*>(_Chat->getCtrl("header_opened:channel_select:tab6"));
1048 break;
1053 ////////////////////////
1054 // CChatWindowManager //
1055 ////////////////////////
1057 //=================================================================================
1058 CChatWindowManager::CChatWindowManager() : _WindowID(0)
1062 //=================================================================================
1063 CChatWindowManager::~CChatWindowManager()
1065 for(TChatWindowMap::iterator it = _ChatWindowMap.begin(); it != _ChatWindowMap.end(); ++it)
1067 delete it->second;
1071 //=================================================================================
1072 CChatWindow *CChatWindowManager::createChatWindow(const CChatWindowDesc &desc)
1074 if (getChatWindow(desc.Title))
1076 return NULL; // duplicate name encountered
1078 CChatWindow *w;
1079 w = new CChatWindow;
1080 string zeId;
1081 if (desc.Id.empty())
1082 zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
1083 else
1084 zeId = desc.Id;
1086 if (w->create(desc, zeId))
1088 if (desc.Id.empty())
1089 _WindowID++;
1091 _ChatWindowMap[desc.Title] = w;
1093 w->setAHOnActive(desc.AHOnActive);
1094 w->setAHOnActiveParams(desc.AHOnActiveParams);
1095 w->setAHOnDeactive(desc.AHOnDeactive);
1096 w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
1097 w->setAHOnCloseButton(desc.AHOnCloseButton);
1098 w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
1099 if (!desc.HeaderColor.empty())
1100 w->setHeaderColor(desc.HeaderColor);
1102 return w;
1104 else
1106 return NULL;
1110 //=================================================================================
1111 CChatWindow *CChatWindowManager::createChatGroupWindow(const CChatWindowDesc &desc)
1113 if (getChatWindow(desc.Title))
1115 return NULL; // duplicate name encountered
1117 CChatGroupWindow *w;
1118 w = new CChatGroupWindow;
1119 string zeId;
1120 if (desc.Id.empty())
1121 zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
1122 else
1123 zeId = desc.Id;
1125 if (w->create(desc, zeId))
1127 if (desc.Id.empty())
1128 _WindowID++;
1130 _ChatWindowMap[desc.Title] = w;
1132 w->setAHOnActive(desc.AHOnActive);
1133 w->setAHOnActiveParams(desc.AHOnActiveParams);
1134 w->setAHOnDeactive(desc.AHOnDeactive);
1135 w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
1136 w->setAHOnCloseButton(desc.AHOnCloseButton);
1137 w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
1138 if (!desc.HeaderColor.empty())
1139 w->setHeaderColor(desc.HeaderColor);
1142 // because root group was created from template, element from scrollbar target attribute was not created yet
1143 CInterfaceGroup *pIG = w->getContainer()->getGroup("header_opened:channel_select");
1144 if (pIG)
1146 CCtrlScroll *sb = dynamic_cast<CCtrlScroll*>(w->getContainer()->getCtrl("channel_scroll"));
1147 if (sb) sb->setTarget(pIG);
1150 return w;
1152 else
1154 return NULL;
1158 //=================================================================================
1159 CChatWindow *CChatWindowManager::getChatWindow(const string &title)
1161 TChatWindowMap::iterator it = _ChatWindowMap.find(title);
1162 if (it == _ChatWindowMap.end())
1163 return NULL;
1164 else
1166 nlassert(it->second != NULL);
1167 return it->second;
1171 //=================================================================================
1172 void CChatWindowManager::removeChatWindow(const string &title)
1174 TChatWindowMap::iterator it = _ChatWindowMap.find(title);
1175 if (it == _ChatWindowMap.end())
1177 nlwarning("Unknown chat window '%s'", title.c_str());
1178 return;
1180 it->second->deleteContainer();
1181 delete it->second;
1182 _ChatWindowMap.erase(it);
1185 //=================================================================================
1186 CChatWindowManager &CChatWindowManager::getInstance()
1188 static CChatWindowManager instance;
1189 return instance;
1192 //=================================================================================
1193 CChatWindow *CChatWindowManager::getChatWindowFromCaller(CCtrlBase *caller)
1195 // retrieve pointer on the CChatWindow instance associated with the ui
1196 // find first enclosing group container
1197 CGroupContainer *father = NULL;
1198 while (caller)
1200 father = dynamic_cast<CGroupContainer *>(caller);
1201 if (father) break;
1202 caller = caller->getParent();
1204 if (!father) return NULL;
1206 return getChatWindow(father->getTitle());
1209 //=================================================================================
1210 bool CChatWindowManager::rename(const string &oldName, const string &newName, bool newNameLocalize)
1212 // if (oldName == newName) return true;
1213 CChatWindow *newWin = getChatWindow(newName);
1214 if (newWin != NULL) return false; // target window exists
1215 TChatWindowMap::iterator it = _ChatWindowMap.find(oldName);
1216 if (it == _ChatWindowMap.end()) return false;
1217 _ChatWindowMap[newName] = it->second;
1218 it->second->getContainer()->setLocalize(newNameLocalize);
1219 it->second->getContainer()->setTitle(newName);
1220 _ChatWindowMap.erase(it);
1221 return true;
1224 //=================================================================================
1225 CGroupEditBox *CChatWindow::getEditBox() const
1227 if (!_Chat) return NULL;
1228 return dynamic_cast<CGroupEditBox *>(_Chat->getGroup("eb"));
1231 //=================================================================================
1232 void CChatWindowManager::removeChatWindow(CChatWindow *cw)
1234 if (!cw) return;
1235 removeChatWindow(cw->getTitle());
1238 //=================================================================================
1239 CChatWindow *CChatWindowManager::getChatWindowByIndex(uint index)
1241 TChatWindowMap::iterator it = _ChatWindowMap.begin();
1242 while (index--) { ++it; }
1243 return it->second;
1246 /////////////////////
1247 // ACTION HANDLERS //
1248 /////////////////////
1251 // ***************************************************************************************
1252 class CHandlerChatBoxEntry : public IActionHandler
1254 public:
1255 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1257 CGroupEditBox *pEB = dynamic_cast<CGroupEditBox*>(pCaller);
1258 if (pEB == NULL) return;
1259 string text = pEB->getInputString();
1260 // If the line is empty, do nothing
1261 if(text.empty())
1262 return;
1265 CChatWindow *chat = getChatWndMgr().getChatWindowFromCaller(pCaller);
1266 if (!chat)
1268 nlwarning("No chat box associated with %s", pEB->getId().c_str());
1269 return;
1272 // Parse any tokens in the text
1273 if ( ! CInterfaceManager::parseTokens(text))
1275 pEB->setInputString(std::string());
1276 return;
1279 // if, it s a command, execute it and don't send the command to the server
1280 if(text[0] == '/')
1282 CChatWindow::_ChatWindowLaunchingCommand = chat;
1283 string str = text;
1284 string cmdWithArgs = str.substr(1);
1286 // Get the command name from the string, can contain spaces
1287 string cmd = cmdWithArgs.substr(0, cmdWithArgs.find(' '));
1288 if (cmdWithArgs.find('"') == 0)
1290 string::size_type pos = cmdWithArgs.find('"', 1);
1291 if (string::npos != pos)
1293 cmd = cmdWithArgs.substr(1, pos - 1);
1297 if ( NLMISC::ICommand::exists( cmd ) )
1299 NLMISC::ICommand::execute( cmdWithArgs, g_log );
1301 else
1303 CInterfaceManager *im = CInterfaceManager::getInstance();
1304 im->displaySystemInfo (cmd + ": " + CI18N::get ("uiCommandNotExists"));
1307 else
1309 if (chat->getListener())
1311 chat->getListener()->msgEntered(text, chat);
1314 // Clear input string
1315 pEB->setInputString (std::string());
1316 CGroupContainer *gc = static_cast< CGroupContainer* >( pEB->getEnclosingContainer() );
1318 if (gc)
1320 // Restore position of enclosing container if it hasn't been moved/scaled/poped by the user
1321 if (!gc->getTouchFlag(true))
1323 gc->restorePosition();
1328 REGISTER_ACTION_HANDLER(CHandlerChatBoxEntry, "chat_box_entry");
1332 static string getFreeTellerName(CInterfaceElement *pCaller)
1334 if (!pCaller) return string();
1335 CChatGroupWindow *cgw = PeopleInterraction.getChatGroupWindow();
1336 if (!cgw) return string();
1337 CInterfaceGroup *freeTeller = pCaller->getParentContainer();
1338 if (!freeTeller) return string();
1339 return cgw->getFreeTellerName( freeTeller->getId() );
1342 // ***************************************************************************************
1343 class CHandlerAddTellerToFriendList : public IActionHandler
1345 public:
1346 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1348 string playerName = ::getFreeTellerName(pCaller);
1349 if (!playerName.empty())
1351 sint playerIndex = PeopleInterraction.IgnoreList.getIndexFromName(playerName);
1352 // if already in friend list, ask to move rather than add
1353 if (playerIndex != -1)
1355 PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.IgnoreList, &PeopleInterraction.FriendList);
1357 else
1359 PeopleInterraction.askAddContact(playerName, &PeopleInterraction.FriendList);
1364 REGISTER_ACTION_HANDLER(CHandlerAddTellerToFriendList, "add_teller_to_friend_list");
1367 // ***************************************************************************************
1368 class CHandlerAddTellerToIgnoreList : public IActionHandler
1370 public:
1371 void execute (CCtrlBase *pCaller, const std::string &sParams)
1373 CInterfaceManager *im = CInterfaceManager::getInstance();
1374 std::string callerId = getParam(sParams, "id");
1375 CInterfaceElement *prevCaller = CWidgetManager::getInstance()->getElementFromId(callerId);
1376 string playerName = ::getFreeTellerName(prevCaller);
1377 if (!playerName.empty())
1379 // if already in friend list, ask to move rather than add
1380 sint playerIndex = PeopleInterraction.FriendList.getIndexFromName(playerName);
1381 if (playerIndex != -1)
1383 PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.FriendList, &PeopleInterraction.IgnoreList);
1385 else
1387 PeopleInterraction.askAddContact(playerName, &PeopleInterraction.IgnoreList);
1389 if (pCaller)
1391 CInterfaceGroup *win = prevCaller->getParentContainer();
1392 if (win)
1394 static_cast< CGroupContainer* >( win )->setActive(false);
1400 REGISTER_ACTION_HANDLER(CHandlerAddTellerToIgnoreList, "add_teller_to_ignore_list");
1402 // ***************************************************************************************
1403 class CHandlerInviteToRingSession : public IActionHandler
1405 public:
1406 void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
1408 string playerName = ::getFreeTellerName(pCaller);
1409 if (!playerName.empty())
1411 // ask the SBS to invite the character in the session
1412 CSessionBrowserImpl::getInstance().inviteCharacterByName(CSessionBrowserImpl::getInstance().getCharId(), playerName);
1413 // additionaly, send a tell to signal the player he has been invited to a ring session
1414 ChatMngr.tell(playerName, CI18N::get("uiRingInviteNotification"));
1416 CInterfaceManager *im = CInterfaceManager::getInstance();
1417 im->displaySystemInfo("@{6F6F}" + playerName +" @{FFFF}" + CI18N::get("uiRingInvitationSent"), "BC");
1418 // force a refresh of the ui
1419 CLuaManager::getInstance().executeLuaScript("CharTracking:forceRefresh()");
1423 REGISTER_ACTION_HANDLER(CHandlerInviteToRingSession, "invite_to_ring_session");