// // Created by koncord on 04.03.16. // #include "GUIChat.hpp" #include #include "apps/openmw/mwbase/environment.hpp" #include "apps/openmw/mwgui/windowmanagerimp.hpp" #include "apps/openmw/mwinput/inputmanagerimp.hpp" #include #include #include #include #include "../Networking.hpp" #include "../Main.hpp" #include "../LocalPlayer.hpp" #include "../../mwbase/world.hpp" #include "../../mwworld/timestamp.hpp" #include "../GUIController.hpp" namespace mwmp { GUIChat::GUIChat(int x, int y, int w, int h) : WindowBase("tes3mp_chat.layout") { netStat = false; setCoord(x, y, w, h); getWidget(mCommandLine, "edit_Command"); getWidget(mHistory, "list_History"); getWidget(mChannelPrevBtn, "btn_prev_ch"); mChannelPrevBtn->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::prevChannel); getWidget(mChannelNextBtn, "btn_next_ch"); mChannelNextBtn->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::nextChannel); getWidget(mBoxChannels, "box_channels"); for (int i = 0; i < 3; ++i) { getWidget(mChannelBtns[i], "btn_ch" + std::to_string(i + 1)); mChannelBtns[i]->eventMouseButtonClick += MyGUI::newDelegate(this, &GUIChat::onClickChannel); } // Set up the command line box mCommandLine->eventEditSelectAccept += newDelegate(this, &GUIChat::acceptCommand); mCommandLine->eventKeyButtonPressed += newDelegate(this, &GUIChat::keyPress); mHistory->setOverflowToTheLeft(true); mHistory->setEditWordWrap(true); mHistory->setTextShadow(true); mHistory->setTextShadowColour(MyGUI::Colour::Black); mHistory->setNeedKeyFocus(false); windowState = 0; mCommandLine->setVisible(false); mBoxChannels->setVisible(false); delay = 3; // 3 sec. page = 0; currentChannel = 0; addChannel(0, "Default"); setChannel(0); redrawChannels(); defaultColor = mChannelPrevBtn->getTextColour(); } void GUIChat::onOpen() { // Give keyboard focus to the combo box whenever the console is // turned on setEditState(0); windowState = CHAT_ENABLED; } void GUIChat::onClose() { // Apparently, hidden widgets can retain key focus // Remove for MyGUI 3.2.2 windowState = CHAT_DISABLED; setEditState(0); } bool GUIChat::exit() { //WindowBase::exit(); return true; } void GUIChat::acceptCommand(MyGUI::EditBox *_sender) { const std::string &cm = MyGUI::TextIterator::toTagsString(mCommandLine->getCaption()); // If they enter nothing, then it should be canceled. // Otherwise, there's no way of closing without having text. if (cm.empty()) { mCommandLine->setCaption(""); setEditState(false); return; } if (cm.find("//", 0, 2) == 0) //clientside commands { typedef boost::char_separator csep; csep sep(" "); boost::tokenizer tokens(cm, sep); std::vector cmd; for (const auto& t : tokens) cmd.push_back(t); if (cmd[0] == "//channel") { if (cmd[1] == "close") { LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Closed \"%d\" channel", currentChannel); closeChannel(currentChannel); } } } else { LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Player: %s", cm); send (cm); } // Add the command to the history, and set the current pointer to // the end of the list if (mCommandHistory.empty() || mCommandHistory.back() != cm) mCommandHistory.push_back(cm); mCurrent = mCommandHistory.end(); mEditString.clear(); mCommandLine->setCaption(""); setEditState(false); } void GUIChat::onResChange(int width, int height) { setCoord(10,10, width-10, height/2); } void GUIChat::setFont(const std::string &fntName) { mHistory->setFontName(fntName); mCommandLine->setFontName(fntName); } void GUIChat::print(unsigned channelId, const std::string &msg, const std::string &color) { if (windowState == 2 && !isVisible()) { setVisible(true); } if (channelId != currentChannel) { try { auto it = getChannel(channelId); if (it != channels.end()) { it->channelText = color + msg; it->newMessages = true; } } catch(std::out_of_range &e) {} } else mHistory->addText(color + msg); LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", msg.c_str()); } void GUIChat::send(const std::string &str) { LocalPlayer *localPlayer = Main::get().getLocalPlayer(); Networking *networking = Main::get().getNetworking(); localPlayer->chat.action = Chat::Action::print; localPlayer->chat.channel = currentChannel; localPlayer->chat.message = str; networking->getPlayerPacket(ID_CHAT_MESSAGE)->setPlayer(localPlayer); networking->getPlayerPacket(ID_CHAT_MESSAGE)->Send(); } void GUIChat::clean(unsigned channelId) { if (channelId == currentChannel) mHistory->setCaption(""); else { auto it = getChannel(channelId); if (it != channels.end()) { it->channelText.clear(); } } } void GUIChat::pressedChatMode() { windowState++; if (windowState == 3) windowState = 0; std::string chatMode = windowState == CHAT_DISABLED ? "Chat disabled" : windowState == CHAT_ENABLED ? "Chat enabled" : "Chat in hidden mode"; LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Switch chat mode to %s", chatMode.c_str()); MWBase::Environment::get().getWindowManager()->messageBox(chatMode); switch (windowState) { case CHAT_DISABLED: this->mMainWidget->setVisible(false); setEditState(false); break; case CHAT_ENABLED: this->mMainWidget->setVisible(true); break; default: //CHAT_HIDDENMODE this->mMainWidget->setVisible(true); curTime = 0; } } void GUIChat::setEditState(bool state) { editState = state; mCommandLine->setVisible(editState); mBoxChannels->setVisible(editState); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(editState ? mCommandLine : nullptr); } void GUIChat::pressedSay() { if (windowState == CHAT_DISABLED) return; if (!mCommandLine->getVisible()) LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Opening chat."); if (windowState == CHAT_HIDDENMODE) { setVisible(true); curTime = 0; } setEditState(true); } void GUIChat::keyPress(MyGUI::Widget *_sender, MyGUI::KeyCode key, MyGUI::Char _char) { if (mCommandHistory.empty()) return; // Traverse history with up and down arrows if (key == MyGUI::KeyCode::ArrowUp) { // If the user was editing a string, store it for later if (mCurrent == mCommandHistory.end()) mEditString = mCommandLine->getOnlyText(); if (mCurrent != mCommandHistory.begin()) { --mCurrent; mCommandLine->setCaption(*mCurrent); } } else if (key == MyGUI::KeyCode::ArrowDown) { if (mCurrent != mCommandHistory.end()) { ++mCurrent; if (mCurrent != mCommandHistory.end()) mCommandLine->setCaption(*mCurrent); else // Restore the edit string mCommandLine->setCaption(mEditString); } } } void GUIChat::update(float dt) { if (windowState == CHAT_HIDDENMODE && !editState && isVisible()) { curTime += dt; if (curTime >= delay) { setEditState(false); this->mMainWidget->setVisible(false); } } if (netStat) { auto rss = Main::get().getNetworking()->getNetworkStatistics(); mHistory->setCaption(rss); } static float time = 0; time += dt; if (time >= 1) { time = 0; static bool phase = false; phase = !phase; auto color = phase ? MyGUI::Colour::Blue : defaultColor; for (auto it = channels.begin(); it != channels.end(); ++it) { if (!it->newMessages || it->channel == currentChannel) continue; long pos = it - channels.begin(); if (pos < page * pageM) mChannelPrevBtn->setTextColour(color); else if (pos >= page * pageM + 3) mChannelNextBtn->setTextColour(color); else { for (auto &btn : mChannelBtns) { if (it->channelName != btn->getCaption().asUTF8()) continue; btn->setTextColour(color); break; } } } } } void GUIChat::setCaption(const std::string &str) { mHistory->setCaption(str); } void GUIChat::setDelay(float delay) { this->delay = delay; } void GUIChat::switchNetstat() { netStat = !netStat; } void GUIChat::addChannel(unsigned ch, const std::string &name) { auto channel = getChannel(ch); if (channel == channels.end()) { LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Adding channel id: %d %s", ch, name); channels.push_back(ChannelData{ch, name.substr(0, 9), "", false}); } redrawChannels(); } void GUIChat::renameChannel(unsigned ch, const std::string &newName) { auto it = getChannel(ch); if (it != channels.end()) { it->channelName = newName.substr(0, 9); redrawChannels(); } } void GUIChat::setChannel(const std::string &channel, bool saveHistory) { auto ch = channel.substr(0, 9); auto it = std::find_if(channels.begin(), channels.end(), [&ch](const ChannelData &item) { return item.channelName == ch; }); if (it != channels.end()) setChannel(it, saveHistory); } void GUIChat::setChannel(unsigned ch, bool saveHistory) { auto it = getChannel(ch); if (it != channels.end()) setChannel(it, saveHistory); } void GUIChat::setChannel(ChannelIter it, bool saveHistory) { if (saveHistory && !mHistory->getCaption().empty()) getChannel(currentChannel)->channelText = mHistory->getCaption(); it->newMessages = false; mHistory->setCaption(it->channelText); currentChannel = it->channel; redrawChannels(); } void GUIChat::closeChannel(unsigned ch) { if (ch == 0) // that's impossible return; auto it = getChannel(ch); if (it != channels.end()) { if (ch == it->channel) setChannel(0, false); // reset to default channel channels.erase(it); } redrawChannels(); } void GUIChat::redrawChannels() { mChannelPrevBtn->setVisible(page != 0); mChannelNextBtn->setVisible(channels.size() > 3 && page != lastPage()); mChannelPrevBtn->setTextColour(defaultColor); mChannelNextBtn->setTextColour(defaultColor); if (page >=lastPage()) page = lastPage(); unsigned showElems = page * pageM + 3; if (showElems > channels.size()) showElems = static_cast(channels.size()); auto it = channels.begin() + page * pageM; auto endIt = channels.begin() + showElems; for (auto &btn : mChannelBtns) { if (it == endIt) btn->setVisible(false); else { btn->setVisible(true); if (currentChannel == it->channel) { setTitle("Chat: " + it->channelName + "(" + std::to_string(it->channel) + ")"); btn->setEnabled(false); btn->setStateSelected(true); btn->setTextColour(MyGUI::Colour::Red); } else { btn->setEnabled(true); btn->setTextColour(defaultColor); } btn->setCaption(it++->channelName); } } } void GUIChat::nextChannel(MyGUI::Widget* _sender) { page++; if (page > lastPage()) page = lastPage(); redrawChannels(); } void GUIChat::prevChannel(MyGUI::Widget* _sender) { if (page > 0) page--; redrawChannels(); } void GUIChat::onClickChannel(MyGUI::Widget *_sender) { auto sender = dynamic_cast(_sender); setChannel(sender->getCaption().asUTF8()); } unsigned GUIChat::lastPage() { return static_cast(channels.size() / pageM + channels.size() % pageM - 1); } GUIChat::ChannelIter GUIChat::getChannel(unsigned ch) { return std::find_if(channels.begin(), channels.end(), [&ch](const ChannelData &data){ return data.channel == ch; }); } }