diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llui/llfloater.cpp | 2 | ||||
-rw-r--r-- | indra/llui/llfloater.h | 2 | ||||
-rw-r--r-- | indra/llui/llview.cpp | 2 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
-rwxr-xr-x | indra/newview/llfloaterpreference.cpp | 8 | ||||
-rw-r--r-- | indra/newview/llimconversation.cpp | 280 | ||||
-rw-r--r-- | indra/newview/llimconversation.h | 97 | ||||
-rw-r--r-- | indra/newview/llimfloater.cpp | 600 | ||||
-rw-r--r-- | indra/newview/llimfloater.h | 58 | ||||
-rw-r--r-- | indra/newview/llimfloatercontainer.cpp | 1 | ||||
-rw-r--r-- | indra/newview/llimview.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llnearbychat.cpp | 26 | ||||
-rw-r--r-- | indra/newview/llnearbychat.h | 10 | ||||
-rw-r--r-- | indra/newview/llnearbychatbar.cpp | 110 | ||||
-rw-r--r-- | indra/newview/llnearbychatbar.h | 16 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_chat_bar.xml | 162 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_nearby_chat.xml | 21 |
17 files changed, 847 insertions, 552 deletions
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index b087205a5c..5635905327 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1635,6 +1635,7 @@ void LLFloater::onClickTearOff(LLFloater* self) // give focus to new window to keep continuity for the user self->setFocus(TRUE); self->setTornOff(true); + } else //Attach to parent. { @@ -1649,6 +1650,7 @@ void LLFloater::onClickTearOff(LLFloater* self) self->setTornOff(false); } self->updateTitleButtons(); + self->setOpenPositioning(LLFloaterEnums::OPEN_POSITIONING_NONE); } // static diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index a7cc9ae961..cd02310bf8 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -329,6 +329,8 @@ public: virtual void setDocked(bool docked, bool pop_on_undock = true); virtual void setTornOff(bool torn_off) { mTornOff = torn_off; } + bool getTornOff() {return mTornOff;} + void setOpenPositioning(LLFloaterEnums::EOpenPositioning pos) {mOpenPositioning = pos;} // Return a closeable floater, if any, given the current focus. static LLFloater* getClosableFloaterFromFocus(); diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 421166dcd4..166cd99d03 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -349,7 +349,7 @@ void LLView::removeChild(LLView* child) } else { - llwarns << child->getName() << "is not a child of " << getName() << llendl; + llwarns << "\"" << child->getName() << "\" is not a child of " << getName() << llendl; } updateBoundingRect(); } diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index b780a27ce2..86d30c239f 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -277,6 +277,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimconversation.cpp llimfloater.cpp llimfloatercontainer.cpp llimhandler.cpp @@ -834,6 +835,7 @@ set(viewer_HEADER_FILES llhudrender.h llhudtext.h llhudview.h + llimconversation.h llimfloater.h llimfloatercontainer.h llimview.h diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 3ed575086c..18ab9dc264 100755 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -423,13 +423,9 @@ void LLFloaterPreference::saveAvatarProperties( void ) BOOL LLFloaterPreference::postBuild() { - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate)); +// gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); - gSavedSettings.getControl("PlainTextChatHistory")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMFloater::processChatHistoryStyleUpdate)); - - gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLNearbyChat::processChatHistoryStyleUpdate)); + gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLIMConversation::processChatHistoryStyleUpdate)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); diff --git a/indra/newview/llimconversation.cpp b/indra/newview/llimconversation.cpp new file mode 100644 index 0000000000..7220ab6a82 --- /dev/null +++ b/indra/newview/llimconversation.cpp @@ -0,0 +1,280 @@ +/** + * @file llimconversation.cpp + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llpanelimcontrolpanel.h" + +#include "lldraghandle.h" +#include "llfloaterreg.h" +#include "llimconversation.h" +#include "llimfloater.h" +#include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container +#include "lllayoutstack.h" +#include "llnearbychat.h" +#include "llnearbychatbar.h" + +LLIMConversation::LLIMConversation(const LLUUID& session_id) + : LLTransientDockableFloater(NULL, true, session_id) + , mControlPanel(NULL) + , mIsP2PChat(false) + , mExpandCollapseBtn(NULL) + , mTearOffBtn(NULL) + , mCloseBtn(NULL) + , mSessionID(session_id) +{ + mCommitCallbackRegistrar.add("IMSession.Menu.Action", + boost::bind(&LLIMConversation::onIMSessionMenuItemClicked, this, _2)); +// mCommitCallbackRegistrar.add("IMSession.ExpCollapseBtn.Click", +// boost::bind(&LLIMConversation::onSlide, this)); +// mCommitCallbackRegistrar.add("IMSession.CloseBtn.Click", +// boost::bind(&LLFloater::onClickClose, this)); + mCommitCallbackRegistrar.add("IMSession.TearOffBtn.Click", + boost::bind(&LLIMConversation::onTearOffClicked, this)); + mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", + boost::bind(&LLIMConversation::onIMCompactExpandedMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", + boost::bind(&LLIMConversation::onIMShowModesMenuItemCheck, this, _2)); + mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", + boost::bind(&LLIMConversation::onIMShowModesMenuItemEnable, this, _2)); +} + +BOOL LLIMConversation::postBuild() +{ + mCloseBtn = getChild<LLButton>("close_btn"); + mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); + + mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); + mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMConversation::onSlide, this)); + + if (mControlPanel) + { + mControlPanel->setSessionId(mSessionID); + mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel")); + + mExpandCollapseBtn->setImageOverlay( + getString(mControlPanel->getParent()->getVisible() ? "collapse_icon" : "expand_icon")); + } + else + { + mExpandCollapseBtn->setEnabled(false); + getChild<LLLayoutPanel>("im_control_panel_holder")->setVisible(false); + } + + mTearOffBtn = getChild<LLButton>("tear_off_btn"); + mTearOffBtn->setCommitCallback(boost::bind(&LLIMConversation::onTearOffClicked, this)); + + if (!getTornOff()) + { + setOpenPositioning(LLFloaterEnums::OPEN_POSITIONING_NONE); + } + + if (isChatMultiTab()) + { + return LLFloater::postBuild(); + } + else + { + return LLDockableFloater::postBuild(); + } + +} + +void LLIMConversation::onIMSessionMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == "compact_view" || item == "expanded_view") + { + gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view"); + } + else + { + bool prev_value = gSavedSettings.getBOOL(item); + gSavedSettings.setBOOL(item, !prev_value); + } + + LLIMConversation::processChatHistoryStyleUpdate(); +} + + +bool LLIMConversation::onIMCompactExpandedMenuItemCheck(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory"); + + return is_plain_text_mode? item == "compact_view" : item == "expanded_view"; +} + + +bool LLIMConversation::onIMShowModesMenuItemCheck(const LLSD& userdata) +{ + return gSavedSettings.getBOOL(userdata.asString()); +} + +// enable/disable states for the "show time" and "show names" items of the show-modes menu +bool LLIMConversation::onIMShowModesMenuItemEnable(const LLSD& userdata) +{ + std::string item = userdata.asString(); + bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); + bool is_not_names = (item != "IMShowNamesForP2PConv"); + return (plain_text && (is_not_names || mIsP2PChat)); +} + +void LLIMConversation::updateHeaderAndToolbar() +{ + bool is_hosted = getHost() != NULL; + + if (is_hosted) + { + for (S32 i = 0; i < BUTTON_COUNT; i++) + { + if (mButtons[i]) + { + // Hide the standard header buttons in a docked IM floater. + mButtons[i]->setVisible(false); + } + } + } + + bool is_control_panel_visible = false; + if (mControlPanel) + { + // Control panel should be visible only in torn off floaters. + is_control_panel_visible = !is_hosted && gSavedSettings.getBOOL("IMShowControlPanel"); + mControlPanel->getParent()->setVisible(is_control_panel_visible); + } + + // Display collapse image (<<) if the floater is hosted + // or if it is torn off but has an open control panel. + bool is_expanded = is_hosted || is_control_panel_visible; + mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); + + LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); + if (session) + { + // The button (>>) should be disabled for torn off P2P conversations. + mExpandCollapseBtn->setEnabled(is_hosted || !session->isP2PSessionType()); + } + else + { + if (!mIsNearbyChat) + { + llwarns << "IM session not found." << llendl; + } + } + + if (mDragHandle) + { + // toggle floater's drag handle and title visibility + mDragHandle->setVisible(!is_hosted); + } + + mTearOffBtn->setImageOverlay(getString(is_hosted ? "tear_off_icon" : "return_icon")); + + mCloseBtn->setVisible(is_hosted); + + enableDisableCallBtn(); +} + +// static +void LLIMConversation::processChatHistoryStyleUpdate() +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); + if (floater) + { + floater->reloadMessages(); + } + } + + LLNearbyChatBar* nearby_chat_bar = LLNearbyChatBar::getInstance(); + if (nearby_chat_bar) + { + nearby_chat_bar->reloadMessages(); + } +} + +void LLIMConversation::updateCallBtnState(bool callIsActive) +{ + getChild<LLButton>("voice_call_btn")->setImageOverlay( + callIsActive? getString("call_btn_stop") : getString("call_btn_start")); + enableDisableCallBtn(); + +} + +void LLIMConversation::onSlide(LLIMConversation* self) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(self->getHost()); + if (host_floater) + { + // Hide the messages pane if a floater is hosted in the Conversations + host_floater->collapseMessagesPane(true); + } + else ///< floater is torn off + { + if (self->mControlPanel) + { + bool expand = !self->mControlPanel->getParent()->getVisible(); + + // Expand/collapse the IM control panel + self->mControlPanel->getParent()->setVisible(expand); + + gSavedSettings.setBOOL("IMShowControlPanel", expand); + + self->mExpandCollapseBtn->setImageOverlay(self->getString(expand ? "collapse_icon" : "expand_icon")); + } + } +} + +/*virtual*/ +void LLIMConversation::onOpen(const LLSD& key) +{ + LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); + if (host_floater) + { + // Show the messages pane when opening a floater hosted in the Conversations + host_floater->collapseMessagesPane(false); + } + + updateHeaderAndToolbar(); +} + +void LLIMConversation::onTearOffClicked() +{ + onClickTearOff(this); + updateHeaderAndToolbar(); +} + +// static +bool LLIMConversation::isChatMultiTab() +{ + // Restart is required in order to change chat window type. + static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; + return is_single_window; +} diff --git a/indra/newview/llimconversation.h b/indra/newview/llimconversation.h new file mode 100644 index 0000000000..501977e061 --- /dev/null +++ b/indra/newview/llimconversation.h @@ -0,0 +1,97 @@ +/** + * @file llimconversation.h + * @brief LLIMConversation class implements the common behavior of LNearbyChatBar + * @brief and LLIMFloater for hosting both in LLIMContainer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_IMCONVERSATION_H +#define LL_IMCONVERSATION_H + +#include "lltransientdockablefloater.h" +#include "llviewercontrol.h" + +class LLPanelChatControlPanel; + +class LLIMConversation + : public LLTransientDockableFloater +{ + +public: + LOG_CLASS(LLIMConversation); + + LLIMConversation(const LLUUID& session_id); + + // reload all message with new settings of visual modes + static void processChatHistoryStyleUpdate(); + + /** + * Returns true if chat is displayed in multi tabbed floater + * false if chat is displayed in multiple windows + */ + static bool isChatMultiTab(); + + // LLFloater overrides + /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ BOOL postBuild(); + +protected: + + // callback for click on any items of the visual states menu + void onIMSessionMenuItemClicked(const LLSD& userdata); + + // callback for check/uncheck of the expanded/collapse mode's switcher + bool onIMCompactExpandedMenuItemCheck(const LLSD& userdata); + + // + bool onIMShowModesMenuItemCheck(const LLSD& userdata); + bool onIMShowModesMenuItemEnable(const LLSD& userdata); + static void onSlide(LLIMConversation *self); + void onTearOffClicked(); + + // refresh a visual state of the Call button + void updateCallBtnState(bool callIsActive); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn() = 0; + +// /* virtual */ void updateTitleButtons(); + + + LLPanelChatControlPanel* mControlPanel; + bool mIsNearbyChat; + bool mIsP2PChat; + + LLUUID mSessionID; + + LLButton* mExpandCollapseBtn; + LLButton* mTearOffBtn; + LLButton* mCloseBtn; + +private: + /// Update floater header and toolbar buttons when hosted/torn off state is toggled. + void updateHeaderAndToolbar(); +}; + + +#endif // LL_IMCONVERSATION_H diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index cd795fcfc7..5339bcb936 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -41,10 +41,9 @@ #include "llfloaterreg.h" #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container #include "llinventoryfunctions.h" -#include "lllayoutstack.h" +//#include "lllayoutstack.h" #include "lllineeditor.h" #include "lllogchat.h" -#include "llnearbychat.h" #include "llpanelimcontrolpanel.h" #include "llscreenchannel.h" #include "llsyswellwindow.h" @@ -60,18 +59,12 @@ #include "llnotificationmanager.h" LLIMFloater::LLIMFloater(const LLUUID& session_id) - : LLTransientDockableFloater(NULL, true, session_id), - mControlPanel(NULL), - mSessionID(session_id), + : LLIMConversation(session_id), mLastMessageIndex(-1), mDialog(IM_NOTHING_SPECIAL), mInputEditor(NULL), - mCloseBtn(NULL), - mExpandCollapseBtn(NULL), - mTearOffBtn(NULL), mSavedTitle(), mTypingStart(), - mIsP2PChat(false), mShouldSendTypingState(false), mChatHistory(NULL), mMeTyping(false), @@ -81,6 +74,8 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) mPositioned(false), mSessionInitialized(false) { + mIsNearbyChat = false; + mSession = LLIMModel::getInstance()->findIMSession(mSessionID); if (mSession) @@ -97,7 +92,7 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) case IM_SESSION_GROUP_START: mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); break; - case IM_SESSION_INVITE: + case IM_SESSION_INVITE: if (gAgent.isInGroup(mSessionID)) { mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); @@ -116,60 +111,30 @@ LLIMFloater::LLIMFloater(const LLUUID& session_id) LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); setDocked(true); - mCommitCallbackRegistrar.add("IMSession.Menu.Action", - boost::bind(&LLIMFloater::onIMSessionMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("IMSession.Menu.CompactExpandedModes.CheckItem", - boost::bind(&LLIMFloater::onIMCompactExpandedMenuItemCheck, this, _2)); - mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.CheckItem", - boost::bind(&LLIMFloater::onIMShowModesMenuItemCheck, this, _2)); - mEnableCallbackRegistrar.add("IMSession.Menu.ShowModes.Enable", - boost::bind(&LLIMFloater::onIMShowModesMenuItemEnable, this, _2)); -} - -bool LLIMFloater::onIMCompactExpandedMenuItemCheck(const LLSD& userdata) -{ - std::string item = userdata.asString(); - bool is_plain_text_mode = gSavedSettings.getBOOL("PlainTextChatHistory"); - - return is_plain_text_mode? item == "compact_view" : item == "expanded_view"; -} - -bool LLIMFloater::onIMShowModesMenuItemCheck(const LLSD& userdata) -{ - return gSavedSettings.getBOOL(userdata.asString()); } -// enable/disable states for the "show time" and "show names" items of the show-modes menu -bool LLIMFloater::onIMShowModesMenuItemEnable(const LLSD& userdata) +// static +void* LLIMFloater::createPanelGroupControl(void* userdata) { - std::string item = userdata.asString(); - bool plain_text = gSavedSettings.getBOOL("PlainTextChatHistory"); - bool is_not_names = (item != "IMShowNamesForP2PConv"); - return (plain_text && (is_not_names || mIsP2PChat)); + LLIMFloater *self = (LLIMFloater*) userdata; + self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID); + self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); + return self->mControlPanel; } -void LLIMFloater::onIMSessionMenuItemClicked(const LLSD& userdata) +// static +void* LLIMFloater::createPanelAdHocControl(void* userdata) { - std::string item = userdata.asString(); - - if (item == "compact_view" || item == "expanded_view") - { - gSavedSettings.setBOOL("PlainTextChatHistory", item == "compact_view"); - } - else - { - bool prev_value = gSavedSettings.getBOOL(item); - gSavedSettings.setBOOL(item, !prev_value); - } - - LLIMFloater::processChatHistoryStyleUpdate(); - LLNearbyChat::processChatHistoryStyleUpdate(); + LLIMFloater *self = (LLIMFloater*) userdata; + self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); + self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); + return self->mControlPanel; } void LLIMFloater::onFocusLost() { LLIMModel::getInstance()->resetActiveSessionID(); - + LLChicletBar::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false); } @@ -185,19 +150,6 @@ void LLIMFloater::onFocusReceived() } } -/*virtual*/ -void LLIMFloater::onOpen(const LLSD& key) -{ - LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); - if (host_floater) - { - // Show the messages pane when opening a floater hosted in the Conversations - host_floater->collapseMessagesPane(false); - } - - updateHeaderAndToolbar(); -} - // virtual void LLIMFloater::onClose(bool app_quitting) { @@ -219,10 +171,9 @@ void LLIMFloater::newIMCallback(const LLSD& data) LLUUID session_id = data["session_id"].asUUID(); LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); - if (floater == NULL) return; // update if visible, otherwise will be updated when opened - if (floater->getVisible()) + if (floater && floater->getVisible()) { floater->updateMessages(); } @@ -255,38 +206,39 @@ void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata ) void LLIMFloater::sendMsg() { - if (!gAgent.isGodlike() - && (mDialog == IM_NOTHING_SPECIAL) - && mOtherParticipantUUID.isNull()) - { - llinfos << "Cannot send IM to everyone unless you're a god." << llendl; - return; - } - - if (mInputEditor) + if (gAgent.isGodlike() + || (mDialog != IM_NOTHING_SPECIAL) + || !mOtherParticipantUUID.isNull()) { - LLWString text = mInputEditor->getConvertedText(); - if(!text.empty()) + if (mInputEditor) { - // Truncate and convert to UTF8 for transport - std::string utf8_text = wstring_to_utf8str(text); - utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - - if (mSessionInitialized) + LLWString text = mInputEditor->getConvertedText(); + if(!text.empty()) { - LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); - } - else - { - //queue up the message to send once the session is initialized - mQueuedMsgsForInit.append(utf8_text); - } + // Truncate and convert to UTF8 for transport + std::string utf8_text = wstring_to_utf8str(text); + utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); - mInputEditor->setText(LLStringUtil::null); + if (mSessionInitialized) + { + LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID, mDialog); + } + else + { + //queue up the message to send once the session is initialized + mQueuedMsgsForInit.append(utf8_text); + } + + mInputEditor->setText(LLStringUtil::null); - updateMessages(); + updateMessages(); + } } } + else + { + llinfos << "Cannot send IM to everyone unless you're a god." << llendl; + } } LLIMFloater::~LLIMFloater() @@ -312,28 +264,6 @@ BOOL LLIMFloater::postBuild() boundVoiceChannel(); - mCloseBtn = getChild<LLButton>("close_btn"); - mCloseBtn->setCommitCallback(boost::bind(&LLFloater::onClickClose, this)); - - mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); - mExpandCollapseBtn->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); - - if (mControlPanel) - { - mControlPanel->setSessionId(mSessionID); - mControlPanel->getParent()->setVisible(gSavedSettings.getBOOL("IMShowControlPanel")); - - mExpandCollapseBtn->setImageOverlay( - getString(mControlPanel->getParent()->getVisible() ? "collapse_icon" : "expand_icon")); - } - else - { - mExpandCollapseBtn->setEnabled(false); - getChild<LLLayoutPanel>("im_control_panel_holder")->setVisible(false); - } - - mTearOffBtn = getChild<LLButton>("tear_off_btn"); - mTearOffBtn->setCommitCallback(boost::bind(&LLIMFloater::onTearOffClicked, this)); mInputEditor = getChild<LLLineEditor>("chat_editor"); mInputEditor->setMaxTextLength(1023); @@ -386,21 +316,7 @@ BOOL LLIMFloater::postBuild() //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla" //see LLFloaterIMPanel for how it is done (IB) - if(isChatMultiTab()) - { - return LLFloater::postBuild(); - } - else - { - return LLDockableFloater::postBuild(); - } -} - -void LLIMFloater::onTearOffClicked() -{ - onClickTearOff(this); - - updateHeaderAndToolbar(); + return LLIMConversation::postBuild(); } void LLIMFloater::boundVoiceChannel() @@ -412,37 +328,29 @@ void LLIMFloater::boundVoiceChannel() boost::bind(&LLIMFloater::onVoiceChannelStateChanged, this, _1, _2)); //call (either p2p, group or ad-hoc) can be already in started state - updateCallState(voice_channel->getState()); + bool callIsActive = voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); } } -void LLIMFloater::updateCallState(LLVoiceChannel::EState state) -{ - bool is_call_started = state >= LLVoiceChannel::STATE_CALL_STARTED; - getChild<LLButton>("voice_call_btn")->setImageOverlay( - is_call_started? getString("call_btn_stop") : getString("call_btn_start")); - enableDisableCallBtn(); - -} - void LLIMFloater::enableDisableCallBtn() { bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); - if (!mSession) + if (mSession) + { + bool session_initialized = mSession->mSessionInitialized; + bool callback_enabled = mSession->mCallBackEnabled; + + BOOL enable_connect = + session_initialized && voice_enabled && callback_enabled; + getChildView("voice_call_btn")->setEnabled(enable_connect); + } + else { getChildView("voice_call_btn")->setEnabled(false); - return; } - - bool session_initialized = mSession->mSessionInitialized; - bool callback_enabled = mSession->mCallBackEnabled; - - BOOL enable_connect = session_initialized - && voice_enabled - && callback_enabled; - getChildView("voice_call_btn")->setEnabled(enable_connect); } @@ -470,18 +378,17 @@ void LLIMFloater::onCallButtonClicked() void LLIMFloater::onChange(EStatusType status, const std::string &channelURI, bool proximal) { - if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) + if(status != STATUS_JOINING && status != STATUS_LEFT_CHANNEL) { - return; + enableDisableCallBtn(); } - - enableDisableCallBtn(); } void LLIMFloater::onVoiceChannelStateChanged( const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state) { - updateCallState(new_state); + bool callIsActive = new_state >= LLVoiceChannel::STATE_CALL_STARTED; + updateCallBtnState(callIsActive); } void LLIMFloater::updateSessionName(const std::string& ui_title, @@ -516,48 +423,6 @@ void LLIMFloater::draw() LLTransientDockableFloater::draw(); } -// static -void* LLIMFloater::createPanelGroupControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); - return self->mControlPanel; -} - -// static -void* LLIMFloater::createPanelAdHocControl(void* userdata) -{ - LLIMFloater *self = (LLIMFloater*)userdata; - self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID); - self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml"); - return self->mControlPanel; -} - -void LLIMFloater::onSlide() -{ - LLIMFloaterContainer* host_floater = dynamic_cast<LLIMFloaterContainer*>(getHost()); - if (host_floater) - { - // Hide the messages pane if a floater is hosted in the Conversations - host_floater->collapseMessagesPane(true); - } - else ///< floater is torn off - { - if (mControlPanel) - { - bool expand = !mControlPanel->getParent()->getVisible(); - - // Expand/collapse the IM control panel - mControlPanel->getParent()->setVisible(expand); - - gSavedSettings.setBOOL("IMShowControlPanel", expand); - - mExpandCollapseBtn->setImageOverlay(getString(expand ? "collapse_icon" : "expand_icon")); - } - } -} - //static LLIMFloater* LLIMFloater::show(const LLUUID& session_id) { @@ -635,6 +500,22 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) return floater; } +//static +LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} + +LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) +{ + LLIMFloater* conversation = + LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); + + return conversation; +} void LLIMFloater::setDocked(bool docked, bool pop_on_undock) { @@ -697,6 +578,8 @@ void LLIMFloater::setVisible(BOOL visible) BOOL LLIMFloater::getVisible() { + bool visible; + if(isChatMultiTab()) { LLIMFloaterContainer* im_container = @@ -708,17 +591,21 @@ BOOL LLIMFloater::getVisible() //torn off floater is always inactive if (!is_active && getHost() != im_container) { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); + } + else + { + // getVisible() returns TRUE when Tabbed IM window is minimized. + visible = is_active && !im_container->isMinimized() + && im_container->getVisible(); } - - // getVisible() returns TRUE when Tabbed IM window is minimized. - return is_active && !im_container->isMinimized() - && im_container->getVisible(); } else { - return LLTransientDockableFloater::getVisible(); + visible = LLTransientDockableFloater::getVisible(); } + + return visible; } //static @@ -748,17 +635,6 @@ bool LLIMFloater::toggle(const LLUUID& session_id) return true; } -//static -LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id) -{ - return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); -} - -LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id) -{ - return LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id); -} - void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) { mSessionInitialized = true; @@ -782,14 +658,15 @@ void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id) //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB) //need to send delayed messaged collected while waiting for session initialization - if (!mQueuedMsgsForInit.size()) - return; - LLSD::array_iterator iter; - for ( iter = mQueuedMsgsForInit.beginArray(); - iter != mQueuedMsgsForInit.endArray(); ++iter) + if (mQueuedMsgsForInit.size()) { - LLIMModel::sendMessage(iter->asString(), mSessionID, - mOtherParticipantUUID, mDialog); + LLSD::array_iterator iter; + for ( iter = mQueuedMsgsForInit.beginArray(); + iter != mQueuedMsgsForInit.endArray(); ++iter) + { + LLIMModel::sendMessage(iter->asString(), mSessionID, + mOtherParticipantUUID, mDialog); + } } } @@ -878,16 +755,16 @@ void LLIMFloater::updateMessages() if (chat.mNotifId.notNull() && LLNotificationsUtil::find(chat.mNotifId) != NULL) { if (++iter == iter_end) - { - break; - } - else - { - mLastMessageIndex++; - } - } - } - } + { + break; + } + else + { + mLastMessageIndex++; + } + } + } + } } void LLIMFloater::reloadMessages() @@ -895,6 +772,7 @@ void LLIMFloater::reloadMessages() mChatHistory->clear(); mLastMessageIndex = -1; updateMessages(); + mInputEditor->setFont(LLViewerChat::getChatFont()); } // static @@ -923,28 +801,22 @@ void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userd // static void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata) { - LLIMFloater* self = (LLIMFloater*)userdata; + LLIMFloater* self = (LLIMFloater*) userdata; std::string text = self->mInputEditor->getText(); - if (!text.empty()) - { - self->setTyping(true); - } - else - { - // Deleting all text counts as stopping typing. - self->setTyping(false); - } + + // Deleting all text counts as stopping typing. + self->setTyping(!text.empty()); } void LLIMFloater::setTyping(bool typing) { - if ( typing ) + if (typing) { // Started or proceeded typing, reset the typing timeout timer mTypingTimeoutTimer.reset(); } - if ( mMeTyping != typing ) + if (mMeTyping != typing) { // Typing state is changed mMeTyping = typing; @@ -956,24 +828,16 @@ void LLIMFloater::setTyping(bool typing) // Don't want to send typing indicators to multiple people, potentially too // much network traffic. Only send in person-to-person IMs. - if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL ) + if (mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL) { - if ( mMeTyping ) - { - if ( mTypingTimer.getElapsedTimeF32() > 1.f ) - { - // Still typing, send 'start typing' notification - LLIMModel::instance().sendTypingState(mSessionID, - mOtherParticipantUUID, TRUE); - mShouldSendTypingState = false; - } - } - else + // Still typing, send 'start typing' notification or + // send 'stop typing' notification immediately + if (!mMeTyping || mTypingTimer.getElapsedTimeF32() > 1.f) { - // Send 'stop typing' notification immediately LLIMModel::instance().sendTypingState(mSessionID, - mOtherParticipantUUID, FALSE); + mOtherParticipantUUID, mMeTyping); mShouldSendTypingState = false; + } } @@ -985,7 +849,7 @@ void LLIMFloater::setTyping(bool typing) void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) { - if ( typing ) + if (typing) { // other user started typing addTypingIndicator(im_info); @@ -999,10 +863,7 @@ void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing) void LLIMFloater::processAgentListUpdates(const LLSD& body) { - if (!body.isMap()) - return; - - if ( body.has("agent_updates") && body["agent_updates"].isMap() ) + if (body.isMap() && body.has("agent_updates") && body["agent_updates"].isMap()) { LLSD agent_data = body["agent_updates"].get(gAgentID.asString()); if (agent_data.isMap() && agent_data.has("info")) @@ -1011,7 +872,7 @@ void LLIMFloater::processAgentListUpdates(const LLSD& body) if (agent_info.has("mutes")) { - BOOL moderator_muted_text = agent_info["mutes"]["text"].asBoolean(); + BOOL moderator_muted_text = agent_info["mutes"]["text"].asBoolean(); mInputEditor->setEnabled(!moderator_muted_text); std::string label; if (moderator_muted_text) @@ -1027,27 +888,11 @@ void LLIMFloater::processAgentListUpdates(const LLSD& body) } } -void LLIMFloater::processChatHistoryStyleUpdate() -{ - LLFontGL* font = LLViewerChat::getChatFont(); - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater) - { - floater->reloadMessages(); - floater->mInputEditor->setFont(font); - } - } -} - void LLIMFloater::processSessionUpdate(const LLSD& session_update) { // *TODO : verify following code when moderated mode will be implemented - if ( false && session_update.has("moderated_mode") && - session_update["moderated_mode"].has("voice") ) + if (false && session_update.has("moderated_mode") && + session_update["moderated_mode"].has("voice")) { BOOL voice_moderated = session_update["moderated_mode"]["voice"]; const std::string session_label = LLIMModel::instance().getName(mSessionID); @@ -1069,14 +914,14 @@ void LLIMFloater::processSessionUpdate(const LLSD& session_update) } BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, - BOOL drop, EDragAndDropType cargo_type, - void *cargo_data, EAcceptance *accept, - std::string& tooltip_msg) + BOOL drop, EDragAndDropType cargo_type, + void *cargo_data, EAcceptance *accept, + std::string& tooltip_msg) { if (mDialog == IM_NOTHING_SPECIAL) { LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop, - cargo_type, cargo_data, accept); + cargo_type, cargo_data, accept); } // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites @@ -1086,14 +931,14 @@ BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, if (cargo_type == DAD_CALLINGCARD) { - if (dropCallingCard((LLInventoryItem*)cargo_data, drop)) + if (dropCallingCard((LLInventoryItem*) cargo_data, drop)) { *accept = ACCEPT_YES_MULTI; } } else if (cargo_type == DAD_CATEGORY) { - if (dropCategory((LLInventoryCategory*)cargo_data, drop)) + if (dropCategory((LLInventoryCategory*) cargo_data, drop)) { *accept = ACCEPT_YES_MULTI; } @@ -1105,9 +950,9 @@ BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop) { BOOL rv = isInviteAllowed(); - if(rv && item && item->getCreatorUUID().notNull()) + if (rv && item && item->getCreatorUUID().notNull()) { - if(drop) + if (drop) { uuid_vec_t ids; ids.push_back(item->getCreatorUUID()); @@ -1125,26 +970,26 @@ BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop) BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop) { BOOL rv = isInviteAllowed(); - if(rv && category) + if (rv && category) { LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; LLUniqueBuddyCollector buddies; gInventory.collectDescendentsIf(category->getUUID(), - cats, - items, - LLInventoryModel::EXCLUDE_TRASH, - buddies); + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + buddies); S32 count = items.count(); - if(count == 0) + if (count == 0) { rv = FALSE; } - else if(drop) + else if (drop) { uuid_vec_t ids; ids.reserve(count); - for(S32 i = 0; i < count; ++i) + for (S32 i = 0; i < count; ++i) { ids.push_back(items.get(i)->getCreatorUUID()); } @@ -1156,11 +1001,11 @@ BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop) BOOL LLIMFloater::isInviteAllowed() const { - return ( (IM_SESSION_CONFERENCE_START == mDialog) - || (IM_SESSION_INVITE == mDialog) ); + return ((IM_SESSION_CONFERENCE_START == mDialog) + || (IM_SESSION_INVITE == mDialog)); } -class LLSessionInviteResponder : public LLHTTPClient::Responder +class LLSessionInviteResponder: public LLHTTPClient::Responder { public: LLSessionInviteResponder(const LLUUID& session_id) @@ -1181,60 +1026,60 @@ private: BOOL LLIMFloater::inviteToSession(const uuid_vec_t& ids) { LLViewerRegion* region = gAgent.getRegion(); - if (!region) + bool is_region_exist = !!region; + + if (is_region_exist) { - return FALSE; - } + S32 count = ids.size(); - S32 count = ids.size(); + if (isInviteAllowed() && (count > 0)) + { + llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; - if( isInviteAllowed() && (count > 0) ) - { - llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl; + std::string url = region->getCapability("ChatSessionRequest"); - std::string url = region->getCapability("ChatSessionRequest"); + LLSD data; - LLSD data; + data["params"] = LLSD::emptyArray(); + for (int i = 0; i < count; i++) + { + data["params"].append(ids[i]); + } - data["params"] = LLSD::emptyArray(); - for (int i = 0; i < count; i++) + data["method"] = "invite"; + data["session-id"] = mSessionID; + LLHTTPClient::post( + url, + data, + new LLSessionInviteResponder(mSessionID)); + } + else { - data["params"].append(ids[i]); + llinfos << "LLIMFloater::inviteToSession -" + << " no need to invite agents for " + << mDialog << llendl; + // successful add, because everyone that needed to get added + // was added. } - - data["method"] = "invite"; - data["session-id"] = mSessionID; - LLHTTPClient::post( - url, - data, - new LLSessionInviteResponder(mSessionID)); - } - else - { - llinfos << "LLIMFloater::inviteToSession -" - << " no need to invite agents for " - << mDialog << llendl; - // successful add, because everyone that needed to get added - // was added. } - return TRUE; + return is_region_exist; } void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) { // We may have lost a "stop-typing" packet, don't add it twice - if ( im_info && !mOtherTyping ) + if (im_info && !mOtherTyping) { mOtherTyping = true; // Save and set new title mSavedTitle = getTitle(); - setTitle (mTypingStart); + setTitle(mTypingStart); // Update speaker LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if ( speaker_mgr ) + if (speaker_mgr) { speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE); } @@ -1243,18 +1088,18 @@ void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info) void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) { - if ( mOtherTyping ) + if (mOtherTyping) { mOtherTyping = false; // Revert the title to saved one setTitle(mSavedTitle); - if ( im_info ) + if (im_info) { // Update speaker LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID); - if ( speaker_mgr ) + if (speaker_mgr) { speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE); } @@ -1262,59 +1107,6 @@ void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info) } } -void LLIMFloater::updateHeaderAndToolbar() -{ - bool is_hosted = getHost() != NULL; - - if (is_hosted) - { - for (S32 i = 0; i < BUTTON_COUNT; i++) - { - if (!mButtons[i]) - { - continue; - } - - // Hide the standard header buttons in a docked IM floater. - mButtons[i]->setVisible(false); - } -} - - bool is_control_panel_visible = false; - if (mControlPanel) - { - // Control panel should be visible only in torn off floaters. - is_control_panel_visible = !is_hosted && gSavedSettings.getBOOL("IMShowControlPanel"); - mControlPanel->getParent()->setVisible(is_control_panel_visible); - } - - // Display collapse image (<<) if the floater is hosted - // or if it is torn off but has an open control panel. - bool is_expanded = is_hosted || is_control_panel_visible; - mExpandCollapseBtn->setImageOverlay(getString(is_expanded ? "collapse_icon" : "expand_icon")); - - LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSessionID); - if (session) - { - // The button (>>) should be disabled for torn off P2P conversations. - mExpandCollapseBtn->setEnabled(is_hosted || !session->isP2PSessionType()); - } - else - { - llwarns << "IM session not found." << llendl; - } - - if (mDragHandle) - { - // toggle floater's drag handle and title visibility - mDragHandle->setVisible(!is_hosted); - } - - mTearOffBtn->setImageOverlay(getString(is_hosted ? "tear_off_icon" : "return_icon")); - - mCloseBtn->setVisible(is_hosted); -} - // static void LLIMFloater::closeHiddenIMToasts() { @@ -1352,14 +1144,6 @@ void LLIMFloater::confirmLeaveCallCallback(const LLSD& notification, const LLSD& } // static -bool LLIMFloater::isChatMultiTab() -{ - // Restart is required in order to change chat window type. - static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1; - return is_single_window; -} - -// static void LLIMFloater::initIMFloater() { // This is called on viewer start up @@ -1390,28 +1174,32 @@ void LLIMFloater::sRemoveTypingIndicator(const LLSD& data) void LLIMFloater::onIMChicletCreated( const LLUUID& session_id ) { + LLIMFloater::addToHost(session_id); +} - if (isChatMultiTab()) +void LLIMFloater::addToHost(const LLUUID& session_id) +{ + if (LLIMConversation::isChatMultiTab()) { - LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + LLIMFloaterContainer* im_box = LLIMFloaterContainer::findInstance(); if (!im_box) - return; - - if (LLIMFloater::findInstance(session_id)) - return; - - LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); + { + im_box = LLIMFloaterContainer::getInstance(); + } - im_box->addFloater(new_tab, FALSE, LLTabContainer::END); + if (im_box && !LLIMFloater::findInstance(session_id)) + { + LLIMFloater* new_tab = LLIMFloater::getInstance(session_id); + im_box->addFloater(new_tab, FALSE, LLTabContainer::END); + } } - } void LLIMFloater::onClickCloseBtn() { LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession( - mSessionID); + mSessionID); if (session == NULL) { diff --git a/indra/newview/llimfloater.h b/indra/newview/llimfloater.h index 8e7ab4cc21..c7793f73eb 100644 --- a/indra/newview/llimfloater.h +++ b/indra/newview/llimfloater.h @@ -28,17 +28,16 @@ #define LL_IMFLOATER_H #include "llimview.h" +#include "llimconversation.h" #include "llinstantmessage.h" #include "lllogchat.h" #include "lltooldraganddrop.h" #include "llvoicechannel.h" #include "llvoiceclient.h" -#include "lltransientdockablefloater.h" class LLAvatarName; class LLButton; class LLLineEditor; -class LLPanelChatControlPanel; class LLChatHistory; class LLInventoryItem; class LLInventoryCategory; @@ -48,8 +47,8 @@ class LLInventoryCategory; * optionally "docked" to the bottom tray. */ class LLIMFloater - : public LLTransientDockableFloater - , public LLVoiceClientStatusObserver + : public LLVoiceClientStatusObserver + , public LLIMConversation { LOG_CLASS(LLIMFloater); public: @@ -64,11 +63,16 @@ public: // Check typing timeout timer. /*virtual*/ void draw(); + static void* createPanelGroupControl(void* userdata); + static void* createPanelAdHocControl(void* userdata); + + static LLIMFloater* findInstance(const LLUUID& session_id); + static LLIMFloater* getInstance(const LLUUID& session_id); + static void addToHost(const LLUUID& session_id); + // LLFloater overrides - /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); - // Make IM conversion visible and update the message history static LLIMFloater* show(const LLUUID& session_id); @@ -76,10 +80,6 @@ public: // Returns true iff panel became visible static bool toggle(const LLUUID& session_id); - static LLIMFloater* findInstance(const LLUUID& session_id); - - static LLIMFloater* getInstance(const LLUUID& session_id); - void sessionInitReplyReceived(const LLUUID& im_session_id); // get new messages from LLIMModel @@ -102,6 +102,7 @@ public: void onChange(EStatusType status, const std::string &channelURI, bool proximal); + virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } virtual void onVoiceChannelStateChanged( const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state); @@ -110,28 +111,18 @@ public: void processAgentListUpdates(const LLSD& body); void processSessionUpdate(const LLSD& session_update); - static void processChatHistoryStyleUpdate(); - BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void *cargo_data, EAcceptance *accept, std::string& tooltip_msg); - /** - * Returns true if chat is displayed in multi tabbed floater - * false if chat is displayed in multiple windows - */ - static bool isChatMultiTab(); static void initIMFloater(); //used as a callback on receiving new IM message static void sRemoveTypingIndicator(const LLSD& data); - static void onIMChicletCreated(const LLUUID& session_id); - virtual LLTransientFloaterMgr::ETransientGroup getGroup() { return LLTransientFloaterMgr::IM; } - protected: /* virtual */ void onClickCloseBtn(); @@ -156,23 +147,13 @@ private: static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); void setTyping(bool typing); - void onSlide(); - static void* createPanelGroupControl(void* userdata); - static void* createPanelAdHocControl(void* userdata); - void onTearOffClicked(); - - bool onIMCompactExpandedMenuItemCheck(const LLSD& userdata); - bool onIMShowModesMenuItemCheck(const LLSD& userdata); - bool onIMShowModesMenuItemEnable(const LLSD& userdata); - void onIMSessionMenuItemClicked(const LLSD& userdata); void onCallButtonClicked(); - void boundVoiceChannel(); - void enableDisableCallBtn(); + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); - // refresh a visual state of the Call button - void updateCallState(LLVoiceChannel::EState state); + void boundVoiceChannel(); // Add the "User is typing..." indicator. void addTypingIndicator(const LLIMInfo* im_info); @@ -180,15 +161,11 @@ private: // Remove the "User is typing..." indicator. void removeTypingIndicator(const LLIMInfo* im_info = NULL); - /// Update floater header and toolbar buttons when hosted/torn off state is toggled. - void updateHeaderAndToolbar(); - static void closeHiddenIMToasts(); static void confirmLeaveCallCallback(const LLSD& notification, const LLSD& response); - LLPanelChatControlPanel* mControlPanel; - LLUUID mSessionID; + LLIMModel::LLIMSession* mSession; S32 mLastMessageIndex; @@ -204,7 +181,6 @@ private: bool mMeTyping; bool mOtherTyping; bool mShouldSendTypingState; - bool mIsP2PChat; LLFrameTimer mTypingTimer; LLFrameTimer mTypingTimeoutTimer; @@ -213,10 +189,6 @@ private: // connection to voice channel state change signal boost::signals2::connection mVoiceChannelStateChangeConnection; - - LLButton* mCloseBtn; - LLButton* mExpandCollapseBtn; - LLButton* mTearOffBtn; }; #endif // LL_IMFLOATER_H diff --git a/indra/newview/llimfloatercontainer.cpp b/indra/newview/llimfloatercontainer.cpp index b051440589..f72ddef412 100644 --- a/indra/newview/llimfloatercontainer.cpp +++ b/indra/newview/llimfloatercontainer.cpp @@ -31,6 +31,7 @@ #include "llfloaterreg.h" #include "lllayoutstack.h" +#include "llnearbychatbar.h" #include "llagent.h" #include "llavatariconctrl.h" diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 18d39b7aa4..c3ac1d32cb 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -41,7 +41,7 @@ #include "lltextutil.h" #include "lltrans.h" #include "lluictrlfactory.h" - +#include "llimconversation.h" #include "llagent.h" #include "llagentui.h" #include "llappviewer.h" diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index 3c4b0b9aae..497690d656 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -25,7 +25,6 @@ */ #include "llviewerprecompiledheaders.h" -#include "llnearbychat.h" #include "llviewercontrol.h" #include "llviewerwindow.h" #include "llrootview.h" @@ -93,9 +92,9 @@ static const S32 RESIZE_BAR_THICKNESS = 3; static LLRegisterPanelClassWrapper<LLNearbyChat> t_panel_nearby_chat("panel_nearby_chat"); -LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) -: LLPanel(p), - mChatHistory(NULL) +LLNearbyChat::LLNearbyChat(const LLNearbyChat::Params& p) + : LLPanel(p), + mChatHistory(NULL) { } @@ -117,10 +116,7 @@ BOOL LLNearbyChat::postBuild() mChatHistory = getChild<LLChatHistory>("chat_history"); - if(!LLPanel::postBuild()) - return false; - - return true; + return LLPanel::postBuild(); } @@ -139,8 +135,7 @@ void LLNearbyChat::appendMessage(const LLChat& chat, const LLSD &args) chat_args["use_plain_text_chat_history"] = gSavedSettings.getBOOL("PlainTextChatHistory"); chat_args["show_time"] = gSavedSettings.getBOOL("IMShowTime"); - chat_args["show_names_for_p2p_conv"] = false - || gSavedSettings.getBOOL("IMShowNamesForP2PConv"); + chat_args["show_names_for_p2p_conv"] = true; mChatHistory->appendMessage(chat, chat_args); } @@ -223,7 +218,7 @@ void LLNearbyChat::getAllowedRect(LLRect& rect) rect = gViewerWindow->getWorldViewRectScaled(); } -void LLNearbyChat::updateChatHistoryStyle() +void LLNearbyChat::reloadMessages() { mChatHistory->clear(); @@ -236,15 +231,6 @@ void LLNearbyChat::updateChatHistoryStyle() } } -//static -void LLNearbyChat::processChatHistoryStyleUpdate() -{ - LLFloater* chat_bar = LLFloaterReg::getInstance("chat_bar"); - LLNearbyChat* nearby_chat = chat_bar->findChild<LLNearbyChat>("nearby_chat"); - if(nearby_chat) - nearby_chat->updateChatHistoryStyle(); -} - void LLNearbyChat::loadHistory() { LLSD do_not_log; diff --git a/indra/newview/llnearbychat.h b/indra/newview/llnearbychat.h index 47f4de1c6d..62a41c17cb 100644 --- a/indra/newview/llnearbychat.h +++ b/indra/newview/llnearbychat.h @@ -29,13 +29,13 @@ #include "llscrollbar.h" #include "llviewerchat.h" -#include "llfloater.h" +#include "llpanel.h" class LLResizeBar; class LLChatHistory; class LLNearbyChat -: public LLPanel + : public LLPanel { public: LLNearbyChat(const Params& p = LLPanel::getDefaultParams()); @@ -56,12 +56,8 @@ public: /*virtual*/ void setVisible(BOOL visible); - virtual void updateChatHistoryStyle(); - - static void processChatHistoryStyleUpdate(); - void loadHistory(); - + void reloadMessages(); static LLNearbyChat* getInstance(); void removeScreenChat(); diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index b4224e30e6..82c00253e8 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -31,7 +31,7 @@ #include "llappviewer.h" #include "llfloaterreg.h" #include "lltrans.h" - +#include "llimfloatercontainer.h" #include "llfirstuse.h" #include "llnearbychatbar.h" #include "llagent.h" @@ -54,7 +54,7 @@ S32 LLNearbyChatBar::sLastSpecialChatChannel = 0; -const S32 EXPANDED_HEIGHT = 300; +const S32 EXPANDED_HEIGHT = 266; const S32 COLLAPSED_HEIGHT = 60; const S32 EXPANDED_MIN_HEIGHT = 150; @@ -72,7 +72,7 @@ static LLChatTypeTrigger sChatTypeTriggers[] = { }; LLNearbyChatBar::LLNearbyChatBar(const LLSD& key) -: LLFloater(key), +: LLIMConversation(key), mChatBox(NULL), mNearbyChat(NULL), mOutputMonitor(NULL), @@ -116,14 +116,44 @@ BOOL LLNearbyChatBar::postBuild() // Register for font change notifications LLViewerChat::setFontChangedCallback(boost::bind(&LLNearbyChatBar::onChatFontChange, this, _1)); + // childSetAction("voice_call_btn", boost::bind(&LLNearbyChatBar::onCallButtonClicked, this)); + enableResizeCtrls(true, true, false); - return TRUE; + addToHost(); + + return LLIMConversation::postBuild();; +} + +void LLNearbyChatBar::onCallButtonClicked() +{ + LLAgent::toggleMicrophone(NULL); +} + +void LLNearbyChatBar::enableDisableCallBtn() +{ + // bool btn_enabled = LLAgent::isActionAllowed("speak"); + + getChildView("voice_call_btn")->setEnabled(false /*btn_enabled*/); +} + +void LLNearbyChatBar::addToHost() +{ + if (LLIMConversation::isChatMultiTab()) + { + LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance(); + + if (im_box) + { + im_box->addFloater(this, FALSE, LLTabContainer::END); + } + } } // virtual void LLNearbyChatBar::onOpen(const LLSD& key) { + LLIMConversation::onOpen(key); showTranslationCheckbox(LLTranslate::isTranslationConfigured()); } @@ -160,6 +190,12 @@ LLNearbyChatBar* LLNearbyChatBar::getInstance() return LLFloaterReg::getTypedInstance<LLNearbyChatBar>("chat_bar"); } +//static +//LLNearbyChatBar* LLNearbyChatBar::findInstance() +//{ +// return LLFloaterReg::findTypedInstance<LLNearbyChatBar>("chat_bar"); +//} + void LLNearbyChatBar::showHistory() { openFloater(); @@ -178,7 +214,8 @@ void LLNearbyChatBar::showTranslationCheckbox(BOOL show) void LLNearbyChatBar::draw() { displaySpeakingIndicator(); - LLFloater::draw(); + updateCallBtnState(LLVoiceClient::getInstance()->getUserPTTState()); + LLIMConversation::draw(); } std::string LLNearbyChatBar::getCurrentChat() @@ -206,22 +243,24 @@ BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::strin U32 in_len = in_str.length(); S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers); - for (S32 n = 0; n < cnt; n++) - { - if (in_len > sChatTypeTriggers[n].name.length()) - continue; - - std::string trigger_trunc = sChatTypeTriggers[n].name; - LLStringUtil::truncate(trigger_trunc, in_len); + bool string_was_found = false; - if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + for (S32 n = 0; n < cnt && !string_was_found; n++) + { + if (in_len <= sChatTypeTriggers[n].name.length()) { - *out_str = sChatTypeTriggers[n].name; - return TRUE; + std::string trigger_trunc = sChatTypeTriggers[n].name; + LLStringUtil::truncate(trigger_trunc, in_len); + + if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc)) + { + *out_str = sChatTypeTriggers[n].name; + string_was_found = true; + } } } - return FALSE; + return string_was_found; } void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) @@ -421,6 +460,11 @@ void LLNearbyChatBar::onToggleNearbyChatPanel() gSavedSettings.setBOOL("nearbychat_history_visibility", mNearbyChat->getVisible()); } +void LLNearbyChatBar::reloadMessages() +{ + LLNearbyChat::getInstance()->reloadMessages(); +} + void LLNearbyChatBar::setMinimized(BOOL b) { LLNearbyChat* nearby_chat = getChild<LLNearbyChat>("nearby_chat"); @@ -531,20 +575,20 @@ void LLNearbyChatBar::startChat(const char* line) { LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - if (!cb ) - return; + if (cb ) + { + cb->setVisible(TRUE); + cb->setFocus(TRUE); + cb->mChatBox->setFocus(TRUE); - cb->setVisible(TRUE); - cb->setFocus(TRUE); - cb->mChatBox->setFocus(TRUE); + if (line) + { + std::string line_string(line); + cb->mChatBox->setText(line_string); + } - if (line) - { - std::string line_string(line); - cb->mChatBox->setText(line_string); + cb->mChatBox->setCursorToEnd(); } - - cb->mChatBox->setCursorToEnd(); } // Exit "chat mode" and do the appropriate focus changes @@ -553,13 +597,13 @@ void LLNearbyChatBar::stopChat() { LLNearbyChatBar* cb = LLNearbyChatBar::getInstance(); - if (!cb) - return; - - cb->mChatBox->setFocus(FALSE); + if (cb) + { + cb->mChatBox->setFocus(FALSE); - // stop typing animation - gAgent.stopTyping(); + // stop typing animation + gAgent.stopTyping(); + } } // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 8547cf0bce..e714c04498 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -27,26 +27,31 @@ #ifndef LL_LLNEARBYCHATBAR_H #define LL_LLNEARBYCHATBAR_H -#include "llfloater.h" +#include "llimconversation.h" #include "llcombobox.h" #include "llgesturemgr.h" #include "llchat.h" +#include "llnearbychat.h" #include "llvoiceclient.h" #include "lloutputmonitorctrl.h" #include "llspeakers.h" -class LLNearbyChatBar : public LLFloater +class LLNearbyChatBar : public LLIMConversation { public: // constructor for inline chat-bars (e.g. hosted in chat history window) LLNearbyChatBar(const LLSD& key); ~LLNearbyChatBar() {} - virtual BOOL postBuild(); + /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); static LLNearbyChatBar* getInstance(); +// static LLNearbyChatBar* findInstance(); + void addToHost(); + + void reloadMessages(); LLLineEditor* getChatBox() { return mChatBox; } virtual void draw(); @@ -83,6 +88,11 @@ protected: void displaySpeakingIndicator(); + void onCallButtonClicked(); + + // set the enable/disable state for the Call button + virtual void enableDisableCallBtn(); + // Which non-zero channel did we last chat on? static S32 sLastSpecialChatChannel; diff --git a/indra/newview/skins/default/xui/en/floater_chat_bar.xml b/indra/newview/skins/default/xui/en/floater_chat_bar.xml index 63992462b3..7688525e13 100644 --- a/indra/newview/skins/default/xui/en/floater_chat_bar.xml +++ b/indra/newview/skins/default/xui/en/floater_chat_bar.xml @@ -3,32 +3,151 @@ open_positioning="specified" specified_left="10" specified_bottom="10" - height="60" + background_visible="true" + default_tab_group="1" + height="355" + help_topic="chat_bar" layout="topleft" - legacy_header_height="25" - single_instance="true" - title="NEARBY CHAT" - save_rect="true" - save_visibility="true" - can_close="true" + name="chat_bar" + can_dock="false" can_minimize="true" - help_topic="chat_bar" - min_height="60" - min_width="150" + can_close="true" + visible="false" + width="394" can_resize="true" - default_tab_group="1" - name="chat_bar" - width="300"> + can_tear_off="false" + min_width="250" + min_height="80" + single_instance="true" + title="Nearby chat"> + <floater.string name="call_btn_start">VoicePTT_Off</floater.string> + <floater.string name="call_btn_stop">VoicePTT_On</floater.string> + <floater.string + name="collapse_icon" + value="TabIcon_Open_Off"/> + <floater.string + name="expand_icon" + value="TabIcon_Close_Off"/> + <floater.string + name="tear_off_icon" + value="tearoffbox.tga"/> + <floater.string + name="return_icon" + value="Icon_Dock_Foreground"/> + <view + follows="all" + layout="topleft" + name="contents_view" + top="0" + left="0" + height="355" + width="394"> + <panel + follows="left|top|right" + layout="topleft" + name="toolbar_panel" + top="0" + left="0" + height="35" + width="394"> + <menu_button + menu_filename="menu_im_session_showmodes.xml" + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Left_Over" + image_overlay="OptionsMenu_Off" + image_selected="Toolbar_Left_Selected" + image_unselected="Toolbar_Left_Off" + layout="topleft" + left="5" + name="view_options_btn" + top="5" + width="31" /> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="AddItem_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="4" + name="add_btn" + width="31"> + <commit_callback + function="Chats.add" /> + </button> + <button + follows="top|left" + height="25" + image_hover_unselected="Toolbar_Right_Over" + image_overlay="VoicePTT_Off" + image_selected="Toolbar_Right_Selected" + image_unselected="Toolbar_Right_Off" + layout="topleft" + top="5" + left_pad="4" + name="voice_call_btn" + width="31"> + <commit_callback + function="Chats.add" /> + </button> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="Icon_Close_Foreground" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left="283" + name="close_btn" + width="31"> + </button> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="TabIcon_Open_Off" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="expand_collapse_btn" + width="31"> + </button> + <button + follows="right|top" + height="25" + image_hover_unselected="Toolbar_Middle_Over" + image_overlay="tearoffbox.tga" + image_selected="Toolbar_Middle_Selected" + image_unselected="Toolbar_Middle_Off" + layout="topleft" + top="5" + left_pad="5" + name="tear_off_btn" + width="31"> + </button> + </panel> <panel - top="20" + top="35" + left="0" class="panel_nearby_chat" follow="all" - width="300" + width="390" height="0" visible="false" filename="panel_nearby_chat.xml" name="nearby_chat" /> - <panel width="300" + <panel + width="390" + height="10" + visible="true" /> + <panel width="394" height="31" left="0" name="bottom_panel" @@ -39,18 +158,15 @@ border_style="line" border_thickness="1" follows="left|right" - height="23" + height="20" label="Click here to chat." layout="topleft" - left_delta="7" - left="0" + left="1" max_length_bytes="1023" name="chat_box" - text_pad_left="5" - text_pad_right="25" tool_tip="Press Enter to say, Ctrl+Enter to shout" top="2" - width="255" /> + width="384" /> <output_monitor auto_update="true" follows="right" @@ -65,6 +181,7 @@ width="20" /> <button follows="right" + visible="false" is_toggle="true" width="20" top="2" @@ -81,4 +198,5 @@ tool_tip="Shows/hides nearby chat log"> </button> </panel> + </view> </floater> diff --git a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml index d683116eb8..b415ba780d 100644 --- a/indra/newview/skins/default/xui/en/panel_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/panel_nearby_chat.xml @@ -1,20 +1,21 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel follows="all" - height="300" + top="0" + bottom_delta="10" help_topic="nearby_chat" layout="topleft" name="nearby_chat" - width="320"> + width="394"> <layout_stack follows="all" - height="295" + height="278" layout="topleft" left="0" name="stack" top="5" orientation="vertical" - width="320"> + width="394"> <layout_panel auto_resize="false" height="26" @@ -23,7 +24,7 @@ name="translate_chat_checkbox_lp" top_delta="0" visible="true" - width="313"> + width="387"> <check_box top="10" control_name="TranslateChat" @@ -33,15 +34,15 @@ layout="topleft" left="5" name="translate_chat_checkbox" - width="300" /> + width="374" /> </layout_panel> <layout_panel auto_resize="true" - height="277" + height="256" left_delta="0" layout="topleft" name="chat_history_lp" - width="318"> + width="394"> <chat_history bg_readonly_color="ChatHistoryBgColor" bg_writeable_color="ChatHistoryBgColor" @@ -49,7 +50,7 @@ layout="topleft" left="5" left_widget_pad="0" - height="272" + height="240" name="chat_history" parse_highlights="true" parse_urls="true" @@ -57,7 +58,7 @@ text_color="ChatHistoryTextColor" text_readonly_color="ChatHistoryTextColor" top="0" - width="313" /> + width="384" /> </layout_panel> </layout_stack> </panel> |