// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
// client
#include "stdpch.h"
#include "game_share/shard_names.h"
#include "../r2/editor.h"
#include "chat_window.h"
#include "chat_text_manager.h"
#include "../user_entity.h"
#include "people_interraction.h"
#include "../connection.h"
//
#include "group_container.h"
#include "group_editbox.h"
#include "group_tab.h"
#include "interface_manager.h"
#include "nel/gui/action_handler.h"
#include "../client_chat_manager.h"
//
#include "../session_browser_impl.h"
#include "../r2/editor.h"
#include "../r2/dmc/client_edition_module.h"
using namespace NLMISC;
using namespace std;
////////////
// STATIC //
////////////
CChatWindow *CChatWindow::_ChatWindowLaunchingCommand = NULL;
////////////
// EXTERN //
////////////
extern NLMISC::CLog g_log;
extern CClientChatManager ChatMngr;
/////////////////////
// CChatWindowDesc //
/////////////////////
CChatWindowDesc::CChatWindowDesc() : InsertPosition(-1),
ParentBlink(false),
Savable(false),
Localize(false),
Listener(NULL)
{
}
/////////////////
// CChatWindow //
/////////////////
//=================================================================================
CChatWindow::CChatWindow() : _Listener(NULL), _Chat(NULL), _EB(NULL), _ParentBlink(false)
{
}
//=================================================================================
bool CChatWindow::create(const CChatWindowDesc &desc, const std::string &chatId)
{
deleteContainer();
CInterfaceManager *im = CInterfaceManager::getInstance();
// get the father container
CGroupContainer *fatherContainer = NULL;
if (!desc.FatherContainer.empty())
{
if (desc.FatherContainer != "ui:interface" )
{
fatherContainer = dynamic_cast(CWidgetManager::getInstance()->getElementFromId(desc.FatherContainer));
if (!fatherContainer)
{
nlwarning(" Can't get father group, or bad type");
return false;
}
}
}
// get the good template
std::string chatTemplate;
if (desc.ChatTemplate.empty())
{
// no chat template provided : use default
chatTemplate = "chat_id";
}
else
{
chatTemplate = desc.ChatTemplate;
}
// build params
CChatWindowDesc::TTemplateParams params;
params.push_back(make_pair(string("id"), chatId));
params.insert(params.end(), desc.ChatTemplateParams.begin(), desc.ChatTemplateParams.end());
// create a chat container from the template
CInterfaceGroup *chatGroup = im->createGroupInstance(chatTemplate, "ui:interface", params);
if (chatGroup)
{
_Chat = dynamic_cast(chatGroup);
if (!_Chat)
{
nlwarning(" Bad type for chat group");
delete chatGroup;
return false;
}
_Chat->setLocalize (desc.Localize);
if (desc.Localize)
_Chat->setTitle(desc.Title.toString());
else
_Chat->setUCTitle(desc.Title);
_Chat->setSavable(desc.Savable);
// groups like system info don't have edit box.
_EB = dynamic_cast(_Chat->getGroup("eb"));
if (_EB)
{
_EB->setAHOnEnter("chat_box_entry");
}
CInterfaceGroup *pRoot = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
if (fatherContainer)
{
fatherContainer->attachContainer(_Chat, desc.InsertPosition);
}
// If root container
if (desc.FatherContainer == "ui:interface")
{
CWidgetManager::getInstance()->addWindowToMasterGroup("ui:interface", _Chat);
_Chat->setParent(pRoot);
_Chat->setMovable(true);
_Chat->setActive(false);
_Chat->setOpen(true);
}
_ParentBlink = desc.ParentBlink;
_Listener = desc.Listener;
pRoot->addGroup (_Chat);
return true;
}
else
{
return false;
}
}
//=================================================================================
bool CChatWindow::isVisible() const
{
if (!_Chat) return false;
if (_Chat->isOpen())
{
CInterfaceGroup *ig = _Chat;
do
{
if (ig->isGroupContainer())
{
if (!static_cast(ig)->isOpen()) break;
}
if (!ig->getActive()) break;
ig = ig->getParent();
}
while(ig);
return ig == NULL; // all parent windows must be open & visible
}
else
{
return false;
}
}
//=================================================================================
void CChatWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks /* = 0*/, bool *windowVisible /*= NULL*/)
{
if (!_Chat)
{
nlwarning(" There's no global chat");
return;
}
CGroupList *gl;
CChatTextManager &ctm = getChatTextMngr();
gl = dynamic_cast(_Chat->getGroup("cb:text_list"));
if (gl) gl->addChild(ctm.createMsgText(msg, col));
// if the group is closed, make it blink
if (!_Chat->isOpen())
{
if (numBlinks) _Chat->enableBlink(numBlinks);
}
if (_ParentBlink)
{
CGroupContainer *father = dynamic_cast(_Chat->getParent());
if (father && !father->isOpen())
{
father->enableBlink(numBlinks);
}
}
if (windowVisible != NULL)
{
*windowVisible = isVisible();
}
/*for(std::vector::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
{
(*it)->displayMessage(this, msg, col, numBlinks);
}*/
}
//=================================================================================
void CChatWindow::setMenu(const std::string &menuName)
{
if (!_Chat) return;
if (_Chat->getHeaderOpened())
{
_Chat->getHeaderOpened()->setRightClickHandler("active_menu");
_Chat->getHeaderOpened()->setRightClickHandlerParams("menu=" + menuName);
}
if (_Chat->getHeaderClosed())
{
_Chat->getHeaderClosed()->setRightClickHandler("active_menu");
_Chat->getHeaderClosed()->setRightClickHandlerParams("menu=" + menuName);
}
}
//=================================================================================
void CChatWindow::setPrompt(const ucstring &prompt)
{
if (!_Chat) return;
CGroupEditBox *eb = dynamic_cast(_Chat->getGroup("eb"));
if (!eb) return;
eb->setPrompt(prompt);
}
void CChatWindow::setPromptColor(NLMISC::CRGBA col)
{
if (!_Chat)
return;
CGroupEditBox *eb = dynamic_cast(_Chat->getGroup("eb"));
if (!eb)
return;
eb->setColor(col);
}
//=================================================================================
void CChatWindow::deleteContainer()
{
if (!_Chat) return;
CGroupContainer *proprietaryContainer = _Chat->getProprietaryContainer();
if (proprietaryContainer)
{
if (_Chat->isPopuped())
{
_Chat->popin(-1, false); // popin & detach
}
else
{
proprietaryContainer->detachContainer(_Chat); // just detach
}
CInterfaceManager *im = CInterfaceManager::getInstance();
CInterfaceGroup *pRoot = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
pRoot->delGroup (_Chat);
}
else
{
CWidgetManager::getInstance()->unMakeWindow(_Chat);
if (_Chat->getParent())
{
_Chat->getParent()->delGroup(_Chat);
}
}
// Removes from parent group
_Chat = NULL;
}
//=================================================================================
bool CChatWindow::rename(const ucstring &newName, bool newNameLocalize)
{
return getChatWndMgr().rename(getTitle(), newName, newNameLocalize);
}
//=================================================================================
void CChatWindow::setKeyboardFocus()
{
if (!_EB || !_Chat) return;
CWidgetManager::getInstance()->setCaptureKeyboard(_EB);
if (!_Chat->isOpenable() || _Chat->isOpenWhenPopup())
{
if (_Chat->isPopable() && !_Chat->isPopuped())
{
_Chat->popup();
if (_Chat->getPopupW() != -1) // restore previous popup position if there's one
{
_Chat->setX(_Chat->getPopupX());
_Chat->setY(_Chat->getPopupY());
_Chat->setW(_Chat->getPopupW());
// must resize the children to get correct height
//_Chat->setChildrenH(_Chat->getPopupChildrenH());
}
}
}
}
//=================================================================================
void CChatWindow::enableBlink(uint numBlinks)
{
if (!_Chat) return;
_Chat->enableBlink(numBlinks);
}
//=================================================================================
void CChatWindow::setCommand(const std::string &command, bool execute)
{
if (!_EB) return;
_EB->setCommand(ucstring(command), execute);
}
void CChatWindow::setCommand(const ucstring &command,bool execute)
{
if (!_EB) return;
_EB->setCommand(command, execute);
}
//=================================================================================
void CChatWindow::setEntry(const ucstring &entry)
{
if (!_EB) return;
_EB->setInputString(entry);
}
//=================================================================================
ucstring CChatWindow::getTitle() const
{
if (!_Chat)
{
return ucstring("");
}
else
{
return _Chat->getUCTitle();
}
}
//=================================================================================
void CChatWindow::addObserver(IObserver *obs)
{
if (!obs)
{
nlwarning("NULL observer is invalid");
return;
}
if (isObserver(obs))
{
nlwarning("Observer added twice");
return;
}
_Observers.push_back(obs);
}
//=================================================================================
void CChatWindow::removeObserver(IObserver *obs)
{
std::vector::iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
if (it == _Observers.end())
{
nlwarning("Observer doesn't belong to this chatbox");
return;
}
_Observers.erase(it);
}
//=================================================================================
bool CChatWindow::isObserver(const IObserver *obs) const
{
std::vector::const_iterator it = std::find(_Observers.begin(), _Observers.end(), obs);
return it != _Observers.end();
}
//=================================================================================
CChatWindow::~CChatWindow()
{
for(std::vector::iterator it = _Observers.begin(); it != _Observers.end(); ++it)
{
(*it)->chatWindowRemoved(this);
}
if (this == _ChatWindowLaunchingCommand)
{
_ChatWindowLaunchingCommand = NULL;
}
}
//=================================================================================
void CChatWindow::setAHOnActive(const std::string &n)
{
if (_Chat) _Chat->setOnActiveHandler(n);
}
//=================================================================================
void CChatWindow::setAHOnActiveParams(const std::string &n)
{
if (_Chat) _Chat->setOnActiveParams(n);
}
//=================================================================================
void CChatWindow::setAHOnDeactive(const std::string &n)
{
if (_Chat) _Chat->setOnDeactiveHandler(n);
}
//=================================================================================
void CChatWindow::setAHOnDeactiveParams(const std::string &n)
{
if (_Chat) _Chat->setOnDeactiveParams(n);
}
//=================================================================================
void CChatWindow::setAHOnCloseButton(const std::string &n)
{
if (_Chat) _Chat->setOnCloseButtonHandler(n);
}
//=================================================================================
void CChatWindow::setAHOnCloseButtonParams(const std::string &n)
{
if (_Chat) _Chat->setOnCloseButtonParams(n);
}
//=================================================================================
void CChatWindow::setHeaderColor(const std::string &n)
{
if (_Chat) _Chat->setHeaderColor(n);
}
//=================================================================================
void CChatWindow::displayLocalPlayerTell(const ucstring &receiver, const ucstring &msg, uint numBlinks /*= 0*/)
{
ucstring finalMsg;
CInterfaceProperty prop;
prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," ");
encodeColorTag(prop.getRGBA(), finalMsg, false);
ucstring csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : "");
finalMsg += csr + CI18N::get("youTell") + ": ";
prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," ");
encodeColorTag(prop.getRGBA(), finalMsg, true);
finalMsg += msg;
ucstring s = CI18N::get("youTellPlayer");
strFindReplace(s, "%name", receiver);
strFindReplace(finalMsg, CI18N::get("youTell"), s);
displayMessage(finalMsg, prop.getRGBA(), CChatGroup::tell, 0, numBlinks);
CInterfaceManager::getInstance()->log(finalMsg);
}
void CChatWindow::encodeColorTag(const NLMISC::CRGBA &color, ucstring &text, bool append)
{
// WARNING : The lookup table MUST contains 17 element (with the last doubled)
// because we add 7 to the 8 bit color before shifting to right in order to match color
// more accurately.
// Have 17 entry remove the need for a %16 for each color component.
// By the way, this comment is more longer to type than to add the %16...
//
static ucchar ConvTable[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'F'};
ucstring str;
if (append)
{
str.reserve(7 + str.size());
str = text;
}
else
str.reserve(7);
str += "@{";
str += ConvTable[(uint(color.R)+7)>>4];
str += ConvTable[(uint(color.G)+7)>>4];
str += ConvTable[(uint(color.B)+7)>>4];
str += ConvTable[(uint(color.A)+7)>>4];
str += '}';
text.swap(str);
}
//=================================================================================
void CChatWindow::clearMessages(CChatGroup::TGroupType /* gt */, uint32 /* dynamicChatDbIndex */)
{
// if not correctly init, abort
if(!_Chat)
return;
// get the group list
CGroupList *gl = dynamic_cast(_Chat->getGroup("cb:text_list"));
if (gl) gl->deleteAllChildren();
}
//////////////////////
// CChatGroupWindow //
//////////////////////
void CChatGroupWindow::displayMessage(const ucstring &msg, NLMISC::CRGBA col, CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, uint numBlinks, bool *windowVisible)
{
if (!_Chat)
{
nlwarning(" There's no global chat");
return;
}
CChatTextManager &ctm = getChatTextMngr();
if (_Chat->getHeaderOpened()==NULL)
return;
// *** Display the message in the correct tab window
// get the gl and tab according to filter
CGroupList *gl;
CCtrlTabButton *tab;
getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
// on a new message, change the Tab color
CInterfaceManager *pIM= CInterfaceManager::getInstance();
CRGBA newMsgColor= CRGBA::stringToRGBA(pIM->getDefine("chat_group_tab_color_newmsg").c_str());
ucstring newmsg = msg;
ucstring prefix;
if (gl != NULL)
{
gl->addChild(ctm.createMsgText(newmsg, col));
if (!gl->getParent()->getActive())
if (tab != NULL)
tab->setTextColorNormal(newMsgColor);
}
// *** Display the message in the UserChat (special case)
{
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab5"));
gl = NULL;
CGroupList *gl2 = dynamic_cast(_Chat->getGroup("content:cb:user:text_list"));
CChatWindow *cw = PeopleInterraction.TheUserChat.Window;
CChatStdInput &ci = PeopleInterraction.ChatInput;
switch(gt)
{
default:
case CChatGroup::arround:
case CChatGroup::say: if (ci.AroundMe.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::region: if (ci.Region.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::team: if (ci.Team.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::guild: if (ci.Guild.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::system: if (ci.SystemInfo.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::universe: if (ci.Universe.isListeningWindow(cw)) gl = gl2; break;
case CChatGroup::dyn_chat:
if (ci.DynamicChat[dynamicChatDbIndex].isListeningWindow(cw))
{
gl = gl2;
// Add dyn chan number before string
ucstring prefix("[" + NLMISC::toString(dynamicChatDbIndex) + "]");
// Find position to put the new string
// After timestamp?
size_t pos = newmsg.find(ucstring("]"));
size_t colonpos = newmsg.find(ucstring(": @{"));
// If no ] found or if found but after the colon (so part of the user chat)
if (pos == ucstring::npos || (colonpos < pos))
{
// No timestamp, so put it right after the color and add a space
pos = newmsg.find(ucstring("}"));;
prefix += " ";
}
newmsg = newmsg.substr(0, pos + 1) + prefix + newmsg.substr(pos + 1);
// Add dynchannel number and optionally name before text if user channel
CCDBNodeLeaf* node = NLGUI::CDBManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_DYN_CHANNEL_NAME_IN_CHAT_CB", false);
if (node && node->getValueBool())
{
uint32 textId = ChatMngr.getDynamicChannelNameFromDbIndex(dynamicChatDbIndex);
ucstring title;
STRING_MANAGER::CStringManagerClient::instance()->getDynString(textId, title);
prefix = title.empty() ? ucstring("") : ucstring(" ") + title;
pos = newmsg.find(ucstring("] "));
newmsg = newmsg.substr(0, pos) + prefix + newmsg.substr(pos);
}
}
break;
// NB: the yubo chat cannot be in a user chat
case CChatGroup::yubo_chat: gl = NULL; break;
}
if (gl != NULL)
{
gl->addChild(ctm.createMsgText(newmsg, col));
if (!gl->getParent()->getActive())
if (tab != NULL)
tab->setTextColorNormal(newMsgColor);
}
}
// *** Blink and visibility event
// if the group is closed, make it blink
if (!_Chat->isOpen())
{
if (numBlinks) _Chat->enableBlink(numBlinks);
}
if (_ParentBlink)
{
CGroupContainer *father = dynamic_cast(_Chat->getParent());
if (father && !father->isOpen())
{
father->enableBlink(numBlinks);
}
}
if (windowVisible != NULL)
{
*windowVisible = isVisible();
}
}
//=================================================================================
void CChatGroupWindow::displayTellMessage(const ucstring &msg, NLMISC::CRGBA col, const ucstring &sender)
{
// If we are here with a tell message this is because the teller doesn't belong to any people list
CGroupContainer *gcChat = createFreeTeller(sender);
if (gcChat == NULL)
{
nlwarning(" cannot open chat.");
return;
}
gcChat->requireAttention();
CWidgetManager::getInstance()->setTopWindow(gcChat);
// add the text to this window
CGroupList *gl = dynamic_cast(gcChat->getGroup("text_list"));
if (gl == NULL)
{
nlwarning(" can't get text_list.");
return;
}
gl->addChild(getChatTextMngr().createMsgText(msg, col));
}
//=================================================================================
sint32 CChatGroupWindow::getTabIndex()
{
CGroupTab *pTab = dynamic_cast(_Chat->getGroup("header_opened:channel_select"));
if (pTab != NULL)
return pTab->getSelection();
else
return -1;
}
//=================================================================================
void CChatGroupWindow::setTabIndex(sint32 n)
{
CGroupTab *pTab = dynamic_cast(_Chat->getGroup("header_opened:channel_select"));
if (pTab != NULL)
{
pTab->select(n);
// if the current button is hidden, select default not hid
pTab->selectDefaultIfCurrentHid();
}
}
//=================================================================================
const string CChatGroupWindow::getValidUiStringId(const string &stringId)
{
string validStringId;
for (uint32 i=0; i < stringId.length(); i++)
{
if ((stringId[i] < 'a') || (stringId[i] > 'z'))
validStringId += '_';
else
validStringId += stringId[i];
}
return validStringId;
}
//=================================================================================
CGroupContainer *CChatGroupWindow::createFreeTeller(const ucstring &winNameIn, const string &winColor)
{
// must parse the entity name, and eventually make it Full with shard name (eg: 'ani.yoyo' becomes 'yoyo(Aniro)')
string winNameFull= CShardNames::getInstance().makeFullNameFromRelative(PlayerSelectedMainland, winNameIn.toString());
// remove shard name if necessary
ucstring winName= CEntityCL::removeShardFromName(winNameFull);
// get the color
string sWinColor = winColor;
if (sWinColor.empty())
sWinColor = "UI:SAVE:WIN:COLORS:COM";
// Look if the free teller do not already exists
uint32 i;
string sWinName = winName.toString();
sWinName = toLower(sWinName);
for (i = 0; i < _FreeTellers.size(); ++i)
{
CGroupContainer *pGC = _FreeTellers[i];
if (toLower(pGC->getUCTitle().toString()) == sWinName)
break;
}
// Create container if not present
if (i == _FreeTellers.size())
{
// Corresponding Chat not created -> create and open
vector > properties;
properties.push_back(make_pair(string("posparent"), string("parent")));
properties.push_back(make_pair(string("id"), "free_chat_" + getValidUiStringId(sWinName)));
properties.push_back(make_pair(string("title"), std::string("")));
properties.push_back(make_pair(string("header_color"), sWinColor));
std::string templateName = "contact_chat_friend";
CInterfaceManager *pIM = CInterfaceManager::getInstance();
CInterfaceGroup *pIG = pIM->createGroupInstance(templateName, "ui:interface", properties);
if (!pIG) return NULL;
CGroupContainer *pGC = dynamic_cast(pIG);
if (!pGC)
{
delete pIG;
nlwarning(" group is not a container.(%s)", winName.toString().c_str());
return NULL;
}
// set title from the name
pGC->setUCTitle(winName);
//
pGC->setSavable(true);
pGC->setEscapable(true);
CInterfaceGroup *pRoot = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
pRoot->addGroup (pGC);
pGC->setParent(pRoot); // must be done before makeWindow
CWidgetManager::getInstance()->makeWindow(pGC);
pGC->open();
pGC->updateCoords();
pGC->center();
_FreeTellers.push_back(pGC);
CInterfaceElement *result = pRoot->findFromShortId(string("invite"));
if(result)
{
CCtrlBaseButton * inviteButton = dynamic_cast(result);
if(inviteButton)
{
bool isDM = false;
R2::CEditor::TMode mode = R2::getEditor().getMode();
if (mode != R2::CEditor::NotInitialized )
{
isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
&& uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
&& uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
}
inviteButton->setActive(isDM);
}
}
// the group is only active on the current desktop
pGC->setActive(true);
}
if (!winColor.empty())
_FreeTellers[i]->setHeaderColor(winColor);
// updateFreeTellerHeader(*_FreeTellers[i]);
return _FreeTellers[i];
}
//=================================================================================
void CChatGroupWindow::updateAllFreeTellerHeaders()
{
for(uint k = 0; k < _FreeTellers.size(); ++k)
{
if (_FreeTellers[k])
{
updateFreeTellerHeader(*_FreeTellers[k]);
}
}
}
//=================================================================================
void CChatGroupWindow::updateFreeTellerHeader(CGroupContainer &ft)
{
ucstring name = ft.getUCTitle();
CCtrlBaseButton *newFriendBut = dynamic_cast(ft.getCtrl("new_friend"));
CCtrlBaseButton *ignoreBut = dynamic_cast(ft.getCtrl("ignore"));
CCtrlBaseButton *inviteBut = dynamic_cast(ft.getCtrl("invite"));
if (newFriendBut)
{
newFriendBut->setFrozen(PeopleInterraction.isContactInList(name, 0));
}
if (ignoreBut)
{
ignoreBut->setFrozen(PeopleInterraction.isContactInList(name, 1));
}
if (inviteBut)
{
bool isDM = false;
R2::CEditor::TMode mode = R2::getEditor().getMode();
if (mode != R2::CEditor::NotInitialized )
{
isDM = R2::getEditor().getDMC().getEditionModule().isSessionOwner()
&& uint32(R2::CEditor::AnimationModeLoading) <= uint32(mode)
&& uint32(mode) <= uint32(R2::CEditor::AnimationModeGoingToPlay);
}
inviteBut->setActive(isDM);
if (isDM)
{
inviteBut->setFrozen(false); // TODO Boris : true if player is already invited in anim + do
// PeopleInterraction.updateAllFreeTellerHeaders() when list is updated
}
}
}
//=================================================================================
void CChatGroupWindow::setActiveFreeTeller(const ucstring &winName, bool bActive)
{
CGroupContainer *pGC = createFreeTeller(winName);
if (pGC != NULL)
pGC->setActive(bActive);
}
//=================================================================================
ucstring CChatGroupWindow::getFreeTellerName(const std::string &containerID)
{
uint32 i;
for (i = 0; i < _FreeTellers.size(); ++i)
{
CGroupContainer *pGC = _FreeTellers[i];
if (pGC->getId() == containerID)
break;
}
if (i == _FreeTellers.size())
return ucstring("");
return _FreeTellers[i]->getUCTitle();
}
//=================================================================================
bool CChatGroupWindow::removeFreeTeller(const std::string &containerID)
{
uint32 i;
for (i = 0; i < _FreeTellers.size(); ++i)
{
CGroupContainer *pGC = _FreeTellers[i];
if (pGC->getId() == containerID)
break;
}
if (i == _FreeTellers.size())
return false;
CInterfaceManager *pIM = CInterfaceManager::getInstance();
// Create the free teller in all the desktops images
for (uint m = 0; m < MAX_NUM_MODES; ++m)
{
pIM->removeGroupContainerImage(_FreeTellers[i]->getId(), m);
}
CInterfaceGroup *pRoot = dynamic_cast(CWidgetManager::getInstance()->getElementFromId("ui:interface"));
CWidgetManager::getInstance()->unMakeWindow(_FreeTellers[i]);
pRoot->delGroup (_FreeTellers[i]);
_FreeTellers[i] = NULL;
_FreeTellers.erase(_FreeTellers.begin()+i);
return true;
}
//=================================================================================
void CChatGroupWindow::removeAllFreeTellers()
{
while (!_FreeTellers.empty())
{
if (_FreeTellers[0])
{
removeFreeTeller(_FreeTellers[0]->getId());
}
else
{
_FreeTellers.erase(_FreeTellers.begin());
}
}
}
//=================================================================================
void CChatGroupWindow::saveFreeTeller(NLMISC::IStream &f)
{
f.serialVersion(2);
// Save the free teller only if it is present in the friend list to avoid the only-growing situation
// because free tellers are never deleted in game if we save/load all the free tellers, we just create more
// and more container.
uint32 i, nNbFreeTellerSaved = 0;
for (i = 0; i < _FreeTellers.size(); ++i)
if (PeopleInterraction.FriendList.getIndexFromName(_FreeTellers[i]->getUCTitle()) != -1)
nNbFreeTellerSaved++;
f.serial(nNbFreeTellerSaved);
for (i = 0; i < _FreeTellers.size(); ++i)
{
CGroupContainer *pGC = _FreeTellers[i];
if (PeopleInterraction.FriendList.getIndexFromName(pGC->getUCTitle()) != -1)
{
ucstring sTitle = pGC->getUCTitle();
f.serial(sTitle);
}
}
}
//=================================================================================
void CChatGroupWindow::loadFreeTeller(NLMISC::IStream &f)
{
sint ver = f.serialVersion(2);
if (ver == 1)
{
// Old serialized FreeTellIdCounter (deprecated with v2).
uint32 tmp;
f.serial(tmp);
}
uint32 i, nNbFreeTellerSaved = 0;
f.serial(nNbFreeTellerSaved);
for (i = 0; i < nNbFreeTellerSaved; ++i)
{
if (ver == 1)
{
// Old serialized sID (deprecated with v2).
string sID;
f.serial(sID);
}
ucstring sTitle;
f.serial(sTitle);
CGroupContainer *pGC = createFreeTeller(sTitle, "");
// With version 1 all tells are active because windows information have "title based" ids and no "sID based".
if ((ver == 1) && (pGC != NULL))
pGC->setActive(false);
}
}
//=================================================================================
void CChatGroupWindow::clearMessages(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex)
{
// if not correctly init, abort
if(!_Chat)
return;
// get the gl and tab according to filter
CGroupList *gl;
CCtrlTabButton *tab;
getAssociatedSubWindow(gt, dynamicChatDbIndex, gl, tab);
// delete all text lines
if(gl!=NULL)
{
gl->deleteAllChildren();
}
}
//=================================================================================
void CChatGroupWindow::getAssociatedSubWindow(CChatGroup::TGroupType gt, uint32 dynamicChatDbIndex, CGroupList *&gl, CCtrlTabButton *&tab)
{
nlassert(_Chat);
gl= NULL;
tab= NULL;
switch(gt)
{
default:
case CChatGroup::say:
gl = dynamic_cast(_Chat->getGroup("content:cb:around:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab0"));
break;
case CChatGroup::team:
gl = dynamic_cast(_Chat->getGroup("content:cb:team:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab2"));
break;
case CChatGroup::guild:
gl = dynamic_cast(_Chat->getGroup("content:cb:guild:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab3"));
break;
case CChatGroup::system:
gl = dynamic_cast(_Chat->getGroup("content:cb:sysinfo:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab4"));
break;
case CChatGroup::region:
gl = dynamic_cast(_Chat->getGroup("content:cb:region:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab1"));
break;
case CChatGroup::yubo_chat:
gl = dynamic_cast(_Chat->getGroup("content:cb:yubo_chat:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab6"));
break;
case CChatGroup::dyn_chat:
{
// use dynamicChatDbIndex to get the wanted tab button/group
gl = dynamic_cast(_Chat->getGroup(toString("content:cb:dyn_chat%d:text_list", dynamicChatDbIndex)));
tab = dynamic_cast(_Chat->getCtrl(toString("header_opened:channel_select:tab_array0_%d",dynamicChatDbIndex)));
}
break;
case CChatGroup::universe:
gl = dynamic_cast(_Chat->getGroup("content:cb:universe:text_list"));
tab = dynamic_cast(_Chat->getCtrl("header_opened:channel_select:tab7"));
break;
}
}
////////////////////////
// CChatWindowManager //
////////////////////////
//=================================================================================
CChatWindowManager::CChatWindowManager() : _WindowID(0)
{
}
//=================================================================================
CChatWindowManager::~CChatWindowManager()
{
for(TChatWindowMap::iterator it = _ChatWindowMap.begin(); it != _ChatWindowMap.end(); ++it)
{
delete it->second;
}
}
//=================================================================================
CChatWindow *CChatWindowManager::createChatWindow(const CChatWindowDesc &desc)
{
if (getChatWindow(desc.Title))
{
return NULL; // duplicate name encountered
}
CChatWindow *w;
w = new CChatWindow;
string zeId;
if (desc.Id.empty())
zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
else
zeId = desc.Id;
if (w->create(desc, zeId))
{
if (desc.Id.empty())
_WindowID++;
if (desc.Localize)
_ChatWindowMap[CI18N::get(desc.Title.toString())] = w;
else
_ChatWindowMap[desc.Title] = w;
w->setAHOnActive(desc.AHOnActive);
w->setAHOnActiveParams(desc.AHOnActiveParams);
w->setAHOnDeactive(desc.AHOnDeactive);
w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
w->setAHOnCloseButton(desc.AHOnCloseButton);
w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
if (!desc.HeaderColor.empty())
w->setHeaderColor(desc.HeaderColor);
return w;
}
else
{
return NULL;
}
}
//=================================================================================
CChatWindow *CChatWindowManager::createChatGroupWindow(const CChatWindowDesc &desc)
{
if (getChatWindow(desc.Title))
{
return NULL; // duplicate name encountered
}
CChatGroupWindow *w;
w = new CChatGroupWindow;
string zeId;
if (desc.Id.empty())
zeId = NLMISC::toString("chat_window_id_%d", (int) _WindowID);
else
zeId = desc.Id;
if (w->create(desc, zeId))
{
if (desc.Id.empty())
_WindowID++;
if (desc.Localize)
_ChatWindowMap[CI18N::get(desc.Title.toString())] = w;
else
_ChatWindowMap[desc.Title] = w;
w->setAHOnActive(desc.AHOnActive);
w->setAHOnActiveParams(desc.AHOnActiveParams);
w->setAHOnDeactive(desc.AHOnDeactive);
w->setAHOnDeactiveParams(desc.AHOnDeactiveParams);
w->setAHOnCloseButton(desc.AHOnCloseButton);
w->setAHOnCloseButtonParams(desc.AHOnCloseButtonParams);
if (!desc.HeaderColor.empty())
w->setHeaderColor(desc.HeaderColor);
return w;
}
else
{
return NULL;
}
}
//=================================================================================
CChatWindow *CChatWindowManager::getChatWindow(const ucstring &title)
{
TChatWindowMap::iterator it = _ChatWindowMap.find(title);
if (it == _ChatWindowMap.end())
return NULL;
else
{
nlassert(it->second != NULL);
return it->second;
}
}
//=================================================================================
void CChatWindowManager::removeChatWindow(const ucstring &title)
{
TChatWindowMap::iterator it = _ChatWindowMap.find(title);
if (it == _ChatWindowMap.end())
{
nlwarning("unknwown window %s", title.toString().c_str());
return;
}
it->second->deleteContainer();
delete it->second;
_ChatWindowMap.erase(it);
}
//=================================================================================
CChatWindowManager &CChatWindowManager::getInstance()
{
static CChatWindowManager instance;
return instance;
}
//=================================================================================
CChatWindow *CChatWindowManager::getChatWindowFromCaller(CCtrlBase *caller)
{
// retrieve pointer on the CChatWindow instance associated with the ui
// find first enclosing group container
CGroupContainer *father = NULL;
while (caller)
{
father = dynamic_cast(caller);
if (father) break;
caller = caller->getParent();
}
if (!father) return NULL;
return getChatWindow(father->getUCTitle());
}
//=================================================================================
bool CChatWindowManager::rename(const ucstring &oldName, const ucstring &newName, bool newNameLocalize)
{
// if (oldName == newName) return true;
CChatWindow *newWin = getChatWindow(newName);
if (newWin != NULL) return false; // target window exists
TChatWindowMap::iterator it = _ChatWindowMap.find(oldName);
if (it == _ChatWindowMap.end()) return false;
if (newNameLocalize)
{
_ChatWindowMap[CI18N::get(newName.toString())] = it->second;
it->second->getContainer()->setLocalize(true);
it->second->getContainer()->setTitle(newName.toString());
}
else
{
_ChatWindowMap[newName] = it->second;
it->second->getContainer()->setLocalize(false);
it->second->getContainer()->setUCTitle(newName);
}
_ChatWindowMap.erase(it);
return true;
}
//=================================================================================
CGroupEditBox *CChatWindow::getEditBox() const
{
if (!_Chat) return NULL;
return dynamic_cast(_Chat->getGroup("eb"));
}
//=================================================================================
void CChatWindowManager::removeChatWindow(CChatWindow *cw)
{
if (!cw) return;
removeChatWindow(cw->getTitle());
}
//=================================================================================
CChatWindow *CChatWindowManager::getChatWindowByIndex(uint index)
{
TChatWindowMap::iterator it = _ChatWindowMap.begin();
while (index--) { ++it; }
return it->second;
}
/////////////////////
// ACTION HANDLERS //
/////////////////////
// ***************************************************************************************
class CHandlerChatBoxEntry : public IActionHandler
{
public:
void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
{
CGroupEditBox *pEB = dynamic_cast(pCaller);
if (pEB == NULL) return;
ucstring text = pEB->getInputString();
// If the line is empty, do nothing
if(text.size() == 0)
return;
CChatWindow *chat = getChatWndMgr().getChatWindowFromCaller(pCaller);
if (!chat)
{
nlwarning("No chat box associated with %s", pEB->getId().c_str());
return;
}
// Parse any tokens in the text
if ( ! CInterfaceManager::parseTokens(text))
{
pEB->setInputString (string(""));
return;
}
// if, it s a command, execute it and don't send the command to the server
if(text[0] == '/')
{
CChatWindow::_ChatWindowLaunchingCommand = chat;
string str = text.toUtf8();
string cmdWithArgs = str.substr(1);
// Get the command name from the string, can contain spaces
string cmd = cmdWithArgs.substr(0, cmdWithArgs.find(' '));
if (cmdWithArgs.find('"') == 0)
{
string::size_type pos = cmdWithArgs.find('"', 1);
if (string::npos != pos)
{
cmd = cmdWithArgs.substr(1, pos - 1);
}
}
if ( NLMISC::ICommand::exists( cmd ) )
{
NLMISC::ICommand::execute( cmdWithArgs, g_log );
}
else
{
CInterfaceManager *im = CInterfaceManager::getInstance();
im->displaySystemInfo (ucstring(cmd+": ")+CI18N::get ("uiCommandNotExists"));
}
}
else
{
if (chat->getListener())
{
chat->getListener()->msgEntered(text, chat);
}
}
// Clear input string
pEB->setInputString (ucstring(""));
CGroupContainer *gc = static_cast< CGroupContainer* >( pEB->getEnclosingContainer() );
if (gc)
{
// Restore position of enclosing container if it hasn't been moved/scaled/poped by the user
if (!gc->getTouchFlag(true))
{
gc->restorePosition();
}
}
}
};
REGISTER_ACTION_HANDLER(CHandlerChatBoxEntry, "chat_box_entry");
static ucstring getFreeTellerName(CInterfaceElement *pCaller)
{
if (!pCaller) return ucstring();
CChatGroupWindow *cgw = PeopleInterraction.getChatGroupWindow();
if (!cgw) return ucstring();
CInterfaceGroup *freeTeller = pCaller->getParentContainer();
if (!freeTeller) return ucstring();
return cgw->getFreeTellerName( freeTeller->getId() );
}
// ***************************************************************************************
class CHandlerAddTellerToFriendList : public IActionHandler
{
public:
void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
{
ucstring playerName = ::getFreeTellerName(pCaller);
if (!playerName.empty())
{
sint playerIndex = PeopleInterraction.IgnoreList.getIndexFromName(playerName);
// if already in friend list, ask to move rather than add
if (playerIndex != -1)
{
PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.IgnoreList, &PeopleInterraction.FriendList);
}
else
{
PeopleInterraction.askAddContact(playerName, &PeopleInterraction.FriendList);
}
}
}
};
REGISTER_ACTION_HANDLER(CHandlerAddTellerToFriendList, "add_teller_to_friend_list");
// ***************************************************************************************
class CHandlerAddTellerToIgnoreList : public IActionHandler
{
public:
void execute (CCtrlBase *pCaller, const std::string &sParams)
{
CInterfaceManager *im = CInterfaceManager::getInstance();
std::string callerId = getParam(sParams, "id");
CInterfaceElement *prevCaller = CWidgetManager::getInstance()->getElementFromId(callerId);
ucstring playerName = ::getFreeTellerName(prevCaller);
if (!playerName.empty())
{
// if already in friend list, ask to move rather than add
sint playerIndex = PeopleInterraction.FriendList.getIndexFromName(playerName);
if (playerIndex != -1)
{
PeopleInterraction.askMoveContact(playerIndex, &PeopleInterraction.FriendList, &PeopleInterraction.IgnoreList);
}
else
{
PeopleInterraction.askAddContact(playerName, &PeopleInterraction.IgnoreList);
}
if (pCaller)
{
CInterfaceGroup *win = prevCaller->getParentContainer();
if (win)
{
static_cast< CGroupContainer* >( win )->setActive(false);
}
}
}
}
};
REGISTER_ACTION_HANDLER(CHandlerAddTellerToIgnoreList, "add_teller_to_ignore_list");
// ***************************************************************************************
class CHandlerInviteToRingSession : public IActionHandler
{
public:
void execute (CCtrlBase *pCaller, const std::string &/* sParams */)
{
ucstring playerName = ::getFreeTellerName(pCaller);
if (!playerName.empty())
{
// ask the SBS to invite the character in the session
CSessionBrowserImpl::getInstance().inviteCharacterByName(CSessionBrowserImpl::getInstance().getCharId(), playerName.toUtf8());
// additionaly, send a tell to signal the player he has been invited to a ring session
ChatMngr.tell(playerName.toUtf8(), CI18N::get("uiRingInviteNotification"));
//
CInterfaceManager *im = CInterfaceManager::getInstance();
im->displaySystemInfo(ucstring("@{6F6F}") + playerName +ucstring(" @{FFFF}") + CI18N::get("uiRingInvitationSent"), "BC");
// force a refresh of the ui
im->executeLuaScript("CharTracking:forceRefresh()");
}
}
};
REGISTER_ACTION_HANDLER(CHandlerInviteToRingSession, "invite_to_ring_session");