1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 Winch Gate Property Limited
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>
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/>.
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
;
57 CChatWindow
*CChatWindow::_ChatWindowLaunchingCommand
= NULL
;
64 extern NLMISC::CLog g_log
;
65 extern CClientChatManager ChatMngr
;
71 CChatWindowDesc::CChatWindowDesc() : InsertPosition(-1),
83 //=================================================================================
84 CChatWindow::CChatWindow() : _Listener(NULL
), _Chat(NULL
), _EB(NULL
), _ParentBlink(false)
88 //=================================================================================
89 bool CChatWindow::create(const CChatWindowDesc
&desc
, const std::string
&chatId
)
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");
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";
118 chatTemplate
= desc
.ChatTemplate
;
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
);
130 _Chat
= dynamic_cast<CGroupContainer
*>(chatGroup
);
133 nlwarning("<CChatWindow::create> Bad type for chat group");
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"));
145 _EB
->setAHOnEnter("chat_box_entry");
148 CInterfaceGroup
*pRoot
= dynamic_cast<CInterfaceGroup
*>(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
152 fatherContainer
->attachContainer(_Chat
, desc
.InsertPosition
);
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
);
178 //=================================================================================
179 bool CChatWindow::isVisible() const
181 if (!_Chat
) return false;
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();
195 return ig
== NULL
; // all parent windows must be open & visible
203 //=================================================================================
204 void CChatWindow::displayMessage(const string
&msg
, NLMISC::CRGBA col
, CChatGroup::TGroupType gt
, uint32 dynamicChatDbIndex
, uint numBlinks
/* = 0*/, bool *windowVisible
/*= NULL*/)
208 if (msg
!= "WRN: <CChatWindow::displayMessage> There's no global chat")
209 nlwarning("<CChatWindow::displayMessage> There's no global chat");
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
;
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
);
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
);
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
)
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
)
285 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(_Chat
->getGroup("eb"));
287 eb
->setPrompt(prompt
);
290 void CChatWindow::setPromptColor(NLMISC::CRGBA col
)
295 CGroupEditBox
*eb
= dynamic_cast<CGroupEditBox
*>(_Chat
->getGroup("eb"));
302 //=================================================================================
303 void CChatWindow::deleteContainer()
306 CGroupContainer
*proprietaryContainer
= _Chat
->getProprietaryContainer();
307 if (proprietaryContainer
)
309 if (_Chat
->isPopuped())
311 _Chat
->popin(-1, false); // popin & detach
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
);
323 CWidgetManager::getInstance()->unMakeWindow(_Chat
);
324 if (_Chat
->getParent())
326 _Chat
->getParent()->delGroup(_Chat
);
329 // Removes from parent group
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())
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
)
366 _Chat
->enableBlink(numBlinks
);
370 //=================================================================================
371 void CChatWindow::setCommand(const std::string
&command
, bool execute
)
374 _EB
->setCommand(command
, execute
);
377 //=================================================================================
378 void CChatWindow::setEntry(const string
&entry
)
381 _EB
->setInputString(entry
);
384 //=================================================================================
385 string
CChatWindow::getTitle() const
393 return _Chat
->getTitle();
397 //=================================================================================
398 void CChatWindow::addObserver(IObserver
*obs
)
402 nlwarning("NULL observer is invalid");
407 nlwarning("Observer added twice");
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");
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*/)
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);
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
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'};
520 str
.reserve(7 + str
.size());
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];
537 //=================================================================================
538 void CChatWindow::clearMessages(CChatGroup::TGroupType
/* gt */, uint32
/* dynamicChatDbIndex */)
540 // if not correctly init, abort
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
)
558 if (msg
!= "WRN: <CChatGroupWindow::displayMessage> There's no global chat")
559 nlwarning("<CChatGroupWindow::displayMessage> There's no global chat");
563 CChatTextManager
&ctm
= getChatTextMngr();
565 if (_Chat
->getHeaderOpened()==NULL
)
569 // *** Display the message in the correct tab window
570 // get the gl and tab according to filter
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());
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();
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
;
599 child
= ctm
.createMsgText(newmsg
, col
);
603 if (!gl
->getParent()->getActive())
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"));
613 CGroupList
*gl2
= dynamic_cast<CGroupList
*>(_Chat
->getGroup("content:cb:user:text_list"));
615 CChatWindow
*cw
= PeopleInterraction
.TheUserChat
.Window
;
616 CChatStdInput
&ci
= PeopleInterraction
.ChatInput
;
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
))
633 // Add dyn chan number before string
634 string prefix
= "[" + NLMISC::toString(dynamicChatDbIndex
) + "]";
635 // Find position to put the new string
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("}");
647 if (pos
== string::npos
)
648 newmsg
= prefix
+ newmsg
;
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
);
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
;
665 newmsg
= newmsg
.substr(0, pos
) + prefix
+ newmsg
.substr(pos
);
673 child
= ctm
.createMsgText(newmsg
, col
);
677 if (!gl
->getParent()->getActive())
679 tab
->setTextColorNormal(newMsgColor
);
686 // *** Blink and visibility event
687 // if the group is closed, make it blink
688 if (!_Chat
->isOpen())
690 if (numBlinks
) _Chat
->enableBlink(numBlinks
);
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
);
714 nlwarning("<CChatGroupWindow::displayTellMessage> cannot open chat.");
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"));
726 nlwarning("<CChatGroupWindow::displayTellMessage> can't get text_list.");
729 CViewBase
*child
= getChatTextMngr().createMsgText(msg
, col
);
734 //=================================================================================
735 sint32
CChatGroupWindow::getTabIndex()
737 CGroupTab
*pTab
= dynamic_cast<CGroupTab
*>(_Chat
->getGroup("header_opened:channel_select"));
739 return pTab
->getSelection();
744 //=================================================================================
745 void CChatGroupWindow::setTabIndex(sint32 n
)
747 CGroupTab
*pTab
= dynamic_cast<CGroupTab
*>(_Chat
->getGroup("header_opened:channel_select"));
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
+= '_';
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
);
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
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
)
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
);
814 nlwarning("<CChatGroupWindow::createFreeTeller> group is not a container.(%s)", winName
.c_str());
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
);
830 _FreeTellers
.push_back(pGC
);
832 CInterfaceElement
*result
= pRoot
->findFromShortId(string("invite"));
835 CCtrlBaseButton
* inviteButton
= dynamic_cast<CCtrlBaseButton
*>(result
);
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
)
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"));
883 newFriendBut
->setFrozen(PeopleInterraction
.isContactInList(name
, 0));
887 ignoreBut
->setFrozen(PeopleInterraction
.isContactInList(name
, 1));
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
);
904 inviteBut
->setFrozen(false); // TODO Boris : true if player is already invited in anim + do
905 // PeopleInterraction.updateAllFreeTellerHeaders() when list is updated
911 inviteTeamBut
->setActive(PeopleInterraction
.isContactOnline(name
));
916 //=================================================================================
917 void CChatGroupWindow::setActiveFreeTeller(const string
&winName
, bool bActive
)
919 CGroupContainer
*pGC
= createFreeTeller(winName
);
921 pGC
->setActive(bActive
);
924 //=================================================================================
925 string
CChatGroupWindow::getFreeTellerName(const std::string
&containerID
)
928 for (i
= 0; i
< _FreeTellers
.size(); ++i
)
930 CGroupContainer
*pGC
= _FreeTellers
[i
];
931 if (pGC
->getId() == containerID
)
934 if (i
== _FreeTellers
.size())
936 return _FreeTellers
[i
]->getTitle();
939 //=================================================================================
940 bool CChatGroupWindow::removeFreeTeller(const std::string
&containerID
)
943 for (i
= 0; i
< _FreeTellers
.size(); ++i
)
945 CGroupContainer
*pGC
= _FreeTellers
[i
];
946 if (pGC
->getId() == containerID
)
949 if (i
== _FreeTellers
.size())
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
);
962 //=================================================================================
963 void CChatGroupWindow::removeAllFreeTellers()
965 while (!_FreeTellers
.empty())
969 removeFreeTeller(_FreeTellers
[0]->getId());
973 _FreeTellers
.erase(_FreeTellers
.begin());
978 //=================================================================================
979 void CChatGroupWindow::saveFreeTeller(NLMISC::IStream
&f
)
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();
1006 //=================================================================================
1007 void CChatGroupWindow::loadFreeTeller(NLMISC::IStream
&f
)
1009 sint ver
= f
.serialVersion(3);
1013 // Old serialized FreeTellIdCounter (deprecated with v2).
1018 uint32 i
, nNbFreeTellerSaved
= 0;
1019 f
.serial(nNbFreeTellerSaved
);
1021 for (i
= 0; i
< nNbFreeTellerSaved
; ++i
)
1025 // Old serialized sID (deprecated with v2).
1032 ucstring sTitle
; // Old UTF-16 serial
1034 title
= sTitle
.toUtf8();
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
1054 // get the gl and tab according to filter
1056 CCtrlTabButton
*tab
;
1057 getAssociatedSubWindow(gt
, dynamicChatDbIndex
, gl
, tab
);
1059 // delete all text lines
1062 gl
->deleteAllChildren();
1066 //=================================================================================
1067 void CChatGroupWindow::getAssociatedSubWindow(CChatGroup::TGroupType gt
, uint32 dynamicChatDbIndex
, CGroupList
*&gl
, CCtrlTabButton
*&tab
)
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"));
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"));
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"));
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"));
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"));
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
)));
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"));
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
)
1129 //=================================================================================
1130 CChatWindow
*CChatWindowManager::createChatWindow(const CChatWindowDesc
&desc
)
1132 if (getChatWindow(desc
.Title
))
1134 return NULL
; // duplicate name encountered
1137 w
= new CChatWindow
;
1139 if (desc
.Id
.empty())
1140 zeId
= NLMISC::toString("chat_window_id_%d", (int) _WindowID
);
1144 if (w
->create(desc
, zeId
))
1146 if (desc
.Id
.empty())
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
);
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
;
1178 if (desc
.Id
.empty())
1179 zeId
= NLMISC::toString("chat_window_id_%d", (int) _WindowID
);
1183 if (w
->create(desc
, zeId
))
1185 if (desc
.Id
.empty())
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");
1204 CCtrlScroll
*sb
= dynamic_cast<CCtrlScroll
*>(w
->getContainer()->getCtrl("channel_scroll"));
1205 if (sb
) sb
->setTarget(pIG
);
1216 //=================================================================================
1217 CChatWindow
*CChatWindowManager::getChatWindow(const string
&title
)
1219 TChatWindowMap::iterator it
= _ChatWindowMap
.find(title
);
1220 if (it
== _ChatWindowMap
.end())
1224 nlassert(it
->second
!= NULL
);
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());
1238 it
->second
->deleteContainer();
1240 _ChatWindowMap
.erase(it
);
1243 //=================================================================================
1244 CChatWindowManager
&CChatWindowManager::getInstance()
1246 static CChatWindowManager 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
;
1258 father
= dynamic_cast<CGroupContainer
*>(caller
);
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
);
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
)
1293 removeChatWindow(cw
->getTitle());
1296 //=================================================================================
1297 CChatWindow
*CChatWindowManager::getChatWindowByIndex(uint index
)
1299 TChatWindowMap::iterator it
= _ChatWindowMap
.begin();
1300 while (index
--) { ++it
; }
1304 /////////////////////
1305 // ACTION HANDLERS //
1306 /////////////////////
1309 // ***************************************************************************************
1310 class CHandlerChatBoxEntry
: public IActionHandler
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
1323 CChatWindow
*chat
= getChatWndMgr().getChatWindowFromCaller(pCaller
);
1326 nlwarning("No chat box associated with %s", pEB
->getId().c_str());
1330 // Parse any tokens in the text
1331 if ( ! CInterfaceManager::parseTokens(text
))
1333 pEB
->setInputString(std::string());
1337 // if, it s a command, execute it and don't send the command to the server
1340 CChatWindow::_ChatWindowLaunchingCommand
= chat
;
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
);
1361 CInterfaceManager
*im
= CInterfaceManager::getInstance();
1362 im
->displaySystemInfo (cmd
+ ": " + CI18N::get ("uiCommandNotExists"));
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() );
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
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
);
1417 PeopleInterraction
.askAddContact(playerName
, &PeopleInterraction
.FriendList
);
1422 REGISTER_ACTION_HANDLER(CHandlerAddTellerToFriendList
, "add_teller_to_friend_list");
1425 // ***************************************************************************************
1426 class CHandlerAddTellerToIgnoreList
: public IActionHandler
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
);
1445 PeopleInterraction
.askAddContact(playerName
, &PeopleInterraction
.IgnoreList
);
1449 CInterfaceGroup
*win
= prevCaller
->getParentContainer();
1452 static_cast< CGroupContainer
* >( win
)->setActive(false);
1458 REGISTER_ACTION_HANDLER(CHandlerAddTellerToIgnoreList
, "add_teller_to_ignore_list");
1460 // ***************************************************************************************
1461 class CHandlerInviteToRingSession
: public IActionHandler
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");